DO NOT MERGE ANYWHERE Fix max number of sched scan SSIDs based on driver capability am: 0fbcc4cde5  -s ours
am: 0261db291a  -s ours

* commit '0261db291a46b21b557187e17368be5aabbe74ee':
  DO NOT MERGE ANYWHERE Fix max number of sched scan SSIDs based on driver capability

Change-Id: I3388e5f77d034c121e8e78d810b77dd1f0a713ca
diff --git a/CONTRIBUTIONS b/CONTRIBUTIONS
index ca09bae..76600bc 100644
--- a/CONTRIBUTIONS
+++ b/CONTRIBUTIONS
@@ -29,6 +29,34 @@
 unfortunately be accepted.
 
 
+The preferred method of submitting the contribution to the project is by
+email to the hostap mailing list:
+hostap@lists.infradead.org
+Note that the list may require subscription before accepting message
+without moderation. You can subscribe to the list at this address:
+http://lists.infradead.org/mailman/listinfo/hostap
+
+The message should contain an inlined patch against the current
+development branch (i.e., the master branch of
+git://w1.fi/hostap.git). Please make sure the software you use for
+sending the patch does not corrupt whitespace. If that cannot be fixed
+for some reason, it is better to include an attached version of the
+patch file than just send a whitespace damaged version in the message
+body.
+
+The patches should be separate logical changes rather than doing
+everything in a single patch. In other words, please keep cleanup, new
+features, and bug fixes all in their own patches. Each patch needs a
+commit log that describes the changes (what the changes fix, what
+functionality is added, why the changes are useful, etc.).
+
+Please try to follow the coding style used in the project.
+
+In general, the best way of generating a suitable formatted patch file
+is by committing the changes to a cloned git repository and using git
+format-patch. The patch can then be sent, e.g., with git send-email.
+
+
 History of license and contributions terms
 ------------------------------------------
 
@@ -112,7 +140,7 @@
 
 Modified BSD license (no advertisement clause):
 
-Copyright (c) 2002-2015, Jouni Malinen <j@w1.fi> and contributors
+Copyright (c) 2002-2016, Jouni Malinen <j@w1.fi> and contributors
 All Rights Reserved.
 
 Redistribution and use in source and binary forms, with or without
diff --git a/COPYING b/COPYING
index 5962e2f..7efce0d 100644
--- a/COPYING
+++ b/COPYING
@@ -1,7 +1,7 @@
 wpa_supplicant and hostapd
 --------------------------
 
-Copyright (c) 2002-2015, Jouni Malinen <j@w1.fi> and contributors
+Copyright (c) 2002-2016, Jouni Malinen <j@w1.fi> and contributors
 All Rights Reserved.
 
 
diff --git a/NOTICE b/NOTICE
new file mode 100644
index 0000000..8a98582
--- /dev/null
+++ b/NOTICE
@@ -0,0 +1,22 @@
+wpa_supplicant and hostapd
+--------------------------
+
+Copyright (c) 2002-2012, 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/README b/README
index 07d1d25..9685f58 100644
--- a/README
+++ b/README
@@ -1,7 +1,7 @@
 wpa_supplicant and hostapd
 --------------------------
 
-Copyright (c) 2002-2015, Jouni Malinen <j@w1.fi> and contributors
+Copyright (c) 2002-2016, Jouni Malinen <j@w1.fi> and contributors
 All Rights Reserved.
 
 These programs are licensed under the BSD license (the one with
diff --git a/hostapd/Android.mk b/hostapd/Android.mk
index 52d4cfe..67ca129 100644
--- a/hostapd/Android.mk
+++ b/hostapd/Android.mk
@@ -126,6 +126,15 @@
 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
@@ -166,17 +175,25 @@
 L_CFLAGS += -DCONFIG_NO_VLAN
 else
 OBJS += src/ap/vlan_init.c
-ifdef CONFIG_VLAN_NETLINK
+OBJS += src/ap/vlan_ifconfig.c
+OBJS += src/ap/vlan.c
 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
+OBJS += src/ap/vlan_full.c
+ifdef CONFIG_VLAN_NETLINK
 OBJS += src/ap/vlan_util.c
+else
+OBJS += src/ap/vlan_ioctl.c
 endif
-L_CFLAGS += -DCONFIG_VLAN_NETLINK
 endif
 endif
 
 ifdef CONFIG_NO_CTRL_IFACE
 L_CFLAGS += -DCONFIG_NO_CTRL_IFACE
 else
+OBJS += src/common/ctrl_iface_common.c
 OBJS += ctrl_iface.c
 OBJS += src/ap/ctrl_iface_ap.c
 endif
@@ -252,6 +269,27 @@
 L_CFLAGS += -DCONFIG_IEEE80211AC
 endif
 
+ifdef CONFIG_MBO
+L_CFLAGS += -DCONFIG_MBO
+OBJS += src/ap/mbo_ap.c
+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)
@@ -529,6 +567,7 @@
 ifeq ($(CONFIG_TLS), openssl)
 ifdef TLS_FUNCS
 OBJS += src/crypto/tls_openssl.c
+OBJS += src/crypto/tls_openssl_ocsp.c
 LIBS += -lssl
 endif
 OBJS += src/crypto/crypto_openssl.c
@@ -536,6 +575,8 @@
 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
@@ -623,6 +664,8 @@
 CONFIG_INTERNAL_MD4=y
 CONFIG_INTERNAL_MD5=y
 CONFIG_INTERNAL_SHA256=y
+CONFIG_INTERNAL_SHA384=y
+CONFIG_INTERNAL_SHA512=y
 CONFIG_INTERNAL_RC4=y
 CONFIG_INTERNAL_DH_GROUP5=y
 endif
@@ -751,11 +794,17 @@
 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
@@ -772,6 +821,17 @@
 endif
 ifdef NEED_SHA384
 L_CFLAGS += -DCONFIG_SHA384
+OBJS += src/crypto/sha384-prf.c
+endif
+
+ifdef CONFIG_INTERNAL_SHA384
+L_CFLAGS += -DCONFIG_INTERNAL_SHA384
+OBJS += src/crypto/sha384-internal.c
+endif
+
+ifdef CONFIG_INTERNAL_SHA512
+L_CFLAGS += -DCONFIG_INTERNAL_SHA512
+OBJS += src/crypto/sha512-internal.c
 endif
 
 ifdef NEED_DH_GROUPS
@@ -815,12 +875,6 @@
 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
diff --git a/hostapd/ChangeLog b/hostapd/ChangeLog
index e6f8c6a..af54e1e 100644
--- a/hostapd/ChangeLog
+++ b/hostapd/ChangeLog
@@ -1,5 +1,41 @@
 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)
diff --git a/hostapd/Makefile b/hostapd/Makefile
index d4fd36e..fa4af82 100644
--- a/hostapd/Makefile
+++ b/hostapd/Makefile
@@ -6,18 +6,39 @@
 CFLAGS = -MMD -O2 -Wall -g
 endif
 
+ifdef LIBS
+# If LIBS is set with some global build system defaults, clone those for
+# LIBS_c, LIBS_h, and LIBS_n to cover hostapd_cli, hlr_auc_gw, and
+# nt_password_hash as well.
+ifndef LIBS_c
+LIBS_c := $(LIBS)
+endif
+ifndef LIBS_h
+LIBS_h := $(LIBS)
+endif
+ifndef LIBS_n
+LIBS_n := $(LIBS)
+endif
+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
 
+ifndef CONFIG_NO_GITVER
+# Add VERSION_STR postfix for builds from a git repository
+ifeq ($(wildcard ../.git),../.git)
+GITVER := $(shell git describe --dirty=+)
+ifneq ($(GITVER),)
+CFLAGS += -DGIT_VERSION_STR_POSTFIX=\"-$(GITVER)\"
+endif
+endif
+endif
+
 ifdef CONFIG_TESTING_OPTIONS
 CFLAGS += -DCONFIG_TESTING_OPTIONS
 CONFIG_WPS_TESTING=y
@@ -107,6 +128,18 @@
 LIBS_n += -lrt
 endif
 
+ifdef CONFIG_ELOOP_POLL
+CFLAGS += -DCONFIG_ELOOP_POLL
+endif
+
+ifdef CONFIG_ELOOP_EPOLL
+CFLAGS += -DCONFIG_ELOOP_EPOLL
+endif
+
+ifdef CONFIG_ELOOP_KQUEUE
+CFLAGS += -DCONFIG_ELOOP_KQUEUE
+endif
+
 OBJS += ../src/utils/common.o
 OBJS_c += ../src/utils/common.o
 OBJS += ../src/utils/wpa_debug.o
@@ -157,22 +190,53 @@
 CFLAGS += -DCONFIG_NO_VLAN
 else
 OBJS += ../src/ap/vlan_init.o
-ifdef CONFIG_VLAN_NETLINK
+OBJS += ../src/ap/vlan_ifconfig.o
+OBJS += ../src/ap/vlan.o
 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
+OBJS += ../src/ap/vlan_full.o
+ifdef CONFIG_VLAN_NETLINK
 OBJS += ../src/ap/vlan_util.o
+else
+OBJS += ../src/ap/vlan_ioctl.o
 endif
-CFLAGS += -DCONFIG_VLAN_NETLINK
 endif
 endif
 
 ifdef CONFIG_NO_CTRL_IFACE
 CFLAGS += -DCONFIG_NO_CTRL_IFACE
 else
+ifeq ($(CONFIG_CTRL_IFACE), udp)
+CFLAGS += -DCONFIG_CTRL_IFACE_UDP
+else
+ifeq ($(CONFIG_CTRL_IFACE), udp6)
+CFLAGS += -DCONFIG_CTRL_IFACE_UDP
+CFLAGS += -DCONFIG_CTRL_IFACE_UDP_IPV6
+else
+ifeq ($(CONFIG_CTRL_IFACE), udp-remote)
+CFLAGS += -DCONFIG_CTRL_IFACE_UDP
+CFLAGS += -DCONFIG_CTRL_IFACE_UDP_REMOTE
+else
+ifeq ($(CONFIG_CTRL_IFACE), udp6-remote)
+CFLAGS += -DCONFIG_CTRL_IFACE_UDP
+CFLAGS += -DCONFIG_CTRL_IFACE_UDP_REMOTE
+CFLAGS += -DCONFIG_CTRL_IFACE_UDP_IPV6
+else
+CFLAGS += -DCONFIG_CTRL_IFACE_UNIX
+endif
+endif
+endif
+endif
+OBJS += ../src/common/ctrl_iface_common.o
 OBJS += ctrl_iface.o
 OBJS += ../src/ap/ctrl_iface_ap.o
 endif
 
-CFLAGS += -DCONFIG_CTRL_IFACE -DCONFIG_CTRL_IFACE_UNIX
+ifndef CONFIG_NO_CTRL_IFACE
+CFLAGS += -DCONFIG_CTRL_IFACE
+endif
 
 ifdef CONFIG_IAPP
 CFLAGS += -DCONFIG_IAPP
@@ -244,6 +308,11 @@
 CFLAGS += -DCONFIG_IEEE80211AC
 endif
 
+ifdef CONFIG_MBO
+CFLAGS += -DCONFIG_MBO
+OBJS += ../src/ap/mbo_ap.o
+endif
+
 include ../src/drivers/drivers.mak
 OBJS += $(DRV_AP_OBJS)
 CFLAGS += $(DRV_AP_CFLAGS)
@@ -526,6 +595,7 @@
 ifeq ($(CONFIG_TLS), openssl)
 ifdef TLS_FUNCS
 OBJS += ../src/crypto/tls_openssl.o
+OBJS += ../src/crypto/tls_openssl_ocsp.o
 LIBS += -lssl
 endif
 OBJS += ../src/crypto/crypto_openssl.o
@@ -533,8 +603,14 @@
 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)
@@ -620,6 +696,8 @@
 CONFIG_INTERNAL_MD4=y
 CONFIG_INTERNAL_MD5=y
 CONFIG_INTERNAL_SHA256=y
+CONFIG_INTERNAL_SHA384=y
+CONFIG_INTERNAL_SHA512=y
 CONFIG_INTERNAL_RC4=y
 CONFIG_INTERNAL_DH_GROUP5=y
 endif
@@ -747,11 +825,17 @@
 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
@@ -771,6 +855,17 @@
 endif
 ifdef NEED_SHA384
 CFLAGS += -DCONFIG_SHA384
+OBJS += ../src/crypto/sha384-prf.o
+endif
+
+ifdef CONFIG_INTERNAL_SHA384
+CFLAGS += -DCONFIG_INTERNAL_SHA384
+OBJS += ../src/crypto/sha384-internal.o
+endif
+
+ifdef CONFIG_INTERNAL_SHA512
+CFLAGS += -DCONFIG_INTERNAL_SHA512
+OBJS += ../src/crypto/sha512-internal.o
 endif
 
 ifdef NEED_DH_GROUPS
@@ -814,12 +909,6 @@
 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
@@ -898,6 +987,21 @@
 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)
@@ -960,9 +1064,11 @@
 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
diff --git a/hostapd/README b/hostapd/README
index 366b199..5d5fd36 100644
--- a/hostapd/README
+++ b/hostapd/README
@@ -2,7 +2,7 @@
 	  Authenticator and RADIUS authentication server
 ================================================================
 
-Copyright (c) 2002-2015, Jouni Malinen <j@w1.fi> and contributors
+Copyright (c) 2002-2016, Jouni Malinen <j@w1.fi> and contributors
 All Rights Reserved.
 
 This program is licensed under the BSD license (the one with
diff --git a/hostapd/android.config b/hostapd/android.config
index 938aa54..e382c40 100644
--- a/hostapd/android.config
+++ b/hostapd/android.config
@@ -25,6 +25,9 @@
 #LIBS += -L$(LIBNL)/lib
 CONFIG_LIBNL20=y
 
+# QCA vendor extensions to nl80211
+CONFIG_DRIVER_NL80211_QCA=y
+
 # Driver interface for FreeBSD net80211 layer (e.g., Atheros driver)
 #CONFIG_DRIVER_BSD=y
 #CFLAGS += -I/usr/local/include
@@ -180,5 +183,19 @@
 #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
+
+# Multiband Operation support
+# These extentions facilitate efficient use of multiple frequency bands
+# available to the AP and the devices that may associate with it.
+#CONFIG_MBO=y
diff --git a/hostapd/config_file.c b/hostapd/config_file.c
index 49f8320..c35d5ae 100644
--- a/hostapd/config_file.c
+++ b/hostapd/config_file.c
@@ -97,6 +97,8 @@
 		}
 
 		vlan->vlan_id = vlan_id;
+		vlan->vlan_desc.untagged = vlan_id;
+		vlan->vlan_desc.notempty = !!vlan_id;
 		os_strlcpy(vlan->ifname, pos, sizeof(vlan->ifname));
 		vlan->next = bss->vlan;
 		bss->vlan = vlan;
@@ -197,7 +199,10 @@
 
 		*acl = newacl;
 		os_memcpy((*acl)[*num].addr, addr, ETH_ALEN);
-		(*acl)[*num].vlan_id = vlan_id;
+		os_memset(&(*acl)[*num].vlan_id, 0,
+			  sizeof((*acl)[*num].vlan_id));
+		(*acl)[*num].vlan_id.untagged = vlan_id;
+		(*acl)[*num].vlan_id.notempty = !!vlan_id;
 		(*num)++;
 	}
 
@@ -912,11 +917,11 @@
 	IEEE80211_TX_QUEUE_DATA3 = 3 /* used for EDCA AC_BK data */
 };
 
-static int hostapd_config_tx_queue(struct hostapd_config *conf, char *name,
-				   char *val)
+static int hostapd_config_tx_queue(struct hostapd_config *conf,
+				   const char *name, const char *val)
 {
 	int num;
-	char *pos;
+	const char *pos;
 	struct hostapd_tx_queue_params *queue;
 
 	/* skip 'tx_queue_' prefix */
@@ -1160,9 +1165,21 @@
 	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]"))
@@ -1507,6 +1524,54 @@
 }
 
 
+static int parse_anqp_elem(struct hostapd_bss_config *bss, char *buf, int line)
+{
+	char *delim;
+	u16 infoid;
+	size_t len;
+	struct wpabuf *payload;
+	struct anqp_element *elem;
+
+	delim = os_strchr(buf, ':');
+	if (!delim)
+		return -1;
+	delim++;
+	infoid = atoi(buf);
+	len = os_strlen(delim);
+	if (len & 1)
+		return -1;
+	len /= 2;
+	payload = wpabuf_alloc(len);
+	if (!payload)
+		return -1;
+	if (hexstr2bin(delim, wpabuf_put(payload, len), len) < 0) {
+		wpabuf_free(payload);
+		return -1;
+	}
+
+	dl_list_for_each(elem, &bss->anqp_elem, struct anqp_element, list) {
+		if (elem->infoid == infoid) {
+			/* Update existing entry */
+			wpabuf_free(elem->payload);
+			elem->payload = payload;
+			return 0;
+		}
+	}
+
+	/* Add a new entry */
+	elem = os_zalloc(sizeof(*elem));
+	if (!elem) {
+		wpabuf_free(payload);
+		return -1;
+	}
+	elem->infoid = infoid;
+	elem->payload = payload;
+	dl_list_add(&bss->anqp_elem, &elem->list);
+
+	return 0;
+}
+
+
 static int parse_qos_map_set(struct hostapd_bss_config *bss,
 			     char *buf, int line)
 {
@@ -1924,7 +1989,7 @@
 
 static int hostapd_config_fill(struct hostapd_config *conf,
 			       struct hostapd_bss_config *bss,
-			       char *buf, char *pos, int line)
+			       const char *buf, char *pos, int line)
 {
 	if (os_strcmp(buf, "interface") == 0) {
 		os_strlcpy(conf->bss[0]->iface, pos,
@@ -2067,9 +2132,14 @@
 		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, "ocsp_stapling_response_multi") == 0) {
+		os_free(bss->ocsp_stapling_response_multi);
+		bss->ocsp_stapling_response_multi = os_strdup(pos);
 	} else if (os_strcmp(buf, "dh_file") == 0) {
 		os_free(bss->dh_file);
 		bss->dh_file = os_strdup(pos);
@@ -2125,6 +2195,8 @@
 	} 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_db_timeout") == 0) {
+		bss->eap_sim_db_timeout = atoi(pos);
 	} else if (os_strcmp(buf, "eap_sim_aka_result_ind") == 0) {
 		bss->eap_sim_aka_result_ind = atoi(pos);
 #endif /* EAP_SERVER_SIM */
@@ -2630,7 +2702,7 @@
 		}
 	} else if (os_strcmp(buf, "rts_threshold") == 0) {
 		conf->rts_threshold = atoi(pos);
-		if (conf->rts_threshold < 0 || conf->rts_threshold > 2347) {
+		if (conf->rts_threshold < -1 || conf->rts_threshold > 65535) {
 			wpa_printf(MSG_ERROR,
 				   "Line %d: invalid rts_threshold %d",
 				   line, conf->rts_threshold);
@@ -2638,8 +2710,10 @@
 		}
 	} else if (os_strcmp(buf, "fragm_threshold") == 0) {
 		conf->fragm_threshold = atoi(pos);
-		if (conf->fragm_threshold < 256 ||
-		    conf->fragm_threshold > 2346) {
+		if (conf->fragm_threshold == -1) {
+			/* allow a value of -1 */
+		} else if (conf->fragm_threshold < 256 ||
+			   conf->fragm_threshold > 2346) {
 			wpa_printf(MSG_ERROR,
 				   "Line %d: invalid fragm_threshold %d",
 				   line, conf->fragm_threshold);
@@ -2672,6 +2746,8 @@
 			conf->preamble = LONG_PREAMBLE;
 	} else if (os_strcmp(buf, "ignore_broadcast_ssid") == 0) {
 		bss->ignore_broadcast_ssid = atoi(pos);
+	} else if (os_strcmp(buf, "no_probe_resp_if_max_sta") == 0) {
+		bss->no_probe_resp_if_max_sta = atoi(pos);
 	} else if (os_strcmp(buf, "wep_default_key") == 0) {
 		bss->ssid.wep.idx = atoi(pos);
 		if (bss->ssid.wep.idx > 3) {
@@ -2693,6 +2769,8 @@
 #ifndef CONFIG_NO_VLAN
 	} else if (os_strcmp(buf, "dynamic_vlan") == 0) {
 		bss->ssid.dynamic_vlan = atoi(pos);
+	} else if (os_strcmp(buf, "per_sta_vif") == 0) {
+		bss->ssid.per_sta_vif = 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'",
@@ -2748,6 +2826,8 @@
 				   line);
 			return 1;
 		}
+	} else if (os_strcmp(buf, "use_driver_iface_addr") == 0) {
+		conf->use_driver_iface_addr = atoi(pos);
 #ifdef CONFIG_IEEE80211W
 	} else if (os_strcmp(buf, "ieee80211w") == 0) {
 		bss->ieee80211w = atoi(pos);
@@ -3122,6 +3202,9 @@
 	} else if (os_strcmp(buf, "nai_realm") == 0) {
 		if (parse_nai_realm(bss, pos, line) < 0)
 			return 1;
+	} else if (os_strcmp(buf, "anqp_elem") == 0) {
+		if (parse_anqp_elem(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) {
@@ -3135,13 +3218,15 @@
 		os_free(bss->dump_msk_file);
 		bss->dump_msk_file = os_strdup(pos);
 #endif /* CONFIG_RADIUS_TEST */
+#ifdef CONFIG_PROXYARP
+	} else if (os_strcmp(buf, "proxy_arp") == 0) {
+		bss->proxy_arp = atoi(pos);
+#endif /* CONFIG_PROXYARP */
 #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) {
@@ -3217,6 +3302,10 @@
 	} else if (os_strcmp(buf, "subscr_remediation_method") == 0) {
 		bss->subscr_remediation_method = atoi(pos);
 #endif /* CONFIG_HS20 */
+#ifdef CONFIG_MBO
+	} else if (os_strcmp(buf, "mbo") == 0) {
+		bss->mbo_enabled = atoi(pos);
+#endif /* CONFIG_MBO */
 #ifdef CONFIG_TESTING_OPTIONS
 #define PARSE_TEST_PROBABILITY(_val)				\
 	} else if (os_strcmp(buf, #_val) == 0) {		\
@@ -3235,6 +3324,8 @@
 	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, "ecsa_ie_only") == 0) {
+		conf->ecsa_ie_only = atoi(pos);
 	} else if (os_strcmp(buf, "bss_load_test") == 0) {
 		WPA_PUT_LE16(bss->bss_load_test, atoi(pos));
 		pos = os_strchr(pos, ':');
@@ -3256,6 +3347,24 @@
 		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;
@@ -3309,6 +3418,75 @@
 	} 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 ||
+		    (unsigned long int) 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'",
@@ -3329,7 +3507,7 @@
 {
 	struct hostapd_config *conf;
 	FILE *f;
-	char buf[512], *pos;
+	char buf[4096], *pos;
 	int line = 0;
 	int errors = 0;
 	size_t i;
@@ -3411,7 +3589,8 @@
 
 
 int hostapd_set_iface(struct hostapd_config *conf,
-		      struct hostapd_bss_config *bss, char *field, char *value)
+		      struct hostapd_bss_config *bss, const char *field,
+		      char *value)
 {
 	int errors;
 	size_t i;
diff --git a/hostapd/config_file.h b/hostapd/config_file.h
index fba57b8..c98bdb6 100644
--- a/hostapd/config_file.h
+++ b/hostapd/config_file.h
@@ -11,7 +11,7 @@
 
 struct hostapd_config * hostapd_config_read(const char *fname);
 int hostapd_set_iface(struct hostapd_config *conf,
-		      struct hostapd_bss_config *bss, char *field,
+		      struct hostapd_bss_config *bss, const char *field,
 		      char *value);
 
 #endif /* CONFIG_FILE_H */
diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
index c606f2c..a87f117 100644
--- a/hostapd/ctrl_iface.c
+++ b/hostapd/ctrl_iface.c
@@ -19,12 +19,18 @@
 #include <sys/stat.h>
 #include <stddef.h>
 
+#ifdef CONFIG_CTRL_IFACE_UDP
+#include <netdb.h>
+#endif /* CONFIG_CTRL_IFACE_UDP */
+
 #include "utils/common.h"
 #include "utils/eloop.h"
 #include "common/version.h"
 #include "common/ieee802_11_defs.h"
+#include "common/ctrl_iface_common.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"
@@ -43,18 +49,22 @@
 #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"
 
 
-struct wpa_ctrl_dst {
-	struct wpa_ctrl_dst *next;
-	struct sockaddr_un addr;
-	socklen_t addrlen;
-	int debug_level;
-	int errors;
-};
+#define HOSTAPD_CLI_DUP_VALUE_MAX_LEN 256
 
+#ifdef CONFIG_CTRL_IFACE_UDP
+#define COOKIE_LEN 8
+static unsigned char cookie[COOKIE_LEN];
+static unsigned char gcookie[COOKIE_LEN];
+#define HOSTAPD_CTRL_IFACE_PORT		8877
+#define HOSTAPD_CTRL_IFACE_PORT_LIMIT	50
+#define HOSTAPD_GLOBAL_CTRL_IFACE_PORT		8878
+#define HOSTAPD_GLOBAL_CTRL_IFACE_PORT_LIMIT	50
+#endif /* CONFIG_CTRL_IFACE_UDP */
 
 static void hostapd_ctrl_iface_send(struct hostapd_data *hapd, int level,
 				    enum wpa_msg_type type,
@@ -62,81 +72,27 @@
 
 
 static int hostapd_ctrl_iface_attach(struct hostapd_data *hapd,
-				     struct sockaddr_un *from,
+				     struct sockaddr_storage *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;
+	return ctrl_iface_attach(&hapd->ctrl_dst, from, fromlen);
 }
 
 
 static int hostapd_ctrl_iface_detach(struct hostapd_data *hapd,
-				     struct sockaddr_un *from,
+				     struct sockaddr_storage *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;
+	return ctrl_iface_detach(&hapd->ctrl_dst, from, fromlen);
 }
 
 
 static int hostapd_ctrl_iface_level(struct hostapd_data *hapd,
-				    struct sockaddr_un *from,
+				    struct sockaddr_storage *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;
+	return ctrl_iface_level(&hapd->ctrl_dst, from, fromlen, level);
 }
 
 
@@ -880,6 +836,8 @@
 	int ret;
 	u8 nei_rep[1000];
 	u8 *nei_pos = nei_rep;
+	u8 mbo[10];
+	size_t mbo_len = 0;
 
 	if (hwaddr_aton(cmd, addr)) {
 		wpa_printf(MSG_DEBUG, "Invalid STA MAC address");
@@ -1045,10 +1003,66 @@
 	if (os_strstr(cmd, " disassoc_imminent=1"))
 		req_mode |= WNM_BSS_TM_REQ_DISASSOC_IMMINENT;
 
+#ifdef CONFIG_MBO
+	pos = os_strstr(cmd, "mbo=");
+	if (pos) {
+		unsigned int mbo_reason, cell_pref, reassoc_delay;
+		u8 *mbo_pos = mbo;
+
+		ret = sscanf(pos, "mbo=%u:%u:%u", &mbo_reason,
+			     &reassoc_delay, &cell_pref);
+		if (ret != 3) {
+			wpa_printf(MSG_DEBUG,
+				   "MBO requires three arguments: mbo=<reason>:<reassoc_delay>:<cell_pref>");
+			return -1;
+		}
+
+		if (mbo_reason > MBO_TRANSITION_REASON_PREMIUM_AP) {
+			wpa_printf(MSG_DEBUG,
+				   "Invalid MBO transition reason code %u",
+				   mbo_reason);
+			return -1;
+		}
+
+		/* Valid values for Cellular preference are: 0, 1, 255 */
+		if (cell_pref != 0 && cell_pref != 1 && cell_pref != 255) {
+			wpa_printf(MSG_DEBUG,
+				   "Invalid MBO cellular capability %u",
+				   cell_pref);
+			return -1;
+		}
+
+		if (reassoc_delay > 65535 ||
+		    (reassoc_delay &&
+		     !(req_mode & WNM_BSS_TM_REQ_DISASSOC_IMMINENT))) {
+			wpa_printf(MSG_DEBUG,
+				   "MBO: Assoc retry delay is only valid in disassoc imminent mode");
+			return -1;
+		}
+
+		*mbo_pos++ = MBO_ATTR_ID_TRANSITION_REASON;
+		*mbo_pos++ = 1;
+		*mbo_pos++ = mbo_reason;
+		*mbo_pos++ = MBO_ATTR_ID_CELL_DATA_PREF;
+		*mbo_pos++ = 1;
+		*mbo_pos++ = cell_pref;
+
+		if (reassoc_delay) {
+			*mbo_pos++ = MBO_ATTR_ID_ASSOC_RETRY_DELAY;
+			*mbo_pos++ = 2;
+			WPA_PUT_LE16(mbo_pos, reassoc_delay);
+			mbo_pos += 2;
+		}
+
+		mbo_len = mbo_pos - mbo;
+	}
+#endif /* CONFIG_MBO */
+
 	ret = wnm_send_bss_tm_req(hapd, sta, req_mode, disassoc_timer,
 				  valid_int, bss_term_dur, url,
 				  nei_pos > nei_rep ? nei_rep : NULL,
-				  nei_pos - nei_rep);
+				  nei_pos - nei_rep, mbo_len ? mbo : NULL,
+				  mbo_len);
 	os_free(url);
 	return ret;
 }
@@ -1056,6 +1070,97 @@
 #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)
 {
@@ -1105,82 +1210,20 @@
 	}
 #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;
 
-		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;
-		}
+		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))
@@ -1287,9 +1330,28 @@
 	} else if (os_strcasecmp(cmd, "ext_eapol_frame_io") == 0) {
 		hapd->ext_eapol_frame_io = atoi(value);
 #endif /* CONFIG_TESTING_OPTIONS */
+#ifdef CONFIG_MBO
+	} else if (os_strcasecmp(cmd, "mbo_assoc_disallow") == 0) {
+		int val;
+
+		if (!hapd->conf->mbo_enabled)
+			return -1;
+
+		val = atoi(value);
+		if (val < 0 || val > 1)
+			return -1;
+
+		hapd->mbo_assoc_disallow = val;
+		ieee802_11_update_beacons(hapd->iface);
+
+		/*
+		 * TODO: Need to configure drivers that do AP MLME offload with
+		 * disallowing station logic.
+		 */
+#endif /* CONFIG_MBO */
 	} else {
 		struct sta_info *sta;
-		int vlan_id;
+		struct vlan_description vlan_id;
 
 		ret = hostapd_set_iface(hapd->iconf, hapd->conf, cmd, value);
 		if (ret)
@@ -1301,7 +1363,8 @@
 					    hapd->conf->deny_mac,
 					    hapd->conf->num_deny_mac, sta->addr,
 					    &vlan_id) &&
-				    (!vlan_id || vlan_id == sta->vlan_id))
+				    (!vlan_id.notempty ||
+				     !vlan_compare(&vlan_id, sta->vlan_desc)))
 					ap_sta_disconnect(
 						hapd, sta, sta->addr,
 						WLAN_REASON_UNSPECIFIED);
@@ -1313,7 +1376,8 @@
 					    hapd->conf->accept_mac,
 					    hapd->conf->num_accept_mac,
 					    sta->addr, &vlan_id) ||
-				    (vlan_id && vlan_id != sta->vlan_id))
+				    (vlan_id.notempty &&
+				     vlan_compare(&vlan_id, sta->vlan_desc)))
 					ap_sta_disconnect(
 						hapd, sta, sta->addr,
 						WLAN_REASON_UNSPECIFIED);
@@ -1529,7 +1593,7 @@
 {
 	struct hostapd_data *hapd = ctx;
 	const struct ether_header *eth;
-	const struct iphdr *ip;
+	struct iphdr ip;
 	const u8 *pos;
 	unsigned int i;
 
@@ -1537,14 +1601,14 @@
 		return;
 
 	eth = (const struct ether_header *) buf;
-	ip = (const struct iphdr *) (eth + 1);
-	pos = (const u8 *) (ip + 1);
+	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)
+	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++) {
+	for (i = 0; i < HWSIM_IP_LEN - sizeof(ip); i++) {
 		if (*pos != (u8) i)
 			return;
 		pos++;
@@ -1600,7 +1664,7 @@
 	int used;
 	long int val;
 	u8 tos;
-	u8 buf[HWSIM_PACKETLEN];
+	u8 buf[2 + HWSIM_PACKETLEN];
 	struct ether_header *eth;
 	struct iphdr *ip;
 	u8 *dpos;
@@ -1628,7 +1692,7 @@
 		return -1;
 	tos = val;
 
-	eth = (struct ether_header *) buf;
+	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);
@@ -1640,14 +1704,14 @@
 	ip->tos = tos;
 	ip->tot_len = htons(HWSIM_IP_LEN);
 	ip->protocol = 1;
-	ip->saddr = htonl(192 << 24 | 168 << 16 | 1 << 8 | 1);
-	ip->daddr = htonl(192 << 24 | 168 << 16 | 1 << 8 | 2);
+	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,
+	if (l2_packet_send(hapd->l2_test, dst, ETHERTYPE_IP, &buf[2],
 			   HWSIM_PACKETLEN) < 0)
 		return -1;
 
@@ -1747,6 +1811,45 @@
 #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 */
 
 
@@ -1803,13 +1906,13 @@
 
 	/* cmd: <vendor id> <subcommand id> [<hex formatted data>] */
 	vendor_id = strtoul(cmd, &pos, 16);
-	if (!isblank(*pos))
+	if (!isblank((unsigned char) *pos))
 		return -EINVAL;
 
 	subcmd = strtoul(pos, &pos, 10);
 
 	if (*pos != '\0') {
-		if (!isblank(*pos++))
+		if (!isblank((unsigned char) *pos++))
 			return -EINVAL;
 		data_len = os_strlen(pos);
 	}
@@ -1848,41 +1951,134 @@
 }
 
 
-static void hostapd_ctrl_iface_receive(int sock, void *eloop_ctx,
-				       void *sock_ctx)
+static int hostapd_ctrl_iface_eapol_reauth(struct hostapd_data *hapd,
+					   const char *cmd)
 {
-	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;
+	u8 addr[ETH_ALEN];
+	struct sta_info *sta;
 
-	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;
+	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;
 	}
-	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));
+	while (*cmd == ' ')
+		cmd++;
+
+	stamp = os_strchr(cmd, ' ');
+	if (stamp) {
+		*stamp++ = '\0';
+		while (*stamp == ' ') {
+			stamp++;
 		}
-		return;
 	}
 
+	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_storage *from,
+					      socklen_t fromlen)
+{
+	int reply_len, res;
+
 	os_memcpy(reply, "OK\n", 3);
 	reply_len = 3;
 
@@ -1939,13 +2135,13 @@
 		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))
+		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))
+		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,
+		if (hostapd_ctrl_iface_level(hapd, from, fromlen,
 						    buf + 6))
 			reply_len = -1;
 	} else if (os_strncmp(buf, "NEW_STA ", 8) == 0) {
@@ -2080,6 +2276,11 @@
 	} 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))
@@ -2092,6 +2293,25 @@
 #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 if (os_strcmp(buf, "PMKSA") == 0) {
+		reply_len = hostapd_ctrl_iface_pmksa_list(hapd, reply,
+							  reply_size);
+	} else if (os_strcmp(buf, "PMKSA_FLUSH") == 0) {
+		hostapd_ctrl_iface_pmksa_flush(hapd);
 	} else {
 		os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
 		reply_len = 16;
@@ -2101,6 +2321,86 @@
 		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_storage from;
+	socklen_t fromlen = sizeof(from);
+	char *reply, *pos = buf;
+	const int reply_size = 4096;
+	int reply_len;
+	int level = MSG_DEBUG;
+#ifdef CONFIG_CTRL_IFACE_UDP
+	unsigned char lcookie[COOKIE_LEN];
+#endif /* CONFIG_CTRL_IFACE_UDP */
+
+	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';
+
+	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;
+	}
+
+#ifdef CONFIG_CTRL_IFACE_UDP
+	if (os_strcmp(buf, "GET_COOKIE") == 0) {
+		os_memcpy(reply, "COOKIE=", 7);
+		wpa_snprintf_hex(reply + 7, 2 * COOKIE_LEN + 1,
+				 cookie, COOKIE_LEN);
+		reply_len = 7 + 2 * COOKIE_LEN;
+		goto done;
+	}
+
+	if (os_strncmp(buf, "COOKIE=", 7) != 0 ||
+	    hexstr2bin(buf + 7, lcookie, COOKIE_LEN) < 0) {
+		wpa_printf(MSG_DEBUG,
+			   "CTRL: No cookie in the request - drop request");
+		os_free(reply);
+		return;
+	}
+
+	if (os_memcmp(cookie, lcookie, COOKIE_LEN) != 0) {
+		wpa_printf(MSG_DEBUG,
+			   "CTRL: Invalid cookie in the request - drop request");
+		os_free(reply);
+		return;
+	}
+
+	pos = buf + 7 + 2 * COOKIE_LEN;
+	while (*pos == ' ')
+		pos++;
+#endif /* CONFIG_CTRL_IFACE_UDP */
+
+	if (os_strcmp(pos, "PING") == 0)
+		level = MSG_EXCESSIVE;
+	wpa_hexdump_ascii(level, "RX ctrl_iface", pos, res);
+
+	reply_len = hostapd_ctrl_iface_receive_process(hapd, pos,
+						       reply, reply_size,
+						       &from, fromlen);
+
+#ifdef CONFIG_CTRL_IFACE_UDP
+done:
+#endif /* CONFIG_CTRL_IFACE_UDP */
 	if (sendto(sock, reply, reply_len, 0, (struct sockaddr *) &from,
 		   fromlen) < 0) {
 		wpa_printf(MSG_DEBUG, "CTRL: sendto failed: %s",
@@ -2110,6 +2410,7 @@
 }
 
 
+#ifndef CONFIG_CTRL_IFACE_UDP
 static char * hostapd_ctrl_iface_path(struct hostapd_data *hapd)
 {
 	char *buf;
@@ -2129,6 +2430,7 @@
 	buf[len - 1] = '\0';
 	return buf;
 }
+#endif /* CONFIG_CTRL_IFACE_UDP */
 
 
 static void hostapd_ctrl_iface_msg_cb(void *ctx, int level,
@@ -2144,6 +2446,99 @@
 
 int hostapd_ctrl_iface_init(struct hostapd_data *hapd)
 {
+#ifdef CONFIG_CTRL_IFACE_UDP
+	int port = HOSTAPD_CTRL_IFACE_PORT;
+	char p[32] = { 0 };
+	char port_str[40], *tmp;
+	char *pos;
+	struct addrinfo hints = { 0 }, *res, *saveres;
+	int n;
+
+	if (hapd->ctrl_sock > -1) {
+		wpa_printf(MSG_DEBUG, "ctrl_iface already exists!");
+		return 0;
+	}
+
+	if (hapd->conf->ctrl_interface == NULL)
+		return 0;
+
+	pos = os_strstr(hapd->conf->ctrl_interface, "udp:");
+	if (pos) {
+		pos += 4;
+		port = atoi(pos);
+		if (port <= 0) {
+			wpa_printf(MSG_ERROR, "Invalid ctrl_iface UDP port");
+			goto fail;
+		}
+	}
+
+	dl_list_init(&hapd->ctrl_dst);
+	hapd->ctrl_sock = -1;
+	os_get_random(cookie, COOKIE_LEN);
+
+#ifdef CONFIG_CTRL_IFACE_UDP_REMOTE
+	hints.ai_flags = AI_PASSIVE;
+#endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */
+
+#ifdef CONFIG_CTRL_IFACE_UDP_IPV6
+	hints.ai_family = AF_INET6;
+#else /* CONFIG_CTRL_IFACE_UDP_IPV6 */
+	hints.ai_family = AF_INET;
+#endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */
+	hints.ai_socktype = SOCK_DGRAM;
+
+try_again:
+	os_snprintf(p, sizeof(p), "%d", port);
+	n = getaddrinfo(NULL, p, &hints, &res);
+	if (n) {
+		wpa_printf(MSG_ERROR, "getaddrinfo(): %s", gai_strerror(n));
+		goto fail;
+	}
+
+	saveres = res;
+	hapd->ctrl_sock = socket(res->ai_family, res->ai_socktype,
+				 res->ai_protocol);
+	if (hapd->ctrl_sock < 0) {
+		wpa_printf(MSG_ERROR, "socket(PF_INET): %s", strerror(errno));
+		goto fail;
+	}
+
+	if (bind(hapd->ctrl_sock, res->ai_addr, res->ai_addrlen) < 0) {
+		port--;
+		if ((HOSTAPD_CTRL_IFACE_PORT - port) <
+		    HOSTAPD_CTRL_IFACE_PORT_LIMIT && !pos)
+			goto try_again;
+		wpa_printf(MSG_ERROR, "bind(AF_INET): %s", strerror(errno));
+		goto fail;
+	}
+
+	freeaddrinfo(saveres);
+
+	os_snprintf(port_str, sizeof(port_str), "udp:%d", port);
+	tmp = os_strdup(port_str);
+	if (tmp) {
+		os_free(hapd->conf->ctrl_interface);
+		hapd->conf->ctrl_interface = tmp;
+	}
+	wpa_printf(MSG_DEBUG, "ctrl_iface_init UDP port: %d", port);
+
+	if (eloop_register_read_sock(hapd->ctrl_sock,
+				     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 (hapd->ctrl_sock >= 0)
+		close(hapd->ctrl_sock);
+	return -1;
+#else /* CONFIG_CTRL_IFACE_UDP */
 	struct sockaddr_un addr;
 	int s = -1;
 	char *fname = NULL;
@@ -2153,6 +2548,8 @@
 		return 0;
 	}
 
+	dl_list_init(&hapd->ctrl_dst);
+
 	if (hapd->conf->ctrl_interface == NULL)
 		return 0;
 
@@ -2292,6 +2689,7 @@
 		os_free(fname);
 	}
 	return -1;
+#endif /* CONFIG_CTRL_IFACE_UDP */
 }
 
 
@@ -2300,10 +2698,14 @@
 	struct wpa_ctrl_dst *dst, *prev;
 
 	if (hapd->ctrl_sock > -1) {
+#ifndef CONFIG_CTRL_IFACE_UDP
 		char *fname;
+#endif /* !CONFIG_CTRL_IFACE_UDP */
+
 		eloop_unregister_read_sock(hapd->ctrl_sock);
 		close(hapd->ctrl_sock);
 		hapd->ctrl_sock = -1;
+#ifndef CONFIG_CTRL_IFACE_UDP
 		fname = hostapd_ctrl_iface_path(hapd);
 		if (fname)
 			unlink(fname);
@@ -2322,15 +2724,12 @@
 					   strerror(errno));
 			}
 		}
+#endif /* !CONFIG_CTRL_IFACE_UDP */
 	}
 
-	dst = hapd->ctrl_dst;
-	hapd->ctrl_dst = NULL;
-	while (dst) {
-		prev = dst;
-		dst = dst->next;
-		os_free(prev);
-	}
+	dl_list_for_each_safe(dst, prev, &hapd->ctrl_dst, struct wpa_ctrl_dst,
+			      list)
+		os_free(dst);
 
 #ifdef CONFIG_TESTING_OPTIONS
 	l2_packet_deinit(hapd->l2_test);
@@ -2362,54 +2761,18 @@
 
 
 static int hostapd_global_ctrl_iface_attach(struct hapd_interfaces *interfaces,
-					    struct sockaddr_un *from,
+					    struct sockaddr_storage *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;
+	return ctrl_iface_attach(&interfaces->global_ctrl_dst, from, fromlen);
 }
 
 
 static int hostapd_global_ctrl_iface_detach(struct hapd_interfaces *interfaces,
-					    struct sockaddr_un *from,
+					    struct sockaddr_storage *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;
+	return ctrl_iface_detach(&interfaces->global_ctrl_dst, from, fromlen);
 }
 
 
@@ -2423,19 +2786,275 @@
 }
 
 
+#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_interfaces(struct hapd_interfaces *interfaces,
+				     const char *input,
+				     char *reply, int reply_size)
+{
+	size_t i, j;
+	int res;
+	char *pos, *end;
+	struct hostapd_iface *iface;
+	int show_ctrl = 0;
+
+	if (input)
+		show_ctrl = !!os_strstr(input, "ctrl");
+
+	pos = reply;
+	end = reply + reply_size;
+
+	for (i = 0; i < interfaces->count; i++) {
+		iface = interfaces->iface[i];
+
+		for (j = 0; j < iface->num_bss; j++) {
+			struct hostapd_bss_config *conf;
+
+			conf = iface->conf->bss[j];
+			if (show_ctrl)
+				res = os_snprintf(pos, end - pos,
+						  "%s ctrl_iface=%s\n",
+						  conf->iface,
+						  conf->ctrl_interface ?
+						  conf->ctrl_interface : "N/A");
+			else
+				res = os_snprintf(pos, end - pos, "%s\n",
+						  conf->iface);
+			if (os_snprintf_error(end - pos, res)) {
+				*pos = '\0';
+				return pos - reply;
+			}
+			pos += res;
+		}
+	}
+
+	return pos - reply;
+}
+
+
+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_storage *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];
+	char buffer[256], *buf = buffer;
 	int res;
-	struct sockaddr_un from;
+	struct sockaddr_storage from;
 	socklen_t fromlen = sizeof(from);
 	char *reply;
 	int reply_len;
 	const int reply_size = 4096;
+#ifdef CONFIG_CTRL_IFACE_UDP
+	unsigned char lcookie[COOKIE_LEN];
+#endif /* CONFIG_CTRL_IFACE_UDP */
 
-	res = recvfrom(sock, buf, sizeof(buf) - 1, 0,
+	res = recvfrom(sock, buffer, sizeof(buffer) - 1, 0,
 		       (struct sockaddr *) &from, &fromlen);
 	if (res < 0) {
 		wpa_printf(MSG_ERROR, "recvfrom(ctrl_iface): %s",
@@ -2458,6 +3077,47 @@
 	os_memcpy(reply, "OK\n", 3);
 	reply_len = 3;
 
+#ifdef CONFIG_CTRL_IFACE_UDP
+	if (os_strcmp(buf, "GET_COOKIE") == 0) {
+		os_memcpy(reply, "COOKIE=", 7);
+		wpa_snprintf_hex(reply + 7, 2 * COOKIE_LEN + 1,
+				 gcookie, COOKIE_LEN);
+		reply_len = 7 + 2 * COOKIE_LEN;
+		goto send_reply;
+	}
+
+	if (os_strncmp(buf, "COOKIE=", 7) != 0 ||
+	    hexstr2bin(buf + 7, lcookie, COOKIE_LEN) < 0) {
+		wpa_printf(MSG_DEBUG,
+			   "CTRL: No cookie in the request - drop request");
+		os_free(reply);
+		return;
+	}
+
+	if (os_memcmp(gcookie, lcookie, COOKIE_LEN) != 0) {
+		wpa_printf(MSG_DEBUG,
+			   "CTRL: Invalid cookie in the request - drop request");
+		os_free(reply);
+		return;
+	}
+
+	buf += 7 + 2 * COOKIE_LEN;
+	while (*buf == ' ')
+		buf++;
+#endif /* CONFIG_CTRL_IFACE_UDP */
+
+	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;
@@ -2486,12 +3146,38 @@
 		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 if (os_strncmp(buf, "INTERFACES", 10) == 0) {
+		reply_len = hostapd_global_ctrl_iface_interfaces(
+			interfaces, buf + 10, reply, sizeof(buffer));
+	} else if (os_strcmp(buf, "TERMINATE") == 0) {
+		eloop_terminate();
 	} 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;
@@ -2506,6 +3192,7 @@
 }
 
 
+#ifndef CONFIG_CTRL_IFACE_UDP
 static char * hostapd_global_ctrl_iface_path(struct hapd_interfaces *interface)
 {
 	char *buf;
@@ -2525,10 +3212,95 @@
 	buf[len - 1] = '\0';
 	return buf;
 }
+#endif /* CONFIG_CTRL_IFACE_UDP */
 
 
 int hostapd_global_ctrl_iface_init(struct hapd_interfaces *interface)
 {
+#ifdef CONFIG_CTRL_IFACE_UDP
+	int port = HOSTAPD_GLOBAL_CTRL_IFACE_PORT;
+	char p[32] = { 0 };
+	char *pos;
+	struct addrinfo hints = { 0 }, *res, *saveres;
+	int n;
+
+	if (interface->global_ctrl_sock > -1) {
+		wpa_printf(MSG_DEBUG, "ctrl_iface already exists!");
+		return 0;
+	}
+
+	if (interface->global_iface_path == NULL)
+		return 0;
+
+	pos = os_strstr(interface->global_iface_path, "udp:");
+	if (pos) {
+		pos += 4;
+		port = atoi(pos);
+		if (port <= 0) {
+			wpa_printf(MSG_ERROR, "Invalid global ctrl UDP port");
+			goto fail;
+		}
+	}
+
+	dl_list_init(&interface->global_ctrl_dst);
+	interface->global_ctrl_sock = -1;
+	os_get_random(gcookie, COOKIE_LEN);
+
+#ifdef CONFIG_CTRL_IFACE_UDP_REMOTE
+	hints.ai_flags = AI_PASSIVE;
+#endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */
+
+#ifdef CONFIG_CTRL_IFACE_UDP_IPV6
+	hints.ai_family = AF_INET6;
+#else /* CONFIG_CTRL_IFACE_UDP_IPV6 */
+	hints.ai_family = AF_INET;
+#endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */
+	hints.ai_socktype = SOCK_DGRAM;
+
+try_again:
+	os_snprintf(p, sizeof(p), "%d", port);
+	n = getaddrinfo(NULL, p, &hints, &res);
+	if (n) {
+		wpa_printf(MSG_ERROR, "getaddrinfo(): %s", gai_strerror(n));
+		goto fail;
+	}
+
+	saveres = res;
+	interface->global_ctrl_sock = socket(res->ai_family, res->ai_socktype,
+					     res->ai_protocol);
+	if (interface->global_ctrl_sock < 0) {
+		wpa_printf(MSG_ERROR, "socket(PF_INET): %s", strerror(errno));
+		goto fail;
+	}
+
+	if (bind(interface->global_ctrl_sock, res->ai_addr, res->ai_addrlen) <
+	    0) {
+		port++;
+		if ((port - HOSTAPD_GLOBAL_CTRL_IFACE_PORT) <
+		    HOSTAPD_GLOBAL_CTRL_IFACE_PORT_LIMIT && !pos)
+			goto try_again;
+		wpa_printf(MSG_ERROR, "bind(AF_INET): %s", strerror(errno));
+		goto fail;
+	}
+
+	freeaddrinfo(saveres);
+
+	wpa_printf(MSG_DEBUG, "global ctrl_iface_init UDP port: %d", port);
+
+	if (eloop_register_read_sock(interface->global_ctrl_sock,
+				     hostapd_global_ctrl_iface_receive,
+				     interface, NULL) < 0) {
+		hostapd_global_ctrl_iface_deinit(interface);
+		return -1;
+	}
+
+	return 0;
+
+fail:
+	if (interface->global_ctrl_sock >= 0)
+		close(interface->global_ctrl_sock);
+	return -1;
+#else /* CONFIG_CTRL_IFACE_UDP */
 	struct sockaddr_un addr;
 	int s = -1;
 	char *fname = NULL;
@@ -2634,18 +3406,22 @@
 		os_free(fname);
 	}
 	return -1;
+#endif /* CONFIG_CTRL_IFACE_UDP */
 }
 
 
 void hostapd_global_ctrl_iface_deinit(struct hapd_interfaces *interfaces)
 {
+#ifndef CONFIG_CTRL_IFACE_UDP
 	char *fname = NULL;
+#endif /* CONFIG_CTRL_IFACE_UDP */
 	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;
+#ifndef CONFIG_CTRL_IFACE_UDP
 		fname = hostapd_global_ctrl_iface_path(interfaces);
 		if (fname) {
 			unlink(fname);
@@ -2665,18 +3441,15 @@
 					   strerror(errno));
 			}
 		}
+#endif /* CONFIG_CTRL_IFACE_UDP */
 	}
 
 	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);
-	}
+	dl_list_for_each_safe(dst, prev, &interfaces->global_ctrl_dst,
+			      struct wpa_ctrl_dst, list)
+		os_free(dst);
 }
 
 
@@ -2685,6 +3458,7 @@
 				    const char *buf, size_t len)
 {
 	struct wpa_ctrl_dst *dst, *next;
+	struct dl_list *ctrl_dst;
 	struct msghdr msg;
 	int idx;
 	struct iovec io[2];
@@ -2693,13 +3467,13 @@
 
 	if (type != WPA_MSG_ONLY_GLOBAL) {
 		s = hapd->ctrl_sock;
-		dst = hapd->ctrl_dst;
+		ctrl_dst = &hapd->ctrl_dst;
 	} else {
 		s = hapd->iface->interfaces->global_ctrl_sock;
-		dst = hapd->iface->interfaces->global_ctrl_dst;
+		ctrl_dst = &hapd->iface->interfaces->global_ctrl_dst;
 	}
 
-	if (s < 0 || dst == NULL)
+	if (s < 0 || dl_list_empty(ctrl_dst))
 		return;
 
 	os_snprintf(levelstr, sizeof(levelstr), "<%d>", level);
@@ -2712,12 +3486,10 @@
 	msg.msg_iovlen = 2;
 
 	idx = 0;
-	while (dst) {
-		next = dst->next;
+	dl_list_for_each_safe(dst, next, ctrl_dst, struct wpa_ctrl_dst, list) {
 		if (level >= dst->debug_level) {
-			wpa_hexdump(MSG_DEBUG, "CTRL_IFACE monitor send",
-				    (u8 *) dst->addr.sun_path, dst->addrlen -
-				    offsetof(struct sockaddr_un, sun_path));
+			sockaddr_print(MSG_DEBUG, "CTRL_IFACE monitor send",
+				       &dst->addr, dst->addrlen);
 			msg.msg_name = &dst->addr;
 			msg.msg_namelen = dst->addrlen;
 			if (sendmsg(s, &msg, 0) < 0) {
@@ -2741,7 +3513,6 @@
 				dst->errors = 0;
 		}
 		idx++;
-		dst = next;
 	}
 }
 
diff --git a/hostapd/defconfig b/hostapd/defconfig
index 4cde2b5..f7b60e0 100644
--- a/hostapd/defconfig
+++ b/hostapd/defconfig
@@ -18,6 +18,9 @@
 # Driver interface for drivers using the nl80211 kernel interface
 CONFIG_DRIVER_NL80211=y
 
+# QCA vendor extensions to nl80211
+#CONFIG_DRIVER_NL80211_QCA=y
+
 # driver_nl80211.c requires libnl. If you are compiling it yourself
 # you may need to point hostapd to your version of libnl.
 #
@@ -240,6 +243,15 @@
 # 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
+
+# Should we use kqueue instead of select? Select is used by default.
+#CONFIG_ELOOP_KQUEUE=y
+
 # Select TLS implementation
 # openssl = OpenSSL (default)
 # gnutls = GnuTLS
@@ -283,6 +295,12 @@
 # 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
@@ -314,3 +332,8 @@
 # http://wireless.kernel.org/en/users/Documentation/acs
 #
 #CONFIG_ACS=y
+
+# Multiband Operation support
+# These extentions facilitate efficient use of multiple frequency bands
+# available to the AP and the devices that may associate with it.
+#CONFIG_MBO=y
diff --git a/hostapd/hlr_auc_gw.c b/hostapd/hlr_auc_gw.c
index 8afe457..2117d34 100644
--- a/hostapd/hlr_auc_gw.c
+++ b/hostapd/hlr_auc_gw.c
@@ -1,6 +1,6 @@
 /*
  * HLR/AuC testing gateway for hostapd EAP-SIM/AKA database/authenticator
- * Copyright (c) 2005-2007, 2012-2013, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2005-2007, 2012-2016, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -284,7 +284,7 @@
 
 	f = fopen(fname, "r");
 	if (f == NULL) {
-		printf("Could not open GSM tripler data file '%s'\n", fname);
+		printf("Could not open GSM triplet data file '%s'\n", fname);
 		return -1;
 	}
 
@@ -312,66 +312,40 @@
 		}
 
 		/* 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);
+		pos2 = NULL;
+		pos = str_token(buf, ":", &pos2);
+		if (!pos || os_strlen(pos) >= sizeof(g->imsi)) {
+			printf("%s:%d - Invalid IMSI\n", fname, line);
 			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);
+		pos = str_token(buf, ":", &pos2);
+		if (!pos || os_strlen(pos) != 16 || hexstr2bin(pos, g->kc, 8)) {
+			printf("%s:%d - Invalid Kc\n", fname, line);
 			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);
+		pos = str_token(buf, ":", &pos2);
+		if (!pos || os_strlen(pos) != 8 ||
+		    hexstr2bin(pos, g->sres, 4)) {
+			printf("%s:%d - Invalid SRES\n", fname, line);
 			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);
+		pos = str_token(buf, ":", &pos2);
+		if (!pos || os_strlen(pos) != 32 ||
+		    hexstr2bin(pos, g->_rand, 16)) {
+			printf("%s:%d - Invalid RAND\n", fname, line);
 			ret = -1;
 			break;
 		}
-		pos = pos2 + 1;
 
 		g->next = gsm_db;
 		gsm_db = g;
@@ -450,86 +424,58 @@
 		}
 
 		/* 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);
+		pos2 = NULL;
+		pos = str_token(buf, " ", &pos2);
+		if (!pos || os_strlen(pos) >= sizeof(m->imsi)) {
+			printf("%s:%d - Invalid IMSI\n", fname, line);
 			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);
+		pos = str_token(buf, " ", &pos2);
+		if (!pos || os_strlen(pos) != 32 ||
+		    hexstr2bin(pos, m->ki, 16)) {
+			printf("%s:%d - Invalid Ki\n", fname, line);
 			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);
+		pos = str_token(buf, " ", &pos2);
+		if (!pos || os_strlen(pos) != 32 ||
+		    hexstr2bin(pos, m->opc, 16)) {
+			printf("%s:%d - Invalid OPc\n", fname, line);
 			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);
+		pos = str_token(buf, " ", &pos2);
+		if (!pos || os_strlen(pos) != 4 || hexstr2bin(pos, m->amf, 2)) {
+			printf("%s:%d - Invalid AMF\n", fname, line);
 			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);
+		pos = str_token(buf, " ", &pos2);
+		if (!pos || os_strlen(pos) != 12 ||
+		    hexstr2bin(pos, m->sqn, 6)) {
+			printf("%s:%d - Invalid SEQ\n", fname, line);
 			ret = -1;
 			break;
 		}
 
-		if (pos2) {
-			pos = pos2 + 1;
+		pos = str_token(buf, " ", &pos2);
+		if (pos) {
 			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);
+				printf("%s:%d - Invalid RES_len\n",
+				       fname, line);
 				ret = -1;
 				break;
 			}
@@ -550,7 +496,7 @@
 static void update_milenage_file(const char *fname)
 {
 	FILE *f, *f2;
-	char buf[500], *pos;
+	char name[500], buf[500], *pos;
 	char *end = buf + sizeof(buf);
 	struct milenage_parameters *m;
 	size_t imsi_len;
@@ -561,10 +507,10 @@
 		return;
 	}
 
-	snprintf(buf, sizeof(buf), "%s.new", fname);
-	f2 = fopen(buf, "w");
+	snprintf(name, sizeof(name), "%s.new", fname);
+	f2 = fopen(name, "w");
 	if (f2 == NULL) {
-		printf("Could not write Milenage data file '%s'\n", buf);
+		printf("Could not write Milenage data file '%s'\n", name);
 		fclose(f);
 		return;
 	}
@@ -606,14 +552,14 @@
 	fclose(f2);
 	fclose(f);
 
-	snprintf(buf, sizeof(buf), "%s.bak", fname);
-	if (rename(fname, buf) < 0) {
+	snprintf(name, sizeof(name), "%s.bak", fname);
+	if (rename(fname, name) < 0) {
 		perror("rename");
 		return;
 	}
 
-	snprintf(buf, sizeof(buf), "%s.new", fname);
-	if (rename(buf, fname) < 0) {
+	snprintf(name, sizeof(name), "%s.new", fname);
+	if (rename(name, fname) < 0) {
 		perror("rename");
 		return;
 	}
@@ -1027,7 +973,7 @@
 {
 	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"
+	       "Copyright (c) 2005-2016, Jouni Malinen <j@w1.fi>\n"
 	       "\n"
 	       "usage:\n"
 	       "hlr_auc_gw [-hu] [-s<socket path>] [-g<triplet file>] "
diff --git a/hostapd/hostapd.conf b/hostapd/hostapd.conf
index 00fc142..d943a43 100644
--- a/hostapd/hostapd.conf
+++ b/hostapd/hostapd.conf
@@ -125,11 +125,13 @@
 # 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.
+# Operation mode (a = IEEE 802.11a (5 GHz), b = IEEE 802.11b (2.4 GHz),
+# g = IEEE 802.11g (2.4 GHz), ad = IEEE 802.11ad (60 GHz); a/g options are used
+# with IEEE 802.11n (HT), too, to specify band). For IEEE 802.11ac (VHT), this
+# needs to be set to hw_mode=a. When using ACS (see channel parameter), a
+# 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
 
@@ -173,7 +175,7 @@
 # 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
+# channels can be specified by space (' ') separated values
 # Default: all channels allowed in selected hw_mode
 #chanlist=100 104 108 112 116
 #chanlist=1 6 11-13
@@ -192,16 +194,16 @@
 # (default: 2007)
 max_num_sta=255
 
-# RTS/CTS threshold; 2347 = disabled (default); range 0..2347
+# RTS/CTS threshold; -1 = disabled (default); range -1..65535
 # 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
+rts_threshold=-1
 
-# Fragmentation threshold; 2346 = disabled (default); range 256..2346
+# Fragmentation threshold; -1 = disabled (default); range -1, 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
+fragm_threshold=-1
 
 # Rate configuration
 # Default is to enable all rates supported by the hardware. This configuration
@@ -267,7 +269,14 @@
 #     requests for broadcast SSID
 ignore_broadcast_ssid=0
 
-# Additional vendor specfic elements for Beacon and Probe Response frames
+# Do not reply to broadcast Probe Request frames from unassociated STA if there
+# is no room for additional stations (max_num_sta). This can be used to
+# discourage a STA from trying to associate with this AP if the association
+# would be rejected due to maximum STA limit.
+# Default: 0 (disabled)
+#no_probe_resp_if_max_sta=0
+
+# 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
@@ -470,6 +479,7 @@
 # 0 = disabled (default)
 # 1 = enabled
 # Note: You will also need to enable WMM for full HT functionality.
+# Note: hw_mode=g (2.4 GHz) and hw_mode=a (5 GHz) is used to specify the band.
 #ieee80211n=1
 
 # ht_capab: HT capabilities (list of flags)
@@ -523,6 +533,7 @@
 # 0 = disabled (default)
 # 1 = enabled
 # Note: You will also need to enable WMM for full VHT functionality.
+# Note: hw_mode=a is used to specify that 5 GHz band is used with VHT.
 #ieee80211ac=1
 
 # vht_capab: VHT capabilities (list of flags)
@@ -582,14 +593,16 @@
 # 0 = Not supported (default)
 # 1 = Supported
 #
-# Compressed Steering Number of Beamformer Antennas Supported: [BF-ANTENNA-2]
+# 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]
+# 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
@@ -603,9 +616,9 @@
 # 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
+# 0 = VHT AP doesn't 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
+# 1 = VHT AP supports VHT TXOP PS mode (OR) VHT STA is in VHT TXOP power save
 #  mode
 #
 # +HTC-VHT Capable: [HTC-VHT]
@@ -766,6 +779,12 @@
 # 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.
@@ -780,6 +799,11 @@
 #	-respout /tmp/ocsp-cache.der
 #ocsp_stapling_response=/tmp/ocsp-cache.der
 
+# Cached OCSP stapling response list (DER encoded OCSPResponseList)
+# This is similar to ocsp_stapling_response, but the extended version defined in
+# RFC 6961 to allow multiple OCSP responses to be provided.
+#ocsp_stapling_response_multi=/tmp/ocsp-multi-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
@@ -817,6 +841,11 @@
 #eap_sim_db=unix:/tmp/hlr_auc_gw.sock
 #eap_sim_db=unix:/tmp/hlr_auc_gw.sock db=/tmp/hostapd.db
 
+# EAP-SIM DB request timeout
+# This parameter sets the maximum time to wait for a database request response.
+# The parameter value is in seconds.
+#eap_sim_db_timeout=1
+
 # 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:
@@ -880,11 +909,23 @@
 # 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 string for RADIUS messages. When used, this should be unique
+# to the NAS within the scope of the RADIUS server. Please note that hostapd
+# uses a separate RADIUS client for each BSS and as such, a unique
+# nas_identifier value should be configured separately for each BSS. This is
+# particularly important for cases where RADIUS accounting is used
+# (Accounting-On/Off messages are interpreted as clearing all ongoing sessions
+# and that may get interpreted as applying to all BSSes if the same
+# NAS-Identifier value is used.) For example, a fully qualified domain name
+# prefixed with a unique identifier of the BSS (e.g., BSSID) can be used here.
+#
 # When using IEEE 802.11r, nas_identifier must be set and must be between 1 and
 # 48 octets long.
+#
+# It is mandatory to configure either own_ip_addr or nas_identifier to be
+# compliant with the RADIUS protocol. When using RADIUS accounting, it is
+# strongly recommended that nas_identifier is set to a unique value for each
+# BSS.
 #nas_identifier=ap.example.com
 
 # RADIUS client forced local IP address for the access point
@@ -949,6 +990,17 @@
 # 2 = required; reject authentication if RADIUS server does not include VLAN ID
 #dynamic_vlan=0
 
+# Per-Station AP_VLAN interface mode
+# If enabled, each station is assigned its own AP_VLAN interface.
+# This implies per-station group keying and ebtables filtering of inter-STA
+# traffic (when passed through the AP).
+# If the sta is not assigned to any VLAN, then its AP_VLAN interface will be
+# added to the bridge given by the "bridge" configuration option (see above).
+# Otherwise, it will be added to the per-VLAN bridge.
+# 0 = disabled (default)
+# 1 = enabled
+#per_sta_vif=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
@@ -1220,6 +1272,7 @@
 
 # PMK-R1 Key Holder identifier (dot11FTR1KeyHolderID)
 # 6-octet identifier as a hex string.
+# Defaults to BSSID.
 #r1_key_holder=000102030405
 
 # Reassociation deadline in time units (TUs / 1.024 ms; range 1000..65535)
@@ -1249,6 +1302,11 @@
 # 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
@@ -1266,6 +1324,43 @@
 # 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) #############################################
 
@@ -1634,6 +1729,17 @@
 # username/password
 #nai_realm=0,example.org,13[5:6],21[2:4][5:7]
 
+# Arbitrary ANQP-element configuration
+# Additional ANQP-elements with arbitrary values can be defined by specifying
+# their contents in raw format as a hexdump of the payload. Note that these
+# values will override ANQP-element contents that may have been specified in the
+# more higher layer configuration parameters listed above.
+# format: anqp_elem=<InfoID>:<hexdump of payload>
+# For example, AP Geospatial Location ANQP-element with unknown location:
+#anqp_elem=265:0000
+# For example, AP Civic Location ANQP-element with unknown location:
+#anqp_elem=266:000000
+
 # QoS Map Set configuration
 #
 # Comma delimited QoS Map Set in decimal values
@@ -1747,6 +1853,32 @@
 #
 #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
@@ -1768,6 +1900,10 @@
 #
 # Corrupt Key MIC in GTK rekey EAPOL-Key frames with the given probability
 #corrupt_gtk_rekey_mic_probability=0.0
+#
+# Include only ECSA IE without CSA IE where possible
+# (channel switch operating class is needed)
+#ecsa_ie_only=0
 
 ##### Multiple BSSID support ##################################################
 #
@@ -1790,6 +1926,10 @@
 # - is not the same as the MAC address of the radio
 # - is not the same as any other explicitly specified BSSID
 #
+# Alternatively, the 'use_driver_iface_addr' parameter can be used to request
+# hostapd to use the driver auto-generated interface address (e.g., to use the
+# exact MAC addresses allocated to the device).
+#
 # 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"
diff --git a/hostapd/hostapd_cli.c b/hostapd/hostapd_cli.c
index e299183..bf86d37 100644
--- a/hostapd/hostapd_cli.c
+++ b/hostapd/hostapd_cli.c
@@ -1,6 +1,6 @@
 /*
  * hostapd - command line interface for hostapd daemon
- * Copyright (c) 2004-2015, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2016, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -16,10 +16,11 @@
 #include "utils/edit.h"
 #include "common/version.h"
 
+#ifndef CONFIG_NO_CTRL_IFACE
 
 static const char *const hostapd_cli_version =
 "hostapd_cli v" VERSION_STR "\n"
-"Copyright (c) 2004-2015, Jouni Malinen <j@w1.fi> and contributors";
+"Copyright (c) 2004-2016, Jouni Malinen <j@w1.fi> and contributors";
 
 
 static const char *const hostapd_cli_license =
@@ -97,6 +98,7 @@
 #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;
@@ -112,13 +114,15 @@
 		"\n"
 		"usage: hostapd_cli [-p<path>] [-i<ifname>] [-hvB] "
 		"[-a<path>] \\\n"
-		"                   [-G<ping interval>] [command..]\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"
@@ -133,21 +137,35 @@
 
 static struct wpa_ctrl * hostapd_cli_open_connection(const char *ifname)
 {
+#ifndef CONFIG_CTRL_IFACE_UDP
 	char *cfile;
 	int flen;
+#endif /* !CONFIG_CTRL_IFACE_UDP */
 
 	if (ifname == NULL)
 		return NULL;
 
+#ifdef CONFIG_CTRL_IFACE_UDP
+	ctrl_conn = wpa_ctrl_open(ifname);
+	return ctrl_conn;
+#else /* CONFIG_CTRL_IFACE_UDP */
 	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);
 
-	ctrl_conn = wpa_ctrl_open(cfile);
+	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;
+#endif /* CONFIG_CTRL_IFACE_UDP */
 }
 
 
@@ -205,6 +223,52 @@
 }
 
 
+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 hostapd_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 hostapd_cli_cmd_ping(struct wpa_ctrl *ctrl, int argc, char *argv[])
 {
 	return wpa_ctrl_command(ctrl, "PING");
@@ -922,6 +986,35 @@
 }
 
 
+#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[])
 {
@@ -1010,6 +1103,46 @@
 }
 
 
+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);
+}
+
+
+static int hostapd_cli_cmd_raw(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+	if (argc == 0)
+		return -1;
+	return hostapd_cli_cmd(ctrl, argv[0], 0, argc - 1, &argv[1]);
+}
+
+
+static int hostapd_cli_cmd_pmksa(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+	return wpa_ctrl_command(ctrl, "PMKSA");
+}
+
+
+static int hostapd_cli_cmd_pmksa_flush(struct wpa_ctrl *ctrl, int argc,
+				       char *argv[])
+{
+	return wpa_ctrl_command(ctrl, "PMKSA_FLUSH");
+}
+
+
 struct hostapd_cli_cmd {
 	const char *cmd;
 	int (*handler)(struct wpa_ctrl *ctrl, int argc, char *argv[]);
@@ -1049,6 +1182,10 @@
 	{ "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 */
+	{ "raw", hostapd_cli_cmd_raw },
 	{ "level", hostapd_cli_cmd_level },
 	{ "license", hostapd_cli_cmd_license },
 	{ "quit", hostapd_cli_cmd_quit },
@@ -1064,6 +1201,9 @@
 	{ "reload", hostapd_cli_cmd_reload },
 	{ "disable", hostapd_cli_cmd_disable },
 	{ "erp_flush", hostapd_cli_cmd_erp_flush },
+	{ "log_level", hostapd_cli_cmd_log_level },
+	{ "pmksa", hostapd_cli_cmd_pmksa },
+	{ "pmksa_flush", hostapd_cli_cmd_pmksa_flush },
 	{ NULL, NULL }
 };
 
@@ -1285,7 +1425,7 @@
 		return -1;
 
 	for (;;) {
-		c = getopt(argc, argv, "a:BhG:i:p:v");
+		c = getopt(argc, argv, "a:BhG:i:p:P:s:v");
 		if (c < 0)
 			break;
 		switch (c) {
@@ -1311,6 +1451,12 @@
 		case 'p':
 			ctrl_iface_dir = optarg;
 			break;
+		case 'P':
+			pid_file = optarg;
+			break;
+		case 's':
+			client_socket_dir = optarg;
+			break;
 		default:
 			usage();
 			return -1;
@@ -1376,7 +1522,7 @@
 		}
 	}
 
-	if (daemonize && os_daemonize(pid_file))
+	if (daemonize && os_daemonize(pid_file) && eloop_sock_requeue())
 		return -1;
 
 	if (interactive)
@@ -1391,3 +1537,12 @@
 	hostapd_cli_cleanup();
 	return 0;
 }
+
+#else /* CONFIG_NO_CTRL_IFACE */
+
+int main(int argc, char *argv[])
+{
+	return -1;
+}
+
+#endif /* CONFIG_NO_CTRL_IFACE */
diff --git a/hostapd/main.c b/hostapd/main.c
index 62d0775..1d9e63e 100644
--- a/hostapd/main.c
+++ b/hostapd/main.c
@@ -1,6 +1,6 @@
 /*
  * hostapd / main()
- * Copyright (c) 2002-2015, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2002-2016, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -24,6 +24,7 @@
 #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"
@@ -170,7 +171,8 @@
 
 		if (global.drv_priv[i] == NULL &&
 		    wpa_drivers[i]->global_init) {
-			global.drv_priv[i] = wpa_drivers[i]->global_init();
+			global.drv_priv[i] =
+				wpa_drivers[i]->global_init(iface->interfaces);
 			if (global.drv_priv[i] == NULL) {
 				wpa_printf(MSG_ERROR, "Failed to initialize "
 					   "driver '%s'",
@@ -407,9 +409,16 @@
 	}
 #endif /* EAP_SERVER_TNC */
 
-	if (daemonize && os_daemonize(pid_file)) {
-		wpa_printf(MSG_ERROR, "daemon: %s", strerror(errno));
-		return -1;
+	if (daemonize) {
+		if (os_daemonize(pid_file)) {
+			wpa_printf(MSG_ERROR, "daemon: %s", strerror(errno));
+			return -1;
+		}
+		if (eloop_sock_requeue()) {
+			wpa_printf(MSG_ERROR, "eloop_sock_requeue: %s",
+				   strerror(errno));
+			return -1;
+		}
 	}
 
 	eloop_run();
@@ -424,7 +433,7 @@
 		"hostapd v" VERSION_STR "\n"
 		"User space daemon for IEEE 802.11 AP management,\n"
 		"IEEE 802.1X/WPA/WPA2/EAP/RADIUS Authenticator\n"
-		"Copyright (c) 2002-2015, Jouni Malinen <j@w1.fi> "
+		"Copyright (c) 2002-2016, Jouni Malinen <j@w1.fi> "
 		"and contributors\n");
 }
 
@@ -455,6 +464,7 @@
 		"   -T = record to Linux tracing in addition to logging\n"
 		"        (records all messages regardless of debug verbosity)\n"
 #endif /* CONFIG_DEBUG_LINUX_TRACING */
+		"   -S   start all the interfaces synchronously\n"
 		"   -t   include timestamps in some debug messages\n"
 		"   -v   show hostapd version\n");
 
@@ -465,9 +475,8 @@
 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;
+	if (hapd && hapd->conf)
+		return hapd->conf->iface;
 	return NULL;
 }
 
@@ -475,11 +484,16 @@
 static int hostapd_get_global_ctrl_iface(struct hapd_interfaces *interfaces,
 					 const char *path)
 {
+#ifndef CONFIG_CTRL_IFACE_UDP
 	char *pos;
+#endif /* !CONFIG_CTRL_IFACE_UDP */
+
 	os_free(interfaces->global_iface_path);
 	interfaces->global_iface_path = os_strdup(path);
 	if (interfaces->global_iface_path == NULL)
 		return -1;
+
+#ifndef CONFIG_CTRL_IFACE_UDP
 	pos = os_strrchr(interfaces->global_iface_path, '/');
 	if (pos == NULL) {
 		wpa_printf(MSG_ERROR, "No '/' in the global control interface "
@@ -491,6 +505,7 @@
 
 	*pos = '\0';
 	interfaces->global_iface_name = pos + 1;
+#endif /* !CONFIG_CTRL_IFACE_UDP */
 
 	return 0;
 }
@@ -533,6 +548,28 @@
 #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;
@@ -547,6 +584,7 @@
 #ifdef CONFIG_DEBUG_LINUX_TRACING
 	int enable_trace_dbg = 0;
 #endif /* CONFIG_DEBUG_LINUX_TRACING */
+	int start_ifaces_in_sync = 0;
 
 	if (os_program_init())
 		return -1;
@@ -561,10 +599,10 @@
 	interfaces.global_iface_path = NULL;
 	interfaces.global_iface_name = NULL;
 	interfaces.global_ctrl_sock = -1;
-	interfaces.global_ctrl_dst = NULL;
+	dl_list_init(&interfaces.global_ctrl_dst);
 
 	for (;;) {
-		c = getopt(argc, argv, "b:Bde:f:hKP:Ttu:vg:G:");
+		c = getopt(argc, argv, "b:Bde:f:hKP:STtu:vg:G:");
 		if (c < 0)
 			break;
 		switch (c) {
@@ -621,6 +659,9 @@
 			bss_config = tmp_bss;
 			bss_config[num_bss_configs++] = optarg;
 			break;
+		case 'S':
+			start_ifaces_in_sync = 1;
+			break;
 #ifdef CONFIG_WPS
 		case 'u':
 			return gen_uuid(optarg);
@@ -666,6 +707,20 @@
 		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,
@@ -675,6 +730,8 @@
 			wpa_printf(MSG_ERROR, "Failed to initialize interface");
 			goto out;
 		}
+		if (start_ifaces_in_sync)
+			interfaces.iface[i]->need_to_start_in_sync = 1;
 	}
 
 	/* Allocate and parse configuration for per-BSS files */
@@ -750,6 +807,7 @@
 	}
 	os_free(interfaces.iface);
 
+	eloop_cancel_timeout(hostapd_periodic, &interfaces, NULL);
 	hostapd_global_deinit(pid_file);
 	os_free(pid_file);
 
@@ -759,6 +817,8 @@
 
 	os_free(bss_config);
 
+	fst_global_deinit();
+
 	os_program_deinit();
 
 	return ret;
diff --git a/hs20/client/Android.mk b/hs20/client/Android.mk
index a71e86d..e4db322 100644
--- a/hs20/client/Android.mk
+++ b/hs20/client/Android.mk
@@ -54,6 +54,7 @@
 OBJS += ../../src/crypto/md5-internal.c
 OBJS += ../../src/crypto/sha1-internal.c
 OBJS += ../../src/crypto/sha256-internal.c
+OBJS += ../../src/crypto/tls_openssl_ocsp.c
 
 L_CFLAGS += -DEAP_TLS_OPENSSL
 
diff --git a/hs20/client/Makefile b/hs20/client/Makefile
index 94cd5f1..fc9b619 100644
--- a/hs20/client/Makefile
+++ b/hs20/client/Makefile
@@ -76,6 +76,7 @@
 endif
 
 CFLAGS += -DEAP_TLS_OPENSSL
+OBJS += ../../src/crypto/tls_openssl_ocsp.o
 LIBS += -lssl -lcrypto
 
 hs20-osu-client: $(OBJS)
diff --git a/hs20/client/est.c b/hs20/client/est.c
index ec05bc4..9f1519b 100644
--- a/hs20/client/est.c
+++ b/hs20/client/est.c
@@ -16,6 +16,9 @@
 #include <openssl/asn1t.h>
 #include <openssl/x509.h>
 #include <openssl/x509v3.h>
+#ifdef OPENSSL_IS_BORINGSSL
+#include <openssl/buf.h>
+#endif /* OPENSSL_IS_BORINGSSL */
 
 #include "common.h"
 #include "utils/base64.h"
@@ -27,12 +30,28 @@
 static int pkcs7_to_cert(struct hs20_osu_client *ctx, const u8 *pkcs7,
 			 size_t len, char *pem_file, char *der_file)
 {
+#ifdef OPENSSL_IS_BORINGSSL
+	CBS pkcs7_cbs;
+#else /* OPENSSL_IS_BORINGSSL */
 	PKCS7 *p7 = NULL;
 	const unsigned char *p = pkcs7;
+#endif /* OPENSSL_IS_BORINGSSL */
 	STACK_OF(X509) *certs;
 	int i, num, ret = -1;
 	BIO *out = NULL;
 
+#ifdef OPENSSL_IS_BORINGSSL
+	certs = sk_X509_new_null();
+	if (!certs)
+		goto fail;
+	CBS_init(&pkcs7_cbs, pkcs7, len);
+	if (!PKCS7_get_certificates(certs, &pkcs7_cbs)) {
+		wpa_printf(MSG_INFO, "Could not parse PKCS#7 object: %s",
+			   ERR_error_string(ERR_get_error(), NULL));
+		write_result(ctx, "Could not parse PKCS#7 object from EST");
+		goto fail;
+	}
+#else /* OPENSSL_IS_BORINGSSL */
 	p7 = d2i_PKCS7(NULL, &p, len);
 	if (p7 == NULL) {
 		wpa_printf(MSG_INFO, "Could not parse PKCS#7 object: %s",
@@ -52,6 +71,7 @@
 		certs = NULL;
 		break;
 	}
+#endif /* OPENSSL_IS_BORINGSSL */
 
 	if (!certs || ((num = sk_X509_num(certs)) == 0)) {
 		wpa_printf(MSG_INFO, "No certificates found in PKCS#7 object");
@@ -84,7 +104,12 @@
 	ret = 0;
 
 fail:
+#ifdef OPENSSL_IS_BORINGSSL
+	if (certs)
+		sk_X509_pop_free(certs, X509_free);
+#else /* OPENSSL_IS_BORINGSSL */
 	PKCS7_free(p7);
+#endif /* OPENSSL_IS_BORINGSSL */
 	if (out)
 		BIO_free_all(out);
 
@@ -310,6 +335,23 @@
 	if (!csrattrs || ! csrattrs->attrs)
 		return;
 
+#ifdef OPENSSL_IS_BORINGSSL
+	num = sk_num(CHECKED_CAST(_STACK *, STACK_OF(AttrOrOID) *,
+				  csrattrs->attrs));
+	for (i = 0; i < num; i++) {
+		AttrOrOID *ao = sk_value(
+			CHECKED_CAST(_STACK *, const STACK_OF(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;
+		}
+	}
+#else /* OPENSSL_IS_BORINGSSL */
 	num = SKM_sk_num(AttrOrOID, csrattrs->attrs);
 	for (i = 0; i < num; i++) {
 		AttrOrOID *ao = SKM_sk_value(AttrOrOID, csrattrs->attrs, i);
@@ -322,6 +364,7 @@
 			break;
 		}
 	}
+#endif /* OPENSSL_IS_BORINGSSL */
 }
 
 
@@ -340,6 +383,7 @@
 	STACK_OF(X509_EXTENSION) *exts = NULL;
 	X509_EXTENSION *ex;
 	BIO *out;
+	CONF *ctmp = NULL;
 
 	wpa_printf(MSG_INFO, "Generate RSA private key");
 	write_summary(ctx, "Generate RSA private key");
@@ -421,20 +465,20 @@
 	if (!exts)
 		goto fail;
 
-	ex = X509V3_EXT_conf_nid(NULL, NULL, NID_basic_constraints,
-				 "CA:FALSE");
+	ex = X509V3_EXT_nconf_nid(ctmp, NULL, NID_basic_constraints,
+				  "CA:FALSE");
 	if (ex == NULL ||
 	    !sk_X509_EXTENSION_push(exts, ex))
 		goto fail;
 
-	ex = X509V3_EXT_conf_nid(NULL, NULL, NID_key_usage,
-				 "nonRepudiation,digitalSignature,keyEncipherment");
+	ex = X509V3_EXT_nconf_nid(ctmp, NULL, NID_key_usage,
+				  "nonRepudiation,digitalSignature,keyEncipherment");
 	if (ex == NULL ||
 	    !sk_X509_EXTENSION_push(exts, ex))
 		goto fail;
 
-	ex = X509V3_EXT_conf_nid(NULL, NULL, NID_ext_key_usage,
-				 "1.3.6.1.4.1.40808.1.1.2");
+	ex = X509V3_EXT_nconf_nid(ctmp, NULL, NID_ext_key_usage,
+				  "1.3.6.1.4.1.40808.1.1.2");
 	if (ex == NULL ||
 	    !sk_X509_EXTENSION_push(exts, ex))
 		goto fail;
@@ -454,7 +498,9 @@
 		char *txt;
 		size_t rlen;
 
+#if !defined(ANDROID) || !defined(OPENSSL_IS_BORINGSSL)
 		X509_REQ_print(out, req);
+#endif
 		rlen = BIO_ctrl_pending(out);
 		txt = os_malloc(rlen + 1);
 		if (txt) {
@@ -473,7 +519,9 @@
 		FILE *f = fopen(csr_pem, "w");
 		if (f == NULL)
 			goto fail;
+#if !defined(ANDROID) || !defined(OPENSSL_IS_BORINGSSL)
 		X509_REQ_print_fp(f, req);
+#endif
 		if (!PEM_write_X509_REQ(f, req)) {
 			fclose(f);
 			goto fail;
diff --git a/hs20/client/osu_client.c b/hs20/client/osu_client.c
index 0315f7b..c05c57d 100644
--- a/hs20/client/osu_client.c
+++ b/hs20/client/osu_client.c
@@ -2229,7 +2229,7 @@
 		fprintf(f, "</table></a><br><small>BSSID: %s<br>\n"
 			"SSID: %s<br>\n",
 			last->bssid, last->osu_ssid);
-		if (last->osu_nai)
+		if (last->osu_nai[0])
 			fprintf(f, "NAI: %s<br>\n", last->osu_nai);
 		fprintf(f, "URL: %s<br>\n"
 			"methods:%s%s<br>\n"
@@ -2339,12 +2339,23 @@
 		return -1;
 
 	snprintf(fname, sizeof(fname), "%s/osu-info", dir);
-	if (mkdir(fname, S_IRWXU | S_IRWXG) < 0 && errno != EEXIST) {
+	if (mkdir(fname, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) < 0 &&
+	    errno != EEXIST) {
 		wpa_printf(MSG_INFO, "mkdir(%s) failed: %s",
 			   fname, strerror(errno));
 		return -1;
 	}
 
+#ifdef ANDROID
+	/* Allow processes running with Group ID as AID_WIFI
+	 * to read/write files from osu-info directory
+	 */
+	if (chown(fname, -1, AID_WIFI)) {
+		wpa_printf(MSG_INFO, "Could not chown osu-info directory: %s",
+			   strerror(errno));
+	}
+#endif /* ANDROID */
+
 	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");
@@ -2559,7 +2570,7 @@
 	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) {
+		if (address && os_strncmp(address, "fqdn=", 5) == 0) {
 			wpa_printf(MSG_INFO, "Use requested FQDN from command line");
 			os_snprintf(buf, sizeof(buf), "%s", address + 5);
 			address = NULL;
@@ -3122,20 +3133,12 @@
 			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);
+		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,
+		ret = cmd_pol_upd(&ctx,
+				  argc > optind + 1 ? argv[optind + 1] : NULL,
 				  argc > optind + 2 ? argv[optind + 2] : NULL,
 				  argc > optind + 3 ? argv[optind + 3] : NULL);
 	} else if (strcmp(argv[optind], "prov") == 0) {
diff --git a/src/Makefile b/src/Makefile
index 10e0171..c9e84c1 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -1,4 +1,5 @@
 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
diff --git a/src/ap/accounting.c b/src/ap/accounting.c
index a096de4..854174e 100644
--- a/src/ap/accounting.c
+++ b/src/ap/accounting.c
@@ -1,6 +1,6 @@
 /*
  * hostapd / RADIUS Accounting
- * Copyright (c) 2002-2009, 2012, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2002-2009, 2012-2015, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -41,6 +41,7 @@
 	size_t len;
 	int i;
 	struct wpabuf *b;
+	struct os_time now;
 
 	msg = radius_msg_new(RADIUS_CODE_ACCOUNTING_REQUEST,
 			     radius_client_get_id(hapd->radius));
@@ -49,25 +50,9 @@
 		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_make_authenticator(msg) < 0) {
+		wpa_printf(MSG_INFO, "Could not make Request Authenticator");
+		goto fail;
 	}
 
 	if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_STATUS_TYPE,
@@ -76,17 +61,18 @@
 		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) {
+		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;
+		}
+
 		/* Use 802.1X identity if available */
 		val = ieee802_1x_get_identity(sta->eapol_sm, &len);
 
@@ -147,6 +133,32 @@
 			wpa_printf(MSG_ERROR, "Could not add CUI from ACL");
 			goto fail;
 		}
+
+		if (sta->ipaddr &&
+		    !radius_msg_add_attr_int32(msg,
+					       RADIUS_ATTR_FRAMED_IP_ADDRESS,
+					       be_to_host32(sta->ipaddr))) {
+			wpa_printf(MSG_ERROR,
+				   "Could not add Framed-IP-Address");
+			goto fail;
+		}
+	}
+
+	os_get_time(&now);
+	if (now.sec > 1000000000 &&
+	    !radius_msg_add_attr_int32(msg, RADIUS_ATTR_EVENT_TIMESTAMP,
+				       now.sec)) {
+		wpa_printf(MSG_INFO, "Could not add Event-Timestamp");
+		goto fail;
+	}
+
+	/*
+	 * Add Acct-Delay-Time with zero value for the first transmission. This
+	 * will be updated within radius_client.c when retransmitting the frame.
+	 */
+	if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_DELAY_TIME, 0)) {
+		wpa_printf(MSG_INFO, "Could not add Acct-Delay-Time");
+		goto fail;
 	}
 
 	return msg;
@@ -164,19 +176,25 @@
 	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;
+	if (!data->bytes_64bit) {
+		/* Extend 32-bit counters from the driver to 64-bit counters */
+		if (sta->last_rx_bytes_lo > data->rx_bytes)
+			sta->last_rx_bytes_hi++;
+		sta->last_rx_bytes_lo = data->rx_bytes;
+
+		if (sta->last_tx_bytes_lo > data->tx_bytes)
+			sta->last_tx_bytes_hi++;
+		sta->last_tx_bytes_lo = 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);
+		       HOSTAPD_LEVEL_DEBUG,
+		       "updated TX/RX stats: rx_bytes=%llu [%u:%u] tx_bytes=%llu [%u:%u] bytes_64bit=%d",
+		       data->rx_bytes, sta->last_rx_bytes_hi,
+		       sta->last_rx_bytes_lo,
+		       data->tx_bytes, sta->last_tx_bytes_hi,
+		       sta->last_tx_bytes_lo,
+		       data->bytes_64bit);
 
 	return 0;
 }
@@ -217,12 +235,14 @@
 
 	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);
+		       "starting accounting session %016llX",
+		       (unsigned long long) sta->acct_session_id);
 
 	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;
+	sta->last_rx_bytes_hi = 0;
+	sta->last_rx_bytes_lo = 0;
+	sta->last_tx_bytes_hi = 0;
+	sta->last_tx_bytes_lo = 0;
 	hostapd_drv_sta_clear_stats(hapd, sta->addr);
 
 	if (!hapd->conf->radius->acct_server)
@@ -251,8 +271,7 @@
 	int cause = sta->acct_terminate_cause;
 	struct hostap_sta_driver_data data;
 	struct os_reltime now_r, diff;
-	struct os_time now;
-	u32 gigawords;
+	u64 bytes;
 
 	if (!hapd->conf->radius->acct_server)
 		return;
@@ -266,7 +285,6 @@
 	}
 
 	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)) {
@@ -287,48 +305,42 @@
 			wpa_printf(MSG_INFO, "Could not add Acct-Output-Packets");
 			goto fail;
 		}
+		if (data.bytes_64bit)
+			bytes = data.rx_bytes;
+		else
+			bytes = ((u64) sta->last_rx_bytes_hi << 32) |
+				sta->last_rx_bytes_lo;
 		if (!radius_msg_add_attr_int32(msg,
 					       RADIUS_ATTR_ACCT_INPUT_OCTETS,
-					       data.rx_bytes)) {
+					       (u32) 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)) {
+		if (!radius_msg_add_attr_int32(msg,
+					       RADIUS_ATTR_ACCT_INPUT_GIGAWORDS,
+					       (u32) (bytes >> 32))) {
 			wpa_printf(MSG_INFO, "Could not add Acct-Input-Gigawords");
 			goto fail;
 		}
+		if (data.bytes_64bit)
+			bytes = data.tx_bytes;
+		else
+			bytes = ((u64) sta->last_tx_bytes_hi << 32) |
+				sta->last_tx_bytes_lo;
 		if (!radius_msg_add_attr_int32(msg,
 					       RADIUS_ATTR_ACCT_OUTPUT_OCTETS,
-					       data.tx_bytes)) {
+					       (u32) 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)) {
+		if (!radius_msg_add_attr_int32(msg,
+					       RADIUS_ATTR_ACCT_OUTPUT_GIGAWORDS,
+					       (u32) (bytes >> 32))) {
 			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;
 
@@ -375,22 +387,17 @@
 		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);
+			       "stopped accounting session %016llX",
+			       (unsigned long long) sta->acct_session_id);
 		sta->acct_session_started = 0;
 	}
 }
 
 
-void accounting_sta_get_id(struct hostapd_data *hapd,
-				  struct sta_info *sta)
+int 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;
+	return radius_gen_session_id((u8 *) &sta->acct_session_id,
+				     sizeof(sta->acct_session_id));
 }
 
 
@@ -437,12 +444,14 @@
 	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 (hapd->acct_session_id) {
+		char buf[20];
+
+		os_snprintf(buf, sizeof(buf), "%016llX",
+			    (unsigned long long) hapd->acct_session_id);
+		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");
 	}
 
 	if (radius_client_send(hapd->radius, msg, RADIUS_ACCT, NULL) < 0)
@@ -450,6 +459,63 @@
 }
 
 
+static void accounting_interim_error_cb(const u8 *addr, void *ctx)
+{
+	struct hostapd_data *hapd = ctx;
+	struct sta_info *sta;
+	unsigned int i, wait_time;
+	int res;
+
+	sta = ap_get_sta(hapd, addr);
+	if (!sta)
+		return;
+	sta->acct_interim_errors++;
+	if (sta->acct_interim_errors > 10 /* RADIUS_CLIENT_MAX_RETRIES */) {
+		wpa_printf(MSG_DEBUG,
+			   "Interim RADIUS accounting update failed for " MACSTR
+			   " - too many errors, abandon this interim accounting update",
+			   MAC2STR(addr));
+		sta->acct_interim_errors = 0;
+		/* Next update will be tried after normal update interval */
+		return;
+	}
+
+	/*
+	 * Use a shorter update interval as an improved retransmission mechanism
+	 * for failed interim accounting updates. This allows the statistics to
+	 * be updated for each retransmission.
+	 *
+	 * RADIUS client code has already waited RADIUS_CLIENT_FIRST_WAIT.
+	 * Schedule the first retry attempt immediately and every following one
+	 * with exponential backoff.
+	 */
+	if (sta->acct_interim_errors == 1) {
+		wait_time = 0;
+	} else {
+		wait_time = 3; /* RADIUS_CLIENT_FIRST_WAIT */
+		for (i = 1; i < sta->acct_interim_errors; i++)
+			wait_time *= 2;
+	}
+	res = eloop_deplete_timeout(wait_time, 0, accounting_interim_update,
+				    hapd, sta);
+	if (res == 1)
+		wpa_printf(MSG_DEBUG,
+			   "Interim RADIUS accounting update failed for " MACSTR
+			   " (error count: %u) - schedule next update in %u seconds",
+			   MAC2STR(addr), sta->acct_interim_errors, wait_time);
+	else if (res == 0)
+		wpa_printf(MSG_DEBUG,
+			   "Interim RADIUS accounting update failed for " MACSTR
+			   " (error count: %u)", MAC2STR(addr),
+			   sta->acct_interim_errors);
+	else
+		wpa_printf(MSG_DEBUG,
+			   "Interim RADIUS accounting update failed for " MACSTR
+			   " (error count: %u) - no timer found", MAC2STR(addr),
+			   sta->acct_interim_errors);
+}
+
+
 /**
  * accounting_init: Initialize accounting
  * @hapd: hostapd BSS data
@@ -457,20 +523,15 @@
  */
 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_gen_session_id((u8 *) &hapd->acct_session_id,
+				  sizeof(hapd->acct_session_id)) < 0)
+		return -1;
 
 	if (radius_client_register(hapd->radius, RADIUS_ACCT,
 				   accounting_receive, hapd))
 		return -1;
+	radius_client_set_interim_error_cb(hapd->radius,
+					   accounting_interim_error_cb, hapd);
 
 	accounting_report_state(hapd, 1);
 
diff --git a/src/ap/accounting.h b/src/ap/accounting.h
index dcc54ee..de5a33f 100644
--- a/src/ap/accounting.h
+++ b/src/ap/accounting.h
@@ -10,9 +10,10 @@
 #define ACCOUNTING_H
 
 #ifdef CONFIG_NO_ACCOUNTING
-static inline void accounting_sta_get_id(struct hostapd_data *hapd,
-					 struct sta_info *sta)
+static inline int accounting_sta_get_id(struct hostapd_data *hapd,
+					struct sta_info *sta)
 {
+	return 0;
 }
 
 static inline void accounting_sta_start(struct hostapd_data *hapd,
@@ -34,7 +35,7 @@
 {
 }
 #else /* CONFIG_NO_ACCOUNTING */
-void accounting_sta_get_id(struct hostapd_data *hapd, struct sta_info *sta);
+int 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);
diff --git a/src/ap/acs.c b/src/ap/acs.c
index 03d797f..5e83805 100644
--- a/src/ap/acs.c
+++ b/src/ap/acs.c
@@ -599,8 +599,7 @@
 	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);
+		   80);
 
 	for (i = 0; i < iface->current_mode->num_channels; i++) {
 		double total_weight;
@@ -933,6 +932,9 @@
 		return HOSTAPD_CHAN_ACS;
 	}
 
+	if (!iface->current_mode)
+		return HOSTAPD_CHAN_INVALID;
+
 	acs_cleanup(iface);
 
 	err = acs_request_scan(iface);
diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c
index 455013a..66b843c 100644
--- a/src/ap/ap_config.c
+++ b/src/ap/ap_config.c
@@ -38,6 +38,8 @@
 
 void hostapd_config_defaults_bss(struct hostapd_bss_config *bss)
 {
+	dl_list_init(&bss->anqp_elem);
+
 	bss->logger_syslog_level = HOSTAPD_LEVEL_INFO;
 	bss->logger_stdout_level = HOSTAPD_LEVEL_INFO;
 	bss->logger_syslog = (unsigned int) -1;
@@ -63,6 +65,7 @@
 	bss->dtim_period = 2;
 
 	bss->radius_server_auth_port = 1812;
+	bss->eap_sim_db_timeout = 1;
 	bss->ap_max_inactivity = AP_MAX_INACTIVITY;
 	bss->eapol_version = EAPOL_VERSION;
 
@@ -172,6 +175,7 @@
 
 	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;
@@ -179,6 +183,7 @@
 	conf->ignore_assoc_probability = 0.0;
 	conf->ignore_reassoc_probability = 0.0;
 	conf->corrupt_gtk_rekey_mic_probability = 0.0;
+	conf->ecsa_ie_only = 0;
 #endif /* CONFIG_TESTING_OPTIONS */
 
 	conf->acs = 0;
@@ -197,13 +202,6 @@
 }
 
 
-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)
 {
@@ -409,6 +407,19 @@
 }
 
 
+static void hostapd_config_free_anqp_elem(struct hostapd_bss_config *conf)
+{
+	struct anqp_element *elem;
+
+	while ((elem = dl_list_first(&conf->anqp_elem, struct anqp_element,
+				     list))) {
+		dl_list_del(&elem->list);
+		wpabuf_free(elem->payload);
+		os_free(elem);
+	}
+}
+
+
 void hostapd_config_free_bss(struct hostapd_bss_config *conf)
 {
 	struct hostapd_eap_user *user, *prev_user;
@@ -453,6 +464,7 @@
 	os_free(conf->private_key);
 	os_free(conf->private_key_passwd);
 	os_free(conf->ocsp_stapling_response);
+	os_free(conf->ocsp_stapling_response_multi);
 	os_free(conf->dh_file);
 	os_free(conf->openssl_ciphers);
 	os_free(conf->pac_opaque_encr_key);
@@ -522,6 +534,7 @@
 	os_free(conf->network_auth_type);
 	os_free(conf->anqp_3gpp_cell_net);
 	os_free(conf->domain_name);
+	hostapd_config_free_anqp_elem(conf);
 
 #ifdef CONFIG_RADIUS_TEST
 	os_free(conf->dump_msk_file);
@@ -561,6 +574,13 @@
 
 	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);
 }
 
@@ -602,7 +622,7 @@
  * 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)
+			  const u8 *addr, struct vlan_description *vlan_id)
 {
 	int start, end, middle, res;
 
@@ -642,11 +662,26 @@
 }
 
 
-int hostapd_vlan_id_valid(struct hostapd_vlan *vlan, int vlan_id)
+int hostapd_vlan_valid(struct hostapd_vlan *vlan,
+		       struct vlan_description *vlan_desc)
 {
 	struct hostapd_vlan *v = vlan;
+	int i;
+
+	if (!vlan_desc->notempty || vlan_desc->untagged < 0 ||
+	    vlan_desc->untagged > MAX_VLAN_ID)
+		return 0;
+	for (i = 0; i < MAX_NUM_TAGGED_VLAN; i++) {
+		if (vlan_desc->tagged[i] < 0 ||
+		    vlan_desc->tagged[i] > MAX_VLAN_ID)
+			return 0;
+	}
+	if (!vlan_desc->untagged && !vlan_desc->tagged[0])
+		return 0;
+
 	while (v) {
-		if (v->vlan_id == vlan_id || v->vlan_id == VLAN_ID_WILDCARD)
+		if (!vlan_compare(&v->vlan_desc, vlan_desc) ||
+		    v->vlan_id == VLAN_ID_WILDCARD)
 			return 1;
 		v = v->next;
 	}
@@ -748,7 +783,7 @@
 		return -1;
 	}
 
-	if (full_config && hostapd_mac_comp_empty(bss->bssid) != 0) {
+	if (full_config && !is_zero_ether_addr(bss->bssid)) {
 		size_t i;
 
 		for (i = 0; i < conf->num_bss; i++) {
@@ -839,6 +874,15 @@
 	}
 #endif /* CONFIG_HS20 */
 
+#ifdef CONFIG_MBO
+	if (full_config && bss->mbo_enabled && (bss->wpa & 2) &&
+	    bss->ieee80211w == NO_MGMT_FRAME_PROTECTION) {
+		wpa_printf(MSG_ERROR,
+			   "MBO: PMF needs to be enabled whenever using WPA2 with MBO");
+		return -1;
+	}
+#endif /* CONFIG_MBO */
+
 	return 0;
 }
 
@@ -967,10 +1011,11 @@
 		bss->rsn_pairwise = WPA_CIPHER_CCMP;
 	} else {
 		bss->ssid.security_policy = SECURITY_PLAINTEXT;
-		bss->wpa_group = WPA_CIPHER_NONE;
-		bss->wpa_pairwise = WPA_CIPHER_NONE;
-		bss->rsn_pairwise = WPA_CIPHER_NONE;
-		if (full_config)
+		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/src/ap/ap_config.h b/src/ap/ap_config.h
index c14eeda..2d07c67 100644
--- a/src/ap/ap_config.h
+++ b/src/ap/ap_config.h
@@ -1,6 +1,6 @@
 /*
  * hostapd / Configuration definitions and helpers functions
- * Copyright (c) 2003-2012, Jouni Malinen <j@w1.fi>
+ * 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.
@@ -10,11 +10,14 @@
 #define HOSTAPD_CONFIG_H
 
 #include "common/defs.h"
+#include "utils/list.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"
+#include "vlan.h"
 
 /**
  * mesh_conf - local MBSS state and settings
@@ -32,8 +35,8 @@
 	u8 mesh_sp_id;
 	/* Authentication Protocol Identifier */
 	u8 mesh_auth_id;
-	u8 *ies;
-	int ie_len;
+	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)
@@ -51,7 +54,7 @@
 
 struct mac_acl_entry {
 	macaddr addr;
-	int vlan_id;
+	struct vlan_description vlan_id;
 };
 
 struct hostapd_radius_servers;
@@ -101,6 +104,7 @@
 #define DYNAMIC_VLAN_NAMING_WITH_DEVICE 1
 #define DYNAMIC_VLAN_NAMING_END 2
 	int vlan_naming;
+	int per_sta_vif;
 #ifdef CONFIG_FULL_DYNAMIC_VLAN
 	char *vlan_tagged_interface;
 #endif /* CONFIG_FULL_DYNAMIC_VLAN */
@@ -112,6 +116,7 @@
 struct hostapd_vlan {
 	struct hostapd_vlan *next;
 	int vlan_id; /* VLAN ID or -1 (VLAN_ID_WILDCARD) for wildcard entry */
+	struct vlan_description vlan_desc;
 	char ifname[IFNAMSIZ + 1];
 	int configured;
 	int dynamic_vlan;
@@ -123,9 +128,14 @@
 };
 
 #define PMK_LEN 32
+#define MIN_PASSPHRASE_LEN 8
+#define MAX_PASSPHRASE_LEN 63
 struct hostapd_sta_wpa_psk_short {
 	struct hostapd_sta_wpa_psk_short *next;
+	unsigned int is_passphrase:1;
 	u8 psk[PMK_LEN];
+	char passphrase[MAX_PASSPHRASE_LEN + 1];
+	int ref; /* (number of references held) - 1 */
 };
 
 struct hostapd_wpa_psk {
@@ -204,6 +214,13 @@
 	} eap_method[MAX_NAI_EAP_METHODS];
 };
 
+struct anqp_element {
+	struct dl_list list;
+	u16 infoid;
+	struct wpabuf *payload;
+};
+
+
 /**
  * struct hostapd_bss_config - Per-BSS configuration
  */
@@ -230,6 +247,7 @@
 	struct hostapd_eap_user *eap_user;
 	char *eap_user_sqlite;
 	char *eap_sim_db;
+	unsigned int eap_sim_db_timeout;
 	int eap_server_erp; /* Whether ERP is enabled on internal EAP server */
 	struct hostapd_ip_addr own_ip_addr;
 	char *nas_identifier;
@@ -329,7 +347,9 @@
 	char *private_key;
 	char *private_key_passwd;
 	int check_crl;
+	unsigned int tls_session_lifetime;
 	char *ocsp_stapling_response;
+	char *ocsp_stapling_response_multi;
 	char *dh_file;
 	char *openssl_ciphers;
 	u8 *pac_opaque_encr_key;
@@ -356,6 +376,7 @@
 
 	int ap_max_inactivity;
 	int ignore_broadcast_ssid;
+	int no_probe_resp_if_max_sta;
 
 	int wmm_enabled;
 	int wmm_uapsd;
@@ -479,6 +500,8 @@
 	unsigned int nai_realm_count;
 	struct hostapd_nai_realm_data *nai_realm_data;
 
+	struct dl_list anqp_elem; /* list of struct anqp_element */
+
 	u16 gas_comeback_delay;
 	int gas_frag_limit;
 
@@ -543,6 +566,7 @@
 #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)
@@ -551,6 +575,15 @@
 	int radio_measurements;
 
 	int vendor_vht;
+
+	char *no_probe_resp_if_seen_on;
+	char *no_auth_if_seen_on;
+
+	int pbss;
+
+#ifdef CONFIG_MBO
+	int mbo_enabled;
+#endif /* CONFIG_MBO */
 };
 
 
@@ -583,6 +616,9 @@
 	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
@@ -619,6 +655,7 @@
 	u16 ht_capab;
 	int ieee80211n;
 	int secondary_channel;
+	int no_pri_sec_switch;
 	int require_ht;
 	int obss_interval;
 	u32 vht_capab;
@@ -628,6 +665,13 @@
 	u8 vht_oper_centr_freq_seg0_idx;
 	u8 vht_oper_centr_freq_seg1_idx;
 
+	/* Use driver-generated interface addresses when adding multiple BSSs */
+	u8 use_driver_iface_addr;
+
+#ifdef CONFIG_FST
+	struct fst_iface_cfg fst_cfg;
+#endif /* CONFIG_FST */
+
 #ifdef CONFIG_P2P
 	u8 p2p_go_ctwindow;
 #endif /* CONFIG_P2P */
@@ -638,6 +682,7 @@
 	double ignore_assoc_probability;
 	double ignore_reassoc_probability;
 	double corrupt_gtk_rekey_mic_probability;
+	int ecsa_ie_only;
 #endif /* CONFIG_TESTING_OPTIONS */
 
 #ifdef CONFIG_ACS
@@ -652,7 +697,6 @@
 
 
 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);
@@ -660,13 +704,14 @@
 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);
+			  const u8 *addr, struct vlan_description *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);
+int hostapd_vlan_valid(struct hostapd_vlan *vlan,
+		       struct vlan_description *vlan_desc);
 const char * hostapd_get_vlan_id_ifname(struct hostapd_vlan *vlan,
 					int vlan_id);
 struct hostapd_radius_attr *
diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
index f3f7edd..b89f60e 100644
--- a/src/ap/ap_drv_ops.c
+++ b/src/ap/ap_drv_ops.c
@@ -33,10 +33,36 @@
 		res |= WPA_STA_SHORT_PREAMBLE;
 	if (flags & WLAN_STA_MFP)
 		res |= WPA_STA_MFP;
+	if (flags & WLAN_STA_AUTH)
+		res |= WPA_STA_AUTHENTICATED;
+	if (flags & WLAN_STA_ASSOC)
+		res |= WPA_STA_ASSOCIATED;
 	return res;
 }
 
 
+static int add_buf(struct wpabuf **dst, const struct wpabuf *src)
+{
+	if (!src)
+		return 0;
+	if (wpabuf_resize(dst, wpabuf_len(src)) != 0)
+		return -1;
+	wpabuf_put_buf(*dst, src);
+	return 0;
+}
+
+
+static int add_buf_data(struct wpabuf **dst, const u8 *data, size_t len)
+{
+	if (!data || !len)
+		return 0;
+	if (wpabuf_resize(dst, len) != 0)
+		return -1;
+	wpabuf_put_data(*dst, data, len);
+	return 0;
+}
+
+
 int hostapd_build_ap_extra_ies(struct hostapd_data *hapd,
 			       struct wpabuf **beacon_ret,
 			       struct wpabuf **proberesp_ret,
@@ -49,66 +75,38 @@
 
 	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);
-	}
+	if (add_buf_data(&beacon, buf, pos - buf) < 0)
+		goto fail;
 	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);
-	}
+	if (add_buf_data(&proberesp, buf, pos - buf) < 0)
+		goto fail;
 
 	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);
-	}
+	if (add_buf_data(&assocresp, buf, pos - buf) < 0)
+		goto fail;
 	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 (add_buf_data(&beacon, buf, pos - buf) < 0 ||
+	    add_buf_data(&proberesp, buf, pos - buf) < 0)
+		goto fail;
 
-		if (wpabuf_resize(&proberesp, pos - buf) != 0)
-			goto fail;
-		wpabuf_put_data(proberesp, buf, pos - buf);
-	}
+#ifdef CONFIG_FST
+	if (add_buf(&beacon, hapd->iface->fst_ies) < 0 ||
+	    add_buf(&proberesp, hapd->iface->fst_ies) < 0 ||
+	    add_buf(&assocresp, hapd->iface->fst_ies) < 0)
+		goto fail;
+#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);
-	}
+	if (add_buf(&beacon, hapd->wps_beacon_ie) < 0 ||
+	    add_buf(&proberesp, hapd->wps_probe_resp_ie) < 0)
+		goto fail;
 
 #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);
-	}
+	if (add_buf(&beacon, hapd->p2p_beacon_ie) < 0 ||
+	    add_buf(&proberesp, hapd->p2p_probe_resp_ie) < 0)
+		goto fail;
 #endif /* CONFIG_P2P */
 
 #ifdef CONFIG_P2P_MANAGER
@@ -132,8 +130,7 @@
 #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);
+		add_buf(&assocresp, a);
 		wpabuf_free(a);
 	}
 #endif /* CONFIG_WPS */
@@ -153,44 +150,35 @@
 	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);
+		add_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_hs20_indication(hapd, buf);
+	if (add_buf_data(&beacon, buf, pos - buf) < 0 ||
+	    add_buf_data(&proberesp, buf, pos - buf) < 0)
+		goto fail;
 
 	pos = hostapd_eid_osen(hapd, buf);
-	if (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);
-	}
+	if (add_buf_data(&beacon, buf, pos - buf) < 0 ||
+	    add_buf_data(&proberesp, buf, pos - buf) < 0)
+		goto fail;
 #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);
+#ifdef CONFIG_MBO
+	if (hapd->conf->mbo_enabled) {
+		pos = hostapd_eid_mbo(hapd, buf, sizeof(buf));
+		if (add_buf_data(&beacon, buf, pos - buf) < 0 ||
+		    add_buf_data(&proberesp, buf, pos - buf) < 0 ||
+		    add_buf_data(&assocresp, buf, pos - buf) < 0)
+			goto fail;
 	}
+#endif /* CONFIG_MBO */
+
+	add_buf(&beacon, hapd->conf->vendor_elements);
+	add_buf(&proberesp, hapd->conf->vendor_elements);
 
 	*beacon_ret = beacon;
 	*proberesp_ret = proberesp;
@@ -374,7 +362,7 @@
 		    u16 listen_interval,
 		    const struct ieee80211_ht_capabilities *ht_capab,
 		    const struct ieee80211_vht_capabilities *vht_capab,
-		    u32 flags, u8 qosinfo, u8 vht_opmode)
+		    u32 flags, u8 qosinfo, u8 vht_opmode, int set)
 {
 	struct hostapd_sta_add_params params;
 
@@ -396,6 +384,7 @@
 	params.vht_opmode = vht_opmode;
 	params.flags = hostapd_sta_flags_to_drv(flags);
 	params.qosinfo = qosinfo;
+	params.set = set;
 	return hapd->driver->sta_add(hapd->drv_priv, &params);
 }
 
@@ -452,7 +441,7 @@
 		return -1;
 	return hapd->driver->if_add(hapd->drv_priv, type, ifname, addr,
 				    bss_ctx, drv_priv, force_ifname, if_addr,
-				    bridge, use_existing);
+				    bridge, use_existing, 1);
 }
 
 
@@ -633,7 +622,19 @@
 {
 	if (hapd->driver == NULL || hapd->driver->send_mlme == NULL)
 		return 0;
-	return hapd->driver->send_mlme(hapd->drv_priv, msg, len, noack, 0);
+	return hapd->driver->send_mlme(hapd->drv_priv, msg, len, noack, 0,
+				       NULL, 0);
+}
+
+
+int hostapd_drv_send_mlme_csa(struct hostapd_data *hapd,
+			      const void *msg, size_t len, int noack,
+			      const u16 *csa_offs, size_t csa_offs_len)
+{
+	if (hapd->driver == NULL || hapd->driver->send_mlme == NULL)
+		return 0;
+	return hapd->driver->send_mlme(hapd->drv_priv, msg, len, noack, 0,
+				       csa_offs, csa_offs_len);
 }
 
 
@@ -727,6 +728,25 @@
 }
 
 
+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;
@@ -734,6 +754,7 @@
 	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;
@@ -749,24 +770,35 @@
 		acs_ch_list_all = 1;
 
 	mode = hapd->iface->current_mode;
-	if (mode == NULL)
-		return -1;
-	channels = os_malloc(mode->num_channels);
-	if (channels == NULL)
-		return -1;
+	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;
+		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 &
diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
index 82eaf3f..757a706 100644
--- a/src/ap/ap_drv_ops.h
+++ b/src/ap/ap_drv_ops.h
@@ -41,7 +41,7 @@
 		    u16 listen_interval,
 		    const struct ieee80211_ht_capabilities *ht_capab,
 		    const struct ieee80211_vht_capabilities *vht_capab,
-		    u32 flags, u8 qosinfo, u8 vht_opmode);
+		    u32 flags, u8 qosinfo, u8 vht_opmode, int set);
 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);
@@ -88,6 +88,9 @@
 			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_send_mlme_csa(struct hostapd_data *hapd,
+			      const void *msg, size_t len, int noack,
+			      const u16 *csa_offs, size_t csa_offs_len);
 int hostapd_drv_sta_deauth(struct hostapd_data *hapd,
 			   const u8 *addr, int reason);
 int hostapd_drv_sta_disassoc(struct hostapd_data *hapd,
diff --git a/src/ap/ap_list.c b/src/ap/ap_list.c
index 78a1f7c..8bf6dde 100644
--- a/src/ap/ap_list.c
+++ b/src/ap/ap_list.c
@@ -248,15 +248,12 @@
 }
 
 
-static void ap_list_timer(void *eloop_ctx, void *timeout_ctx)
+void ap_list_timer(struct hostapd_iface *iface)
 {
-	struct hostapd_iface *iface = eloop_ctx;
 	struct os_reltime now;
 	struct ap_info *ap;
 	int set_beacon = 0;
 
-	eloop_register_timeout(10, 0, ap_list_timer, iface, NULL);
-
 	if (!iface->ap_list)
 		return;
 
@@ -305,13 +302,11 @@
 
 int ap_list_init(struct hostapd_iface *iface)
 {
-	eloop_register_timeout(10, 0, ap_list_timer, iface, NULL);
 	return 0;
 }
 
 
 void ap_list_deinit(struct hostapd_iface *iface)
 {
-	eloop_cancel_timeout(ap_list_timer, iface, NULL);
 	hostapd_free_aps(iface);
 }
diff --git a/src/ap/ap_list.h b/src/ap/ap_list.h
index 93dc0ed..9e0353c 100644
--- a/src/ap/ap_list.h
+++ b/src/ap/ap_list.h
@@ -39,6 +39,7 @@
 #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)
 {
@@ -48,6 +49,10 @@
 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/src/ap/ap_mlme.c b/src/ap/ap_mlme.c
index 13604ed..e7308a0 100644
--- a/src/ap/ap_mlme.c
+++ b/src/ap/ap_mlme.c
@@ -59,6 +59,7 @@
 		       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);
+	ap_sta_clear_disconnect_timeouts(hapd, sta);
 }
 
 
@@ -106,6 +107,7 @@
 		       MAC2STR(sta->addr));
 	if (sta->auth_alg != WLAN_AUTH_FT)
 		mlme_deletekeys_request(hapd, sta);
+	ap_sta_clear_disconnect_timeouts(hapd, sta);
 }
 
 
@@ -130,6 +132,7 @@
 		       MAC2STR(sta->addr));
 	if (sta->auth_alg != WLAN_AUTH_FT)
 		mlme_deletekeys_request(hapd, sta);
+	ap_sta_clear_disconnect_timeouts(hapd, sta);
 }
 
 
diff --git a/src/ap/authsrv.c b/src/ap/authsrv.c
index f10e1b7..cdb49cd 100644
--- a/src/ap/authsrv.c
+++ b/src/ap/authsrv.c
@@ -132,6 +132,7 @@
 #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) {
@@ -151,9 +152,12 @@
 	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;
 
-		hapd->ssl_ctx = tls_init(NULL);
+		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);
@@ -169,6 +173,8 @@
 		params.openssl_ciphers = hapd->conf->openssl_ciphers;
 		params.ocsp_stapling_response =
 			hapd->conf->ocsp_stapling_response;
+		params.ocsp_stapling_response_multi =
+			hapd->conf->ocsp_stapling_response_multi;
 
 		if (tls_global_set_params(hapd->ssl_ctx, &params)) {
 			wpa_printf(MSG_ERROR, "Failed to set TLS parameters");
@@ -189,6 +195,7 @@
 	if (hapd->conf->eap_sim_db) {
 		hapd->eap_sim_db_priv =
 			eap_sim_db_init(hapd->conf->eap_sim_db,
+					hapd->conf->eap_sim_db_timeout,
 					hostapd_sim_db_cb, hapd);
 		if (hapd->eap_sim_db_priv == NULL) {
 			wpa_printf(MSG_ERROR, "Failed to initialize EAP-SIM "
diff --git a/src/ap/beacon.c b/src/ap/beacon.c
index 51d0c15..19bff7b 100644
--- a/src/ap/beacon.c
+++ b/src/ap/beacon.c
@@ -297,65 +297,65 @@
 
 static u8 * hostapd_eid_csa(struct hostapd_data *hapd, u8 *eid)
 {
-	u8 chan;
-
-	if (!hapd->cs_freq_params.freq)
+#ifdef CONFIG_TESTING_OPTIONS
+	if (hapd->iface->cs_oper_class && hapd->iconf->ecsa_ie_only)
 		return eid;
+#endif /* CONFIG_TESTING_OPTIONS */
 
-	if (ieee80211_freq_to_chan(hapd->cs_freq_params.freq, &chan) ==
-	    NUM_HOSTAPD_MODES)
+	if (!hapd->cs_freq_params.channel)
 		return eid;
 
 	*eid++ = WLAN_EID_CHANNEL_SWITCH;
 	*eid++ = 3;
 	*eid++ = hapd->cs_block_tx;
-	*eid++ = chan;
+	*eid++ = hapd->cs_freq_params.channel;
 	*eid++ = hapd->cs_count;
 
 	return eid;
 }
 
 
-static u8 * hostapd_eid_secondary_channel(struct hostapd_data *hapd, u8 *eid)
+static u8 * hostapd_eid_ecsa(struct hostapd_data *hapd, u8 *eid)
 {
-	u8 sec_ch;
-
-	if (!hapd->cs_freq_params.sec_channel_offset)
+	if (!hapd->cs_freq_params.channel || !hapd->iface->cs_oper_class)
 		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;
+	*eid++ = WLAN_EID_EXT_CHANSWITCH_ANN;
+	*eid++ = 4;
+	*eid++ = hapd->cs_block_tx;
+	*eid++ = hapd->iface->cs_oper_class;
+	*eid++ = hapd->cs_freq_params.channel;
+	*eid++ = hapd->cs_count;
 
 	return eid;
 }
 
 
-static u8 * hostapd_add_csa_elems(struct hostapd_data *hapd, u8 *pos,
-				  u8 *start, unsigned int *csa_counter_off)
+static u8 * hostapd_eid_supported_op_classes(struct hostapd_data *hapd, u8 *eid)
 {
-	u8 *old_pos = pos;
+	u8 op_class, channel;
 
-	if (!csa_counter_off)
-		return pos;
+	if (!(hapd->iface->drv_flags & WPA_DRIVER_FLAGS_AP_CSA) ||
+	    !hapd->iface->freq)
+		return eid;
 
-	*csa_counter_off = 0;
-	pos = hostapd_eid_csa(hapd, pos);
+	if (ieee80211_freq_to_channel_ext(hapd->iface->freq,
+					  hapd->iconf->secondary_channel,
+					  hapd->iconf->vht_oper_chwidth,
+					  &op_class, &channel) ==
+	    NUM_HOSTAPD_MODES)
+		return eid;
 
-	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);
-	}
+	*eid++ = WLAN_EID_SUPPORTED_OPERATING_CLASSES;
+	*eid++ = 2;
 
-	return pos;
+	/* Current Operating Class */
+	*eid++ = op_class;
+
+	/* TODO: Advertise all the supported operating classes */
+	*eid++ = 0;
+
+	return eid;
 }
 
 
@@ -364,7 +364,7 @@
 				   int is_p2p, size_t *resp_len)
 {
 	struct ieee80211_mgmt *resp;
-	u8 *pos, *epos;
+	u8 *pos, *epos, *csa_pos;
 	size_t buflen;
 
 #define MAX_PROBERESP_LEN 768
@@ -377,12 +377,19 @@
 	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);
 	}
+
+	buflen += hostapd_mbo_ie_len(hapd);
+
 	resp = os_zalloc(buflen);
 	if (resp == NULL)
 		return NULL;
@@ -420,6 +427,12 @@
 	/* Power Constraint element */
 	pos = hostapd_eid_pwr_constraint(hapd, pos);
 
+	/* CSA IE */
+	csa_pos = hostapd_eid_csa(hapd, pos);
+	if (csa_pos != pos)
+		hapd->cs_c_off_proberesp = csa_pos - (u8 *) resp - 1;
+	pos = csa_pos;
+
 	/* ERP Information element */
 	pos = hostapd_eid_erp_info(hapd, pos);
 
@@ -433,7 +446,19 @@
 
 	pos = hostapd_eid_rm_enabled_capab(hapd, pos, epos - pos);
 
+	/* eCSA IE */
+	csa_pos = hostapd_eid_ecsa(hapd, pos);
+	if (csa_pos != pos)
+		hapd->cs_c_off_ecsa_proberesp = csa_pos - (u8 *) resp - 1;
+	pos = csa_pos;
+
+	pos = hostapd_eid_supported_op_classes(hapd, pos);
+
 #ifdef CONFIG_IEEE80211N
+	/* Secondary Channel Offset element */
+	/* TODO: The standard doesn't specify a position for this element. */
+	pos = hostapd_eid_secondary_channel(hapd, pos);
+
 	pos = hostapd_eid_ht_capabilities(hapd, pos);
 	pos = hostapd_eid_ht_operation(hapd, pos);
 #endif /* CONFIG_IEEE80211N */
@@ -447,12 +472,20 @@
 	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);
+		pos = hostapd_eid_txpower_envelope(hapd, pos);
+		pos = hostapd_eid_wb_chsw_wrapper(hapd, pos);
 	}
 	if (hapd->conf->vendor_vht)
 		pos = hostapd_eid_vendor_vht(hapd, pos);
@@ -488,6 +521,8 @@
 	pos = hostapd_eid_osen(hapd, pos);
 #endif /* CONFIG_HS20 */
 
+	pos = hostapd_eid_mbo(hapd, pos, (u8 *) resp + buflen - pos);
+
 	if (hapd->conf->vendor_elements) {
 		os_memcpy(pos, wpabuf_head(hapd->conf->vendor_elements),
 			  wpabuf_len(hapd->conf->vendor_elements));
@@ -524,8 +559,8 @@
 
 	pos = ssid_list;
 	end = ssid_list + ssid_list_len;
-	while (pos + 1 <= end) {
-		if (pos + 2 + pos[1] > end)
+	while (end - pos >= 1) {
+		if (2 + pos[1] > end - pos)
 			break;
 		if (pos[1] == 0)
 			wildcard = 1;
@@ -539,6 +574,102 @@
 }
 
 
+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)
@@ -550,11 +681,16 @@
 	size_t i, resp_len;
 	int noack;
 	enum ssid_match_result res;
+	int ret;
+	u16 csa_offs[2];
+	size_t csa_offs_len;
 
-	ie = mgmt->u.probe_req.variable;
-	if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.probe_req))
+	if (len < IEEE80211_HDRLEN)
 		return;
-	ie_len = len - (IEEE80211_HDRLEN + sizeof(mgmt->u.probe_req));
+	ie = ((const u8 *) mgmt) + IEEE80211_HDRLEN;
+	if (hapd->iconf->track_sta_max_num)
+		sta_track_add(hapd->iface, mgmt->sa);
+	ie_len = len - IEEE80211_HDRLEN;
 
 	for (i = 0; hapd->probereq_cb && i < hapd->num_probereq_cb; i++)
 		if (hapd->probereq_cb[i].cb(hapd->probereq_cb[i].ctx,
@@ -600,7 +736,7 @@
 	}
 
 #ifdef CONFIG_P2P
-	if (hapd->p2p && elems.wps_ie) {
+	if (hapd->p2p && hapd->p2p_group && 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)) {
@@ -613,7 +749,7 @@
 		wpabuf_free(wps);
 	}
 
-	if (hapd->p2p && elems.p2p) {
+	if (hapd->p2p && hapd->p2p_group && 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)) {
@@ -702,6 +838,29 @@
 	/* 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;
+	}
+
+	if (hapd->conf->no_probe_resp_if_max_sta &&
+	    is_multicast_ether_addr(mgmt->da) &&
+	    is_multicast_ether_addr(mgmt->bssid) &&
+	    hapd->num_sta >= hapd->conf->max_num_sta &&
+	    !ap_get_sta(hapd, mgmt->sa)) {
+		wpa_printf(MSG_MSGDUMP, "%s: Ignore Probe Request from " MACSTR
+			   " since no room for additional STA",
+			   hapd->conf->iface, MAC2STR(mgmt->sa));
+		return;
+	}
+
 #ifdef CONFIG_TESTING_OPTIONS
 	if (hapd->iconf->ignore_probe_probability > 0.0 &&
 	    drand48() < hapd->iconf->ignore_probe_probability) {
@@ -724,7 +883,22 @@
 	noack = !!(res == WILDCARD_SSID_MATCH &&
 		   is_broadcast_ether_addr(mgmt->da));
 
-	if (hostapd_drv_send_mlme(hapd, resp, resp_len, noack) < 0)
+	csa_offs_len = 0;
+	if (hapd->csa_in_progress) {
+		if (hapd->cs_c_off_proberesp)
+			csa_offs[csa_offs_len++] =
+				hapd->cs_c_off_proberesp;
+
+		if (hapd->cs_c_off_ecsa_proberesp)
+			csa_offs[csa_offs_len++] =
+				hapd->cs_c_off_ecsa_proberesp;
+	}
+
+	ret = hostapd_drv_send_mlme_csa(hapd, resp, resp_len, noack,
+					csa_offs_len ? csa_offs : NULL,
+					csa_offs_len);
+
+	if (ret < 0)
 		wpa_printf(MSG_INFO, "handle_probe_req: send failed");
 
 	os_free(resp);
@@ -783,7 +957,7 @@
 	size_t resp_len = 0;
 #ifdef NEED_AP_MLME
 	u16 capab_info;
-	u8 *pos, *tailpos;
+	u8 *pos, *tailpos, *csa_pos;
 
 #define BEACON_HEAD_BUF_SIZE 256
 #define BEACON_TAIL_BUF_SIZE 512
@@ -797,6 +971,10 @@
 	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);
 
@@ -807,6 +985,8 @@
 	}
 #endif /* CONFIG_IEEE80211AC */
 
+	tail_len += hostapd_mbo_ie_len(hapd);
+
 	tailpos = tail = os_malloc(tail_len);
 	if (head == NULL || tail == NULL) {
 		wpa_printf(MSG_ERROR, "Failed to set beacon data");
@@ -860,6 +1040,12 @@
 	/* Power Constraint element */
 	tailpos = hostapd_eid_pwr_constraint(hapd, tailpos);
 
+	/* CSA IE */
+	csa_pos = hostapd_eid_csa(hapd, tailpos);
+	if (csa_pos != tailpos)
+		hapd->cs_c_off_beacon = csa_pos - tail - 1;
+	tailpos = csa_pos;
+
 	/* ERP Information element */
 	tailpos = hostapd_eid_erp_info(hapd, tailpos);
 
@@ -877,7 +1063,19 @@
 	tailpos = hostapd_eid_bss_load(hapd, tailpos,
 				       tail + BEACON_TAIL_BUF_SIZE - tailpos);
 
+	/* eCSA IE */
+	csa_pos = hostapd_eid_ecsa(hapd, tailpos);
+	if (csa_pos != tailpos)
+		hapd->cs_c_off_ecsa_beacon = csa_pos - tail - 1;
+	tailpos = csa_pos;
+
+	tailpos = hostapd_eid_supported_op_classes(hapd, tailpos);
+
 #ifdef CONFIG_IEEE80211N
+	/* Secondary Channel Offset element */
+	/* TODO: The standard doesn't specify a position for this element. */
+	tailpos = hostapd_eid_secondary_channel(hapd, tailpos);
+
 	tailpos = hostapd_eid_ht_capabilities(hapd, tailpos);
 	tailpos = hostapd_eid_ht_operation(hapd, tailpos);
 #endif /* CONFIG_IEEE80211N */
@@ -893,12 +1091,21 @@
 	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);
+		tailpos = hostapd_eid_txpower_envelope(hapd, tailpos);
+		tailpos = hostapd_eid_wb_chsw_wrapper(hapd, tailpos);
 	}
 	if (hapd->conf->vendor_vht)
 		tailpos = hostapd_eid_vendor_vht(hapd, tailpos);
@@ -933,6 +1140,8 @@
 	tailpos = hostapd_eid_osen(hapd, tailpos);
 #endif /* CONFIG_HS20 */
 
+	tailpos = hostapd_eid_mbo(hapd, tailpos, tail + tail_len - tailpos);
+
 	if (hapd->conf->vendor_elements) {
 		os_memcpy(tailpos, wpabuf_head(hapd->conf->vendor_elements),
 			  wpabuf_len(hapd->conf->vendor_elements));
@@ -1017,6 +1226,7 @@
 		params->osen = 1;
 	}
 #endif /* CONFIG_HS20 */
+	params->pbss = hapd->conf->pbss;
 	return 0;
 }
 
diff --git a/src/ap/beacon.h b/src/ap/beacon.h
index 722159a..d98f42e 100644
--- a/src/ap/beacon.h
+++ b/src/ap/beacon.h
@@ -21,5 +21,10 @@
 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/src/ap/ctrl_iface_ap.c b/src/ap/ctrl_iface_ap.c
index 60afcb0..317b238 100644
--- a/src/ap/ctrl_iface_ap.c
+++ b/src/ap/ctrl_iface_ap.c
@@ -12,6 +12,7 @@
 #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"
@@ -21,6 +22,7 @@
 #include "p2p_hostapd.h"
 #include "ctrl_iface_ap.h"
 #include "ap_drv_ops.h"
+#include "mbo_ap.h"
 
 
 static int hostapd_get_sta_tx_rx(struct hostapd_data *hapd,
@@ -34,7 +36,7 @@
 		return 0;
 
 	ret = os_snprintf(buf, buflen, "rx_packets=%lu\ntx_packets=%lu\n"
-			  "rx_bytes=%lu\ntx_bytes=%lu\n",
+			  "rx_bytes=%llu\ntx_bytes=%llu\n",
 			  data.rx_packets, data.tx_packets,
 			  data.rx_bytes, data.tx_bytes);
 	if (os_snprintf_error(buflen, ret))
@@ -160,6 +162,19 @@
 			len += res;
 	}
 
+	res = mbo_ap_get_info(sta, buf + len, buflen - len);
+	if (res >= 0)
+		len += res;
+
+	if (sta->supp_op_classes &&
+	    buflen - len > (unsigned) (17 + 2 * sta->supp_op_classes[0])) {
+		len += os_snprintf(buf + len, buflen - len, "supp_op_classes=");
+		len += wpa_snprintf_hex(buf + len, buflen - len,
+					sta->supp_op_classes + 1,
+					sta->supp_op_classes[0]);
+		len += os_snprintf(buf + len, buflen - len, "\n");
+	}
+
 	return len;
 }
 
@@ -206,7 +221,10 @@
 		return -1;
 	}
 
-	return hostapd_ctrl_iface_sta_mib(hapd, sta, buf, buflen);
+	ret = hostapd_ctrl_iface_sta_mib(hapd, sta, buf, buflen);
+	ret += fst_ctrl_iface_mb_info(addr, buf + ret, buflen - ret);
+
+	return ret;
 }
 
 
@@ -550,3 +568,16 @@
 {
 	return hostapd_drv_stop_ap(hapd);
 }
+
+
+int hostapd_ctrl_iface_pmksa_list(struct hostapd_data *hapd, char *buf,
+				  size_t len)
+{
+	return wpa_auth_pmksa_list(hapd->wpa_auth, buf, len);
+}
+
+
+void hostapd_ctrl_iface_pmksa_flush(struct hostapd_data *hapd)
+{
+	wpa_auth_pmksa_flush(hapd->wpa_auth);
+}
diff --git a/src/ap/ctrl_iface_ap.h b/src/ap/ctrl_iface_ap.h
index e5297d0..3ad622f 100644
--- a/src/ap/ctrl_iface_ap.h
+++ b/src/ap/ctrl_iface_ap.h
@@ -24,5 +24,8 @@
 int hostapd_parse_csa_settings(const char *pos,
 			       struct csa_settings *settings);
 int hostapd_ctrl_iface_stop_ap(struct hostapd_data *hapd);
+int hostapd_ctrl_iface_pmksa_list(struct hostapd_data *hapd, char *buf,
+				  size_t len);
+void hostapd_ctrl_iface_pmksa_flush(struct hostapd_data *hapd);
 
 #endif /* CTRL_IFACE_AP_H */
diff --git a/src/ap/dfs.c b/src/ap/dfs.c
index 715f19b..47adba7 100644
--- a/src/ap/dfs.c
+++ b/src/ap/dfs.c
@@ -450,7 +450,7 @@
 		return NULL;
 
 	if (os_get_random((u8 *) &_rand, sizeof(_rand)) < 0)
-		_rand = os_random();
+		return NULL;
 	chan_idx = _rand % num_available_chandefs;
 	dfs_find_channel(iface, &chan, chan_idx, skip_radar);
 
@@ -704,7 +704,8 @@
 							skip_radar);
 			if (!channel) {
 				wpa_printf(MSG_ERROR, "could not get valid channel");
-				return -1;
+				hostapd_set_state(iface, HAPD_IFACE_DFS);
+				return 0;
 			}
 
 			iface->freq = channel->freq;
@@ -793,7 +794,6 @@
 
 	if (!channel) {
 		wpa_printf(MSG_ERROR, "No valid channel available");
-		hostapd_setup_interface_complete(iface, err);
 		return err;
 	}
 
@@ -817,16 +817,6 @@
 }
 
 
-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;
@@ -868,8 +858,9 @@
 						&vht_oper_centr_freq_seg1_idx,
 						skip_radar);
 		if (!channel) {
-			/* FIXME: Wait for channel(s) to become available */
-			hostapd_disable_iface(iface);
+			wpa_printf(MSG_INFO,
+				   "%s: no DFS channels left, waiting for NOP to finish",
+				   __func__);
 			return err;
 		}
 
@@ -992,6 +983,11 @@
 	/* TODO add correct implementation here */
 	set_dfs_state(iface, freq, ht_enabled, chan_offset, chan_width,
 		      cf1, cf2, HOSTAPD_CHAN_DFS_USABLE);
+
+	/* Handle cases where all channels were initially unavailable */
+	if (iface->state == HAPD_IFACE_DFS && !iface->cac_started)
+		hostapd_handle_dfs(iface);
+
 	return 0;
 }
 
diff --git a/src/ap/drv_callbacks.c b/src/ap/drv_callbacks.c
index 6ecd094..3ab5bf3 100644
--- a/src/ap/drv_callbacks.c
+++ b/src/ap/drv_callbacks.c
@@ -18,9 +18,11 @@
 #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 "ieee802_11_auth.h"
 #include "sta_info.h"
 #include "accounting.h"
 #include "tkip_countermeasures.h"
@@ -32,6 +34,7 @@
 #include "hw_features.h"
 #include "dfs.h"
 #include "beacon.h"
+#include "mbo_ap.h"
 
 
 int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr,
@@ -42,10 +45,10 @@
 	struct ieee802_11_elems elems;
 	const u8 *ie;
 	size_t ielen;
-#ifdef CONFIG_IEEE80211R
+#if defined(CONFIG_IEEE80211R) || defined(CONFIG_IEEE80211W)
 	u8 buf[sizeof(struct ieee80211_mgmt) + 1024];
 	u8 *p = buf;
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R || CONFIG_IEEE80211W */
 	u16 reason = WLAN_REASON_UNSPECIFIED;
 	u16 status = WLAN_STATUS_SUCCESS;
 	const u8 *p2p_dev_addr = NULL;
@@ -58,8 +61,8 @@
 		 * running, so better make sure we stop processing such an
 		 * event here.
 		 */
-		wpa_printf(MSG_DEBUG, "hostapd_notif_assoc: Skip event with "
-			   "no address");
+		wpa_printf(MSG_DEBUG,
+			   "hostapd_notif_assoc: Skip event with no address");
 		return -1;
 	}
 	random_add_randomness(addr, ETH_ALEN);
@@ -89,8 +92,8 @@
 	} else {
 		ie = NULL;
 		ielen = 0;
-		wpa_printf(MSG_DEBUG, "STA did not include WPS/RSN/WPA IE in "
-			   "(Re)AssocReq");
+		wpa_printf(MSG_DEBUG,
+			   "STA did not include WPS/RSN/WPA IE in (Re)AssocReq");
 	}
 
 	sta = ap_get_sta(hapd, addr);
@@ -113,6 +116,14 @@
 	}
 	sta->flags &= ~(WLAN_STA_WPS | WLAN_STA_MAYBE_WPS | WLAN_STA_WPS2);
 
+	res = hostapd_check_acl(hapd, addr, NULL);
+	if (res != HOSTAPD_ACL_ACCEPT) {
+		wpa_printf(MSG_INFO, "STA " MACSTR " not allowed to connect",
+			   MAC2STR(addr));
+		reason = WLAN_REASON_UNSPECIFIED;
+		goto fail;
+	}
+
 #ifdef CONFIG_P2P
 	if (elems.p2p) {
 		wpabuf_free(sta->p2p_ie);
@@ -155,13 +166,25 @@
 		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 */
+
+	mbo_ap_check_sta_assoc(hapd, sta, &elems);
+
+	ap_copy_sta_supp_op_classes(sta, elems.supp_op_classes,
+				    elems.supp_op_classes_len);
+
 	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");
+				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;
 			}
@@ -174,13 +197,14 @@
 		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");
+					wpa_printf(MSG_DEBUG,
+						   "WPS: STA supports WPS 2.0");
 					sta->flags |= WLAN_STA_WPS2;
 				}
 				wpabuf_free(wps);
@@ -194,16 +218,17 @@
 							sta->addr,
 							p2p_dev_addr);
 		if (sta->wpa_sm == NULL) {
-			wpa_printf(MSG_ERROR, "Failed to initialize WPA state "
-				   "machine");
+			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_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;
@@ -246,14 +271,12 @@
 			if (sta->sa_query_count == 0)
 				ap_sta_start_sa_query(hapd, sta);
 
-#ifdef CONFIG_IEEE80211R
 			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);
-#endif /* CONFIG_IEEE80211R */
 			return 0;
 		}
 
@@ -281,6 +304,7 @@
 	} 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);
@@ -297,8 +321,8 @@
 		if (wps) {
 			sta->flags |= WLAN_STA_WPS;
 			if (wps_is_20(wps)) {
-				wpa_printf(MSG_DEBUG, "WPS: STA supports "
-					   "WPS 2.0");
+				wpa_printf(MSG_DEBUG,
+					   "WPS: STA supports WPS 2.0");
 				sta->flags |= WLAN_STA_WPS2;
 			}
 		} else
@@ -320,8 +344,8 @@
 			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");
+			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,
@@ -329,6 +353,17 @@
 			return WLAN_STATUS_INVALID_IE;
 #endif /* CONFIG_HS20 */
 	}
+
+#ifdef CONFIG_MBO
+	if (hapd->conf->mbo_enabled && (hapd->conf->wpa & 2) &&
+	    elems.mbo && sta->cell_capa && !(sta->flags & WLAN_STA_MFP) &&
+	    hapd->conf->ieee80211w != NO_MGMT_FRAME_PROTECTION) {
+		wpa_printf(MSG_INFO,
+			   "MBO: Reject WPA2 association without PMF");
+		return WLAN_STATUS_UNSPECIFIED_FAILURE;
+	}
+#endif /* CONFIG_MBO */
+
 #ifdef CONFIG_WPS
 skip_wpa_check:
 #endif /* CONFIG_WPS */
@@ -393,8 +428,8 @@
 		 * 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");
+		wpa_printf(MSG_DEBUG,
+			   "hostapd_notif_disassoc: Skip event with no address");
 		return;
 	}
 
@@ -403,8 +438,9 @@
 
 	sta = ap_get_sta(hapd, addr);
 	if (sta == NULL) {
-		wpa_printf(MSG_DEBUG, "Disassociation notification for "
-			   "unknown STA " MACSTR, MAC2STR(addr));
+		wpa_printf(MSG_DEBUG,
+			   "Disassociation notification for unknown STA "
+			   MACSTR, MAC2STR(addr));
 		return;
 	}
 
@@ -425,8 +461,8 @@
 		return;
 
 	hostapd_logger(hapd, addr, HOSTAPD_MODULE_IEEE80211,
-		       HOSTAPD_LEVEL_INFO, "disconnected due to excessive "
-		       "missing ACKs");
+		       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);
@@ -437,7 +473,8 @@
 			     int offset, int width, int cf1, int cf2)
 {
 #ifdef NEED_AP_MLME
-	int channel, chwidth, seg0_idx = 0, seg1_idx = 0, is_dfs;
+	int channel, chwidth, is_dfs;
+	u8 seg0_idx = 0, seg1_idx = 0;
 
 	hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211,
 		       HOSTAPD_LEVEL_INFO,
@@ -450,8 +487,8 @@
 	channel = hostapd_hw_get_channel(hapd, freq);
 	if (!channel) {
 		hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211,
-			       HOSTAPD_LEVEL_WARNING, "driver switched to "
-			       "bad channel!");
+			       HOSTAPD_LEVEL_WARNING,
+			       "driver switched to bad channel!");
 		return;
 	}
 
@@ -481,8 +518,8 @@
 			seg1_idx = (cf2 - 5000) / 5;
 		break;
 	default:
-		seg0_idx = hostapd_hw_get_channel(hapd, cf1);
-		seg1_idx = hostapd_hw_get_channel(hapd, cf2);
+		ieee80211_freq_to_chan(cf1, &seg0_idx);
+		ieee80211_freq_to_chan(cf2, &seg1_idx);
 		break;
 	}
 
@@ -529,10 +566,11 @@
 
 
 #ifdef CONFIG_ACS
-static void hostapd_acs_channel_selected(struct hostapd_data *hapd,
-					 struct acs_selected_channels *acs_res)
+void hostapd_acs_channel_selected(struct hostapd_data *hapd,
+				  struct acs_selected_channels *acs_res)
 {
 	int ret, i;
+	int err = 0;
 
 	if (hapd->iconf->channel) {
 		wpa_printf(MSG_INFO, "ACS: Channel was already set to %d",
@@ -554,7 +592,8 @@
 			hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211,
 				       HOSTAPD_LEVEL_WARNING,
 				       "driver selected to bad hw_mode");
-			return;
+			err = 1;
+			goto out;
 		}
 	}
 
@@ -564,7 +603,8 @@
 		hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211,
 			       HOSTAPD_LEVEL_WARNING,
 			       "driver switched to bad channel");
-		return;
+		err = 1;
+		goto out;
 	}
 
 	hapd->iconf->channel = acs_res->pri_channel;
@@ -578,7 +618,8 @@
 		hapd->iconf->secondary_channel = 1;
 	else {
 		wpa_printf(MSG_ERROR, "Invalid secondary channel!");
-		return;
+		err = 1;
+		goto out;
 	}
 
 	if (hapd->iface->conf->ieee80211ac) {
@@ -607,7 +648,8 @@
 		}
 	}
 
-	ret = hostapd_acs_completed(hapd->iface, 0);
+out:
+	ret = hostapd_acs_completed(hapd->iface, err);
 	if (ret) {
 		wpa_printf(MSG_ERROR,
 			   "ACS: Possibly channel configuration is invalid");
@@ -688,8 +730,8 @@
 			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");
+			wpa_printf(MSG_DEBUG,
+				   "FT: Failed to initialize WPA state machine");
 			status = WLAN_STATUS_UNSPECIFIED_FAILURE;
 			goto fail;
 		}
@@ -724,7 +766,7 @@
 	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",
+	wpa_printf(MSG_DEBUG, "RX_ACTION cat %d action plen %d",
 		   mgmt->u.action.category, (int) plen);
 
 	sta = ap_get_sta(hapd, mgmt->sa);
@@ -735,6 +777,7 @@
 #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 */
@@ -751,6 +794,13 @@
 		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 */
+
 }
 
 
@@ -802,6 +852,7 @@
 	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);
@@ -819,8 +870,7 @@
 
 	hapd = get_hapd_bssid(iface, bssid);
 	if (hapd == NULL) {
-		u16 fc;
-		fc = le_to_host16(hdr->frame_control);
+		u16 fc = le_to_host16(hdr->frame_control);
 
 		/*
 		 * Drop frames to unknown BSSIDs except for Beacon frames which
@@ -839,6 +889,7 @@
 
 	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
@@ -865,6 +916,7 @@
 			       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)
@@ -878,6 +930,7 @@
 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;
 
@@ -904,17 +957,18 @@
 	size_t j;
 
 	for (j = 0; j < iface->num_bss; j++) {
-		if ((sta = ap_get_sta(iface->bss[j], src))) {
-			if (sta->flags & WLAN_STA_ASSOC) {
-				hapd = iface->bss[j];
-				break;
-			}
+		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);
 }
 
+#endif /* HOSTAPD */
+
 
 static struct hostapd_channel_data * hostapd_get_mode_channel(
 	struct hostapd_iface *iface, unsigned int freq)
@@ -924,8 +978,6 @@
 
 	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;
 	}
@@ -968,7 +1020,8 @@
 	if (!chan || chan->flag & HOSTAPD_CHAN_DISABLED)
 		return;
 
-	wpa_printf(MSG_DEBUG, "Single Channel Survey: (freq=%d channel_time=%ld channel_time_busy=%ld)",
+	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);
@@ -988,10 +1041,9 @@
 }
 
 
-static void hostapd_event_get_survey(struct hostapd_data *hapd,
-				     struct survey_results *survey_results)
+void hostapd_event_get_survey(struct hostapd_iface *iface,
+			      struct survey_results *survey_results)
 {
-	struct hostapd_iface *iface = hapd->iface;
 	struct freq_survey *survey, *tmp;
 	struct hostapd_channel_data *chan;
 
@@ -1023,6 +1075,7 @@
 }
 
 
+#ifdef HOSTAPD
 #ifdef NEED_AP_MLME
 
 static void hostapd_event_iface_unavailable(struct hostapd_data *hapd)
@@ -1102,6 +1155,7 @@
 	    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 &&
@@ -1229,7 +1283,7 @@
 			data->connect_failed_reason.code);
 		break;
 	case EVENT_SURVEY:
-		hostapd_event_get_survey(hapd, &data->survey_results);
+		hostapd_event_get_survey(hapd->iface, &data->survey_results);
 		break;
 #ifdef NEED_AP_MLME
 	case EVENT_INTERFACE_UNAVAILABLE:
@@ -1299,4 +1353,31 @@
 	}
 }
 
+
+void wpa_supplicant_event_global(void *ctx, enum wpa_event_type event,
+				 union wpa_event_data *data)
+{
+	struct hapd_interfaces *interfaces = ctx;
+	struct hostapd_data *hapd;
+
+	if (event != EVENT_INTERFACE_STATUS)
+		return;
+
+	hapd = hostapd_get_iface(interfaces, data->interface_status.ifname);
+	if (hapd && hapd->driver && hapd->driver->get_ifindex &&
+	    hapd->drv_priv) {
+		unsigned int ifindex;
+
+		ifindex = hapd->driver->get_ifindex(hapd->drv_priv);
+		if (ifindex != data->interface_status.ifindex) {
+			wpa_dbg(hapd->msg_ctx, MSG_DEBUG,
+				"interface status ifindex %d mismatch (%d)",
+				ifindex, data->interface_status.ifindex);
+			return;
+		}
+	}
+	if (hapd)
+		wpa_supplicant_event(hapd, event, data);
+}
+
 #endif /* HOSTAPD */
diff --git a/src/ap/gas_serv.c b/src/ap/gas_serv.c
index 9d19f98..179dc7a 100644
--- a/src/ap/gas_serv.c
+++ b/src/ap/gas_serv.c
@@ -101,6 +101,7 @@
 		if (sta->gas_dialog[i].dialog_token != dialog_token ||
 		    !sta->gas_dialog[i].valid)
 			continue;
+		ap_sta_replenish_timeout(hapd, sta, 5);
 		return &sta->gas_dialog[i];
 	}
 	wpa_printf(MSG_DEBUG, "ANQP: Could not find dialog for "
@@ -167,27 +168,107 @@
 #endif /* CONFIG_HS20 */
 
 
+static struct anqp_element * get_anqp_elem(struct hostapd_data *hapd,
+					   u16 infoid)
+{
+	struct anqp_element *elem;
+
+	dl_list_for_each(elem, &hapd->conf->anqp_elem, struct anqp_element,
+			 list) {
+		if (elem->infoid == infoid)
+			return elem;
+	}
+
+	return NULL;
+}
+
+
+static void anqp_add_elem(struct hostapd_data *hapd, struct wpabuf *buf,
+			  u16 infoid)
+{
+	struct anqp_element *elem;
+
+	elem = get_anqp_elem(hapd, infoid);
+	if (!elem)
+		return;
+	if (wpabuf_tailroom(buf) < 2 + 2 + wpabuf_len(elem->payload)) {
+		wpa_printf(MSG_DEBUG, "ANQP: No room for InfoID %u payload",
+			   infoid);
+		return;
+	}
+
+	wpabuf_put_le16(buf, infoid);
+	wpabuf_put_le16(buf, wpabuf_len(elem->payload));
+	wpabuf_put_buf(buf, elem->payload);
+}
+
+
+static int anqp_add_override(struct hostapd_data *hapd, struct wpabuf *buf,
+			     u16 infoid)
+{
+	if (get_anqp_elem(hapd, infoid)) {
+		anqp_add_elem(hapd, buf, infoid);
+		return 1;
+	}
+
+	return 0;
+}
+
+
 static void anqp_add_capab_list(struct hostapd_data *hapd,
 				struct wpabuf *buf)
 {
 	u8 *len;
+	u16 id;
+
+	if (anqp_add_override(hapd, buf, ANQP_CAPABILITY_LIST))
+		return;
 
 	len = gas_anqp_add_element(buf, ANQP_CAPABILITY_LIST);
 	wpabuf_put_le16(buf, ANQP_CAPABILITY_LIST);
-	if (hapd->conf->venue_name)
+	if (hapd->conf->venue_name || get_anqp_elem(hapd, ANQP_VENUE_NAME))
 		wpabuf_put_le16(buf, ANQP_VENUE_NAME);
-	if (hapd->conf->network_auth_type)
+	if (get_anqp_elem(hapd, ANQP_EMERGENCY_CALL_NUMBER))
+		wpabuf_put_le16(buf, ANQP_EMERGENCY_CALL_NUMBER);
+	if (hapd->conf->network_auth_type ||
+	    get_anqp_elem(hapd, ANQP_NETWORK_AUTH_TYPE))
 		wpabuf_put_le16(buf, ANQP_NETWORK_AUTH_TYPE);
-	if (hapd->conf->roaming_consortium)
+	if (hapd->conf->roaming_consortium ||
+	    get_anqp_elem(hapd, ANQP_ROAMING_CONSORTIUM))
 		wpabuf_put_le16(buf, ANQP_ROAMING_CONSORTIUM);
-	if (hapd->conf->ipaddr_type_configured)
+	if (hapd->conf->ipaddr_type_configured ||
+	    get_anqp_elem(hapd, ANQP_IP_ADDR_TYPE_AVAILABILITY))
 		wpabuf_put_le16(buf, ANQP_IP_ADDR_TYPE_AVAILABILITY);
-	if (hapd->conf->nai_realm_data)
+	if (hapd->conf->nai_realm_data ||
+	    get_anqp_elem(hapd, ANQP_NAI_REALM))
 		wpabuf_put_le16(buf, ANQP_NAI_REALM);
-	if (hapd->conf->anqp_3gpp_cell_net)
+	if (hapd->conf->anqp_3gpp_cell_net ||
+	    get_anqp_elem(hapd, ANQP_3GPP_CELLULAR_NETWORK))
 		wpabuf_put_le16(buf, ANQP_3GPP_CELLULAR_NETWORK);
-	if (hapd->conf->domain_name)
+	if (get_anqp_elem(hapd, ANQP_AP_GEOSPATIAL_LOCATION))
+		wpabuf_put_le16(buf, ANQP_AP_GEOSPATIAL_LOCATION);
+	if (get_anqp_elem(hapd, ANQP_AP_CIVIC_LOCATION))
+		wpabuf_put_le16(buf, ANQP_AP_CIVIC_LOCATION);
+	if (get_anqp_elem(hapd, ANQP_AP_LOCATION_PUBLIC_URI))
+		wpabuf_put_le16(buf, ANQP_AP_LOCATION_PUBLIC_URI);
+	if (hapd->conf->domain_name || get_anqp_elem(hapd, ANQP_DOMAIN_NAME))
 		wpabuf_put_le16(buf, ANQP_DOMAIN_NAME);
+	if (get_anqp_elem(hapd, ANQP_EMERGENCY_ALERT_URI))
+		wpabuf_put_le16(buf, ANQP_EMERGENCY_ALERT_URI);
+	if (get_anqp_elem(hapd, ANQP_EMERGENCY_NAI))
+		wpabuf_put_le16(buf, ANQP_EMERGENCY_NAI);
+	if (get_anqp_elem(hapd, ANQP_NEIGHBOR_REPORT))
+		wpabuf_put_le16(buf, ANQP_NEIGHBOR_REPORT);
+	for (id = 273; id < 277; id++) {
+		if (get_anqp_elem(hapd, id))
+			wpabuf_put_le16(buf, id);
+	}
+	if (get_anqp_elem(hapd, ANQP_VENUE_URL))
+		wpabuf_put_le16(buf, ANQP_VENUE_URL);
+	if (get_anqp_elem(hapd, ANQP_ADVICE_OF_CHARGE))
+		wpabuf_put_le16(buf, ANQP_ADVICE_OF_CHARGE);
+	if (get_anqp_elem(hapd, ANQP_LOCAL_CONTENT))
+		wpabuf_put_le16(buf, ANQP_LOCAL_CONTENT);
 #ifdef CONFIG_HS20
 	anqp_add_hs_capab_list(hapd, buf);
 #endif /* CONFIG_HS20 */
@@ -197,6 +278,9 @@
 
 static void anqp_add_venue_name(struct hostapd_data *hapd, struct wpabuf *buf)
 {
+	if (anqp_add_override(hapd, buf, ANQP_VENUE_NAME))
+		return;
+
 	if (hapd->conf->venue_name) {
 		u8 *len;
 		unsigned int i;
@@ -218,6 +302,9 @@
 static void anqp_add_network_auth_type(struct hostapd_data *hapd,
 				       struct wpabuf *buf)
 {
+	if (anqp_add_override(hapd, buf, ANQP_NETWORK_AUTH_TYPE))
+		return;
+
 	if (hapd->conf->network_auth_type) {
 		wpabuf_put_le16(buf, ANQP_NETWORK_AUTH_TYPE);
 		wpabuf_put_le16(buf, hapd->conf->network_auth_type_len);
@@ -233,6 +320,9 @@
 	unsigned int i;
 	u8 *len;
 
+	if (anqp_add_override(hapd, buf, ANQP_ROAMING_CONSORTIUM))
+		return;
+
 	len = gas_anqp_add_element(buf, ANQP_ROAMING_CONSORTIUM);
 	for (i = 0; i < hapd->conf->roaming_consortium_count; i++) {
 		struct hostapd_roaming_consortium *rc;
@@ -247,6 +337,9 @@
 static void anqp_add_ip_addr_type_availability(struct hostapd_data *hapd,
 					       struct wpabuf *buf)
 {
+	if (anqp_add_override(hapd, buf, ANQP_IP_ADDR_TYPE_AVAILABILITY))
+		return;
+
 	if (hapd->conf->ipaddr_type_configured) {
 		wpabuf_put_le16(buf, ANQP_IP_ADDR_TYPE_AVAILABILITY);
 		wpabuf_put_le16(buf, 1);
@@ -309,7 +402,7 @@
 
 	pos = home_realm;
 	end = pos + home_realm_len;
-	if (pos + 1 > end) {
+	if (end - pos < 1) {
 		wpa_hexdump(MSG_DEBUG, "Too short NAI Home Realm Query",
 			    home_realm, home_realm_len);
 		return -1;
@@ -317,7 +410,7 @@
 	num_realms = *pos++;
 
 	for (i = 0; i < num_realms && num_matching < 10; i++) {
-		if (pos + 2 > end) {
+		if (end - pos < 2) {
 			wpa_hexdump(MSG_DEBUG,
 				    "Truncated NAI Home Realm Query",
 				    home_realm, home_realm_len);
@@ -325,7 +418,7 @@
 		}
 		encoding = *pos++;
 		realm_len = *pos++;
-		if (pos + realm_len > end) {
+		if (realm_len > end - pos) {
 			wpa_hexdump(MSG_DEBUG,
 				    "Truncated NAI Home Realm Query",
 				    home_realm, home_realm_len);
@@ -391,6 +484,10 @@
 			       const u8 *home_realm, size_t home_realm_len,
 			       int nai_realm, int nai_home_realm)
 {
+	if (nai_realm && !nai_home_realm &&
+	    anqp_add_override(hapd, buf, ANQP_NAI_REALM))
+		return;
+
 	if (nai_realm && hapd->conf->nai_realm_data) {
 		u8 *len;
 		unsigned int i, j;
@@ -424,6 +521,9 @@
 static void anqp_add_3gpp_cellular_network(struct hostapd_data *hapd,
 					   struct wpabuf *buf)
 {
+	if (anqp_add_override(hapd, buf, ANQP_3GPP_CELLULAR_NETWORK))
+		return;
+
 	if (hapd->conf->anqp_3gpp_cell_net) {
 		wpabuf_put_le16(buf, ANQP_3GPP_CELLULAR_NETWORK);
 		wpabuf_put_le16(buf,
@@ -436,6 +536,9 @@
 
 static void anqp_add_domain_name(struct hostapd_data *hapd, struct wpabuf *buf)
 {
+	if (anqp_add_override(hapd, buf, ANQP_DOMAIN_NAME))
+		return;
+
 	if (hapd->conf->domain_name) {
 		wpabuf_put_le16(buf, ANQP_DOMAIN_NAME);
 		wpabuf_put_le16(buf, hapd->conf->domain_name_len);
@@ -683,20 +786,42 @@
 #endif /* CONFIG_HS20 */
 
 
+static size_t anqp_get_required_len(struct hostapd_data *hapd,
+				    const u16 *infoid,
+				    unsigned int num_infoid)
+{
+	size_t len = 0;
+	unsigned int i;
+
+	for (i = 0; i < num_infoid; i++) {
+		struct anqp_element *elem = get_anqp_elem(hapd, infoid[i]);
+
+		if (elem)
+			len += 2 + 2 + wpabuf_len(elem->payload);
+	}
+
+	return len;
+}
+
+
 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)
+				const u8 *icon_name, size_t icon_name_len,
+				const u16 *extra_req,
+				unsigned int num_extra_req)
 {
 	struct wpabuf *buf;
 	size_t len;
+	unsigned int i;
 
 	len = 1400;
 	if (request & (ANQP_REQ_NAI_REALM | ANQP_REQ_NAI_HOME_REALM))
 		len += 1000;
 	if (request & ANQP_REQ_ICON_REQUEST)
 		len += 65536;
+	len += anqp_get_required_len(hapd, extra_req, num_extra_req);
 
 	buf = wpabuf_alloc(len);
 	if (buf == NULL)
@@ -706,6 +831,8 @@
 		anqp_add_capab_list(hapd, buf);
 	if (request & ANQP_REQ_VENUE_NAME)
 		anqp_add_venue_name(hapd, buf);
+	if (request & ANQP_REQ_EMERGENCY_CALL_NUMBER)
+		anqp_add_elem(hapd, buf, ANQP_EMERGENCY_CALL_NUMBER);
 	if (request & ANQP_REQ_NETWORK_AUTH_TYPE)
 		anqp_add_network_auth_type(hapd, buf);
 	if (request & ANQP_REQ_ROAMING_CONSORTIUM)
@@ -718,8 +845,23 @@
 				   request & ANQP_REQ_NAI_HOME_REALM);
 	if (request & ANQP_REQ_3GPP_CELLULAR_NETWORK)
 		anqp_add_3gpp_cellular_network(hapd, buf);
+	if (request & ANQP_REQ_AP_GEOSPATIAL_LOCATION)
+		anqp_add_elem(hapd, buf, ANQP_AP_GEOSPATIAL_LOCATION);
+	if (request & ANQP_REQ_AP_CIVIC_LOCATION)
+		anqp_add_elem(hapd, buf, ANQP_AP_CIVIC_LOCATION);
+	if (request & ANQP_REQ_AP_LOCATION_PUBLIC_URI)
+		anqp_add_elem(hapd, buf, ANQP_AP_LOCATION_PUBLIC_URI);
 	if (request & ANQP_REQ_DOMAIN_NAME)
 		anqp_add_domain_name(hapd, buf);
+	if (request & ANQP_REQ_EMERGENCY_ALERT_URI)
+		anqp_add_elem(hapd, buf, ANQP_EMERGENCY_ALERT_URI);
+	if (request & ANQP_REQ_TDLS_CAPABILITY)
+		anqp_add_elem(hapd, buf, ANQP_TDLS_CAPABILITY);
+	if (request & ANQP_REQ_EMERGENCY_NAI)
+		anqp_add_elem(hapd, buf, ANQP_EMERGENCY_NAI);
+
+	for (i = 0; i < num_extra_req; i++)
+		anqp_add_elem(hapd, buf, extra_req[i]);
 
 #ifdef CONFIG_HS20
 	if (request & ANQP_REQ_HS_CAPABILITY_LIST)
@@ -742,6 +884,8 @@
 }
 
 
+#define ANQP_MAX_EXTRA_REQ 20
+
 struct anqp_query_info {
 	unsigned int request;
 	const u8 *home_realm_query;
@@ -749,6 +893,8 @@
 	const u8 *icon_name;
 	size_t icon_name_len;
 	int p2p_sd;
+	u16 extra_req[ANQP_MAX_EXTRA_REQ];
+	unsigned int num_extra_req;
 };
 
 
@@ -776,6 +922,11 @@
 		set_anqp_req(ANQP_REQ_VENUE_NAME, "Venue Name",
 			     hapd->conf->venue_name != NULL, qi);
 		break;
+	case ANQP_EMERGENCY_CALL_NUMBER:
+		set_anqp_req(ANQP_REQ_EMERGENCY_CALL_NUMBER,
+			     "Emergency Call Number",
+			     get_anqp_elem(hapd, info_id) != 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);
@@ -798,13 +949,55 @@
 			     "3GPP Cellular Network",
 			     hapd->conf->anqp_3gpp_cell_net != NULL, qi);
 		break;
+	case ANQP_AP_GEOSPATIAL_LOCATION:
+		set_anqp_req(ANQP_REQ_AP_GEOSPATIAL_LOCATION,
+			     "AP Geospatial Location",
+			     get_anqp_elem(hapd, info_id) != NULL, qi);
+		break;
+	case ANQP_AP_CIVIC_LOCATION:
+		set_anqp_req(ANQP_REQ_AP_CIVIC_LOCATION,
+			     "AP Civic Location",
+			     get_anqp_elem(hapd, info_id) != NULL, qi);
+		break;
+	case ANQP_AP_LOCATION_PUBLIC_URI:
+		set_anqp_req(ANQP_REQ_AP_LOCATION_PUBLIC_URI,
+			     "AP Location Public URI",
+			     get_anqp_elem(hapd, info_id) != NULL, qi);
+		break;
 	case ANQP_DOMAIN_NAME:
 		set_anqp_req(ANQP_REQ_DOMAIN_NAME, "Domain Name",
 			     hapd->conf->domain_name != NULL, qi);
 		break;
+	case ANQP_EMERGENCY_ALERT_URI:
+		set_anqp_req(ANQP_REQ_EMERGENCY_ALERT_URI,
+			     "Emergency Alert URI",
+			     get_anqp_elem(hapd, info_id) != NULL, qi);
+		break;
+	case ANQP_TDLS_CAPABILITY:
+		set_anqp_req(ANQP_REQ_TDLS_CAPABILITY,
+			     "TDLS Capability",
+			     get_anqp_elem(hapd, info_id) != NULL, qi);
+		break;
+	case ANQP_EMERGENCY_NAI:
+		set_anqp_req(ANQP_REQ_EMERGENCY_NAI,
+			     "Emergency NAI",
+			     get_anqp_elem(hapd, info_id) != NULL, qi);
+		break;
 	default:
-		wpa_printf(MSG_DEBUG, "ANQP: Unsupported Info Id %u",
-			   info_id);
+		if (!get_anqp_elem(hapd, info_id)) {
+			wpa_printf(MSG_DEBUG, "ANQP: Unsupported Info Id %u",
+				   info_id);
+			break;
+		}
+		if (qi->num_extra_req == ANQP_MAX_EXTRA_REQ) {
+			wpa_printf(MSG_DEBUG,
+				   "ANQP: No more room for extra requests - ignore Info Id %u",
+				   info_id);
+			break;
+		}
+		wpa_printf(MSG_DEBUG, "ANQP: Info Id %u (local)", info_id);
+		qi->extra_req[qi->num_extra_req] = info_id;
+		qi->num_extra_req++;
 		break;
 	}
 }
@@ -817,7 +1010,7 @@
 	wpa_printf(MSG_DEBUG, "ANQP: %u Info IDs requested in Query list",
 		   (unsigned int) (end - pos) / 2);
 
-	while (pos + 2 <= end) {
+	while (end - pos >= 2) {
 		rx_anqp_query_list_id(hapd, WPA_GET_LE16(pos), qi);
 		pos += 2;
 	}
@@ -906,7 +1099,7 @@
 	u32 oui;
 	u8 subtype;
 
-	if (pos + 4 > end) {
+	if (end - pos < 4) {
 		wpa_printf(MSG_DEBUG, "ANQP: Too short vendor specific ANQP "
 			   "Query element");
 		return;
@@ -942,7 +1135,7 @@
 	}
 	pos++;
 
-	if (pos + 1 >= end)
+	if (end - pos <= 1)
 		return;
 
 	subtype = *pos++;
@@ -980,7 +1173,8 @@
 	buf = gas_serv_build_gas_resp_payload(hapd, qi->request,
 					      qi->home_realm_query,
 					      qi->home_realm_query_len,
-					      qi->icon_name, qi->icon_name_len);
+					      qi->icon_name, qi->icon_name_len,
+					      qi->extra_req, qi->num_extra_req);
 	wpa_hexdump_buf(MSG_MSGDUMP, "ANQP: Locally generated ANQP responses",
 			buf);
 	if (!buf)
@@ -1069,12 +1263,12 @@
 	adv_proto = pos++;
 
 	slen = *pos++;
-	next = pos + slen;
-	if (next > end || slen < 2) {
+	if (slen > end - pos || slen < 2) {
 		wpa_msg(hapd->msg_ctx, MSG_DEBUG,
 			"GAS: Invalid IE in GAS Initial Request");
 		return;
 	}
+	next = pos + slen;
 	pos++; /* skip QueryRespLenLimit and PAME-BI */
 
 	if (*pos != ACCESS_NETWORK_QUERY_PROTOCOL) {
@@ -1101,11 +1295,11 @@
 
 	pos = next;
 	/* Query Request */
-	if (pos + 2 > end)
+	if (end - pos < 2)
 		return;
 	slen = WPA_GET_LE16(pos);
 	pos += 2;
-	if (pos + slen > end)
+	if (slen > end - pos)
 		return;
 	end = pos + slen;
 
@@ -1113,7 +1307,7 @@
 	while (pos < end) {
 		u16 info_id, elen;
 
-		if (pos + 4 > end)
+		if (end - pos < 4)
 			return;
 
 		info_id = WPA_GET_LE16(pos);
@@ -1121,7 +1315,7 @@
 		elen = WPA_GET_LE16(pos);
 		pos += 2;
 
-		if (pos + elen > end) {
+		if (elen > end - pos) {
 			wpa_printf(MSG_DEBUG, "ANQP: Invalid Query Request");
 			return;
 		}
diff --git a/src/ap/gas_serv.h b/src/ap/gas_serv.h
index 4ec3201..9051e4f 100644
--- a/src/ap/gas_serv.h
+++ b/src/ap/gas_serv.h
@@ -9,10 +9,13 @@
 #ifndef GAS_SERV_H
 #define GAS_SERV_H
 
+/* First 16 ANQP InfoIDs can be included in the optimized bitmap */
 #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_EMERGENCY_CALL_NUMBER \
+	(1 << (ANQP_EMERGENCY_CALL_NUMBER - ANQP_QUERY_LIST))
 #define ANQP_REQ_NETWORK_AUTH_TYPE \
 	(1 << (ANQP_NETWORK_AUTH_TYPE - ANQP_QUERY_LIST))
 #define ANQP_REQ_ROAMING_CONSORTIUM \
@@ -23,8 +26,24 @@
 	(1 << (ANQP_NAI_REALM - ANQP_QUERY_LIST))
 #define ANQP_REQ_3GPP_CELLULAR_NETWORK \
 	(1 << (ANQP_3GPP_CELLULAR_NETWORK - ANQP_QUERY_LIST))
+#define ANQP_REQ_AP_GEOSPATIAL_LOCATION \
+	(1 << (ANQP_AP_GEOSPATIAL_LOCATION - ANQP_QUERY_LIST))
+#define ANQP_REQ_AP_CIVIC_LOCATION \
+	(1 << (ANQP_AP_CIVIC_LOCATION - ANQP_QUERY_LIST))
+#define ANQP_REQ_AP_LOCATION_PUBLIC_URI \
+	(1 << (ANQP_AP_LOCATION_PUBLIC_URI - ANQP_QUERY_LIST))
 #define ANQP_REQ_DOMAIN_NAME \
 	(1 << (ANQP_DOMAIN_NAME - ANQP_QUERY_LIST))
+#define ANQP_REQ_EMERGENCY_ALERT_URI \
+	(1 << (ANQP_EMERGENCY_ALERT_URI - ANQP_QUERY_LIST))
+#define ANQP_REQ_TDLS_CAPABILITY \
+	(1 << (ANQP_TDLS_CAPABILITY - ANQP_QUERY_LIST))
+#define ANQP_REQ_EMERGENCY_NAI \
+	(1 << (ANQP_EMERGENCY_NAI - ANQP_QUERY_LIST))
+/*
+ * First 16 Hotspot 2.0 vendor specific ANQP-elements can be included in the
+ * optimized bitmap.
+ */
 #define ANQP_REQ_HS_CAPABILITY_LIST \
 	(0x10000 << HS20_STYPE_CAPABILITY_LIST)
 #define ANQP_REQ_OPERATOR_FRIENDLY_NAME \
diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
index 5abe5ed..ee80f4f 100644
--- a/src/ap/hostapd.c
+++ b/src/ap/hostapd.c
@@ -12,11 +12,13 @@
 #include "utils/eloop.h"
 #include "common/ieee802_11_defs.h"
 #include "common/wpa_ctrl.h"
+#include "common/hw_features_common.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"
@@ -260,6 +262,7 @@
 {
 	os_free(hapd->probereq_cb);
 	hapd->probereq_cb = NULL;
+	hapd->num_probereq_cb = 0;
 
 #ifdef CONFIG_P2P
 	wpabuf_free(hapd->p2p_beacon_ie);
@@ -354,6 +357,22 @@
 }
 
 
+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);
@@ -369,6 +388,7 @@
 	os_free(iface->basic_rates);
 	iface->basic_rates = NULL;
 	ap_list_deinit(iface);
+	sta_track_deinit(iface);
 }
 
 
@@ -492,6 +512,9 @@
 	if (hostapd_drv_none(hapd))
 		return 0;
 
+	if (iface->conf->use_driver_iface_addr)
+		return 0;
+
 	/* Generate BSSID mask that is large enough to cover the BSSIDs. */
 
 	/* Determine the bits necessary to cover the number of BSSIDs. */
@@ -501,7 +524,7 @@
 	/* Determine the bits necessary to any configured BSSIDs,
 	   if they are higher than the number of BSSIDs. */
 	for (j = 0; j < iface->conf->num_bss; j++) {
-		if (hostapd_mac_comp_empty(iface->conf->bss[j]->bssid) == 0) {
+		if (is_zero_ether_addr(iface->conf->bss[j]->bssid)) {
 			if (j)
 				auto_addr++;
 			continue;
@@ -653,7 +676,7 @@
 
 	if (attr->acct_session_id) {
 		num_attr++;
-		if (attr->acct_session_id_len != 17) {
+		if (attr->acct_session_id_len != 16) {
 			wpa_printf(MSG_DEBUG,
 				   "RADIUS DAS: Acct-Session-Id cannot match");
 			return NULL;
@@ -663,10 +686,9 @@
 		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)
+			os_snprintf(buf, sizeof(buf), "%016llX",
+				    (unsigned long long) sta->acct_session_id);
+			if (os_memcmp(attr->acct_session_id, buf, 16) != 0)
 				sta->radius_das_match = 0;
 			else
 				count++;
@@ -682,7 +704,7 @@
 
 	if (attr->acct_multi_session_id) {
 		num_attr++;
-		if (attr->acct_multi_session_id_len != 17) {
+		if (attr->acct_multi_session_id_len != 16) {
 			wpa_printf(MSG_DEBUG,
 				   "RADIUS DAS: Acct-Multi-Session-Id cannot match");
 			return NULL;
@@ -693,14 +715,14 @@
 			if (!sta->radius_das_match)
 				continue;
 			if (!sta->eapol_sm ||
-			    !sta->eapol_sm->acct_multi_session_id_hi) {
+			    !sta->eapol_sm->acct_multi_session_id) {
 				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) !=
+			os_snprintf(buf, sizeof(buf), "%016llX",
+				    (unsigned long long)
+				    sta->eapol_sm->acct_multi_session_id);
+			if (os_memcmp(attr->acct_multi_session_id, buf, 16) !=
 			    0)
 				sta->radius_das_match = 0;
 			else
@@ -886,12 +908,9 @@
 	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 {
+		u8 *addr = hapd->own_addr;
+
+		if (!is_zero_ether_addr(conf->bssid)) {
 			/* Allocate the configured BSSID. */
 			os_memcpy(hapd->own_addr, conf->bssid, ETH_ALEN);
 
@@ -903,11 +922,18 @@
 					   "the radio", conf->iface);
 				return -1;
 			}
+		} else if (hapd->iconf->use_driver_iface_addr) {
+			addr = NULL;
+		} else {
+			/* Allocate the next available BSSID. */
+			do {
+				inc_byte_array(hapd->own_addr, ETH_ALEN);
+			} while (mac_in_conf(hapd->iconf, hapd->own_addr));
 		}
 
 		hapd->interface_added = 1;
 		if (hostapd_if_add(hapd->iface->bss[0], WPA_IF_AP_BSS,
-				   conf->iface, hapd->own_addr, hapd,
+				   conf->iface, addr, hapd,
 				   &hapd->drv_priv, force_ifname, if_addr,
 				   conf->bridge[0] ? conf->bridge : NULL,
 				   first == -1)) {
@@ -916,11 +942,19 @@
 			hapd->interface_added = 0;
 			return -1;
 		}
+
+		if (!addr)
+			os_memcpy(hapd->own_addr, if_addr, ETH_ALEN);
 	}
 
 	if (conf->wmm_enabled < 0)
 		conf->wmm_enabled = hapd->iconf->ieee80211n;
 
+#ifdef CONFIG_IEEE80211R
+	if (is_zero_ether_addr(conf->r1_key_holder))
+		os_memcpy(conf->r1_key_holder, hapd->own_addr, ETH_ALEN);
+#endif /* CONFIG_IEEE80211R */
+
 #ifdef CONFIG_MESH
 	if (hapd->iface->mconf == NULL)
 		flush_old_stations = 0;
@@ -1364,15 +1398,134 @@
 }
 
 
-/**
- * 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)
+#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 */
+
+
+static int hostapd_setup_interface_complete_sync(struct hostapd_iface *iface,
+						 int err)
 {
 	struct hostapd_data *hapd = iface->bss[0];
 	size_t j;
@@ -1488,7 +1641,7 @@
 			} while (j-- > 0);
 			goto fail;
 		}
-		if (hostapd_mac_comp_empty(hapd->conf->bssid) == 0)
+		if (is_zero_ether_addr(hapd->conf->bssid))
 			prev_addr = hapd->own_addr;
 	}
 	hapd = iface->bss[0];
@@ -1496,6 +1649,7 @@
 	hostapd_tx_queue_params(iface);
 
 	ap_list_init(iface);
+	dl_list_init(&iface->sta_seen);
 
 	hostapd_set_acl(hapd);
 
@@ -1529,6 +1683,22 @@
 #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)
@@ -1545,6 +1715,12 @@
 	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;
@@ -1552,6 +1728,89 @@
 
 
 /**
+ * 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 hapd_interfaces *interfaces = iface->interfaces;
+	struct hostapd_data *hapd = iface->bss[0];
+	unsigned int i;
+	int not_ready_in_sync_ifaces = 0;
+
+	if (!iface->need_to_start_in_sync)
+		return hostapd_setup_interface_complete_sync(iface, err);
+
+	if (err) {
+		wpa_printf(MSG_ERROR, "Interface initialization failed");
+		hostapd_set_state(iface, HAPD_IFACE_DISABLED);
+		iface->need_to_start_in_sync = 0;
+		wpa_msg(hapd->msg_ctx, MSG_INFO, AP_EVENT_DISABLED);
+		if (interfaces && interfaces->terminate_on_error)
+			eloop_terminate();
+		return -1;
+	}
+
+	if (iface->ready_to_start_in_sync) {
+		/* Already in ready and waiting. should never happpen */
+		return 0;
+	}
+
+	for (i = 0; i < interfaces->count; i++) {
+		if (interfaces->iface[i]->need_to_start_in_sync &&
+		    !interfaces->iface[i]->ready_to_start_in_sync)
+			not_ready_in_sync_ifaces++;
+	}
+
+	/*
+	 * Check if this is the last interface, if yes then start all the other
+	 * waiting interfaces. If not, add this interface to the waiting list.
+	 */
+	if (not_ready_in_sync_ifaces > 1 && iface->state == HAPD_IFACE_DFS) {
+		/*
+		 * If this interface went through CAC, do not synchronize, just
+		 * start immediately.
+		 */
+		iface->need_to_start_in_sync = 0;
+		wpa_printf(MSG_INFO,
+			   "%s: Finished CAC - bypass sync and start interface",
+			   iface->bss[0]->conf->iface);
+		return hostapd_setup_interface_complete_sync(iface, err);
+	}
+
+	if (not_ready_in_sync_ifaces > 1) {
+		/* need to wait as there are other interfaces still coming up */
+		iface->ready_to_start_in_sync = 1;
+		wpa_printf(MSG_INFO,
+			   "%s: Interface waiting to sync with other interfaces",
+			   iface->bss[0]->conf->iface);
+		return 0;
+	}
+
+	wpa_printf(MSG_INFO,
+		   "%s: Last interface to sync - starting all interfaces",
+		   iface->bss[0]->conf->iface);
+	iface->need_to_start_in_sync = 0;
+	hostapd_setup_interface_complete_sync(iface, err);
+	for (i = 0; i < interfaces->count; i++) {
+		if (interfaces->iface[i]->need_to_start_in_sync &&
+		    interfaces->iface[i]->ready_to_start_in_sync) {
+			hostapd_setup_interface_complete_sync(
+				interfaces->iface[i], 0);
+			/* Only once the interfaces are sync started */
+			interfaces->iface[i]->need_to_start_in_sync = 0;
+		}
+	}
+
+	return 0;
+}
+
+
+/**
  * hostapd_setup_interface - Setup of an interface
  * @iface: Pointer to interface data.
  * Returns: 0 on success, -1 on failure
@@ -1610,6 +1869,7 @@
 	hapd->iface = hapd_iface;
 	hapd->driver = hapd->iconf->driver;
 	hapd->ctrl_sock = -1;
+	dl_list_init(&hapd->ctrl_dst);
 
 	return hapd;
 }
@@ -1644,6 +1904,13 @@
 	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]);
 }
@@ -2030,7 +2297,7 @@
 
 static struct hostapd_config *
 hostapd_config_alloc(struct hapd_interfaces *interfaces, const char *ifname,
-		     const char *ctrl_iface)
+		     const char *ctrl_iface, const char *driver)
 {
 	struct hostapd_bss_config *bss;
 	struct hostapd_config *conf;
@@ -2043,6 +2310,21 @@
 		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!");
@@ -2050,6 +2332,7 @@
 		return NULL;
 	}
 
+skip:
 	bss = conf->last_bss = conf->bss[0];
 
 	os_strlcpy(bss->iface, ifname, sizeof(bss->iface));
@@ -2210,8 +2493,14 @@
 		if (conf && conf->bss)
 			os_strlcpy(conf->bss[0]->iface, buf,
 				   sizeof(conf->bss[0]->iface));
-	} else
-		conf = hostapd_config_alloc(interfaces, buf, ptr);
+	} 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__);
@@ -2360,6 +2649,7 @@
 	}
 
 	hostapd_prune_associations(hapd, sta->addr);
+	ap_sta_clear_disconnect_timeouts(hapd, sta);
 
 	/* IEEE 802.11F (IAPP) */
 	if (hapd->conf->ieee802_11f)
@@ -2393,9 +2683,10 @@
 		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),
+		wpa_printf(MSG_DEBUG,
+			   "%s: %s: reschedule ap_handle_timer timeout for "
+			   MACSTR " (%d seconds - ap_max_inactivity)",
+			   hapd->conf->iface, __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,
@@ -2436,6 +2727,17 @@
 }
 
 
+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;
+}
+
+
 #ifdef NEED_AP_MLME
 
 static void free_beacon_data(struct beacon_data *beacon)
@@ -2547,9 +2849,9 @@
 
 
 /*
- * 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.
+ * TODO: This flow currently supports only changing channel and width within
+ * the same hw_mode. Any other changes to MAC parameters or provided settings
+ * are not supported.
  */
 static int hostapd_change_config_freq(struct hostapd_data *hapd,
 				      struct hostapd_config *conf,
@@ -2568,15 +2870,44 @@
 		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;
+	if (old_params &&
+	    hostapd_set_freq_params(old_params, conf->hw_mode,
+				    hostapd_hw_get_freq(hapd, conf->channel),
+				    conf->channel, conf->ieee80211n,
+				    conf->ieee80211ac,
+				    conf->secondary_channel,
+				    conf->vht_oper_chwidth,
+				    conf->vht_oper_centr_freq_seg0_idx,
+				    conf->vht_oper_centr_freq_seg1_idx,
+				    conf->vht_capab))
+		return -1;
+
+	switch (params->bandwidth) {
+	case 0:
+	case 20:
+	case 40:
+		conf->vht_oper_chwidth = VHT_CHANWIDTH_USE_HT;
+		break;
+	case 80:
+		if (params->center_freq2)
+			conf->vht_oper_chwidth = VHT_CHANWIDTH_80P80MHZ;
+		else
+			conf->vht_oper_chwidth = VHT_CHANWIDTH_80MHZ;
+		break;
+	case 160:
+		conf->vht_oper_chwidth = VHT_CHANWIDTH_160MHZ;
+		break;
+	default:
+		return -1;
 	}
 
 	conf->channel = channel;
 	conf->ieee80211n = params->ht_enabled;
 	conf->secondary_channel = params->sec_channel_offset;
+	ieee80211_freq_to_chan(params->center_freq1,
+			       &conf->vht_oper_centr_freq_seg0_idx);
+	ieee80211_freq_to_chan(params->center_freq2,
+			       &conf->vht_oper_centr_freq_seg1_idx);
 
 	/* TODO: maybe call here hostapd_config_check here? */
 
@@ -2590,11 +2921,43 @@
 	struct hostapd_iface *iface = hapd->iface;
 	struct hostapd_freq_params old_freq;
 	int ret;
+	u8 chan, vht_bandwidth;
 
 	os_memset(&old_freq, 0, sizeof(old_freq));
 	if (!iface || !iface->freq || hapd->csa_in_progress)
 		return -1;
 
+	switch (settings->freq_params.bandwidth) {
+	case 80:
+		if (settings->freq_params.center_freq2)
+			vht_bandwidth = VHT_CHANWIDTH_80P80MHZ;
+		else
+			vht_bandwidth = VHT_CHANWIDTH_80MHZ;
+		break;
+	case 160:
+		vht_bandwidth = VHT_CHANWIDTH_160MHZ;
+		break;
+	default:
+		vht_bandwidth = VHT_CHANWIDTH_USE_HT;
+		break;
+	}
+
+	if (ieee80211_freq_to_channel_ext(
+		    settings->freq_params.freq,
+		    settings->freq_params.sec_channel_offset,
+		    vht_bandwidth,
+		    &hapd->iface->cs_oper_class,
+		    &chan) == NUM_HOSTAPD_MODES) {
+		wpa_printf(MSG_DEBUG,
+			   "invalid frequency for channel switch (freq=%d, sec_channel_offset=%d, vht_enabled=%d)",
+			   settings->freq_params.freq,
+			   settings->freq_params.sec_channel_offset,
+			   settings->freq_params.vht_enabled);
+		return -1;
+	}
+
+	settings->freq_params.channel = chan;
+
 	ret = hostapd_change_config_freq(iface->bss[0], iface->conf,
 					 &settings->freq_params,
 					 &old_freq);
@@ -2621,8 +2984,10 @@
 		return ret;
 	}
 
-	settings->counter_offset_beacon = hapd->cs_c_off_beacon;
-	settings->counter_offset_presp = hapd->cs_c_off_proberesp;
+	settings->counter_offset_beacon[0] = hapd->cs_c_off_beacon;
+	settings->counter_offset_presp[0] = hapd->cs_c_off_proberesp;
+	settings->counter_offset_beacon[1] = hapd->cs_c_off_ecsa_beacon;
+	settings->counter_offset_presp[1] = hapd->cs_c_off_ecsa_proberesp;
 
 	return 0;
 }
@@ -2636,6 +3001,8 @@
 	hapd->cs_c_off_beacon = 0;
 	hapd->cs_c_off_proberesp = 0;
 	hapd->csa_in_progress = 0;
+	hapd->cs_c_off_ecsa_beacon = 0;
+	hapd->cs_c_off_ecsa_proberesp = 0;
 }
 
 
@@ -2724,3 +3091,42 @@
 }
 
 #endif /* NEED_AP_MLME */
+
+
+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;
+}
+
+
+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/src/ap/hostapd.h b/src/ap/hostapd.h
index dc71694..75a7c04 100644
--- a/src/ap/hostapd.h
+++ b/src/ap/hostapd.h
@@ -41,7 +41,7 @@
 
 	size_t count;
 	int global_ctrl_sock;
-	struct wpa_ctrl_dst *global_ctrl_dst;
+	struct dl_list global_ctrl_dst;
 	char *global_iface_path;
 	char *global_iface_name;
 #ifndef CONFIG_NATIVE_WINDOWS
@@ -138,7 +138,7 @@
 	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;
+	u64 acct_session_id;
 	struct radius_das_data *radius_das;
 
 	struct iapp_data *iapp;
@@ -155,7 +155,7 @@
 	int tkip_countermeasures;
 
 	int ctrl_sock;
-	struct wpa_ctrl_dst *ctrl_dst;
+	struct dl_list ctrl_dst;
 
 	void *ssl_ctx;
 	void *eap_sim_db_priv;
@@ -228,6 +228,8 @@
 	unsigned int cs_c_off_beacon;
 	unsigned int cs_c_off_proberesp;
 	int csa_in_progress;
+	unsigned int cs_c_off_ecsa_beacon;
+	unsigned int cs_c_off_ecsa_proberesp;
 
 	/* BSS Load */
 	unsigned int bss_load_update_timeout;
@@ -256,9 +258,11 @@
 #ifdef CONFIG_MESH
 	int num_plinks;
 	int max_plinks;
-	void (*mesh_sta_free_cb)(struct sta_info *sta);
+	void (*mesh_sta_free_cb)(struct hostapd_data *hapd,
+				 struct sta_info *sta);
 	struct wpabuf *mesh_pending_auth;
 	struct os_reltime mesh_pending_auth_time;
+	u8 mesh_required_peer[ETH_ALEN];
 #endif /* CONFIG_MESH */
 
 #ifdef CONFIG_SQLITE
@@ -269,6 +273,7 @@
 	/** 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
@@ -277,9 +282,19 @@
 
 	struct l2_packet_data *l2_test;
 #endif /* CONFIG_TESTING_OPTIONS */
+
+#ifdef CONFIG_MBO
+	unsigned int mbo_assoc_disallow;
+#endif /* CONFIG_MBO */
 };
 
 
+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
  */
@@ -309,6 +324,10 @@
 
 	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
@@ -316,6 +335,15 @@
 	 */
 	unsigned int driver_ap_teardown:1;
 
+	/*
+	 * When set, indicates that this interface is part of list of
+	 * interfaces that need to be started together (synchronously).
+	 */
+	unsigned int need_to_start_in_sync:1;
+
+	/* Ready to start but waiting for other interfaces to become ready. */
+	unsigned int ready_to_start_in_sync: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];
@@ -391,6 +419,9 @@
 	u64 last_channel_time_busy;
 	u8 channel_utilization;
 
+	/* eCSA IE will be added only if operating class is specified */
+	u8 cs_oper_class;
+
 	unsigned int dfs_cac_ms;
 	struct os_reltime dfs_cac_start;
 
@@ -404,6 +435,9 @@
 
 	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 */
@@ -435,12 +469,14 @@
 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_csa_in_progress(struct hostapd_iface *iface);
 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,
@@ -463,9 +499,22 @@
 			 int ssi_signal);
 void hostapd_event_ch_switch(struct hostapd_data *hapd, int freq, int ht,
 			     int offset, int width, int cf1, int cf2);
+struct survey_results;
+void hostapd_event_get_survey(struct hostapd_iface *iface,
+			      struct survey_results *survey_results);
+void hostapd_acs_channel_selected(struct hostapd_data *hapd,
+				  struct acs_selected_channels *acs_res);
 
 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/src/ap/hw_features.c b/src/ap/hw_features.c
index 28324a8..16887ac 100644
--- a/src/ap/hw_features.c
+++ b/src/ap/hw_features.c
@@ -260,8 +260,14 @@
 
 	res = check_40mhz_5g(iface->current_mode, scan_res, pri_chan, sec_chan);
 
-	if (res == 2)
-		ieee80211n_switch_pri_sec(iface);
+	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;
 }
@@ -323,6 +329,7 @@
 	res = ieee80211n_allowed_ht40_channel_pair(iface);
 	if (!res) {
 		iface->conf->secondary_channel = 0;
+		res = 1;
 		wpa_printf(MSG_INFO, "Fallback to 20 MHz");
 	}
 
@@ -466,8 +473,9 @@
 	struct wpa_driver_scan_params params;
 	int ret;
 
-	if (!iface->conf->secondary_channel)
-		return 0; /* HT40 not used */
+	/* Check that HT40 is used and PRI / SEC switch is allowed */
+	if (!iface->conf->secondary_channel || iface->conf->no_pri_sec_switch)
+		return 0;
 
 	hostapd_set_state(iface, HAPD_IFACE_HT_SCAN);
 	wpa_printf(MSG_DEBUG, "Scan for neighboring BSSes prior to enabling "
@@ -726,6 +734,15 @@
 	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
diff --git a/src/ap/iapp.c b/src/ap/iapp.c
index 99aa04d..07672ce 100644
--- a/src/ap/iapp.c
+++ b/src/ap/iapp.c
@@ -34,11 +34,7 @@
 #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"
diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
index db20c86..6a373c5 100644
--- a/src/ap/ieee802_11.c
+++ b/src/ap/ieee802_11.c
@@ -23,6 +23,7 @@
 #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"
@@ -38,8 +39,10 @@
 #include "p2p_hostapd.h"
 #include "ap_drv_ops.h"
 #include "wnm_ap.h"
+#include "hw_features.h"
 #include "ieee802_11.h"
 #include "dfs.h"
+#include "mbo_ap.h"
 
 
 u8 * hostapd_eid_supp_rates(struct hostapd_data *hapd, u8 *eid)
@@ -191,6 +194,7 @@
 }
 
 
+#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)
@@ -204,16 +208,17 @@
 		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);
+			if (os_get_random(key, sizeof(key)) < 0) {
+				os_free(sta->challenge);
+				sta->challenge = NULL;
+				return WLAN_STATUS_UNSPECIFIED_FAILURE;
+			}
+
 			rc4_skip(key, sizeof(key), 0,
 				 sta->challenge, WLAN_AUTH_CHALLENGE_LEN);
 		}
@@ -244,21 +249,23 @@
 
 	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)
+static int 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;
+	int reply_res = WLAN_STATUS_UNSPECIFIED_FAILURE;
 
 	rlen = IEEE80211_HDRLEN + sizeof(reply->u.auth) + ies_len;
 	buf = os_zalloc(rlen);
 	if (buf == NULL)
-		return;
+		return -1;
 
 	reply = (struct ieee80211_mgmt *) buf;
 	reply->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
@@ -279,9 +286,13 @@
 		   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");
+		wpa_printf(MSG_INFO, "send_auth_reply: send failed");
+	else
+		reply_res = WLAN_STATUS_SUCCESS;
 
 	os_free(buf);
+
+	return reply_res;
 }
 
 
@@ -292,17 +303,25 @@
 {
 	struct hostapd_data *hapd = ctx;
 	struct sta_info *sta;
+	int reply_res;
 
-	send_auth_reply(hapd, dst, bssid, WLAN_AUTH_FT, auth_transaction,
-			status, ies, ies_len);
-
-	if (status != WLAN_STATUS_SUCCESS)
-		return;
+	reply_res = send_auth_reply(hapd, dst, bssid, WLAN_AUTH_FT,
+				    auth_transaction, status, ies, ies_len);
 
 	sta = ap_get_sta(hapd, dst);
 	if (sta == NULL)
 		return;
 
+	if (sta->added_unassoc && (reply_res != WLAN_STATUS_SUCCESS ||
+				   status != WLAN_STATUS_SUCCESS)) {
+		hostapd_drv_sta_remove(hapd, sta->addr);
+		sta->added_unassoc = 0;
+		return;
+	}
+
+	if (status != WLAN_STATUS_SUCCESS)
+		return;
+
 	hostapd_logger(hapd, dst, HOSTAPD_MODULE_IEEE80211,
 		       HOSTAPD_LEVEL_DEBUG, "authentication OK (FT)");
 	sta->flags |= WLAN_STA_AUTH;
@@ -313,7 +332,6 @@
 
 #ifdef CONFIG_SAE
 
-#define dot11RSNASAERetransPeriod 40	/* msec */
 #define dot11RSNASAESync 5		/* attempts */
 
 
@@ -366,18 +384,19 @@
 				const u8 *bssid, int update)
 {
 	struct wpabuf *data;
+	int reply_res;
 
 	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));
+	reply_res = 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;
+	return reply_res;
 }
 
 
@@ -386,18 +405,19 @@
 				 const u8 *bssid)
 {
 	struct wpabuf *data;
+	int reply_res;
 
 	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));
+	reply_res = 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;
+	return reply_res;
 }
 
 
@@ -496,12 +516,14 @@
 	switch (sta->sae->state) {
 	case SAE_COMMITTED:
 		ret = auth_sae_send_commit(hapd, sta, hapd->own_addr, 0);
-		eloop_register_timeout(0, dot11RSNASAERetransPeriod * 1000,
+		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, dot11RSNASAERetransPeriod * 1000,
+		eloop_register_timeout(0,
+				       hapd->dot11RSNASAERetransPeriod * 1000,
 				       auth_sae_retransmit_timer, hapd, sta);
 		break;
 	default:
@@ -527,11 +549,23 @@
 		return;
 
 	eloop_cancel_timeout(auth_sae_retransmit_timer, hapd, sta);
-	eloop_register_timeout(0, dot11RSNASAERetransPeriod * 1000,
+	eloop_register_timeout(0, hapd->dot11RSNASAERetransPeriod * 1000,
 			       auth_sae_retransmit_timer, hapd, sta);
 }
 
 
+void sae_accept_sta(struct hostapd_data *hapd, struct sta_info *sta)
+{
+	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, sta->sae->pmkid);
+}
+
+
 static int sae_sm_step(struct hostapd_data *hapd, struct sta_info *sta,
 		       const u8 *bssid, u8 auth_transaction)
 {
@@ -654,13 +688,7 @@
 
 			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);
+			sae_accept_sta(hapd, sta);
 		}
 		break;
 	case SAE_ACCEPTED:
@@ -669,6 +697,7 @@
 				   ") doing reauthentication",
 				   MAC2STR(sta->addr));
 			ap_free_sta(hapd, sta);
+			wpa_auth_pmksa_remove(hapd->wpa_auth, sta->addr);
 		} else {
 			if (sae_check_big_sync(sta))
 				return WLAN_STATUS_SUCCESS;
@@ -693,19 +722,31 @@
 			    const struct ieee80211_mgmt *mgmt, size_t len,
 			    u16 auth_transaction, u16 status_code)
 {
-	u16 resp = WLAN_STATUS_SUCCESS;
+	int resp = WLAN_STATUS_SUCCESS;
 	struct wpabuf *data = NULL;
 
 	if (!sta->sae) {
-		if (auth_transaction != 1 || status_code != WLAN_STATUS_SUCCESS)
-			return;
+		if (auth_transaction != 1 ||
+		    status_code != WLAN_STATUS_SUCCESS) {
+			resp = -1;
+			goto remove_sta;
+		}
 		sta->sae = os_zalloc(sizeof(*sta->sae));
-		if (sta->sae == NULL)
-			return;
+		if (!sta->sae) {
+			resp = -1;
+			goto remove_sta;
+		}
 		sta->sae->state = SAE_NOTHING;
 		sta->sae->sync = 0;
 	}
 
+	if (sta->mesh_sae_pmksa_caching) {
+		wpa_printf(MSG_DEBUG,
+			   "SAE: Cancel use of mesh PMKSA caching because peer starts SAE authentication");
+		wpa_auth_pmksa_remove(hapd->wpa_auth, sta->addr);
+		sta->mesh_sae_pmksa_caching = 0;
+	}
+
 	if (auth_transaction == 1) {
 		const u8 *token = NULL, *pos, *end;
 		size_t token_len = 0;
@@ -741,7 +782,8 @@
 			if (sta->sae->tmp->anti_clogging_token == NULL) {
 				wpa_printf(MSG_ERROR,
 					   "SAE: Failed to alloc for anti-clogging token");
-				return;
+				resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+				goto remove_sta;
 			}
 
 			/*
@@ -751,10 +793,11 @@
 			 * Authentication frame, and the commit-scalar and
 			 * COMMIT-ELEMENT previously sent.
 			 */
-			if (auth_sae_send_commit(hapd, sta, mgmt->bssid, 0)) {
+			resp = auth_sae_send_commit(hapd, sta, mgmt->bssid, 0);
+			if (resp != WLAN_STATUS_SUCCESS) {
 				wpa_printf(MSG_ERROR,
 					   "SAE: Failed to send commit message");
-				return;
+				goto remove_sta;
 			}
 			sta->sae->state = SAE_COMMITTED;
 			sta->sae->sync = 0;
@@ -763,7 +806,7 @@
 		}
 
 		if (status_code != WLAN_STATUS_SUCCESS)
-			return;
+			goto remove_sta;
 
 		resp = sae_parse_commit(sta->sae, mgmt->u.auth.variable,
 					((const u8 *) mgmt) + len -
@@ -773,14 +816,15 @@
 			wpa_printf(MSG_DEBUG,
 				   "SAE: Drop commit message from " MACSTR " due to reflection attack",
 				   MAC2STR(sta->addr));
-			return;
+			goto remove_sta;
 		}
 		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;
+			resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+			goto remove_sta;
 		}
 
 		if (resp != WLAN_STATUS_SUCCESS)
@@ -805,7 +849,7 @@
 			       "SAE authentication (RX confirm, status=%u)",
 			       status_code);
 		if (status_code != WLAN_STATUS_SUCCESS)
-			return;
+			goto remove_sta;
 		if (sta->sae->state >= SAE_CONFIRMED ||
 		    !(hapd->conf->mesh & MESH_ENABLED)) {
 			if (sae_check_confirm(sta->sae, mgmt->u.auth.variable,
@@ -822,7 +866,7 @@
 			       "unexpected SAE authentication transaction %u (status=%u)",
 			       auth_transaction, status_code);
 		if (status_code != WLAN_STATUS_SUCCESS)
-			return;
+			goto remove_sta;
 		resp = WLAN_STATUS_UNKNOWN_AUTH_TRANSACTION;
 	}
 
@@ -833,6 +877,13 @@
 				data ? wpabuf_head(data) : (u8 *) "",
 				data ? wpabuf_len(data) : 0);
 	}
+
+remove_sta:
+	if (sta->added_unassoc && (resp != WLAN_STATUS_SUCCESS ||
+				   status_code != WLAN_STATUS_SUCCESS)) {
+		hostapd_drv_sta_remove(hapd, sta->addr);
+		sta->added_unassoc = 0;
+	}
 	wpabuf_free(data);
 }
 
@@ -877,11 +928,11 @@
 	u16 auth_alg, auth_transaction, status_code;
 	u16 resp = WLAN_STATUS_SUCCESS;
 	struct sta_info *sta = NULL;
-	int res;
+	int res, reply_res;
 	u16 fc;
 	const u8 *challenge = NULL;
 	u32 session_timeout, acct_interim_interval;
-	int vlan_id = 0;
+	struct vlan_description vlan_id;
 	struct hostapd_sta_wpa_psk_short *psk = NULL;
 	u8 resp_ies[2 + WLAN_AUTH_CHALLENGE_LEN];
 	size_t resp_ies_len = 0;
@@ -889,6 +940,8 @@
 	char *radius_cui = NULL;
 	u16 seq_ctrl;
 
+	os_memset(&vlan_id, 0, sizeof(vlan_id));
+
 	if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.auth)) {
 		wpa_printf(MSG_INFO, "handle_auth - too short payload (len=%lu)",
 			   (unsigned long) len);
@@ -925,6 +978,16 @@
 		   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;
@@ -963,6 +1026,61 @@
 		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,
@@ -997,13 +1115,22 @@
 				       seq_ctrl);
 			return;
 		}
+#ifdef CONFIG_MESH
+		if ((hapd->conf->mesh & MESH_ENABLED) &&
+		    sta->plink_state == PLINK_BLOCKED) {
+			wpa_printf(MSG_DEBUG, "Mesh peer " MACSTR
+				   " is blocked - drop Authentication frame",
+				   MAC2STR(mgmt->sa));
+			return;
+		}
+#endif /* CONFIG_MESH */
 	} 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",
+				   " not yet known - drop Authentication frame",
 				   MAC2STR(mgmt->sa));
 			/*
 			 * Save a copy of the frame so that it can be processed
@@ -1025,19 +1152,23 @@
 	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;
+	if (vlan_id.notempty &&
+	    !hostapd_vlan_valid(hapd->conf->vlan, &vlan_id)) {
+		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS,
+			       HOSTAPD_LEVEL_INFO,
+			       "Invalid VLAN %d%s received from RADIUS server",
+			       vlan_id.untagged,
+			       vlan_id.tagged[0] ? "+" : "");
+		resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+		goto fail;
+	}
+	if (ap_sta_set_vlan(hapd, sta, &vlan_id) < 0) {
+		resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+		goto fail;
+	}
+	if (sta->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) {
@@ -1062,6 +1193,46 @@
 	else
 		ap_sta_no_session_timeout(hapd, sta);
 
+	/*
+	 * If the driver supports full AP client state, add a station to the
+	 * driver before sending authentication reply to make sure the driver
+	 * has resources, and not to go through the entire authentication and
+	 * association handshake, and fail it at the end.
+	 *
+	 * If this is not the first transaction, in a multi-step authentication
+	 * algorithm, the station already exists in the driver
+	 * (sta->added_unassoc = 1) so skip it.
+	 *
+	 * In mesh mode, the station was already added to the driver when the
+	 * NEW_PEER_CANDIDATE event is received.
+	 */
+	if (FULL_AP_CLIENT_STATE_SUPP(hapd->iface->drv_flags) &&
+	    !(hapd->conf->mesh & MESH_ENABLED) &&
+	    !(sta->added_unassoc)) {
+		/*
+		 * If a station that is already associated to the AP, is trying
+		 * to authenticate again, remove the STA entry, in order to make
+		 * sure the STA PS state gets cleared and configuration gets
+		 * updated. To handle this, station's added_unassoc flag is
+		 * cleared once the station has completed association.
+		 */
+		hostapd_drv_sta_remove(hapd, sta->addr);
+		sta->flags &= ~(WLAN_STA_ASSOC | WLAN_STA_AUTH |
+				WLAN_STA_AUTHORIZED);
+
+		if (hostapd_sta_add(hapd, sta->addr, 0, 0, 0, 0, 0,
+				    NULL, NULL, sta->flags, 0, 0, 0)) {
+			hostapd_logger(hapd, sta->addr,
+				       HOSTAPD_MODULE_IEEE80211,
+				       HOSTAPD_LEVEL_NOTICE,
+				       "Could not add STA to kernel driver");
+			resp = WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA;
+			goto fail;
+		}
+
+		sta->added_unassoc = 1;
+	}
+
 	switch (auth_alg) {
 	case WLAN_AUTH_OPEN:
 		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
@@ -1072,6 +1243,7 @@
 		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);
@@ -1085,6 +1257,7 @@
 			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;
@@ -1133,12 +1306,19 @@
 	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);
+	reply_res = send_auth_reply(hapd, mgmt->sa, mgmt->bssid, auth_alg,
+				    auth_transaction + 1, resp, resp_ies,
+				    resp_ies_len);
+
+	if (sta && sta->added_unassoc && (resp != WLAN_STATUS_SUCCESS ||
+					  reply_res != WLAN_STATUS_SUCCESS)) {
+		hostapd_drv_sta_remove(hapd, sta->addr);
+		sta->added_unassoc = 0;
+	}
 }
 
 
-static int hostapd_get_aid(struct hostapd_data *hapd, struct sta_info *sta)
+int hostapd_get_aid(struct hostapd_data *hapd, struct sta_info *sta)
 {
 	int i, j = 32, aid;
 
@@ -1255,6 +1435,9 @@
 	}
 #endif /* CONFIG_INTERWORKING */
 
+	if (ext_capab_ie_len > 0)
+		sta->ecsa_supported = !!(ext_capab_ie[0] & BIT(2));
+
 	return WLAN_STATUS_SUCCESS;
 }
 
@@ -1301,13 +1484,15 @@
 #endif /* CONFIG_IEEE80211N */
 
 #ifdef CONFIG_IEEE80211AC
-	resp = copy_sta_vht_capab(hapd, sta, elems.vht_capabilities);
-	if (resp != WLAN_STATUS_SUCCESS)
-		return resp;
+	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;
+		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)) {
@@ -1535,6 +1720,29 @@
 		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 */
+
+#ifdef CONFIG_MBO
+	mbo_ap_check_sta_assoc(hapd, sta, &elems);
+
+	if (hapd->conf->mbo_enabled && (hapd->conf->wpa & 2) &&
+	    elems.mbo && sta->cell_capa && !(sta->flags & WLAN_STA_MFP) &&
+	    hapd->conf->ieee80211w != NO_MGMT_FRAME_PROTECTION) {
+		wpa_printf(MSG_INFO,
+			   "MBO: Reject WPA2 association without PMF");
+		return WLAN_STATUS_UNSPECIFIED_FAILURE;
+	}
+#endif /* CONFIG_MBO */
+
+	ap_copy_sta_supp_op_classes(sta, elems.supp_op_classes,
+				    elems.supp_op_classes_len);
+
 	return WLAN_STATUS_SUCCESS;
 }
 
@@ -1561,9 +1769,65 @@
 }
 
 
-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)
+static int add_associated_sta(struct hostapd_data *hapd,
+			      struct sta_info *sta)
+{
+	struct ieee80211_ht_capabilities ht_cap;
+	struct ieee80211_vht_capabilities vht_cap;
+
+	/*
+	 * Remove the STA entry to ensure the STA PS state gets cleared and
+	 * configuration gets updated. This is relevant for cases, such as
+	 * FT-over-the-DS, where a station re-associates back to the same AP but
+	 * skips the authentication flow, or if working with a driver that
+	 * does not support full AP client state.
+	 */
+	if (!sta->added_unassoc)
+		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 */
+
+	/*
+	 * Add the station with forced WLAN_STA_ASSOC flag. The sta->flags
+	 * will be set when the ACK frame for the (Re)Association Response frame
+	 * is processed (TX status driver event).
+	 */
+	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 | WLAN_STA_ASSOC, sta->qosinfo,
+			    sta->vht_opmode, sta->added_unassoc)) {
+		hostapd_logger(hapd, sta->addr,
+			       HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_NOTICE,
+			       "Could not %s STA to kernel driver",
+			       sta->added_unassoc ? "set" : "add");
+
+		if (sta->added_unassoc) {
+			hostapd_drv_sta_remove(hapd, sta->addr);
+			sta->added_unassoc = 0;
+		}
+
+		return -1;
+	}
+
+	sta->added_unassoc = 0;
+
+	return 0;
+}
+
+
+static u16 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];
@@ -1623,6 +1887,14 @@
 	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);
@@ -1644,7 +1916,7 @@
 #endif /* CONFIG_WPS */
 
 #ifdef CONFIG_P2P
-	if (sta->p2p_ie) {
+	if (sta->p2p_ie && hapd->p2p_group) {
 		struct wpabuf *p2p_resp_ie;
 		enum p2p_status_code status;
 		switch (status_code) {
@@ -1673,11 +1945,17 @@
 		p = hostapd_eid_p2p_manage(hapd, p);
 #endif /* CONFIG_P2P_MANAGER */
 
+	p = hostapd_eid_mbo(hapd, p, buf + sizeof(buf) - p);
+
 	send_len += p - reply->u.assoc_resp.variable;
 
-	if (hostapd_drv_send_mlme(hapd, reply, send_len, 0) < 0)
+	if (hostapd_drv_send_mlme(hapd, reply, send_len, 0) < 0) {
 		wpa_printf(MSG_INFO, "Failed to send assoc resp: %s",
 			   strerror(errno));
+		return WLAN_STATUS_UNSPECIFIED_FAILURE;
+	}
+
+	return WLAN_STATUS_SUCCESS;
 }
 
 
@@ -1686,7 +1964,7 @@
 			 int reassoc)
 {
 	u16 capab_info, listen_interval, seq_ctrl, fc;
-	u16 resp = WLAN_STATUS_SUCCESS;
+	u16 resp = WLAN_STATUS_SUCCESS, reply_res;
 	const u8 *pos;
 	int left, i;
 	struct sta_info *sta;
@@ -1753,6 +2031,12 @@
 		wpa_printf(MSG_DEBUG, "FT: Allow STA " MACSTR " to associate "
 			   "prior to authentication since it is using "
 			   "over-the-DS FT", MAC2STR(mgmt->sa));
+
+		/*
+		 * Mark station as authenticated, to avoid adding station
+		 * entry in the driver as associated and not authenticated
+		 */
+		sta->flags |= WLAN_STA_AUTH;
 	} else
 #endif /* CONFIG_IEEE80211R */
 	if (sta == NULL || (sta->flags & WLAN_STA_AUTH) == 0) {
@@ -1796,6 +2080,13 @@
 		goto fail;
 	}
 
+#ifdef CONFIG_MBO
+	if (hapd->conf->mbo_enabled && hapd->mbo_assoc_disallow) {
+		resp = WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA;
+		goto fail;
+	}
+#endif /* CONFIG_MBO */
+
 	/* followed by SSID and Supported rates; and HT capabilities if 802.11n
 	 * is used */
 	resp = check_assoc_ies(hapd, sta, pos, left, reassoc);
@@ -1880,7 +2171,39 @@
 	sta->timeout_next = STA_NULLFUNC;
 
  fail:
-	send_assoc_resp(hapd, sta, resp, reassoc, pos, left);
+	/*
+	 * In case of a successful response, add the station to the driver.
+	 * Otherwise, the kernel may ignore Data frames before we process the
+	 * ACK frame (TX status). In case of a failure, this station will be
+	 * removed.
+	 *
+	 * Note that this is not compliant with the IEEE 802.11 standard that
+	 * states that a non-AP station should transition into the
+	 * authenticated/associated state only after the station acknowledges
+	 * the (Re)Association Response frame. However, still do this as:
+	 *
+	 * 1. In case the station does not acknowledge the (Re)Association
+	 *    Response frame, it will be removed.
+	 * 2. Data frames will be dropped in the kernel until the station is
+	 *    set into authorized state, and there are no significant known
+	 *    issues with processing other non-Data Class 3 frames during this
+	 *    window.
+	 */
+	if (resp == WLAN_STATUS_SUCCESS && add_associated_sta(hapd, sta))
+		resp = WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA;
+
+	reply_res = send_assoc_resp(hapd, sta, resp, reassoc, pos, left);
+
+	/*
+	 * Remove the station in case tranmission of a success response fails
+	 * (the STA was added associated to the driver) or if the station was
+	 * previously added unassociated.
+	 */
+	if ((reply_res != WLAN_STATUS_SUCCESS &&
+	     resp == WLAN_STATUS_SUCCESS) || sta->added_unassoc) {
+		hostapd_drv_sta_remove(hapd, sta->addr);
+		sta->added_unassoc = 0;
+	}
 }
 
 
@@ -1917,11 +2240,12 @@
 	/* Stop Accounting and IEEE 802.1X sessions, but leave the STA
 	 * authenticated. */
 	accounting_sta_stop(hapd, sta);
-	ieee802_1x_free_station(sta);
+	ieee802_1x_free_station(hapd, 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);
+	sta->added_unassoc = 0;
 
 	if (sta->timeout_next == STA_NULLFUNC ||
 	    sta->timeout_next == STA_DISASSOC) {
@@ -2101,6 +2425,15 @@
 		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
@@ -2238,6 +2571,9 @@
 		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");
@@ -2286,16 +2622,10 @@
 	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);
+	sta = ap_get_sta(hapd, mgmt->da);
+	if (!sta) {
+		wpa_printf(MSG_INFO, "handle_auth_cb: STA " MACSTR " not found",
+			   MAC2STR(mgmt->da));
 		return;
 	}
 
@@ -2303,11 +2633,17 @@
 	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 (!ok) {
+		hostapd_logger(hapd, mgmt->da, HOSTAPD_MODULE_IEEE80211,
+			       HOSTAPD_LEVEL_NOTICE,
+			       "did not acknowledge authentication response");
+		goto fail;
+	}
+
+	if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.auth)) {
+		wpa_printf(MSG_INFO, "handle_auth_cb - too short payload (len=%lu)",
+			   (unsigned long) len);
+		goto fail;
 	}
 
 	if (status_code == WLAN_STATUS_SUCCESS &&
@@ -2316,6 +2652,15 @@
 		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
 			       HOSTAPD_LEVEL_INFO, "authenticated");
 		sta->flags |= WLAN_STA_AUTH;
+		if (sta->added_unassoc)
+			hostapd_set_sta_flags(hapd, sta);
+		return;
+	}
+
+fail:
+	if (status_code != WLAN_STATUS_SUCCESS && sta->added_unassoc) {
+		hostapd_drv_sta_remove(hapd, sta->addr);
+		sta->added_unassoc = 0;
 	}
 }
 
@@ -2351,15 +2696,6 @@
 	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) {
@@ -2368,11 +2704,12 @@
 		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;
+	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);
+		hostapd_drv_sta_remove(hapd, sta->addr);
 		return;
 	}
 
@@ -2381,6 +2718,18 @@
 	else
 		status = le_to_host16(mgmt->u.assoc_resp.status_code);
 
+	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;
+		/* The STA is added only in case of SUCCESS */
+		if (status == WLAN_STATUS_SUCCESS)
+			hostapd_drv_sta_remove(hapd, sta->addr);
+
+		return;
+	}
+
 	if (status != WLAN_STATUS_SUCCESS)
 		return;
 
@@ -2415,38 +2764,6 @@
 	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];
@@ -2478,8 +2795,26 @@
 	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);
+
+	if (sta->pending_eapol_rx) {
+		struct os_reltime now, age;
+
+		os_get_reltime(&now);
+		os_reltime_sub(&now, &sta->pending_eapol_rx->rx_time, &age);
+		if (age.sec == 0 && age.usec < 200000) {
+			wpa_printf(MSG_DEBUG,
+				   "Process pending EAPOL frame that was received from " MACSTR " just before association notification",
+				   MAC2STR(sta->addr));
+			ieee802_1x_receive(
+				hapd, mgmt->da,
+				wpabuf_head(sta->pending_eapol_rx->buf),
+				wpabuf_len(sta->pending_eapol_rx->buf));
+		}
+		wpabuf_free(sta->pending_eapol_rx->buf);
+		os_free(sta->pending_eapol_rx);
+		sta->pending_eapol_rx = NULL;
+	}
 }
 
 
@@ -2568,7 +2903,7 @@
 		handle_assoc_cb(hapd, mgmt, len, 1, ok);
 		break;
 	case WLAN_FC_STYPE_PROBE_RESP:
-		wpa_printf(MSG_EXCESSIVE, "mgmt::proberesp cb");
+		wpa_printf(MSG_EXCESSIVE, "mgmt::proberesp cb ok=%d", ok);
 		break;
 	case WLAN_FC_STYPE_DEAUTH:
 		wpa_printf(MSG_DEBUG, "mgmt::deauth cb");
@@ -2579,7 +2914,7 @@
 		handle_disassoc_cb(hapd, mgmt, len, ok);
 		break;
 	case WLAN_FC_STYPE_ACTION:
-		wpa_printf(MSG_DEBUG, "mgmt::action cb");
+		wpa_printf(MSG_DEBUG, "mgmt::action cb ok=%d", ok);
 		break;
 	default:
 		wpa_printf(MSG_INFO, "unknown mgmt cb frame subtype %d", stype);
diff --git a/src/ap/ieee802_11.h b/src/ap/ieee802_11.h
index 44c1bff..71b3b49 100644
--- a/src/ap/ieee802_11.h
+++ b/src/ap/ieee802_11.h
@@ -49,9 +49,13 @@
 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_secondary_channel(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);
+u8 * hostapd_eid_wb_chsw_wrapper(struct hostapd_data *hapd, u8 *eid);
+u8 * hostapd_eid_txpower_envelope(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);
@@ -61,6 +65,7 @@
 void hostapd_get_vht_capab(struct hostapd_data *hapd,
 			   struct ieee80211_vht_capabilities *vht_cap,
 			   struct ieee80211_vht_capabilities *neg_vht_cap);
+int hostapd_get_aid(struct hostapd_data *hapd, struct sta_info *sta);
 u16 copy_sta_ht_capab(struct hostapd_data *hapd, struct sta_info *sta,
 		      const u8 *ht_capab);
 u16 copy_sta_vendor_vht(struct hostapd_data *hapd, struct sta_info *sta,
@@ -97,6 +102,7 @@
 #ifdef CONFIG_SAE
 void sae_clear_retransmit_timer(struct hostapd_data *hapd,
 				struct sta_info *sta);
+void sae_accept_sta(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)
@@ -104,4 +110,29 @@
 }
 #endif /* CONFIG_SAE */
 
+#ifdef CONFIG_MBO
+
+u8 * hostapd_eid_mbo(struct hostapd_data *hapd, u8 *eid, size_t len);
+
+u8 hostapd_mbo_ie_len(struct hostapd_data *hapd);
+
+#else /* CONFIG_MBO */
+
+static inline u8 * hostapd_eid_mbo(struct hostapd_data *hapd, u8 *eid,
+				   size_t len)
+{
+	return eid;
+}
+
+static inline u8 hostapd_mbo_ie_len(struct hostapd_data *hapd)
+{
+	return 0;
+}
+
+#endif /* CONFIG_MBO */
+
+void ap_copy_sta_supp_op_classes(struct sta_info *sta,
+				 const u8 *supp_op_classes,
+				 size_t supp_op_classes_len);
+
 #endif /* IEEE802_11_H */
diff --git a/src/ap/ieee802_11_auth.c b/src/ap/ieee802_11_auth.c
index 0238257..9609152 100644
--- a/src/ap/ieee802_11_auth.c
+++ b/src/ap/ieee802_11_auth.c
@@ -15,7 +15,6 @@
 
 #include "utils/common.h"
 #include "utils/eloop.h"
-#include "crypto/sha1.h"
 #include "radius/radius.h"
 #include "radius/radius_client.h"
 #include "hostapd.h"
@@ -35,7 +34,7 @@
 	struct hostapd_cached_radius_acl *next;
 	u32 session_timeout;
 	u32 acct_interim_interval;
-	int vlan_id;
+	struct vlan_description vlan_id;
 	struct hostapd_sta_wpa_psk_short *psk;
 	char *identity;
 	char *radius_cui;
@@ -77,29 +76,20 @@
 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;
+	if (!psk)
+		return;
 
-	/* 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;
+	if (src)
+		src->ref++;
+
+	*psk = src;
 }
 
 
 static int hostapd_acl_cache_get(struct hostapd_data *hapd, const u8 *addr,
 				 u32 *session_timeout,
-				 u32 *acct_interim_interval, int *vlan_id,
+				 u32 *acct_interim_interval,
+				 struct vlan_description *vlan_id,
 				 struct hostapd_sta_wpa_psk_short **psk,
 				 char **identity, char **radius_cui)
 {
@@ -165,7 +155,10 @@
 	if (msg == NULL)
 		return -1;
 
-	radius_msg_make_authenticator(msg, addr, ETH_ALEN);
+	if (radius_msg_make_authenticator(msg) < 0) {
+		wpa_printf(MSG_INFO, "Could not make Request Authenticator");
+		goto fail;
+	}
 
 	os_snprintf(buf, sizeof(buf), RADIUS_ADDR_FORMAT, MAC2STR(addr));
 	if (!radius_msg_add_attr(msg, RADIUS_ATTR_USER_NAME, (u8 *) buf,
@@ -213,6 +206,33 @@
 
 
 /**
+ * hostapd_check_acl - Check a specified STA against accept/deny ACLs
+ * @hapd: hostapd BSS data
+ * @addr: MAC address of the STA
+ * @vlan_id: Buffer for returning VLAN ID
+ * Returns: HOSTAPD_ACL_ACCEPT, HOSTAPD_ACL_REJECT, or HOSTAPD_ACL_PENDING
+ */
+int hostapd_check_acl(struct hostapd_data *hapd, const u8 *addr,
+		      struct vlan_description *vlan_id)
+{
+	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;
+
+	return HOSTAPD_ACL_PENDING;
+}
+
+
+/**
  * hostapd_allowed_address - Check whether a specified STA can be authenticated
  * @hapd: hostapd BSS data
  * @addr: MAC address of the STA
@@ -231,16 +251,19 @@
  */
 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,
+			    u32 *acct_interim_interval,
+			    struct vlan_description *vlan_id,
 			    struct hostapd_sta_wpa_psk_short **psk,
 			    char **identity, char **radius_cui)
 {
+	int res;
+
 	if (session_timeout)
 		*session_timeout = 0;
 	if (acct_interim_interval)
 		*acct_interim_interval = 0;
 	if (vlan_id)
-		*vlan_id = 0;
+		os_memset(vlan_id, 0, sizeof(*vlan_id));
 	if (psk)
 		*psk = NULL;
 	if (identity)
@@ -248,18 +271,9 @@
 	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;
+	res = hostapd_check_acl(hapd, addr, vlan_id);
+	if (res != HOSTAPD_ACL_PENDING)
+		return res;
 
 	if (hapd->conf->macaddr_acl == USE_EXTERNAL_RADIUS_AUTH) {
 #ifdef CONFIG_NO_RADIUS
@@ -268,10 +282,9 @@
 		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);
+		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;
@@ -399,19 +412,15 @@
 
 /**
  * hostapd_acl_expire - ACL cache expiration callback
- * @eloop_ctx: struct hostapd_data *
- * @timeout_ctx: Not used
+ * @hapd: struct hostapd_data *
  */
-static void hostapd_acl_expire(void *eloop_ctx, void *timeout_ctx)
+void hostapd_acl_expire(struct hostapd_data *hapd)
 {
-	struct hostapd_data *hapd = eloop_ctx;
 	struct os_reltime now;
 
 	os_get_reltime(&now);
 	hostapd_acl_expire_cache(hapd, &now);
 	hostapd_acl_expire_queries(hapd, &now);
-
-	eloop_register_timeout(10, 0, hostapd_acl_expire, hapd, NULL);
 }
 
 
@@ -423,7 +432,7 @@
 				    struct hostapd_cached_radius_acl *cache)
 {
 	int passphraselen;
-	char *passphrase, *strpassphrase;
+	char *passphrase;
 	size_t i;
 	struct hostapd_sta_wpa_psk_short *psk;
 
@@ -440,23 +449,40 @@
 		 */
 		if (passphrase == NULL)
 			break;
+
+		/*
+		 * Passphase should be 8..63 chars (to be hashed with SSID)
+		 * or 64 chars hex string (no separate hashing with SSID).
+		 */
+
+		if (passphraselen < MIN_PASSPHRASE_LEN ||
+		    passphraselen > MAX_PASSPHRASE_LEN + 1)
+			continue;
+
 		/*
 		 * 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);
+		if (psk) {
+			if ((passphraselen == MAX_PASSPHRASE_LEN + 1) &&
+			    (hexstr2bin(passphrase, psk->psk, PMK_LEN) < 0)) {
+				hostapd_logger(hapd, cache->addr,
+					       HOSTAPD_MODULE_RADIUS,
+					       HOSTAPD_LEVEL_WARNING,
+					       "invalid hex string (%d chars) in Tunnel-Password",
+					       passphraselen);
+				goto skip;
+			} else if (passphraselen <= MAX_PASSPHRASE_LEN) {
+				os_memcpy(psk->passphrase, passphrase,
+					  passphraselen);
+				psk->is_passphrase = 1;
+			}
 			psk->next = cache->psk;
 			cache->psk = psk;
 			psk = NULL;
 		}
-		os_free(strpassphrase);
+skip:
 		os_free(psk);
 		os_free(passphrase);
 	}
@@ -482,6 +508,7 @@
 	struct hostapd_acl_query_data *query, *prev;
 	struct hostapd_cached_radius_acl *cache;
 	struct radius_hdr *hdr = radius_msg_get_hdr(msg);
+	int *untagged, *tagged, *notempty;
 
 	query = hapd->acl_queries;
 	prev = NULL;
@@ -539,7 +566,12 @@
 			cache->acct_interim_interval = 0;
 		}
 
-		cache->vlan_id = radius_msg_get_vlanid(msg);
+		notempty = &cache->vlan_id.notempty;
+		untagged = &cache->vlan_id.untagged;
+		tagged = cache->vlan_id.tagged;
+		*notempty = !!radius_msg_get_vlanid(msg, untagged,
+						    MAX_NUM_TAGGED_VLAN,
+						    tagged);
 
 		decode_tunnel_passwords(hapd, shared_secret, shared_secret_len,
 					msg, req, cache);
@@ -562,17 +594,18 @@
 		    !cache->psk)
 			cache->accepted = HOSTAPD_ACL_REJECT;
 
-		if (cache->vlan_id &&
-		    !hostapd_vlan_id_valid(hapd->conf->vlan, cache->vlan_id)) {
+		if (cache->vlan_id.notempty &&
+		    !hostapd_vlan_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;
+				       "Invalid VLAN %d%s received from RADIUS server",
+				       cache->vlan_id.untagged,
+				       cache->vlan_id.tagged[0] ? "+" : "");
+			os_memset(&cache->vlan_id, 0, sizeof(cache->vlan_id));
 		}
 		if (hapd->conf->ssid.dynamic_vlan == DYNAMIC_VLAN_REQUIRED &&
-		    !cache->vlan_id)
+		    !cache->vlan_id.notempty)
 			cache->accepted = HOSTAPD_ACL_REJECT;
 	} else
 		cache->accepted = HOSTAPD_ACL_REJECT;
@@ -615,8 +648,6 @@
 	if (radius_client_register(hapd->radius, RADIUS_AUTH,
 				   hostapd_acl_recv_radius, hapd))
 		return -1;
-
-	eloop_register_timeout(10, 0, hostapd_acl_expire, hapd, NULL);
 #endif /* CONFIG_NO_RADIUS */
 
 	return 0;
@@ -632,8 +663,6 @@
 	struct hostapd_acl_query_data *query, *prev;
 
 #ifndef CONFIG_NO_RADIUS
-	eloop_cancel_timeout(hostapd_acl_expire, hapd, NULL);
-
 	hostapd_acl_cache_free(hapd->acl_cache);
 #endif /* CONFIG_NO_RADIUS */
 
@@ -648,6 +677,12 @@
 
 void hostapd_free_psk_list(struct hostapd_sta_wpa_psk_short *psk)
 {
+	if (psk && psk->ref) {
+		/* This will be freed when the last reference is dropped. */
+		psk->ref--;
+		return;
+	}
+
 	while (psk) {
 		struct hostapd_sta_wpa_psk_short *prev = psk;
 		psk = psk->next;
diff --git a/src/ap/ieee802_11_auth.h b/src/ap/ieee802_11_auth.h
index 2bc1065..71f53b9 100644
--- a/src/ap/ieee802_11_auth.h
+++ b/src/ap/ieee802_11_auth.h
@@ -16,13 +16,17 @@
 	HOSTAPD_ACL_ACCEPT_TIMEOUT = 3
 };
 
+int hostapd_check_acl(struct hostapd_data *hapd, const u8 *addr,
+		      struct vlan_description *vlan_id);
 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,
+			    u32 *acct_interim_interval,
+			    struct vlan_description *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/src/ap/ieee802_11_ht.c b/src/ap/ieee802_11_ht.c
index 11fde2a..5eb1060 100644
--- a/src/ap/ieee802_11_ht.c
+++ b/src/ap/ieee802_11_ht.c
@@ -108,6 +108,29 @@
 }
 
 
+u8 * hostapd_eid_secondary_channel(struct hostapd_data *hapd, u8 *eid)
+{
+	u8 sec_ch;
+
+	if (!hapd->cs_freq_params.channel ||
+	    !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;
+}
+
+
 /*
 op_mode
 Set to 0 (HT pure) under the followign conditions
diff --git a/src/ap/ieee802_11_shared.c b/src/ap/ieee802_11_shared.c
index d462ac8..af858f0 100644
--- a/src/ap/ieee802_11_shared.c
+++ b/src/ap/ieee802_11_shared.c
@@ -172,6 +172,8 @@
 	case 0: /* Bits 0-7 */
 		if (hapd->iconf->obss_interval)
 			*pos |= 0x01; /* Bit 0 - Coexistence management */
+		if (hapd->iface->drv_flags & WPA_DRIVER_FLAGS_AP_CSA)
+			*pos |= 0x04; /* Bit 2 - Extended Channel Switching */
 		break;
 	case 1: /* Bits 8-15 */
 		if (hapd->conf->proxy_arp)
@@ -207,6 +209,10 @@
 		if (hapd->conf->hs20)
 			*pos |= 0x40; /* Bit 46 - WNM-Notification */
 #endif /* CONFIG_HS20 */
+#ifdef CONFIG_MBO
+		if (hapd->conf->mbo_enabled)
+			*pos |= 0x40; /* Bit 46 - WNM-Notification */
+#endif /* CONFIG_MBO */
 		break;
 	case 6: /* Bits 48-55 */
 		if (hapd->conf->ssid.utf8_ssid)
@@ -239,6 +245,10 @@
 	if (hapd->conf->hs20 && len < 6)
 		len = 6;
 #endif /* CONFIG_HS20 */
+#ifdef CONFIG_MBO
+	if (hapd->conf->mbo_enabled && len < 6)
+		len = 6;
+#endif /* CONFIG_MBO */
 	if (len < hapd->iface->extended_capa_len)
 		len = hapd->iface->extended_capa_len;
 	if (len == 0)
@@ -506,3 +516,62 @@
 
 	return pos;
 }
+
+
+#ifdef CONFIG_MBO
+
+u8 * hostapd_eid_mbo(struct hostapd_data *hapd, u8 *eid, size_t len)
+{
+	u8 mbo[6], *mbo_pos = mbo;
+	u8 *pos = eid;
+
+	if (!hapd->conf->mbo_enabled)
+		return eid;
+
+	*mbo_pos++ = MBO_ATTR_ID_AP_CAPA_IND;
+	*mbo_pos++ = 1;
+	/* Not Cellular aware */
+	*mbo_pos++ = 0;
+
+	if (hapd->mbo_assoc_disallow) {
+		*mbo_pos++ = MBO_ATTR_ID_ASSOC_DISALLOW;
+		*mbo_pos++ = 1;
+		*mbo_pos++ = hapd->mbo_assoc_disallow;
+	}
+
+	pos += mbo_add_ie(pos, len, mbo, mbo_pos - mbo);
+
+	return pos;
+}
+
+
+u8 hostapd_mbo_ie_len(struct hostapd_data *hapd)
+{
+	if (!hapd->conf->mbo_enabled)
+		return 0;
+
+	/*
+	 * MBO IE header (6) + Capability Indication attribute (3) +
+	 * Association Disallowed attribute (3) = 12
+	 */
+	return 6 + 3 + (hapd->mbo_assoc_disallow ? 3 : 0);
+}
+
+#endif /* CONFIG_MBO */
+
+
+void ap_copy_sta_supp_op_classes(struct sta_info *sta,
+				 const u8 *supp_op_classes,
+				 size_t supp_op_classes_len)
+{
+	if (!supp_op_classes)
+		return;
+	os_free(sta->supp_op_classes);
+	sta->supp_op_classes = os_malloc(1 + supp_op_classes_len);
+	if (!sta->supp_op_classes)
+		return;
+
+	sta->supp_op_classes[0] = supp_op_classes_len;
+	os_memcpy(sta->supp_op_classes + 1, supp_op_classes,
+		  supp_op_classes_len);
+}
diff --git a/src/ap/ieee802_11_vht.c b/src/ap/ieee802_11_vht.c
index 5bf1b5d..0841898 100644
--- a/src/ap/ieee802_11_vht.c
+++ b/src/ap/ieee802_11_vht.c
@@ -17,6 +17,7 @@
 #include "sta_info.h"
 #include "beacon.h"
 #include "ieee802_11.h"
+#include "dfs.h"
 
 
 u8 * hostapd_eid_vht_capabilities(struct hostapd_data *hapd, u8 *eid)
@@ -80,6 +81,26 @@
 		hapd->iconf->vht_oper_centr_freq_seg1_idx;
 
 	oper->vht_op_info_chwidth = hapd->iconf->vht_oper_chwidth;
+	if (hapd->iconf->vht_oper_chwidth == 2) {
+		/*
+		 * Convert 160 MHz channel width to new style as interop
+		 * workaround.
+		 */
+		oper->vht_op_info_chwidth = 1;
+		oper->vht_op_info_chan_center_freq_seg1_idx =
+			oper->vht_op_info_chan_center_freq_seg0_idx;
+		if (hapd->iconf->channel <
+		    hapd->iconf->vht_oper_centr_freq_seg0_idx)
+			oper->vht_op_info_chan_center_freq_seg0_idx -= 8;
+		else
+			oper->vht_op_info_chan_center_freq_seg0_idx += 8;
+	} else if (hapd->iconf->vht_oper_chwidth == 3) {
+		/*
+		 * Convert 80+80 MHz channel width to new style as interop
+		 * workaround.
+		 */
+		oper->vht_op_info_chwidth = 1;
+	}
 
 	/* VHT Basic MCS set comes from hw */
 	/* Hard code 1 stream, MCS0-7 is a min Basic VHT MCS rates */
@@ -131,6 +152,171 @@
 }
 
 
+u8 * hostapd_eid_wb_chsw_wrapper(struct hostapd_data *hapd, u8 *eid)
+{
+	u8 bw, chan1, chan2 = 0;
+	int freq1;
+
+	if (!hapd->cs_freq_params.channel ||
+	    !hapd->cs_freq_params.vht_enabled)
+		return eid;
+
+	/* bandwidth: 0: 40, 1: 80, 2: 160, 3: 80+80 */
+	switch (hapd->cs_freq_params.bandwidth) {
+	case 40:
+		bw = 0;
+		break;
+	case 80:
+		/* check if it's 80+80 */
+		if (!hapd->cs_freq_params.center_freq2)
+			bw = 1;
+		else
+			bw = 3;
+		break;
+	case 160:
+		bw = 2;
+		break;
+	default:
+		/* not valid VHT bandwidth or not in CSA */
+		return eid;
+	}
+
+	freq1 = hapd->cs_freq_params.center_freq1 ?
+		hapd->cs_freq_params.center_freq1 :
+		hapd->cs_freq_params.freq;
+	if (ieee80211_freq_to_chan(freq1, &chan1) !=
+	    HOSTAPD_MODE_IEEE80211A)
+		return eid;
+
+	if (hapd->cs_freq_params.center_freq2 &&
+	    ieee80211_freq_to_chan(hapd->cs_freq_params.center_freq2,
+				   &chan2) != HOSTAPD_MODE_IEEE80211A)
+		return eid;
+
+	*eid++ = WLAN_EID_VHT_CHANNEL_SWITCH_WRAPPER;
+	*eid++ = 5; /* Length of Channel Switch Wrapper */
+	*eid++ = WLAN_EID_VHT_WIDE_BW_CHSWITCH;
+	*eid++ = 3; /* Length of Wide Bandwidth Channel Switch element */
+	*eid++ = bw; /* New Channel Width */
+	*eid++ = chan1; /* New Channel Center Frequency Segment 0 */
+	*eid++ = chan2; /* New Channel Center Frequency Segment 1 */
+
+	return eid;
+}
+
+
+u8 * hostapd_eid_txpower_envelope(struct hostapd_data *hapd, u8 *eid)
+{
+	struct hostapd_iface *iface = hapd->iface;
+	struct hostapd_config *iconf = iface->conf;
+	struct hostapd_hw_modes *mode = iface->current_mode;
+	struct hostapd_channel_data *chan;
+	int dfs, i;
+	u8 channel, tx_pwr_count, local_pwr_constraint;
+	int max_tx_power;
+	u8 tx_pwr;
+
+	if (!mode)
+		return eid;
+
+	if (ieee80211_freq_to_chan(iface->freq, &channel) == NUM_HOSTAPD_MODES)
+		return eid;
+
+	for (i = 0; i < mode->num_channels; i++) {
+		if (mode->channels[i].freq == iface->freq)
+			break;
+	}
+	if (i == mode->num_channels)
+		return eid;
+
+	switch (iface->conf->vht_oper_chwidth) {
+	case VHT_CHANWIDTH_USE_HT:
+		if (iconf->secondary_channel == 0) {
+			/* Max Transmit Power count = 0 (20 MHz) */
+			tx_pwr_count = 0;
+		} else {
+			/* Max Transmit Power count = 1 (20, 40 MHz) */
+			tx_pwr_count = 1;
+		}
+		break;
+	case VHT_CHANWIDTH_80MHZ:
+		/* Max Transmit Power count = 2 (20, 40, and 80 MHz) */
+		tx_pwr_count = 2;
+		break;
+	case VHT_CHANWIDTH_80P80MHZ:
+	case VHT_CHANWIDTH_160MHZ:
+		/* Max Transmit Power count = 3 (20, 40, 80, 160/80+80 MHz) */
+		tx_pwr_count = 3;
+		break;
+	default:
+		return eid;
+	}
+
+	/*
+	 * Below local_pwr_constraint logic is referred from
+	 * hostapd_eid_pwr_constraint.
+	 *
+	 * Check if DFS is required by regulatory.
+	 */
+	dfs = hostapd_is_dfs_required(hapd->iface);
+	if (dfs < 0)
+		dfs = 0;
+
+	/*
+	 * 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 = (dfs == 0) ? 0 : 3;
+	else
+		local_pwr_constraint = hapd->iconf->local_pwr_constraint;
+
+	/*
+	 * 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.
+	 */
+	chan = &mode->channels[i];
+	max_tx_power = chan->max_tx_power - local_pwr_constraint;
+
+	/*
+	 * Local Maximum Transmit power is encoded as two's complement
+	 * with a 0.5 dB step.
+	 */
+	max_tx_power *= 2; /* in 0.5 dB steps */
+	if (max_tx_power > 127) {
+		/* 63.5 has special meaning of 63.5 dBm or higher */
+		max_tx_power = 127;
+	}
+	if (max_tx_power < -128)
+		max_tx_power = -128;
+	if (max_tx_power < 0)
+		tx_pwr = 0x80 + max_tx_power + 128;
+	else
+		tx_pwr = max_tx_power;
+
+	*eid++ = WLAN_EID_VHT_TRANSMIT_POWER_ENVELOPE;
+	*eid++ = 2 + tx_pwr_count;
+
+	/*
+	 * Max Transmit Power count and
+	 * Max Transmit Power units = 0 (EIRP)
+	 */
+	*eid++ = tx_pwr_count;
+
+	for (i = 0; i <= tx_pwr_count; i++)
+		*eid++ = tx_pwr;
+
+	return eid;
+}
+
+
 u16 copy_sta_vht_capab(struct hostapd_data *hapd, struct sta_info *sta,
 		       const u8 *vht_capab)
 {
diff --git a/src/ap/ieee802_1x.c b/src/ap/ieee802_1x.c
index d45b98f..42b0299 100644
--- a/src/ap/ieee802_1x.c
+++ b/src/ap/ieee802_1x.c
@@ -34,6 +34,9 @@
 #include "ieee802_1x.h"
 
 
+#ifdef CONFIG_HS20
+static void ieee802_1x_wnm_notif_send(void *eloop_ctx, void *timeout_ctx);
+#endif /* CONFIG_HS20 */
 static void ieee802_1x_finished(struct hostapd_data *hapd,
 				struct sta_info *sta, int success,
 				int remediation);
@@ -125,6 +128,9 @@
 }
 
 
+#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,
@@ -204,7 +210,7 @@
 }
 
 
-void ieee802_1x_tx_key(struct hostapd_data *hapd, struct sta_info *sta)
+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;
@@ -216,7 +222,7 @@
 		   MAC2STR(sta->addr));
 
 #ifndef CONFIG_NO_VLAN
-	if (sta->vlan_id > 0 && sta->vlan_id <= MAX_VLAN_ID) {
+	if (sta->vlan_id > 0) {
 		wpa_printf(MSG_ERROR, "Using WEP with vlans is not supported.");
 		return;
 	}
@@ -259,6 +265,9 @@
 	}
 }
 
+#endif /* CONFIG_NO_RC4 */
+#endif /* CONFIG_FIPS */
+
 
 const char *radius_mode_txt(struct hostapd_data *hapd)
 {
@@ -396,6 +405,14 @@
 	char buf[128];
 
 	if (!hostapd_config_get_radius_attr(req_attr,
+					    RADIUS_ATTR_SERVICE_TYPE) &&
+	    !radius_msg_add_attr_int32(msg, RADIUS_ATTR_SERVICE_TYPE,
+				       RADIUS_SERVICE_TYPE_FRAMED)) {
+		wpa_printf(MSG_ERROR, "Could not add Service-Type");
+		return -1;
+	}
+
+	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");
@@ -429,9 +446,9 @@
 		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 (sta->acct_session_id) {
+		os_snprintf(buf, sizeof(buf), "%016llX",
+			    (unsigned long long) sta->acct_session_id);
 		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");
@@ -439,6 +456,21 @@
 		}
 	}
 
+	if ((hapd->conf->wpa & 2) &&
+	    !hapd->conf->disable_pmksa_caching &&
+	    sta->eapol_sm && sta->eapol_sm->acct_multi_session_id) {
+		os_snprintf(buf, sizeof(buf), "%016llX",
+			    (unsigned long long)
+			    sta->eapol_sm->acct_multi_session_id);
+		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");
+			return -1;
+		}
+	}
+
 #ifdef CONFIG_IEEE80211R
 	if (hapd->conf->wpa && wpa_key_mgmt_ft(hapd->conf->wpa_key_mgmt) &&
 	    sta->wpa_sm &&
@@ -469,6 +501,7 @@
 {
 	char buf[128];
 	struct hostapd_radius_attr *attr;
+	int len;
 
 	if (!hostapd_config_get_radius_attr(req_attr,
 					    RADIUS_ATTR_NAS_IP_ADDRESS) &&
@@ -500,15 +533,15 @@
 		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';
+	len = os_snprintf(buf, sizeof(buf), RADIUS_802_1X_ADDR_FORMAT ":",
+			  MAC2STR(hapd->own_addr));
+	os_memcpy(&buf[len], hapd->conf->ssid.ssid,
+		  hapd->conf->ssid.ssid_len);
+	len += hapd->conf->ssid.ssid_len;
 	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))) {
+				 (u8 *) buf, len)) {
 		wpa_printf(MSG_ERROR, "Could not add Called-Station-Id");
 		return -1;
 	}
@@ -577,7 +610,10 @@
 		return;
 	}
 
-	radius_msg_make_authenticator(msg, (u8 *) sta, sizeof(*sta));
+	if (radius_msg_make_authenticator(msg) < 0) {
+		wpa_printf(MSG_INFO, "Could not make Request Authenticator");
+		goto fail;
+	}
 
 	if (sm->identity &&
 	    !radius_msg_add_attr(msg, RADIUS_ATTR_USER_NAME,
@@ -825,6 +861,29 @@
 }
 
 
+static void ieee802_1x_save_eapol(struct sta_info *sta, const u8 *buf,
+				  size_t len)
+{
+	if (sta->pending_eapol_rx) {
+		wpabuf_free(sta->pending_eapol_rx->buf);
+	} else {
+		sta->pending_eapol_rx =
+			os_malloc(sizeof(*sta->pending_eapol_rx));
+		if (!sta->pending_eapol_rx)
+			return;
+	}
+
+	sta->pending_eapol_rx->buf = wpabuf_alloc_copy(buf, len);
+	if (!sta->pending_eapol_rx->buf) {
+		os_free(sta->pending_eapol_rx);
+		sta->pending_eapol_rx = NULL;
+		return;
+	}
+
+	os_get_reltime(&sta->pending_eapol_rx->rx_time);
+}
+
+
 /**
  * ieee802_1x_receive - Process the EAPOL frames from the Supplicant
  * @hapd: hostapd BSS data
@@ -855,6 +914,13 @@
 		     !(hapd->iface->drv_flags & WPA_DRIVER_FLAGS_WIRED))) {
 		wpa_printf(MSG_DEBUG, "IEEE 802.1X data frame from not "
 			   "associated/Pre-authenticating STA");
+
+		if (sta && (sta->flags & WLAN_STA_AUTH)) {
+			wpa_printf(MSG_DEBUG, "Saving EAPOL frame from " MACSTR
+				   " for later use", MAC2STR(sta->addr));
+			ieee802_1x_save_eapol(sta, buf, len);
+		}
+
 		return;
 	}
 
@@ -1041,7 +1107,7 @@
 		 * Clear any possible EAPOL authenticator state to support
 		 * reassociation change from WPS to PSK.
 		 */
-		ieee802_1x_free_station(sta);
+		ieee802_1x_free_station(hapd, sta);
 		return;
 	}
 
@@ -1052,7 +1118,7 @@
 		 * Clear any possible EAPOL authenticator state to support
 		 * reassociation change from WPA-EAP to PSK.
 		 */
-		ieee802_1x_free_station(sta);
+		ieee802_1x_free_station(hapd, sta);
 		return;
 	}
 
@@ -1100,6 +1166,7 @@
 		sta->eapol_sm->be_auth_state = BE_AUTH_SUCCESS;
 		sta->eapol_sm->authSuccess = TRUE;
 		sta->eapol_sm->authFail = FALSE;
+		sta->eapol_sm->portValid = TRUE;
 		if (sta->eapol_sm->eap)
 			eap_sm_notify_cached(sta->eapol_sm->eap);
 		/* TODO: get vlan_id from R0KH using RRB message */
@@ -1122,7 +1189,7 @@
 		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);
+		pmksa_cache_to_eapol_data(hapd, pmksa, sta->eapol_sm);
 		ap_sta_bind_vlan(hapd, sta);
 	} else {
 		if (reassoc) {
@@ -1138,10 +1205,20 @@
 }
 
 
-void ieee802_1x_free_station(struct sta_info *sta)
+void ieee802_1x_free_station(struct hostapd_data *hapd, struct sta_info *sta)
 {
 	struct eapol_state_machine *sm = sta->eapol_sm;
 
+#ifdef CONFIG_HS20
+	eloop_cancel_timeout(ieee802_1x_wnm_notif_send, hapd, sta);
+#endif /* CONFIG_HS20 */
+
+	if (sta->pending_eapol_rx) {
+		wpabuf_free(sta->pending_eapol_rx->buf);
+		os_free(sta->pending_eapol_rx);
+		sta->pending_eapol_rx = NULL;
+	}
+
 	if (sm == NULL)
 		return;
 
@@ -1150,10 +1227,8 @@
 #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);
 }
 
@@ -1586,10 +1661,16 @@
 	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;
+	int session_timeout_set;
 	struct eapol_state_machine *sm;
 	int override_eapReq = 0;
 	struct radius_hdr *hdr = radius_msg_get_hdr(msg);
+	struct vlan_description vlan_desc;
+#ifndef CONFIG_NO_VLAN
+	int *untagged, *tagged, *notempty;
+#endif /* CONFIG_NO_VLAN */
+
+	os_memset(&vlan_desc, 0, sizeof(vlan_desc));
 
 	sm = ieee802_1x_search_radius_identifier(hapd, hdr->identifier);
 	if (sm == NULL) {
@@ -1653,27 +1734,32 @@
 
 	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) {
+		if (hapd->conf->ssid.dynamic_vlan != DYNAMIC_VLAN_DISABLED) {
+			notempty = &vlan_desc.notempty;
+			untagged = &vlan_desc.untagged;
+			tagged = vlan_desc.tagged;
+			*notempty = !!radius_msg_get_vlanid(msg, untagged,
+							    MAX_NUM_TAGGED_VLAN,
+							    tagged);
+		}
+
+		if (vlan_desc.notempty &&
+		    !hostapd_vlan_valid(hapd->conf->vlan, &vlan_desc)) {
 			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);
+				       "Invalid VLAN %d%s received from RADIUS server",
+				       vlan_desc.untagged,
+				       vlan_desc.tagged[0] ? "+" : "");
+			os_memset(&vlan_desc, 0, sizeof(vlan_desc));
+			ap_sta_set_vlan(hapd, sta, &vlan_desc);
 			break;
-		} else if (hapd->conf->ssid.dynamic_vlan ==
-			   DYNAMIC_VLAN_REQUIRED) {
+		}
+
+		if (hapd->conf->ssid.dynamic_vlan == DYNAMIC_VLAN_REQUIRED &&
+		    !vlan_desc.notempty) {
 			sta->eapol_sm->authFail = TRUE;
 			hostapd_logger(hapd, sta->addr,
 				       HOSTAPD_MODULE_IEEE8021X,
@@ -1684,7 +1770,18 @@
 		}
 #endif /* CONFIG_NO_VLAN */
 
-		sta->vlan_id = vlan_id;
+		if (ap_sta_set_vlan(hapd, sta, &vlan_desc) < 0)
+			break;
+
+#ifndef CONFIG_NO_VLAN
+		if (sta->vlan_id > 0) {
+			hostapd_logger(hapd, sta->addr,
+				       HOSTAPD_MODULE_RADIUS,
+				       HOSTAPD_LEVEL_INFO,
+				       "VLAN ID %d", sta->vlan_id);
+		}
+#endif /* CONFIG_NO_VLAN */
+
 		if ((sta->flags & WLAN_STA_ASSOC) &&
 		    ap_sta_bind_vlan(hapd, sta) < 0)
 			break;
@@ -1709,15 +1806,6 @@
 		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;
@@ -2023,9 +2111,13 @@
 
 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 */
 }
 
 
@@ -2096,6 +2188,7 @@
 	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;
@@ -2179,7 +2272,7 @@
 {
 	eloop_cancel_timeout(ieee802_1x_rekey, hapd, NULL);
 
-	if (hapd->driver != NULL &&
+	if (hapd->driver && hapd->drv_priv &&
 	    (hapd->conf->ieee802_1x || hapd->conf->wpa))
 		hostapd_set_drv_ieee8021x(hapd, hapd->conf->iface, 0);
 
@@ -2484,12 +2577,12 @@
 			  /* TODO: dot1xAuthSessionOctetsTx */
 			  /* TODO: dot1xAuthSessionFramesRx */
 			  /* TODO: dot1xAuthSessionFramesTx */
-			  "dot1xAuthSessionId=%08X-%08X\n"
+			  "dot1xAuthSessionId=%016llX\n"
 			  "dot1xAuthSessionAuthenticMethod=%d\n"
 			  "dot1xAuthSessionTime=%u\n"
 			  "dot1xAuthSessionTerminateCause=999\n"
 			  "dot1xAuthSessionUserName=%s\n",
-			  sta->acct_session_id_hi, sta->acct_session_id_lo,
+			  (unsigned long long) sta->acct_session_id,
 			  (wpa_key_mgmt_wpa_ieee8021x(
 				   wpa_auth_sta_key_mgmt(sta->wpa_sm))) ?
 			  1 : 2,
@@ -2499,11 +2592,11 @@
 		return len;
 	len += ret;
 
-	if (sm->acct_multi_session_id_hi) {
+	if (sm->acct_multi_session_id) {
 		ret = os_snprintf(buf + len, buflen - len,
-				  "authMultiSessionId=%08X+%08X\n",
-				  sm->acct_multi_session_id_hi,
-				  sm->acct_multi_session_id_lo);
+				  "authMultiSessionId=%016llX\n",
+				  (unsigned long long)
+				  sm->acct_multi_session_id);
 		if (os_snprintf_error(buflen - len, ret))
 			return len;
 		len += ret;
@@ -2524,6 +2617,34 @@
 }
 
 
+#ifdef CONFIG_HS20
+static void ieee802_1x_wnm_notif_send(void *eloop_ctx, void *timeout_ctx)
+{
+	struct hostapd_data *hapd = eloop_ctx;
+	struct sta_info *sta = timeout_ctx;
+
+	if (sta->remediation) {
+		wpa_printf(MSG_DEBUG, "HS 2.0: Send WNM-Notification to "
+			   MACSTR " to indicate Subscription Remediation",
+			   MAC2STR(sta->addr));
+		hs20_send_wnm_notification(hapd, sta->addr,
+					   sta->remediation_method,
+					   sta->remediation_url);
+		os_free(sta->remediation_url);
+		sta->remediation_url = NULL;
+	}
+
+	if (sta->hs20_deauth_req) {
+		wpa_printf(MSG_DEBUG, "HS 2.0: Send WNM-Notification to "
+			   MACSTR " to indicate imminent deauthentication",
+			   MAC2STR(sta->addr));
+		hs20_send_wnm_notification_deauth_req(hapd, sta->addr,
+						      sta->hs20_deauth_req);
+	}
+}
+#endif /* CONFIG_HS20 */
+
+
 static void ieee802_1x_finished(struct hostapd_data *hapd,
 				struct sta_info *sta, int success,
 				int remediation)
@@ -2543,26 +2664,12 @@
 		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);
-		}
+	if (success && (sta->remediation || sta->hs20_deauth_req)) {
+		wpa_printf(MSG_DEBUG, "HS 2.0: Schedule WNM-Notification to "
+			   MACSTR " in 100 ms", MAC2STR(sta->addr));
+		eloop_cancel_timeout(ieee802_1x_wnm_notif_send, hapd, sta);
+		eloop_register_timeout(0, 100000, ieee802_1x_wnm_notif_send,
+				       hapd, sta);
 	}
 #endif /* CONFIG_HS20 */
 
@@ -2573,7 +2680,7 @@
 		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,
+	    wpa_auth_pmksa_add(sta->wpa_sm, key, len, session_timeout,
 			       sta->eapol_sm) == 0) {
 		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA,
 			       HOSTAPD_LEVEL_DEBUG,
diff --git a/src/ap/ieee802_1x.h b/src/ap/ieee802_1x.h
index de6e0e7..ec80199 100644
--- a/src/ap/ieee802_1x.h
+++ b/src/ap/ieee802_1x.h
@@ -21,9 +21,8 @@
 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_free_station(struct hostapd_data *hapd, struct sta_info *sta);
 
-void ieee802_1x_tx_key(struct hostapd_data *hapd, 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);
diff --git a/src/ap/mbo_ap.c b/src/ap/mbo_ap.c
new file mode 100644
index 0000000..5e0f92a
--- /dev/null
+++ b/src/ap/mbo_ap.c
@@ -0,0 +1,245 @@
+/*
+ * hostapd - MBO
+ * Copyright (c) 2016, 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 "hostapd.h"
+#include "sta_info.h"
+#include "mbo_ap.h"
+
+
+void mbo_ap_sta_free(struct sta_info *sta)
+{
+	struct mbo_non_pref_chan_info *info, *prev;
+
+	info = sta->non_pref_chan;
+	sta->non_pref_chan = NULL;
+	while (info) {
+		prev = info;
+		info = info->next;
+		os_free(prev);
+	}
+}
+
+
+static void mbo_ap_parse_non_pref_chan(struct sta_info *sta,
+				       const u8 *buf, size_t len)
+{
+	struct mbo_non_pref_chan_info *info, *tmp;
+	char channels[200], *pos, *end;
+	size_t num_chan, i;
+	int ret;
+
+	if (len <= 4)
+		return; /* Not enough room for any channels */
+
+	num_chan = len - 4;
+	info = os_zalloc(sizeof(*info) + num_chan);
+	if (!info)
+		return;
+	info->op_class = buf[0];
+	info->pref = buf[len - 3];
+	info->reason_code = buf[len - 2];
+	info->reason_detail = buf[len - 1];
+	info->num_channels = num_chan;
+	buf++;
+	os_memcpy(info->channels, buf, num_chan);
+	if (!sta->non_pref_chan) {
+		sta->non_pref_chan = info;
+	} else {
+		tmp = sta->non_pref_chan;
+		while (tmp->next)
+			tmp = tmp->next;
+		tmp->next = info;
+	}
+
+	pos = channels;
+	end = pos + sizeof(channels);
+	*pos = '\0';
+	for (i = 0; i < num_chan; i++) {
+		ret = os_snprintf(pos, end - pos, "%s%u",
+				  i == 0 ? "" : " ", buf[i]);
+		if (os_snprintf_error(end - pos, ret)) {
+			*pos = '\0';
+			break;
+		}
+		pos += ret;
+	}
+
+	wpa_printf(MSG_DEBUG, "MBO: STA " MACSTR
+		   " non-preferred channel list (op class %u, pref %u, reason code %u, reason detail %u, channels %s)",
+		   MAC2STR(sta->addr), info->op_class, info->pref,
+		   info->reason_code, info->reason_detail, channels);
+}
+
+
+void mbo_ap_check_sta_assoc(struct hostapd_data *hapd, struct sta_info *sta,
+			    struct ieee802_11_elems *elems)
+{
+	const u8 *pos, *attr, *end;
+	size_t len;
+
+	if (!hapd->conf->mbo_enabled || !elems->mbo)
+		return;
+
+	pos = elems->mbo + 4;
+	len = elems->mbo_len - 4;
+	wpa_hexdump(MSG_DEBUG, "MBO: Association Request attributes", pos, len);
+
+	attr = get_ie(pos, len, MBO_ATTR_ID_CELL_DATA_CAPA);
+	if (attr && attr[1] >= 1)
+		sta->cell_capa = attr[2];
+
+	mbo_ap_sta_free(sta);
+	end = pos + len;
+	while (end - pos > 1) {
+		u8 ie_len = pos[1];
+
+		if (2 + ie_len > end - pos)
+			break;
+
+		if (pos[0] == MBO_ATTR_ID_NON_PREF_CHAN_REPORT)
+			mbo_ap_parse_non_pref_chan(sta, pos + 2, ie_len);
+		pos += 2 + pos[1];
+	}
+}
+
+
+int mbo_ap_get_info(struct sta_info *sta, char *buf, size_t buflen)
+{
+	char *pos = buf, *end = buf + buflen;
+	int ret;
+	struct mbo_non_pref_chan_info *info;
+	u8 i;
+	unsigned int count = 0;
+
+	if (!sta->cell_capa)
+		return 0;
+
+	ret = os_snprintf(pos, end - pos, "mbo_cell_capa=%u\n", sta->cell_capa);
+	if (os_snprintf_error(end - pos, ret))
+		return pos - buf;
+	pos += ret;
+
+	for (info = sta->non_pref_chan; info; info = info->next) {
+		char *pos2 = pos;
+
+		ret = os_snprintf(pos2, end - pos2,
+				  "non_pref_chan[%u]=%u:%u:%u:%u:",
+				  count, info->op_class, info->pref,
+				  info->reason_code, info->reason_detail);
+		count++;
+		if (os_snprintf_error(end - pos2, ret))
+			break;
+		pos2 += ret;
+
+		for (i = 0; i < info->num_channels; i++) {
+			ret = os_snprintf(pos2, end - pos2, "%u%s",
+					  info->channels[i],
+					  i + 1 < info->num_channels ?
+					  "," : "");
+			if (os_snprintf_error(end - pos2, ret)) {
+				pos2 = NULL;
+				break;
+			}
+			pos2 += ret;
+		}
+
+		if (!pos2)
+			break;
+		ret = os_snprintf(pos2, end - pos2, "\n");
+		if (os_snprintf_error(end - pos2, ret))
+			break;
+		pos2 += ret;
+		pos = pos2;
+	}
+
+	return pos - buf;
+}
+
+
+static void mbo_ap_wnm_notif_req_cell_capa(struct sta_info *sta,
+					   const u8 *buf, size_t len)
+{
+	if (len < 1)
+		return;
+	wpa_printf(MSG_DEBUG, "MBO: STA " MACSTR
+		   " updated cellular data capability: %u",
+		   MAC2STR(sta->addr), buf[0]);
+	sta->cell_capa = buf[0];
+}
+
+
+static void mbo_ap_wnm_notif_req_elem(struct sta_info *sta, u8 type,
+				      const u8 *buf, size_t len,
+				      int *first_non_pref_chan)
+{
+	switch (type) {
+	case WFA_WNM_NOTIF_SUBELEM_NON_PREF_CHAN_REPORT:
+		if (*first_non_pref_chan) {
+			/*
+			 * Need to free the previously stored entries now to
+			 * allow the update to replace all entries.
+			 */
+			*first_non_pref_chan = 0;
+			mbo_ap_sta_free(sta);
+		}
+		mbo_ap_parse_non_pref_chan(sta, buf, len);
+		break;
+	case WFA_WNM_NOTIF_SUBELEM_CELL_DATA_CAPA:
+		mbo_ap_wnm_notif_req_cell_capa(sta, buf, len);
+		break;
+	default:
+		wpa_printf(MSG_DEBUG,
+			   "MBO: Ignore unknown WNM Notification WFA subelement %u",
+			   type);
+		break;
+	}
+}
+
+
+void mbo_ap_wnm_notification_req(struct hostapd_data *hapd, const u8 *addr,
+				 const u8 *buf, size_t len)
+{
+	const u8 *pos, *end;
+	u8 ie_len;
+	struct sta_info *sta;
+	int first_non_pref_chan = 1;
+
+	if (!hapd->conf->mbo_enabled)
+		return;
+
+	sta = ap_get_sta(hapd, addr);
+	if (!sta)
+		return;
+
+	pos = buf;
+	end = buf + len;
+
+	while (end - pos > 1) {
+		ie_len = pos[1];
+
+		if (2 + ie_len > end - pos)
+			break;
+
+		if (pos[0] == WLAN_EID_VENDOR_SPECIFIC &&
+		    ie_len >= 4 && WPA_GET_BE24(pos + 2) == OUI_WFA)
+			mbo_ap_wnm_notif_req_elem(sta, pos[5],
+						  pos + 6, ie_len - 4,
+						  &first_non_pref_chan);
+		else
+			wpa_printf(MSG_DEBUG,
+				   "MBO: Ignore unknown WNM Notification element %u (len=%u)",
+				   pos[0], pos[1]);
+
+		pos += 2 + pos[1];
+	}
+}
diff --git a/src/ap/mbo_ap.h b/src/ap/mbo_ap.h
new file mode 100644
index 0000000..9f37f28
--- /dev/null
+++ b/src/ap/mbo_ap.h
@@ -0,0 +1,51 @@
+/*
+ * MBO related functions and structures
+ * Copyright (c) 2016, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef MBO_AP_H
+#define MBO_AP_H
+
+struct hostapd_data;
+struct sta_info;
+struct ieee802_11_elems;
+
+#ifdef CONFIG_MBO
+
+void mbo_ap_check_sta_assoc(struct hostapd_data *hapd, struct sta_info *sta,
+			    struct ieee802_11_elems *elems);
+int mbo_ap_get_info(struct sta_info *sta, char *buf, size_t buflen);
+void mbo_ap_wnm_notification_req(struct hostapd_data *hapd, const u8 *addr,
+				 const u8 *buf, size_t len);
+void mbo_ap_sta_free(struct sta_info *sta);
+
+#else /* CONFIG_MBO */
+
+static inline void mbo_ap_check_sta_assoc(struct hostapd_data *hapd,
+					  struct sta_info *sta,
+					  struct ieee802_11_elems *elems)
+{
+}
+
+static inline int mbo_ap_get_info(struct sta_info *sta, char *buf,
+				  size_t buflen)
+{
+	return 0;
+}
+
+static inline void mbo_ap_wnm_notification_req(struct hostapd_data *hapd,
+					       const u8 *addr,
+					       const u8 *buf, size_t len)
+{
+}
+
+static inline void mbo_ap_sta_free(struct sta_info *sta)
+{
+}
+
+#endif /* CONFIG_MBO */
+
+#endif /* MBO_AP_H */
diff --git a/src/ap/ndisc_snoop.c b/src/ap/ndisc_snoop.c
index 0adcc97..4a87721 100644
--- a/src/ap/ndisc_snoop.c
+++ b/src/ap/ndisc_snoop.c
@@ -98,7 +98,7 @@
 {
 	struct hostapd_data *hapd = ctx;
 	struct icmpv6_ndmsg *msg;
-	struct in6_addr *saddr;
+	struct in6_addr saddr;
 	struct sta_info *sta;
 	int res;
 	char addrtxt[INET6_ADDRSTRLEN + 1];
@@ -113,25 +113,30 @@
 		if (msg->opt_type != SOURCE_LL_ADDR)
 			return;
 
-		saddr = &msg->ipv6h.ip6_src;
-		if (!(saddr->s6_addr32[0] == 0 && saddr->s6_addr32[1] == 0 &&
-		      saddr->s6_addr32[2] == 0 && saddr->s6_addr32[3] == 0)) {
+		/*
+		 * 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))
+			if (sta_has_ip6addr(sta, &saddr))
 				return;
 
-			if (inet_ntop(AF_INET6, saddr, addrtxt, sizeof(addrtxt))
-			    == NULL)
+			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,
+			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,
@@ -140,7 +145,7 @@
 				return;
 			}
 
-			if (sta_ip6addr_add(sta, saddr))
+			if (sta_ip6addr_add(sta, &saddr))
 				return;
 		}
 		break;
diff --git a/src/ap/pmksa_cache_auth.c b/src/ap/pmksa_cache_auth.c
index 877affe..d610e7e 100644
--- a/src/ap/pmksa_cache_auth.c
+++ b/src/ap/pmksa_cache_auth.c
@@ -38,6 +38,7 @@
 
 static void _pmksa_cache_free_entry(struct rsn_pmksa_cache_entry *entry)
 {
+	os_free(entry->vlan_desc);
 	os_free(entry->identity);
 	wpabuf_free(entry->cui);
 #ifndef CONFIG_NO_RADIUS
@@ -91,6 +92,20 @@
 }
 
 
+/**
+ * pmksa_cache_auth_flush - Flush all PMKSA cache entries
+ * @pmksa: Pointer to PMKSA cache data from pmksa_cache_auth_init()
+ */
+void pmksa_cache_auth_flush(struct rsn_pmksa_cache *pmksa)
+{
+	while (pmksa->pmksa) {
+		wpa_printf(MSG_DEBUG, "RSN: Flush PMKSA cache entry for "
+			   MACSTR, MAC2STR(pmksa->pmksa->spa));
+		pmksa_cache_free_entry(pmksa, pmksa->pmksa);
+	}
+}
+
+
 static void pmksa_cache_expire(void *eloop_ctx, void *timeout_ctx)
 {
 	struct rsn_pmksa_cache *pmksa = eloop_ctx;
@@ -126,6 +141,8 @@
 static void pmksa_cache_from_eapol_data(struct rsn_pmksa_cache_entry *entry,
 					struct eapol_state_machine *eapol)
 {
+	struct vlan_description *vlan_desc;
+
 	if (eapol == NULL)
 		return;
 
@@ -146,14 +163,22 @@
 #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;
+	vlan_desc = ((struct sta_info *) eapol->sta)->vlan_desc;
+	if (vlan_desc && vlan_desc->notempty) {
+		entry->vlan_desc = os_zalloc(sizeof(struct vlan_description));
+		if (entry->vlan_desc)
+			*entry->vlan_desc = *vlan_desc;
+	} else {
+		entry->vlan_desc = NULL;
+	}
+
+	entry->acct_multi_session_id = eapol->acct_multi_session_id;
 }
 
 
-void pmksa_cache_to_eapol_data(struct rsn_pmksa_cache_entry *entry,
+void pmksa_cache_to_eapol_data(struct hostapd_data *hapd,
+			       struct rsn_pmksa_cache_entry *entry,
 			       struct eapol_state_machine *eapol)
 {
 	if (entry == NULL || eapol == NULL)
@@ -186,10 +211,11 @@
 	}
 
 	eapol->eap_type_authsrv = entry->eap_type_authsrv;
-	((struct sta_info *) eapol->sta)->vlan_id = entry->vlan_id;
+#ifndef CONFIG_NO_VLAN
+	ap_sta_set_vlan(hapd, eapol->sta, entry->vlan_desc);
+#endif /* CONFIG_NO_VLAN */
 
-	eapol->acct_multi_session_id_hi = entry->acct_multi_session_id_hi;
-	eapol->acct_multi_session_id_lo = entry->acct_multi_session_id_lo;
+	eapol->acct_multi_session_id = entry->acct_multi_session_id;
 }
 
 
@@ -234,6 +260,7 @@
  * @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)
+ * @pmkid: Calculated PMKID
  * @kck: Key confirmation key or %NULL if not yet derived
  * @kck_len: KCK length in bytes
  * @aa: Authenticator address
@@ -250,7 +277,7 @@
  */
 struct rsn_pmksa_cache_entry *
 pmksa_cache_auth_add(struct rsn_pmksa_cache *pmksa,
-		     const u8 *pmk, size_t pmk_len,
+		     const u8 *pmk, size_t pmk_len, const u8 *pmkid,
 		     const u8 *kck, size_t kck_len,
 		     const u8 *aa, const u8 *spa, int session_timeout,
 		     struct eapol_state_machine *eapol, int akmp)
@@ -258,7 +285,7 @@
 	struct rsn_pmksa_cache_entry *entry, *pos;
 	struct os_reltime now;
 
-	if (pmk_len > PMK_LEN)
+	if (pmk_len > PMK_LEN_MAX)
 		return NULL;
 
 	if (wpa_key_mgmt_suite_b(akmp) && !kck)
@@ -269,7 +296,9 @@
 		return NULL;
 	os_memcpy(entry->pmk, pmk, pmk_len);
 	entry->pmk_len = pmk_len;
-	if (akmp == WPA_KEY_MGMT_IEEE8021X_SUITE_B_192)
+	if (pmkid)
+		os_memcpy(entry->pmkid, pmkid, PMKID_LEN);
+	else 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);
@@ -337,7 +366,13 @@
 	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;
+	if (old_entry->vlan_desc) {
+		entry->vlan_desc = os_zalloc(sizeof(struct vlan_description));
+		if (entry->vlan_desc)
+			*entry->vlan_desc = *old_entry->vlan_desc;
+	} else {
+		entry->vlan_desc = NULL;
+	}
 	entry->opportunistic = 1;
 
 	pmksa_cache_link_entry(pmksa, entry);
@@ -471,12 +506,11 @@
 	if (attr->acct_multi_session_id) {
 		char buf[20];
 
-		if (attr->acct_multi_session_id_len != 17)
+		if (attr->acct_multi_session_id_len != 16)
 			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)
+		os_snprintf(buf, sizeof(buf), "%016llX",
+			    (unsigned long long) entry->acct_multi_session_id);
+		if (os_memcmp(attr->acct_multi_session_id, buf, 16) != 0)
 			return 0;
 		match++;
 	}
@@ -526,3 +560,48 @@
 
 	return found ? 0 : -1;
 }
+
+
+/**
+ * pmksa_cache_auth_list - Dump text list of entries in PMKSA cache
+ * @pmksa: Pointer to PMKSA cache data from pmksa_cache_auth_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_auth_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 / SPA / PMKID / expiration (in seconds) / opportunistic\n");
+	if (os_snprintf_error(buf + len - pos, ret))
+		return pos - buf;
+	pos += ret;
+	i = 0;
+	entry = pmksa->pmksa;
+	while (entry) {
+		ret = os_snprintf(pos, buf + len - pos, "%d " MACSTR " ",
+				  i, MAC2STR(entry->spa));
+		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;
+}
diff --git a/src/ap/pmksa_cache_auth.h b/src/ap/pmksa_cache_auth.h
index 8b7be12..d8d9c5a 100644
--- a/src/ap/pmksa_cache_auth.h
+++ b/src/ap/pmksa_cache_auth.h
@@ -17,7 +17,7 @@
 struct rsn_pmksa_cache_entry {
 	struct rsn_pmksa_cache_entry *next, *hnext;
 	u8 pmkid[PMKID_LEN];
-	u8 pmk[PMK_LEN];
+	u8 pmk[PMK_LEN_MAX];
 	size_t pmk_len;
 	os_time_t expiration;
 	int akmp; /* WPA_KEY_MGMT_* */
@@ -28,11 +28,10 @@
 	struct wpabuf *cui;
 	struct radius_class_data radius_class;
 	u8 eap_type_authsrv;
-	int vlan_id;
+	struct vlan_description *vlan_desc;
 	int opportunistic;
 
-	u32 acct_multi_session_id_hi;
-	u32 acct_multi_session_id_lo;
+	u64 acct_multi_session_id;
 };
 
 struct rsn_pmksa_cache;
@@ -49,7 +48,7 @@
 	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 *pmk, size_t pmk_len, const u8 *pmkid,
 		     const u8 *kck, size_t kck_len,
 		     const u8 *aa, const u8 *spa, int session_timeout,
 		     struct eapol_state_machine *eapol, int akmp);
@@ -57,11 +56,14 @@
 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,
+void pmksa_cache_to_eapol_data(struct hostapd_data *hapd,
+			       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);
+int pmksa_cache_auth_list(struct rsn_pmksa_cache *pmksa, char *buf, size_t len);
+void pmksa_cache_auth_flush(struct rsn_pmksa_cache *pmksa);
 
 #endif /* PMKSA_CACHE_H */
diff --git a/src/ap/sta_info.c b/src/ap/sta_info.c
index 20847d5..c36842b 100644
--- a/src/ap/sta_info.c
+++ b/src/ap/sta_info.c
@@ -16,6 +16,7 @@
 #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"
@@ -31,8 +32,10 @@
 #include "ap_drv_ops.h"
 #include "gas_serv.h"
 #include "wnm_ap.h"
+#include "mbo_ap.h"
 #include "ndisc_snoop.h"
 #include "sta_info.h"
+#include "vlan.h"
 
 static void ap_sta_remove_in_other_bss(struct hostapd_data *hapd,
 				       struct sta_info *sta);
@@ -168,21 +171,10 @@
 	ap_sta_ip6addr_del(hapd, sta);
 
 	if (!hapd->iface->driver_ap_teardown &&
-	    !(sta->flags & WLAN_STA_PREAUTH))
+	    !(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);
+		sta->added_unassoc = 0;
 	}
-#endif /* CONFIG_NO_VLAN */
 
 	ap_sta_hash_del(hapd, sta);
 	ap_sta_list_del(hapd, sta);
@@ -250,7 +242,7 @@
 
 #ifdef CONFIG_MESH
 	if (hapd->mesh_sta_free_cb)
-		hapd->mesh_sta_free_cb(sta);
+		hapd->mesh_sta_free_cb(hapd, sta);
 #endif /* CONFIG_MESH */
 
 	if (set_beacon)
@@ -261,11 +253,10 @@
 	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);
+	ap_sta_clear_disconnect_timeouts(hapd, sta);
 	sae_clear_retransmit_timer(hapd, sta);
 
-	ieee802_1x_free_station(sta);
+	ieee802_1x_free_station(hapd, sta);
 	wpa_auth_sta_deinit(sta->wpa_sm);
 	rsn_preauth_free_station(hapd, sta);
 #ifndef CONFIG_NO_RADIUS
@@ -273,6 +264,28 @@
 		radius_client_flush_auth(hapd->radius, sta->addr);
 #endif /* CONFIG_NO_RADIUS */
 
+#ifndef CONFIG_NO_VLAN
+	/*
+	 * sta->wpa_sm->group needs to be released before so that
+	 * vlan_remove_dynamic() can check that no stations are left on the
+	 * AP_VLAN netdev.
+	 */
+	if (sta->vlan_id)
+		vlan_remove_dynamic(hapd, sta->vlan_id);
+	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);
+			sta->added_unassoc = 0;
+		}
+		vlan_remove_dynamic(hapd, sta->vlan_id_bound);
+	}
+#endif /* CONFIG_NO_VLAN */
+
 	os_free(sta->challenge);
 
 #ifdef CONFIG_IEEE80211W
@@ -296,6 +309,9 @@
 	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);
@@ -311,6 +327,9 @@
 	os_free(sta->sae);
 #endif /* CONFIG_SAE */
 
+	mbo_ap_sta_free(sta);
+	os_free(sta->supp_op_classes);
+
 	os_free(sta);
 }
 
@@ -350,8 +369,8 @@
 	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,
+	wpa_printf(MSG_DEBUG, "%s: %s: " MACSTR " flags=0x%x timeout_next=%d",
+		   hapd->conf->iface, __func__, MAC2STR(sta->addr), sta->flags,
 		   sta->timeout_next);
 	if (sta->timeout_next == STA_REMOVE) {
 		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
@@ -478,7 +497,7 @@
 			sta->acct_terminate_cause =
 				RADIUS_ACCT_TERMINATE_CAUSE_IDLE_TIMEOUT;
 		accounting_sta_stop(hapd, sta);
-		ieee802_1x_free_station(sta);
+		ieee802_1x_free_station(hapd, sta);
 		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
 			       HOSTAPD_LEVEL_INFO, "disassociated due to "
 			       "inactivity");
@@ -515,6 +534,8 @@
 	struct hostapd_data *hapd = eloop_ctx;
 	struct sta_info *sta = timeout_ctx;
 
+	wpa_printf(MSG_DEBUG, "%s: Session timer for STA " MACSTR,
+		   hapd->conf->iface, MAC2STR(sta->addr));
 	if (!(sta->flags & WLAN_STA_AUTH)) {
 		if (sta->flags & WLAN_STA_GAS) {
 			wpa_printf(MSG_DEBUG, "GAS: Remove temporary STA "
@@ -573,8 +594,8 @@
 	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));
+	wpa_printf(MSG_DEBUG, "%s: WNM: Session warning time reached for "
+		   MACSTR, hapd->conf->iface, MAC2STR(sta->addr));
 	if (sta->hs20_session_info_url == NULL)
 		return;
 
@@ -615,7 +636,10 @@
 		return NULL;
 	}
 	sta->acct_interim_interval = hapd->conf->acct_interim_interval;
-	accounting_sta_get_id(hapd, sta);
+	if (accounting_sta_get_id(hapd, sta) < 0) {
+		os_free(sta);
+		return NULL;
+	}
 
 	if (!(hapd->iface->drv_flags & WPA_DRIVER_FLAGS_INACTIVITY_TIMER)) {
 		wpa_printf(MSG_DEBUG, "%s: register ap_handle_timer timeout "
@@ -648,14 +672,16 @@
 		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));
+	wpa_printf(MSG_DEBUG, "%s: Removing STA " MACSTR " from kernel driver",
+		   hapd->conf->iface, 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));
+		wpa_printf(MSG_DEBUG, "%s: Could not remove station " MACSTR
+			   " from kernel driver",
+			   hapd->conf->iface, MAC2STR(sta->addr));
 		return -1;
 	}
+	sta->added_unassoc = 0;
 	return 0;
 }
 
@@ -679,6 +705,10 @@
 		if (!sta2)
 			continue;
 
+		wpa_printf(MSG_DEBUG, "%s: disconnect old STA " MACSTR
+			   " association from another BSS %s",
+			   hapd->conf->iface, MAC2STR(sta2->addr),
+			   bss->conf->iface);
 		ap_sta_disconnect(bss, sta2, sta2->addr,
 				  WLAN_REASON_PREV_AUTH_NOT_VALID);
 	}
@@ -690,6 +720,8 @@
 	struct hostapd_data *hapd = eloop_ctx;
 	struct sta_info *sta = timeout_ctx;
 
+	wpa_printf(MSG_DEBUG, "%s: Disassociation callback for STA " MACSTR,
+		   hapd->conf->iface, MAC2STR(sta->addr));
 	ap_sta_remove(hapd, sta);
 	mlme_disassociate_indication(hapd, sta, sta->disassoc_reason);
 }
@@ -713,7 +745,7 @@
 	eloop_register_timeout(AP_MAX_INACTIVITY_AFTER_DISASSOC, 0,
 			       ap_handle_timer, hapd, sta);
 	accounting_sta_stop(hapd, sta);
-	ieee802_1x_free_station(sta);
+	ieee802_1x_free_station(hapd, sta);
 
 	sta->disassoc_reason = reason;
 	sta->flags |= WLAN_STA_PENDING_DISASSOC_CB;
@@ -729,6 +761,8 @@
 	struct hostapd_data *hapd = eloop_ctx;
 	struct sta_info *sta = timeout_ctx;
 
+	wpa_printf(MSG_DEBUG, "%s: Deauthentication callback for STA " MACSTR,
+		   hapd->conf->iface, MAC2STR(sta->addr));
 	ap_sta_remove(hapd, sta);
 	mlme_deauthenticate_indication(hapd, sta, sta->deauth_reason);
 }
@@ -752,7 +786,7 @@
 	eloop_register_timeout(AP_MAX_INACTIVITY_AFTER_DEAUTH, 0,
 			       ap_handle_timer, hapd, sta);
 	accounting_sta_stop(hapd, sta);
-	ieee802_1x_free_station(sta);
+	ieee802_1x_free_station(hapd, sta);
 
 	sta->deauth_reason = reason;
 	sta->flags |= WLAN_STA_PENDING_DEAUTH_CB;
@@ -780,6 +814,128 @@
 #endif /* CONFIG_WPS */
 
 
+static int ap_sta_get_free_vlan_id(struct hostapd_data *hapd)
+{
+	struct hostapd_vlan *vlan;
+	int vlan_id = MAX_VLAN_ID + 2;
+
+retry:
+	for (vlan = hapd->conf->vlan; vlan; vlan = vlan->next) {
+		if (vlan->vlan_id == vlan_id) {
+			vlan_id++;
+			goto retry;
+		}
+	}
+	return vlan_id;
+}
+
+
+int ap_sta_set_vlan(struct hostapd_data *hapd, struct sta_info *sta,
+		    struct vlan_description *vlan_desc)
+{
+	struct hostapd_vlan *vlan = NULL, *wildcard_vlan = NULL;
+	int old_vlan_id, vlan_id = 0, ret = 0;
+
+	if (hapd->conf->ssid.dynamic_vlan == DYNAMIC_VLAN_DISABLED)
+		vlan_desc = NULL;
+
+	/* Check if there is something to do */
+	if (hapd->conf->ssid.per_sta_vif && !sta->vlan_id) {
+		/* This sta is lacking its own vif */
+	} else if (hapd->conf->ssid.dynamic_vlan == DYNAMIC_VLAN_DISABLED &&
+		   !hapd->conf->ssid.per_sta_vif && sta->vlan_id) {
+		/* sta->vlan_id needs to be reset */
+	} else if (!vlan_compare(vlan_desc, sta->vlan_desc)) {
+		return 0; /* nothing to change */
+	}
+
+	/* Now the real VLAN changed or the STA just needs its own vif */
+	if (hapd->conf->ssid.per_sta_vif) {
+		/* Assign a new vif, always */
+		/* find a free vlan_id sufficiently big */
+		vlan_id = ap_sta_get_free_vlan_id(hapd);
+		/* Get wildcard VLAN */
+		for (vlan = hapd->conf->vlan; vlan; vlan = vlan->next) {
+			if (vlan->vlan_id == VLAN_ID_WILDCARD)
+				break;
+		}
+		if (!vlan) {
+			hostapd_logger(hapd, sta->addr,
+				       HOSTAPD_MODULE_IEEE80211,
+				       HOSTAPD_LEVEL_DEBUG,
+				       "per_sta_vif missing wildcard");
+			vlan_id = 0;
+			ret = -1;
+			goto done;
+		}
+	} else if (vlan_desc && vlan_desc->notempty) {
+		for (vlan = hapd->conf->vlan; vlan; vlan = vlan->next) {
+			if (!vlan_compare(&vlan->vlan_desc, vlan_desc))
+				break;
+			if (vlan->vlan_id == VLAN_ID_WILDCARD)
+				wildcard_vlan = vlan;
+		}
+		if (vlan) {
+			vlan_id = vlan->vlan_id;
+		} else if (wildcard_vlan) {
+			vlan = wildcard_vlan;
+			vlan_id = vlan_desc->untagged;
+			if (vlan_desc->tagged[0]) {
+				/* Tagged VLAN configuration */
+				vlan_id = ap_sta_get_free_vlan_id(hapd);
+			}
+		} else {
+			hostapd_logger(hapd, sta->addr,
+				       HOSTAPD_MODULE_IEEE80211,
+				       HOSTAPD_LEVEL_DEBUG,
+				       "missing vlan and wildcard for vlan=%d%s",
+				       vlan_desc->untagged,
+				       vlan_desc->tagged[0] ? "+" : "");
+			vlan_id = 0;
+			ret = -1;
+			goto done;
+		}
+	}
+
+	if (vlan && vlan->vlan_id == VLAN_ID_WILDCARD) {
+		vlan = vlan_add_dynamic(hapd, vlan, vlan_id, vlan_desc);
+		if (vlan == NULL) {
+			hostapd_logger(hapd, sta->addr,
+				       HOSTAPD_MODULE_IEEE80211,
+				       HOSTAPD_LEVEL_DEBUG,
+				       "could not add dynamic VLAN interface for vlan=%d%s",
+				       vlan_desc ? vlan_desc->untagged : -1,
+				       (vlan_desc && vlan_desc->tagged[0]) ?
+				       "+" : "");
+			vlan_id = 0;
+			ret = -1;
+			goto done;
+		}
+
+		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+			       HOSTAPD_LEVEL_DEBUG,
+			       "added new dynamic VLAN interface '%s'",
+			       vlan->ifname);
+	} else if (vlan && vlan->dynamic_vlan > 0) {
+		vlan->dynamic_vlan++;
+		hostapd_logger(hapd, sta->addr,
+			       HOSTAPD_MODULE_IEEE80211,
+			       HOSTAPD_LEVEL_DEBUG,
+			       "updated existing dynamic VLAN interface '%s'",
+			       vlan->ifname);
+	}
+done:
+	old_vlan_id = sta->vlan_id;
+	sta->vlan_id = vlan_id;
+	sta->vlan_desc = vlan ? &vlan->vlan_desc : NULL;
+
+	if (vlan_id != old_vlan_id && old_vlan_id)
+		vlan_remove_dynamic(hapd, old_vlan_id);
+
+	return ret;
+}
+
+
 int ap_sta_bind_vlan(struct hostapd_data *hapd, struct sta_info *sta)
 {
 #ifndef CONFIG_NO_VLAN
@@ -792,20 +948,11 @@
 	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 (sta->vlan_id > 0) {
+		for (vlan = hapd->conf->vlan; vlan; vlan = vlan->next) {
 			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;
 	}
@@ -825,54 +972,13 @@
 			       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);
-		}
+	} else if (vlan && 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);
 	}
 
 	/* ref counters have been increased, so mark the station */
@@ -938,6 +1044,10 @@
 	unsigned int timeout, sec, usec;
 	u8 *trans_id, *nbuf;
 
+	wpa_printf(MSG_DEBUG, "%s: SA Query timer for STA " MACSTR
+		   " (count=%d)",
+		   hapd->conf->iface, MAC2STR(sta->addr), sta->sa_query_count);
+
 	if (sta->sa_query_count > 0 &&
 	    ap_check_sa_query_timeout(hapd, sta))
 		return;
@@ -1060,12 +1170,30 @@
 			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)
+		wpa_printf(MSG_DEBUG, "%s: %s STA " MACSTR " reason=%u",
+			   hapd->conf->iface, __func__, MAC2STR(sta->addr),
+			   reason);
+	else if (addr)
+		wpa_printf(MSG_DEBUG, "%s: %s addr " MACSTR " reason=%u",
+			   hapd->conf->iface, __func__, MAC2STR(addr),
+			   reason);
 
 	if (sta == NULL && addr)
 		sta = ap_get_sta(hapd, addr);
@@ -1079,10 +1207,10 @@
 	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 "
+	wpa_printf(MSG_DEBUG, "%s: %s: reschedule ap_handle_timer timeout "
 		   "for " MACSTR " (%d seconds - "
 		   "AP_MAX_INACTIVITY_AFTER_DEAUTH)",
-		   __func__, MAC2STR(sta->addr),
+		   hapd->conf->iface, __func__, MAC2STR(sta->addr),
 		   AP_MAX_INACTIVITY_AFTER_DEAUTH);
 	eloop_cancel_timeout(ap_handle_timer, hapd, sta);
 	eloop_register_timeout(AP_MAX_INACTIVITY_AFTER_DEAUTH, 0,
@@ -1122,6 +1250,22 @@
 }
 
 
+void ap_sta_clear_disconnect_timeouts(struct hostapd_data *hapd,
+				      struct sta_info *sta)
+{
+	if (eloop_cancel_timeout(ap_sta_deauth_cb_timeout, hapd, sta) > 0)
+		wpa_printf(MSG_DEBUG,
+			   "%s: Removed ap_sta_deauth_cb_timeout timeout for "
+			   MACSTR,
+			   hapd->conf->iface, MAC2STR(sta->addr));
+	if (eloop_cancel_timeout(ap_sta_disassoc_cb_timeout, hapd, sta) > 0)
+		wpa_printf(MSG_DEBUG,
+			   "%s: Removed ap_sta_disassoc_cb_timeout timeout for "
+			   MACSTR,
+			   hapd->conf->iface, MAC2STR(sta->addr));
+}
+
+
 int ap_sta_flags_txt(u32 flags, char *buf, size_t buflen)
 {
 	int res;
diff --git a/src/ap/sta_info.h b/src/ap/sta_info.h
index 52a9997..3d9a928 100644
--- a/src/ap/sta_info.h
+++ b/src/ap/sta_info.h
@@ -15,6 +15,7 @@
 #endif /* CONFIG_MESH */
 
 #include "list.h"
+#include "vlan.h"
 
 /* STA flags */
 #define WLAN_STA_AUTH BIT(0)
@@ -45,6 +46,21 @@
 #define WLAN_SUPP_RATES_MAX 32
 
 
+struct mbo_non_pref_chan_info {
+	struct mbo_non_pref_chan_info *next;
+	u8 op_class;
+	u8 pref;
+	u8 reason_code;
+	u8 reason_detail;
+	u8 num_channels;
+	u8 channels[];
+};
+
+struct pending_eapol_rx {
+	struct wpabuf *buf;
+	struct os_reltime rx_time;
+};
+
 struct sta_info {
 	struct sta_info *next; /* next entry in sta list */
 	struct sta_info *hnext; /* next entry in hash table list */
@@ -86,6 +102,8 @@
 	unsigned int hs20_deauth_requested:1;
 	unsigned int session_timeout_set:1;
 	unsigned int radius_das_match:1;
+	unsigned int ecsa_supported:1;
+	unsigned int added_unassoc:1;
 
 	u16 auth_alg;
 
@@ -100,17 +118,20 @@
 	/* IEEE 802.1X related data */
 	struct eapol_state_machine *eapol_sm;
 
-	u32 acct_session_id_hi;
-	u32 acct_session_id_lo;
+	struct pending_eapol_rx *pending_eapol_rx;
+
+	u64 acct_session_id;
 	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 int acct_interim_errors;
 
-	unsigned long last_rx_bytes;
-	unsigned long last_tx_bytes;
-	u32 acct_input_gigawords; /* Acct-Input-Gigawords */
-	u32 acct_output_gigawords; /* Acct-Output-Gigawords */
+	/* For extending 32-bit driver counters to 64-bit counters */
+	u32 last_rx_bytes_hi;
+	u32 last_rx_bytes_lo;
+	u32 last_tx_bytes_hi;
+	u32 last_tx_bytes_lo;
 
 	u8 *challenge; /* IEEE 802.11 Shared Key Authentication Challenge */
 
@@ -118,6 +139,7 @@
 	struct rsn_preauth_interface *preauth_iface;
 
 	int vlan_id; /* 0: none, >0: VID */
+	struct vlan_description *vlan_desc;
 	int vlan_id_bound; /* updated by ap_sta_bind_vlan() */
 	 /* PSKs from RADIUS authentication server */
 	struct hostapd_sta_wpa_psk_short *psk;
@@ -153,11 +175,15 @@
 	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;
+	unsigned int mesh_sae_pmksa_caching:1;
 #endif /* CONFIG_SAE */
 
 	u32 session_timeout; /* valid only if session_timeout_set == 1 */
@@ -167,6 +193,15 @@
 	u16 last_seq_ctrl;
 	/* Last Authentication/(Re)Association Request/Action frame subtype */
 	u8 last_subtype;
+
+#ifdef CONFIG_MBO
+	u8 cell_capa; /* 0 = unknown (not an MBO STA); otherwise,
+		       * enum mbo_cellular_capa values */
+	struct mbo_non_pref_chan_info *non_pref_chan;
+#endif /* CONFIG_MBO */
+
+	u8 *supp_op_classes; /* Supported Operating Classes element, if
+			      * received, starting from the Length field */
 };
 
 
@@ -177,7 +212,7 @@
  * 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_DISASSOC_DELAY (3)
 #define AP_DEAUTH_DELAY (1)
 /* Number of seconds to keep STA entry with Authenticated flag after it has
  * been disassociated. */
@@ -217,6 +252,8 @@
 		      struct sta_info *sta, void *ctx);
 #endif /* CONFIG_WPS */
 int ap_sta_bind_vlan(struct hostapd_data *hapd, struct sta_info *sta);
+int ap_sta_set_vlan(struct hostapd_data *hapd, struct sta_info *sta,
+		    struct vlan_description *vlan_desc);
 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);
@@ -232,6 +269,8 @@
 
 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);
+void ap_sta_clear_disconnect_timeouts(struct hostapd_data *hapd,
+				      struct sta_info *sta);
 
 int ap_sta_flags_txt(u32 flags, char *buf, size_t buflen);
 
diff --git a/src/ap/utils.c b/src/ap/utils.c
index d60555a..fcb371b 100644
--- a/src/ap/utils.c
+++ b/src/ap/utils.c
@@ -10,6 +10,7 @@
 
 #include "common.h"
 #include "common/ieee802_11_defs.h"
+#include "fst/fst.h"
 #include "sta_info.h"
 #include "hostapd.h"
 
@@ -55,6 +56,14 @@
 		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;
diff --git a/src/ap/vlan.c b/src/ap/vlan.c
new file mode 100644
index 0000000..b6f6bb1
--- /dev/null
+++ b/src/ap/vlan.c
@@ -0,0 +1,34 @@
+/*
+ * hostapd / VLAN definition
+ * Copyright (c) 2016, 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 "ap/vlan.h"
+
+/* compare the two arguments, NULL is treated as empty
+ * return zero iff they are equal
+ */
+int vlan_compare(struct vlan_description *a, struct vlan_description *b)
+{
+	int i;
+	const int a_empty = !a || !a->notempty;
+	const int b_empty = !b || !b->notempty;
+
+	if (a_empty && b_empty)
+		return 0;
+	if (a_empty || b_empty)
+		return 1;
+	if (a->untagged != b->untagged)
+		return 1;
+	for (i = 0; i < MAX_NUM_TAGGED_VLAN; i++) {
+		if (a->tagged[i] != b->tagged[i])
+			return 1;
+	}
+	return 0;
+}
diff --git a/src/ap/vlan.h b/src/ap/vlan.h
new file mode 100644
index 0000000..af84929
--- /dev/null
+++ b/src/ap/vlan.h
@@ -0,0 +1,30 @@
+/*
+ * hostapd / VLAN definition
+ * 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 VLAN_H
+#define VLAN_H
+
+#define MAX_NUM_TAGGED_VLAN 32
+
+struct vlan_description {
+	int notempty; /* 0 : no vlan information present, 1: else */
+	int untagged; /* >0 802.1q vid */
+	int tagged[MAX_NUM_TAGGED_VLAN]; /* first k items, ascending order */
+};
+
+#ifndef CONFIG_NO_VLAN
+int vlan_compare(struct vlan_description *a, struct vlan_description *b);
+#else /* CONFIG_NO_VLAN */
+static inline int
+vlan_compare(struct vlan_description *a, struct vlan_description *b)
+{
+	return 0;
+}
+#endif /* CONFIG_NO_VLAN */
+
+#endif /* VLAN_H */
diff --git a/src/ap/vlan_full.c b/src/ap/vlan_full.c
new file mode 100644
index 0000000..aa42335
--- /dev/null
+++ b/src/ap/vlan_full.c
@@ -0,0 +1,752 @@
+/*
+ * hostapd / VLAN initialization - full dynamic VLAN
+ * 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"
+#include <net/if.h>
+/* Avoid conflicts due to NetBSD net/if.h if_type define with driver.h */
+#undef if_type
+#include <sys/ioctl.h>
+
+#include "utils/common.h"
+#include "drivers/priv_netlink.h"
+#include "common/linux_bridge.h"
+#include "common/linux_vlan.h"
+#include "utils/eloop.h"
+#include "hostapd.h"
+#include "ap_config.h"
+#include "ap_drv_ops.h"
+#include "wpa_auth.h"
+#include "vlan_init.h"
+#include "vlan_util.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_down(const char *if_name)
+{
+	wpa_printf(MSG_DEBUG, "VLAN: Set interface %s down", if_name);
+	return ifconfig_helper(if_name, 0);
+}
+
+
+/* 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 = (void *) 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 = (void *) 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 = (void *) 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;
+}
+
+
+static void vlan_newlink_tagged(int vlan_naming, const char *tagged_interface,
+				const char *br_name, int vid,
+				struct hostapd_data *hapd)
+{
+	char vlan_ifname[IFNAMSIZ];
+	int clean;
+
+	if (vlan_naming == DYNAMIC_VLAN_NAMING_WITH_DEVICE)
+		os_snprintf(vlan_ifname, sizeof(vlan_ifname), "%s.%d",
+			    tagged_interface, vid);
+	else
+		os_snprintf(vlan_ifname, sizeof(vlan_ifname), "vlan%d", vid);
+
+	clean = 0;
+	ifconfig_up(tagged_interface);
+	if (!vlan_add(tagged_interface, vid, 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);
+}
+
+
+static void vlan_bridge_name(char *br_name, struct hostapd_data *hapd, int vid)
+{
+	char *tagged_interface = hapd->conf->ssid.vlan_tagged_interface;
+
+	if (hapd->conf->vlan_bridge[0]) {
+		os_snprintf(br_name, IFNAMSIZ, "%s%d",
+			    hapd->conf->vlan_bridge, vid);
+	} else if (tagged_interface) {
+		os_snprintf(br_name, IFNAMSIZ, "br%s.%d",
+			    tagged_interface, vid);
+	} else {
+		os_snprintf(br_name, IFNAMSIZ, "brvlan%d", vid);
+	}
+}
+
+
+static void vlan_get_bridge(const char *br_name, struct hostapd_data *hapd,
+			    int vid)
+{
+	char *tagged_interface = hapd->conf->ssid.vlan_tagged_interface;
+	int vlan_naming = hapd->conf->ssid.vlan_naming;
+
+	dyn_iface_get(hapd, br_name, br_addbr(br_name) ? 0 : DVLAN_CLEAN_BR);
+
+	ifconfig_up(br_name);
+
+	if (tagged_interface)
+		vlan_newlink_tagged(vlan_naming, tagged_interface, br_name,
+				    vid, hapd);
+}
+
+
+void vlan_newlink(const char *ifname, struct hostapd_data *hapd)
+{
+	char br_name[IFNAMSIZ];
+	struct hostapd_vlan *vlan;
+	int untagged, *tagged, i, notempty;
+
+	wpa_printf(MSG_DEBUG, "VLAN: vlan_newlink(%s)", ifname);
+
+	for (vlan = hapd->conf->vlan; vlan; vlan = vlan->next) {
+		if (vlan->configured ||
+		    os_strcmp(ifname, vlan->ifname) != 0)
+			continue;
+		break;
+	}
+	if (!vlan)
+		return;
+
+	vlan->configured = 1;
+
+	notempty = vlan->vlan_desc.notempty;
+	untagged = vlan->vlan_desc.untagged;
+	tagged = vlan->vlan_desc.tagged;
+
+	if (!notempty) {
+		/* Non-VLAN STA */
+		if (hapd->conf->bridge[0] &&
+		    !br_addif(hapd->conf->bridge, ifname))
+			vlan->clean |= DVLAN_CLEAN_WLAN_PORT;
+	} else if (untagged > 0 && untagged <= MAX_VLAN_ID) {
+		vlan_bridge_name(br_name, hapd, untagged);
+
+		vlan_get_bridge(br_name, hapd, untagged);
+
+		if (!br_addif(br_name, ifname))
+			vlan->clean |= DVLAN_CLEAN_WLAN_PORT;
+	}
+
+	for (i = 0; i < MAX_NUM_TAGGED_VLAN && tagged[i]; i++) {
+		if (tagged[i] == untagged ||
+		    tagged[i] <= 0 || tagged[i] > MAX_VLAN_ID ||
+		    (i > 0 && tagged[i] == tagged[i - 1]))
+			continue;
+		vlan_bridge_name(br_name, hapd, tagged[i]);
+		vlan_get_bridge(br_name, hapd, tagged[i]);
+		vlan_newlink_tagged(DYNAMIC_VLAN_NAMING_WITH_DEVICE,
+				    ifname, br_name, tagged[i], hapd);
+	}
+
+	ifconfig_up(ifname);
+}
+
+
+static void vlan_dellink_tagged(int vlan_naming, const char *tagged_interface,
+				const char *br_name, int vid,
+				struct hostapd_data *hapd)
+{
+	char vlan_ifname[IFNAMSIZ];
+	int clean;
+
+	if (vlan_naming == DYNAMIC_VLAN_NAMING_WITH_DEVICE)
+		os_snprintf(vlan_ifname, sizeof(vlan_ifname), "%s.%d",
+			    tagged_interface, vid);
+	else
+		os_snprintf(vlan_ifname, sizeof(vlan_ifname), "vlan%d", vid);
+
+	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);
+	}
+}
+
+
+static void vlan_put_bridge(const char *br_name, struct hostapd_data *hapd,
+			    int vid)
+{
+	int clean;
+	char *tagged_interface = hapd->conf->ssid.vlan_tagged_interface;
+	int vlan_naming = hapd->conf->ssid.vlan_naming;
+
+	if (tagged_interface)
+		vlan_dellink_tagged(vlan_naming, tagged_interface, br_name,
+				    vid, hapd);
+
+	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);
+	}
+}
+
+
+void vlan_dellink(const char *ifname, struct hostapd_data *hapd)
+{
+	struct hostapd_vlan *first, *prev, *vlan = hapd->conf->vlan;
+
+	wpa_printf(MSG_DEBUG, "VLAN: vlan_dellink(%s)", ifname);
+
+	first = prev = vlan;
+
+	while (vlan) {
+		if (os_strcmp(ifname, vlan->ifname) != 0) {
+			prev = vlan;
+			vlan = vlan->next;
+			continue;
+		}
+		break;
+	}
+	if (!vlan)
+		return;
+
+	if (vlan->configured) {
+		int notempty = vlan->vlan_desc.notempty;
+		int untagged = vlan->vlan_desc.untagged;
+		int *tagged = vlan->vlan_desc.tagged;
+		char br_name[IFNAMSIZ];
+		int i;
+
+		for (i = 0; i < MAX_NUM_TAGGED_VLAN && tagged[i]; i++) {
+			if (tagged[i] == untagged ||
+			    tagged[i] <= 0 || tagged[i] > MAX_VLAN_ID ||
+			    (i > 0 && tagged[i] == tagged[i - 1]))
+				continue;
+			vlan_bridge_name(br_name, hapd, tagged[i]);
+			vlan_dellink_tagged(DYNAMIC_VLAN_NAMING_WITH_DEVICE,
+					    ifname, br_name, tagged[i], hapd);
+			vlan_put_bridge(br_name, hapd, tagged[i]);
+		}
+
+		if (!notempty) {
+			/* Non-VLAN STA */
+			if (hapd->conf->bridge[0] &&
+			    (vlan->clean & DVLAN_CLEAN_WLAN_PORT))
+				br_delif(hapd->conf->bridge, ifname);
+		} else if (untagged > 0 && untagged <= MAX_VLAN_ID) {
+			vlan_bridge_name(br_name, hapd, untagged);
+
+			if (vlan->clean & DVLAN_CLEAN_WLAN_PORT)
+				br_delif(br_name, vlan->ifname);
+
+			vlan_put_bridge(br_name, hapd, untagged);
+		}
+	}
+
+	/*
+	 * Ensure this VLAN interface is actually removed even if
+	 * NEWLINK message is only received later.
+	 */
+	if (if_nametoindex(vlan->ifname) && vlan_if_remove(hapd, vlan))
+		wpa_printf(MSG_ERROR,
+			   "VLAN: Could not remove VLAN iface: %s: %s",
+			   vlan->ifname, strerror(errno));
+
+	if (vlan == first)
+		hapd->conf->vlan = vlan->next;
+	else
+		prev->next = vlan->next;
+
+	os_free(vlan);
+}
+
+
+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);
+	}
+}
+
+
+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;
+
+	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);
+
+	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;
+}
+
+
+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);
+}
diff --git a/src/ap/vlan_ifconfig.c b/src/ap/vlan_ifconfig.c
new file mode 100644
index 0000000..ef953a5
--- /dev/null
+++ b/src/ap/vlan_ifconfig.c
@@ -0,0 +1,69 @@
+/*
+ * hostapd / VLAN ifconfig helpers
+ * 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"
+#include <net/if.h>
+#include <sys/ioctl.h>
+
+#include "utils/common.h"
+#include "vlan_util.h"
+
+
+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;
+}
+
+
+int ifconfig_up(const char *if_name)
+{
+	wpa_printf(MSG_DEBUG, "VLAN: Set interface %s up", if_name);
+	return ifconfig_helper(if_name, 1);
+}
+
+
+int iface_exists(const char *ifname)
+{
+	return if_nametoindex(ifname);
+}
diff --git a/src/ap/vlan_init.c b/src/ap/vlan_init.c
index fd1c8dd..31e4fc6 100644
--- a/src/ap/vlan_init.c
+++ b/src/ap/vlan_init.c
@@ -9,902 +9,72 @@
  */
 
 #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 "wpa_auth.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)
+static int vlan_if_add(struct hostapd_data *hapd, struct hostapd_vlan *vlan,
+		       int existsok)
 {
-	struct dynamic_iface *next, **dynamic_ifaces;
-	struct hapd_interfaces *interfaces;
+	int ret, i;
 
-	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;
+	for (i = 0; i < NUM_WEP_KEYS; i++) {
+		if (!hapd->conf->ssid.wep.key[i])
+			continue;
+		wpa_printf(MSG_ERROR,
+			   "VLAN: Refusing to set up VLAN iface %s with WEP",
+			   vlan->ifname);
+		return -1;
 	}
 
-	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;
+	if (!iface_exists(vlan->ifname))
+		ret = hostapd_vlan_if_add(hapd, vlan->ifname);
+	else if (!existsok)
+		return -1;
 	else
-		*dynamic_ifaces = next->next;
-	clean = next->clean;
-	os_free(next);
+		ret = 0;
 
-	return clean;
+	if (ret)
+		return ret;
+
+	ifconfig_up(vlan->ifname); /* else wpa group will fail fatal */
+
+	if (hapd->wpa_auth)
+		ret = wpa_auth_ensure_group(hapd->wpa_auth, vlan->vlan_id);
+
+	if (ret == 0)
+		return ret;
+
+	wpa_printf(MSG_ERROR, "WPA initialization for VLAN %d failed (%d)",
+		   vlan->vlan_id, ret);
+	if (wpa_auth_release_group(hapd->wpa_auth, vlan->vlan_id))
+		wpa_printf(MSG_ERROR, "WPA deinit of %s failed", vlan->ifname);
+
+	/* group state machine setup failed */
+	if (hostapd_vlan_if_remove(hapd, vlan->ifname))
+		wpa_printf(MSG_ERROR, "Removal of %s failed", vlan->ifname);
+
+	return ret;
 }
 
 
-static int ifconfig_helper(const char *if_name, int up)
+int vlan_if_remove(struct hostapd_data *hapd, struct hostapd_vlan *vlan)
 {
-	int fd;
-	struct ifreq ifr;
+	int ret;
 
-	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;
-	}
+	ret = wpa_auth_release_group(hapd->wpa_auth, vlan->vlan_id);
+	if (ret)
+		wpa_printf(MSG_ERROR,
+			   "WPA deinitialization for VLAN %d failed (%d)",
+			   vlan->vlan_id, ret);
 
-	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;
+	return hostapd_vlan_if_remove(hapd, vlan->ifname);
 }
 
 
@@ -913,17 +83,14 @@
 {
 	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;
-				}
+			if (vlan_if_add(hapd, vlan, 1)) {
+				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);
+			vlan_newlink(vlan->ifname, hapd);
 #endif /* CONFIG_FULL_DYNAMIC_VLAN */
 		}
 
@@ -942,15 +109,17 @@
 	while (vlan) {
 		next = vlan->next;
 
+#ifdef CONFIG_FULL_DYNAMIC_VLAN
+		/* vlan_dellink() takes care of cleanup and interface removal */
+		if (vlan->vlan_id != VLAN_ID_WILDCARD)
+			vlan_dellink(vlan->ifname, hapd);
+#else /* CONFIG_FULL_DYNAMIC_VLAN */
 		if (vlan->vlan_id != VLAN_ID_WILDCARD &&
-		    hostapd_vlan_if_remove(hapd, vlan->ifname)) {
+		    vlan_if_remove(hapd, vlan)) {
 			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;
@@ -964,7 +133,8 @@
 	hapd->full_dynamic_vlan = full_dynamic_vlan_init(hapd);
 #endif /* CONFIG_FULL_DYNAMIC_VLAN */
 
-	if (hapd->conf->ssid.dynamic_vlan != DYNAMIC_VLAN_DISABLED &&
+	if ((hapd->conf->ssid.dynamic_vlan != DYNAMIC_VLAN_DISABLED ||
+	     hapd->conf->ssid.per_sta_vif) &&
 	    !hapd->conf->vlan) {
 		/* dynamic vlans enabled but no (or empty) vlan_file given */
 		struct hostapd_vlan *vlan;
@@ -1002,50 +172,45 @@
 
 struct hostapd_vlan * vlan_add_dynamic(struct hostapd_data *hapd,
 				       struct hostapd_vlan *vlan,
-				       int vlan_id)
+				       int vlan_id,
+				       struct vlan_description *vlan_desc)
 {
-	struct hostapd_vlan *n = NULL;
-	char *ifname, *pos;
+	struct hostapd_vlan *n;
+	char ifname[IFNAMSIZ + 1], *pos;
 
-	if (vlan == NULL || vlan_id <= 0 || vlan_id > MAX_VLAN_ID ||
-	    vlan->vlan_id != VLAN_ID_WILDCARD)
+	if (vlan == NULL || 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;
+	os_strlcpy(ifname, vlan->ifname, sizeof(ifname));
 	pos = os_strchr(ifname, '#');
 	if (pos == NULL)
-		goto free_ifname;
+		return NULL;
 	*pos++ = '\0';
 
 	n = os_zalloc(sizeof(*n));
 	if (n == NULL)
-		goto free_ifname;
+		return NULL;
 
 	n->vlan_id = vlan_id;
+	if (vlan_desc)
+		n->vlan_desc = *vlan_desc;
 	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 */
+	/* hapd->conf->vlan needs this new VLAN here for WPA setup */
+	if (vlan_if_add(hapd, n, 0)) {
+		hapd->conf->vlan = n->next;
+		os_free(n);
+		n = NULL;
+	}
 
-free_ifname:
-	os_free(ifname);
 	return n;
 }
 
@@ -1054,7 +219,7 @@
 {
 	struct hostapd_vlan *vlan;
 
-	if (vlan_id <= 0 || vlan_id > MAX_VLAN_ID)
+	if (vlan_id <= 0)
 		return 1;
 
 	wpa_printf(MSG_DEBUG, "VLAN: %s(ifname=%s vlan_id=%d)",
@@ -1073,7 +238,7 @@
 		return 1;
 
 	if (vlan->dynamic_vlan == 0) {
-		hostapd_vlan_if_remove(hapd, vlan->ifname);
+		vlan_if_remove(hapd, vlan);
 #ifdef CONFIG_FULL_DYNAMIC_VLAN
 		vlan_dellink(vlan->ifname, hapd);
 #endif /* CONFIG_FULL_DYNAMIC_VLAN */
diff --git a/src/ap/vlan_init.h b/src/ap/vlan_init.h
index fc39443..d17c82c 100644
--- a/src/ap/vlan_init.h
+++ b/src/ap/vlan_init.h
@@ -15,10 +15,9 @@
 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_id,
+				       struct vlan_description *vlan_desc);
 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)
 {
@@ -29,9 +28,9 @@
 {
 }
 
-static inline struct hostapd_vlan * vlan_add_dynamic(struct hostapd_data *hapd,
-						     struct hostapd_vlan *vlan,
-						     int vlan_id)
+static inline struct hostapd_vlan *
+vlan_add_dynamic(struct hostapd_data *hapd, struct hostapd_vlan *vlan,
+		 int vlan_id, struct vlan_description *vlan_desc)
 {
 	return NULL;
 }
@@ -40,12 +39,6 @@
 {
 	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/src/ap/vlan_ioctl.c b/src/ap/vlan_ioctl.c
new file mode 100644
index 0000000..987b612
--- /dev/null
+++ b/src/ap/vlan_ioctl.c
@@ -0,0 +1,155 @@
+/*
+ * hostapd / VLAN ioctl API
+ * 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"
+#include <sys/ioctl.h>
+
+#include "utils/common.h"
+#include "common/linux_vlan.h"
+#include "vlan_util.h"
+
+
+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_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;
+}
+
+
+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;
+}
diff --git a/src/ap/vlan_util.c b/src/ap/vlan_util.c
index d4e0efb..56d1d3d 100644
--- a/src/ap/vlan_util.c
+++ b/src/ap/vlan_util.c
@@ -7,18 +7,10 @@
  */
 
 #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"
 
 /*
@@ -33,7 +25,6 @@
 {
 	int err, ret = -1;
 	struct nl_sock *handle = NULL;
-	struct nl_cache *cache = NULL;
 	struct rtnl_link *rlink = NULL;
 	int if_idx = 0;
 
@@ -65,22 +56,19 @@
 		goto vlan_add_error;
 	}
 
-	err = rtnl_link_alloc_cache(handle, AF_UNSPEC, &cache);
+	err = rtnl_link_get_kernel(handle, 0, if_name, &rlink);
 	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_idx = rtnl_link_get_ifindex(rlink);
+	rtnl_link_put(rlink);
+	rlink = NULL;
 
-	if ((rlink = rtnl_link_get_by_name(cache, vlan_if_name))) {
+	err = rtnl_link_get_kernel(handle, 0, vlan_if_name, &rlink);
+	if (err >= 0) {
 		/* link does exist */
 		rtnl_link_put(rlink);
 		rlink = NULL;
@@ -127,8 +115,6 @@
 vlan_add_error:
 	if (rlink)
 		rtnl_link_put(rlink);
-	if (cache)
-		nl_cache_free(cache);
 	if (handle)
 		nl_socket_free(handle);
 	return ret;
@@ -139,7 +125,6 @@
 {
 	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);
@@ -157,15 +142,8 @@
 		goto vlan_rem_error;
 	}
 
-	err = rtnl_link_alloc_cache(handle, AF_UNSPEC, &cache);
+	err = rtnl_link_get_kernel(handle, 0, if_name, &rlink);
 	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);
@@ -184,9 +162,13 @@
 vlan_rem_error:
 	if (rlink)
 		rtnl_link_put(rlink);
-	if (cache)
-		nl_cache_free(cache);
 	if (handle)
 		nl_socket_free(handle);
 	return ret;
 }
+
+
+int vlan_set_name_type(unsigned int name_type)
+{
+	return 0;
+}
diff --git a/src/ap/vlan_util.h b/src/ap/vlan_util.h
index bef5a16..2446859 100644
--- a/src/ap/vlan_util.h
+++ b/src/ap/vlan_util.h
@@ -1,5 +1,5 @@
 /*
- * hostapd / VLAN netlink api
+ * hostapd / VLAN netlink/ioctl api
  * Copyright (c) 2012, Michael Braun <michael-dev@fami-braun.de>
  *
  * This software may be distributed under the terms of the BSD license.
@@ -9,7 +9,23 @@
 #ifndef VLAN_UTIL_H
 #define VLAN_UTIL_H
 
+struct hostapd_data;
+struct hostapd_vlan;
+struct full_dynamic_vlan;
+
 int vlan_add(const char *if_name, int vid, const char *vlan_if_name);
 int vlan_rem(const char *if_name);
+int vlan_set_name_type(unsigned int name_type);
+
+int ifconfig_helper(const char *if_name, int up);
+int ifconfig_up(const char *if_name);
+int iface_exists(const char *ifname);
+int vlan_if_remove(struct hostapd_data *hapd, struct hostapd_vlan *vlan);
+
+struct full_dynamic_vlan *
+full_dynamic_vlan_init(struct hostapd_data *hapd);
+void full_dynamic_vlan_deinit(struct full_dynamic_vlan *priv);
+void vlan_newlink(const char *ifname, struct hostapd_data *hapd);
+void vlan_dellink(const char *ifname, struct hostapd_data *hapd);
 
 #endif /* VLAN_UTIL_H */
diff --git a/src/ap/wnm_ap.c b/src/ap/wnm_ap.c
index 4c8bc10..41d50ce 100644
--- a/src/ap/wnm_ap.c
+++ b/src/ap/wnm_ap.c
@@ -17,6 +17,7 @@
 #include "ap/ap_config.h"
 #include "ap/ap_drv_ops.h"
 #include "ap/wpa_auth.h"
+#include "mbo_ap.h"
 #include "wnm_ap.h"
 
 #define MAX_TFS_IE_LEN  1024
@@ -94,6 +95,7 @@
 	if (mgmt == NULL) {
 		wpa_printf(MSG_DEBUG, "MLME: Failed to allocate buffer for "
 			   "WNM-Sleep Response action frame");
+		os_free(wnmtfs_ie);
 		return -1;
 	}
 	os_memcpy(mgmt->da, addr, ETH_ALEN);
@@ -376,6 +378,29 @@
 }
 
 
+static void ieee802_11_rx_wnm_notification_req(struct hostapd_data *hapd,
+					       const u8 *addr, const u8 *buf,
+					       size_t len)
+{
+	u8 dialog_token, type;
+
+	if (len < 2)
+		return;
+	dialog_token = *buf++;
+	type = *buf++;
+	len -= 2;
+
+	wpa_printf(MSG_DEBUG,
+		   "WNM: Received WNM Notification Request frame from "
+		   MACSTR " (dialog_token=%u type=%u)",
+		   MAC2STR(addr), dialog_token, type);
+	wpa_hexdump(MSG_MSGDUMP, "WNM: Notification Request subelements",
+		    buf, len);
+	if (type == WLAN_EID_VENDOR_SPECIFIC)
+		mbo_ap_wnm_notification_req(hapd, addr, buf, len);
+}
+
+
 int ieee802_11_rx_wnm_action_ap(struct hostapd_data *hapd,
 				const struct ieee80211_mgmt *mgmt, size_t len)
 {
@@ -402,6 +427,10 @@
 	case WNM_SLEEP_MODE_REQ:
 		ieee802_11_rx_wnmsleep_req(hapd, mgmt->sa, payload, plen);
 		return 0;
+	case WNM_NOTIFICATION_REQ:
+		ieee802_11_rx_wnm_notification_req(hapd, mgmt->sa, payload,
+						   plen);
+		return 0;
 	}
 
 	wpa_printf(MSG_DEBUG, "WNM: Unsupported WNM Action %u from " MACSTR,
@@ -527,7 +556,8 @@
 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)
+			const u8 *nei_rep, size_t nei_rep_len,
+			const u8 *mbo_attrs, size_t mbo_len)
 {
 	u8 *buf, *pos;
 	struct ieee80211_mgmt *mgmt;
@@ -536,7 +566,7 @@
 	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);
+	buf = os_zalloc(1000 + nei_rep_len + mbo_len);
 	if (buf == NULL)
 		return -1;
 	mgmt = (struct ieee80211_mgmt *) buf;
@@ -579,6 +609,11 @@
 		pos += nei_rep_len;
 	}
 
+	if (mbo_len > 0) {
+		pos += mbo_add_ie(pos, buf + sizeof(buf) - pos, mbo_attrs,
+				  mbo_len);
+	}
+
 	if (hostapd_drv_send_mlme(hapd, buf, pos - buf, 0) < 0) {
 		wpa_printf(MSG_DEBUG,
 			   "Failed to send BSS Transition Management Request frame");
diff --git a/src/ap/wnm_ap.h b/src/ap/wnm_ap.h
index 7789307..a44eadb 100644
--- a/src/ap/wnm_ap.h
+++ b/src/ap/wnm_ap.h
@@ -21,6 +21,7 @@
 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);
+			const u8 *nei_rep, size_t nei_rep_len,
+			const u8 *mbo_attrs, size_t mbo_len);
 
 #endif /* WNM_AP_H */
diff --git a/src/ap/wpa_auth.c b/src/ap/wpa_auth.c
index f23a57a..3587086 100644
--- a/src/ap/wpa_auth.c
+++ b/src/ap/wpa_auth.c
@@ -44,7 +44,8 @@
 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);
+			  const u8 *pmk, unsigned int pmk_len,
+			  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,
@@ -827,6 +828,7 @@
 	struct wpa_ptk PTK;
 	int ok = 0;
 	const u8 *pmk = NULL;
+	unsigned int pmk_len;
 
 	for (;;) {
 		if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt)) {
@@ -834,10 +836,13 @@
 					       sm->p2p_dev_addr, pmk);
 			if (pmk == NULL)
 				break;
-		} else
+			pmk_len = PMK_LEN;
+		} else {
 			pmk = sm->PMK;
+			pmk_len = sm->pmk_len;
+		}
 
-		wpa_derive_ptk(sm, sm->alt_SNonce, pmk, &PTK);
+		wpa_derive_ptk(sm, sm->alt_SNonce, pmk, pmk_len, &PTK);
 
 		if (wpa_verify_key_mic(sm->wpa_key_mgmt, &PTK, data, data_len)
 		    == 0) {
@@ -1540,6 +1545,7 @@
 			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,
@@ -1555,6 +1561,7 @@
 			else
 				WPA_PUT_BE16(key->key_data_length,
 					     key_data_len);
+#endif /* CONFIG_NO_RC4 */
 		} else {
 			os_free(hdr);
 			os_free(buf);
@@ -1669,7 +1676,7 @@
 }
 
 
-int wpa_auth_sm_event(struct wpa_state_machine *sm, wpa_event event)
+int wpa_auth_sm_event(struct wpa_state_machine *sm, enum wpa_event event)
 {
 	int remove_ptk = 1;
 
@@ -1757,6 +1764,14 @@
 			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);
 }
 
@@ -1841,9 +1856,13 @@
 		group->reject_4way_hs_for_entropy = FALSE;
 	}
 
-	wpa_group_init_gmk_and_counter(wpa_auth, group);
-	wpa_gtk_update(wpa_auth, group);
-	wpa_group_config_group_keys(wpa_auth, group);
+	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;
+	}
 }
 
 
@@ -1890,11 +1909,27 @@
 #endif /* CONFIG_IEEE80211R */
 	if (sm->pmksa) {
 		wpa_printf(MSG_DEBUG, "WPA: PMK from PMKSA cache");
-		os_memcpy(sm->PMK, sm->pmksa->pmk, PMK_LEN);
+		os_memcpy(sm->PMK, sm->pmksa->pmk, sm->pmksa->pmk_len);
+		sm->pmk_len = sm->pmksa->pmk_len;
 	} else if (wpa_auth_get_msk(sm->wpa_auth, sm->addr, msk, &len) == 0) {
+		unsigned int pmk_len;
+
+		if (sm->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B_192)
+			pmk_len = PMK_LEN_SUITE_B_192;
+		else
+			pmk_len = PMK_LEN;
 		wpa_printf(MSG_DEBUG, "WPA: PMK from EAPOL state machine "
-			   "(len=%lu)", (unsigned long) len);
-		os_memcpy(sm->PMK, msk, PMK_LEN);
+			   "(MSK len=%lu PMK len=%u)", (unsigned long) len,
+			   pmk_len);
+		if (len < pmk_len) {
+			wpa_printf(MSG_DEBUG,
+				   "WPA: MSK not long enough (%u) to create PMK (%u)",
+				   (unsigned int) len, (unsigned int) pmk_len);
+			sm->Disconnect = TRUE;
+			return;
+		}
+		os_memcpy(sm->PMK, msk, pmk_len);
+		sm->pmk_len = pmk_len;
 #ifdef CONFIG_IEEE80211R
 		if (len >= 2 * PMK_LEN) {
 			os_memcpy(sm->xxkey, msk + PMK_LEN, PMK_LEN);
@@ -1929,6 +1964,7 @@
 	psk = wpa_auth_get_psk(sm->wpa_auth, sm->addr, sm->p2p_dev_addr, NULL);
 	if (psk) {
 		os_memcpy(sm->PMK, psk, PMK_LEN);
+		sm->pmk_len = PMK_LEN;
 #ifdef CONFIG_IEEE80211R
 		os_memcpy(sm->xxkey, psk, PMK_LEN);
 		sm->xxkey_len = PMK_LEN;
@@ -1980,7 +2016,7 @@
 			 * Calculate PMKID since no PMKSA cache entry was
 			 * available with pre-calculated PMKID.
 			 */
-			rsn_pmkid(sm->PMK, PMK_LEN, sm->wpa_auth->addr,
+			rsn_pmkid(sm->PMK, sm->pmk_len, sm->wpa_auth->addr,
 				  sm->addr, &pmkid[2 + RSN_SELECTOR_LEN],
 				  wpa_key_mgmt_sha256(sm->wpa_key_mgmt));
 		}
@@ -1992,14 +2028,15 @@
 
 
 static int wpa_derive_ptk(struct wpa_state_machine *sm, const u8 *snonce,
-			  const u8 *pmk, struct wpa_ptk *ptk)
+			  const u8 *pmk, unsigned int pmk_len,
+			  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",
+	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);
 }
@@ -2010,6 +2047,7 @@
 	struct wpa_ptk PTK;
 	int ok = 0, psk_found = 0;
 	const u8 *pmk = NULL;
+	unsigned int pmk_len;
 
 	SM_ENTRY_MA(WPA_PTK, PTKCALCNEGOTIATING, wpa_ptk);
 	sm->EAPOLKeyReceived = FALSE;
@@ -2025,10 +2063,13 @@
 			if (pmk == NULL)
 				break;
 			psk_found = 1;
-		} else
+			pmk_len = PMK_LEN;
+		} else {
 			pmk = sm->PMK;
+			pmk_len = sm->pmk_len;
+		}
 
-		wpa_derive_ptk(sm, sm->SNonce, pmk, &PTK);
+		wpa_derive_ptk(sm, sm->SNonce, pmk, pmk_len, &PTK);
 
 		if (wpa_verify_key_mic(sm->wpa_key_mgmt, &PTK,
 				       sm->last_rx_eapol_key,
@@ -2078,6 +2119,7 @@
 		 * state machine data based on whatever PSK was selected here.
 		 */
 		os_memcpy(sm->PMK, pmk, PMK_LEN);
+		sm->pmk_len = PMK_LEN;
 	}
 
 	sm->MICVerified = TRUE;
@@ -2256,14 +2298,19 @@
 	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);
+		int res;
+		size_t elen;
+
+		elen = pos - kde;
+		res = wpa_insert_pmkid(kde, &elen, 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;
+		pos -= wpa_ie_len;
+		pos += elen;
 	}
 #endif /* CONFIG_IEEE80211R */
 	if (gtk) {
@@ -2281,10 +2328,18 @@
 		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 (sm->assoc_resp_ftie &&
+		    kde + kde_len - pos >= 2 + sm->assoc_resp_ftie[1]) {
+			os_memcpy(pos, sm->assoc_resp_ftie,
+				  2 + sm->assoc_resp_ftie[1]);
+			res = 2 + sm->assoc_resp_ftie[1];
+		} else {
+			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");
@@ -3229,13 +3284,21 @@
 
 
 int wpa_auth_pmksa_add(struct wpa_state_machine *sm, const u8 *pmk,
+		       unsigned int pmk_len,
 		       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,
+	if (sm->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B_192) {
+		if (pmk_len > PMK_LEN_SUITE_B_192)
+			pmk_len = PMK_LEN_SUITE_B_192;
+	} else if (pmk_len > PMK_LEN) {
+		pmk_len = PMK_LEN;
+	}
+
+	if (pmksa_cache_auth_add(sm->wpa_auth->pmksa, pmk, pmk_len, NULL,
 				 sm->PTK.kck, sm->PTK.kck_len,
 				 sm->wpa_auth->addr, sm->addr, session_timeout,
 				 eapol, sm->wpa_key_mgmt))
@@ -3253,7 +3316,7 @@
 	if (wpa_auth == NULL)
 		return -1;
 
-	if (pmksa_cache_auth_add(wpa_auth->pmksa, pmk, len,
+	if (pmksa_cache_auth_add(wpa_auth->pmksa, pmk, len, NULL,
 				 NULL, 0,
 				 wpa_auth->addr,
 				 sta_addr, session_timeout, eapol,
@@ -3265,12 +3328,12 @@
 
 
 int wpa_auth_pmksa_add_sae(struct wpa_authenticator *wpa_auth, const u8 *addr,
-			   const u8 *pmk)
+			   const u8 *pmk, const u8 *pmkid)
 {
 	if (wpa_auth->conf.disable_pmksa_caching)
 		return -1;
 
-	if (pmksa_cache_auth_add(wpa_auth->pmksa, pmk, PMK_LEN,
+	if (pmksa_cache_auth_add(wpa_auth->pmksa, pmk, PMK_LEN, pmkid,
 				 NULL, 0,
 				 wpa_auth->addr, addr, 0, NULL,
 				 WPA_KEY_MGMT_SAE))
@@ -3296,6 +3359,46 @@
 }
 
 
+int wpa_auth_pmksa_list(struct wpa_authenticator *wpa_auth, char *buf,
+			size_t len)
+{
+	if (!wpa_auth || !wpa_auth->pmksa)
+		return 0;
+	return pmksa_cache_auth_list(wpa_auth->pmksa, buf, len);
+}
+
+
+void wpa_auth_pmksa_flush(struct wpa_authenticator *wpa_auth)
+{
+	if (wpa_auth && wpa_auth->pmksa)
+		pmksa_cache_auth_flush(wpa_auth->pmksa);
+}
+
+
+struct rsn_pmksa_cache_entry *
+wpa_auth_pmksa_get(struct wpa_authenticator *wpa_auth, const u8 *sta_addr)
+{
+	if (!wpa_auth || !wpa_auth->pmksa)
+		return NULL;
+	return pmksa_cache_auth_get(wpa_auth->pmksa, sta_addr, NULL);
+}
+
+
+void wpa_auth_pmksa_set_to_sm(struct rsn_pmksa_cache_entry *pmksa,
+			      struct wpa_state_machine *sm,
+			      struct wpa_authenticator *wpa_auth,
+			      u8 *pmkid, u8 *pmk)
+{
+	if (!sm)
+		return;
+
+	sm->pmksa = pmksa;
+	os_memcpy(pmk, pmksa->pmk, PMK_LEN);
+	os_memcpy(pmkid, pmksa->pmkid, PMKID_LEN);
+	os_memcpy(wpa_auth->dot11RSNAPMKIDUsed, pmksa->pmkid, PMKID_LEN);
+}
+
+
 /*
  * 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
@@ -3374,6 +3477,98 @@
 }
 
 
+/*
+ * Enforce that the group state machine for the VLAN is running, increase
+ * reference counter as interface is up. References might have been increased
+ * even if a negative value is returned.
+ * Returns: -1 on error (group missing, group already failed); otherwise, 0
+ */
+int wpa_auth_ensure_group(struct wpa_authenticator *wpa_auth, int vlan_id)
+{
+	struct wpa_group *group;
+
+	if (wpa_auth == NULL)
+		return 0;
+
+	group = wpa_auth->group;
+	while (group) {
+		if (group->vlan_id == vlan_id)
+			break;
+		group = group->next;
+	}
+
+	if (group == NULL) {
+		group = wpa_auth_add_group(wpa_auth, vlan_id);
+		if (group == NULL)
+			return -1;
+	}
+
+	wpa_printf(MSG_DEBUG,
+		   "WPA: Ensure group state machine running for VLAN ID %d",
+		   vlan_id);
+
+	wpa_group_get(wpa_auth, group);
+	group->num_setup_iface++;
+
+	if (group->wpa_group_state == WPA_GROUP_FATAL_FAILURE)
+		return -1;
+
+	return 0;
+}
+
+
+/*
+ * Decrease reference counter, expected to be zero afterwards.
+ * returns: -1 on error (group not found, group in fail state)
+ *          -2 if wpa_group is still referenced
+ *           0 else
+ */
+int wpa_auth_release_group(struct wpa_authenticator *wpa_auth, int vlan_id)
+{
+	struct wpa_group *group;
+	int ret = 0;
+
+	if (wpa_auth == NULL)
+		return 0;
+
+	group = wpa_auth->group;
+	while (group) {
+		if (group->vlan_id == vlan_id)
+			break;
+		group = group->next;
+	}
+
+	if (group == NULL)
+		return -1;
+
+	wpa_printf(MSG_DEBUG,
+		   "WPA: Try stopping group state machine for VLAN ID %d",
+		   vlan_id);
+
+	if (group->num_setup_iface <= 0) {
+		wpa_printf(MSG_ERROR,
+			   "WPA: wpa_auth_release_group called more often than wpa_auth_ensure_group for VLAN ID %d, skipping.",
+			   vlan_id);
+		return -1;
+	}
+	group->num_setup_iface--;
+
+	if (group->wpa_group_state == WPA_GROUP_FATAL_FAILURE)
+		ret = -1;
+
+	if (group->references > 1) {
+		wpa_printf(MSG_DEBUG,
+			   "WPA: Cannot stop group state machine for VLAN ID %d as references are still hold",
+			   vlan_id);
+		ret = -2;
+	}
+
+	wpa_group_put(wpa_auth, group);
+
+	return ret;
+}
+
+
 int wpa_auth_sta_set_vlan(struct wpa_state_machine *sm, int vlan_id)
 {
 	struct wpa_group *group;
diff --git a/src/ap/wpa_auth.h b/src/ap/wpa_auth.h
index e747806..0de8d97 100644
--- a/src/ap/wpa_auth.h
+++ b/src/ap/wpa_auth.h
@@ -1,6 +1,6 @@
 /*
  * hostapd - IEEE 802.11i-2004 / WPA Authenticator
- * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi>
+ * 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.
@@ -14,6 +14,8 @@
 #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 */
@@ -40,10 +42,11 @@
 #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
+#define FT_R0KH_R1KH_PULL_DATA_LEN (FT_R0KH_R1KH_PULL_NONCE_LEN + \
+				    WPA_PMK_NAME_LEN + FT_R1KH_ID_LEN + \
+				    ETH_ALEN)
+#define FT_R0KH_R1KH_PULL_PAD_LEN ((8 - FT_R0KH_R1KH_PULL_DATA_LEN % 8) % 8)
 
 struct ft_r0kh_r1kh_pull_frame {
 	u8 frame_type; /* RSN_REMOTE_FRAME_TYPE_FT_RRB */
@@ -55,14 +58,18 @@
 	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 pad[FT_R0KH_R1KH_PULL_PAD_LEN]; /* 8-octet boundary for AES block */
 	u8 key_wrap_extra[8];
 } STRUCT_PACKED;
 
+#define FT_R0KH_R1KH_RESP_DATA_LEN (FT_R0KH_R1KH_PULL_NONCE_LEN + \
+				    FT_R1KH_ID_LEN + ETH_ALEN + PMK_LEN + \
+				    WPA_PMK_NAME_LEN + 2)
+#define FT_R0KH_R1KH_RESP_PAD_LEN ((8 - FT_R0KH_R1KH_RESP_DATA_LEN % 8) % 8)
 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) */
+	le16 data_length; /* little endian length of data (78) */
 	u8 ap_address[ETH_ALEN];
 
 	u8 nonce[FT_R0KH_R1KH_PULL_NONCE_LEN]; /* copied from pull */
@@ -71,14 +78,18 @@
 	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 pad[FT_R0KH_R1KH_RESP_PAD_LEN]; /* 8-octet boundary for AES block */
 	u8 key_wrap_extra[8];
 } STRUCT_PACKED;
 
+#define FT_R0KH_R1KH_PUSH_DATA_LEN (4 + FT_R1KH_ID_LEN + ETH_ALEN + \
+				    WPA_PMK_NAME_LEN + PMK_LEN + \
+				    WPA_PMK_NAME_LEN + 2)
+#define FT_R0KH_R1KH_PUSH_PAD_LEN ((8 - FT_R0KH_R1KH_PUSH_DATA_LEN % 8) % 8)
 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) */
+	le16 data_length; /* little endian length of data (82) */
 	u8 ap_address[ETH_ALEN];
 
 	/* Encrypted with AES key-wrap */
@@ -90,7 +101,7 @@
 	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 pad[FT_R0KH_R1KH_PUSH_PAD_LEN]; /* 8-octet boundary for AES block */
 	u8 key_wrap_extra[8];
 } STRUCT_PACKED;
 
@@ -164,6 +175,8 @@
 	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];
@@ -252,12 +265,12 @@
 void wpa_receive(struct wpa_authenticator *wpa_auth,
 		 struct wpa_state_machine *sm,
 		 u8 *data, size_t data_len);
-typedef enum {
+enum wpa_event {
 	WPA_AUTH, WPA_ASSOC, WPA_DISASSOC, WPA_DEAUTH, WPA_REAUTH,
 	WPA_REAUTH_EAPOL, WPA_ASSOC_FT
-} wpa_event;
+};
 void wpa_remove_ptk(struct wpa_state_machine *sm);
-int wpa_auth_sm_event(struct wpa_state_machine *sm, wpa_event event);
+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);
@@ -275,15 +288,25 @@
 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,
+		       unsigned int pmk_len,
 		       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);
+			   const u8 *pmk, const u8 *pmkid);
 void wpa_auth_pmksa_remove(struct wpa_authenticator *wpa_auth,
 			   const u8 *sta_addr);
+int wpa_auth_pmksa_list(struct wpa_authenticator *wpa_auth, char *buf,
+			size_t len);
+void wpa_auth_pmksa_flush(struct wpa_authenticator *wpa_auth);
+struct rsn_pmksa_cache_entry *
+wpa_auth_pmksa_get(struct wpa_authenticator *wpa_auth, const u8 *sta_addr);
+void wpa_auth_pmksa_set_to_sm(struct rsn_pmksa_cache_entry *pmksa,
+			      struct wpa_state_machine *sm,
+			      struct wpa_authenticator *wpa_auth,
+			      u8 *pmkid, u8 *pmk);
 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);
@@ -321,4 +344,7 @@
 					 struct radius_das_attrs *attr);
 void wpa_auth_reconfig_group_keys(struct wpa_authenticator *wpa_auth);
 
+int wpa_auth_ensure_group(struct wpa_authenticator *wpa_auth, int vlan_id);
+int wpa_auth_release_group(struct wpa_authenticator *wpa_auth, int vlan_id);
+
 #endif /* WPA_AUTH_H */
diff --git a/src/ap/wpa_auth_ft.c b/src/ap/wpa_auth_ft.c
index eeaffbf..42242a5 100644
--- a/src/ap/wpa_auth_ft.c
+++ b/src/ap/wpa_auth_ft.c
@@ -720,11 +720,6 @@
 	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 */
@@ -750,6 +745,11 @@
 		       _ftie->mic) < 0)
 		wpa_printf(MSG_DEBUG, "FT: Failed to calculate MIC");
 
+	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);
+
 	return pos;
 }
 
diff --git a/src/ap/wpa_auth_glue.c b/src/ap/wpa_auth_glue.c
index 7cd0b6c..5fe0987 100644
--- a/src/ap/wpa_auth_glue.c
+++ b/src/ap/wpa_auth_glue.c
@@ -12,6 +12,7 @@
 #include "common/ieee802_11_defs.h"
 #include "common/sae.h"
 #include "common/wpa_ctrl.h"
+#include "crypto/sha1.h"
 #include "eapol_auth/eapol_auth_sm.h"
 #include "eapol_auth/eapol_auth_sm_i.h"
 #include "eap_server/eap.h"
@@ -92,6 +93,13 @@
 #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);
@@ -239,6 +247,13 @@
 		struct hostapd_sta_wpa_psk_short *pos;
 		psk = sta->psk->psk;
 		for (pos = sta->psk; pos; pos = pos->next) {
+			if (pos->is_passphrase) {
+				pbkdf2_sha1(pos->passphrase,
+					    hapd->conf->ssid.ssid,
+					    hapd->conf->ssid.ssid_len, 4096,
+					    pos->psk, PMK_LEN);
+				pos->is_passphrase = 0;
+			}
 			if (pos->psk == prev_psk) {
 				psk = pos->next ? pos->next->psk : NULL;
 				break;
@@ -406,6 +421,8 @@
 		hapd = iface->bss[j];
 		if (hapd == idata->src_hapd)
 			continue;
+		if (!hapd->wpa_auth)
+			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 -> "
@@ -556,6 +573,9 @@
 	ethhdr = (struct l2_ethhdr *) buf;
 	wpa_printf(MSG_DEBUG, "FT: RRB received packet " MACSTR " -> "
 		   MACSTR, MAC2STR(ethhdr->h_source), MAC2STR(ethhdr->h_dest));
+	if (!is_multicast_ether_addr(ethhdr->h_dest) &&
+	    os_memcmp(hapd->own_addr, ethhdr->h_dest, ETH_ALEN) != 0)
+		return;
 	wpa_ft_rrb_rx(hapd->wpa_auth, ethhdr->h_source, buf + sizeof(*ethhdr),
 		      len - sizeof(*ethhdr));
 }
@@ -630,7 +650,8 @@
 	}
 
 #ifdef CONFIG_IEEE80211R
-	if (!hostapd_drv_none(hapd)) {
+	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,
@@ -666,13 +687,14 @@
 		wpa_deinit(hapd->wpa_auth);
 		hapd->wpa_auth = NULL;
 
-		if (hostapd_set_privacy(hapd, 0)) {
+		if (hapd->drv_priv && 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)) {
+		if (hapd->drv_priv &&
+		    hostapd_set_generic_elem(hapd, (u8 *) "", 0)) {
 			wpa_printf(MSG_DEBUG, "Could not remove generic "
 				   "information element from interface %s",
 				   hapd->conf->iface);
diff --git a/src/ap/wpa_auth_i.h b/src/ap/wpa_auth_i.h
index 57b098f..72b7eb3 100644
--- a/src/ap/wpa_auth_i.h
+++ b/src/ap/wpa_auth_i.h
@@ -60,7 +60,8 @@
 	u8 SNonce[WPA_NONCE_LEN];
 	u8 alt_SNonce[WPA_NONCE_LEN];
 	u8 alt_replay_counter[WPA_REPLAY_COUNTER_LEN];
-	u8 PMK[PMK_LEN];
+	u8 PMK[PMK_LEN_MAX];
+	unsigned int pmk_len;
 	struct wpa_ptk PTK;
 	Boolean PTK_valid;
 	Boolean pairwise_set;
@@ -171,6 +172,7 @@
 #endif /* CONFIG_IEEE80211W */
 	/* Number of references except those in struct wpa_group->next */
 	unsigned int references;
+	unsigned int num_setup_iface;
 };
 
 
diff --git a/src/ap/wpa_auth_ie.c b/src/ap/wpa_auth_ie.c
index f287297..f79783b 100644
--- a/src/ap/wpa_auth_ie.c
+++ b/src/ap/wpa_auth_ie.c
@@ -251,7 +251,7 @@
 	pos += 2;
 
 	if (pmkid) {
-		if (pos + 2 + PMKID_LEN > buf + len)
+		if (2 + PMKID_LEN > buf + len - pos)
 			return -1;
 		/* PMKID Count */
 		WPA_PUT_LE16(pos, 1);
@@ -261,8 +261,9 @@
 	}
 
 #ifdef CONFIG_IEEE80211W
-	if (conf->ieee80211w != NO_MGMT_FRAME_PROTECTION) {
-		if (pos + 2 + 4 > buf + len)
+	if (conf->ieee80211w != NO_MGMT_FRAME_PROTECTION &&
+	    conf->group_mgmt_cipher != WPA_CIPHER_AES_128_CMAC) {
+		if (2 + 4 > buf + len - pos)
 			return -1;
 		if (pmkid == NULL) {
 			/* PMKID Count */
@@ -377,6 +378,23 @@
 	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) {
@@ -694,11 +712,14 @@
 		}
 	}
 	if (sm->pmksa && pmkid) {
+		struct vlan_description *vlan;
+
+		vlan = sm->pmksa->vlan_desc;
 		wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_DEBUG,
-				 "PMKID found from PMKSA cache "
-				 "eap_type=%d vlan_id=%d",
+				 "PMKID found from PMKSA cache eap_type=%d vlan=%d%s",
 				 sm->pmksa->eap_type_authsrv,
-				 sm->pmksa->vlan_id);
+				 vlan ? vlan->untagged : 0,
+				 (vlan && vlan->tagged[0]) ? "+" : "");
 		os_memcpy(wpa_auth->dot11RSNAPMKIDUsed, pmkid, PMKID_LEN);
 	}
 
@@ -773,7 +794,7 @@
 		return 0;
 	}
 
-	if (pos + 1 + RSN_SELECTOR_LEN < end &&
+	if (1 + RSN_SELECTOR_LEN < end - pos &&
 	    pos[1] >= RSN_SELECTOR_LEN + PMKID_LEN &&
 	    RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_PMKID) {
 		ie->pmkid = pos + 2 + RSN_SELECTOR_LEN;
@@ -869,13 +890,13 @@
 	int ret = 0;
 
 	os_memset(ie, 0, sizeof(*ie));
-	for (pos = buf, end = pos + len; pos + 1 < end; pos += 2 + pos[1]) {
+	for (pos = buf, end = pos + len; end - pos > 1; pos += 2 + pos[1]) {
 		if (pos[0] == 0xdd &&
 		    ((pos == buf + len - 1) || pos[1] == 0)) {
 			/* Ignore padding */
 			break;
 		}
-		if (pos + 2 + pos[1] > end) {
+		if (2 + pos[1] > end - pos) {
 			wpa_printf(MSG_DEBUG, "WPA: EAPOL-Key Key Data "
 				   "underflow (ie=%d len=%d pos=%d)",
 				   pos[0], pos[1], (int) (pos - buf));
diff --git a/src/ap/wps_hostapd.c b/src/ap/wps_hostapd.c
index caed01e..faf38c9 100644
--- a/src/ap/wps_hostapd.c
+++ b/src/ap/wps_hostapd.c
@@ -1,6 +1,6 @@
 /*
  * hostapd / WPS integration
- * Copyright (c) 2008-2012, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2008-2016, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -445,6 +445,8 @@
 	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->encr_types_rsn = cred->encr_type;
+	hapd->wps->encr_types_wpa = 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;
@@ -452,6 +454,11 @@
 		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) {
@@ -867,7 +874,8 @@
 	hapd->wps_probe_resp_ie = NULL;
 
 	if (deinit_only) {
-		hostapd_reset_ap_wps_ie(hapd);
+		if (hapd->drv_priv)
+			hostapd_reset_ap_wps_ie(hapd);
 		return;
 	}
 
@@ -1062,10 +1070,14 @@
 		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))
+		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_rsn |= WPS_ENCR_AES;
+		}
+		if (conf->rsn_pairwise & WPA_CIPHER_TKIP) {
 			wps->encr_types |= WPS_ENCR_TKIP;
+			wps->encr_types_rsn |= WPS_ENCR_TKIP;
+		}
 	}
 
 	if (conf->wpa & WPA_PROTO_WPA) {
@@ -1074,10 +1086,14 @@
 		if (conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X)
 			wps->auth_types |= WPS_AUTH_WPA;
 
-		if (conf->wpa_pairwise & WPA_CIPHER_CCMP)
+		if (conf->wpa_pairwise & WPA_CIPHER_CCMP) {
 			wps->encr_types |= WPS_ENCR_AES;
-		if (conf->wpa_pairwise & WPA_CIPHER_TKIP)
+			wps->encr_types_wpa |= WPS_ENCR_AES;
+		}
+		if (conf->wpa_pairwise & WPA_CIPHER_TKIP) {
 			wps->encr_types |= WPS_ENCR_TKIP;
+			wps->encr_types_wpa |= WPS_ENCR_TKIP;
+		}
 	}
 
 	if (conf->ssid.security_policy == SECURITY_PLAINTEXT) {
@@ -1117,6 +1133,8 @@
 		/* 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->encr_types_rsn = WPS_ENCR_AES | WPS_ENCR_TKIP;
+		wps->encr_types_wpa = WPS_ENCR_AES | WPS_ENCR_TKIP;
 	}
 
 	wps->ap_settings = conf->ap_settings;
@@ -1299,30 +1317,53 @@
 }
 
 
+struct wps_button_pushed_ctx {
+	const u8 *p2p_dev_addr;
+	unsigned int count;
+};
+
 static int wps_button_pushed(struct hostapd_data *hapd, void *ctx)
 {
-	const u8 *p2p_dev_addr = ctx;
-	if (hapd->wps == NULL)
-		return -1;
-	return wps_registrar_button_pushed(hapd->wps->registrar, p2p_dev_addr);
+	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)
 {
-	return hostapd_wps_for_each(hapd, wps_button_pushed,
-				    (void *) 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)
 {
-	if (hapd->wps == NULL)
-		return -1;
+	struct wps_cancel_ctx *data = ctx;
 
-	wps_registrar_wps_cancel(hapd->wps->registrar);
-	ap_for_each_sta(hapd, ap_sta_wps_cancel, NULL);
+	if (hapd->wps) {
+		data->count++;
+		wps_registrar_wps_cancel(hapd->wps->registrar);
+		ap_for_each_sta(hapd, ap_sta_wps_cancel, NULL);
+	}
 
 	return 0;
 }
@@ -1330,7 +1371,14 @@
 
 int hostapd_wps_cancel(struct hostapd_data *hapd)
 {
-	return hostapd_wps_for_each(hapd, wps_cancel, NULL);
+	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;
 }
 
 
@@ -1560,6 +1608,10 @@
 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
@@ -1575,7 +1627,8 @@
 	unsigned int pin;
 	struct wps_ap_pin_data data;
 
-	pin = wps_generate_pin();
+	if (wps_generate_pin(&pin) < 0)
+		return NULL;
 	os_snprintf(data.pin_txt, sizeof(data.pin_txt), "%08u", pin);
 	data.timeout = timeout;
 	hostapd_wps_for_each(hapd, wps_ap_pin_set, &data);
diff --git a/src/common/ctrl_iface_common.c b/src/common/ctrl_iface_common.c
new file mode 100644
index 0000000..ebbe6ff
--- /dev/null
+++ b/src/common/ctrl_iface_common.c
@@ -0,0 +1,173 @@
+/*
+ * Common hostapd/wpa_supplicant ctrl iface code.
+ * 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 "utils/includes.h"
+#include <netdb.h>
+#include <sys/un.h>
+
+#include "utils/common.h"
+#include "ctrl_iface_common.h"
+
+static int sockaddr_compare(struct sockaddr_storage *a, socklen_t a_len,
+			    struct sockaddr_storage *b, socklen_t b_len)
+{
+	if (a->ss_family != b->ss_family)
+		return 1;
+
+	switch (a->ss_family) {
+#ifdef CONFIG_CTRL_IFACE_UDP
+	case AF_INET:
+	{
+		struct sockaddr_in *in_a, *in_b;
+
+		in_a = (struct sockaddr_in *) a;
+		in_b = (struct sockaddr_in *) b;
+
+		if (in_a->sin_port != in_b->sin_port)
+			return 1;
+		if (in_a->sin_addr.s_addr != in_b->sin_addr.s_addr)
+			return 1;
+		break;
+	}
+	case AF_INET6:
+	{
+		struct sockaddr_in6 *in6_a, *in6_b;
+
+		in6_a = (struct sockaddr_in6 *) a;
+		in6_b = (struct sockaddr_in6 *) b;
+
+		if (in6_a->sin6_port != in6_b->sin6_port)
+			return 1;
+		if (os_memcmp(&in6_a->sin6_addr, &in6_b->sin6_addr,
+			      sizeof(in6_a->sin6_addr)) != 0)
+			return 1;
+		break;
+	}
+#endif /* CONFIG_CTRL_IFACE_UDP */
+#ifdef CONFIG_CTRL_IFACE_UNIX
+	case AF_UNIX:
+	{
+		struct sockaddr_un *u_a, *u_b;
+
+		u_a = (struct sockaddr_un *) a;
+		u_b = (struct sockaddr_un *) b;
+
+		if (a_len != b_len ||
+		    os_memcmp(u_a->sun_path, u_b->sun_path,
+			      a_len - offsetof(struct sockaddr_un, sun_path))
+		    != 0)
+			return 1;
+		break;
+	}
+#endif /* CONFIG_CTRL_IFACE_UNIX */
+	default:
+		return 1;
+	}
+
+	return 0;
+}
+
+
+void sockaddr_print(int level, const char *msg, struct sockaddr_storage *sock,
+		    socklen_t socklen)
+{
+	switch (sock->ss_family) {
+#ifdef CONFIG_CTRL_IFACE_UDP
+	case AF_INET:
+	case AF_INET6:
+	{
+		char host[NI_MAXHOST] = { 0 };
+		char service[NI_MAXSERV] = { 0 };
+
+		getnameinfo((struct sockaddr *) sock, socklen,
+			    host, sizeof(host),
+			    service, sizeof(service),
+			    NI_NUMERICHOST);
+
+		wpa_printf(level, "%s %s:%s", msg, host, service);
+		break;
+	}
+#endif /* CONFIG_CTRL_IFACE_UDP */
+#ifdef CONFIG_CTRL_IFACE_UNIX
+	case AF_UNIX:
+	{
+		char addr_txt[200];
+
+		printf_encode(addr_txt, sizeof(addr_txt),
+			      (u8 *) ((struct sockaddr_un *) sock)->sun_path,
+			      socklen - offsetof(struct sockaddr_un, sun_path));
+		wpa_printf(level, "%s %s", msg, addr_txt);
+		break;
+	}
+#endif /* CONFIG_CTRL_IFACE_UNIX */
+	default:
+		wpa_printf(level, "%s", msg);
+		break;
+	}
+}
+
+
+int ctrl_iface_attach(struct dl_list *ctrl_dst, struct sockaddr_storage *from,
+		      socklen_t fromlen)
+{
+	struct wpa_ctrl_dst *dst;
+
+	dst = os_zalloc(sizeof(*dst));
+	if (dst == NULL)
+		return -1;
+	os_memcpy(&dst->addr, from, fromlen);
+	dst->addrlen = fromlen;
+	dst->debug_level = MSG_INFO;
+	dl_list_add(ctrl_dst, &dst->list);
+
+	sockaddr_print(MSG_DEBUG, "CTRL_IFACE monitor attached", from, fromlen);
+	return 0;
+}
+
+
+int ctrl_iface_detach(struct dl_list *ctrl_dst, struct sockaddr_storage *from,
+		      socklen_t fromlen)
+{
+	struct wpa_ctrl_dst *dst;
+
+	dl_list_for_each(dst, ctrl_dst, struct wpa_ctrl_dst, list) {
+		if (!sockaddr_compare(from, fromlen,
+				      &dst->addr, dst->addrlen)) {
+			sockaddr_print(MSG_DEBUG, "CTRL_IFACE monitor detached",
+				       from, fromlen);
+			dl_list_del(&dst->list);
+			os_free(dst);
+			return 0;
+		}
+	}
+
+	return -1;
+}
+
+
+int ctrl_iface_level(struct dl_list *ctrl_dst, struct sockaddr_storage *from,
+		     socklen_t fromlen, const char *level)
+{
+	struct wpa_ctrl_dst *dst;
+
+	wpa_printf(MSG_DEBUG, "CTRL_IFACE LEVEL %s", level);
+
+	dl_list_for_each(dst, ctrl_dst, struct wpa_ctrl_dst, list) {
+		if (!sockaddr_compare(from, fromlen,
+				      &dst->addr, dst->addrlen)) {
+			sockaddr_print(MSG_DEBUG,
+				       "CTRL_IFACE changed monitor level",
+				       from, fromlen);
+			dst->debug_level = atoi(level);
+			return 0;
+		}
+	}
+
+	return -1;
+}
diff --git a/src/common/ctrl_iface_common.h b/src/common/ctrl_iface_common.h
new file mode 100644
index 0000000..0b6e3e7
--- /dev/null
+++ b/src/common/ctrl_iface_common.h
@@ -0,0 +1,38 @@
+/*
+ * Common hostapd/wpa_supplicant ctrl iface code.
+ * 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 CONTROL_IFACE_COMMON_H
+#define CONTROL_IFACE_COMMON_H
+
+#include "utils/list.h"
+
+/**
+ * struct wpa_ctrl_dst - Data structure of control interface monitors
+ *
+ * This structure is used to store information about registered control
+ * interface monitors into struct wpa_supplicant.
+ */
+struct wpa_ctrl_dst {
+	struct dl_list list;
+	struct sockaddr_storage addr;
+	socklen_t addrlen;
+	int debug_level;
+	int errors;
+};
+
+void sockaddr_print(int level, const char *msg, struct sockaddr_storage *sock,
+		    socklen_t socklen);
+
+int ctrl_iface_attach(struct dl_list *ctrl_dst, struct sockaddr_storage *from,
+		       socklen_t fromlen);
+int ctrl_iface_detach(struct dl_list *ctrl_dst, struct sockaddr_storage *from,
+		      socklen_t fromlen);
+int ctrl_iface_level(struct dl_list *ctrl_dst, struct sockaddr_storage *from,
+		     socklen_t fromlen, const char *level);
+
+#endif /* CONTROL_IFACE_COMMON_H */
diff --git a/src/common/defs.h b/src/common/defs.h
index eb080ea..6ef929c 100644
--- a/src/common/defs.h
+++ b/src/common/defs.h
@@ -174,7 +174,7 @@
 	/**
 	 * WPA_INTERFACE_DISABLED - Interface disabled
 	 *
-	 * This stat eis entered if the network interface is disabled, e.g.,
+	 * 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.
 	 */
@@ -312,6 +312,7 @@
 	WPA_CTRL_REQ_EAP_PASSPHRASE,
 	WPA_CTRL_REQ_SIM,
 	WPA_CTRL_REQ_PSK_PASSPHRASE,
+	WPA_CTRL_REQ_EXT_CERT_CHECK,
 	NUM_WPA_CTRL_REQS
 };
 
@@ -334,4 +335,10 @@
 	WPA_SETBAND_2G
 };
 
+enum wpa_radio_work_band {
+	BAND_2_4_GHZ = BIT(0),
+	BAND_5_GHZ = BIT(1),
+	BAND_60_GHZ = BIT(2),
+};
+
 #endif /* DEFS_H */
diff --git a/src/common/hw_features_common.c b/src/common/hw_features_common.c
index e589a1a..9c37ea6 100644
--- a/src/common/hw_features_common.c
+++ b/src/common/hw_features_common.c
@@ -272,10 +272,8 @@
 	int affected_start, affected_end;
 	size_t i;
 
-	if (!mode || !scan_res || !pri_chan || !sec_chan)
-		return 0;
-
-	if (pri_chan == sec_chan)
+	if (!mode || !scan_res || !pri_chan || !sec_chan ||
+	    pri_chan == sec_chan)
 		return 0;
 
 	pri_freq = hw_get_freq(mode, pri_chan);
diff --git a/src/common/ieee802_11_common.c b/src/common/ieee802_11_common.c
index 5385faf..5b05b68 100644
--- a/src/common/ieee802_11_common.c
+++ b/src/common/ieee802_11_common.c
@@ -11,6 +11,7 @@
 #include "common.h"
 #include "defs.h"
 #include "wpa_common.h"
+#include "qca-vendor.h"
 #include "ieee802_11_defs.h"
 #include "ieee802_11_common.h"
 
@@ -114,6 +115,11 @@
 			elems->osen = pos;
 			elems->osen_len = elen;
 			break;
+		case MBO_OUI_TYPE:
+			/* MBO-OCE */
+			elems->mbo = pos;
+			elems->mbo_len = elen;
+			break;
 		default:
 			wpa_printf(MSG_MSGDUMP, "Unknown WFA "
 				   "information element ignored "
@@ -147,6 +153,20 @@
 		}
 		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 "
@@ -339,6 +359,22 @@
 			/* 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;
+		case WLAN_EID_SUPPORTED_OPERATING_CLASSES:
+			elems->supp_op_classes = pos;
+			elems->supp_op_classes_len = elen;
+			break;
 		default:
 			unknown++;
 			if (!show_errors)
@@ -371,8 +407,8 @@
 	pos = ies;
 	end = ies + ies_len;
 
-	while (pos + 2 <= end) {
-		if (pos + 2 + pos[1] > end)
+	while (end - pos >= 2) {
+		if (2 + pos[1] > end - pos)
 			break;
 		count++;
 		pos += 2 + pos[1];
@@ -392,8 +428,8 @@
 	end = ies + ies_len;
 	ie = NULL;
 
-	while (pos + 1 < end) {
-		if (pos + 2 + pos[1] > end)
+	while (end - pos > 1) {
+		if (2 + pos[1] > end - pos)
 			return NULL;
 		if (pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 &&
 		    WPA_GET_BE32(&pos[2]) == oui_type) {
@@ -414,8 +450,8 @@
 	 * 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)
+	while (end - pos > 1) {
+		if (2 + pos[1] > end - pos)
 			break;
 		if (pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 &&
 		    WPA_GET_BE32(&pos[2]) == oui_type)
@@ -541,26 +577,166 @@
 
 enum hostapd_hw_mode ieee80211_freq_to_chan(int freq, u8 *channel)
 {
-	enum hostapd_hw_mode mode = NUM_HOSTAPD_MODES;
+	u8 op_class;
+
+	return ieee80211_freq_to_channel_ext(freq, 0, VHT_CHANWIDTH_USE_HT,
+					     &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: VHT channel width (VHT_CHANWIDTH_*)
+ * @op_class: Buffer for returning operating class
+ * @channel: Buffer for returning channel number
+ * Returns: hw_mode on success, NUM_HOSTAPD_MODES on failure
+ */
+enum hostapd_hw_mode ieee80211_freq_to_channel_ext(unsigned int freq,
+						   int sec_channel, int vht,
+						   u8 *op_class, u8 *channel)
+{
+	u8 vht_opclass;
+
+	/* TODO: more operating classes */
+
+	if (sec_channel > 1 || sec_channel < -1)
+		return NUM_HOSTAPD_MODES;
 
 	if (freq >= 2412 && freq <= 2472) {
-		mode = HOSTAPD_MODE_IEEE80211G;
+		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;
-	} else if (freq == 2484) {
-		mode = HOSTAPD_MODE_IEEE80211B;
-		*channel = 14;
-	} else if (freq >= 4900 && freq < 5000) {
-		mode = HOSTAPD_MODE_IEEE80211A;
-		*channel = (freq - 4000) / 5;
-	} else if (freq >= 5000 && freq < 5900) {
-		mode = HOSTAPD_MODE_IEEE80211A;
-		*channel = (freq - 5000) / 5;
-	} else if (freq >= 56160 + 2160 * 1 && freq <= 56160 + 2160 * 4) {
-		mode = HOSTAPD_MODE_IEEE80211AD;
-		*channel = (freq - 56160) / 2160;
+
+		return HOSTAPD_MODE_IEEE80211G;
 	}
 
-	return mode;
+	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;
+	}
+
+	switch (vht) {
+	case VHT_CHANWIDTH_80MHZ:
+		vht_opclass = 128;
+		break;
+	case VHT_CHANWIDTH_160MHZ:
+		vht_opclass = 129;
+		break;
+	case VHT_CHANWIDTH_80P80MHZ:
+		vht_opclass = 130;
+		break;
+	default:
+		vht_opclass = 0;
+		break;
+	}
+
+	/* 5 GHz, channels 36..48 */
+	if (freq >= 5180 && freq <= 5240) {
+		if ((freq - 5000) % 5)
+			return NUM_HOSTAPD_MODES;
+
+		if (vht_opclass)
+			*op_class = vht_opclass;
+		else if (sec_channel == 1)
+			*op_class = 116;
+		else if (sec_channel == -1)
+			*op_class = 117;
+		else
+			*op_class = 115;
+
+		*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;
+
+		if (vht_opclass)
+			*op_class = vht_opclass;
+		else if (sec_channel == 1)
+			*op_class = 126;
+		else if (sec_channel == -1)
+			*op_class = 127;
+		else if (freq <= 5805)
+			*op_class = 124;
+		else
+			*op_class = 125;
+
+		*channel = (freq - 5000) / 5;
+
+		return HOSTAPD_MODE_IEEE80211A;
+	}
+
+	/* 5 GHz, channels 100..140 */
+	if (freq >= 5000 && freq <= 5700) {
+		if ((freq - 5000) % 5)
+			return NUM_HOSTAPD_MODES;
+
+		if (vht_opclass)
+			*op_class = vht_opclass;
+		else if (sec_channel == 1)
+			*op_class = 122;
+		else if (sec_channel == -1)
+			*op_class = 123;
+		else
+			*op_class = 121;
+
+		*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;
 }
 
 
@@ -946,3 +1122,194 @@
 	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;
+}
+
+
+const struct oper_class_map global_op_class[] = {
+	{ HOSTAPD_MODE_IEEE80211G, 81, 1, 13, 1, BW20, P2P_SUPP },
+	{ HOSTAPD_MODE_IEEE80211G, 82, 14, 14, 1, BW20, NO_P2P_SUPP },
+
+	/* Do not enable HT40 on 2.4 GHz for P2P use for now */
+	{ HOSTAPD_MODE_IEEE80211G, 83, 1, 9, 1, BW40PLUS, NO_P2P_SUPP },
+	{ HOSTAPD_MODE_IEEE80211G, 84, 5, 13, 1, BW40MINUS, NO_P2P_SUPP },
+
+	{ HOSTAPD_MODE_IEEE80211A, 115, 36, 48, 4, BW20, P2P_SUPP },
+	{ HOSTAPD_MODE_IEEE80211A, 116, 36, 44, 8, BW40PLUS, P2P_SUPP },
+	{ HOSTAPD_MODE_IEEE80211A, 117, 40, 48, 8, BW40MINUS, P2P_SUPP },
+	{ HOSTAPD_MODE_IEEE80211A, 118, 52, 64, 4, BW20, NO_P2P_SUPP },
+	{ HOSTAPD_MODE_IEEE80211A, 119, 52, 60, 8, BW40PLUS, NO_P2P_SUPP },
+	{ HOSTAPD_MODE_IEEE80211A, 120, 56, 64, 8, BW40MINUS, NO_P2P_SUPP },
+	{ HOSTAPD_MODE_IEEE80211A, 121, 100, 140, 4, BW20, NO_P2P_SUPP },
+	{ HOSTAPD_MODE_IEEE80211A, 122, 100, 132, 8, BW40PLUS, NO_P2P_SUPP },
+	{ HOSTAPD_MODE_IEEE80211A, 123, 104, 136, 8, BW40MINUS, NO_P2P_SUPP },
+	{ HOSTAPD_MODE_IEEE80211A, 124, 149, 161, 4, BW20, P2P_SUPP },
+	{ HOSTAPD_MODE_IEEE80211A, 125, 149, 169, 4, BW20, P2P_SUPP },
+	{ HOSTAPD_MODE_IEEE80211A, 126, 149, 157, 8, BW40PLUS, P2P_SUPP },
+	{ HOSTAPD_MODE_IEEE80211A, 127, 153, 161, 8, BW40MINUS, P2P_SUPP },
+
+	/*
+	 * 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
+	 * wpas_p2p_allow_channel() fail). wpas_p2p_verify_80mhz() should take
+	 * care of removing invalid channels.
+	 */
+	{ HOSTAPD_MODE_IEEE80211A, 128, 36, 161, 4, BW80, P2P_SUPP },
+	{ HOSTAPD_MODE_IEEE80211A, 129, 50, 114, 16, BW160, P2P_SUPP },
+	{ HOSTAPD_MODE_IEEE80211A, 130, 36, 161, 4, BW80P80, P2P_SUPP },
+	{ HOSTAPD_MODE_IEEE80211AD, 180, 1, 4, 1, BW2160, P2P_SUPP },
+	{ -1, 0, 0, 0, 0, BW20, NO_P2P_SUPP }
+};
+
+
+static enum phy_type ieee80211_phy_type_by_freq(int freq)
+{
+	enum hostapd_hw_mode hw_mode;
+	u8 channel;
+
+	hw_mode = ieee80211_freq_to_chan(freq, &channel);
+
+	switch (hw_mode) {
+	case HOSTAPD_MODE_IEEE80211A:
+		return PHY_TYPE_OFDM;
+	case HOSTAPD_MODE_IEEE80211B:
+		return PHY_TYPE_HRDSSS;
+	case HOSTAPD_MODE_IEEE80211G:
+		return PHY_TYPE_ERP;
+	case HOSTAPD_MODE_IEEE80211AD:
+		return PHY_TYPE_DMG;
+	default:
+		return PHY_TYPE_UNSPECIFIED;
+	};
+}
+
+
+/* ieee80211_get_phy_type - Derive the phy type by freq and bandwidth */
+enum phy_type ieee80211_get_phy_type(int freq, int ht, int vht)
+{
+	if (vht)
+		return PHY_TYPE_VHT;
+	if (ht)
+		return PHY_TYPE_HT;
+
+	return ieee80211_phy_type_by_freq(freq);
+}
+
+
+size_t global_op_class_size = ARRAY_SIZE(global_op_class);
+
+
+/**
+ * get_ie - Fetch a specified information element from IEs buffer
+ * @ies: Information elements buffer
+ * @len: Information elements buffer length
+ * @eid: Information element identifier (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 IEs
+ * buffer or %NULL in case the element is not found.
+ */
+const u8 * get_ie(const u8 *ies, size_t len, u8 eid)
+{
+	const u8 *end;
+
+	if (!ies)
+		return NULL;
+
+	end = ies + len;
+
+	while (end - ies > 1) {
+		if (2 + ies[1] > end - ies)
+			break;
+
+		if (ies[0] == eid)
+			return ies;
+
+		ies += 2 + ies[1];
+	}
+
+	return NULL;
+}
+
+
+size_t mbo_add_ie(u8 *buf, size_t len, const u8 *attr, size_t attr_len)
+{
+	/*
+	 * MBO IE requires 6 bytes without the attributes: EID (1), length (1),
+	 * OUI (3), OUI type (1).
+	 */
+	if (len < 6 + attr_len) {
+		wpa_printf(MSG_DEBUG,
+			   "MBO: Not enough room in buffer for MBO IE: buf len = %zu, attr_len = %zu",
+			   len, attr_len);
+		return 0;
+	}
+
+	*buf++ = WLAN_EID_VENDOR_SPECIFIC;
+	*buf++ = attr_len + 4;
+	WPA_PUT_BE24(buf, OUI_WFA);
+	buf += 3;
+	*buf++ = MBO_OUI_TYPE;
+	os_memcpy(buf, attr, attr_len);
+
+	return 6 + attr_len;
+}
diff --git a/src/common/ieee802_11_common.h b/src/common/ieee802_11_common.h
index c84d8a7..d9fecd6 100644
--- a/src/common/ieee802_11_common.h
+++ b/src/common/ieee802_11_common.h
@@ -9,6 +9,18 @@
 #ifndef IEEE802_11_COMMON_H
 #define IEEE802_11_COMMON_H
 
+#include "defs.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;
@@ -46,8 +58,11 @@
 	const u8 *bss_max_idle_period;
 	const u8 *ssid_list;
 	const u8 *osen;
+	const u8 *mbo;
 	const u8 *ampe;
 	const u8 *mic;
+	const u8 *pref_freq_list;
+	const u8 *supp_op_classes;
 
 	u8 ssid_len;
 	u8 supp_rates_len;
@@ -74,8 +89,13 @@
 	u8 ext_capab_len;
 	u8 ssid_list_len;
 	u8 osen_len;
+	u8 mbo_len;
 	u8 ampe_len;
 	u8 mic_len;
+	u8 pref_freq_list_len;
+	u8 supp_op_classes_len;
+
+	struct mb_ies_info mb_ies;
 };
 
 typedef enum { ParseOK = 0, ParseUnknown = 1, ParseFailed = -1 } ParseRes;
@@ -101,9 +121,34 @@
 			  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);
+enum phy_type ieee80211_get_phy_type(int freq, int ht, int vht);
 
 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);
+
+struct 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, BW160, BW80P80 } bw;
+	enum { P2P_SUPP, NO_P2P_SUPP } p2p;
+};
+
+extern const struct oper_class_map global_op_class[];
+extern size_t global_op_class_size;
+
+const u8 * get_ie(const u8 *ies, size_t len, u8 eid);
+
+size_t mbo_add_ie(u8 *buf, size_t len, const u8 *attr, size_t attr_len);
+
 #endif /* IEEE802_11_COMMON_H */
diff --git a/src/common/ieee802_11_defs.h b/src/common/ieee802_11_defs.h
index 47b15de..e1a8ef7 100644
--- a/src/common/ieee802_11_defs.h
+++ b/src/common/ieee802_11_defs.h
@@ -94,8 +94,13 @@
 #define WLAN_CAPABILITY_PBCC BIT(6)
 #define WLAN_CAPABILITY_CHANNEL_AGILITY BIT(7)
 #define WLAN_CAPABILITY_SPECTRUM_MGMT BIT(8)
+#define WLAN_CAPABILITY_QOS BIT(9)
 #define WLAN_CAPABILITY_SHORT_SLOT_TIME BIT(10)
+#define WLAN_CAPABILITY_APSD BIT(11)
+#define WLAN_CAPABILITY_RADIO_MEASUREMENT BIT(12)
 #define WLAN_CAPABILITY_DSSS_OFDM BIT(13)
+#define WLAN_CAPABILITY_DELAYED_BLOCK_ACK BIT(14)
+#define WLAN_CAPABILITY_IMM_BLOCK_ACK BIT(15)
 
 /* Status codes (IEEE 802.11-2007, 7.3.1.9, Table 7-23) */
 #define WLAN_STATUS_SUCCESS 0
@@ -165,7 +170,10 @@
 #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) */
@@ -244,6 +252,7 @@
 #define WLAN_EID_TIMEOUT_INTERVAL 56
 #define WLAN_EID_RIC_DATA 57
 #define WLAN_EID_SUPPORTED_OPERATING_CLASSES 59
+#define WLAN_EID_EXT_CHANSWITCH_ANN 60
 #define WLAN_EID_HT_OPERATION 61
 #define WLAN_EID_SECONDARY_CHANNEL_OFFSET 62
 #define WLAN_EID_WAPI 68
@@ -271,6 +280,8 @@
 #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
@@ -299,6 +310,7 @@
 #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 */
@@ -401,7 +413,12 @@
 	ANQP_AP_LOCATION_PUBLIC_URI = 267,
 	ANQP_DOMAIN_NAME = 268,
 	ANQP_EMERGENCY_ALERT_URI = 269,
+	ANQP_TDLS_CAPABILITY = 270,
 	ANQP_EMERGENCY_NAI = 271,
+	ANQP_NEIGHBOR_REPORT = 272,
+	ANQP_VENUE_URL = 277,
+	ANQP_ADVICE_OF_CHARGE = 278,
+	ANQP_LOCAL_CONTENT = 279,
 	ANQP_VENDOR_SPECIFIC = 56797
 };
 
@@ -510,10 +527,7 @@
 			 * 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;
+		/* probe_req: only variable items: SSID, Supported rates */
 		struct {
 			u8 timestamp[8];
 			le16 beacon_int;
@@ -615,6 +629,10 @@
 					u8 action; /* 15 */
 					u8 variable[];
 				} STRUCT_PACKED slf_prot_action;
+				struct {
+					u8 action;
+					u8 variable[];
+				} STRUCT_PACKED fst_action;
 			} u;
 		} STRUCT_PACKED action;
 	} u;
@@ -869,6 +887,8 @@
 #define WFD_OUI_TYPE 10
 #define HS20_IE_VENDOR_TYPE 0x506f9a10
 #define OSEN_IE_VENDOR_TYPE 0x506f9a12
+#define MBO_IE_VENDOR_TYPE 0x506f9a16
+#define MBO_OUI_TYPE 22
 
 #define WMM_OUI_TYPE 2
 #define WMM_OUI_SUBTYPE_INFORMATION_ELEMENT 0
@@ -1011,6 +1031,91 @@
 #define HS20_DEAUTH_REASON_CODE_BSS 0
 #define HS20_DEAUTH_REASON_CODE_ESS 1
 
+/* MBO v0.0_r19, 4.2: MBO Attributes */
+/* Table 4-5: MBO Attributes */
+enum mbo_attr_id {
+	MBO_ATTR_ID_AP_CAPA_IND = 1,
+	MBO_ATTR_ID_NON_PREF_CHAN_REPORT = 2,
+	MBO_ATTR_ID_CELL_DATA_CAPA = 3,
+	MBO_ATTR_ID_ASSOC_DISALLOW = 4,
+	MBO_ATTR_ID_CELL_DATA_PREF = 5,
+	MBO_ATTR_ID_TRANSITION_REASON = 6,
+	MBO_ATTR_ID_TRANSITION_REJECT_REASON = 7,
+	MBO_ATTR_ID_ASSOC_RETRY_DELAY = 8,
+};
+
+/* MBO v0.0_r19, 4.2.1: MBO AP Capability Indication Attribute */
+/* Table 4-7: MBO AP Capability Indication Field Values */
+#define MBO_AP_CAPA_CELL_AWARE BIT(6)
+
+/* MBO v0.0_r19, 4.2.2: Non-preferred Channel Report Attribute */
+/* Table 4-10: Reason Code Field Values */
+enum mbo_non_pref_chan_reason {
+	MBO_NON_PREF_CHAN_REASON_UNSPECIFIED = 0,
+	MBO_NON_PREF_CHAN_REASON_RSSI = 1,
+	MBO_NON_PREF_CHAN_REASON_EXT_INTERFERENCE = 2,
+	MBO_NON_PREF_CHAN_REASON_INT_INTERFERENCE = 3,
+};
+
+/* MBO v0.0_r19, 4.2.3: Cellular Data Capabilities Attribute */
+/* Table 4-13: Cellular Data Connectivity Field */
+enum mbo_cellular_capa {
+	MBO_CELL_CAPA_AVAILABLE = 1,
+	MBO_CELL_CAPA_NOT_AVAILABLE = 2,
+	MBO_CELL_CAPA_NOT_SUPPORTED = 3,
+};
+
+/* MBO v0.0_r19, 4.2.4: Association Disallowed Attribute */
+/* Table 4-15: Reason Code Field Values */
+enum mbo_assoc_disallow_reason {
+	MBO_ASSOC_DISALLOW_REASON_UNSPECIFIED = 1,
+	MBO_ASSOC_DISALLOW_REASON_MAX_STA = 2,
+	MBO_ASSOC_DISALLOW_REASON_AIR_INTERFERENCE = 3,
+	MBO_ASSOC_DISALLOW_REASON_AUTH_SERVER_OVERLOAD = 4,
+	MBO_ASSOC_DISALLOW_REASON_LOW_RSSI = 5,
+};
+
+/* MBO v0.0_r19, 4.2.5: Cellular Data Connection Preference Attribute */
+/* Table 4-17: Cellular Preference Field Values */
+enum mbo_cell_pref {
+	MBO_CELL_PREF_EXCLUDED = 0,
+	MBO_CELL_PREF_NO_USE = 1,
+	MBO_CELL_PREF_USE = 255
+};
+
+/* MBO v0.0_r19, 4.2.6: Transition Reason Code Attribute */
+/* Table 4-19: Transition Reason Code Field Values */
+enum mbo_transition_reason {
+	MBO_TRANSITION_REASON_UNSPECIFIED = 0,
+	MBO_TRANSITION_REASON_FRAME_LOSS = 1,
+	MBO_TRANSITION_REASON_DELAY = 2,
+	MBO_TRANSITION_REASON_BANDWIDTH = 3,
+	MBO_TRANSITION_REASON_LOAD_BALANCE = 4,
+	MBO_TRANSITION_REASON_RSSI = 5,
+	MBO_TRANSITION_REASON_RETRANSMISSIONS = 6,
+	MBO_TRANSITION_REASON_INTERFERENCE = 7,
+	MBO_TRANSITION_REASON_GRAY_ZONE = 8,
+	MBO_TRANSITION_REASON_PREMIUM_AP = 9,
+};
+
+/* MBO v0.0_r19, 4.2.7: Transition Rejection Reason Code Attribute */
+/* Table 4-21: Transition Rejection Reason Code Field Values */
+enum mbo_transition_reject_reason {
+	MBO_TRANSITION_REJECT_REASON_UNSPECIFIED = 0,
+	MBO_TRANSITION_REJECT_REASON_FRAME_LOSS = 1,
+	MBO_TRANSITION_REJECT_REASON_DELAY = 2,
+	MBO_TRANSITION_REJECT_REASON_QOS_CAPACITY = 3,
+	MBO_TRANSITION_REJECT_REASON_RSSI = 4,
+	MBO_TRANSITION_REJECT_REASON_INTERFERENCE = 5,
+	MBO_TRANSITION_REJECT_REASON_SERVICES = 6,
+};
+
+/* MBO v0.0_r19, 4.4: WNM-Notification vendor subelements */
+enum wfa_wnm_notif_subelem_id {
+	WFA_WNM_NOTIF_SUBELEM_NON_PREF_CHAN_REPORT = 2,
+	WFA_WNM_NOTIF_SUBELEM_CELL_DATA_CAPA = 3,
+};
+
 /* Wi-Fi Direct (P2P) */
 
 #define P2P_OUI_TYPE 9
@@ -1067,6 +1172,15 @@
 #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)
 
@@ -1358,4 +1472,90 @@
 
 #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,
+};
+
+/* IEEE Std 802.11ac-2013, Annex C - dot11PHYType */
+enum phy_type {
+	PHY_TYPE_UNSPECIFIED = 0,
+	PHY_TYPE_FHSS = 1,
+	PHY_TYPE_DSSS = 2,
+	PHY_TYPE_IRBASEBAND = 3,
+	PHY_TYPE_OFDM = 4,
+	PHY_TYPE_HRDSSS = 5,
+	PHY_TYPE_ERP = 6,
+	PHY_TYPE_HT = 7,
+	PHY_TYPE_DMG = 8,
+	PHY_TYPE_VHT = 9,
+};
+
+/* IEEE Std 802.11-2012, 8.4.2.39 - Neighbor Report element */
+/* BSSID Information Field */
+#define NEI_REP_BSSID_INFO_AP_NOT_REACH BIT(0)
+#define NEI_REP_BSSID_INFO_AP_UNKNOWN_REACH BIT(1)
+#define NEI_REP_BSSID_INFO_AP_REACHABLE (BIT(0) | BIT(1))
+#define NEI_REP_BSSID_INFO_SECURITY BIT(2)
+#define NEI_REP_BSSID_INFO_KEY_SCOPE BIT(3)
+#define NEI_REP_BSSID_INFO_SPECTRUM_MGMT BIT(4)
+#define NEI_REP_BSSID_INFO_QOS BIT(5)
+#define NEI_REP_BSSID_INFO_APSD BIT(6)
+#define NEI_REP_BSSID_INFO_RM BIT(7)
+#define NEI_REP_BSSID_INFO_DELAYED_BA BIT(8)
+#define NEI_REP_BSSID_INFO_IMM_BA BIT(9)
+#define NEI_REP_BSSID_INFO_MOBILITY_DOMAIN BIT(10)
+#define NEI_REP_BSSID_INFO_HT BIT(11)
+
 #endif /* IEEE802_11_DEFS_H */
diff --git a/src/common/linux_bridge.h b/src/common/linux_bridge.h
new file mode 100644
index 0000000..7b76846
--- /dev/null
+++ b/src/common/linux_bridge.h
@@ -0,0 +1,24 @@
+/*
+ * Linux bridge configuration kernel interface
+ * Copyright (c) 2016, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef LINUX_BRIDGE_H
+#define LINUX_BRIDGE_H
+
+/* This interface is defined in linux/if_bridge.h */
+
+#define BRCTL_GET_VERSION 0
+#define BRCTL_GET_BRIDGES 1
+#define BRCTL_ADD_BRIDGE 2
+#define BRCTL_DEL_BRIDGE 3
+#define BRCTL_ADD_IF 4
+#define BRCTL_DEL_IF 5
+#define BRCTL_GET_BRIDGE_INFO 6
+#define BRCTL_GET_PORT_LIST 7
+#define BRCTL_SET_BRIDGE_FORWARD_DELAY 8
+
+#endif /* LINUX_BRIDGE_H */
diff --git a/src/common/linux_vlan.h b/src/common/linux_vlan.h
new file mode 100644
index 0000000..8a1dd6e
--- /dev/null
+++ b/src/common/linux_vlan.h
@@ -0,0 +1,52 @@
+/*
+ * Linux VLAN configuration kernel interface
+ * Copyright (c) 2016, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef LINUX_VLAN_H
+#define LINUX_VLAN_H
+
+/* This ioctl is defined in linux/sockios.h */
+
+#ifndef SIOCSIFVLAN
+#define SIOCSIFVLAN 0x8983
+#endif /* SIOCSIFVLAN */
+
+/* This interface is defined in linux/if_vlan.h */
+
+#define ADD_VLAN_CMD 0
+#define DEL_VLAN_CMD 1
+#define SET_VLAN_INGRESS_PRIORITY_CMD 2
+#define SET_VLAN_EGRESS_PRIORITY_CMD 3
+#define GET_VLAN_INGRESS_PRIORITY_CMD 4
+#define GET_VLAN_EGRESS_PRIORITY_CMD 5
+#define SET_VLAN_NAME_TYPE_CMD 6
+#define SET_VLAN_FLAG_CMD 7
+#define GET_VLAN_REALDEV_NAME_CMD 8
+#define GET_VLAN_VID_CMD 9
+
+#define VLAN_NAME_TYPE_PLUS_VID 0
+#define VLAN_NAME_TYPE_RAW_PLUS_VID 1
+#define VLAN_NAME_TYPE_PLUS_VID_NO_PAD 2
+#define VLAN_NAME_TYPE_RAW_PLUS_VID_NO_PAD 3
+
+struct vlan_ioctl_args {
+	int cmd;
+	char device1[24];
+
+	union {
+		char device2[24];
+		int VID;
+		unsigned int skb_priority;
+		unsigned int name_type;
+		unsigned int bind_type;
+		unsigned int flag;
+	} u;
+
+	short vlan_qos;
+};
+
+#endif /* LINUX_VLAN_H */
diff --git a/src/common/privsep_commands.h b/src/common/privsep_commands.h
index c6a472d..8dff303 100644
--- a/src/common/privsep_commands.h
+++ b/src/common/privsep_commands.h
@@ -26,6 +26,25 @@
 	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
@@ -68,6 +87,18 @@
 	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/src/common/qca-vendor.h b/src/common/qca-vendor.h
index 2a6e242..87bbc05 100644
--- a/src/common/qca-vendor.h
+++ b/src/common/qca-vendor.h
@@ -156,6 +156,15 @@
 	QCA_NL80211_VENDOR_SUBCMD_GET_PREFERRED_FREQ_LIST = 103,
 	QCA_NL80211_VENDOR_SUBCMD_SET_PROBABLE_OPER_CHANNEL = 104,
 	QCA_NL80211_VENDOR_SUBCMD_SETBAND = 105,
+	QCA_NL80211_VENDOR_SUBCMD_TRIGGER_SCAN = 106,
+	QCA_NL80211_VENDOR_SUBCMD_SCAN_DONE = 107,
+	QCA_NL80211_VENDOR_SUBCMD_OTA_TEST = 108,
+	QCA_NL80211_VENDOR_SUBCMD_SET_TXPOWER_SCALE = 109,
+	/* 110..114 - reserved for QCA */
+	QCA_NL80211_VENDOR_SUBCMD_SET_TXPOWER_DECR_DB = 115,
+	/* 116..118 - reserved for QCA */
+	QCA_NL80211_VENDOR_SUBCMD_TSF = 119,
+	QCA_NL80211_VENDOR_SUBCMD_WISA = 120,
 };
 
 
@@ -205,6 +214,7 @@
 	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,
+	QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_SUBNET_STATUS,
 	/* keep last */
 	QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_AFTER_LAST,
 	QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_MAX =
@@ -223,6 +233,7 @@
 	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 =
@@ -246,11 +257,14 @@
  *	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.
+ * @QCA_WLAN_VENDOR_FEATURE_OFFCHANNEL_SIMULTANEOUS: Device supports
+ * 	simultaneous off-channel operations.
  * @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,
+	QCA_WLAN_VENDOR_FEATURE_OFFCHANNEL_SIMULTANEOUS = 2,
 	NUM_QCA_WLAN_VENDOR_FEATURES /* keep last */
 };
 
@@ -323,4 +337,174 @@
 	QCA_SETBAND_2G,
 };
 
+/**
+ * enum qca_vendor_attr_get_tsf: Vendor attributes for TSF capture
+ * @QCA_WLAN_VENDOR_ATTR_TSF_CMD: enum qca_tsf_operation (u32)
+ * @QCA_WLAN_VENDOR_ATTR_TSF_TIMER_VALUE: Unsigned 64 bit TSF timer value
+ * @QCA_WLAN_VENDOR_ATTR_TSF_SOC_TIMER_VALUE: Unsigned 64 bit Synchronized
+ *	SOC timer value at TSF capture
+ */
+enum qca_vendor_attr_tsf_cmd {
+	QCA_WLAN_VENDOR_ATTR_TSF_INVALID = 0,
+	QCA_WLAN_VENDOR_ATTR_TSF_CMD,
+	QCA_WLAN_VENDOR_ATTR_TSF_TIMER_VALUE,
+	QCA_WLAN_VENDOR_ATTR_TSF_SOC_TIMER_VALUE,
+	QCA_WLAN_VENDOR_ATTR_TSF_AFTER_LAST,
+	QCA_WLAN_VENDOR_ATTR_TSF_MAX =
+	QCA_WLAN_VENDOR_ATTR_TSF_AFTER_LAST - 1
+};
+
+/**
+ * enum qca_tsf_operation: TSF driver commands
+ * @QCA_TSF_CAPTURE: Initiate TSF Capture
+ * @QCA_TSF_GET: Get TSF capture value
+ * @QCA_TSF_SYNC_GET: Initiate TSF capture and return with captured value
+ */
+enum qca_tsf_cmd {
+	QCA_TSF_CAPTURE,
+	QCA_TSF_GET,
+	QCA_TSF_SYNC_GET,
+};
+
+/**
+ * enum qca_vendor_attr_wisa_cmd
+ * @QCA_WLAN_VENDOR_ATTR_WISA_MODE: WISA mode value (u32)
+ * WISA setup vendor commands
+ */
+enum qca_vendor_attr_wisa_cmd {
+	QCA_WLAN_VENDOR_ATTR_WISA_INVALID = 0,
+	QCA_WLAN_VENDOR_ATTR_WISA_MODE,
+	QCA_WLAN_VENDOR_ATTR_WISA_AFTER_LAST,
+	QCA_WLAN_VENDOR_ATTR_WISA_MAX =
+	QCA_WLAN_VENDOR_ATTR_WISA_AFTER_LAST - 1
+};
+
+/* 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,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_scan - Specifies vendor scan attributes
+ *
+ * @QCA_WLAN_VENDOR_ATTR_SCAN_IE: IEs that should be included as part of scan
+ * @QCA_WLAN_VENDOR_ATTR_SCAN_FREQUENCIES: Nested unsigned 32-bit attributes
+ *	with frequencies to be scanned (in MHz)
+ * @QCA_WLAN_VENDOR_ATTR_SCAN_SSIDS: Nested attribute with SSIDs to be scanned
+ * @QCA_WLAN_VENDOR_ATTR_SCAN_SUPP_RATES: Nested array attribute of supported
+ *	rates to be included
+ * @QCA_WLAN_VENDOR_ATTR_SCAN_TX_NO_CCK_RATE: flag used to send probe requests
+ * 	at non CCK rate in 2GHz band
+ * @QCA_WLAN_VENDOR_ATTR_SCAN_FLAGS: Unsigned 32-bit scan flags
+ * @QCA_WLAN_VENDOR_ATTR_SCAN_COOKIE: Unsigned 64-bit cookie provided by the
+ * 	driver for the specific scan request
+ * @QCA_WLAN_VENDOR_ATTR_SCAN_STATUS: Unsigned 8-bit status of the scan
+ * 	request decoded as in enum scan_status
+ * @QCA_WLAN_VENDOR_ATTR_SCAN_MAC: 6-byte MAC address to use when randomisation
+ * 	scan flag is set
+ * @QCA_WLAN_VENDOR_ATTR_SCAN_MAC_MASK: 6-byte MAC address mask to be used with
+ * 	randomisation
+ */
+enum qca_wlan_vendor_attr_scan {
+	QCA_WLAN_VENDOR_ATTR_SCAN_INVALID_PARAM = 0,
+	QCA_WLAN_VENDOR_ATTR_SCAN_IE,
+	QCA_WLAN_VENDOR_ATTR_SCAN_FREQUENCIES,
+	QCA_WLAN_VENDOR_ATTR_SCAN_SSIDS,
+	QCA_WLAN_VENDOR_ATTR_SCAN_SUPP_RATES,
+	QCA_WLAN_VENDOR_ATTR_SCAN_TX_NO_CCK_RATE,
+	QCA_WLAN_VENDOR_ATTR_SCAN_FLAGS,
+	QCA_WLAN_VENDOR_ATTR_SCAN_COOKIE,
+	QCA_WLAN_VENDOR_ATTR_SCAN_STATUS,
+	QCA_WLAN_VENDOR_ATTR_SCAN_MAC,
+	QCA_WLAN_VENDOR_ATTR_SCAN_MAC_MASK,
+	QCA_WLAN_VENDOR_ATTR_SCAN_AFTER_LAST,
+	QCA_WLAN_VENDOR_ATTR_SCAN_MAX =
+	QCA_WLAN_VENDOR_ATTR_SCAN_AFTER_LAST - 1
+};
+
+/**
+ * enum scan_status - Specifies the valid values the vendor scan attribute
+ * 	QCA_WLAN_VENDOR_ATTR_SCAN_STATUS can take
+ *
+ * @VENDOR_SCAN_STATUS_NEW_RESULTS: implies the vendor scan is successful with
+ * 	new scan results
+ * @VENDOR_SCAN_STATUS_ABORTED: implies the vendor scan was aborted in-between
+ */
+enum scan_status {
+	VENDOR_SCAN_STATUS_NEW_RESULTS,
+	VENDOR_SCAN_STATUS_ABORTED,
+	VENDOR_SCAN_STATUS_MAX,
+};
+
+/**
+ * enum qca_vendor_attr_ota_test - Specifies the values for vendor
+ *                       command QCA_NL80211_VENDOR_SUBCMD_OTA_TEST
+ * @QCA_WLAN_VENDOR_ATTR_OTA_TEST_ENABLE: enable ota test
+ */
+enum qca_vendor_attr_ota_test {
+	QCA_WLAN_VENDOR_ATTR_OTA_TEST_INVALID,
+	/* 8-bit unsigned value to indicate if OTA test is enabled */
+	QCA_WLAN_VENDOR_ATTR_OTA_TEST_ENABLE,
+	/* keep last */
+	QCA_WLAN_VENDOR_ATTR_OTA_TEST_AFTER_LAST,
+	QCA_WLAN_VENDOR_ATTR_OTA_TEST_MAX =
+	QCA_WLAN_VENDOR_ATTR_OTA_TEST_AFTER_LAST - 1
+};
+
+/**
+ * enum qca_vendor_attr_txpower_scale - vendor sub commands index
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TXPOWER_SCALE: scaling value
+ */
+enum qca_vendor_attr_txpower_scale {
+	QCA_WLAN_VENDOR_ATTR_TXPOWER_SCALE_INVALID,
+	/* 8-bit unsigned value to indicate the scaling of tx power */
+	QCA_WLAN_VENDOR_ATTR_TXPOWER_SCALE,
+	/* keep last */
+	QCA_WLAN_VENDOR_ATTR_TXPOWER_SCALE_AFTER_LAST,
+	QCA_WLAN_VENDOR_ATTR_TXPOWER_SCALE_MAX =
+	QCA_WLAN_VENDOR_ATTR_TXPOWER_SCALE_AFTER_LAST - 1
+};
+
+/**
+ * enum qca_vendor_attr_txpower_decr_db - Attributes for TX power decrease
+ *
+ * These attributes are used with QCA_NL80211_VENDOR_SUBCMD_SET_TXPOWER_DECR_DB.
+ */
+enum qca_vendor_attr_txpower_decr_db {
+	QCA_WLAN_VENDOR_ATTR_TXPOWER_DECR_DB_INVALID,
+	/* 8-bit unsigned value to indicate the reduction of TX power in dB for
+	 * a virtual interface. */
+	QCA_WLAN_VENDOR_ATTR_TXPOWER_DECR_DB,
+	/* keep last */
+	QCA_WLAN_VENDOR_ATTR_TXPOWER_DECR_DB_AFTER_LAST,
+	QCA_WLAN_VENDOR_ATTR_TXPOWER_DECR_DB_MAX =
+	QCA_WLAN_VENDOR_ATTR_TXPOWER_DECR_DB_AFTER_LAST - 1
+};
+
 #endif /* QCA_VENDOR_H */
diff --git a/src/common/sae.c b/src/common/sae.c
index 503fa1d..9f70f03 100644
--- a/src/common/sae.c
+++ b/src/common/sae.c
@@ -1,6 +1,6 @@
 /*
  * Simultaneous authentication of equals
- * Copyright (c) 2012-2015, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2012-2016, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -275,8 +275,9 @@
 
 	/* 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 (sha256_prf_bits(pwd_seed, SHA256_MAC_LEN, "SAE Hunting and Pecking",
+			    prime, sae->tmp->prime_len, pwd_value, bits) < 0)
+		return -1;
 	if (bits % 8)
 		buf_shift_right(pwd_value, sizeof(pwd_value), 8 - bits % 8);
 	wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-value",
@@ -318,11 +319,10 @@
 	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);
+	if (sha256_prf_bits(pwd_seed, SHA256_MAC_LEN, "SAE Hunting and Pecking",
+			    sae->tmp->dh->prime, sae->tmp->prime_len, pwd_value,
+			    bits) < 0)
+		return -1;
 	wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-value", pwd_value,
 			sae->tmp->prime_len);
 
@@ -811,11 +811,13 @@
 	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));
+	if (sha256_prf(keyseed, sizeof(keyseed), "SAE KCK and PMK",
+		       val, sae->tmp->prime_len, keys, sizeof(keys)) < 0)
+		goto fail;
 	os_memset(keyseed, 0, sizeof(keyseed));
 	os_memcpy(sae->tmp->kck, keys, SAE_KCK_LEN);
 	os_memcpy(sae->pmk, keys + SAE_KCK_LEN, SAE_PMK_LEN);
+	os_memcpy(sae->pmkid, val, SAE_PMKID_LEN);
 	os_memset(keys, 0, sizeof(keys));
 	wpa_hexdump_key(MSG_DEBUG, "SAE: KCK", sae->tmp->kck, SAE_KCK_LEN);
 	wpa_hexdump_key(MSG_DEBUG, "SAE: PMK", sae->pmk, SAE_PMK_LEN);
@@ -923,7 +925,7 @@
 				   const u8 *end, const u8 **token,
 				   size_t *token_len)
 {
-	if (*pos + (sae->tmp->ec ? 3 : 2) * sae->tmp->prime_len < end) {
+	if ((sae->tmp->ec ? 3 : 2) * sae->tmp->prime_len < end - *pos) {
 		size_t tlen = end - (*pos + (sae->tmp->ec ? 3 : 2) *
 				     sae->tmp->prime_len);
 		wpa_hexdump(MSG_DEBUG, "SAE: Anti-Clogging Token", *pos, tlen);
@@ -946,7 +948,7 @@
 {
 	struct crypto_bignum *peer_scalar;
 
-	if (*pos + sae->tmp->prime_len > end) {
+	if (sae->tmp->prime_len > end - *pos) {
 		wpa_printf(MSG_DEBUG, "SAE: Not enough data for scalar");
 		return WLAN_STATUS_UNSPECIFIED_FAILURE;
 	}
@@ -994,7 +996,7 @@
 {
 	u8 prime[SAE_MAX_ECC_PRIME_LEN];
 
-	if (pos + 2 * sae->tmp->prime_len > end) {
+	if (2 * sae->tmp->prime_len > end - pos) {
 		wpa_printf(MSG_DEBUG, "SAE: Not enough data for "
 			   "commit-element");
 		return WLAN_STATUS_UNSPECIFIED_FAILURE;
@@ -1040,7 +1042,7 @@
 	struct crypto_bignum *res, *one;
 	const u8 one_bin[1] = { 0x01 };
 
-	if (pos + sae->tmp->prime_len > end) {
+	if (sae->tmp->prime_len > end - pos) {
 		wpa_printf(MSG_DEBUG, "SAE: Not enough data for "
 			   "commit-element");
 		return WLAN_STATUS_UNSPECIFIED_FAILURE;
@@ -1098,7 +1100,7 @@
 	u16 res;
 
 	/* Check Finite Cyclic Group */
-	if (pos + 2 > end)
+	if (end - pos < 2)
 		return WLAN_STATUS_UNSPECIFIED_FAILURE;
 	res = sae_group_allowed(sae, allowed_groups, WPA_GET_LE16(pos));
 	if (res != WLAN_STATUS_SUCCESS)
diff --git a/src/common/sae.h b/src/common/sae.h
index c07026c..a4270bc 100644
--- a/src/common/sae.h
+++ b/src/common/sae.h
@@ -45,6 +45,7 @@
 	enum { SAE_NOTHING, SAE_COMMITTED, SAE_CONFIRMED, SAE_ACCEPTED } state;
 	u16 send_confirm;
 	u8 pmk[SAE_PMK_LEN];
+	u8 pmkid[SAE_PMKID_LEN];
 	struct crypto_bignum *peer_commit_scalar;
 	int group;
 	int sync;
diff --git a/src/common/version.h b/src/common/version.h
index 5ddf617..ae5c9d4 100644
--- a/src/common/version.h
+++ b/src/common/version.h
@@ -5,6 +5,10 @@
 #define VERSION_STR_POSTFIX ""
 #endif /* VERSION_STR_POSTFIX */
 
-#define VERSION_STR "2.5-devel" VERSION_STR_POSTFIX
+#ifndef GIT_VERSION_STR_POSTFIX
+#define GIT_VERSION_STR_POSTFIX ""
+#endif /* GIT_VERSION_STR_POSTFIX */
+
+#define VERSION_STR "2.6-devel" VERSION_STR_POSTFIX GIT_VERSION_STR_POSTFIX
 
 #endif /* VERSION_H */
diff --git a/src/common/wpa_common.c b/src/common/wpa_common.c
index e485b5b..d6295b2 100644
--- a/src/common/wpa_common.c
+++ b/src/common/wpa_common.c
@@ -170,6 +170,12 @@
 	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),
@@ -286,38 +292,47 @@
 	pos = ie + sizeof(struct rsn_ftie);
 	end = ie + ie_len;
 
-	while (pos + 2 <= end && pos + 2 + pos[1] <= end) {
-		switch (pos[0]) {
+	while (end - pos >= 2) {
+		u8 id, len;
+
+		id = *pos++;
+		len = *pos++;
+		if (len > end - pos)
+			break;
+
+		switch (id) {
 		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]);
+			if (len != FT_R1KH_ID_LEN) {
+				wpa_printf(MSG_DEBUG,
+					   "FT: Invalid R1KH-ID length in FTIE: %d",
+					   len);
 				return -1;
 			}
-			parse->r1kh_id = pos + 2;
+			parse->r1kh_id = pos;
 			break;
 		case FTIE_SUBELEM_GTK:
-			parse->gtk = pos + 2;
-			parse->gtk_len = pos[1];
+			parse->gtk = pos;
+			parse->gtk_len = len;
 			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]);
+			if (len < 1 || len > FT_R0KH_ID_MAX_LEN) {
+				wpa_printf(MSG_DEBUG,
+					   "FT: Invalid R0KH-ID length in FTIE: %d",
+					   len);
 				return -1;
 			}
-			parse->r0kh_id = pos + 2;
-			parse->r0kh_id_len = pos[1];
+			parse->r0kh_id = pos;
+			parse->r0kh_id_len = len;
 			break;
 #ifdef CONFIG_IEEE80211W
 		case FTIE_SUBELEM_IGTK:
-			parse->igtk = pos + 2;
-			parse->igtk_len = pos[1];
+			parse->igtk = pos;
+			parse->igtk_len = len;
 			break;
 #endif /* CONFIG_IEEE80211W */
 		}
 
-		pos += 2 + pos[1];
+		pos += len;
 	}
 
 	return 0;
@@ -339,11 +354,18 @@
 
 	pos = ies;
 	end = ies + ies_len;
-	while (pos + 2 <= end && pos + 2 + pos[1] <= end) {
-		switch (pos[0]) {
+	while (end - pos >= 2) {
+		u8 id, len;
+
+		id = *pos++;
+		len = *pos++;
+		if (len > end - pos)
+			break;
+
+		switch (id) {
 		case WLAN_EID_RSN:
-			parse->rsn = pos + 2;
-			parse->rsn_len = pos[1];
+			parse->rsn = pos;
+			parse->rsn_len = len;
 			ret = wpa_parse_wpa_ie_rsn(parse->rsn - 2,
 						   parse->rsn_len + 2,
 						   &data);
@@ -356,32 +378,32 @@
 				parse->rsn_pmkid = data.pmkid;
 			break;
 		case WLAN_EID_MOBILITY_DOMAIN:
-			if (pos[1] < sizeof(struct rsn_mdie))
+			if (len < sizeof(struct rsn_mdie))
 				return -1;
-			parse->mdie = pos + 2;
-			parse->mdie_len = pos[1];
+			parse->mdie = pos;
+			parse->mdie_len = len;
 			break;
 		case WLAN_EID_FAST_BSS_TRANSITION:
-			if (pos[1] < sizeof(*ftie))
+			if (len < sizeof(*ftie))
 				return -1;
-			ftie = (const struct rsn_ftie *) (pos + 2);
+			ftie = (const struct rsn_ftie *) pos;
 			prot_ie_count = ftie->mic_control[1];
-			if (wpa_ft_parse_ftie(pos + 2, pos[1], parse) < 0)
+			if (wpa_ft_parse_ftie(pos, len, parse) < 0)
 				return -1;
 			break;
 		case WLAN_EID_TIMEOUT_INTERVAL:
-			if (pos[1] != 5)
+			if (len != 5)
 				break;
-			parse->tie = pos + 2;
-			parse->tie_len = pos[1];
+			parse->tie = pos;
+			parse->tie_len = len;
 			break;
 		case WLAN_EID_RIC_DATA:
 			if (parse->ric == NULL)
-				parse->ric = pos;
+				parse->ric = pos - 2;
 			break;
 		}
 
-		pos += 2 + pos[1];
+		pos += len;
 	}
 
 	if (prot_ie_count == 0)
@@ -410,13 +432,15 @@
 	}
 
 	/* 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];
+	if (parse->ric) {
+		pos = parse->ric;
+		while (end - pos >= 2 && 2 + pos[1] <= end - pos &&
+		       prot_ie_count) {
+			prot_ie_count--;
+			pos += 2 + pos[1];
+		}
+		parse->ric_len = pos - parse->ric;
 	}
-	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);
@@ -492,7 +516,7 @@
 }
 
 
-static int wpa_cipher_valid_group(int cipher)
+int wpa_cipher_valid_group(int cipher)
 {
 	return wpa_cipher_valid_pairwise(cipher) ||
 		cipher == WPA_CIPHER_GTK_NOT_USED;
@@ -576,8 +600,10 @@
 	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);
+			wpa_printf(MSG_DEBUG,
+				   "%s: invalid group cipher 0x%x (%08x)",
+				   __func__, data->group_cipher,
+				   WPA_GET_BE32(pos));
 			return -1;
 		}
 		pos += RSN_SELECTOR_LEN;
@@ -665,9 +691,10 @@
 	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);
+			wpa_printf(MSG_DEBUG,
+				   "%s: Unsupported management group cipher 0x%x (%08x)",
+				   __func__, data->mgmt_group_cipher,
+				   WPA_GET_BE32(pos));
 			return -10;
 		}
 		pos += RSN_SELECTOR_LEN;
@@ -1255,13 +1282,13 @@
 
 
 #ifdef CONFIG_IEEE80211R
-int wpa_insert_pmkid(u8 *ies, size_t ies_len, const u8 *pmkid)
+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;
+	end = ies + *ies_len;
 
 	while (start < end) {
 		if (*start == WLAN_EID_RSN)
@@ -1292,6 +1319,9 @@
 		os_memmove(rpos + 2, rpos, end - rpos);
 		*rpos++ = 0;
 		*rpos++ = 0;
+		added += 2;
+		start[1] += 2;
+		rend = rpos;
 	} else {
 		/* Skip RSN Capabilities */
 		rpos += 2;
@@ -1304,22 +1334,40 @@
 
 	if (rpos == rend) {
 		/* No PMKID-Count field included; add it */
-		os_memmove(rpos + 2 + PMKID_LEN, rpos, end - rpos);
+		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");
+		u16 num_pmkid;
+
+		if (rend - rpos < 2)
 			return -1;
+		num_pmkid = WPA_GET_LE16(rpos);
+		/* PMKID-Count was included; use it */
+		if (num_pmkid != 0) {
+			u8 *after;
+
+			if (num_pmkid * PMKID_LEN > rend - rpos - 2)
+				return -1;
+			/*
+			 * PMKID may have been included in RSN IE in
+			 * (Re)Association Request frame, so remove the old
+			 * PMKID(s) first before adding the new one.
+			 */
+			wpa_printf(MSG_DEBUG,
+				   "FT: Remove %u old PMKID(s) from RSN IE",
+				   num_pmkid);
+			after = rpos + 2 + num_pmkid * PMKID_LEN;
+			os_memmove(rpos + 2, after, rend - after);
+			start[1] -= num_pmkid * PMKID_LEN;
+			added -= num_pmkid * PMKID_LEN;
 		}
 		WPA_PUT_LE16(rpos, 1);
 		rpos += 2;
-		os_memmove(rpos + PMKID_LEN, rpos, end - rpos);
+		os_memmove(rpos + PMKID_LEN, rpos, end + added - rpos);
 		os_memcpy(rpos, pmkid, PMKID_LEN);
 		added += PMKID_LEN;
 		start[1] += PMKID_LEN;
@@ -1328,7 +1376,9 @@
 	wpa_hexdump(MSG_DEBUG, "FT: RSN IE after modification "
 		    "(PMKID inserted)", start, 2 + start[1]);
 
-	return added;
+	*ies_len += added;
+
+	return 0;
 }
 #endif /* CONFIG_IEEE80211R */
 
diff --git a/src/common/wpa_common.h b/src/common/wpa_common.h
index d7a590f..af1d0f0 100644
--- a/src/common/wpa_common.h
+++ b/src/common/wpa_common.h
@@ -12,6 +12,8 @@
 /* IEEE 802.11i */
 #define PMKID_LEN 16
 #define PMK_LEN 32
+#define PMK_LEN_SUITE_B_192 48
+#define PMK_LEN_MAX 48
 #define WPA_REPLAY_COUNTER_LEN 8
 #define WPA_NONCE_LEN 32
 #define WPA_KEY_RSC_LEN 8
@@ -407,7 +409,7 @@
 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);
+int wpa_insert_pmkid(u8 *ies, size_t *ies_len, const u8 *pmkid);
 
 struct wpa_ft_ies {
 	const u8 *mdie;
@@ -435,6 +437,7 @@
 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);
diff --git a/src/common/wpa_ctrl.c b/src/common/wpa_ctrl.c
index 82d4655..623c2a7 100644
--- a/src/common/wpa_ctrl.c
+++ b/src/common/wpa_ctrl.c
@@ -85,6 +85,13 @@
 
 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;
@@ -108,10 +115,18 @@
 	ctrl->local.sun_family = AF_UNIX;
 	counter++;
 try_again:
-	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 (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);
@@ -137,6 +152,8 @@
 
 #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) {
@@ -515,6 +532,8 @@
 		FD_ZERO(&rfds);
 		FD_SET(ctrl->s, &rfds);
 		res = select(ctrl->s + 1, &rfds, NULL, NULL, &tv);
+		if (res < 0 && errno == EINTR)
+			continue;
 		if (res < 0)
 			return res;
 		if (FD_ISSET(ctrl->s, &rfds)) {
diff --git a/src/common/wpa_ctrl.h b/src/common/wpa_ctrl.h
index e700523..d9641bb 100644
--- a/src/common/wpa_ctrl.h
+++ b/src/common/wpa_ctrl.h
@@ -28,6 +28,8 @@
 #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 */
@@ -75,6 +77,19 @@
 /** Regulatory domain channel */
 #define WPA_EVENT_REGDOM_CHANGE "CTRL-EVENT-REGDOM-CHANGE "
 
+/** IP subnet status change notification
+ *
+ * When using an offloaded roaming mechanism where driver/firmware takes care
+ * of roaming and IP subnet validation checks post-roaming, this event can
+ * indicate whether IP subnet has changed.
+ *
+ * The event has a status=<0/1/2> parameter where
+ * 0 = unknown
+ * 1 = IP subnet unchanged (can continue to use the old IP address)
+ * 2 = IP subnet changed (need to get a new IP address)
+ */
+#define WPA_EVENT_SUBNET_STATUS_UPDATE "CTRL-EVENT-SUBNET-STATUS-UPDATE "
+
 /** RSN IBSS 4-way handshakes completed with specified peer */
 #define IBSS_RSN_COMPLETED "IBSS-RSN-COMPLETED "
 
@@ -255,6 +270,12 @@
 /* BSS Transition Management Response frame received */
 #define BSS_TM_RESP "BSS-TM-RESP "
 
+/* MBO IE with cellular data connection preference received */
+#define MBO_CELL_PREFERENCE "MBO-CELL-PREFERENCE "
+
+/* BSS Transition Management Request received with MBO transition reason */
+#define MBO_TRANSITION_REASON "MBO-TRANSITION-REASON "
+
 /* BSS command information masks */
 
 #define WPA_BSS_MASK_ALL		0xFFFDFFFF
@@ -279,6 +300,7 @@
 #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 */
@@ -315,6 +337,20 @@
  */
 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
diff --git a/src/common/wpa_helpers.c b/src/common/wpa_helpers.c
index 28913b9..f159421 100644
--- a/src/common/wpa_helpers.c
+++ b/src/common/wpa_helpers.c
@@ -172,7 +172,8 @@
 	if (ctrl == NULL)
 		return -1;
 	len = sizeof(buf);
-	if (wpa_ctrl_request(ctrl, "STATUS", 6, buf, &len, NULL) < 0) {
+	if (wpa_ctrl_request(ctrl, "STATUS-NO_EVENTS", 16, buf, &len,
+			     NULL) < 0) {
 		wpa_ctrl_close(ctrl);
 		return -1;
 	}
diff --git a/src/crypto/Makefile b/src/crypto/Makefile
index 3e90350..d181e72 100644
--- a/src/crypto/Makefile
+++ b/src/crypto/Makefile
@@ -47,7 +47,9 @@
 	sha256.o \
 	sha256-prf.o \
 	sha256-tlsprf.o \
-	sha256-internal.o
+	sha256-internal.o \
+	sha384-internal.o \
+	sha512-internal.o
 
 LIB_OBJS += crypto_internal.o
 LIB_OBJS += crypto_internal-cipher.o
diff --git a/src/crypto/aes-cbc.c b/src/crypto/aes-cbc.c
index 2833cfc..0835f2c 100644
--- a/src/crypto/aes-cbc.c
+++ b/src/crypto/aes-cbc.c
@@ -28,6 +28,9 @@
 	u8 *pos = data;
 	int i, j, blocks;
 
+	if (TEST_FAIL())
+		return -1;
+
 	ctx = aes_encrypt_init(key, 16);
 	if (ctx == NULL)
 		return -1;
@@ -61,6 +64,9 @@
 	u8 *pos = data;
 	int i, j, blocks;
 
+	if (TEST_FAIL())
+		return -1;
+
 	ctx = aes_decrypt_init(key, 16);
 	if (ctx == NULL)
 		return -1;
diff --git a/src/crypto/aes-omac1.c b/src/crypto/aes-omac1.c
index 375db57..8642516 100644
--- a/src/crypto/aes-omac1.c
+++ b/src/crypto/aes-omac1.c
@@ -48,6 +48,9 @@
 	const u8 *pos, *end;
 	size_t i, e, left, total_len;
 
+	if (TEST_FAIL())
+		return -1;
+
 	ctx = aes_encrypt_init(key, key_len);
 	if (ctx == NULL)
 		return -1;
diff --git a/src/crypto/crypto.h b/src/crypto/crypto.h
index 534c4bd..bdc3ba6 100644
--- a/src/crypto/crypto.h
+++ b/src/crypto/crypto.h
@@ -80,6 +80,28 @@
 		  u8 *mac);
 
 /**
+ * sha384_vector - SHA384 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 sha384_vector(size_t num_elem, const u8 *addr[], const size_t *len,
+		  u8 *mac);
+
+/**
+ * sha512_vector - SHA512 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 sha512_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)
@@ -135,7 +157,8 @@
 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
+	CRYPTO_HASH_ALG_SHA256, CRYPTO_HASH_ALG_HMAC_SHA256,
+	CRYPTO_HASH_ALG_SHA384, CRYPTO_HASH_ALG_SHA512
 };
 
 struct crypto_hash;
diff --git a/src/crypto/crypto_internal.c b/src/crypto/crypto_internal.c
index f3602da..d391f48 100644
--- a/src/crypto/crypto_internal.c
+++ b/src/crypto/crypto_internal.c
@@ -11,6 +11,8 @@
 #include "common.h"
 #include "crypto.h"
 #include "sha256_i.h"
+#include "sha384_i.h"
+#include "sha512_i.h"
 #include "sha1_i.h"
 #include "md5_i.h"
 
@@ -22,6 +24,12 @@
 #ifdef CONFIG_SHA256
 		struct sha256_state sha256;
 #endif /* CONFIG_SHA256 */
+#ifdef CONFIG_INTERNAL_SHA384
+		struct sha384_state sha384;
+#endif /* CONFIG_INTERNAL_SHA384 */
+#ifdef CONFIG_INTERNAL_SHA512
+		struct sha512_state sha512;
+#endif /* CONFIG_INTERNAL_SHA512 */
 	} u;
 	u8 key[64];
 	size_t key_len;
@@ -54,6 +62,16 @@
 		sha256_init(&ctx->u.sha256);
 		break;
 #endif /* CONFIG_SHA256 */
+#ifdef CONFIG_INTERNAL_SHA384
+	case CRYPTO_HASH_ALG_SHA384:
+		sha384_init(&ctx->u.sha384);
+		break;
+#endif /* CONFIG_INTERNAL_SHA384 */
+#ifdef CONFIG_INTERNAL_SHA512
+	case CRYPTO_HASH_ALG_SHA512:
+		sha512_init(&ctx->u.sha512);
+		break;
+#endif /* CONFIG_INTERNAL_SHA512 */
 	case CRYPTO_HASH_ALG_HMAC_MD5:
 		if (key_len > sizeof(k_pad)) {
 			MD5Init(&ctx->u.md5);
@@ -142,6 +160,16 @@
 		sha256_process(&ctx->u.sha256, data, len);
 		break;
 #endif /* CONFIG_SHA256 */
+#ifdef CONFIG_INTERNAL_SHA384
+	case CRYPTO_HASH_ALG_SHA384:
+		sha384_process(&ctx->u.sha384, data, len);
+		break;
+#endif /* CONFIG_INTERNAL_SHA384 */
+#ifdef CONFIG_INTERNAL_SHA512
+	case CRYPTO_HASH_ALG_SHA512:
+		sha512_process(&ctx->u.sha512, data, len);
+		break;
+#endif /* CONFIG_INTERNAL_SHA512 */
 	default:
 		break;
 	}
@@ -191,6 +219,28 @@
 		sha256_done(&ctx->u.sha256, mac);
 		break;
 #endif /* CONFIG_SHA256 */
+#ifdef CONFIG_INTERNAL_SHA384
+	case CRYPTO_HASH_ALG_SHA384:
+		if (*len < 48) {
+			*len = 48;
+			os_free(ctx);
+			return -1;
+		}
+		*len = 48;
+		sha384_done(&ctx->u.sha384, mac);
+		break;
+#endif /* CONFIG_INTERNAL_SHA384 */
+#ifdef CONFIG_INTERNAL_SHA512
+	case CRYPTO_HASH_ALG_SHA512:
+		if (*len < 64) {
+			*len = 64;
+			os_free(ctx);
+			return -1;
+		}
+		*len = 64;
+		sha512_done(&ctx->u.sha512, mac);
+		break;
+#endif /* CONFIG_INTERNAL_SHA512 */
 	case CRYPTO_HASH_ALG_HMAC_MD5:
 		if (*len < 16) {
 			*len = 16;
diff --git a/src/crypto/crypto_module_tests.c b/src/crypto/crypto_module_tests.c
index 1d613c9..087953b 100644
--- a/src/crypto/crypto_module_tests.c
+++ b/src/crypto/crypto_module_tests.c
@@ -516,6 +516,7 @@
 		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,
@@ -531,6 +532,7 @@
 		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,
@@ -547,6 +549,7 @@
 		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,
@@ -564,6 +567,7 @@
 		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,
@@ -624,6 +628,7 @@
 		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)) {
@@ -643,6 +648,7 @@
 		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,
@@ -664,6 +670,7 @@
 		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)) {
@@ -683,6 +690,7 @@
 		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,
@@ -733,6 +741,7 @@
 
 static int test_md5(void)
 {
+#ifndef CONFIG_FIPS
 	struct {
 		char *data;
 		char *hash;
@@ -811,6 +820,10 @@
 		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 */
 }
 
 
@@ -842,6 +855,7 @@
 		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,
@@ -858,6 +872,7 @@
 		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,
@@ -932,6 +947,7 @@
 		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),
@@ -940,6 +956,7 @@
 		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",
@@ -1486,6 +1503,7 @@
 	const u8 *addr[2];
 	size_t len[2];
 	int errors = 0;
+	u8 *key;
 
 	for (i = 0; i < ARRAY_SIZE(tests); i++) {
 		wpa_printf(MSG_INFO, "SHA256 test case %d:", i + 1);
@@ -1556,14 +1574,69 @@
 		   hash, sizeof(hash));
 	/* TODO: add proper test case for this */
 
+	key = os_malloc(8161);
+	if (key) {
+#ifdef CONFIG_HMAC_SHA256_KDF
+		int res;
+
+		res = hmac_sha256_kdf((u8 *) "secret", 6, "label",
+				      (u8 *) "seed", 4, key, 8160);
+		if (res) {
+			wpa_printf(MSG_INFO,
+				   "Unexpected hmac_sha256_kdf(outlen=8160) failure");
+			errors++;
+		}
+
+		res = hmac_sha256_kdf((u8 *) "secret", 6, "label",
+				      (u8 *) "seed", 4, key, 8161);
+		if (res == 0) {
+			wpa_printf(MSG_INFO,
+				   "Unexpected hmac_sha256_kdf(outlen=8161) success");
+			errors++;
+		}
+#endif /* CONFIG_HMAC_SHA256_KDF */
+
+		os_free(key);
+	}
+
 	if (!errors)
 		wpa_printf(MSG_INFO, "SHA256 test cases passed");
 	return errors;
 }
 
 
+static int test_fips186_2_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];
+
+	wpa_printf(MSG_INFO,
+		   "Testing EAP-SIM PRF (FIPS 186-2 + change notice 1)");
+	if (fips186_2_prf(xkey, sizeof(xkey), buf, sizeof(buf)) < 0 ||
+	    os_memcmp(w, buf, sizeof(w)) != 0) {
+		wpa_printf(MSG_INFO, "fips186_2_prf failed");
+		return 1;
+	}
+
+	return 0;
+}
+
+
 static int test_ms_funcs(void)
 {
+#ifndef CONFIG_FIPS
 	/* Test vector from RFC2759 example */
 	char *username = "User";
 	char *password = "clientPass";
@@ -1656,6 +1729,10 @@
 		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 */
 }
 
 
@@ -1673,6 +1750,7 @@
 	    test_md5() ||
 	    test_sha1() ||
 	    test_sha256() ||
+	    test_fips186_2_prf() ||
 	    test_ms_funcs())
 		ret = -1;
 
diff --git a/src/crypto/crypto_openssl.c b/src/crypto/crypto_openssl.c
index 3703b93..c5a28ce 100644
--- a/src/crypto/crypto_openssl.c
+++ b/src/crypto/crypto_openssl.c
@@ -31,6 +31,45 @@
 #include "sha384.h"
 #include "crypto.h"
 
+#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
+/* Compatibility wrappers for older versions. */
+
+static HMAC_CTX * HMAC_CTX_new(void)
+{
+	HMAC_CTX *ctx;
+
+	ctx = os_zalloc(sizeof(*ctx));
+	if (ctx)
+		HMAC_CTX_init(ctx);
+	return ctx;
+}
+
+
+static void HMAC_CTX_free(HMAC_CTX *ctx)
+{
+	HMAC_CTX_cleanup(ctx);
+	bin_clear_free(ctx, sizeof(*ctx));
+}
+
+
+static EVP_MD_CTX * EVP_MD_CTX_new(void)
+{
+	EVP_MD_CTX *ctx;
+
+	ctx = os_zalloc(sizeof(*ctx));
+	if (ctx)
+		EVP_MD_CTX_init(ctx);
+	return ctx;
+}
+
+
+static void EVP_MD_CTX_free(EVP_MD_CTX *ctx)
+{
+	bin_clear_free(ctx, sizeof(*ctx));
+}
+
+#endif /* OpenSSL version < 1.1.0 */
+
 static BIGNUM * get_group5_prime(void)
 {
 #ifdef OPENSSL_IS_BORINGSSL
@@ -65,38 +104,49 @@
 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;
+	EVP_MD_CTX *ctx;
 	size_t i;
 	unsigned int mac_len;
 
-	EVP_MD_CTX_init(&ctx);
-	if (!EVP_DigestInit_ex(&ctx, type, NULL)) {
+	if (TEST_FAIL())
+		return -1;
+
+	ctx = EVP_MD_CTX_new();
+	if (!ctx)
+		return -1;
+	if (!EVP_DigestInit_ex(ctx, type, NULL)) {
 		wpa_printf(MSG_ERROR, "OpenSSL: EVP_DigestInit_ex failed: %s",
 			   ERR_error_string(ERR_get_error(), NULL));
+		EVP_MD_CTX_free(ctx);
 		return -1;
 	}
 	for (i = 0; i < num_elem; i++) {
-		if (!EVP_DigestUpdate(&ctx, addr[i], len[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));
+			EVP_MD_CTX_free(ctx);
 			return -1;
 		}
 	}
-	if (!EVP_DigestFinal(&ctx, mac, &mac_len)) {
+	if (!EVP_DigestFinal(ctx, mac, &mac_len)) {
 		wpa_printf(MSG_ERROR, "OpenSSL: EVP_DigestFinal failed: %s",
 			   ERR_error_string(ERR_get_error(), NULL));
+		EVP_MD_CTX_free(ctx);
 		return -1;
 	}
+	EVP_MD_CTX_free(ctx);
 
 	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)
@@ -120,47 +170,53 @@
 }
 
 
+#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;
+	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))
+	ctx = EVP_CIPHER_CTX_new();
+	if (!ctx ||
+	    !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))
+		if (!EVP_CipherUpdate(ctx, skip_buf, &outl, skip_buf, len))
 			goto out;
 		skip -= len;
 	}
 
-	if (EVP_CipherUpdate(&ctx, data, &outl, data, data_len))
+	if (EVP_CipherUpdate(ctx, data, &outl, data, data_len))
 		res = 0;
 
 out:
-	EVP_CIPHER_CTX_cleanup(&ctx);
+	if (ctx)
+		EVP_CIPHER_CTX_free(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)
@@ -200,14 +256,16 @@
 	EVP_CIPHER_CTX *ctx;
 	const EVP_CIPHER *type;
 
+	if (TEST_FAIL())
+		return NULL;
+
 	type = aes_get_evp_cipher(len);
 	if (type == NULL)
 		return NULL;
 
-	ctx = os_malloc(sizeof(*ctx));
+	ctx = EVP_CIPHER_CTX_new();
 	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;
@@ -241,8 +299,7 @@
 		wpa_printf(MSG_ERROR, "OpenSSL: Unexpected padding length %d "
 			   "in AES encrypt", len);
 	}
-	EVP_CIPHER_CTX_cleanup(c);
-	bin_clear_free(c, sizeof(*c));
+	EVP_CIPHER_CTX_free(c);
 }
 
 
@@ -251,16 +308,18 @@
 	EVP_CIPHER_CTX *ctx;
 	const EVP_CIPHER *type;
 
+	if (TEST_FAIL())
+		return NULL;
+
 	type = aes_get_evp_cipher(len);
 	if (type == NULL)
 		return NULL;
 
-	ctx = os_malloc(sizeof(*ctx));
+	ctx = EVP_CIPHER_CTX_new();
 	if (ctx == NULL)
 		return NULL;
-	EVP_CIPHER_CTX_init(ctx);
 	if (EVP_DecryptInit_ex(ctx, type, NULL, key, NULL) != 1) {
-		os_free(ctx);
+		EVP_CIPHER_CTX_free(ctx);
 		return NULL;
 	}
 	EVP_CIPHER_CTX_set_padding(ctx, 0);
@@ -292,11 +351,13 @@
 		wpa_printf(MSG_ERROR, "OpenSSL: Unexpected padding length %d "
 			   "in AES decrypt", len);
 	}
-	EVP_CIPHER_CTX_cleanup(c);
-	bin_clear_free(c, sizeof(*c));
+	EVP_CIPHER_CTX_free(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;
@@ -323,54 +384,62 @@
 	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;
+	EVP_CIPHER_CTX *ctx;
 	int clen, len;
 	u8 buf[16];
+	int res = -1;
 
-	EVP_CIPHER_CTX_init(&ctx);
-	if (EVP_EncryptInit_ex(&ctx, EVP_aes_128_cbc(), NULL, key, iv) != 1)
+	if (TEST_FAIL())
 		return -1;
-	EVP_CIPHER_CTX_set_padding(&ctx, 0);
 
+	ctx = EVP_CIPHER_CTX_new();
+	if (!ctx)
+		return -1;
 	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);
+	if (EVP_EncryptInit_ex(ctx, EVP_aes_128_cbc(), NULL, key, iv) == 1 &&
+	    EVP_CIPHER_CTX_set_padding(ctx, 0) == 1 &&
+	    EVP_EncryptUpdate(ctx, data, &clen, data, data_len) == 1 &&
+	    clen == (int) data_len &&
+	    EVP_EncryptFinal_ex(ctx, buf, &len) == 1 && len == 0)
+		res = 0;
+	EVP_CIPHER_CTX_free(ctx);
 
-	return 0;
+	return res;
 }
 
 
 int aes_128_cbc_decrypt(const u8 *key, const u8 *iv, u8 *data, size_t data_len)
 {
-	EVP_CIPHER_CTX ctx;
+	EVP_CIPHER_CTX *ctx;
 	int plen, len;
 	u8 buf[16];
+	int res = -1;
 
-	EVP_CIPHER_CTX_init(&ctx);
-	if (EVP_DecryptInit_ex(&ctx, EVP_aes_128_cbc(), NULL, key, iv) != 1)
+	if (TEST_FAIL())
 		return -1;
-	EVP_CIPHER_CTX_set_padding(&ctx, 0);
 
+	ctx = EVP_CIPHER_CTX_new();
+	if (!ctx)
+		return -1;
 	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);
+	if (EVP_DecryptInit_ex(ctx, EVP_aes_128_cbc(), NULL, key, iv) == 1 &&
+	    EVP_CIPHER_CTX_set_padding(ctx, 0) == 1 &&
+	    EVP_DecryptUpdate(ctx, data, &plen, data, data_len) == 1 &&
+	    plen == (int) data_len &&
+	    EVP_DecryptFinal_ex(ctx, buf, &len) == 1 && len == 0)
+		res = 0;
+	EVP_CIPHER_CTX_free(ctx);
 
-	return 0;
+	return res;
+
 }
 
 
@@ -413,8 +482,8 @@
 
 
 struct crypto_cipher {
-	EVP_CIPHER_CTX enc;
-	EVP_CIPHER_CTX dec;
+	EVP_CIPHER_CTX *enc;
+	EVP_CIPHER_CTX *dec;
 };
 
 
@@ -430,11 +499,13 @@
 		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) {
@@ -473,23 +544,25 @@
 		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);
+	if (!(ctx->enc = EVP_CIPHER_CTX_new()) ||
+	    !EVP_CIPHER_CTX_set_padding(ctx->enc, 0) ||
+	    !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)) {
+		if (ctx->enc)
+			EVP_CIPHER_CTX_free(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);
+	if (!(ctx->dec = EVP_CIPHER_CTX_new()) ||
+	    !EVP_CIPHER_CTX_set_padding(ctx->dec, 0) ||
+	    !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_free(ctx->enc);
+		if (ctx->dec)
+			EVP_CIPHER_CTX_free(ctx->dec);
 		os_free(ctx);
 		return NULL;
 	}
@@ -502,7 +575,7 @@
 			  u8 *crypt, size_t len)
 {
 	int outl;
-	if (!EVP_EncryptUpdate(&ctx->enc, crypt, &outl, plain, len))
+	if (!EVP_EncryptUpdate(ctx->enc, crypt, &outl, plain, len))
 		return -1;
 	return 0;
 }
@@ -513,7 +586,7 @@
 {
 	int outl;
 	outl = len;
-	if (!EVP_DecryptUpdate(&ctx->dec, plain, &outl, crypt, len))
+	if (!EVP_DecryptUpdate(ctx->dec, plain, &outl, crypt, len))
 		return -1;
 	return 0;
 }
@@ -521,8 +594,8 @@
 
 void crypto_cipher_deinit(struct crypto_cipher *ctx)
 {
-	EVP_CIPHER_CTX_cleanup(&ctx->enc);
-	EVP_CIPHER_CTX_cleanup(&ctx->dec);
+	EVP_CIPHER_CTX_free(ctx->enc);
+	EVP_CIPHER_CTX_free(ctx->dec);
 	os_free(ctx);
 }
 
@@ -658,7 +731,7 @@
 
 
 struct crypto_hash {
-	HMAC_CTX ctx;
+	HMAC_CTX *ctx;
 };
 
 
@@ -693,16 +766,17 @@
 	ctx = os_zalloc(sizeof(*ctx));
 	if (ctx == NULL)
 		return NULL;
-	HMAC_CTX_init(&ctx->ctx);
+	ctx->ctx = HMAC_CTX_new();
+	if (!ctx->ctx) {
+		os_free(ctx);
+		return NULL;
+	}
 
-#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) {
+	if (HMAC_Init_ex(ctx->ctx, key, key_len, md, NULL) != 1) {
+		HMAC_CTX_free(ctx->ctx);
 		bin_clear_free(ctx, sizeof(*ctx));
 		return NULL;
 	}
-#endif /* openssl < 0.9.9 */
 
 	return ctx;
 }
@@ -712,7 +786,7 @@
 {
 	if (ctx == NULL)
 		return;
-	HMAC_Update(&ctx->ctx, data, len);
+	HMAC_Update(ctx->ctx, data, len);
 }
 
 
@@ -725,18 +799,14 @@
 		return -2;
 
 	if (mac == NULL || len == NULL) {
+		HMAC_CTX_free(ctx->ctx);
 		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);
+	res = HMAC_Final(ctx->ctx, mac, &mdlen);
+	HMAC_CTX_free(ctx->ctx);
 	bin_clear_free(ctx, sizeof(*ctx));
 
 	if (res == 1) {
@@ -753,28 +823,26 @@
 			       const u8 *addr[], const size_t *len, u8 *mac,
 			       unsigned int mdlen)
 {
-	HMAC_CTX ctx;
+	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)
+	if (TEST_FAIL())
 		return -1;
-#endif /* openssl < 0.9.9 */
+
+	ctx = HMAC_CTX_new();
+	if (!ctx)
+		return -1;
+	res = HMAC_Init_ex(ctx, key, key_len, type, NULL);
+	if (res != 1)
+		goto done;
 
 	for (i = 0; i < num_elem; i++)
-		HMAC_Update(&ctx, addr[i], len[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);
+	res = HMAC_Final(ctx, mac, &mdlen);
+done:
+	HMAC_CTX_free(ctx);
 
 	return res == 1 ? 0 : -1;
 }
@@ -878,6 +946,9 @@
 	int ret = -1;
 	size_t outlen, i;
 
+	if (TEST_FAIL())
+		return -1;
+
 	ctx = CMAC_CTX_new();
 	if (ctx == NULL)
 		return -1;
@@ -927,13 +998,20 @@
 
 struct crypto_bignum * crypto_bignum_init(void)
 {
+	if (TEST_FAIL())
+		return NULL;
 	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);
+	BIGNUM *bn;
+
+	if (TEST_FAIL())
+		return NULL;
+
+	bn = BN_bin2bn(buf, len, NULL);
 	return (struct crypto_bignum *) bn;
 }
 
@@ -952,6 +1030,9 @@
 {
 	int num_bytes, offset;
 
+	if (TEST_FAIL())
+		return -1;
+
 	if (padlen > buflen)
 		return -1;
 
@@ -1005,6 +1086,9 @@
 	int res;
 	BN_CTX *bnctx;
 
+	if (TEST_FAIL())
+		return -1;
+
 	bnctx = BN_CTX_new();
 	if (bnctx == NULL)
 		return -1;
@@ -1023,6 +1107,8 @@
 	BIGNUM *res;
 	BN_CTX *bnctx;
 
+	if (TEST_FAIL())
+		return -1;
 	bnctx = BN_CTX_new();
 	if (bnctx == NULL)
 		return -1;
@@ -1038,6 +1124,8 @@
 		      const struct crypto_bignum *b,
 		      struct crypto_bignum *c)
 {
+	if (TEST_FAIL())
+		return -1;
 	return BN_sub((BIGNUM *) c, (const BIGNUM *) a, (const BIGNUM *) b) ?
 		0 : -1;
 }
@@ -1051,6 +1139,9 @@
 
 	BN_CTX *bnctx;
 
+	if (TEST_FAIL())
+		return -1;
+
 	bnctx = BN_CTX_new();
 	if (bnctx == NULL)
 		return -1;
@@ -1071,6 +1162,9 @@
 
 	BN_CTX *bnctx;
 
+	if (TEST_FAIL())
+		return -1;
+
 	bnctx = BN_CTX_new();
 	if (bnctx == NULL)
 		return -1;
@@ -1114,6 +1208,9 @@
 	BIGNUM *exp = NULL, *tmp = NULL;
 	int res = -2;
 
+	if (TEST_FAIL())
+		return -2;
+
 	bnctx = BN_CTX_new();
 	if (bnctx == NULL)
 		return -2;
@@ -1238,6 +1335,8 @@
 
 struct crypto_ec_point * crypto_ec_point_init(struct crypto_ec *e)
 {
+	if (TEST_FAIL())
+		return NULL;
 	if (e == NULL)
 		return NULL;
 	return (struct crypto_ec_point *) EC_POINT_new(e->group);
@@ -1284,6 +1383,9 @@
 	int ret = -1;
 	int len = BN_num_bytes(e->prime);
 
+	if (TEST_FAIL())
+		return -1;
+
 	x_bn = BN_new();
 	y_bn = BN_new();
 
@@ -1314,6 +1416,9 @@
 	EC_POINT *elem;
 	int len = BN_num_bytes(e->prime);
 
+	if (TEST_FAIL())
+		return NULL;
+
 	x = BN_bin2bn(val, len, NULL);
 	y = BN_bin2bn(val + len, len, NULL);
 	elem = EC_POINT_new(e->group);
@@ -1341,6 +1446,8 @@
 			const struct crypto_ec_point *b,
 			struct crypto_ec_point *c)
 {
+	if (TEST_FAIL())
+		return -1;
 	return EC_POINT_add(e->group, (EC_POINT *) c, (const EC_POINT *) a,
 			    (const EC_POINT *) b, e->bnctx) ? 0 : -1;
 }
@@ -1350,6 +1457,8 @@
 			const struct crypto_bignum *b,
 			struct crypto_ec_point *res)
 {
+	if (TEST_FAIL())
+		return -1;
 	return EC_POINT_mul(e->group, (EC_POINT *) res, NULL,
 			    (const EC_POINT *) p, (const BIGNUM *) b, e->bnctx)
 		? 0 : -1;
@@ -1358,6 +1467,8 @@
 
 int crypto_ec_point_invert(struct crypto_ec *e, struct crypto_ec_point *p)
 {
+	if (TEST_FAIL())
+		return -1;
 	return EC_POINT_invert(e->group, (EC_POINT *) p, e->bnctx) ? 0 : -1;
 }
 
@@ -1366,6 +1477,8 @@
 				  struct crypto_ec_point *p,
 				  const struct crypto_bignum *x, int y_bit)
 {
+	if (TEST_FAIL())
+		return -1;
 	if (!EC_POINT_set_compressed_coordinates_GFp(e->group, (EC_POINT *) p,
 						     (const BIGNUM *) x, y_bit,
 						     e->bnctx) ||
@@ -1381,6 +1494,9 @@
 {
 	BIGNUM *tmp, *tmp2, *y_sqr = NULL;
 
+	if (TEST_FAIL())
+		return NULL;
+
 	tmp = BN_new();
 	tmp2 = BN_new();
 
diff --git a/src/crypto/dh_group5.c b/src/crypto/dh_group5.c
index ccdbfc8..425c848 100644
--- a/src/crypto/dh_group5.c
+++ b/src/crypto/dh_group5.c
@@ -15,6 +15,7 @@
 
 void * dh5_init(struct wpabuf **priv, struct wpabuf **publ)
 {
+	wpabuf_free(*publ);
 	*publ = dh_init(dh_groups_get(5), priv);
 	if (*publ == NULL)
 		return NULL;
diff --git a/src/crypto/dh_groups.c b/src/crypto/dh_groups.c
index 3aeb2bb..7912361 100644
--- a/src/crypto/dh_groups.c
+++ b/src/crypto/dh_groups.c
@@ -1218,14 +1218,19 @@
 
 	pv_len = dh->prime_len;
 	pv = wpabuf_alloc(pv_len);
-	if (pv == NULL)
+	if (pv == NULL) {
+		wpabuf_clear_free(*priv);
+		*priv = 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");
+		wpabuf_clear_free(*priv);
+		*priv = NULL;
 		return NULL;
 	}
 	wpabuf_put(pv, pv_len);
diff --git a/src/crypto/fips_prf_openssl.c b/src/crypto/fips_prf_openssl.c
index fb03efc..9d094b8 100644
--- a/src/crypto/fips_prf_openssl.c
+++ b/src/crypto/fips_prf_openssl.c
@@ -17,6 +17,19 @@
 {
 	SHA_CTX context;
 	os_memset(&context, 0, sizeof(context));
+#if defined(OPENSSL_IS_BORINGSSL) && !defined(ANDROID)
+	context.h[0] = state[0];
+	context.h[1] = state[1];
+	context.h[2] = state[2];
+	context.h[3] = state[3];
+	context.h[4] = state[4];
+	SHA1_Transform(&context, data);
+	state[0] = context.h[0];
+	state[1] = context.h[1];
+	state[2] = context.h[2];
+	state[3] = context.h[3];
+	state[4] = context.h[4];
+#else
 	context.h0 = state[0];
 	context.h1 = state[1];
 	context.h2 = state[2];
@@ -28,6 +41,7 @@
 	state[2] = context.h2;
 	state[3] = context.h3;
 	state[4] = context.h4;
+#endif
 }
 
 
diff --git a/src/crypto/md4-internal.c b/src/crypto/md4-internal.c
index cd5e6ca..d9c737a 100644
--- a/src/crypto/md4-internal.c
+++ b/src/crypto/md4-internal.c
@@ -31,6 +31,9 @@
 	MD4_CTX ctx;
 	size_t i;
 
+	if (TEST_FAIL())
+		return -1;
+
 	MD4Init(&ctx);
 	for (i = 0; i < num_elem; i++)
 		MD4Update(&ctx, addr[i], len[i]);
diff --git a/src/crypto/md5-internal.c b/src/crypto/md5-internal.c
index f0a2a5d..944698a 100644
--- a/src/crypto/md5-internal.c
+++ b/src/crypto/md5-internal.c
@@ -33,6 +33,9 @@
 	MD5_CTX ctx;
 	size_t i;
 
+	if (TEST_FAIL())
+		return -1;
+
 	MD5Init(&ctx);
 	for (i = 0; i < num_elem; i++)
 		MD5Update(&ctx, addr[i], len[i]);
diff --git a/src/crypto/ms_funcs.c b/src/crypto/ms_funcs.c
index 5f57656..d0d6a96 100644
--- a/src/crypto/ms_funcs.c
+++ b/src/crypto/ms_funcs.c
@@ -48,7 +48,7 @@
 				WPA_PUT_LE16(ucs2_buffer + j,
 					     ((c & 0x1F) << 6) | (c2 & 0x3F));
 				j += 2;
-			} else if (i == utf8_string_len ||
+			} else if (i == utf8_string_len - 1 ||
 				   j >= ucs2_buffer_size - 1) {
 				/* incomplete surrogate */
 				return -1;
@@ -174,9 +174,8 @@
 	u8 password_hash[16];
 
 	if (challenge_hash(peer_challenge, auth_challenge, username,
-			   username_len, challenge))
-		return -1;
-	if (nt_password_hash(password, password_len, password_hash))
+			   username_len, challenge) ||
+	    nt_password_hash(password, password_len, password_hash))
 		return -1;
 	challenge_response(challenge, password_hash, response);
 	return 0;
@@ -256,12 +255,9 @@
 	addr2[1] = challenge;
 	addr2[2] = magic2;
 
-	if (hash_nt_password_hash(password_hash, password_hash_hash))
-		return -1;
-	if (sha1_vector(3, addr1, len1, response))
-		return -1;
-
-	if (challenge_hash(peer_challenge, auth_challenge, username,
+	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);
@@ -416,6 +412,8 @@
 }
 
 
+#ifndef CONFIG_NO_RC4
+
 #define PWBLOCK_LEN 516
 
 /**
@@ -435,10 +433,8 @@
 
 	os_memset(pw_block, 0, PWBLOCK_LEN);
 
-	if (utf8_to_ucs2(password, password_len, pw_block, 512, &ucs2_len) < 0)
-		return -1;
-
-	if (ucs2_len > 256)
+	if (utf8_to_ucs2(password, password_len, pw_block, 512, &ucs2_len) < 0
+	    || ucs2_len > 256)
 		return -1;
 
 	offset = (256 - ucs2_len) * 2;
@@ -483,6 +479,8 @@
 	return 0;
 }
 
+#endif /* CONFIG_NO_RC4 */
+
 
 /**
  * nt_password_hash_encrypted_with_block - NtPasswordHashEncryptedWithBlock() - RFC 2759, Sect 8.13
diff --git a/src/crypto/random.c b/src/crypto/random.c
index bc758aa..3a86a93 100644
--- a/src/crypto/random.c
+++ b/src/crypto/random.c
@@ -181,6 +181,7 @@
 
 #ifdef CONFIG_FIPS
 	/* Mix in additional entropy from the crypto module */
+	bytes = buf;
 	left = len;
 	while (left) {
 		size_t siz, i;
diff --git a/src/crypto/sha1-internal.c b/src/crypto/sha1-internal.c
index 24bc3ff..ffcba66 100644
--- a/src/crypto/sha1-internal.c
+++ b/src/crypto/sha1-internal.c
@@ -33,6 +33,9 @@
 	SHA1_CTX ctx;
 	size_t i;
 
+	if (TEST_FAIL())
+		return -1;
+
 	SHA1Init(&ctx);
 	for (i = 0; i < num_elem; i++)
 		SHA1Update(&ctx, addr[i], len[i]);
@@ -294,7 +297,6 @@
 			 255);
 	}
 	/* Wipe variables */
-	i = 0;
 	os_memset(context->buffer, 0, 64);
 	os_memset(context->state, 0, 20);
 	os_memset(context->count, 0, 8);
diff --git a/src/crypto/sha256-internal.c b/src/crypto/sha256-internal.c
index 35299b0..86a548e 100644
--- a/src/crypto/sha256-internal.c
+++ b/src/crypto/sha256-internal.c
@@ -28,6 +28,9 @@
 	struct sha256_state ctx;
 	size_t i;
 
+	if (TEST_FAIL())
+		return -1;
+
 	sha256_init(&ctx);
 	for (i = 0; i < num_elem; i++)
 		if (sha256_process(&ctx, addr[i], len[i]))
diff --git a/src/crypto/sha256-prf.c b/src/crypto/sha256-prf.c
index 79791c0..722cad6 100644
--- a/src/crypto/sha256-prf.c
+++ b/src/crypto/sha256-prf.c
@@ -1,6 +1,6 @@
 /*
  * SHA256-based PRF (IEEE 802.11r)
- * Copyright (c) 2003-2013, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2003-2016, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -22,14 +22,16 @@
  * @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 on failure
  *
  * 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,
+int 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);
+	return sha256_prf_bits(key, key_len, label, data, data_len, buf,
+			       buf_len * 8);
 }
 
 
@@ -42,15 +44,16 @@
  * @data_len: Length of the data
  * @buf: Buffer for the generated pseudo-random key
  * @buf_len: Number of bits 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. 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)
+int sha256_prf_bits(const u8 *key, size_t key_len, const char *label,
+		    const u8 *data, size_t data_len, u8 *buf,
+		    size_t buf_len_bits)
 {
 	u16 counter = 1;
 	size_t pos, plen;
@@ -75,11 +78,14 @@
 		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]);
+			if (hmac_sha256_vector(key, key_len, 4, addr, len,
+					       &buf[pos]) < 0)
+				return -1;
 			pos += SHA256_MAC_LEN;
 		} else {
-			hmac_sha256_vector(key, key_len, 4, addr, len, hash);
+			if (hmac_sha256_vector(key, key_len, 4, addr, len,
+					       hash) < 0)
+				return -1;
 			os_memcpy(&buf[pos], hash, plen);
 			pos += plen;
 			break;
@@ -97,4 +103,6 @@
 	}
 
 	os_memset(hash, 0, sizeof(hash));
+
+	return 0;
 }
diff --git a/src/crypto/sha256.h b/src/crypto/sha256.h
index b15f511..5219022 100644
--- a/src/crypto/sha256.h
+++ b/src/crypto/sha256.h
@@ -1,6 +1,6 @@
 /*
  * SHA256 hash implementation and interface functions
- * Copyright (c) 2003-2014, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2003-2016, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -15,11 +15,11 @@
 		       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);
+int 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);
+int sha256_prf_bits(const u8 *key, size_t key_len, const char *label,
+		    const u8 *data, size_t data_len, u8 *buf,
+		    size_t buf_len_bits);
 void tls_prf_sha256(const u8 *secret, size_t secret_len,
 		    const char *label, const u8 *seed, size_t seed_len,
 		    u8 *out, size_t outlen);
diff --git a/src/crypto/sha384-internal.c b/src/crypto/sha384-internal.c
new file mode 100644
index 0000000..646f729
--- /dev/null
+++ b/src/crypto/sha384-internal.c
@@ -0,0 +1,92 @@
+/*
+ * SHA-384 hash implementation and interface functions
+ * Copyright (c) 2015, Pali Rohár <pali.rohar@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 "sha384_i.h"
+#include "crypto.h"
+
+
+/**
+ * sha384_vector - SHA384 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 sha384_vector(size_t num_elem, const u8 *addr[], const size_t *len,
+		  u8 *mac)
+{
+	struct sha384_state ctx;
+	size_t i;
+
+	sha384_init(&ctx);
+	for (i = 0; i < num_elem; i++)
+		if (sha384_process(&ctx, addr[i], len[i]))
+			return -1;
+	if (sha384_done(&ctx, mac))
+		return -1;
+	return 0;
+}
+
+
+/* ===== start - public domain SHA384 implementation ===== */
+
+/* This is based on SHA384 implementation in LibTomCrypt that was released into
+ * public domain by Tom St Denis. */
+
+#define CONST64(n) n ## ULL
+
+/**
+   Initialize the hash state
+   @param md   The hash state you wish to initialize
+   @return CRYPT_OK if successful
+*/
+void sha384_init(struct sha384_state *md)
+{
+	md->curlen = 0;
+	md->length = 0;
+	md->state[0] = CONST64(0xcbbb9d5dc1059ed8);
+	md->state[1] = CONST64(0x629a292a367cd507);
+	md->state[2] = CONST64(0x9159015a3070dd17);
+	md->state[3] = CONST64(0x152fecd8f70e5939);
+	md->state[4] = CONST64(0x67332667ffc00b31);
+	md->state[5] = CONST64(0x8eb44a8768581511);
+	md->state[6] = CONST64(0xdb0c2e0d64f98fa7);
+	md->state[7] = CONST64(0x47b5481dbefa4fa4);
+}
+
+int sha384_process(struct sha384_state *md, const unsigned char *in,
+		   unsigned long inlen)
+{
+	return sha512_process(md, in, inlen);
+}
+
+/**
+   Terminate the hash to get the digest
+   @param md  The hash state
+   @param out [out] The destination of the hash (48 bytes)
+   @return CRYPT_OK if successful
+*/
+int sha384_done(struct sha384_state *md, unsigned char *out)
+{
+	unsigned char buf[64];
+
+	if (md->curlen >= sizeof(md->buf))
+		return -1;
+
+	if (sha512_done(md, buf) != 0)
+		return -1;
+
+	os_memcpy(out, buf, 48);
+	return 0;
+}
+
+/* ===== end - public domain SHA384 implementation ===== */
diff --git a/src/crypto/sha384-prf.c b/src/crypto/sha384-prf.c
new file mode 100644
index 0000000..653920b
--- /dev/null
+++ b/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/src/crypto/sha384.h b/src/crypto/sha384.h
index e6a1fe4..3deafa5 100644
--- a/src/crypto/sha384.h
+++ b/src/crypto/sha384.h
@@ -15,5 +15,10 @@
 		       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/src/crypto/sha384_i.h b/src/crypto/sha384_i.h
new file mode 100644
index 0000000..a00253f
--- /dev/null
+++ b/src/crypto/sha384_i.h
@@ -0,0 +1,23 @@
+/*
+ * SHA-384 internal definitions
+ * Copyright (c) 2015, Pali Rohár <pali.rohar@gmail.com>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef SHA384_I_H
+#define SHA384_I_H
+
+#include "sha512_i.h"
+
+#define SHA384_BLOCK_SIZE SHA512_BLOCK_SIZE
+
+#define sha384_state sha512_state
+
+void sha384_init(struct sha384_state *md);
+int sha384_process(struct sha384_state *md, const unsigned char *in,
+		   unsigned long inlen);
+int sha384_done(struct sha384_state *md, unsigned char *out);
+
+#endif /* SHA384_I_H */
diff --git a/src/crypto/sha512-internal.c b/src/crypto/sha512-internal.c
new file mode 100644
index 0000000..66ef331
--- /dev/null
+++ b/src/crypto/sha512-internal.c
@@ -0,0 +1,264 @@
+/*
+ * SHA-512 hash implementation and interface functions
+ * Copyright (c) 2015, Pali Rohár <pali.rohar@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 "sha512_i.h"
+#include "crypto.h"
+
+
+/**
+ * sha512_vector - SHA512 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 sha512_vector(size_t num_elem, const u8 *addr[], const size_t *len,
+		  u8 *mac)
+{
+	struct sha512_state ctx;
+	size_t i;
+
+	sha512_init(&ctx);
+	for (i = 0; i < num_elem; i++)
+		if (sha512_process(&ctx, addr[i], len[i]))
+			return -1;
+	if (sha512_done(&ctx, mac))
+		return -1;
+	return 0;
+}
+
+
+/* ===== start - public domain SHA512 implementation ===== */
+
+/* This is based on SHA512 implementation in LibTomCrypt that was released into
+ * public domain by Tom St Denis. */
+
+#define CONST64(n) n ## ULL
+
+/* the K array */
+static const u64 K[80] = {
+	CONST64(0x428a2f98d728ae22), CONST64(0x7137449123ef65cd),
+	CONST64(0xb5c0fbcfec4d3b2f), CONST64(0xe9b5dba58189dbbc),
+	CONST64(0x3956c25bf348b538), CONST64(0x59f111f1b605d019),
+	CONST64(0x923f82a4af194f9b), CONST64(0xab1c5ed5da6d8118),
+	CONST64(0xd807aa98a3030242), CONST64(0x12835b0145706fbe),
+	CONST64(0x243185be4ee4b28c), CONST64(0x550c7dc3d5ffb4e2),
+	CONST64(0x72be5d74f27b896f), CONST64(0x80deb1fe3b1696b1),
+	CONST64(0x9bdc06a725c71235), CONST64(0xc19bf174cf692694),
+	CONST64(0xe49b69c19ef14ad2), CONST64(0xefbe4786384f25e3),
+	CONST64(0x0fc19dc68b8cd5b5), CONST64(0x240ca1cc77ac9c65),
+	CONST64(0x2de92c6f592b0275), CONST64(0x4a7484aa6ea6e483),
+	CONST64(0x5cb0a9dcbd41fbd4), CONST64(0x76f988da831153b5),
+	CONST64(0x983e5152ee66dfab), CONST64(0xa831c66d2db43210),
+	CONST64(0xb00327c898fb213f), CONST64(0xbf597fc7beef0ee4),
+	CONST64(0xc6e00bf33da88fc2), CONST64(0xd5a79147930aa725),
+	CONST64(0x06ca6351e003826f), CONST64(0x142929670a0e6e70),
+	CONST64(0x27b70a8546d22ffc), CONST64(0x2e1b21385c26c926),
+	CONST64(0x4d2c6dfc5ac42aed), CONST64(0x53380d139d95b3df),
+	CONST64(0x650a73548baf63de), CONST64(0x766a0abb3c77b2a8),
+	CONST64(0x81c2c92e47edaee6), CONST64(0x92722c851482353b),
+	CONST64(0xa2bfe8a14cf10364), CONST64(0xa81a664bbc423001),
+	CONST64(0xc24b8b70d0f89791), CONST64(0xc76c51a30654be30),
+	CONST64(0xd192e819d6ef5218), CONST64(0xd69906245565a910),
+	CONST64(0xf40e35855771202a), CONST64(0x106aa07032bbd1b8),
+	CONST64(0x19a4c116b8d2d0c8), CONST64(0x1e376c085141ab53),
+	CONST64(0x2748774cdf8eeb99), CONST64(0x34b0bcb5e19b48a8),
+	CONST64(0x391c0cb3c5c95a63), CONST64(0x4ed8aa4ae3418acb),
+	CONST64(0x5b9cca4f7763e373), CONST64(0x682e6ff3d6b2b8a3),
+	CONST64(0x748f82ee5defb2fc), CONST64(0x78a5636f43172f60),
+	CONST64(0x84c87814a1f0ab72), CONST64(0x8cc702081a6439ec),
+	CONST64(0x90befffa23631e28), CONST64(0xa4506cebde82bde9),
+	CONST64(0xbef9a3f7b2c67915), CONST64(0xc67178f2e372532b),
+	CONST64(0xca273eceea26619c), CONST64(0xd186b8c721c0c207),
+	CONST64(0xeada7dd6cde0eb1e), CONST64(0xf57d4f7fee6ed178),
+	CONST64(0x06f067aa72176fba), CONST64(0x0a637dc5a2c898a6),
+	CONST64(0x113f9804bef90dae), CONST64(0x1b710b35131c471b),
+	CONST64(0x28db77f523047d84), CONST64(0x32caab7b40c72493),
+	CONST64(0x3c9ebe0a15c9bebc), CONST64(0x431d67c49c100d4c),
+	CONST64(0x4cc5d4becb3e42b6), CONST64(0x597f299cfc657e2a),
+	CONST64(0x5fcb6fab3ad6faec), CONST64(0x6c44198c4a475817)
+};
+
+/* Various logical functions */
+#define Ch(x,y,z)       (z ^ (x & (y ^ z)))
+#define Maj(x,y,z)      (((x | y) & z) | (x & y))
+#define S(x, n)         ROR64c(x, n)
+#define R(x, n)         (((x) & CONST64(0xFFFFFFFFFFFFFFFF)) >> ((u64) n))
+#define Sigma0(x)       (S(x, 28) ^ S(x, 34) ^ S(x, 39))
+#define Sigma1(x)       (S(x, 14) ^ S(x, 18) ^ S(x, 41))
+#define Gamma0(x)       (S(x, 1) ^ S(x, 8) ^ R(x, 7))
+#define Gamma1(x)       (S(x, 19) ^ S(x, 61) ^ R(x, 6))
+#ifndef MIN
+#define MIN(x, y) (((x) < (y)) ? (x) : (y))
+#endif
+
+#define ROR64c(x, y) \
+    ( ((((x) & CONST64(0xFFFFFFFFFFFFFFFF)) >> ((u64) (y) & CONST64(63))) | \
+      ((x) << ((u64) (64 - ((y) & CONST64(63)))))) & \
+      CONST64(0xFFFFFFFFFFFFFFFF))
+
+/* compress 1024-bits */
+static int sha512_compress(struct sha512_state *md, unsigned char *buf)
+{
+	u64 S[8], W[80], t0, t1;
+	int i;
+
+	/* copy state into S */
+	for (i = 0; i < 8; i++) {
+		S[i] = md->state[i];
+	}
+
+	/* copy the state into 1024-bits into W[0..15] */
+	for (i = 0; i < 16; i++)
+		W[i] = WPA_GET_BE64(buf + (8 * i));
+
+	/* fill W[16..79] */
+	for (i = 16; i < 80; i++) {
+		W[i] = Gamma1(W[i - 2]) + W[i - 7] + Gamma0(W[i - 15]) +
+			W[i - 16];
+	}
+
+	/* Compress */
+	for (i = 0; i < 80; i++) {
+		t0 = S[7] + Sigma1(S[4]) + Ch(S[4], S[5], S[6]) + K[i] + W[i];
+		t1 = Sigma0(S[0]) + Maj(S[0], S[1], S[2]);
+		S[7] = S[6];
+		S[6] = S[5];
+		S[5] = S[4];
+		S[4] = S[3] + t0;
+		S[3] = S[2];
+		S[2] = S[1];
+		S[1] = S[0];
+		S[0] = t0 + t1;
+	}
+
+	/* feedback */
+	for (i = 0; i < 8; i++) {
+		md->state[i] = md->state[i] + S[i];
+	}
+
+	return 0;
+}
+
+
+/**
+   Initialize the hash state
+   @param md   The hash state you wish to initialize
+   @return CRYPT_OK if successful
+*/
+void sha512_init(struct sha512_state *md)
+{
+	md->curlen = 0;
+	md->length = 0;
+	md->state[0] = CONST64(0x6a09e667f3bcc908);
+	md->state[1] = CONST64(0xbb67ae8584caa73b);
+	md->state[2] = CONST64(0x3c6ef372fe94f82b);
+	md->state[3] = CONST64(0xa54ff53a5f1d36f1);
+	md->state[4] = CONST64(0x510e527fade682d1);
+	md->state[5] = CONST64(0x9b05688c2b3e6c1f);
+	md->state[6] = CONST64(0x1f83d9abfb41bd6b);
+	md->state[7] = CONST64(0x5be0cd19137e2179);
+}
+
+
+/**
+   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 sha512_process(struct sha512_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 >= SHA512_BLOCK_SIZE) {
+			if (sha512_compress(md, (unsigned char *) in) < 0)
+				return -1;
+			md->length += SHA512_BLOCK_SIZE * 8;
+			in += SHA512_BLOCK_SIZE;
+			inlen -= SHA512_BLOCK_SIZE;
+		} else {
+			n = MIN(inlen, (SHA512_BLOCK_SIZE - md->curlen));
+			os_memcpy(md->buf + md->curlen, in, n);
+			md->curlen += n;
+			in += n;
+			inlen -= n;
+			if (md->curlen == SHA512_BLOCK_SIZE) {
+				if (sha512_compress(md, md->buf) < 0)
+					return -1;
+				md->length += 8 * SHA512_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 (64 bytes)
+   @return CRYPT_OK if successful
+*/
+int sha512_done(struct sha512_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 * CONST64(8);
+
+	/* append the '1' bit */
+	md->buf[md->curlen++] = (unsigned char) 0x80;
+
+	/* if the length is currently above 112 bytes we append zeros
+	 * then compress.  Then we can fall back to padding zeros and length
+	 * encoding like normal.
+	 */
+	if (md->curlen > 112) {
+		while (md->curlen < 128) {
+			md->buf[md->curlen++] = (unsigned char) 0;
+		}
+		sha512_compress(md, md->buf);
+		md->curlen = 0;
+	}
+
+	/* pad upto 120 bytes of zeroes
+	 * note: that from 112 to 120 is the 64 MSB of the length.  We assume
+	 * that you won't hash > 2^64 bits of data... :-)
+	 */
+	while (md->curlen < 120) {
+		md->buf[md->curlen++] = (unsigned char) 0;
+	}
+
+	/* store length */
+	WPA_PUT_BE64(md->buf + 120, md->length);
+	sha512_compress(md, md->buf);
+
+	/* copy output */
+	for (i = 0; i < 8; i++)
+		WPA_PUT_BE64(out + (8 * i), md->state[i]);
+
+	return 0;
+}
+
+/* ===== end - public domain SHA512 implementation ===== */
diff --git a/src/crypto/sha512_i.h b/src/crypto/sha512_i.h
new file mode 100644
index 0000000..1089589
--- /dev/null
+++ b/src/crypto/sha512_i.h
@@ -0,0 +1,25 @@
+/*
+ * SHA-512 internal definitions
+ * Copyright (c) 2015, Pali Rohár <pali.rohar@gmail.com>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef SHA512_I_H
+#define SHA512_I_H
+
+#define SHA512_BLOCK_SIZE 128
+
+struct sha512_state {
+	u64 length, state[8];
+	u32 curlen;
+	u8 buf[SHA512_BLOCK_SIZE];
+};
+
+void sha512_init(struct sha512_state *md);
+int sha512_process(struct sha512_state *md, const unsigned char *in,
+		   unsigned long inlen);
+int sha512_done(struct sha512_state *md, unsigned char *out);
+
+#endif /* SHA512_I_H */
diff --git a/src/crypto/tls.h b/src/crypto/tls.h
index d13657e..15a3bcf 100644
--- a/src/crypto/tls.h
+++ b/src/crypto/tls.h
@@ -11,7 +11,7 @@
 
 struct tls_connection;
 
-struct tls_keys {
+struct tls_random {
 	const u8 *client_random;
 	size_t client_random_len;
 	const u8 *server_random;
@@ -79,6 +79,7 @@
 	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);
@@ -93,6 +94,9 @@
 #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)
+#define TLS_CONN_EXT_CERT_CHECK BIT(9)
+#define TLS_CONN_REQUIRE_OCSP_ALL BIT(10)
 
 /**
  * struct tls_connection_params - Parameters for TLS connection
@@ -137,6 +141,9 @@
  * @flags: Parameter options (TLS_CONN_*)
  * @ocsp_stapling_response: DER encoded file with cached OCSP stapling response
  *	or %NULL if OCSP is not enabled
+ * @ocsp_stapling_response_multi: DER encoded file with cached OCSP stapling
+ *	response list (OCSPResponseList for ocsp_multi in RFC 6961) or %NULL if
+ *	ocsp_multi is not enabled
  *
  * TLS connection parameters to be configured with tls_connection_set_params()
  * and tls_global_set_params().
@@ -177,6 +184,7 @@
 
 	unsigned int flags;
 	const char *ocsp_stapling_response;
+	const char *ocsp_stapling_response_multi;
 };
 
 
@@ -304,22 +312,28 @@
  * @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);
+					   int verify_peer,
+					   unsigned int flags,
+					   const u8 *session_ctx,
+					   size_t session_ctx_len);
 
 /**
- * tls_connection_get_keys - Get random data from TLS connection
+ * 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()
- * @keys: Structure of client/server random data (filled on success)
+ * @data: Structure of client/server random data (filled on success)
  * Returns: 0 on success, -1 on failure
  */
-int __must_check tls_connection_get_keys(void *tls_ctx,
+int __must_check tls_connection_get_random(void *tls_ctx,
 					 struct tls_connection *conn,
-					 struct tls_keys *keys);
+					 struct tls_random *data);
 
 /**
  * tls_connection_prf - Use TLS-PRF to derive keying material
@@ -333,14 +347,11 @@
  * @out_len: Length of the output buffer
  * Returns: 0 on success, -1 on failure
  *
- * This function is optional to implement if tls_connection_get_keys() provides
- * access to master secret and server/client random values. If these values are
- * not exported from the TLS library, tls_connection_prf() is required so that
- * further keying material can be derived from the master secret. If not
- * implemented, the function will still need to be defined, but it can just
- * return -1. 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).
+ * 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,
@@ -450,7 +461,9 @@
 	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_CIPHER_ANON_DH_AES128_SHA /* 0x0034 */,
+	TLS_CIPHER_RSA_DHE_AES256_SHA /* 0x0039 */,
+	TLS_CIPHER_AES256_SHA /* 0x0035 */,
 };
 
 /**
@@ -466,6 +479,19 @@
 						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()
@@ -532,13 +558,6 @@
 int tls_connection_get_write_alerts(void *tls_ctx,
 				    struct tls_connection *conn);
 
-/**
- * tls_capabilities - Get supported TLS capabilities
- * @tls_ctx: TLS context data from tls_init()
- * Returns: Bit field of supported TLS capabilities (TLS_CAPABILITY_*)
- */
-unsigned int tls_capabilities(void *tls_ctx);
-
 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);
@@ -564,4 +583,14 @@
 
 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/src/crypto/tls_gnutls.c b/src/crypto/tls_gnutls.c
index c7f6464..c4cd3c1 100644
--- a/src/crypto/tls_gnutls.c
+++ b/src/crypto/tls_gnutls.c
@@ -37,6 +37,8 @@
 			 union tls_event_data *data);
 	void *cb_ctx;
 	int cert_in_cb;
+
+	char *ocsp_stapling_response;
 };
 
 struct tls_connection {
@@ -133,6 +135,7 @@
 		if (global->params_set)
 			gnutls_certificate_free_credentials(global->xcred);
 		os_free(global->session_data);
+		os_free(global->ocsp_stapling_response);
 		os_free(global);
 	}
 
@@ -347,6 +350,18 @@
 	if (conn == NULL || params == NULL)
 		return -1;
 
+	if (params->flags & TLS_CONN_REQUIRE_OCSP_ALL) {
+		wpa_printf(MSG_INFO,
+			   "GnuTLS: ocsp=3 not supported");
+		return -1;
+	}
+
+	if (params->flags & TLS_CONN_EXT_CERT_CHECK) {
+		wpa_printf(MSG_INFO,
+			   "GnuTLS: tls_ext_cert_check=1 not supported");
+		return -1;
+	}
+
 	if (params->subject_match) {
 		wpa_printf(MSG_INFO, "GnuTLS: subject_match not supported");
 		return -1;
@@ -596,6 +611,44 @@
 }
 
 
+#if GNUTLS_VERSION_NUMBER >= 0x030103
+static int server_ocsp_status_req(gnutls_session_t session, void *ptr,
+				  gnutls_datum_t *resp)
+{
+	struct tls_global *global = ptr;
+	char *cached;
+	size_t len;
+
+	if (!global->ocsp_stapling_response) {
+		wpa_printf(MSG_DEBUG, "GnuTLS: OCSP status callback - no response configured");
+		return GNUTLS_E_NO_CERTIFICATE_STATUS;
+	}
+
+	cached = os_readfile(global->ocsp_stapling_response, &len);
+	if (!cached) {
+		wpa_printf(MSG_DEBUG,
+			   "GnuTLS: OCSP status callback - could not read response file (%s)",
+			   global->ocsp_stapling_response);
+		return GNUTLS_E_NO_CERTIFICATE_STATUS;
+	}
+
+	wpa_printf(MSG_DEBUG,
+		   "GnuTLS: OCSP status callback - send cached response");
+	resp->data = gnutls_malloc(len);
+	if (!resp->data) {
+		os_free(resp);
+		return GNUTLS_E_MEMORY_ERROR;
+	}
+
+	os_memcpy(resp->data, cached, len);
+	resp->size = len;
+	os_free(cached);
+
+	return GNUTLS_E_SUCCESS;
+}
+#endif /* 3.1.3 */
+
+
 int tls_global_set_params(void *tls_ctx,
 			  const struct tls_connection_params *params)
 {
@@ -690,6 +743,17 @@
 		}
 	}
 
+#if GNUTLS_VERSION_NUMBER >= 0x030103
+	os_free(global->ocsp_stapling_response);
+	if (params->ocsp_stapling_response)
+		global->ocsp_stapling_response =
+			os_strdup(params->ocsp_stapling_response);
+	else
+		global->ocsp_stapling_response = NULL;
+	gnutls_certificate_set_ocsp_status_request_function(
+		global->xcred, server_ocsp_status_req, global);
+#endif /* 3.1.3 */
+
 	global->params_set = 1;
 
 	return 0;
@@ -708,7 +772,8 @@
 
 
 int tls_connection_set_verify(void *ssl_ctx, struct tls_connection *conn,
-			      int verify_peer)
+			      int verify_peer, unsigned int flags,
+			      const u8 *session_ctx, size_t session_ctx_len)
 {
 	if (conn == NULL || conn->session == NULL)
 		return -1;
@@ -722,8 +787,8 @@
 }
 
 
-int tls_connection_get_keys(void *ssl_ctx, struct tls_connection *conn,
-			    struct tls_keys *keys)
+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;
@@ -1426,6 +1491,14 @@
 }
 
 
+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)
 {
@@ -1476,12 +1549,6 @@
 }
 
 
-unsigned int tls_capabilities(void *tls_ctx)
-{
-	return 0;
-}
-
-
 int tls_connection_set_session_ticket_cb(void *tls_ctx,
 					 struct tls_connection *conn,
 					 tls_session_ticket_cb cb, void *ctx)
@@ -1495,3 +1562,26 @@
 	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/src/crypto/tls_internal.c b/src/crypto/tls_internal.c
index afd4695..01a7c97 100644
--- a/src/crypto/tls_internal.c
+++ b/src/crypto/tls_internal.c
@@ -23,6 +23,11 @@
 	int server;
 	struct tlsv1_credentials *server_cred;
 	int check_crl;
+
+	void (*event_cb)(void *ctx, enum tls_event ev,
+			 union tls_event_data *data);
+	void *cb_ctx;
+	int cert_in_cb;
 };
 
 struct tls_connection {
@@ -51,6 +56,11 @@
 	global = os_zalloc(sizeof(*global));
 	if (global == NULL)
 		return NULL;
+	if (conf) {
+		global->event_cb = conf->event_cb;
+		global->cb_ctx = conf->cb_ctx;
+		global->cert_in_cb = conf->cert_in_cb;
+	}
 
 	return global;
 }
@@ -64,10 +74,12 @@
 		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 */
 	}
+#ifdef CONFIG_TLS_INTERNAL_SERVER
+	tlsv1_cred_free(global->server_cred);
+#endif /* CONFIG_TLS_INTERNAL_SERVER */
 	os_free(global);
 }
 
@@ -95,6 +107,8 @@
 			os_free(conn);
 			return NULL;
 		}
+		tlsv1_client_set_cb(conn->client, global->event_cb,
+				    global->cb_ctx, global->cert_in_cb);
 	}
 #endif /* CONFIG_TLS_INTERNAL_CLIENT */
 #ifdef CONFIG_TLS_INTERNAL_SERVER
@@ -186,6 +200,12 @@
 	if (conn->client == NULL)
 		return -1;
 
+	if (params->flags & TLS_CONN_EXT_CERT_CHECK) {
+		wpa_printf(MSG_INFO,
+			   "TLS: tls_ext_cert_check=1 not supported");
+		return -1;
+	}
+
 	cred = tlsv1_cred_alloc();
 	if (cred == NULL)
 		return -1;
@@ -259,8 +279,7 @@
 		return -1;
 	}
 
-	tlsv1_client_set_time_checks(
-		conn->client, !(params->flags & TLS_CONN_DISABLE_TIME_CHECKS));
+	tlsv1_client_set_flags(conn->client, params->flags);
 
 	return 0;
 #else /* CONFIG_TLS_INTERNAL_CLIENT */
@@ -312,6 +331,13 @@
 		return -1;
 	}
 
+	if (params->ocsp_stapling_response)
+		cred->ocsp_stapling_response =
+			os_strdup(params->ocsp_stapling_response);
+	if (params->ocsp_stapling_response_multi)
+		cred->ocsp_stapling_response_multi =
+			os_strdup(params->ocsp_stapling_response_multi);
+
 	return 0;
 #else /* CONFIG_TLS_INTERNAL_SERVER */
 	return -1;
@@ -328,7 +354,8 @@
 
 
 int tls_connection_set_verify(void *tls_ctx, struct tls_connection *conn,
-			      int verify_peer)
+			      int verify_peer, unsigned int flags,
+			      const u8 *session_ctx, size_t session_ctx_len)
 {
 #ifdef CONFIG_TLS_INTERNAL_SERVER
 	if (conn->server)
@@ -338,16 +365,16 @@
 }
 
 
-int tls_connection_get_keys(void *tls_ctx, struct tls_connection *conn,
-			    struct tls_keys *keys)
+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_keys(conn->client, keys);
+		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_keys(conn->server, keys);
+		return tlsv1_server_get_random(conn->server, data);
 #endif /* CONFIG_TLS_INTERNAL_SERVER */
 	return -1;
 }
@@ -389,14 +416,14 @@
 	if (conn->client) {
 		ret = tlsv1_client_prf(conn->client, label,
 				       server_random_first,
-				       _out, out_len);
+				       _out, skip + 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);
+				       _out, skip + out_len);
 	}
 #endif /* CONFIG_TLS_INTERNAL_SERVER */
 	if (ret == 0 && skip_keyblock)
@@ -617,6 +644,19 @@
 }
 
 
+int tls_get_version(void *ssl_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_version(conn->client, buf, buflen);
+#endif /* CONFIG_TLS_INTERNAL_CLIENT */
+	return -1;
+}
+
+
 int tls_get_cipher(void *tls_ctx, struct tls_connection *conn,
 		   char *buf, size_t buflen)
 {
@@ -674,12 +714,6 @@
 }
 
 
-unsigned int tls_capabilities(void *tls_ctx)
-{
-	return 0;
-}
-
-
 int tls_connection_set_session_ticket_cb(void *tls_ctx,
 					 struct tls_connection *conn,
 					 tls_session_ticket_cb cb,
@@ -705,3 +739,26 @@
 {
 	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/src/crypto/tls_none.c b/src/crypto/tls_none.c
index 1b1ba56..ae392ad 100644
--- a/src/crypto/tls_none.c
+++ b/src/crypto/tls_none.c
@@ -72,14 +72,15 @@
 
 
 int tls_connection_set_verify(void *tls_ctx, struct tls_connection *conn,
-			      int verify_peer)
+			      int verify_peer, unsigned int flags,
+			      const u8 *session_ctx, size_t session_ctx_len)
 {
 	return -1;
 }
 
 
-int tls_connection_get_keys(void *tls_ctx, struct tls_connection *conn,
-			    struct tls_keys *keys)
+int tls_connection_get_random(void *tls_ctx, struct tls_connection *conn,
+			      struct tls_random *data)
 {
 	return -1;
 }
@@ -140,6 +141,13 @@
 }
 
 
+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)
 {
@@ -181,13 +189,30 @@
 }
 
 
-unsigned int tls_capabilities(void *tls_ctx)
-{
-	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/src/crypto/tls_openssl.c b/src/crypto/tls_openssl.c
index db2d73e..ebcc545 100644
--- a/src/crypto/tls_openssl.c
+++ b/src/crypto/tls_openssl.c
@@ -1,6 +1,6 @@
 /*
  * SSL/TLS interface functions for OpenSSL
- * Copyright (c) 2004-2013, Jouni Malinen <j@w1.fi>
+ * 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.
@@ -23,18 +23,19 @@
 #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
+#include "tls_openssl.h"
 
 #if defined(OPENSSL_IS_BORINGSSL)
 /* stack_index_t is the return type of OpenSSL's sk_XXX_num() functions. */
@@ -50,6 +51,46 @@
 #endif /* OPENSSL_NO_TLSEXT */
 #endif /* SSL_set_tlsext_status_type */
 
+#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
+/*
+ * SSL_get_client_random() and SSL_get_server_random() were added in OpenSSL
+ * 1.1.0. Provide compatibility wrappers for older versions.
+ */
+
+static size_t SSL_get_client_random(const SSL *ssl, unsigned char *out,
+				    size_t outlen)
+{
+	if (!ssl->s3 || outlen < SSL3_RANDOM_SIZE)
+		return 0;
+	os_memcpy(out, ssl->s3->client_random, SSL3_RANDOM_SIZE);
+	return SSL3_RANDOM_SIZE;
+}
+
+
+static size_t SSL_get_server_random(const SSL *ssl, unsigned char *out,
+				    size_t outlen)
+{
+	if (!ssl->s3 || outlen < SSL3_RANDOM_SIZE)
+		return 0;
+	os_memcpy(out, ssl->s3->server_random, SSL3_RANDOM_SIZE);
+	return SSL3_RANDOM_SIZE;
+}
+
+
+static size_t SSL_SESSION_get_master_key(const SSL_SESSION *session,
+					 unsigned char *out, size_t outlen)
+{
+	if (!session || session->master_key_length < 0 ||
+	    (size_t) session->master_key_length > outlen)
+		return 0;
+	if ((size_t) session->master_key_length < outlen)
+		outlen = session->master_key_length;
+	os_memcpy(out, session->master_key, outlen);
+	return outlen;
+}
+
+#endif
+
 #ifdef ANDROID
 #include <openssl/pem.h>
 #include <keystore/keystore_get.h>
@@ -65,9 +106,69 @@
 	return bio;
 }
 
+
+static int tls_add_ca_from_keystore(X509_STORE *ctx, const char *key_alias)
+{
+	BIO *bio = BIO_from_keystore(key_alias);
+	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) {
+		wpa_printf(MSG_WARNING, "TLS: Failed to parse certificate: %s",
+			   key_alias);
+		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(ctx, info->x509);
+		if (info->crl)
+			X509_STORE_add_crl(ctx, info->crl);
+	}
+
+	sk_X509_INFO_pop_free(stack, X509_INFO_free);
+
+	return 0;
+}
+
+
+static int tls_add_ca_from_keystore_encoded(X509_STORE *ctx,
+					    const char *encoded_key_alias)
+{
+	int rc = -1;
+	int len = os_strlen(encoded_key_alias);
+	unsigned char *decoded_alias;
+
+	if (len & 1) {
+		wpa_printf(MSG_WARNING, "Invalid hex-encoded alias: %s",
+			   encoded_key_alias);
+		return rc;
+	}
+
+	decoded_alias = os_malloc(len / 2 + 1);
+	if (decoded_alias) {
+		if (!hexstr2bin(encoded_key_alias, decoded_alias, len / 2)) {
+			decoded_alias[len / 2] = '\0';
+			rc = tls_add_ca_from_keystore(
+				ctx, (const char *) decoded_alias);
+		}
+		os_free(decoded_alias);
+	}
+
+	return rc;
+}
+
 #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,
@@ -80,6 +181,11 @@
 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;
@@ -103,6 +209,7 @@
 	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];
 
@@ -111,6 +218,9 @@
 	X509 *peer_cert;
 	X509 *peer_issuer;
 	X509 *peer_issuer_issuer;
+
+	unsigned char client_random[SSL3_RANDOM_SIZE];
+	unsigned char server_random[SSL3_RANDOM_SIZE];
 };
 
 
@@ -733,8 +843,27 @@
 #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;
@@ -746,7 +875,9 @@
 #ifdef CONFIG_FIPS
 #ifdef OPENSSL_FIPS
 		if (conf && conf->fips_mode) {
-			if (!FIPS_mode_set(1)) {
+			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();
@@ -754,8 +885,10 @@
 				os_free(tls_global);
 				tls_global = NULL;
 				return NULL;
-			} else
+			} else {
 				wpa_printf(MSG_INFO, "Running in FIPS mode");
+				fips_enabled = 1;
+			}
 		}
 #else /* OPENSSL_FIPS */
 		if (conf && conf->fips_mode) {
@@ -767,6 +900,7 @@
 		}
 #endif /* OPENSSL_FIPS */
 #endif /* CONFIG_FIPS */
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
 		SSL_load_error_strings();
 		SSL_library_init();
 #ifndef OPENSSL_NO_SHA256
@@ -788,6 +922,7 @@
 #endif /* OPENSSL_NO_RC2 */
 		PKCS12_PBE_add();
 #endif  /* PKCS12_FUNCS */
+#endif /* < 1.1.0 */
 	} else {
 		context = tls_context_new(conf);
 		if (context == NULL)
@@ -795,7 +930,11 @@
 	}
 	tls_openssl_ref_count++;
 
-	ssl = SSL_CTX_new(SSLv23_method());
+	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)
@@ -804,14 +943,40 @@
 			os_free(tls_global);
 			tls_global = NULL;
 		}
+		os_free(data);
 		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");
@@ -824,7 +989,7 @@
 		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(ssl);
+			tls_deinit(data);
 			return NULL;
 		}
 	}
@@ -838,24 +1003,28 @@
 		wpa_printf(MSG_ERROR,
 			   "OpenSSL: Failed to set cipher string '%s'",
 			   ciphers);
-		tls_deinit(ssl);
+		tls_deinit(data);
 		return NULL;
 	}
 
-	return ssl;
+	return data;
 }
 
 
 void tls_deinit(void *ssl_ctx)
 {
-	SSL_CTX *ssl = 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) {
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
 #ifndef OPENSSL_NO_ENGINE
 		ENGINE_cleanup();
 #endif /* OPENSSL_NO_ENGINE */
@@ -863,17 +1032,16 @@
 		ERR_remove_thread_state(NULL);
 		ERR_free_strings();
 		EVP_cleanup();
+#endif /* < 1.1.0 */
 		os_free(tls_global->ocsp_stapling_response);
 		tls_global->ocsp_stapling_response = NULL;
 		os_free(tls_global);
 		tls_global = NULL;
 	}
+
+	os_free(data);
 }
 
-#ifdef ANDROID
-/* EVP_PKEY_from_keystore comes from system/security/keystore-engine. */
-EVP_PKEY* EVP_PKEY_from_keystore(const char* key_id);
-#endif
 
 #ifndef OPENSSL_NO_ENGINE
 
@@ -896,6 +1064,11 @@
 #endif /* OPENSSL_NO_ENGINE */
 
 
+#ifdef ANDROID
+/* EVP_PKEY_from_keystore comes from system/security/keystore-engine. */
+EVP_PKEY * EVP_PKEY_from_keystore(const char *key_id);
+#endif /* ANDROID */
+
 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)
@@ -904,7 +1077,8 @@
 #if !defined(OPENSSL_NO_ENGINE)
 #error "This code depends on OPENSSL_NO_ENGINE being defined by BoringSSL."
 #endif
-
+	if (!key_id)
+		return TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED;
 	conn->engine = NULL;
 	conn->private_key = EVP_PKEY_from_keystore(key_id);
 	if (!conn->private_key) {
@@ -914,7 +1088,7 @@
 			   ERR_error_string(ERR_get_error(), NULL));
 		return TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED;
 	}
-#endif
+#endif /* ANDROID && OPENSSL_IS_BORINGSSL */
 
 #ifndef OPENSSL_NO_ENGINE
 	int ret = -1;
@@ -1022,10 +1196,10 @@
 	if (conn->engine) {
 #if !defined(OPENSSL_IS_BORINGSSL)
 		ENGINE_finish(conn->engine);
-#endif
+#endif /* !OPENSSL_IS_BORINGSSL */
 		conn->engine = NULL;
 	}
-#endif /* OPENSSL_NO_ENGINE */
+#endif /* ANDROID || !OPENSSL_NO_ENGINE */
 }
 
 
@@ -1044,14 +1218,83 @@
 }
 
 
+static const char * openssl_content_type(int content_type)
+{
+	switch (content_type) {
+	case 20:
+		return "change cipher spec";
+	case 21:
+		return "alert";
+	case 22:
+		return "handshake";
+	case 23:
+		return "application data";
+	case 24:
+		return "heartbeat";
+	case 256:
+		return "TLS header info"; /* pseudo content type */
+	default:
+		return "?";
+	}
+}
+
+
+static const char * openssl_handshake_type(int content_type, const u8 *buf,
+					   size_t len)
+{
+	if (content_type != 22 || !buf || len == 0)
+		return "";
+	switch (buf[0]) {
+	case 0:
+		return "hello request";
+	case 1:
+		return "client hello";
+	case 2:
+		return "server hello";
+	case 4:
+		return "new session ticket";
+	case 11:
+		return "certificate";
+	case 12:
+		return "server key exchange";
+	case 13:
+		return "certificate request";
+	case 14:
+		return "server hello done";
+	case 15:
+		return "certificate verify";
+	case 16:
+		return "client key exchange";
+	case 20:
+		return "finished";
+	case 21:
+		return "certificate url";
+	case 22:
+		return "certificate status";
+	default:
+		return "?";
+	}
+}
+
+
 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);
+	if (write_p == 2) {
+		wpa_printf(MSG_DEBUG,
+			   "OpenSSL: session ver=0x%x content_type=%d",
+			   version, content_type);
+		wpa_hexdump_key(MSG_MSGDUMP, "OpenSSL: Data", buf, len);
+		return;
+	}
+
+	wpa_printf(MSG_DEBUG, "OpenSSL: %s ver=0x%x content_type=%d (%s/%s)",
+		   write_p ? "TX" : "RX", version, content_type,
+		   openssl_content_type(content_type),
+		   openssl_handshake_type(content_type, buf, len));
 	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);
@@ -1065,7 +1308,8 @@
 
 struct tls_connection * tls_connection_init(void *ssl_ctx)
 {
-	SSL_CTX *ssl = 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);
@@ -1073,7 +1317,7 @@
 	conn = os_zalloc(sizeof(*conn));
 	if (conn == NULL)
 		return NULL;
-	conn->ssl_ctx = ssl_ctx;
+	conn->ssl_ctx = ssl;
 	conn->ssl = SSL_new(ssl);
 	if (conn->ssl == NULL) {
 		tls_show_errors(MSG_INFO, __func__,
@@ -1122,6 +1366,14 @@
 {
 	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);
@@ -1172,6 +1424,8 @@
 			found++;
 	}
 
+	sk_GENERAL_NAME_pop_free(ext, GENERAL_NAME_free);
+
 	return found;
 }
 
@@ -1284,9 +1538,11 @@
 		    1) {
 			wpa_printf(MSG_DEBUG, "TLS: %s in dNSName found",
 				   full ? "Match" : "Suffix match");
+			sk_GENERAL_NAME_pop_free(ext, GENERAL_NAME_free);
 			return 1;
 		}
 	}
+	sk_GENERAL_NAME_pop_free(ext, GENERAL_NAME_free);
 
 	if (dns_name) {
 		wpa_printf(MSG_DEBUG, "TLS: None of the dNSName(s) matched");
@@ -1427,7 +1683,8 @@
 		return;
 
 	os_memset(&ev, 0, sizeof(ev));
-	if (conn->cert_probe || context->cert_in_cb) {
+	if (conn->cert_probe || (conn->flags & TLS_CONN_EXT_CERT_CHECK) ||
+	    context->cert_in_cb) {
 		cert = get_x509_cert(err_cert);
 		ev.peer_cert.cert = cert;
 	}
@@ -1482,6 +1739,7 @@
 		pos += gen->d.ia5->length;
 		*pos = '\0';
 	}
+	sk_GENERAL_NAME_pop_free(ext, GENERAL_NAME_free);
 
 	for (alt = 0; alt < num_altsubject; alt++)
 		ev.peer_cert.altsubject[alt] = altsubject[alt];
@@ -1639,7 +1897,33 @@
 				       TLS_FAIL_SERVER_CHAIN_PROBE);
 	}
 
-	if (preverify_ok && context->event_cb != NULL)
+#ifdef OPENSSL_IS_BORINGSSL
+	if (depth == 0 && (conn->flags & TLS_CONN_REQUEST_OCSP) &&
+	    preverify_ok) {
+		enum ocsp_result res;
+
+		res = check_ocsp_resp(conn->ssl_ctx, conn->ssl, err_cert,
+				      conn->peer_issuer,
+				      conn->peer_issuer_issuer);
+		if (res == OCSP_REVOKED) {
+			preverify_ok = 0;
+			openssl_tls_fail_event(conn, err_cert, err, depth, buf,
+					       "certificate revoked",
+					       TLS_FAIL_REVOKED);
+			if (err == X509_V_OK)
+				X509_STORE_CTX_set_error(
+					x509_ctx, X509_V_ERR_CERT_REVOKED);
+		} else if (res != OCSP_GOOD &&
+			   (conn->flags & TLS_CONN_REQUIRE_OCSP)) {
+			preverify_ok = 0;
+			openssl_tls_fail_event(conn, err_cert, err, depth, buf,
+					       "bad certificate status response",
+					       TLS_FAIL_UNSPECIFIED);
+		}
+	}
+#endif /* OPENSSL_IS_BORINGSSL */
+
+	if (depth == 0 && preverify_ok && context->event_cb != NULL)
 		context->event_cb(context->cb_ctx,
 				  TLS_CERT_CHAIN_SUCCESS, NULL);
 
@@ -1648,9 +1932,9 @@
 
 
 #ifndef OPENSSL_NO_STDIO
-static int tls_load_ca_der(void *_ssl_ctx, const char *ca_cert)
+static int tls_load_ca_der(struct tls_data *data, const char *ca_cert)
 {
-	SSL_CTX *ssl_ctx = _ssl_ctx;
+	SSL_CTX *ssl_ctx = data->ssl;
 	X509_LOOKUP *lookup;
 	int ret = 0;
 
@@ -1680,11 +1964,12 @@
 #endif /* OPENSSL_NO_STDIO */
 
 
-static int tls_connection_ca_cert(void *_ssl_ctx, struct tls_connection *conn,
+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 = _ssl_ctx;
+	SSL_CTX *ssl_ctx = data->ssl;
 	X509_STORE *store;
 
 	/*
@@ -1774,30 +2059,40 @@
 	}
 
 #ifdef ANDROID
+	/* Single alias */
 	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)
+		if (tls_add_ca_from_keystore(ssl_ctx->cert_store,
+					     &ca_cert[11]) < 0)
 			return -1;
+		SSL_set_verify(conn->ssl, SSL_VERIFY_PEER, tls_verify_cb);
+		return 0;
+	}
 
-		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);
+	/* Multiple aliases separated by space */
+	if (ca_cert && os_strncmp("keystores://", ca_cert, 12) == 0) {
+		char *aliases = os_strdup(&ca_cert[12]);
+		const char *delim = " ";
+		int rc = 0;
+		char *savedptr;
+		char *alias;
+
+		if (!aliases)
+			return -1;
+		alias = strtok_r(aliases, delim, &savedptr);
+		for (; alias; alias = strtok_r(NULL, delim, &savedptr)) {
+			if (tls_add_ca_from_keystore_encoded(
+				    ssl_ctx->cert_store, alias)) {
+				wpa_printf(MSG_WARNING,
+					   "OpenSSL: %s - Failed to add ca_cert %s from keystore",
+					   __func__, alias);
+				rc = -1;
+				break;
 			}
 		}
-		sk_X509_INFO_pop_free(stack, X509_INFO_free);
+		os_free(aliases);
+		if (rc)
+			return rc;
+
 		SSL_set_verify(conn->ssl, SSL_VERIFY_PEER, tls_verify_cb);
 		return 0;
 	}
@@ -1819,7 +2114,7 @@
 			tls_show_errors(MSG_WARNING, __func__,
 					"Failed to load root certificates");
 			if (ca_cert &&
-			    tls_load_ca_der(ssl_ctx, ca_cert) == 0) {
+			    tls_load_ca_der(data, ca_cert) == 0) {
 				wpa_printf(MSG_DEBUG, "OpenSSL: %s - loaded "
 					   "DER format CA certificate",
 					   __func__);
@@ -1828,7 +2123,7 @@
 		} else {
 			wpa_printf(MSG_DEBUG, "TLS: Trusted root "
 				   "certificate(s) loaded");
-			tls_get_errors(ssl_ctx);
+			tls_get_errors(data);
 		}
 #else /* OPENSSL_NO_STDIO */
 		wpa_printf(MSG_DEBUG, "OpenSSL: %s - OPENSSL_NO_STDIO",
@@ -1845,8 +2140,10 @@
 }
 
 
-static int tls_global_ca_cert(SSL_CTX *ssl_ctx, const char *ca_cert)
+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)
 		{
@@ -1874,7 +2171,8 @@
 	int flags;
 
 	if (check_crl) {
-		X509_STORE *cs = SSL_CTX_get_cert_store(ssl_ctx);
+		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 "
@@ -1932,10 +2230,44 @@
 }
 
 
+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)
+			      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;
@@ -1950,20 +2282,25 @@
 		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);
 
-	/*
-	 * Set session id context in order to avoid fatal errors when client
-	 * tries to resume a session. However, set the context to a unique
-	 * value in order to effectively disable session resumption for now
-	 * since not all areas of the server code are ready for it (e.g.,
-	 * EAP-TTLS needs special handling for Phase 2 after abbreviated TLS
-	 * handshake).
-	 */
-	counter++;
-	SSL_set_session_id_context(conn->ssl,
-				   (const unsigned char *) &counter,
-				   sizeof(counter));
+	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;
 }
@@ -1977,6 +2314,17 @@
 	if (client_cert == NULL && client_cert_blob == NULL)
 		return 0;
 
+#ifdef PKCS12_FUNCS
+#if OPENSSL_VERSION_NUMBER < 0x10002000L
+	/*
+	 * Clear previously set extra chain certificates, if any, from PKCS#12
+	 * processing in tls_parse_pkcs12() to allow OpenSSL to build a new
+	 * chain properly.
+	 */
+	SSL_CTX_clear_extra_chain_certs(conn->ssl_ctx);
+#endif /* OPENSSL_VERSION_NUMBER < 0x10002000L */
+#endif /* PKCS12_FUNCS */
+
 	if (client_cert_blob &&
 	    SSL_use_certificate_ASN1(conn->ssl, (u8 *) client_cert_blob,
 				     client_cert_blob_len) == 1) {
@@ -2035,9 +2383,12 @@
 }
 
 
-static int tls_global_client_cert(SSL_CTX *ssl_ctx, const char *client_cert)
+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;
 
@@ -2071,7 +2422,7 @@
 
 
 #ifdef PKCS12_FUNCS
-static int tls_parse_pkcs12(SSL_CTX *ssl_ctx, SSL *ssl, PKCS12 *p12,
+static int tls_parse_pkcs12(struct tls_data *data, SSL *ssl, PKCS12 *p12,
 			    const char *passwd)
 {
 	EVP_PKEY *pkey;
@@ -2083,6 +2434,8 @@
 	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");
@@ -2100,7 +2453,7 @@
 			if (SSL_use_certificate(ssl, cert) != 1)
 				res = -1;
 		} else {
-			if (SSL_CTX_use_certificate(ssl_ctx, cert) != 1)
+			if (SSL_CTX_use_certificate(data->ssl, cert) != 1)
 				res = -1;
 		}
 		X509_free(cert);
@@ -2112,13 +2465,64 @@
 			if (SSL_use_PrivateKey(ssl, pkey) != 1)
 				res = -1;
 		} else {
-			if (SSL_CTX_use_PrivateKey(ssl_ctx, pkey) != 1)
+			if (SSL_CTX_use_PrivateKey(data->ssl, pkey) != 1)
 				res = -1;
 		}
 		EVP_PKEY_free(pkey);
 	}
 
 	if (certs) {
+#if OPENSSL_VERSION_NUMBER >= 0x10002000L && !defined(LIBRESSL_VERSION_NUMBER)
+		if (ssl)
+			SSL_clear_chain_certs(ssl);
+		else
+			SSL_CTX_clear_chain_certs(data->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 && SSL_add1_chain_cert(ssl, cert) != 1) ||
+			    (!ssl && SSL_CTX_add1_chain_cert(data->ssl,
+							     cert) != 1)) {
+				tls_show_errors(MSG_DEBUG, __func__,
+						"Failed to add additional certificate");
+				res = -1;
+				X509_free(cert);
+				break;
+			}
+			X509_free(cert);
+		}
+		if (!res) {
+			/* Try to continue anyway */
+		}
+		sk_X509_pop_free(certs, X509_free);
+#ifndef OPENSSL_IS_BORINGSSL
+		if (ssl)
+			res = SSL_build_cert_chain(
+				ssl,
+				SSL_BUILD_CHAIN_FLAG_CHECK |
+				SSL_BUILD_CHAIN_FLAG_IGNORE_ERROR);
+		else
+			res = SSL_CTX_build_cert_chain(
+				data->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 */
+		SSL_CTX_clear_extra_chain_certs(data->ssl);
 		while ((cert = sk_X509_pop(certs)) != NULL) {
 			X509_NAME_oneline(X509_get_subject_name(cert), buf,
 					  sizeof(buf));
@@ -2128,26 +2532,29 @@
 			 * There is no SSL equivalent for the chain cert - so
 			 * always add it to the context...
 			 */
-			if (SSL_CTX_add_extra_chain_cert(ssl_ctx, cert) != 1) {
+			if (SSL_CTX_add_extra_chain_cert(data->ssl, cert) != 1)
+			{
+				X509_free(cert);
 				res = -1;
 				break;
 			}
 		}
-		sk_X509_free(certs);
+		sk_X509_pop_free(certs, X509_free);
+#endif /* OPENSSL_VERSION_NUMBER >= 0x10002000L */
 	}
 
 	PKCS12_free(p12);
 
 	if (res < 0)
-		tls_get_errors(ssl_ctx);
+		tls_get_errors(data);
 
 	return res;
 }
 #endif  /* PKCS12_FUNCS */
 
 
-static int tls_read_pkcs12(SSL_CTX *ssl_ctx, SSL *ssl, const char *private_key,
-			   const char *passwd)
+static int tls_read_pkcs12(struct tls_data *data, SSL *ssl,
+			   const char *private_key, const char *passwd)
 {
 #ifdef PKCS12_FUNCS
 	FILE *f;
@@ -2166,7 +2573,7 @@
 		return -1;
 	}
 
-	return tls_parse_pkcs12(ssl_ctx, ssl, p12, passwd);
+	return tls_parse_pkcs12(data, ssl, p12, passwd);
 
 #else /* PKCS12_FUNCS */
 	wpa_printf(MSG_INFO, "TLS: PKCS12 support disabled - cannot read "
@@ -2176,7 +2583,7 @@
 }
 
 
-static int tls_read_pkcs12_blob(SSL_CTX *ssl_ctx, SSL *ssl,
+static int tls_read_pkcs12_blob(struct tls_data *data, SSL *ssl,
 				const u8 *blob, size_t len, const char *passwd)
 {
 #ifdef PKCS12_FUNCS
@@ -2189,7 +2596,7 @@
 		return -1;
 	}
 
-	return tls_parse_pkcs12(ssl_ctx, ssl, p12, passwd);
+	return tls_parse_pkcs12(data, ssl, p12, passwd);
 
 #else /* PKCS12_FUNCS */
 	wpa_printf(MSG_INFO, "TLS: PKCS12 support disabled - cannot parse "
@@ -2260,13 +2667,13 @@
 }
 
 
-static int tls_connection_engine_ca_cert(void *_ssl_ctx,
+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 = _ssl_ctx;
+	SSL_CTX *ssl_ctx = data->ssl;
 	X509_STORE *store;
 
 	if (tls_engine_get_cert(conn, ca_cert_id, &cert))
@@ -2332,14 +2739,14 @@
 }
 
 
-static int tls_connection_private_key(void *_ssl_ctx,
+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 = _ssl_ctx;
+	SSL_CTX *ssl_ctx = data->ssl;
 	char *passwd;
 	int ok;
 
@@ -2385,7 +2792,7 @@
 			break;
 		}
 
-		if (tls_read_pkcs12_blob(ssl_ctx, conn->ssl, private_key_blob,
+		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");
@@ -2418,7 +2825,7 @@
 			   __func__);
 #endif /* OPENSSL_NO_STDIO */
 
-		if (tls_read_pkcs12(ssl_ctx, conn->ssl, private_key, passwd)
+		if (tls_read_pkcs12(data, conn->ssl, private_key, passwd)
 		    == 0) {
 			wpa_printf(MSG_DEBUG, "OpenSSL: Reading PKCS#12 file "
 				   "--> OK");
@@ -2457,9 +2864,11 @@
 }
 
 
-static int tls_global_private_key(SSL_CTX *ssl_ctx, const char *private_key,
+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)
@@ -2481,7 +2890,7 @@
 	    SSL_CTX_use_PrivateKey_file(ssl_ctx, private_key,
 					SSL_FILETYPE_PEM) != 1 &&
 #endif /* OPENSSL_NO_STDIO */
-	    tls_read_pkcs12(ssl_ctx, NULL, private_key, passwd)) {
+	    tls_read_pkcs12(data, NULL, private_key, passwd)) {
 		tls_show_errors(MSG_INFO, __func__,
 				"Failed to load private key");
 		os_free(passwd);
@@ -2576,7 +2985,7 @@
 }
 
 
-static int tls_global_dh(SSL_CTX *ssl_ctx, const char *dh_file)
+static int tls_global_dh(struct tls_data *data, const char *dh_file)
 {
 #ifdef OPENSSL_NO_DH
 	if (dh_file == NULL)
@@ -2585,6 +2994,7 @@
 		   "dh_file specified");
 	return -1;
 #else /* OPENSSL_NO_DH */
+	SSL_CTX *ssl_ctx = data->ssl;
 	DH *dh;
 	BIO *bio;
 
@@ -2650,35 +3060,33 @@
 }
 
 
-int tls_connection_get_keys(void *ssl_ctx, struct tls_connection *conn,
-			    struct tls_keys *keys)
+int tls_connection_get_random(void *ssl_ctx, struct tls_connection *conn,
+			      struct tls_random *keys)
 {
-#ifdef CONFIG_FIPS
-	wpa_printf(MSG_ERROR, "OpenSSL: TLS keys cannot be exported in FIPS "
-		   "mode");
-	return -1;
-#else /* CONFIG_FIPS */
 	SSL *ssl;
 
 	if (conn == NULL || keys == NULL)
 		return -1;
 	ssl = conn->ssl;
-	if (ssl == NULL || ssl->s3 == NULL || ssl->session == NULL)
+	if (ssl == 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;
+	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));
 
 	return 0;
-#endif /* CONFIG_FIPS */
 }
 
 
+#ifndef CONFIG_FIPS
 static int openssl_get_keyblock_size(SSL *ssl)
 {
+#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
 	const EVP_CIPHER *c;
 	const EVP_MD *h;
 	int md_size;
@@ -2688,17 +3096,11 @@
 		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;
 
@@ -2708,10 +3110,38 @@
 	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(void *tls_ctx, struct tls_connection *conn,
+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)
 {
@@ -2721,11 +3151,17 @@
 	return -1;
 #else /* CONFIG_FIPS */
 	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
@@ -2736,8 +3172,11 @@
 	if (conn == NULL)
 		return -1;
 	ssl = conn->ssl;
-	if (ssl == NULL || ssl->s3 == NULL || ssl->session == NULL ||
-	    ssl->session->master_key_length <= 0)
+	if (ssl == NULL)
+		return -1;
+	ver = SSL_get_version(ssl);
+	sess = SSL_get_session(ssl);
+	if (!ver || !sess)
 		return -1;
 
 	if (skip_keyblock) {
@@ -2756,23 +3195,32 @@
 		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, ssl->s3->server_random, SSL3_RANDOM_SIZE);
-		os_memcpy(rnd + SSL3_RANDOM_SIZE, ssl->s3->client_random,
-			SSL3_RANDOM_SIZE);
+		os_memcpy(rnd, server_random, SSL3_RANDOM_SIZE);
+		os_memcpy(rnd + SSL3_RANDOM_SIZE, 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);
+		os_memcpy(rnd, client_random, SSL3_RANDOM_SIZE);
+		os_memcpy(rnd + SSL3_RANDOM_SIZE, server_random,
+			  SSL3_RANDOM_SIZE);
 	}
 
-	/* TODO: TLSv1.2 may need another PRF. This could use something closer
-	 * to SSL_export_keying_material() design. */
-	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)
+	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);
@@ -2787,22 +3235,18 @@
 		       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(tls_ctx, conn, label,
+		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,
+	if (SSL_export_keying_material(conn->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(tls_ctx, conn, label, server_random_first,
+	return openssl_tls_prf(conn, label, server_random_first,
 			       skip_keyblock, out, out_len);
 }
 
@@ -2818,7 +3262,7 @@
 	 * Give TLS handshake data from the server (if available) to OpenSSL
 	 * for processing.
 	 */
-	if (in_data &&
+	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__,
@@ -2930,8 +3374,14 @@
 		return NULL;
 	}
 
-	if (SSL_is_init_finished(conn->ssl) && appl_data && in_data)
-		*appl_data = openssl_get_appl_data(conn, wpabuf_len(in_data));
+	if (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");
@@ -3056,18 +3506,14 @@
 
 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;
+	char buf[500], *pos, *end;
 	u8 *c;
 	int ret;
 
@@ -3095,6 +3541,12 @@
 		case TLS_CIPHER_ANON_DH_AES128_SHA:
 			suite = "ADH-AES128-SHA";
 			break;
+		case TLS_CIPHER_RSA_DHE_AES256_SHA:
+			suite = "DHE-RSA-AES256-SHA";
+			break;
+		case TLS_CIPHER_AES256_SHA:
+			suite = "AES256-SHA";
+			break;
 		default:
 			wpa_printf(MSG_DEBUG, "TLS: Unsupported "
 				   "cipher selection: %d", *c);
@@ -3110,6 +3562,21 @@
 
 	wpa_printf(MSG_DEBUG, "OpenSSL: cipher suites: %s", buf + 1);
 
+#if OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(LIBRESSL_VERSION_NUMBER)
+#if defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) || defined(EAP_SERVER_FAST)
+	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");
@@ -3120,6 +3587,22 @@
 }
 
 
+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)
 {
@@ -3372,10 +3855,12 @@
 		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_CERTID_free(id);
 		OCSP_BASICRESP_free(basic);
 		OCSP_RESPONSE_free(rsp);
 		return (conn->flags & TLS_CONN_REQUIRE_OCSP) ? 0 : 1;
 	}
+	OCSP_CERTID_free(id);
 
 	if (!OCSP_check_validity(this_update, next_update, 5 * 60, -1)) {
 		tls_show_errors(MSG_INFO, __func__,
@@ -3442,6 +3927,7 @@
 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;
@@ -3453,6 +3939,12 @@
 	if (conn == NULL)
 		return -1;
 
+	if (params->flags & TLS_CONN_REQUIRE_OCSP_ALL) {
+		wpa_printf(MSG_INFO,
+			   "OpenSSL: ocsp=3 not supported");
+		return -1;
+	}
+
 	/*
 	 * If the engine isn't explicitly configured, and any of the
 	 * cert/key fields are actually PKCS#11 URIs, then automatically
@@ -3483,6 +3975,8 @@
 	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");
@@ -3492,6 +3986,8 @@
 			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",
@@ -3513,10 +4009,9 @@
 		return -1;
 
 	if (engine_id && ca_cert_id) {
-		if (tls_connection_engine_ca_cert(tls_ctx, conn,
-						  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(tls_ctx, conn, params->ca_cert,
+	} else if (tls_connection_ca_cert(data, conn, params->ca_cert,
 					  params->ca_cert_blob,
 					  params->ca_cert_blob_len,
 					  params->ca_path))
@@ -3534,7 +4029,7 @@
 		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(tls_ctx, conn,
+	} else if (tls_connection_private_key(data, conn,
 					      params->private_key,
 					      params->private_key_passwd,
 					      params->private_key_blob,
@@ -3558,40 +4053,36 @@
 		return -1;
 	}
 
-#ifdef SSL_OP_NO_TICKET
-	if (params->flags & TLS_CONN_DISABLE_SESSION_TICKET)
-		SSL_set_options(conn->ssl, SSL_OP_NO_TICKET);
-#ifdef SSL_clear_options
-	else
-		SSL_clear_options(conn->ssl, SSL_OP_NO_TICKET);
-#endif /* SSL_clear_options */
-#endif /*  SSL_OP_NO_TICKET */
+	tls_set_conn_flags(conn->ssl, params->flags);
 
-#ifdef SSL_OP_NO_TLSv1_1
-	if (params->flags & TLS_CONN_DISABLE_TLSv1_1)
-		SSL_set_options(conn->ssl, SSL_OP_NO_TLSv1_1);
-	else
-		SSL_clear_options(conn->ssl, SSL_OP_NO_TLSv1_1);
-#endif /* SSL_OP_NO_TLSv1_1 */
-#ifdef SSL_OP_NO_TLSv1_2
-	if (params->flags & TLS_CONN_DISABLE_TLSv1_2)
-		SSL_set_options(conn->ssl, SSL_OP_NO_TLSv1_2);
-	else
-		SSL_clear_options(conn->ssl, SSL_OP_NO_TLSv1_2);
-#endif /* SSL_OP_NO_TLSv1_2 */
-
+#ifdef OPENSSL_IS_BORINGSSL
+	if (params->flags & TLS_CONN_REQUEST_OCSP) {
+		SSL_enable_ocsp_stapling(conn->ssl);
+	}
+#else /* OPENSSL_IS_BORINGSSL */
 #ifdef HAVE_OCSP
 	if (params->flags & TLS_CONN_REQUEST_OCSP) {
-		SSL_CTX *ssl_ctx = tls_ctx;
+		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 */
+#endif /* OPENSSL_IS_BORINGSSL */
 
 	conn->flags = params->flags;
 
-	tls_get_errors(tls_ctx);
+	tls_get_errors(data);
 
 	return 0;
 }
@@ -3600,7 +4091,8 @@
 int tls_global_set_params(void *tls_ctx,
 			  const struct tls_connection_params *params)
 {
-	SSL_CTX *ssl_ctx = tls_ctx;
+	struct tls_data *data = tls_ctx;
+	SSL_CTX *ssl_ctx = data->ssl;
 	unsigned long err;
 
 	while ((err = ERR_get_error())) {
@@ -3608,19 +4100,12 @@
 			   __func__, ERR_error_string(err, NULL));
 	}
 
-	if (tls_global_ca_cert(ssl_ctx, params->ca_cert))
-		return -1;
-
-	if (tls_global_client_cert(ssl_ctx, params->client_cert))
-		return -1;
-
-	if (tls_global_private_key(ssl_ctx, params->private_key,
-				   params->private_key_passwd))
-		return -1;
-
-	if (tls_global_dh(ssl_ctx, params->dh_file)) {
-		wpa_printf(MSG_INFO, "TLS: Failed to load DH file '%s'",
-			   params->dh_file);
+	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;
 	}
 
@@ -3656,18 +4141,12 @@
 }
 
 
-unsigned int tls_capabilities(void *tls_ctx)
-{
-	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
+#if (defined(OPENSSL_IS_BORINGSSL) || OPENSSL_VERSION_NUMBER >= 0x10100000L) && !defined(LIBRESSL_VERSION_NUMBER)
 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)
@@ -3680,6 +4159,7 @@
 	struct tls_connection *conn = arg;
 	int ret;
 
+#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
 	if (conn == NULL || conn->session_ticket_cb == NULL)
 		return 0;
 
@@ -3688,6 +4168,23 @@
 				      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;
 
@@ -3757,7 +4254,80 @@
 
 int tls_get_library_version(char *buf, size_t buf_len)
 {
+#if OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(LIBRESSL_VERSION_NUMBER)
+	return os_snprintf(buf, buf_len, "OpenSSL build=%s run=%s",
+			   OPENSSL_VERSION_TEXT,
+			   OpenSSL_version(OPENSSL_VERSION));
+#else
 	return os_snprintf(buf, buf_len, "OpenSSL build=%s run=%s",
 			   OPENSSL_VERSION_TEXT,
 			   SSLeay_version(SSLEAY_VERSION));
+#endif
+}
+
+
+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/src/crypto/tls_openssl.h b/src/crypto/tls_openssl.h
new file mode 100644
index 0000000..2a62d5c
--- /dev/null
+++ b/src/crypto/tls_openssl.h
@@ -0,0 +1,19 @@
+/*
+ * 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.
+ */
+
+#ifndef TLS_OPENSSL_H
+#define TLS_OPENSSL_H
+
+enum ocsp_result {
+	OCSP_GOOD, OCSP_REVOKED, OCSP_NO_RESPONSE, OCSP_INVALID
+};
+
+enum ocsp_result check_ocsp_resp(SSL_CTX *ssl_ctx, SSL *ssl, X509 *cert,
+				 X509 *issuer, X509 *issuer_issuer);
+
+#endif /* TLS_OPENSSL_H */
diff --git a/src/crypto/tls_openssl_ocsp.c b/src/crypto/tls_openssl_ocsp.c
new file mode 100644
index 0000000..8b37b34
--- /dev/null
+++ b/src/crypto/tls_openssl_ocsp.c
@@ -0,0 +1,846 @@
+/*
+ * SSL/TLS interface functions for OpenSSL - BoringSSL OCSP
+ * 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/ssl.h>
+#include <openssl/err.h>
+#include <openssl/x509v3.h>
+#ifdef OPENSSL_IS_BORINGSSL
+#include <openssl/asn1.h>
+#include <openssl/asn1t.h>
+#endif /* OPENSSL_IS_BORINGSSL */
+
+#include "common.h"
+#include "tls_openssl.h"
+
+
+#ifdef OPENSSL_IS_BORINGSSL
+
+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));
+	}
+}
+
+
+/*
+ * CertID ::= SEQUENCE {
+ *     hashAlgorithm      AlgorithmIdentifier,
+ *     issuerNameHash     OCTET STRING, -- Hash of Issuer's DN
+ *     issuerKeyHash      OCTET STRING, -- Hash of Issuer's public key
+ *     serialNumber       CertificateSerialNumber }
+ */
+typedef struct {
+	X509_ALGOR *hashAlgorithm;
+	ASN1_OCTET_STRING *issuerNameHash;
+	ASN1_OCTET_STRING *issuerKeyHash;
+	ASN1_INTEGER *serialNumber;
+} CertID;
+
+/*
+ * ResponseBytes ::=       SEQUENCE {
+ *     responseType   OBJECT IDENTIFIER,
+ *     response       OCTET STRING }
+ */
+typedef struct {
+	ASN1_OBJECT *responseType;
+	ASN1_OCTET_STRING *response;
+} ResponseBytes;
+
+/*
+ * OCSPResponse ::= SEQUENCE {
+ *    responseStatus         OCSPResponseStatus,
+ *    responseBytes          [0] EXPLICIT ResponseBytes OPTIONAL }
+ */
+typedef struct {
+	ASN1_ENUMERATED *responseStatus;
+	ResponseBytes *responseBytes;
+} OCSPResponse;
+
+ASN1_SEQUENCE(ResponseBytes) = {
+	ASN1_SIMPLE(ResponseBytes, responseType, ASN1_OBJECT),
+	ASN1_SIMPLE(ResponseBytes, response, ASN1_OCTET_STRING)
+} ASN1_SEQUENCE_END(ResponseBytes);
+
+ASN1_SEQUENCE(OCSPResponse) = {
+	ASN1_SIMPLE(OCSPResponse, responseStatus, ASN1_ENUMERATED),
+	ASN1_EXP_OPT(OCSPResponse, responseBytes, ResponseBytes, 0)
+} ASN1_SEQUENCE_END(OCSPResponse);
+
+IMPLEMENT_ASN1_FUNCTIONS(OCSPResponse);
+
+/*
+ * ResponderID ::= CHOICE {
+ *    byName               [1] Name,
+ *    byKey                [2] KeyHash }
+ */
+typedef struct {
+	int type;
+	union {
+		X509_NAME *byName;
+		ASN1_OCTET_STRING *byKey;
+	} value;
+} ResponderID;
+
+/*
+ * RevokedInfo ::= SEQUENCE {
+ *     revocationTime              GeneralizedTime,
+ *     revocationReason    [0]     EXPLICIT CRLReason OPTIONAL }
+ */
+typedef struct {
+	ASN1_GENERALIZEDTIME *revocationTime;
+	ASN1_ENUMERATED *revocationReason;
+} RevokedInfo;
+
+/*
+ * CertStatus ::= CHOICE {
+ *     good        [0]     IMPLICIT NULL,
+ *     revoked     [1]     IMPLICIT RevokedInfo,
+ *     unknown     [2]     IMPLICIT UnknownInfo }
+ */
+typedef struct {
+	int type;
+	union {
+		ASN1_NULL *good;
+		RevokedInfo *revoked;
+		ASN1_NULL *unknown;
+	} value;
+} CertStatus;
+
+/*
+ * SingleResponse ::= SEQUENCE {
+ *    certID                       CertID,
+ *    certStatus                   CertStatus,
+ *    thisUpdate                   GeneralizedTime,
+ *    nextUpdate         [0]       EXPLICIT GeneralizedTime OPTIONAL,
+ *    singleExtensions   [1]       EXPLICIT Extensions OPTIONAL }
+ */
+typedef struct {
+	CertID *certID;
+	CertStatus *certStatus;
+	ASN1_GENERALIZEDTIME *thisUpdate;
+	ASN1_GENERALIZEDTIME *nextUpdate;
+	STACK_OF(X509_EXTENSION) *singleExtensions;
+} SingleResponse;
+
+/*
+ * ResponseData ::= SEQUENCE {
+ *   version              [0] EXPLICIT Version DEFAULT v1,
+ *   responderID              ResponderID,
+ *   producedAt               GeneralizedTime,
+ *   responses                SEQUENCE OF SingleResponse,
+ *   responseExtensions   [1] EXPLICIT Extensions OPTIONAL }
+ */
+typedef struct {
+	ASN1_INTEGER *version;
+	ResponderID *responderID;
+	ASN1_GENERALIZEDTIME *producedAt;
+	STACK_OF(SingleResponse) *responses;
+	STACK_OF(X509_EXTENSION) *responseExtensions;
+} ResponseData;
+
+/*
+ * BasicOCSPResponse       ::= SEQUENCE {
+ *   tbsResponseData      ResponseData,
+ *   signatureAlgorithm   AlgorithmIdentifier,
+ *   signature            BIT STRING,
+ *   certs                [0] EXPLICIT SEQUENCE OF Certificate OPTIONAL }
+ */
+typedef struct {
+	ResponseData *tbsResponseData;
+	X509_ALGOR *signatureAlgorithm;
+	ASN1_BIT_STRING *signature;
+	STACK_OF(X509) *certs;
+} BasicOCSPResponse;
+
+ASN1_SEQUENCE(CertID) = {
+	ASN1_SIMPLE(CertID, hashAlgorithm, X509_ALGOR),
+	ASN1_SIMPLE(CertID, issuerNameHash, ASN1_OCTET_STRING),
+	ASN1_SIMPLE(CertID, issuerKeyHash, ASN1_OCTET_STRING),
+	ASN1_SIMPLE(CertID, serialNumber, ASN1_INTEGER)
+} ASN1_SEQUENCE_END(CertID);
+
+ASN1_CHOICE(ResponderID) = {
+	ASN1_EXP(ResponderID, value.byName, X509_NAME, 1),
+	ASN1_EXP(ResponderID, value.byKey, ASN1_OCTET_STRING, 2)
+} ASN1_CHOICE_END(ResponderID);
+
+ASN1_SEQUENCE(RevokedInfo) = {
+	ASN1_SIMPLE(RevokedInfo, revocationTime, ASN1_GENERALIZEDTIME),
+	ASN1_EXP_OPT(RevokedInfo, revocationReason, ASN1_ENUMERATED, 0)
+} ASN1_SEQUENCE_END(RevokedInfo);
+
+ASN1_CHOICE(CertStatus) = {
+	ASN1_IMP(CertStatus, value.good, ASN1_NULL, 0),
+	ASN1_IMP(CertStatus, value.revoked, RevokedInfo, 1),
+	ASN1_IMP(CertStatus, value.unknown, ASN1_NULL, 2)
+} ASN1_CHOICE_END(CertStatus);
+
+ASN1_SEQUENCE(SingleResponse) = {
+	ASN1_SIMPLE(SingleResponse, certID, CertID),
+	ASN1_SIMPLE(SingleResponse, certStatus, CertStatus),
+	ASN1_SIMPLE(SingleResponse, thisUpdate, ASN1_GENERALIZEDTIME),
+	ASN1_EXP_OPT(SingleResponse, nextUpdate, ASN1_GENERALIZEDTIME, 0),
+	ASN1_EXP_SEQUENCE_OF_OPT(SingleResponse, singleExtensions,
+				 X509_EXTENSION, 1)
+} ASN1_SEQUENCE_END(SingleResponse);
+
+ASN1_SEQUENCE(ResponseData) = {
+	ASN1_EXP_OPT(ResponseData, version, ASN1_INTEGER, 0),
+	ASN1_SIMPLE(ResponseData, responderID, ResponderID),
+	ASN1_SIMPLE(ResponseData, producedAt, ASN1_GENERALIZEDTIME),
+	ASN1_SEQUENCE_OF(ResponseData, responses, SingleResponse),
+	ASN1_EXP_SEQUENCE_OF_OPT(ResponseData, responseExtensions,
+				 X509_EXTENSION, 1)
+} ASN1_SEQUENCE_END(ResponseData);
+
+ASN1_SEQUENCE(BasicOCSPResponse) = {
+	ASN1_SIMPLE(BasicOCSPResponse, tbsResponseData, ResponseData),
+	ASN1_SIMPLE(BasicOCSPResponse, signatureAlgorithm, X509_ALGOR),
+	ASN1_SIMPLE(BasicOCSPResponse, signature, ASN1_BIT_STRING),
+	ASN1_EXP_SEQUENCE_OF_OPT(BasicOCSPResponse, certs, X509, 0)
+} ASN1_SEQUENCE_END(BasicOCSPResponse);
+
+IMPLEMENT_ASN1_FUNCTIONS(BasicOCSPResponse);
+
+#define sk_SingleResponse_num(sk) \
+sk_num(CHECKED_CAST(_STACK *, STACK_OF(SingleResponse) *, sk))
+
+#define sk_SingleResponse_value(sk, i) \
+	((SingleResponse *)						\
+	 sk_value(CHECKED_CAST(_STACK *, STACK_OF(SingleResponse) *, sk), (i)))
+
+
+static char * mem_bio_to_str(BIO *out)
+{
+	char *txt;
+	size_t rlen;
+	int res;
+
+	rlen = BIO_ctrl_pending(out);
+	txt = os_malloc(rlen + 1);
+	if (!txt) {
+		BIO_free(out);
+		return NULL;
+	}
+
+	res = BIO_read(out, txt, rlen);
+	BIO_free(out);
+	if (res < 0) {
+		os_free(txt);
+		return NULL;
+	}
+
+	txt[res] = '\0';
+	return txt;
+}
+
+
+static char * generalizedtime_str(ASN1_GENERALIZEDTIME *t)
+{
+	BIO *out;
+
+	out = BIO_new(BIO_s_mem());
+	if (!out)
+		return NULL;
+
+	if (!ASN1_GENERALIZEDTIME_print(out, t)) {
+		BIO_free(out);
+		return NULL;
+	}
+
+	return mem_bio_to_str(out);
+}
+
+
+static char * responderid_str(ResponderID *rid)
+{
+	BIO *out;
+
+	out = BIO_new(BIO_s_mem());
+	if (!out)
+		return NULL;
+
+	switch (rid->type) {
+	case 0:
+		X509_NAME_print_ex(out, rid->value.byName, 0, XN_FLAG_ONELINE);
+		break;
+	case 1:
+		i2a_ASN1_STRING(out, rid->value.byKey, V_ASN1_OCTET_STRING);
+		break;
+	default:
+		BIO_free(out);
+		return NULL;
+	}
+
+	return mem_bio_to_str(out);
+}
+
+
+static char * octet_string_str(ASN1_OCTET_STRING *o)
+{
+	BIO *out;
+
+	out = BIO_new(BIO_s_mem());
+	if (!out)
+		return NULL;
+
+	i2a_ASN1_STRING(out, o, V_ASN1_OCTET_STRING);
+	return mem_bio_to_str(out);
+}
+
+
+static char * integer_str(ASN1_INTEGER *i)
+{
+	BIO *out;
+
+	out = BIO_new(BIO_s_mem());
+	if (!out)
+		return NULL;
+
+	i2a_ASN1_INTEGER(out, i);
+	return mem_bio_to_str(out);
+}
+
+
+static char * algor_str(X509_ALGOR *alg)
+{
+	BIO *out;
+
+	out = BIO_new(BIO_s_mem());
+	if (!out)
+		return NULL;
+
+	i2a_ASN1_OBJECT(out, alg->algorithm);
+	return mem_bio_to_str(out);
+}
+
+
+static char * extensions_str(const char *title, STACK_OF(X509_EXTENSION) *ext)
+{
+	BIO *out;
+
+	if (!ext)
+		return NULL;
+
+	out = BIO_new(BIO_s_mem());
+	if (!out)
+		return NULL;
+
+	if (!X509V3_extensions_print(out, title, ext, 0, 0)) {
+		BIO_free(out);
+		return NULL;
+	}
+	return mem_bio_to_str(out);
+}
+
+
+static int ocsp_resp_valid(ASN1_GENERALIZEDTIME *thisupd,
+			   ASN1_GENERALIZEDTIME *nextupd)
+{
+	time_t now, tmp;
+
+	if (!ASN1_GENERALIZEDTIME_check(thisupd)) {
+		wpa_printf(MSG_DEBUG,
+			   "OpenSSL: Invalid OCSP response thisUpdate");
+		return 0;
+	}
+
+	time(&now);
+	tmp = now + 5 * 60; /* allow five minute clock difference */
+	if (X509_cmp_time(thisupd, &tmp) > 0) {
+		wpa_printf(MSG_DEBUG, "OpenSSL: OCSP response not yet valid");
+		return 0;
+	}
+
+	if (!nextupd)
+		return 1; /* OK - no limit on response age */
+
+	if (!ASN1_GENERALIZEDTIME_check(nextupd)) {
+		wpa_printf(MSG_DEBUG,
+			   "OpenSSL: Invalid OCSP response nextUpdate");
+		return 0;
+	}
+
+	tmp = now - 5 * 60; /* allow five minute clock difference */
+	if (X509_cmp_time(nextupd, &tmp) < 0) {
+		wpa_printf(MSG_DEBUG, "OpenSSL: OCSP response expired");
+		return 0;
+	}
+
+	if (ASN1_STRING_cmp(nextupd, thisupd) < 0) {
+		wpa_printf(MSG_DEBUG,
+			   "OpenSSL: OCSP response nextUpdate before thisUpdate");
+		return 0;
+	}
+
+	/* Both thisUpdate and nextUpdate are valid */
+	return -1;
+}
+
+
+static int issuer_match(X509 *cert, X509 *issuer, CertID *certid)
+{
+	X509_NAME *iname;
+	ASN1_BIT_STRING *ikey;
+	const EVP_MD *dgst;
+	unsigned int len;
+	unsigned char md[EVP_MAX_MD_SIZE];
+	ASN1_OCTET_STRING *hash;
+	char *txt;
+
+	dgst = EVP_get_digestbyobj(certid->hashAlgorithm->algorithm);
+	if (!dgst) {
+		wpa_printf(MSG_DEBUG,
+			   "OpenSSL: Could not find matching hash algorithm for OCSP");
+		return -1;
+	}
+
+	iname = X509_get_issuer_name(cert);
+	if (!X509_NAME_digest(iname, dgst, md, &len))
+		return -1;
+	hash = ASN1_OCTET_STRING_new();
+	if (!hash)
+		return -1;
+	if (!ASN1_OCTET_STRING_set(hash, md, len)) {
+		ASN1_OCTET_STRING_free(hash);
+		return -1;
+	}
+
+	txt = octet_string_str(hash);
+	if (txt) {
+		wpa_printf(MSG_DEBUG, "OpenSSL: calculated issuerNameHash: %s",
+			   txt);
+		os_free(txt);
+	}
+
+	if (ASN1_OCTET_STRING_cmp(certid->issuerNameHash, hash)) {
+		ASN1_OCTET_STRING_free(hash);
+		return -1;
+	}
+
+	ikey = X509_get0_pubkey_bitstr(issuer);
+	if (!ikey ||
+	    !EVP_Digest(ikey->data, ikey->length, md, &len, dgst, NULL) ||
+	    !ASN1_OCTET_STRING_set(hash, md, len)) {
+		ASN1_OCTET_STRING_free(hash);
+		return -1;
+	}
+
+	txt = octet_string_str(hash);
+	if (txt) {
+		wpa_printf(MSG_DEBUG, "OpenSSL: calculated issuerKeyHash: %s",
+			   txt);
+		os_free(txt);
+	}
+
+	if (ASN1_OCTET_STRING_cmp(certid->issuerKeyHash, hash)) {
+		ASN1_OCTET_STRING_free(hash);
+		return -1;
+	}
+
+	ASN1_OCTET_STRING_free(hash);
+	return 0;
+}
+
+
+static X509 * ocsp_find_signer(STACK_OF(X509) *certs, ResponderID *rid)
+{
+	unsigned int i;
+	unsigned char hash[SHA_DIGEST_LENGTH];
+
+	if (rid->type == 0) {
+		/* byName */
+		return X509_find_by_subject(certs, rid->value.byName);
+	}
+
+	/* byKey */
+	if (rid->value.byKey->length != SHA_DIGEST_LENGTH)
+		return NULL;
+	for (i = 0; i < sk_X509_num(certs); i++) {
+		X509 *x = sk_X509_value(certs, i);
+
+		X509_pubkey_digest(x, EVP_sha1(), hash, NULL);
+		if (os_memcmp(rid->value.byKey->data, hash,
+			      SHA_DIGEST_LENGTH) == 0)
+			return x;
+	}
+
+	return NULL;
+}
+
+
+enum ocsp_result check_ocsp_resp(SSL_CTX *ssl_ctx, SSL *ssl, X509 *cert,
+				 X509 *issuer, X509 *issuer_issuer)
+{
+	const uint8_t *resp_data;
+	size_t resp_len;
+	OCSPResponse *resp;
+	int status;
+	ResponseBytes *bytes;
+	const u8 *basic_data;
+	size_t basic_len;
+	BasicOCSPResponse *basic;
+	ResponseData *rd;
+	char *txt;
+	int i, num;
+	unsigned int j, num_resp;
+	SingleResponse *matching_resp = NULL, *cmp_sresp;
+	enum ocsp_result result = OCSP_INVALID;
+	X509_STORE *store;
+	STACK_OF(X509) *untrusted = NULL, *certs = NULL, *chain = NULL;
+	X509_STORE_CTX ctx;
+	X509 *signer, *tmp_cert;
+	int signer_trusted = 0;
+	EVP_PKEY *skey;
+	int ret;
+	char buf[256];
+
+	txt = integer_str(X509_get_serialNumber(cert));
+	if (txt) {
+		wpa_printf(MSG_DEBUG,
+			   "OpenSSL: Searching OCSP response for peer certificate serialNumber: %s", txt);
+		os_free(txt);
+	}
+
+	SSL_get0_ocsp_response(ssl, &resp_data, &resp_len);
+	if (resp_data == NULL || resp_len == 0) {
+		wpa_printf(MSG_DEBUG, "OpenSSL: No OCSP response received");
+		return OCSP_NO_RESPONSE;
+	}
+
+	wpa_hexdump(MSG_DEBUG, "OpenSSL: OCSP response", resp_data, resp_len);
+
+	resp = d2i_OCSPResponse(NULL, &resp_data, resp_len);
+	if (!resp) {
+		wpa_printf(MSG_INFO, "OpenSSL: Failed to parse OCSPResponse");
+		return OCSP_INVALID;
+	}
+
+	status = ASN1_ENUMERATED_get(resp->responseStatus);
+	if (status != 0) {
+		wpa_printf(MSG_INFO, "OpenSSL: OCSP responder error %d",
+			   status);
+		return OCSP_INVALID;
+	}
+
+	bytes = resp->responseBytes;
+
+	if (!bytes ||
+	    OBJ_obj2nid(bytes->responseType) != NID_id_pkix_OCSP_basic) {
+		wpa_printf(MSG_INFO,
+			   "OpenSSL: Could not find BasicOCSPResponse");
+		return OCSP_INVALID;
+	}
+
+	basic_data = ASN1_STRING_data(bytes->response);
+	basic_len = ASN1_STRING_length(bytes->response);
+	wpa_hexdump(MSG_DEBUG, "OpenSSL: BasicOCSPResponse",
+		    basic_data, basic_len);
+
+	basic = d2i_BasicOCSPResponse(NULL, &basic_data, basic_len);
+	if (!basic) {
+		wpa_printf(MSG_INFO,
+			   "OpenSSL: Could not parse BasicOCSPResponse");
+		OCSPResponse_free(resp);
+		return OCSP_INVALID;
+	}
+
+	rd = basic->tbsResponseData;
+
+	if (basic->certs) {
+		untrusted = sk_X509_dup(basic->certs);
+		if (!untrusted)
+			goto fail;
+
+		num = sk_X509_num(basic->certs);
+		for (i = 0; i < num; i++) {
+			X509 *extra_cert;
+
+			extra_cert = sk_X509_value(basic->certs, i);
+			X509_NAME_oneline(X509_get_subject_name(extra_cert),
+					  buf, sizeof(buf));
+			wpa_printf(MSG_DEBUG,
+				   "OpenSSL: BasicOCSPResponse cert %s", buf);
+
+			if (!sk_X509_push(untrusted, extra_cert)) {
+				wpa_printf(MSG_DEBUG,
+					   "OpenSSL: Could not add certificate to the untrusted stack");
+			}
+		}
+	}
+
+	store = SSL_CTX_get_cert_store(ssl_ctx);
+	if (issuer) {
+		if (X509_STORE_add_cert(store, issuer) != 1) {
+			tls_show_errors(MSG_INFO, __func__,
+					"OpenSSL: Could not add issuer to certificate store");
+		}
+		certs = sk_X509_new_null();
+		if (certs) {
+			tmp_cert = X509_dup(issuer);
+			if (tmp_cert && !sk_X509_push(certs, tmp_cert)) {
+				tls_show_errors(
+					MSG_INFO, __func__,
+					"OpenSSL: Could not add issuer to OCSP responder trust store");
+				X509_free(tmp_cert);
+				sk_X509_free(certs);
+				certs = NULL;
+			}
+			if (certs && issuer_issuer) {
+				tmp_cert = X509_dup(issuer_issuer);
+				if (tmp_cert &&
+				    !sk_X509_push(certs, tmp_cert)) {
+					tls_show_errors(
+						MSG_INFO, __func__,
+						"OpenSSL: Could not add issuer's issuer to OCSP responder trust store");
+					X509_free(tmp_cert);
+				}
+			}
+		}
+	}
+
+	signer = ocsp_find_signer(certs, rd->responderID);
+	if (!signer)
+		signer = ocsp_find_signer(untrusted, rd->responderID);
+	else
+		signer_trusted = 1;
+	if (!signer) {
+		wpa_printf(MSG_DEBUG,
+			   "OpenSSL: Could not find OCSP signer certificate");
+		goto fail;
+	}
+
+	skey = X509_get_pubkey(signer);
+	if (!skey) {
+		wpa_printf(MSG_DEBUG,
+			   "OpenSSL: Could not get OCSP signer public key");
+		goto fail;
+	}
+	if (ASN1_item_verify(ASN1_ITEM_rptr(ResponseData),
+			     basic->signatureAlgorithm, basic->signature,
+			     basic->tbsResponseData, skey) <= 0) {
+		wpa_printf(MSG_DEBUG,
+			   "OpenSSL: BasicOCSPResponse signature is invalid");
+		goto fail;
+	}
+
+	X509_NAME_oneline(X509_get_subject_name(signer), buf, sizeof(buf));
+	wpa_printf(MSG_DEBUG,
+		   "OpenSSL: Found OCSP signer certificate %s and verified BasicOCSPResponse signature",
+		   buf);
+
+	if (!X509_STORE_CTX_init(&ctx, store, signer, untrusted))
+		goto fail;
+	X509_STORE_CTX_set_purpose(&ctx, X509_PURPOSE_OCSP_HELPER);
+	ret = X509_verify_cert(&ctx);
+	chain = X509_STORE_CTX_get1_chain(&ctx);
+	X509_STORE_CTX_cleanup(&ctx);
+	if (ret <= 0) {
+		wpa_printf(MSG_DEBUG,
+			   "OpenSSL: Could not validate OCSP signer certificate");
+		goto fail;
+	}
+
+	if (!chain || sk_X509_num(chain) <= 0) {
+		wpa_printf(MSG_DEBUG, "OpenSSL: No OCSP signer chain found");
+		goto fail;
+	}
+
+	if (!signer_trusted) {
+		X509_check_purpose(signer, -1, 0);
+		if ((signer->ex_flags & EXFLAG_XKUSAGE) &&
+		    (signer->ex_xkusage & XKU_OCSP_SIGN)) {
+			wpa_printf(MSG_DEBUG,
+				   "OpenSSL: OCSP signer certificate delegation OK");
+		} else {
+			tmp_cert = sk_X509_value(chain, sk_X509_num(chain) - 1);
+			if (X509_check_trust(tmp_cert, NID_OCSP_sign, 0) !=
+			    X509_TRUST_TRUSTED) {
+				wpa_printf(MSG_DEBUG,
+					   "OpenSSL: OCSP signer certificate not trusted");
+				result = OCSP_NO_RESPONSE;
+				goto fail;
+			}
+		}
+	}
+
+	wpa_printf(MSG_DEBUG, "OpenSSL: OCSP version: %lu",
+		   ASN1_INTEGER_get(rd->version));
+
+	txt = responderid_str(rd->responderID);
+	if (txt) {
+		wpa_printf(MSG_DEBUG, "OpenSSL: OCSP responderID: %s",
+			   txt);
+		os_free(txt);
+	}
+
+	txt = generalizedtime_str(rd->producedAt);
+	if (txt) {
+		wpa_printf(MSG_DEBUG, "OpenSSL: OCSP producedAt: %s",
+			   txt);
+		os_free(txt);
+	}
+
+	num_resp = sk_SingleResponse_num(rd->responses);
+	if (num_resp == 0) {
+		wpa_printf(MSG_DEBUG,
+			   "OpenSSL: No OCSP SingleResponse within BasicOCSPResponse");
+		result = OCSP_NO_RESPONSE;
+		goto fail;
+	}
+	cmp_sresp = sk_SingleResponse_value(rd->responses, 0);
+	for (j = 0; j < num_resp; j++) {
+		SingleResponse *sresp;
+		CertID *cid1, *cid2;
+
+		sresp = sk_SingleResponse_value(rd->responses, j);
+		wpa_printf(MSG_DEBUG, "OpenSSL: OCSP SingleResponse %u/%u",
+			   j + 1, num_resp);
+
+		txt = algor_str(sresp->certID->hashAlgorithm);
+		if (txt) {
+			wpa_printf(MSG_DEBUG,
+				   "OpenSSL: certID hashAlgorithm: %s", txt);
+			os_free(txt);
+		}
+
+		txt = octet_string_str(sresp->certID->issuerNameHash);
+		if (txt) {
+			wpa_printf(MSG_DEBUG,
+				   "OpenSSL: certID issuerNameHash: %s", txt);
+			os_free(txt);
+		}
+
+		txt = octet_string_str(sresp->certID->issuerKeyHash);
+		if (txt) {
+			wpa_printf(MSG_DEBUG,
+				   "OpenSSL: certID issuerKeyHash: %s", txt);
+			os_free(txt);
+		}
+
+		txt = integer_str(sresp->certID->serialNumber);
+		if (txt) {
+			wpa_printf(MSG_DEBUG,
+				   "OpenSSL: certID serialNumber: %s", txt);
+			os_free(txt);
+		}
+
+		switch (sresp->certStatus->type) {
+		case 0:
+			wpa_printf(MSG_DEBUG, "OpenSSL: certStatus: good");
+			break;
+		case 1:
+			wpa_printf(MSG_DEBUG, "OpenSSL: certStatus: revoked");
+			break;
+		default:
+			wpa_printf(MSG_DEBUG, "OpenSSL: certStatus: unknown");
+			break;
+		}
+
+		txt = generalizedtime_str(sresp->thisUpdate);
+		if (txt) {
+			wpa_printf(MSG_DEBUG, "OpenSSL: thisUpdate: %s", txt);
+			os_free(txt);
+		}
+
+		if (sresp->nextUpdate) {
+			txt = generalizedtime_str(sresp->nextUpdate);
+			if (txt) {
+				wpa_printf(MSG_DEBUG, "OpenSSL: nextUpdate: %s",
+					   txt);
+				os_free(txt);
+			}
+		}
+
+		txt = extensions_str("singleExtensions",
+				     sresp->singleExtensions);
+		if (txt) {
+			wpa_printf(MSG_DEBUG, "OpenSSL: %s", txt);
+			os_free(txt);
+		}
+
+		cid1 = cmp_sresp->certID;
+		cid2 = sresp->certID;
+		if (j > 0 &&
+		    (OBJ_cmp(cid1->hashAlgorithm->algorithm,
+			     cid2->hashAlgorithm->algorithm) != 0 ||
+		     ASN1_OCTET_STRING_cmp(cid1->issuerNameHash,
+					   cid2->issuerNameHash) != 0 ||
+		     ASN1_OCTET_STRING_cmp(cid1->issuerKeyHash,
+					   cid2->issuerKeyHash) != 0)) {
+			wpa_printf(MSG_DEBUG,
+				   "OpenSSL: Different OCSP response issuer information between SingleResponse values within BasicOCSPResponse");
+			goto fail;
+		}
+
+		if (!matching_resp && issuer &&
+		    ASN1_INTEGER_cmp(sresp->certID->serialNumber,
+				     X509_get_serialNumber(cert)) == 0 &&
+		    issuer_match(cert, issuer, sresp->certID) == 0) {
+			wpa_printf(MSG_DEBUG,
+				   "OpenSSL: This response matches peer certificate");
+			matching_resp = sresp;
+		}
+	}
+
+	txt = extensions_str("responseExtensions", rd->responseExtensions);
+	if (txt) {
+		wpa_printf(MSG_DEBUG, "OpenSSL: %s", txt);
+		os_free(txt);
+	}
+
+	if (!matching_resp) {
+		wpa_printf(MSG_DEBUG,
+			   "OpenSSL: Could not find OCSP response that matches the peer certificate");
+		result = OCSP_NO_RESPONSE;
+		goto fail;
+	}
+
+	if (!ocsp_resp_valid(matching_resp->thisUpdate,
+			     matching_resp->nextUpdate)) {
+		wpa_printf(MSG_DEBUG,
+			   "OpenSSL: OCSP response not valid at this time");
+		goto fail;
+	}
+
+	if (matching_resp->certStatus->type == 1) {
+		wpa_printf(MSG_DEBUG,
+			   "OpenSSL: OCSP response indicated that the peer certificate has been revoked");
+		result = OCSP_REVOKED;
+		goto fail;
+	}
+
+	if (matching_resp->certStatus->type != 0) {
+		wpa_printf(MSG_DEBUG,
+			   "OpenSSL: OCSP response did not indicate good status");
+		result = OCSP_NO_RESPONSE;
+		goto fail;
+	}
+
+	/* OCSP response indicated the certificate is good. */
+	result = OCSP_GOOD;
+fail:
+	sk_X509_pop_free(chain, X509_free);
+	sk_X509_free(untrusted);
+	sk_X509_pop_free(certs, X509_free);
+	BasicOCSPResponse_free(basic);
+	OCSPResponse_free(resp);
+
+	return result;
+}
+
+#endif /* OPENSSL_IS_BORINGSSL */
diff --git a/src/drivers/driver.h b/src/drivers/driver.h
index 7fe736b..b7e0d16 100644
--- a/src/drivers/driver.h
+++ b/src/drivers/driver.h
@@ -45,6 +45,15 @@
 #define HOSTAPD_CHAN_INDOOR_ONLY 0x00010000
 #define HOSTAPD_CHAN_GO_CONCURRENT 0x00020000
 
+#define HOSTAPD_CHAN_VHT_10_150 0x00100000
+#define HOSTAPD_CHAN_VHT_30_130 0x00200000
+#define HOSTAPD_CHAN_VHT_50_110 0x00400000
+#define HOSTAPD_CHAN_VHT_70_90  0x00800000
+#define HOSTAPD_CHAN_VHT_90_70  0x01000000
+#define HOSTAPD_CHAN_VHT_110_50 0x02000000
+#define HOSTAPD_CHAN_VHT_130_30 0x04000000
+#define HOSTAPD_CHAN_VHT_150_10 0x08000000
+
 /**
  * enum reg_change_initiator - Regulatory change initiator
  */
@@ -284,6 +293,18 @@
 #define WPAS_MAX_SCAN_SSIDS 16
 
 /**
+ * struct wpa_driver_scan_ssid - SSIDs to scan for
+ * @ssid - specific SSID to scan for (ProbeReq)
+ *	%NULL or zero-length SSID is used to indicate active scan
+ *	with wildcard SSID.
+ * @ssid_len - Length of the SSID in octets
+ */
+struct wpa_driver_scan_ssid {
+	const u8 *ssid;
+	size_t ssid_len;
+};
+
+/**
  * struct wpa_driver_scan_params - Scan parameters
  * Data for struct wpa_driver_ops::scan2().
  */
@@ -291,18 +312,7 @@
 	/**
 	 * 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];
+	struct wpa_driver_scan_ssid ssids[WPAS_MAX_SCAN_SSIDS];
 
 	/**
 	 * num_ssids - Number of entries in ssids array
@@ -407,6 +417,37 @@
 	 */
 	const u8 *mac_addr_mask;
 
+	/**
+	 * sched_scan_plans - Scan plans for scheduled scan
+	 *
+	 * Each scan plan consists of the number of iterations to scan and the
+	 * interval between scans. When a scan plan finishes (i.e., it was run
+	 * for the specified number of iterations), the next scan plan is
+	 * executed. The scan plans are executed in the order they appear in
+	 * the array (lower index first). The last scan plan will run infinitely
+	 * (until requested to stop), thus must not specify the number of
+	 * iterations. All other scan plans must specify the number of
+	 * iterations.
+	 */
+	struct sched_scan_plan {
+		 u32 interval; /* In seconds */
+		 u32 iterations; /* Zero to run infinitely */
+	 } *sched_scan_plans;
+
+	/**
+	 * sched_scan_plans_num - Number of scan plans in sched_scan_plans array
+	 */
+	 unsigned int sched_scan_plans_num;
+
+	/**
+	 * bssid - Specific BSSID to scan for
+	 *
+	 * This optional parameter can be used to replace the default wildcard
+	 * BSSID with a specific BSSID to scan for if results are needed from
+	 * only a single BSS.
+	 */
+	const u8 *bssid;
+
 	/*
 	 * NOTE: Whenever adding new parameters here, please make sure
 	 * wpa_scan_clone_params() and wpa_scan_free_params() get updated with
@@ -828,6 +869,12 @@
 	 * RRM (Radio Resource Measurements)
 	 */
 	int rrm_used;
+
+	/**
+	 * pbss - If set, connect to a PCP in a PBSS. Otherwise, connect to an
+	 * AP as usual. Valid for DMG network only.
+	 */
+	int pbss;
 };
 
 enum hide_ssid {
@@ -1055,6 +1102,12 @@
 	 * reenable - Whether this is to re-enable beaconing
 	 */
 	int reenable;
+
+	/**
+	 * pbss - Whether to start a PCP (in PBSS) instead of an AP in
+	 * infrastructure BSS. Valid only for DMG network.
+	 */
+	int pbss;
 };
 
 struct wpa_driver_mesh_bss_params {
@@ -1214,8 +1267,15 @@
 #define WPA_DRIVER_FLAGS_VHT_IBSS		0x0000002000000000ULL
 /** Driver supports automatic band selection */
 #define WPA_DRIVER_FLAGS_SUPPORT_HW_MODE_ANY	0x0000004000000000ULL
+/** Driver supports simultaneous off-channel operations */
+#define WPA_DRIVER_FLAGS_OFFCHANNEL_SIMULTANEOUS	0x0000008000000000ULL
+/** Driver supports full AP client state */
+#define WPA_DRIVER_FLAGS_FULL_AP_CLIENT_STATE	0x0000010000000000ULL
 	u64 flags;
 
+#define FULL_AP_CLIENT_STATE_SUPP(drv_flags) \
+	(drv_flags & WPA_DRIVER_FLAGS_FULL_AP_CLIENT_STATE)
+
 #define WPA_DRIVER_SMPS_MODE_STATIC			0x00000001
 #define WPA_DRIVER_SMPS_MODE_DYNAMIC			0x00000002
 	unsigned int smps_modes;
@@ -1231,6 +1291,15 @@
 	/** Maximum number of supported active probe SSIDs for sched_scan */
 	int max_sched_scan_ssids;
 
+	/** Maximum number of supported scan plans for scheduled scan */
+	unsigned int max_sched_scan_plans;
+
+	/** Maximum interval in a scan plan. In seconds */
+	u32 max_sched_scan_plan_interval;
+
+	/** Maximum number of iterations in a single scan plan */
+	u32 max_sched_scan_plan_iterations;
+
 	/** Whether sched_scan (offloaded scanning) is supported */
 	int sched_scan_supported;
 
@@ -1297,13 +1366,25 @@
  */
 #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;
+
+	/* Maximum number of supported CSA counters */
+	u16 max_csa_counters;
 };
 
 
 struct hostapd_data;
 
 struct hostap_sta_driver_data {
-	unsigned long rx_packets, tx_packets, rx_bytes, tx_bytes;
+	unsigned long rx_packets, tx_packets;
+	unsigned long long rx_bytes, tx_bytes;
+	int bytes_64bit; /* whether 64-bit byte counters are supported */
 	unsigned long current_tx_rate;
 	unsigned long inactive_msec;
 	unsigned long flags;
@@ -1397,6 +1478,16 @@
 	 * 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 {
@@ -1433,6 +1524,7 @@
 #define WPA_STA_MFP BIT(3)
 #define WPA_STA_TDLS_PEER BIT(4)
 #define WPA_STA_AUTHENTICATED BIT(5)
+#define WPA_STA_ASSOCIATED BIT(6)
 
 enum tdls_oper {
 	TDLS_DISCOVERY_REQ,
@@ -1537,8 +1629,8 @@
 	struct beacon_data beacon_csa;
 	struct beacon_data beacon_after;
 
-	u16 counter_offset_beacon;
-	u16 counter_offset_presp;
+	u16 counter_offset_beacon[2];
+	u16 counter_offset_presp[2];
 };
 
 /* TDLS peer capabilities for send_tdls_mgmt() */
@@ -1602,6 +1694,7 @@
 	/* ACS channel list info */
 	unsigned int ch_list_len;
 	const u8 *ch_list;
+	const int *freq_list;
 };
 
 
@@ -1865,6 +1958,14 @@
 	void (*poll)(void *priv);
 
 	/**
+	 * get_ifindex - Get interface index
+	 * @priv: private driver interface data
+	 *
+	 * Returns: Interface index
+	 */
+	unsigned int (*get_ifindex)(void *priv);
+
+	/**
 	 * get_ifname - Get interface name
 	 * @priv: private driver interface data
 	 *
@@ -1942,10 +2043,13 @@
 	 * @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
+	 * @csa_offs: Array of CSA offsets or %NULL
+	 * @csa_offs_len: Number of elements in csa_offs
 	 * Returns: 0 on success, -1 on failure
 	 */
 	int (*send_mlme)(void *priv, const u8 *data, size_t data_len,
-			 int noack, unsigned int freq);
+			 int noack, unsigned int freq, const u16 *csa_offs,
+			 size_t csa_offs_len);
 
 	/**
 	 * update_ft_ies - Update FT (IEEE 802.11r) IEs
@@ -1995,6 +2099,7 @@
 
 	/**
 	 * global_init - Global driver initialization
+	 * @ctx: wpa_global pointer
 	 * Returns: Pointer to private data (global), %NULL on failure
 	 *
 	 * This optional function is called to initialize the driver wrapper
@@ -2004,7 +2109,7 @@
 	 * use init2() function instead of init() to get the pointer to global
 	 * data available to per-interface initializer.
 	 */
-	void * (*global_init)(void);
+	void * (*global_init)(void *ctx);
 
 	/**
 	 * global_deinit - Global driver deinitialization
@@ -2290,12 +2395,17 @@
 	 * @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
+	 * This function is used to add or set (params->set 1) a station
+	 * entry in the driver. Adding STA entries is used only 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.
+	 * With drivers that don't support full AP client state, this function
+	 * is used to add a station entry to the driver once the station has
+	 * completed association.
+	 *
+	 * With TDLS, this function is used to add or set (params->set 1)
+	 * TDLS peer entries (even with drivers that do not support full AP
+	 * client state).
 	 */
 	int (*sta_add)(void *priv, struct hostapd_sta_add_params *params);
 
@@ -2349,7 +2459,8 @@
 	 * Returns: 0 on success, -1 on failure
 	 */
 	int (*sta_set_flags)(void *priv, const u8 *addr,
-			     int total_flags, int flags_or, int flags_and);
+			     unsigned int total_flags, unsigned int flags_or,
+			     unsigned int flags_and);
 
 	/**
 	 * set_tx_queue_params - Set TX queue parameters
@@ -2380,12 +2491,13 @@
 	 *	change interface address)
 	 * @bridge: Bridge interface to use or %NULL if no bridge configured
 	 * @use_existing: Whether to allow existing interface to be used
+	 * @setup_ap: Whether to setup AP for %WPA_IF_AP_BSS interfaces
 	 * 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);
+		      const char *bridge, int use_existing, int setup_ap);
 
 	/**
 	 * if_remove - Remove a virtual interface
@@ -2967,7 +3079,6 @@
 	 * 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
@@ -2978,8 +3089,7 @@
 	 * 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);
+	int (*sched_scan)(void *priv, struct wpa_driver_scan_params *params);
 
 	/**
 	 * stop_sched_scan - Request the driver to stop a scheduled scan
@@ -3394,6 +3504,39 @@
 	 * 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);
+
+	/**
+	 * abort_scan - Request the driver to abort an ongoing scan
+	 * @priv: Private driver interface data
+	 * Returns 0 on success, -1 on failure
+	 */
+	int (*abort_scan)(void *priv);
 };
 
 
@@ -4053,6 +4196,12 @@
 		 * ptk_kek_len - The length of ptk_kek
 		 */
 		size_t ptk_kek_len;
+
+		/**
+		 * subnet_status - The subnet status:
+		 * 0 = unknown, 1 = unchanged, 2 = changed
+		 */
+		u8 subnet_status;
 	} assoc_info;
 
 	/**
@@ -4129,6 +4278,7 @@
 	 * struct interface_status - Data for EVENT_INTERFACE_STATUS
 	 */
 	struct interface_status {
+		unsigned int ifindex;
 		char ifname[100];
 		enum {
 			EVENT_INTERFACE_ADDED, EVENT_INTERFACE_REMOVED
@@ -4336,6 +4486,9 @@
 	 * @ssids: Scanned SSIDs (%NULL or zero-length SSID indicates wildcard
 	 *	SSID)
 	 * @num_ssids: Number of entries in ssids array
+	 * @external_scan: Whether the scan info is for an external scan
+	 * @nl_scan_event: 1 if the source of this scan event is a normal scan,
+	 * 	0 if the source of the scan event is a vendor scan
 	 */
 	struct scan_info {
 		int aborted;
@@ -4343,6 +4496,8 @@
 		size_t num_freqs;
 		struct wpa_driver_scan_ssid ssids[WPAS_MAX_SCAN_SSIDS];
 		size_t num_ssids;
+		int external_scan;
+		int nl_scan_event;
 	} scan_info;
 
 	/**
@@ -4600,6 +4755,18 @@
 void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
 			  union wpa_event_data *data);
 
+/**
+ * wpa_supplicant_event_global - 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
+ *
+ * Same as wpa_supplicant_event(), but we search for the interface in
+ * wpa_global.
+ */
+void wpa_supplicant_event_global(void *ctx, enum wpa_event_type event,
+				 union wpa_event_data *data);
 
 /*
  * The following inline functions are provided for convenience to simplify
diff --git a/src/drivers/driver_atheros.c b/src/drivers/driver_atheros.c
index b721c32..ba3cad0 100644
--- a/src/drivers/driver_atheros.c
+++ b/src/drivers/driver_atheros.c
@@ -55,6 +55,10 @@
 #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 */
@@ -185,13 +189,13 @@
 	    op == IEEE80211_IOCTL_FILTERFRAME)
 		do_inline = 0;
 
-	memset(&iwr, 0, sizeof(iwr));
+	os_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);
+		os_memcpy(iwr.u.name, data, len);
 	} else {
 		/*
 		 * Argument data too big for inline transfer; setup a
@@ -218,10 +222,10 @@
 {
 	struct iwreq iwr;
 
-	memset(&iwr, 0, sizeof(iwr));
+	os_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));
+	os_memcpy(iwr.u.name + sizeof(__u32), &arg, sizeof(arg));
 
 	if (ioctl(drv->ioctl_sock, IEEE80211_IOCTL_SETPARAM, &iwr) < 0) {
 		wpa_printf(MSG_INFO,
@@ -240,9 +244,9 @@
 	static char buf[sizeof(MACSTR)];
 
 	if (addr != NULL)
-		snprintf(buf, sizeof(buf), MACSTR, MAC2STR(addr));
+		os_snprintf(buf, sizeof(buf), MACSTR, MAC2STR(addr));
 	else
-		snprintf(buf, sizeof(buf), MACSTR, 0,0,0,0,0,0);
+		os_snprintf(buf, sizeof(buf), MACSTR, 0, 0, 0, 0, 0, 0);
 	return buf;
 }
 #endif /* CONFIG_NO_STDOUT_DEBUG */
@@ -418,7 +422,7 @@
 	else
 		mlme.im_op = IEEE80211_MLME_UNAUTHORIZE;
 	mlme.im_reason = 0;
-	memcpy(mlme.im_macaddr, addr, IEEE80211_ADDR_LEN);
+	os_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,
@@ -430,7 +434,8 @@
 
 static int
 atheros_sta_set_flags(void *priv, const u8 *addr,
-		      int total_flags, int flags_or, int flags_and)
+		      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)
@@ -450,9 +455,9 @@
 	wpa_printf(MSG_DEBUG, "%s: addr=%s key_idx=%d",
 		   __func__, ether_sprintf(addr), key_idx);
 
-	memset(&wk, 0, sizeof(wk));
+	os_memset(&wk, 0, sizeof(wk));
 	if (addr != NULL) {
-		memcpy(wk.idk_macaddr, addr, IEEE80211_ADDR_LEN);
+		os_memcpy(wk.idk_macaddr, addr, IEEE80211_ADDR_LEN);
 		wk.idk_keyix = (u8) IEEE80211_KEYIX_NONE;
 	} else {
 		wk.idk_keyix = key_idx;
@@ -533,20 +538,20 @@
 		return -3;
 	}
 
-	memset(&wk, 0, sizeof(wk));
+	os_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);
+		os_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);
+		os_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);
+	os_memcpy(wk.ik_keydata, key, key_len);
 
 	ret = set80211priv(drv, IEEE80211_IOCTL_SETKEY, &wk, sizeof(wk));
 	if (ret < 0) {
@@ -570,11 +575,11 @@
 	wpa_printf(MSG_DEBUG, "%s: addr=%s idx=%d",
 		   __func__, ether_sprintf(addr), idx);
 
-	memset(&wk, 0, sizeof(wk));
+	os_memset(&wk, 0, sizeof(wk));
 	if (addr == NULL)
-		memset(wk.ik_macaddr, 0xff, IEEE80211_ADDR_LEN);
+		os_memset(wk.ik_macaddr, 0xff, IEEE80211_ADDR_LEN);
 	else
-		memcpy(wk.ik_macaddr, addr, IEEE80211_ADDR_LEN);
+		os_memcpy(wk.ik_macaddr, addr, IEEE80211_ADDR_LEN);
 	wk.ik_keyix = idx;
 
 	if (set80211priv(drv, IEEE80211_IOCTL_GETKEY, &wk, sizeof(wk))) {
@@ -595,13 +600,13 @@
 #define WPA_KEY_RSC_LEN 8
 #endif
 		u8 tmp[WPA_KEY_RSC_LEN];
-		memcpy(tmp, &wk.ik_keytsc, sizeof(wk.ik_keytsc));
+		os_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));
+	os_memcpy(seq, &wk.ik_keytsc, sizeof(wk.ik_keytsc));
 #endif /* WORDS_BIGENDIAN */
 	return 0;
 }
@@ -611,7 +616,7 @@
 atheros_flush(void *priv)
 {
 	u8 allsta[IEEE80211_ADDR_LEN];
-	memset(allsta, 0xff, IEEE80211_ADDR_LEN);
+	os_memset(allsta, 0xff, IEEE80211_ADDR_LEN);
 	return atheros_sta_deauth(priv, NULL, allsta,
 				  IEEE80211_REASON_AUTH_LEAVE);
 }
@@ -624,19 +629,19 @@
 	struct atheros_driver_data *drv = priv;
 	struct ieee80211req_sta_stats stats;
 
-	memset(data, 0, sizeof(*data));
+	os_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);
+	os_memset(&stats, 0, sizeof(stats));
+	os_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));
+		if (os_memcmp(addr, drv->acct_mac, ETH_ALEN) == 0) {
+			os_memcpy(data, &drv->acct_data, sizeof(*data));
 			return 0;
 		}
 
@@ -663,7 +668,7 @@
 	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);
+	os_memcpy(mlme.im_macaddr, addr, IEEE80211_ADDR_LEN);
 	ret = set80211priv(drv, IEEE80211_IOCTL_SETMLME, &mlme,
 			   sizeof(mlme));
 	if (ret < 0) {
@@ -739,7 +744,7 @@
 
 	mlme.im_op = IEEE80211_MLME_DEAUTH;
 	mlme.im_reason = reason_code;
-	memcpy(mlme.im_macaddr, addr, IEEE80211_ADDR_LEN);
+	os_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
@@ -763,7 +768,7 @@
 
 	mlme.im_op = IEEE80211_MLME_DISASSOC;
 	mlme.im_reason = reason_code;
-	memcpy(mlme.im_macaddr, addr, IEEE80211_ADDR_LEN);
+	os_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 "
@@ -789,7 +794,7 @@
 		wpa_printf(MSG_ERROR, "Invalid QoS Map");
 		return -1;
 	} else {
-		memset(&req, 0, sizeof(struct ieee80211req_athdbg));
+		os_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));
@@ -823,7 +828,7 @@
 	return 0;
 }
 
-#if defined(CONFIG_WPS) || defined(CONFIG_IEEE80211R) || defined(CONFIG_WNM) || defined(CONFIG_HS20)
+#ifdef ATHEROS_USE_RAW_RECEIVE
 static void atheros_raw_receive(void *ctx, const u8 *src_addr, const u8 *buf,
 				size_t len)
 {
@@ -850,16 +855,15 @@
 		   (int) len);
 
 	if (stype == WLAN_FC_STYPE_PROBE_REQ) {
-		if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.probe_req))
+		if (len < IEEE80211_HDRLEN)
 			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));
+		event.rx_probe_req.ie = buf + IEEE80211_HDRLEN;
+		event.rx_probe_req.ie_len = len - IEEE80211_HDRLEN;
 		wpa_supplicant_event(drv->hapd, EVENT_RX_PROBE_REQ, &event);
 		return;
 	}
@@ -911,7 +915,7 @@
 		break;
 	}
 }
-#endif
+#endif /* ATHEROS_USE_RAW_RECEIVE */
 
 static int atheros_receive_pkt(struct atheros_driver_data *drv)
 {
@@ -923,11 +927,11 @@
 #ifdef CONFIG_WPS
 	filt.app_filterype |= IEEE80211_FILTER_TYPE_PROBE_REQ;
 #endif /* CONFIG_WPS */
-#ifdef CONFIG_IEEE80211R
+#if defined(CONFIG_IEEE80211W) || defined(CONFIG_IEEE80211R)
 	filt.app_filterype |= (IEEE80211_FILTER_TYPE_ASSOC_REQ |
 			       IEEE80211_FILTER_TYPE_AUTH |
 			       IEEE80211_FILTER_TYPE_ACTION);
-#endif
+#endif /* CONFIG_IEEE80211R || CONFIG_IEEE80211W */
 #ifdef CONFIG_WNM
 	filt.app_filterype |= IEEE80211_FILTER_TYPE_ACTION;
 #endif /* CONFIG_WNM */
@@ -1026,7 +1030,7 @@
 #define atheros_set_ap_wps_ie NULL
 #endif /* CONFIG_WPS */
 
-#ifdef CONFIG_IEEE80211R
+#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)
@@ -1102,7 +1106,7 @@
 	}
 	return ret;
 }
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R || CONFIG_IEEE80211W */
 
 static void
 atheros_new_sta(struct atheros_driver_data *drv, u8 addr[IEEE80211_ADDR_LEN])
@@ -1115,8 +1119,8 @@
 	/*
 	 * Fetch negotiated WPA/RSN parameters from the system.
 	 */
-	memset(&ie, 0, sizeof(ie));
-	memcpy(ie.wpa_macaddr, addr, IEEE80211_ADDR_LEN);
+	os_memset(&ie, 0, sizeof(ie));
+	os_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
@@ -1166,10 +1170,10 @@
 no_ie:
 	drv_event_assoc(hapd, addr, iebuf, ielen, 0);
 
-	if (memcmp(addr, drv->acct_mac, ETH_ALEN) == 0) {
+	if (os_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));
+		os_memset(drv->acct_mac, 0, ETH_ALEN);
+		os_memset(&drv->acct_data, 0, sizeof(drv->acct_data));
 	}
 }
 
@@ -1177,12 +1181,13 @@
 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) {
+	if (os_strncmp(custom, "MLME-MICHAELMICFAILURE.indication", 33) == 0) {
 		char *pos;
 		u8 addr[ETH_ALEN];
-		pos = strstr(custom, "addr=");
+		pos = os_strstr(custom, "addr=");
 		if (pos == NULL) {
 			wpa_printf(MSG_DEBUG,
 				   "MLME-MICHAELMICFAILURE.indication "
@@ -1206,36 +1211,33 @@
 		char *key, *value;
 		u32 val;
 		key = custom;
-		while ((key = strchr(key, '\n')) != NULL) {
+		while ((key = os_strchr(key, '\n')) != NULL) {
 			key++;
-			value = strchr(key, '=');
+			value = os_strchr(key, '=');
 			if (value == NULL)
 				continue;
 			*value++ = '\0';
 			val = strtoul(value, NULL, 10);
-			if (strcmp(key, "mac") == 0)
+			if (os_strcmp(key, "mac") == 0)
 				hwaddr_aton(value, drv->acct_mac);
-			else if (strcmp(key, "rx_packets") == 0)
+			else if (os_strcmp(key, "rx_packets") == 0)
 				drv->acct_data.rx_packets = val;
-			else if (strcmp(key, "tx_packets") == 0)
+			else if (os_strcmp(key, "tx_packets") == 0)
 				drv->acct_data.tx_packets = val;
-			else if (strcmp(key, "rx_bytes") == 0)
+			else if (os_strcmp(key, "rx_bytes") == 0)
 				drv->acct_data.rx_bytes = val;
-			else if (strcmp(key, "tx_bytes") == 0)
+			else if (os_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) {
+	} else if (os_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);
-#endif /* CONFIG_WPS */
-#if defined(CONFIG_WPS) || defined(CONFIG_IEEE80211R) || defined(CONFIG_HS20)
-#define MGMT_FRAM_TAG_SIZE 30 /* hardcoded in driver */
-	} else if (strncmp(custom, "Manage.prob_req ", 16) == 0) {
+	} else if (os_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
@@ -1243,47 +1245,53 @@
 		 * Format: "Manage.prob_req <frame len>" | zero padding | frame
 		 */
 		int len = atoi(custom + 16);
-		if (len < 0 || custom + MGMT_FRAM_TAG_SIZE + len > end) {
+		if (len < 0 || MGMT_FRAM_TAG_SIZE + len > end - custom) {
 			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);
-	} else if (strncmp(custom, "Manage.assoc_req ", 17) == 0) {
+#endif /* CONFIG_WPS */
+#if defined(CONFIG_IEEE80211R) || defined(CONFIG_IEEE80211W)
+	} else if (os_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.prob_req/"
-				   "assoc_req/auth event length %d", len);
+		if (len < 0 || MGMT_FRAM_TAG_SIZE + len > end - custom) {
+			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.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.prob_req/"
-				   "assoc_req/auth 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
-		 */
+		} else if (os_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.prob_req/"
-				   "assoc_req/auth event length %d", len);
+			if (len < 0 ||
+			    MGMT_FRAM_TAG_SIZE + len > end - custom) {
+			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_WPS or CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211W || CONFIG_IEEE80211R */
+#ifdef ATHEROS_USE_RAW_RECEIVE
+		} else if (os_strncmp(custom, "Manage.action ", 14) == 0) {
+		/* Format: "Manage.assoc_req <frame len>" | zero padding | frame
+		 */
+		int len = atoi(custom + 14);
+		if (len < 0 || MGMT_FRAM_TAG_SIZE + len > end - custom) {
+			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 */
 	}
 }
 
@@ -1375,7 +1383,7 @@
 
 static void
 atheros_wireless_event_wireless(struct atheros_driver_data *drv,
-				char *data, int len)
+				char *data, unsigned int len)
 {
 	struct iw_event iwe_buf, *iwe = &iwe_buf;
 	char *pos, *end, *custom, *buf;
@@ -1383,13 +1391,13 @@
 	pos = data;
 	end = data + len;
 
-	while (pos + IW_EV_LCP_LEN <= end) {
+	while ((size_t) (end - pos) >= IW_EV_LCP_LEN) {
 		/* Event data may be unaligned, so make a local, aligned copy
 		 * before processing. */
-		memcpy(&iwe_buf, pos, IW_EV_LCP_LEN);
+		os_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)
+		if (iwe->len <= IW_EV_LCP_LEN || iwe->len > end - pos)
 			return;
 
 		custom = pos + IW_EV_POINT_LEN;
@@ -1400,10 +1408,10 @@
 			/* 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);
+			os_memcpy(dpos, pos + IW_EV_LCP_LEN,
+				  sizeof(struct iw_event) - dlen);
 		} else {
-			memcpy(&iwe_buf, pos, sizeof(struct iw_event));
+			os_memcpy(&iwe_buf, pos, sizeof(struct iw_event));
 			custom += IW_EV_POINT_OFF;
 		}
 
@@ -1421,12 +1429,12 @@
 			 * just like IWEVCUSTOM.
 			 */
 		case IWEVCUSTOM:
-			if (custom + iwe->u.data.length > end)
+			if (iwe->u.data.length > end - custom)
 				return;
-			buf = malloc(iwe->u.data.length + 1);
+			buf = os_malloc(iwe->u.data.length + 1);
 			if (buf == NULL)
 				return;		/* XXX */
-			memcpy(buf, custom, iwe->u.data.length);
+			os_memcpy(buf, custom, iwe->u.data.length);
 			buf[iwe->u.data.length] = '\0';
 
 			if (iwe->u.data.flags != 0) {
@@ -1437,7 +1445,7 @@
 				atheros_wireless_event_wireless_custom(
 					drv, buf, buf + iwe->u.data.length);
 			}
-			free(buf);
+			os_free(buf);
 			break;
 		}
 
@@ -1491,7 +1499,7 @@
 	if (range == NULL)
 		return -1;
 
-	memset(&iwr, 0, sizeof(iwr));
+	os_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;
@@ -1560,7 +1568,7 @@
 	 */
 	len = data_len + sizeof(struct l2_ethhdr);
 	if (len > sizeof(buf)) {
-		bp = malloc(len);
+		bp = os_malloc(len);
 		if (bp == NULL) {
 			wpa_printf(MSG_INFO,
 				   "EAPOL frame discarded, cannot malloc temp buffer of size %lu!",
@@ -1569,17 +1577,17 @@
 		}
 	}
 	eth = (struct l2_ethhdr *) bp;
-	memcpy(eth->h_dest, addr, ETH_ALEN);
-	memcpy(eth->h_source, own_addr, ETH_ALEN);
+	os_memcpy(eth->h_dest, addr, ETH_ALEN);
+	os_memcpy(eth->h_source, own_addr, ETH_ALEN);
 	eth->h_proto = host_to_be16(ETH_P_EAPOL);
-	memcpy(eth+1, data, data_len);
+	os_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);
+		os_free(bp);
 	return status;
 }
 
@@ -1613,9 +1621,9 @@
 			   strerror(errno));
 		goto bad;
 	}
-	memcpy(drv->iface, params->ifname, sizeof(drv->iface));
+	os_memcpy(drv->iface, params->ifname, sizeof(drv->iface));
 
-	memset(&ifr, 0, sizeof(ifr));
+	os_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",
@@ -1649,7 +1657,7 @@
 	} else
 		drv->sock_recv = drv->sock_xmit;
 
-	memset(&iwr, 0, sizeof(iwr));
+	os_memset(&iwr, 0, sizeof(iwr));
 	os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ);
 
 	iwr.u.mode = IW_MODE_MASTER;
@@ -1695,10 +1703,10 @@
 	atheros_reset_appfilter(drv);
 
 	if (drv->wpa_ie || drv->wps_beacon_ie || drv->wps_probe_resp_ie) {
+		atheros_set_opt_ie(priv, NULL, 0);
 		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);
@@ -1719,7 +1727,7 @@
 	struct atheros_driver_data *drv = priv;
 	struct iwreq iwr;
 
-	memset(&iwr, 0, sizeof(iwr));
+	os_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;
@@ -1740,7 +1748,7 @@
 	struct iwreq iwr;
 	int ret = 0;
 
-	memset(&iwr, 0, sizeof(iwr));
+	os_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) ?
@@ -1836,10 +1844,11 @@
 }
 
 
-#ifdef CONFIG_IEEE80211R
+#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)
+			     int noack, unsigned int freq,
+			     const u16 *csa_offs, size_t csa_offs_len)
 {
 	struct atheros_driver_data *drv = priv;
 	u8 buf[1510];
@@ -1850,7 +1859,7 @@
 	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);
+	os_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 "
@@ -1861,8 +1870,11 @@
 	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)
 {
@@ -2142,10 +2154,12 @@
 	.set_ap_wps_ie		= atheros_set_ap_wps_ie,
 	.set_authmode		= atheros_set_authmode,
 	.set_ap			= atheros_set_ap,
-#ifdef CONFIG_IEEE80211R
+#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 */
diff --git a/src/drivers/driver_bsd.c b/src/drivers/driver_bsd.c
index 9ac87b4..fd73f2e 100644
--- a/src/drivers/driver_bsd.c
+++ b/src/drivers/driver_bsd.c
@@ -47,14 +47,25 @@
 
 #include "l2_packet/l2_packet.h"
 
+struct bsd_driver_global {
+	void		*ctx;
+	int		sock;			/* socket for 802.11 ioctls */
+	int		route;			/* routing socket for events */
+	char		*event_buf;
+	size_t		event_buf_len;
+	struct dl_list	ifaces;			/* list of interfaces */
+};
+
 struct bsd_driver_data {
+	struct dl_list	list;
+	struct bsd_driver_global *global;
 	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 */
+	int	flags;
 	unsigned int ifindex;		/* interface index */
+	int	if_removed;		/* has the interface been removed? */
 	void	*ctx;
 	struct wpa_driver_capa capa;	/* driver capability */
 	int	is_ap;			/* Access point mode */
@@ -62,18 +73,47 @@
 	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 struct bsd_driver_data *
+bsd_get_drvindex(void *priv, unsigned int ifindex)
+{
+	struct bsd_driver_global *global = priv;
+	struct bsd_driver_data *drv;
+
+	dl_list_for_each(drv, &global->ifaces, struct bsd_driver_data, list) {
+		if (drv->ifindex == ifindex)
+			return drv;
+	}
+	return NULL;
+}
+
+#ifndef HOSTAPD
+static struct bsd_driver_data *
+bsd_get_drvname(void *priv, const char *ifname)
+{
+	struct bsd_driver_global *global = priv;
+	struct bsd_driver_data *drv;
+
+	dl_list_for_each(drv, &global->ifaces, struct bsd_driver_data, list) {
+		if (os_strcmp(drv->ifname, ifname) == 0)
+			return drv;
+	}
+	return NULL;
+}
+#endif /* HOSTAPD */
+
 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;
 
+	if (drv->ifindex == 0 || drv->if_removed)
+		return -1;
+
 	os_memset(&ireq, 0, sizeof(ireq));
 	os_strlcpy(ireq.i_name, drv->ifname, sizeof(ireq.i_name));
 	ireq.i_type = op;
@@ -81,7 +121,7 @@
 	ireq.i_data = (void *) arg;
 	ireq.i_len = arg_len;
 
-	if (ioctl(drv->sock, SIOCS80211, &ireq) < 0) {
+	if (ioctl(drv->global->sock, SIOCS80211, &ireq) < 0) {
 		wpa_printf(MSG_ERROR, "ioctl[SIOCS80211, op=%u, val=%u, "
 			   "arg_len=%u]: %s", op, val, arg_len,
 			   strerror(errno));
@@ -102,7 +142,7 @@
 	ireq->i_len = arg_len;
 	ireq->i_data = arg;
 
-	if (ioctl(drv->sock, SIOCG80211, ireq) < 0) {
+	if (ioctl(drv->global->sock, SIOCG80211, ireq) < 0) {
 		wpa_printf(MSG_ERROR, "ioctl[SIOCS80211, op=%u, "
 			   "arg_len=%u]: %s", op, arg_len, strerror(errno));
 		return -1;
@@ -143,7 +183,7 @@
 	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 ||
+	if (ioctl(drv->global->sock, SIOCG80211NWID, &ifr) < 0 ||
 	    nwid.i_len > IEEE80211_NWID_LEN)
 		return -1;
 	os_memcpy(ssid, nwid.i_nwid, nwid.i_len);
@@ -166,7 +206,7 @@
 	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);
+	return ioctl(drv->global->sock, SIOCS80211NWID, &ifr);
 #else
 	return set80211var(drv, IEEE80211_IOC_SSID, ssid, ssid_len);
 #endif
@@ -181,7 +221,7 @@
 	os_memset(&ifmr, 0, sizeof(ifmr));
 	os_strlcpy(ifmr.ifm_name, drv->ifname, sizeof(ifmr.ifm_name));
 
-	if (ioctl(drv->sock, SIOCGIFMEDIA, &ifmr) < 0) {
+	if (ioctl(drv->global->sock, SIOCGIFMEDIA, &ifmr) < 0) {
 		wpa_printf(MSG_ERROR, "%s: SIOCGIFMEDIA %s", __func__,
 			   strerror(errno));
 		return -1;
@@ -200,7 +240,7 @@
 	os_strlcpy(ifr.ifr_name, drv->ifname, sizeof(ifr.ifr_name));
 	ifr.ifr_media = media;
 
-	if (ioctl(drv->sock, SIOCSIFMEDIA, &ifr) < 0) {
+	if (ioctl(drv->global->sock, SIOCSIFMEDIA, &ifr) < 0) {
 		wpa_printf(MSG_ERROR, "%s: SIOCSIFMEDIA %s", __func__,
 			   strerror(errno));
 		return -1;
@@ -263,11 +303,12 @@
 	os_memset(&ifr, 0, sizeof(ifr));
 	os_strlcpy(ifr.ifr_name, drv->ifname, sizeof(ifr.ifr_name));
 
-	if (ioctl(drv->sock, SIOCGIFFLAGS, &ifr) < 0) {
+	if (ioctl(drv->global->sock, SIOCGIFFLAGS, &ifr) < 0) {
 		wpa_printf(MSG_ERROR, "ioctl[SIOCGIFFLAGS]: %s",
 			   strerror(errno));
 		return -1;
 	}
+	drv->flags = ifr.ifr_flags;
 
 	if (enable) {
 		if (ifr.ifr_flags & IFF_UP)
@@ -279,12 +320,13 @@
 		ifr.ifr_flags &= ~IFF_UP;
 	}
 
-	if (ioctl(drv->sock, SIOCSIFFLAGS, &ifr) < 0) {
+	if (ioctl(drv->global->sock, SIOCSIFFLAGS, &ifr) < 0) {
 		wpa_printf(MSG_ERROR, "ioctl[SIOCSIFFLAGS]: %s",
 			   strerror(errno));
 		return -1;
 	}
 
+	drv->flags = ifr.ifr_flags;
 	return 0;
 }
 
@@ -576,7 +618,7 @@
 	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);
+	return ioctl(drv->global->sock, SIOCS80211CHANNEL, &creq);
 #else /* SIOCS80211CHANNEL */
 	return set80211param(priv, IEEE80211_IOC_CHANNEL, channel);
 #endif /* SIOCS80211CHANNEL */
@@ -730,7 +772,8 @@
 static void
 bsd_wireless_event_receive(int sock, void *ctx, void *sock_ctx)
 {
-	struct bsd_driver_data *drv = ctx;
+	struct bsd_driver_global *global = sock_ctx;
+	struct bsd_driver_data *drv;
 	struct if_announcemsghdr *ifan;
 	struct rt_msghdr *rtm;
 	struct ieee80211_michael_event *mic;
@@ -739,7 +782,7 @@
 	int n;
 	union wpa_event_data data;
 
-	n = read(sock, drv->event_buf, drv->event_buf_len);
+	n = read(sock, global->event_buf, global->event_buf_len);
 	if (n < 0) {
 		if (errno != EINTR && errno != EAGAIN)
 			wpa_printf(MSG_ERROR, "%s read() failed: %s",
@@ -747,15 +790,18 @@
 		return;
 	}
 
-	rtm = (struct rt_msghdr *) drv->event_buf;
+	rtm = (struct rt_msghdr *) global->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:
+		ifan = (struct if_announcemsghdr *) rtm;
+		drv = bsd_get_drvindex(global, ifan->ifan_index);
+		if (drv == NULL)
+			return;
 		switch (ifan->ifan_what) {
 		case RTM_IEEE80211_ASSOC:
 		case RTM_IEEE80211_REASSOC:
@@ -811,21 +857,15 @@
 		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__);
+	drv->ifindex = if_nametoindex(params->ifname);
+	if (drv->ifindex == 0) {
+		wpa_printf(MSG_DEBUG, "%s: interface %s does not exist",
+			   __func__, params->ifname);
 		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;
-	}
+	drv->global = params->global_priv;
 	os_strlcpy(drv->ifname, params->ifname, sizeof(drv->ifname));
 
 	drv->sock_xmit = l2_packet_init(drv->ifname, NULL, ETH_P_EAPOL,
@@ -839,28 +879,18 @@
 	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;
 	}
 
+	dl_list_add(&drv->global->ifaces, &drv->list);
+
 	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;
 }
@@ -871,16 +901,10 @@
 {
 	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->ifindex != 0)
+		bsd_ctrl_iface(drv, 0);
 	if (drv->sock_xmit != NULL)
 		l2_packet_deinit(drv->sock_xmit);
-	os_free(drv->event_buf);
 	os_free(drv);
 }
 
@@ -894,7 +918,8 @@
 
 static int
 bsd_set_sta_authorized(void *priv, const u8 *addr,
-		       int total_flags, int flags_or, int flags_and)
+		       unsigned int total_flags, unsigned int flags_or,
+		       unsigned int flags_and)
 {
 	int authorized = -1;
 
@@ -931,7 +956,7 @@
 	struct ieee80211_bssid bs;
 
 	os_strlcpy(bs.i_name, drv->ifname, sizeof(bs.i_name));
-	if (ioctl(drv->sock, SIOCG80211BSSID, &bs) < 0)
+	if (ioctl(drv->global->sock, SIOCG80211BSSID, &bs) < 0)
 		return -1;
 	os_memcpy(bssid, bs.i_bssid, sizeof(bs.i_bssid));
 	return 0;
@@ -965,7 +990,7 @@
 	int ret = 0;
 
 	wpa_printf(MSG_DEBUG, "%s: wpa=%d privacy=%d",
-		__FUNCTION__, wpa, privacy);
+		__func__, wpa, privacy);
 
 	if (!wpa && wpa_driver_bsd_set_wpa_ie(priv, NULL, 0) < 0)
 		ret = -1;
@@ -980,7 +1005,7 @@
 static int
 wpa_driver_bsd_set_wpa(void *priv, int enabled)
 {
-	wpa_printf(MSG_DEBUG, "%s: enabled=%d", __FUNCTION__, enabled);
+	wpa_printf(MSG_DEBUG, "%s: enabled=%d", __func__, enabled);
 
 	return wpa_driver_bsd_set_wpa_internal(priv, enabled ? 3 : 0, enabled);
 }
@@ -1185,7 +1210,8 @@
 static void
 wpa_driver_bsd_event_receive(int sock, void *ctx, void *sock_ctx)
 {
-	struct bsd_driver_data *drv = sock_ctx;
+	struct bsd_driver_global *global = sock_ctx;
+	struct bsd_driver_data *drv;
 	struct if_announcemsghdr *ifan;
 	struct if_msghdr *ifm;
 	struct rt_msghdr *rtm;
@@ -1195,7 +1221,7 @@
 	struct ieee80211_join_event *join;
 	int n;
 
-	n = read(sock, drv->event_buf, drv->event_buf_len);
+	n = read(sock, global->event_buf, global->event_buf_len);
 	if (n < 0) {
 		if (errno != EINTR && errno != EAGAIN)
 			wpa_printf(MSG_ERROR, "%s read() failed: %s",
@@ -1203,7 +1229,7 @@
 		return;
 	}
 
-	rtm = (struct rt_msghdr *) drv->event_buf;
+	rtm = (struct rt_msghdr *) global->event_buf;
 	if (rtm->rtm_version != RTM_VERSION) {
 		wpa_printf(MSG_DEBUG, "Invalid routing message version=%d",
 			   rtm->rtm_version);
@@ -1213,53 +1239,79 @@
 	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:
+			drv = bsd_get_drvindex(global, ifan->ifan_index);
+			if (drv)
+				drv->if_removed = 1;
 			event.interface_status.ievent = EVENT_INTERFACE_REMOVED;
+			break;
+		case IFAN_ARRIVAL:
+			drv = bsd_get_drvname(global, ifan->ifan_name);
+			if (drv) {
+				drv->ifindex = ifan->ifan_index;
+				drv->if_removed = 0;
+			}
+			event.interface_status.ievent = EVENT_INTERFACE_ADDED;
+			break;
 		default:
+			wpa_printf(MSG_DEBUG, "RTM_IFANNOUNCE: unknown action");
 			return;
 		}
 		wpa_printf(MSG_DEBUG, "RTM_IFANNOUNCE: Interface '%s' %s",
-			   event.interface_status.ifname,
+			   ifan->ifan_name,
 			   ifan->ifan_what == IFAN_DEPARTURE ?
 				"removed" : "added");
-		wpa_supplicant_event(ctx, EVENT_INTERFACE_STATUS, &event);
+		os_strlcpy(event.interface_status.ifname, ifan->ifan_name,
+			   sizeof(event.interface_status.ifname));
+		if (drv) {
+			wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_STATUS,
+					     &event);
+			/*
+			 * Set ifindex to zero after sending the event as the
+			 * event might query the driver to ensure a match.
+			 */
+			if (ifan->ifan_what == IFAN_DEPARTURE)
+				drv->ifindex = 0;
+		} else {
+			wpa_supplicant_event_global(global->ctx,
+						    EVENT_INTERFACE_STATUS,
+						    &event);
+		}
 		break;
 	case RTM_IEEE80211:
 		ifan = (struct if_announcemsghdr *) rtm;
-		if (ifan->ifan_index != drv->ifindex)
-			break;
+		drv = bsd_get_drvindex(global, ifan->ifan_index);
+		if (drv == NULL)
+			return;
 		switch (ifan->ifan_what) {
 		case RTM_IEEE80211_ASSOC:
 		case RTM_IEEE80211_REASSOC:
 			if (drv->is_ap)
 				break;
-			wpa_supplicant_event(ctx, EVENT_ASSOC, NULL);
+			wpa_supplicant_event(drv->ctx, EVENT_ASSOC, NULL);
 			break;
 		case RTM_IEEE80211_DISASSOC:
 			if (drv->is_ap)
 				break;
-			wpa_supplicant_event(ctx, EVENT_DISASSOC, NULL);
+			wpa_supplicant_event(drv->ctx, EVENT_DISASSOC, NULL);
 			break;
 		case RTM_IEEE80211_SCAN:
 			if (drv->is_ap)
 				break;
-			wpa_supplicant_event(ctx, EVENT_SCAN_RESULTS, NULL);
+			wpa_supplicant_event(drv->ctx, EVENT_SCAN_RESULTS,
+					     NULL);
 			break;
 		case RTM_IEEE80211_LEAVE:
 			leave = (struct ieee80211_leave_event *) &ifan[1];
-			drv_event_disassoc(ctx, leave->iev_addr);
+			drv_event_disassoc(drv->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);
+			bsd_new_sta(drv, drv->ctx, join->iev_addr);
 			break;
 		case RTM_IEEE80211_REPLAY:
 			/* ignore */
@@ -1274,23 +1326,30 @@
 			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);
+			wpa_supplicant_event(drv->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;
+		drv = bsd_get_drvindex(global, ifm->ifm_index);
+		if (drv == NULL)
+			return;
+		if ((ifm->ifm_flags & IFF_UP) == 0 &&
+		    (drv->flags & IFF_UP) != 0) {
 			wpa_printf(MSG_DEBUG, "RTM_IFINFO: Interface '%s' DOWN",
-				   event.interface_status.ifname);
-			wpa_supplicant_event(ctx, EVENT_INTERFACE_STATUS, &event);
+				   drv->ifname);
+			wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_DISABLED,
+					     NULL);
+		} else if ((ifm->ifm_flags & IFF_UP) != 0 &&
+		    (drv->flags & IFF_UP) == 0) {
+			wpa_printf(MSG_DEBUG, "RTM_IFINFO: Interface '%s' UP",
+				   drv->ifname);
+			wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_ENABLED,
+					     NULL);
 		}
+		drv->flags = ifm->ifm_flags;
 		break;
 	}
 }
@@ -1476,7 +1535,7 @@
 	(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 (ioctl(drv->global->sock, SIOCGIFMEDIA, (caddr_t)&ifmr) >= 0) {
 		if (ifmr.ifm_current & IFM_IEEE80211_ADHOC) {
 			if (ifmr.ifm_current & IFM_FLAG0)
 				return IEEE80211_M_AHDEMO;
@@ -1496,7 +1555,7 @@
 }
 
 static void *
-wpa_driver_bsd_init(void *ctx, const char *ifname)
+wpa_driver_bsd_init(void *ctx, const char *ifname, void *priv)
 {
 #define	GETPARAM(drv, param, v) \
 	(((v) = get80211param(drv, param)) != -1)
@@ -1506,14 +1565,6 @@
 	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
@@ -1524,24 +1575,12 @@
 	if (drv->ifindex == 0) {
 		wpa_printf(MSG_DEBUG, "%s: interface %s does not exist",
 			   __func__, ifname);
-		goto fail1;
+		goto fail;
 	}
-	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;
+	drv->global = priv;
+	os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname));
 
 	if (!GETPARAM(drv, IEEE80211_IOC_ROAMING, drv->prev_roaming)) {
 		wpa_printf(MSG_DEBUG, "%s: failed to get roaming state: %s",
@@ -1562,13 +1601,15 @@
 	if (wpa_driver_bsd_capa(drv))
 		goto fail;
 
+	/* Down interface during setup. */
+	if (bsd_ctrl_iface(drv, 0) < 0)
+		goto fail;
+
 	drv->opmode = get80211opmode(drv);
+	dl_list_add(&drv->global->ifaces, &drv->list);
 
 	return drv;
 fail:
-	close(drv->sock);
-fail1:
-	os_free(drv->event_buf);
 	os_free(drv);
 	return NULL;
 #undef GETPARAM
@@ -1579,22 +1620,25 @@
 {
 	struct bsd_driver_data *drv = priv;
 
-	wpa_driver_bsd_set_wpa(drv, 0);
-	eloop_unregister_read_sock(drv->route);
+	if (drv->ifindex != 0 && !drv->if_removed) {
+		wpa_driver_bsd_set_wpa(drv, 0);
 
-	/* NB: mark interface down */
-	bsd_ctrl_iface(drv, 0);
+		/* 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__);
+		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);
+	dl_list_del(&drv->list);
 	os_free(drv);
 }
 
@@ -1608,10 +1652,74 @@
 }
 #endif /* HOSTAPD */
 
+static void *
+bsd_global_init(void *ctx)
+{
+	struct bsd_driver_global *global;
+
+	global = os_zalloc(sizeof(*global));
+	if (global == NULL)
+		return NULL;
+
+	global->ctx = ctx;
+	dl_list_init(&global->ifaces);
+
+	global->sock = socket(PF_INET, SOCK_DGRAM, 0);
+	if (global->sock < 0) {
+		wpa_printf(MSG_ERROR, "socket[PF_INET,SOCK_DGRAM]: %s",
+			   strerror(errno));
+		goto fail1;
+	}
+
+	global->route = socket(PF_ROUTE, SOCK_RAW, 0);
+	if (global->route < 0) {
+		wpa_printf(MSG_ERROR, "socket[PF_ROUTE,SOCK_RAW]: %s",
+			   strerror(errno));
+		goto fail;
+	}
+
+	global->event_buf_len = rtbuf_len();
+	global->event_buf = os_malloc(global->event_buf_len);
+	if (global->event_buf == NULL) {
+		wpa_printf(MSG_ERROR, "%s: os_malloc() failed", __func__);
+		goto fail;
+	}
+
+#ifdef HOSTAPD
+	eloop_register_read_sock(global->route, bsd_wireless_event_receive,
+				 NULL, global);
+
+#else /* HOSTAPD */
+	eloop_register_read_sock(global->route, wpa_driver_bsd_event_receive,
+				 NULL, global);
+#endif /* HOSTAPD */
+
+	return global;
+
+fail:
+	close(global->sock);
+fail1:
+	os_free(global);
+	return NULL;
+}
+
+static void
+bsd_global_deinit(void *priv)
+{
+	struct bsd_driver_global *global = priv;
+
+	eloop_unregister_read_sock(global->route);
+	(void) close(global->route);
+	(void) close(global->sock);
+	os_free(global);
+}
+
 
 const struct wpa_driver_ops wpa_driver_bsd_ops = {
 	.name			= "bsd",
 	.desc			= "BSD 802.11 support",
+	.global_init		= bsd_global_init,
+	.global_deinit		= bsd_global_deinit,
 #ifdef HOSTAPD
 	.hapd_init		= bsd_init,
 	.hapd_deinit		= bsd_deinit,
@@ -1624,7 +1732,7 @@
 	.sta_set_flags		= bsd_set_sta_authorized,
 	.commit			= bsd_commit,
 #else /* HOSTAPD */
-	.init			= wpa_driver_bsd_init,
+	.init2			= wpa_driver_bsd_init,
 	.deinit			= wpa_driver_bsd_deinit,
 	.get_bssid		= wpa_driver_bsd_get_bssid,
 	.get_ssid		= wpa_driver_bsd_get_ssid,
diff --git a/src/drivers/driver_common.c b/src/drivers/driver_common.c
index aebea8c..b32d35f 100644
--- a/src/drivers/driver_common.c
+++ b/src/drivers/driver_common.c
@@ -183,12 +183,12 @@
 
 	start = buf;
 	while (*start != '\0') {
-		while (isblank(*start))
+		while (isblank((unsigned char) *start))
 			start++;
 		if (*start == '\0')
 			break;
 		end = start;
-		while (!isblank(*end) && *end != '\0')
+		while (!isblank((unsigned char) *end) && *end != '\0')
 			end++;
 		last = *end == '\0';
 		*end = '\0';
diff --git a/src/drivers/driver_hostap.c b/src/drivers/driver_hostap.c
index 8835005..517a3bb 100644
--- a/src/drivers/driver_hostap.c
+++ b/src/drivers/driver_hostap.c
@@ -140,7 +140,7 @@
 static void handle_frame(struct hostap_driver_data *drv, u8 *buf, size_t len)
 {
 	struct ieee80211_hdr *hdr;
-	u16 fc, extra_len, type, stype;
+	u16 fc, type, stype;
 	size_t data_len = len;
 	int ver;
 	union wpa_event_data event;
@@ -165,19 +165,10 @@
 
 	ver = fc & WLAN_FC_PVER;
 
-	/* protocol version 3 is reserved for indicating extra data after the
-	 * payload, version 2 for indicating ACKed frame (TX callbacks), and
-	 * version 1 for indicating failed frame (no ACK, TX callbacks) */
-	if (ver == 3) {
-		u8 *pos = buf + len - 2;
-		extra_len = WPA_GET_LE16(pos);
-		printf("extra data in frame (elen=%d)\n", extra_len);
-		if ((size_t) extra_len + 2 > len) {
-			printf("  extra data overflow\n");
-			return;
-		}
-		len -= extra_len + 2;
-	} else if (ver == 1 || ver == 2) {
+	/* 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) {
@@ -267,7 +258,8 @@
 
 
 static int hostap_send_mlme(void *priv, const u8 *msg, size_t len, int noack,
-			    unsigned int freq)
+			    unsigned int freq,
+			    const u16 *csa_offs, size_t csa_offs_len)
 {
 	struct hostap_driver_data *drv = priv;
 	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) msg;
@@ -316,7 +308,7 @@
 	pos += 2;
 	memcpy(pos, data, data_len);
 
-	res = hostap_send_mlme(drv, (u8 *) hdr, len, 0);
+	res = hostap_send_mlme(drv, (u8 *) hdr, len, 0, 0, NULL, 0);
 	if (res < 0) {
 		wpa_printf(MSG_ERROR, "hostap_send_eapol - packet len: %lu - "
 			   "failed: %d (%s)",
@@ -329,7 +321,8 @@
 
 
 static int hostap_sta_set_flags(void *priv, const u8 *addr,
-				int total_flags, int flags_or, int flags_and)
+				unsigned int total_flags, unsigned int flags_or,
+				unsigned int flags_and)
 {
 	struct hostap_driver_data *drv = priv;
 	struct prism2_hostapd_param param;
@@ -821,7 +814,7 @@
 
 
 static void hostapd_wireless_event_wireless(struct hostap_driver_data *drv,
-					    char *data, int len)
+					    char *data, unsigned int len)
 {
 	struct iw_event iwe_buf, *iwe = &iwe_buf;
 	char *pos, *end, *custom, *buf;
@@ -829,13 +822,13 @@
 	pos = data;
 	end = data + len;
 
-	while (pos + IW_EV_LCP_LEN <= end) {
+	while ((size_t) (end - pos) >= IW_EV_LCP_LEN) {
 		/* 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)
+		if (iwe->len <= IW_EV_LCP_LEN || iwe->len > end - pos)
 			return;
 
 		custom = pos + IW_EV_POINT_LEN;
@@ -854,7 +847,7 @@
 
 		switch (iwe->cmd) {
 		case IWEVCUSTOM:
-			if (custom + iwe->u.data.length > end)
+			if (iwe->u.data.length > end - custom)
 				return;
 			buf = malloc(iwe->u.data.length + 1);
 			if (buf == NULL)
@@ -1053,7 +1046,7 @@
 	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);
+				sizeof(mgmt.u.deauth), 0, 0, NULL, 0);
 }
 
 
@@ -1091,7 +1084,7 @@
 	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);
+				 sizeof(mgmt.u.disassoc), 0, 0, NULL, 0);
 }
 
 
@@ -1169,7 +1162,7 @@
 	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);
+	hostap_send_mlme(priv, (u8 *)&hdr, sizeof(hdr), 0, 0, NULL, 0);
 }
 
 
diff --git a/src/drivers/driver_ndis.c b/src/drivers/driver_ndis.c
index 669f1b8..9440f01 100644
--- a/src/drivers/driver_ndis.c
+++ b/src/drivers/driver_ndis.c
@@ -35,6 +35,7 @@
 #include "driver.h"
 #include "eloop.h"
 #include "common/ieee802_11_defs.h"
+#include "common/ieee802_11_common.h"
 #include "driver_ndis.h"
 
 int wpa_driver_register_event_cb(struct wpa_driver_ndis_data *drv);
@@ -780,20 +781,7 @@
 
 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;
+	return get_ie((const u8 *) (res + 1), res->ie_len, ie);
 }
 
 
diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
index 2a05a3b..798e694 100644
--- a/src/drivers/driver_nl80211.c
+++ b/src/drivers/driver_nl80211.c
@@ -176,13 +176,19 @@
 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);
+				  int no_cck, int no_ack, int offchanok,
+				  const u16 *csa_offs, size_t csa_offs_len);
 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);
+#define IFIDX_ANY -1
+
+static void add_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx,
+		      int ifidx_reason);
+static void del_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx,
+		      int ifidx_reason);
+static int have_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx,
+		      int ifidx_reason);
 
 static int nl80211_set_channel(struct i802_bss *bss,
 			       struct hostapd_freq_params *freq, int set_chan);
@@ -757,6 +763,15 @@
 }
 
 
+static unsigned int nl80211_get_ifindex(void *priv)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+
+	return drv->ifindex;
+}
+
+
 static int wpa_driver_nl80211_get_bssid(void *priv, u8 *bssid)
 {
 	struct i802_bss *bss = priv;
@@ -780,11 +795,12 @@
 
 
 static void wpa_driver_nl80211_event_newlink(
-	struct wpa_driver_nl80211_data *drv, const char *ifname)
+	struct nl80211_global *global, struct wpa_driver_nl80211_data *drv,
+	int ifindex, const char *ifname)
 {
 	union wpa_event_data event;
 
-	if (os_strcmp(drv->first_bss->ifname, ifname) == 0) {
+	if (drv && 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);
@@ -798,19 +814,25 @@
 	}
 
 	os_memset(&event, 0, sizeof(event));
+	event.interface_status.ifindex = ifindex;
 	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);
+	if (drv)
+		wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_STATUS, &event);
+	else
+		wpa_supplicant_event_global(global->ctx, EVENT_INTERFACE_STATUS,
+					    &event);
 }
 
 
 static void wpa_driver_nl80211_event_dellink(
-	struct wpa_driver_nl80211_data *drv, const char *ifname)
+	struct nl80211_global *global, struct wpa_driver_nl80211_data *drv,
+	int ifindex, const char *ifname)
 {
 	union wpa_event_data event;
 
-	if (os_strcmp(drv->first_bss->ifname, ifname) == 0) {
+	if (drv && 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);
@@ -825,10 +847,15 @@
 	}
 
 	os_memset(&event, 0, sizeof(event));
+	event.interface_status.ifindex = ifindex;
 	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);
+	if (drv)
+		wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_STATUS, &event);
+	else
+		wpa_supplicant_event_global(global->ctx, EVENT_INTERFACE_STATUS,
+					    &event);
 }
 
 
@@ -882,7 +909,7 @@
 	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))
+		    have_ifidx(drv, idx, IFIDX_ANY))
 			return drv;
 	}
 	return NULL;
@@ -902,13 +929,6 @@
 	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);
@@ -952,6 +972,10 @@
 		   (ifi->ifi_flags & IFF_LOWER_UP) ? "[LOWER_UP]" : "",
 		   (ifi->ifi_flags & IFF_DORMANT) ? "[DORMANT]" : "");
 
+	drv = nl80211_find_drv(global, ifi->ifi_index, buf, len);
+	if (!drv)
+		goto event_newlink;
+
 	if (!drv->if_disabled && !(ifi->ifi_flags & IFF_UP)) {
 		namebuf[0] = '\0';
 		if (if_indextoname(ifi->ifi_index, namebuf) &&
@@ -1046,10 +1070,12 @@
 				       -1, IF_OPER_UP);
 	}
 
+event_newlink:
 	if (ifname[0])
-		wpa_driver_nl80211_event_newlink(drv, ifname);
+		wpa_driver_nl80211_event_newlink(global, drv, ifi->ifi_index,
+						 ifname);
 
-	if (ifi->ifi_family == AF_BRIDGE && brid) {
+	if (ifi->ifi_family == AF_BRIDGE && brid && drv) {
 		struct i802_bss *bss;
 
 		/* device has been added to bridge */
@@ -1061,7 +1087,7 @@
 		}
 		wpa_printf(MSG_DEBUG, "nl80211: Add ifindex %u for bridge %s",
 			   brid, namebuf);
-		add_ifidx(drv, brid);
+		add_ifidx(drv, brid, ifi->ifi_index);
 
 		for (bss = drv->first_bss; bss; bss = bss->next) {
 			if (os_strcmp(ifname, bss->ifname) == 0) {
@@ -1085,13 +1111,6 @@
 	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);
@@ -1132,10 +1151,9 @@
 		   (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);
+	drv = nl80211_find_drv(global, ifi->ifi_index, buf, len);
 
-	if (ifi->ifi_family == AF_BRIDGE && brid) {
+	if (ifi->ifi_family == AF_BRIDGE && brid && drv) {
 		/* device has been removed from bridge */
 		char namebuf[IFNAMSIZ];
 
@@ -1148,8 +1166,12 @@
 				   "nl80211: Remove ifindex %u for bridge %s",
 				   brid, namebuf);
 		}
-		del_ifidx(drv, brid);
+		del_ifidx(drv, brid, ifi->ifi_index);
 	}
+
+	if (ifi->ifi_family != AF_BRIDGE || !brid)
+		wpa_driver_nl80211_event_dellink(global, drv, ifi->ifi_index,
+						 ifname);
 }
 
 
@@ -1519,11 +1541,16 @@
 
 static void wpa_driver_nl80211_rfkill_blocked(void *ctx)
 {
+	struct wpa_driver_nl80211_data *drv = ctx;
+
 	wpa_printf(MSG_DEBUG, "nl80211: RFKILL blocked");
+
 	/*
-	 * This may be for any interface; use ifdown event to disable
-	 * interface.
+	 * rtnetlink ifdown handler will report interfaces other than the P2P
+	 * Device interface as disabled.
 	 */
+	if (drv->nlmode == NL80211_IFTYPE_P2P_DEVICE)
+		wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_DISABLED, NULL);
 }
 
 
@@ -1536,7 +1563,16 @@
 			   "after rfkill unblock");
 		return;
 	}
-	/* rtnetlink ifup handler will report interface as enabled */
+
+	if (is_p2p_net_interface(drv->nlmode))
+		nl80211_disable_11b_rates(drv, drv->ifindex, 1);
+
+	/*
+	 * rtnetlink ifup handler will report interfaces other than the P2P
+	 * Device interface as enabled.
+	 */
+	if (drv->nlmode == NL80211_IFTYPE_P2P_DEVICE)
+		wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_ENABLED, NULL);
 }
 
 
@@ -1621,13 +1657,65 @@
 }
 
 
+static void
+wpa_driver_nl80211_drv_init_rfkill(struct wpa_driver_nl80211_data *drv)
+{
+	struct rfkill_config *rcfg;
+
+	if (drv->rfkill)
+		return;
+
+	rcfg = os_zalloc(sizeof(*rcfg));
+	if (!rcfg)
+		return;
+
+	rcfg->ctx = drv;
+
+	/* rfkill uses netdev sysfs for initialization. However, P2P Device is
+	 * not associated with a netdev, so use the name of some other interface
+	 * sharing the same wiphy as the P2P Device interface.
+	 *
+	 * Note: This is valid, as a P2P Device interface is always dynamically
+	 * created and is created only once another wpa_s interface was added.
+	 */
+	if (drv->nlmode == NL80211_IFTYPE_P2P_DEVICE) {
+		struct nl80211_global *global = drv->global;
+		struct wpa_driver_nl80211_data *tmp1;
+
+		dl_list_for_each(tmp1, &global->interfaces,
+				 struct wpa_driver_nl80211_data, list) {
+			if (drv == tmp1 || drv->wiphy_idx != tmp1->wiphy_idx ||
+			    !tmp1->rfkill)
+				continue;
+
+			wpa_printf(MSG_DEBUG,
+				   "nl80211: Use (%s) to initialize P2P Device rfkill",
+				   tmp1->first_bss->ifname);
+			os_strlcpy(rcfg->ifname, tmp1->first_bss->ifname,
+				   sizeof(rcfg->ifname));
+			break;
+		}
+	} else {
+		os_strlcpy(rcfg->ifname, drv->first_bss->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) {
+		wpa_printf(MSG_DEBUG, "nl80211: RFKILL status not available");
+		os_free(rcfg);
+	}
+}
+
+
 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)
@@ -1649,6 +1737,7 @@
 
 	drv->num_if_indices = sizeof(drv->default_if_indices) / sizeof(int);
 	drv->if_indices = drv->default_if_indices;
+	drv->if_indices_reason = drv->default_if_indices_reason;
 
 	drv->first_bss = os_zalloc(sizeof(*drv->first_bss));
 	if (!drv->first_bss) {
@@ -1668,22 +1757,6 @@
 	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;
 
@@ -1878,6 +1951,11 @@
 			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)
@@ -2181,6 +2259,11 @@
 	if (!bss->if_dynamic && nl80211_get_ifmode(bss) == NL80211_IFTYPE_AP)
 		bss->static_ap = 1;
 
+	if (first &&
+	    nl80211_get_ifmode(bss) != NL80211_IFTYPE_P2P_DEVICE &&
+	    linux_iface_up(drv->global->ioctl_sock, bss->ifname) > 0)
+		drv->start_iface_up = 1;
+
 	if (wpa_driver_nl80211_capa(drv))
 		return -1;
 
@@ -2214,6 +2297,8 @@
 	if (nlmode == NL80211_IFTYPE_P2P_DEVICE)
 		nl80211_get_macaddr(bss);
 
+	wpa_driver_nl80211_drv_init_rfkill(drv);
+
 	if (!rfkill_is_blocked(drv->rfkill)) {
 		int ret = i802_set_iface_flags(bss, 1);
 		if (ret) {
@@ -2221,25 +2306,32 @@
 				   "interface '%s' UP", bss->ifname);
 			return ret;
 		}
+
+		if (is_p2p_net_interface(nlmode))
+			nl80211_disable_11b_rates(bss->drv,
+						  bss->drv->ifindex, 1);
+
 		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;
+		if (nlmode != NL80211_IFTYPE_P2P_DEVICE)
+			drv->if_disabled = 1;
+
 		send_rfkill_event = 1;
 	}
 
-	if (!drv->hostapd)
+	if (!drv->hostapd && nlmode != NL80211_IFTYPE_P2P_DEVICE)
 		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 (nlmode != NL80211_IFTYPE_P2P_DEVICE) {
+		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,
@@ -2320,6 +2412,9 @@
 	if (drv->if_indices != drv->default_if_indices)
 		os_free(drv->if_indices);
 
+	if (drv->if_indices_reason != drv->default_if_indices_reason)
+		os_free(drv->if_indices_reason);
+
 	if (drv->disabled_11b_rates)
 		nl80211_disable_11b_rates(drv, drv->ifindex, 0);
 
@@ -2463,6 +2558,7 @@
 }
 
 
+#ifdef CONFIG_DRIVER_NL80211_QCA
 static int issue_key_mgmt_set_key(struct wpa_driver_nl80211_data *drv,
 				  const u8 *key, size_t key_len)
 {
@@ -2490,6 +2586,7 @@
 
 	return ret;
 }
+#endif /* CONFIG_DRIVER_NL80211_QCA */
 
 
 static int wpa_driver_nl80211_set_key(const char *ifname, struct i802_bss *bss,
@@ -2520,6 +2617,7 @@
 	}
 #endif /* CONFIG_TDLS */
 
+#ifdef CONFIG_DRIVER_NL80211_QCA
 	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",
@@ -2527,6 +2625,7 @@
 		ret = issue_key_mgmt_set_key(drv, key, key_len);
 		return ret;
 	}
+#endif /* CONFIG_DRIVER_NL80211_QCA */
 
 	if (alg == WPA_ALG_NONE) {
 		msg = nl80211_ifindex_msg(drv, ifindex, 0, NL80211_CMD_DEL_KEY);
@@ -3084,7 +3183,9 @@
 					 const void *data, size_t len,
 					 int encrypt, int noack,
 					 unsigned int freq, int no_cck,
-					 int offchanok, unsigned int wait_time)
+					 int offchanok, unsigned int wait_time,
+					 const u16 *csa_offs,
+					 size_t csa_offs_len)
 {
 	struct wpa_driver_nl80211_data *drv = bss->drv;
 	u64 cookie;
@@ -3110,7 +3211,8 @@
 
 	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);
+				     &cookie, no_cck, noack, offchanok,
+				     csa_offs, csa_offs_len);
 	if (res == 0 && !noack) {
 		const struct ieee80211_mgmt *mgmt;
 		u16 fc;
@@ -3136,7 +3238,9 @@
 					size_t data_len, int noack,
 					unsigned int freq, int no_cck,
 					int offchanok,
-					unsigned int wait_time)
+					unsigned int wait_time,
+					const u16 *csa_offs,
+					size_t csa_offs_len)
 {
 	struct wpa_driver_nl80211_data *drv = bss->drv;
 	struct ieee80211_mgmt *mgmt;
@@ -3166,7 +3270,7 @@
 		}
 		return nl80211_send_frame_cmd(bss, freq, 0,
 					      data, data_len, NULL, 1, noack,
-					      1);
+					      1, csa_offs, csa_offs_len);
 	}
 
 	if (drv->device_ap_sme && is_ap_interface(drv->nlmode)) {
@@ -3180,7 +3284,8 @@
 					      wait_time,
 					      data, data_len,
 					      &drv->send_action_cookie,
-					      no_cck, noack, offchanok);
+					      no_cck, noack, offchanok,
+					      csa_offs, csa_offs_len);
 	}
 
 	if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT &&
@@ -3200,7 +3305,8 @@
 	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);
+					     wait_time, csa_offs,
+					     csa_offs_len);
 }
 
 
@@ -3253,7 +3359,7 @@
 	struct i802_bss *bss = priv;
 	struct wpa_driver_nl80211_data *drv = bss->drv;
 	struct nl_msg *msg;
-	struct nlattr *acl;
+	struct nl_msg *acl;
 	unsigned int i;
 	int ret;
 
@@ -3266,23 +3372,26 @@
 	wpa_printf(MSG_DEBUG, "nl80211: Set %s ACL (num_mac_acl=%u)",
 		   params->acl_policy ? "Accept" : "Deny", params->num_mac_acl);
 
-	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) ||
-	    (acl = nla_nest_start(msg, NL80211_ATTR_MAC_ADDRS)) == NULL) {
-		nlmsg_free(msg);
+	acl = nlmsg_alloc();
+	if (!acl)
 		return -ENOMEM;
-	}
-
 	for (i = 0; i < params->num_mac_acl; i++) {
-		if (nla_put(msg, i + 1, ETH_ALEN, params->mac_acl[i].addr)) {
-			nlmsg_free(msg);
+		if (nla_put(acl, i + 1, ETH_ALEN, params->mac_acl[i].addr)) {
+			nlmsg_free(acl);
 			return -ENOMEM;
 		}
 	}
 
-	nla_nest_end(msg, acl);
+	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) {
@@ -3433,24 +3542,26 @@
 	    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 (params->ht_opmode != -1) {
+		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 (nla_put_u32(msg, NL80211_ATTR_SMPS_MODE, smps_mode))
-		goto fail;
 
 	if (params->beacon_ies) {
 		wpa_hexdump_buf(MSG_DEBUG, "nl80211: beacon_ies",
@@ -3500,6 +3611,12 @@
 	}
 #endif /* CONFIG_P2P */
 
+	if (params->pbss) {
+		wpa_printf(MSG_DEBUG, "nl80211: PBSS");
+		if (nla_put_flag(msg, NL80211_ATTR_PBSS))
+			goto fail;
+	}
+
 	ret = send_and_recv_msgs(drv, msg, NULL, NULL);
 	if (ret) {
 		wpa_printf(MSG_DEBUG, "nl80211: Beacon set failed: %d (%s)",
@@ -3658,6 +3775,8 @@
 		f |= BIT(NL80211_STA_FLAG_TDLS_PEER);
 	if (flags & WPA_STA_AUTHENTICATED)
 		f |= BIT(NL80211_STA_FLAG_AUTHENTICATED);
+	if (flags & WPA_STA_ASSOCIATED)
+		f |= BIT(NL80211_STA_FLAG_ASSOCIATED);
 
 	return f;
 }
@@ -3710,7 +3829,17 @@
 	if (!msg || nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, params->addr))
 		goto fail;
 
-	if (!params->set || (params->flags & WPA_STA_TDLS_PEER)) {
+	/*
+	 * Set the below properties only in one of the following cases:
+	 * 1. New station is added, already associated.
+	 * 2. Set WPA_STA_TDLS_PEER station.
+	 * 3. Set an already added unassociated station, if driver supports
+	 * full AP client state. (Set these properties after station became
+	 * associated will be rejected by the driver).
+	 */
+	if (!params->set || (params->flags & WPA_STA_TDLS_PEER) ||
+	    (params->set && FULL_AP_CLIENT_STATE_SUPP(drv->capa.flags) &&
+	     (params->flags & WPA_STA_ASSOCIATED))) {
 		wpa_hexdump(MSG_DEBUG, "  * supported rates",
 			    params->supp_rates, params->supp_rates_len);
 		wpa_printf(MSG_DEBUG, "  * capability=0x%x",
@@ -3758,9 +3887,12 @@
 			/*
 			 * 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.
+			 * a dummy STA entry is used for now and for a station
+			 * that is still not associated.
 			 */
-			wpa_printf(MSG_DEBUG, "  * aid=1 (TDLS workaround)");
+			wpa_printf(MSG_DEBUG, "  * aid=1 (%s workaround)",
+				   (params->flags & WPA_STA_TDLS_PEER) ?
+				   "TDLS" : "UNASSOC_STA");
 			if (nla_put_u16(msg, NL80211_ATTR_STA_AID, 1))
 				goto fail;
 		}
@@ -3773,6 +3905,15 @@
 		wpa_printf(MSG_DEBUG, "  * peer_aid=%u", params->aid);
 		if (nla_put_u16(msg, NL80211_ATTR_PEER_AID, params->aid))
 			goto fail;
+	} else if (FULL_AP_CLIENT_STATE_SUPP(drv->capa.flags) &&
+		   (params->flags & WPA_STA_ASSOCIATED)) {
+		wpa_printf(MSG_DEBUG, "  * aid=%u", params->aid);
+		wpa_printf(MSG_DEBUG, "  * listen_interval=%u",
+			   params->listen_interval);
+		if (nla_put_u16(msg, NL80211_ATTR_STA_AID, params->aid) ||
+		    nla_put_u16(msg, NL80211_ATTR_STA_LISTEN_INTERVAL,
+				params->listen_interval))
+			goto fail;
 	}
 
 	if (params->vht_opmode_enabled) {
@@ -3803,6 +3944,36 @@
 	os_memset(&upd, 0, sizeof(upd));
 	upd.set = sta_flags_nl80211(params->flags);
 	upd.mask = upd.set | sta_flags_nl80211(params->flags_mask);
+
+	/*
+	 * If the driver doesn't support full AP client state, ignore ASSOC/AUTH
+	 * flags, as nl80211 driver moves a new station, by default, into
+	 * associated state.
+	 *
+	 * On the other hand, if the driver supports that feature and the
+	 * station is added in unauthenticated state, set the
+	 * authenticated/associated bits in the mask to prevent moving this
+	 * station to associated state before it is actually associated.
+	 *
+	 * This is irrelevant for mesh mode where the station is added to the
+	 * driver as authenticated already, and ASSOCIATED isn't part of the
+	 * nl80211 API.
+	 */
+	if (!is_mesh_interface(drv->nlmode)) {
+		if (!FULL_AP_CLIENT_STATE_SUPP(drv->capa.flags)) {
+			wpa_printf(MSG_DEBUG,
+				   "nl80211: Ignore ASSOC/AUTH flags since driver doesn't support full AP client state");
+			upd.mask &= ~(BIT(NL80211_STA_FLAG_ASSOCIATED) |
+				      BIT(NL80211_STA_FLAG_AUTHENTICATED));
+		} else if (!params->set &&
+			   !(params->flags & WPA_STA_TDLS_PEER)) {
+			if (!(params->flags & WPA_STA_AUTHENTICATED))
+				upd.mask |= BIT(NL80211_STA_FLAG_AUTHENTICATED);
+			if (!(params->flags & WPA_STA_ASSOCIATED))
+				upd.mask |= BIT(NL80211_STA_FLAG_ASSOCIATED);
+		}
+	}
+
 	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))
@@ -3925,7 +4096,11 @@
 	/* 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);
+	{
+		del_ifidx(drv2, ifidx, IFIDX_ANY);
+		/* Remove all bridges learned for this iface */
+		del_ifidx(drv2, IFIDX_ANY, ifidx);
+	}
 
 	msg = nl80211_ifindex_msg(drv, ifidx, 0, NL80211_CMD_DEL_INTERFACE);
 	if (send_and_recv_msgs(drv, msg, NULL, NULL) == 0)
@@ -4033,7 +4208,7 @@
 	    iftype == NL80211_IFTYPE_WDS ||
 	    iftype == NL80211_IFTYPE_MONITOR) {
 		/* start listening for EAPOL on this interface */
-		add_ifidx(drv, ifidx);
+		add_ifidx(drv, ifidx, IFIDX_ANY);
 	}
 
 	if (addr && iftype != NL80211_IFTYPE_MONITOR &&
@@ -4236,7 +4411,7 @@
 	memcpy(pos, data, data_len);
 
 	res = wpa_driver_nl80211_send_frame(bss, (u8 *) hdr, len, encrypt, 0,
-					    0, 0, 0, 0);
+					    0, 0, 0, 0, NULL, 0);
 	if (res < 0) {
 		wpa_printf(MSG_ERROR, "i802_send_eapol - packet len: %lu - "
 			   "failed: %d (%s)",
@@ -4249,8 +4424,9 @@
 
 
 static int wpa_driver_nl80211_sta_set_flags(void *priv, const u8 *addr,
-					    int total_flags,
-					    int flags_or, int flags_and)
+					    unsigned int total_flags,
+					    unsigned int flags_or,
+					    unsigned int flags_and)
 {
 	struct i802_bss *bss = priv;
 	struct nl_msg *msg;
@@ -4464,8 +4640,9 @@
 			goto fail;
 	}
 
-	if (nl80211_ht_vht_overrides(msg, params) < 0)
-		return -1;
+	ret = nl80211_ht_vht_overrides(msg, params);
+	if (ret < 0)
+		goto fail;
 
 	ret = send_and_recv_msgs(drv, msg, NULL, NULL);
 	msg = NULL;
@@ -4658,6 +4835,22 @@
 	if (params->p2p)
 		wpa_printf(MSG_DEBUG, "  * P2P group");
 
+	if (params->pbss) {
+		wpa_printf(MSG_DEBUG, "  * PBSS");
+		if (nla_put_flag(msg, NL80211_ATTR_PBSS))
+			return -1;
+	}
+
+	drv->connect_reassoc = 0;
+	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))
+			return -1;
+		drv->connect_reassoc = 1;
+	}
+
 	return 0;
 }
 
@@ -4671,6 +4864,7 @@
 	int ret;
 	int algs;
 
+#ifdef CONFIG_DRIVER_NL80211_QCA
 	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 ||
@@ -4680,6 +4874,7 @@
 		if (ret)
 			return ret;
 	}
+#endif /* CONFIG_DRIVER_NL80211_QCA */
 
 	wpa_printf(MSG_DEBUG, "nl80211: Connect (ifindex=%d)", drv->ifindex);
 	msg = nl80211_drv_msg(drv, 0, NL80211_CMD_CONNECT);
@@ -4808,14 +5003,6 @@
 	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) {
@@ -5227,6 +5414,8 @@
 		[NL80211_STA_INFO_RX_PACKETS] = { .type = NLA_U32 },
 		[NL80211_STA_INFO_TX_PACKETS] = { .type = NLA_U32 },
 		[NL80211_STA_INFO_TX_FAILED] = { .type = NLA_U32 },
+		[NL80211_STA_INFO_RX_BYTES64] = { .type = NLA_U64 },
+		[NL80211_STA_INFO_TX_BYTES64] = { .type = NLA_U64 },
 	};
 
 	nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
@@ -5252,10 +5441,23 @@
 	if (stats[NL80211_STA_INFO_INACTIVE_TIME])
 		data->inactive_msec =
 			nla_get_u32(stats[NL80211_STA_INFO_INACTIVE_TIME]);
+	/* For backwards compatibility, fetch the 32-bit counters first. */
 	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_BYTES64] &&
+	    stats[NL80211_STA_INFO_TX_BYTES64]) {
+		/*
+		 * The driver supports 64-bit counters, so use them to override
+		 * the 32-bit values.
+		 */
+		data->rx_bytes =
+			nla_get_u64(stats[NL80211_STA_INFO_RX_BYTES64]);
+		data->tx_bytes =
+			nla_get_u64(stats[NL80211_STA_INFO_TX_BYTES64]);
+		data->bytes_64bit = 1;
+	}
 	if (stats[NL80211_STA_INFO_RX_PACKETS])
 		data->rx_packets =
 			nla_get_u32(stats[NL80211_STA_INFO_RX_PACKETS]);
@@ -5424,7 +5626,7 @@
 	return wpa_driver_nl80211_send_mlme(bss, (u8 *) &mgmt,
 					    IEEE80211_HDRLEN +
 					    sizeof(mgmt.u.deauth), 0, 0, 0, 0,
-					    0);
+					    0, NULL, 0);
 }
 
 
@@ -5451,7 +5653,7 @@
 	return wpa_driver_nl80211_send_mlme(bss, (u8 *) &mgmt,
 					    IEEE80211_HDRLEN +
 					    sizeof(mgmt.u.disassoc), 0, 0, 0, 0,
-					    0);
+					    0, NULL, 0);
 }
 
 
@@ -5466,7 +5668,9 @@
 	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]);
+		res = os_snprintf(pos, end - pos, " %d(%d)",
+				  drv->if_indices[i],
+				  drv->if_indices_reason[i]);
 		if (os_snprintf_error(end - pos, res))
 			break;
 		pos += res;
@@ -5478,14 +5682,16 @@
 }
 
 
-static void add_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx)
+static void add_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx,
+		      int ifidx_reason)
 {
 	int i;
-	int *old;
+	int *old, *old_reason;
 
-	wpa_printf(MSG_DEBUG, "nl80211: Add own interface ifindex %d",
-		   ifidx);
-	if (have_ifidx(drv, ifidx)) {
+	wpa_printf(MSG_DEBUG,
+		   "nl80211: Add own interface ifindex %d (ifidx_reason %d)",
+		   ifidx, ifidx_reason);
+	if (have_ifidx(drv, ifidx, ifidx_reason)) {
 		wpa_printf(MSG_DEBUG, "nl80211: ifindex %d already in the list",
 			   ifidx);
 		return;
@@ -5493,6 +5699,7 @@
 	for (i = 0; i < drv->num_if_indices; i++) {
 		if (drv->if_indices[i] == 0) {
 			drv->if_indices[i] = ifidx;
+			drv->if_indices_reason[i] = ifidx_reason;
 			dump_ifidx(drv);
 			return;
 		}
@@ -5503,32 +5710,57 @@
 	else
 		old = NULL;
 
+	if (drv->if_indices_reason != drv->default_if_indices_reason)
+		old_reason = drv->if_indices_reason;
+	else
+		old_reason = NULL;
+
 	drv->if_indices = os_realloc_array(old, drv->num_if_indices + 1,
 					   sizeof(int));
+	drv->if_indices_reason = os_realloc_array(old_reason,
+						  drv->num_if_indices + 1,
+						  sizeof(int));
 	if (!drv->if_indices) {
 		if (!old)
 			drv->if_indices = drv->default_if_indices;
 		else
 			drv->if_indices = old;
+	}
+	if (!drv->if_indices_reason) {
+		if (!old_reason)
+			drv->if_indices_reason = drv->default_if_indices_reason;
+		else
+			drv->if_indices_reason = old_reason;
+	}
+	if (!drv->if_indices || !drv->if_indices_reason) {
 		wpa_printf(MSG_ERROR, "Failed to reallocate memory for "
 			   "interfaces");
 		wpa_printf(MSG_ERROR, "Ignoring EAPOL on interface %d", ifidx);
 		return;
-	} else if (!old)
+	}
+	if (!old)
 		os_memcpy(drv->if_indices, drv->default_if_indices,
 			  sizeof(drv->default_if_indices));
+	if (!old_reason)
+		os_memcpy(drv->if_indices_reason,
+			  drv->default_if_indices_reason,
+			  sizeof(drv->default_if_indices_reason));
 	drv->if_indices[drv->num_if_indices] = ifidx;
+	drv->if_indices_reason[drv->num_if_indices] = ifidx_reason;
 	drv->num_if_indices++;
 	dump_ifidx(drv);
 }
 
 
-static void del_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx)
+static void del_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx,
+		      int ifidx_reason)
 {
 	int i;
 
 	for (i = 0; i < drv->num_if_indices; i++) {
-		if (drv->if_indices[i] == ifidx) {
+		if ((drv->if_indices[i] == ifidx || ifidx == IFIDX_ANY) &&
+		    (drv->if_indices_reason[i] == ifidx_reason ||
+		     ifidx_reason == IFIDX_ANY)) {
 			drv->if_indices[i] = 0;
 			break;
 		}
@@ -5537,12 +5769,15 @@
 }
 
 
-static int have_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx)
+static int have_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx,
+		      int ifidx_reason)
 {
 	int i;
 
 	for (i = 0; i < drv->num_if_indices; i++)
-		if (drv->if_indices[i] == ifidx)
+		if (drv->if_indices[i] == ifidx &&
+		    (drv->if_indices_reason[i] == ifidx_reason ||
+		     ifidx_reason == IFIDX_ANY))
 			return 1;
 
 	return 0;
@@ -5607,7 +5842,7 @@
 		return;
 	}
 
-	if (have_ifidx(drv, lladdr.sll_ifindex))
+	if (have_ifidx(drv, lladdr.sll_ifindex, IFIDX_ANY))
 		drv_event_eapol_rx(drv->ctx, lladdr.sll_addr, buf, len);
 }
 
@@ -5634,7 +5869,7 @@
 		}
 		bss->added_bridge = 1;
 		br_ifindex = if_nametoindex(brname);
-		add_ifidx(drv, br_ifindex);
+		add_ifidx(drv, br_ifindex, drv->ifindex);
 	}
 	bss->br_ifindex = br_ifindex;
 
@@ -5674,8 +5909,8 @@
 	struct wpa_driver_nl80211_data *drv;
 	struct i802_bss *bss;
 	size_t i;
-	char brname[IFNAMSIZ];
-	int ifindex, br_ifindex;
+	char master_ifname[IFNAMSIZ];
+	int ifindex, br_ifindex = 0;
 	int br_added = 0;
 
 	bss = wpa_driver_nl80211_drv_init(hapd, params->ifname,
@@ -5686,41 +5921,55 @@
 
 	drv = bss->drv;
 
-	if (linux_br_get(brname, params->ifname) == 0) {
+	if (linux_br_get(master_ifname, params->ifname) == 0) {
 		wpa_printf(MSG_DEBUG, "nl80211: Interface %s is in bridge %s",
-			   params->ifname, brname);
-		br_ifindex = if_nametoindex(brname);
-		os_strlcpy(bss->brname, brname, IFNAMSIZ);
+			   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), drv->ifindex);
+
+		/* check if master itself is under bridge */
+		if (linux_br_get(master_ifname, master_ifname) == 0) {
+			wpa_printf(MSG_DEBUG, "nl80211: which is in bridge %s",
+				   master_ifname);
+			br_ifindex = if_nametoindex(master_ifname);
+			os_strlcpy(bss->brname, master_ifname, IFNAMSIZ);
+		}
 	} else {
-		brname[0] = '\0';
-		br_ifindex = 0;
+		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);
+				add_ifidx(drv, ifindex, drv->ifindex);
 			if (ifindex == br_ifindex)
 				br_added = 1;
 		}
 	}
 
 	/* start listening for EAPOL on the default AP interface */
-	add_ifidx(drv, drv->ifindex);
+	add_ifidx(drv, drv->ifindex, IFIDX_ANY);
 
 	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], brname) != 0)
+		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);
+		add_ifidx(drv, br_ifindex, drv->ifindex);
 
 #ifdef CONFIG_LIBNL3_ROUTE
 	if (bss->added_if_into_bridge) {
@@ -5792,8 +6041,9 @@
 		return NL80211_IFTYPE_P2P_DEVICE;
 	case WPA_IF_MESH:
 		return NL80211_IFTYPE_MESH_POINT;
+	default:
+		return -1;
 	}
-	return -1;
 }
 
 
@@ -5864,7 +6114,8 @@
 				     const char *ifname, const u8 *addr,
 				     void *bss_ctx, void **drv_priv,
 				     char *force_ifname, u8 *if_addr,
-				     const char *bridge, int use_existing)
+				     const char *bridge, int use_existing,
+				     int setup_ap)
 {
 	enum nl80211_iftype nlmode;
 	struct i802_bss *bss = priv;
@@ -5948,7 +6199,7 @@
 		os_memcpy(if_addr, new_addr, ETH_ALEN);
 	}
 
-	if (type == WPA_IF_AP_BSS) {
+	if (type == WPA_IF_AP_BSS && setup_ap) {
 		struct i802_bss *new_bss = os_zalloc(sizeof(*new_bss));
 		if (new_bss == NULL) {
 			if (added)
@@ -6004,7 +6255,7 @@
 	     nlmode == NL80211_IFTYPE_AP_VLAN ||
 	     nlmode == NL80211_IFTYPE_WDS ||
 	     nlmode == NL80211_IFTYPE_MONITOR))
-		add_ifidx(drv, ifidx);
+		add_ifidx(drv, ifidx, IFIDX_ANY);
 
 	return 0;
 }
@@ -6024,8 +6275,10 @@
 	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);
+				 struct wpa_driver_nl80211_data, list) {
+			del_ifidx(drv2, ifindex, IFIDX_ANY);
+			del_ifidx(drv2, IFIDX_ANY, ifindex);
+		}
 	}
 
 	if (type != WPA_IF_AP_BSS)
@@ -6103,7 +6356,8 @@
 				  unsigned int freq, unsigned int wait,
 				  const u8 *buf, size_t buf_len,
 				  u64 *cookie_out, int no_cck, int no_ack,
-				  int offchanok)
+				  int offchanok, const u16 *csa_offs,
+				  size_t csa_offs_len)
 {
 	struct wpa_driver_nl80211_data *drv = bss->drv;
 	struct nl_msg *msg;
@@ -6123,6 +6377,8 @@
 	     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)) ||
+	    (csa_offs && nla_put(msg, NL80211_ATTR_CSA_C_OFFSETS_TX,
+				 csa_offs_len * sizeof(u16), csa_offs)) ||
 	    nla_put(msg, NL80211_ATTR_FRAME, buf_len, buf))
 		goto fail;
 
@@ -6140,6 +6396,20 @@
 
 		if (cookie_out)
 			*cookie_out = no_ack ? (u64) -1 : cookie;
+
+		if (drv->num_send_action_cookies == MAX_SEND_ACTION_COOKIES) {
+			wpa_printf(MSG_DEBUG,
+				   "nl80211: Drop oldest pending send action cookie 0x%llx",
+				   (long long unsigned int)
+				   drv->send_action_cookies[0]);
+			os_memmove(&drv->send_action_cookies[0],
+				   &drv->send_action_cookies[1],
+				   (MAX_SEND_ACTION_COOKIES - 1) *
+				   sizeof(u64));
+			drv->num_send_action_cookies--;
+		}
+		drv->send_action_cookies[drv->num_send_action_cookies] = cookie;
+		drv->num_send_action_cookies++;
 	}
 
 fail:
@@ -6182,29 +6452,28 @@
 	     !drv->use_monitor))
 		ret = wpa_driver_nl80211_send_mlme(bss, buf, 24 + data_len,
 						   0, freq, no_cck, 1,
-						   wait_time);
+						   wait_time, NULL, 0);
 	else
 		ret = nl80211_send_frame_cmd(bss, freq, wait_time, buf,
 					     24 + data_len,
 					     &drv->send_action_cookie,
-					     no_cck, 0, 1);
+					     no_cck, 0, 1, NULL, 0);
 
 	os_free(buf);
 	return ret;
 }
 
 
-static void wpa_driver_nl80211_send_action_cancel_wait(void *priv)
+static void nl80211_frame_wait_cancel(struct i802_bss *bss, u64 cookie)
 {
-	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);
+		   (long long unsigned int) cookie);
 	if (!(msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_FRAME_WAIT_CANCEL)) ||
-	    nla_put_u64(msg, NL80211_ATTR_COOKIE, drv->send_action_cookie)) {
+	    nla_put_u64(msg, NL80211_ATTR_COOKIE, cookie)) {
 		nlmsg_free(msg);
 		return;
 	}
@@ -6216,6 +6485,30 @@
 }
 
 
+static void wpa_driver_nl80211_send_action_cancel_wait(void *priv)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	unsigned int i;
+	u64 cookie;
+
+	/* Cancel the last pending TX cookie */
+	nl80211_frame_wait_cancel(bss, drv->send_action_cookie);
+
+	/*
+	 * Cancel the other pending TX cookies, if any. This is needed since
+	 * the driver may keep a list of all pending offchannel TX operations
+	 * and free up the radio only once they have expired or cancelled.
+	 */
+	for (i = drv->num_send_action_cookies; i > 0; i--) {
+		cookie = drv->send_action_cookies[i - 1];
+		if (cookie != drv->send_action_cookie)
+			nl80211_frame_wait_cancel(bss, cookie);
+	}
+	drv->num_send_action_cookies = 0;
+}
+
+
 static int wpa_driver_nl80211_remain_on_channel(void *priv, unsigned int freq,
 						unsigned int duration)
 {
@@ -6438,9 +6731,13 @@
 static void wpa_driver_nl80211_resume(void *priv)
 {
 	struct i802_bss *bss = priv;
+	enum nl80211_iftype nlmode = nl80211_get_ifmode(bss);
 
 	if (i802_set_iface_flags(bss, 1))
 		wpa_printf(MSG_DEBUG, "nl80211: Failed to set interface up on resume event");
+
+	if (is_p2p_net_interface(nlmode))
+		nl80211_disable_11b_rates(bss->drv, bss->drv->ifindex, 1);
 }
 
 
@@ -6513,8 +6810,12 @@
 
 	os_memset(si, 0, sizeof(*si));
 	res = nl80211_get_link_signal(drv, si);
-	if (res != 0)
-		return res;
+	if (res) {
+		if (drv->nlmode != NL80211_IFTYPE_ADHOC &&
+		    drv->nlmode != NL80211_IFTYPE_MESH_POINT)
+			return res;
+		si->current_signal = 0;
+	}
 
 	res = nl80211_get_channel_width(drv, si);
 	if (res != 0)
@@ -6529,15 +6830,15 @@
 {
 	struct i802_bss *bss = priv;
 	return wpa_driver_nl80211_send_frame(bss, data, data_len, encrypt, 0,
-					     0, 0, 0, 0);
+					     0, 0, 0, 0, NULL, 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;
+	wpa_printf(MSG_DEBUG, "nl80211: driver param='%s'", param);
 
 #ifdef CONFIG_P2P
 	if (os_strstr(param, "use_p2p_group_interface=1")) {
@@ -6575,7 +6876,7 @@
 }
 
 
-static void * nl80211_global_init(void)
+static void * nl80211_global_init(void *ctx)
 {
 	struct nl80211_global *global;
 	struct netlink_config *cfg;
@@ -6583,6 +6884,7 @@
 	global = os_zalloc(sizeof(*global));
 	if (global == NULL)
 		return NULL;
+	global->ctx = ctx;
 	global->ioctl_sock = -1;
 	dl_list_init(&global->interfaces);
 	global->if_add_ifindex = -1;
@@ -6938,7 +7240,7 @@
 	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)
+					 0, 0, NULL, 0) < 0)
 		wpa_printf(MSG_DEBUG, "nl80211_send_null_frame: Failed to "
 			   "send poll frame");
 }
@@ -7208,6 +7510,19 @@
 				struct wpa_driver_scan_params *params)
 {
 	struct i802_bss *bss = priv;
+#ifdef CONFIG_DRIVER_NL80211_QCA
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+
+	/*
+	 * Do a vendor specific scan if possible. If only_new_results is
+	 * set, do a normal scan since a kernel (cfg80211) BSS cache flush
+	 * cannot be achieved through a vendor scan. The below condition may
+	 * need to be modified if new scan flags are added in the future whose
+	 * functionality can only be achieved through a normal scan.
+	 */
+	if (drv->scan_vendor_cmd_avail && !params->only_new_results)
+		return wpa_driver_nl80211_vendor_scan(bss, params);
+#endif /* CONFIG_DRIVER_NL80211_QCA */
 	return wpa_driver_nl80211_scan(bss, params);
 }
 
@@ -7245,11 +7560,13 @@
 
 static int driver_nl80211_send_mlme(void *priv, const u8 *data,
 				    size_t data_len, int noack,
-				    unsigned int freq)
+				    unsigned int freq,
+				    const u16 *csa_offs, size_t csa_offs_len)
 {
 	struct i802_bss *bss = priv;
 	return wpa_driver_nl80211_send_mlme(bss, data, data_len, noack,
-					    freq, 0, 0, 0);
+					    freq, 0, 0, 0, csa_offs,
+					    csa_offs_len);
 }
 
 
@@ -7476,7 +7793,13 @@
 				  "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.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"
+				  "capa.max_sched_scan_plans=%u\n"
+				  "capa.max_sched_scan_plan_interval=%u\n"
+				  "capa.max_sched_scan_plan_iterations=%u\n",
 				  drv->capa.key_mgmt,
 				  drv->capa.enc,
 				  drv->capa.auth,
@@ -7492,7 +7815,13 @@
 				  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.mac_addr_rand_scan_supported,
+				  drv->capa.conc_capab,
+				  drv->capa.max_conc_chan_2_4,
+				  drv->capa.max_conc_chan_5_0,
+				  drv->capa.max_sched_scan_plans,
+				  drv->capa.max_sched_scan_plan_interval,
+				  drv->capa.max_sched_scan_plan_iterations);
 		if (os_snprintf_error(end - pos, res))
 			return pos - buf;
 		pos += res;
@@ -7535,6 +7864,8 @@
 	struct wpa_driver_nl80211_data *drv = bss->drv;
 	struct nlattr *beacon_csa;
 	int ret = -ENOBUFS;
+	int csa_off_len = 0;
+	int i;
 
 	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,
@@ -7551,20 +7882,56 @@
 	    (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)))
+	/*
+	 * Remove empty counters, assuming Probe Response and Beacon frame
+	 * counters match. This implementation assumes that there are only two
+	 * counters.
+	 */
+	if (settings->counter_offset_beacon[0] &&
+	    !settings->counter_offset_beacon[1]) {
+		csa_off_len = 1;
+	} else if (settings->counter_offset_beacon[1] &&
+		   !settings->counter_offset_beacon[0]) {
+		csa_off_len = 1;
+		settings->counter_offset_beacon[0] =
+			settings->counter_offset_beacon[1];
+		settings->counter_offset_presp[0] =
+			settings->counter_offset_presp[1];
+	} else if (settings->counter_offset_beacon[1] &&
+		   settings->counter_offset_beacon[0]) {
+		csa_off_len = 2;
+	} else {
+		wpa_printf(MSG_ERROR, "nl80211: No CSA counters provided");
+		return -EINVAL;
+	}
+
+	/* Check CSA counters validity */
+	if (drv->capa.max_csa_counters &&
+	    csa_off_len > drv->capa.max_csa_counters) {
+		wpa_printf(MSG_ERROR,
+			   "nl80211: Too many CSA counters provided");
+		return -EINVAL;
+	}
+
+	if (!settings->beacon_csa.tail)
 		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;
+	for (i = 0; i < csa_off_len; i++) {
+		u16 csa_c_off_bcn = settings->counter_offset_beacon[i];
+		u16 csa_c_off_presp = settings->counter_offset_presp[i];
+
+		if ((settings->beacon_csa.tail_len <= csa_c_off_bcn) ||
+		    (settings->beacon_csa.tail[csa_c_off_bcn] !=
+		     settings->cs_count))
+			return -EINVAL;
+
+		if (settings->beacon_csa.probe_resp &&
+		    ((settings->beacon_csa.probe_resp_len <=
+		      csa_c_off_presp) ||
+		     (settings->beacon_csa.probe_resp[csa_c_off_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,
@@ -7588,11 +7955,13 @@
 	if (ret)
 		goto error;
 
-	if (nla_put_u16(msg, NL80211_ATTR_CSA_C_OFF_BEACON,
-			settings->counter_offset_beacon) ||
+	if (nla_put(msg, NL80211_ATTR_CSA_C_OFF_BEACON,
+		    csa_off_len * sizeof(u16),
+		    settings->counter_offset_beacon) ||
 	    (settings->beacon_csa.probe_resp &&
-	     nla_put_u16(msg, NL80211_ATTR_CSA_C_OFF_PRESP,
-			 settings->counter_offset_presp)))
+	     nla_put(msg, NL80211_ATTR_CSA_C_OFF_PRESP,
+		     csa_off_len * sizeof(u16),
+		     settings->counter_offset_presp)))
 		goto fail;
 
 	nla_nest_end(msg, beacon_csa);
@@ -7838,6 +8207,7 @@
 }
 
 
+#ifdef CONFIG_DRIVER_NL80211_QCA
 static int nl80211_roaming(void *priv, int allowed, const u8 *bssid)
 {
 	struct i802_bss *bss = priv;
@@ -7870,6 +8240,7 @@
 
 	return send_and_recv_msgs(drv, msg, NULL, NULL);
 }
+#endif /* CONFIG_DRIVER_NL80211_QCA */
 
 
 static int nl80211_set_mac_addr(void *priv, const u8 *addr)
@@ -8354,6 +8725,8 @@
 }
 
 
+#ifdef CONFIG_DRIVER_NL80211_QCA
+
 static int hw_mode_to_qca_acs(enum hostapd_hw_mode hw_mode)
 {
 	switch (hw_mode) {
@@ -8373,6 +8746,26 @@
 }
 
 
+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;
@@ -8402,7 +8795,8 @@
 			params->ch_width) ||
 	    (params->ch_list_len &&
 	     nla_put(msg, QCA_WLAN_VENDOR_ATTR_ACS_CH_LIST, params->ch_list_len,
-		     params->ch_list))) {
+		     params->ch_list)) ||
+	    add_acs_freq_list(msg, params->freq_list)) {
 		nlmsg_free(msg);
 		return -ENOBUFS;
 	}
@@ -8470,6 +8864,195 @@
 }
 
 
+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;
+}
+
+#endif /* CONFIG_DRIVER_NL80211_QCA */
+
+
 const struct wpa_driver_ops wpa_driver_nl80211_ops = {
 	.name = "nl80211",
 	.desc = "Linux nl80211/cfg80211",
@@ -8480,6 +9063,7 @@
 	.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,
+	.abort_scan = wpa_driver_nl80211_abort_scan,
 	.deauthenticate = driver_nl80211_deauthenticate,
 	.authenticate = driver_nl80211_authenticate,
 	.associate = wpa_driver_nl80211_associate,
@@ -8563,7 +9147,9 @@
 	.vendor_cmd = nl80211_vendor_cmd,
 	.set_qos_map = nl80211_set_qos_map,
 	.set_wowlan = nl80211_set_wowlan,
+#ifdef CONFIG_DRIVER_NL80211_QCA
 	.roaming = nl80211_roaming,
+#endif /* CONFIG_DRIVER_NL80211_QCA */
 	.set_mac_addr = nl80211_set_mac_addr,
 #ifdef CONFIG_MESH
 	.init_mesh = wpa_driver_nl80211_init_mesh,
@@ -8576,6 +9162,11 @@
 	.br_set_net_param = wpa_driver_br_set_net_param,
 	.add_tx_ts = nl80211_add_ts,
 	.del_tx_ts = nl80211_del_ts,
+	.get_ifindex = nl80211_get_ifindex,
+#ifdef CONFIG_DRIVER_NL80211_QCA
 	.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,
+#endif /* CONFIG_DRIVER_NL80211_QCA */
 };
diff --git a/src/drivers/driver_nl80211.h b/src/drivers/driver_nl80211.h
index 1536d2f..b0d2b6d 100644
--- a/src/drivers/driver_nl80211.h
+++ b/src/drivers/driver_nl80211.h
@@ -25,6 +25,7 @@
 #endif /* CONFIG_LIBNL20 */
 
 struct nl80211_global {
+	void *ctx;
 	struct dl_list interfaces;
 	int if_add_ifindex;
 	u64 if_add_wdevid;
@@ -84,6 +85,7 @@
 	struct dl_list list;
 	struct dl_list wiphy_list;
 	char phyname[32];
+	unsigned int wiphy_idx;
 	u8 perm_addr[ETH_ALEN];
 	void *ctx;
 	int ifindex;
@@ -146,9 +148,17 @@
 	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;
+	unsigned int scan_vendor_cmd_avail:1;
+	unsigned int connect_reassoc:1;
 
+	u64 vendor_scan_cookie;
 	u64 remain_on_chan_cookie;
 	u64 send_action_cookie;
+#define MAX_SEND_ACTION_COOKIES 20
+	u64 send_action_cookies[MAX_SEND_ACTION_COOKIES];
+	unsigned int num_send_action_cookies;
 
 	unsigned int last_mgmt_freq;
 
@@ -164,7 +174,10 @@
 	struct nl_handle *rtnl_sk; /* nl_sock for NETLINK_ROUTE */
 
 	int default_if_indices[16];
+	/* the AP/AP_VLAN iface that is in this bridge */
+	int default_if_indices_reason[16];
 	int *if_indices;
+	int *if_indices_reason;
 	int num_if_indices;
 
 	/* From failed authentication command */
@@ -180,6 +193,13 @@
 	int auth_wep_tx_keyidx;
 	int auth_local_state_change;
 	int auth_p2p;
+
+	/*
+	 * Tells whether the last scan issued from wpa_supplicant was a normal
+	 * scan (NL80211_CMD_TRIGGER_SCAN) or a vendor scan
+	 * (NL80211_CMD_VENDOR). 0 if no pending scan request.
+	 */
+	int last_scan_cmd;
 };
 
 struct nl_msg;
@@ -265,11 +285,12 @@
 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);
+				  struct wpa_driver_scan_params *params);
 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);
+int wpa_driver_nl80211_abort_scan(void *priv);
+int wpa_driver_nl80211_vendor_scan(struct i802_bss *bss,
+				   struct wpa_driver_scan_params *params);
 
 #endif /* DRIVER_NL80211_H */
diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c
index 4929cea..14a93a0 100644
--- a/src/drivers/driver_nl80211_capa.c
+++ b/src/drivers/driver_nl80211_capa.c
@@ -428,6 +428,9 @@
 
 	if (flags & NL80211_FEATURE_HT_IBSS)
 		capa->flags |= WPA_DRIVER_FLAGS_HT_IBSS;
+
+	if (flags & NL80211_FEATURE_FULL_AP_CLIENT_STATE)
+		capa->flags |= WPA_DRIVER_FLAGS_FULL_AP_CLIENT_STATE;
 }
 
 
@@ -487,6 +490,9 @@
 	nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
 		  genlmsg_attrlen(gnlh, 0), NULL);
 
+	if (tb[NL80211_ATTR_WIPHY])
+		drv->wiphy_idx = nla_get_u32(tb[NL80211_ATTR_WIPHY]);
+
 	if (tb[NL80211_ATTR_WIPHY_NAME])
 		os_strlcpy(drv->phyname,
 			   nla_get_string(tb[NL80211_ATTR_WIPHY_NAME]),
@@ -499,6 +505,19 @@
 		capa->max_sched_scan_ssids =
 			nla_get_u8(tb[NL80211_ATTR_MAX_NUM_SCHED_SCAN_SSIDS]);
 
+	if (tb[NL80211_ATTR_MAX_NUM_SCHED_SCAN_PLANS] &&
+	    tb[NL80211_ATTR_MAX_SCAN_PLAN_INTERVAL] &&
+	    tb[NL80211_ATTR_MAX_SCAN_PLAN_ITERATIONS]) {
+		capa->max_sched_scan_plans =
+			nla_get_u32(tb[NL80211_ATTR_MAX_NUM_SCHED_SCAN_PLANS]);
+
+		capa->max_sched_scan_plan_interval =
+			nla_get_u32(tb[NL80211_ATTR_MAX_SCAN_PLAN_INTERVAL]);
+
+		capa->max_sched_scan_plan_iterations =
+			nla_get_u32(tb[NL80211_ATTR_MAX_SCAN_PLAN_ITERATIONS]);
+	}
+
 	if (tb[NL80211_ATTR_MAX_MATCH_SETS])
 		capa->max_match_sets =
 			nla_get_u8(tb[NL80211_ATTR_MAX_MATCH_SETS]);
@@ -580,6 +599,7 @@
 				case QCA_NL80211_VENDOR_SUBCMD_TEST:
 					drv->vendor_cmd_test_avail = 1;
 					break;
+#ifdef CONFIG_DRIVER_NL80211_QCA
 				case QCA_NL80211_VENDOR_SUBCMD_ROAMING:
 					drv->roaming_vendor_cmd_avail = 1;
 					break;
@@ -589,6 +609,12 @@
 				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;
@@ -596,6 +622,10 @@
 				case QCA_NL80211_VENDOR_SUBCMD_SETBAND:
 					drv->setband_vendor_cmd_avail = 1;
 					break;
+				case QCA_NL80211_VENDOR_SUBCMD_TRIGGER_SCAN:
+					drv->scan_vendor_cmd_avail = 1;
+					break;
+#endif /* CONFIG_DRIVER_NL80211_QCA */
 				}
 			}
 
@@ -627,6 +657,10 @@
 		capa->max_stations =
 			nla_get_u32(tb[NL80211_ATTR_MAX_AP_ASSOC_STA]);
 
+	if (tb[NL80211_ATTR_MAX_CSA_COUNTERS])
+		capa->max_csa_counters =
+			nla_get_u8(tb[NL80211_ATTR_MAX_CSA_COUNTERS]);
+
 	return NL_SKIP;
 }
 
@@ -683,8 +717,6 @@
 	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 =
@@ -692,10 +724,24 @@
 	drv->capa.mac_addr_rand_scan_supported =
 		info->mac_addr_rand_scan_supported;
 
+	if (info->channel_switch_supported) {
+		drv->capa.flags |= WPA_DRIVER_FLAGS_AP_CSA;
+		if (!drv->capa.max_csa_counters)
+			drv->capa.max_csa_counters = 1;
+	}
+
+	if (!drv->capa.max_sched_scan_plans) {
+		drv->capa.max_sched_scan_plans = 1;
+		drv->capa.max_sched_scan_plan_interval = UINT32_MAX;
+		drv->capa.max_sched_scan_plan_iterations = 0;
+	}
+
 	return 0;
 }
 
 
+#ifdef CONFIG_DRIVER_NL80211_QCA
+
 static int dfs_info_handler(struct nl_msg *msg, void *arg)
 {
 	struct nlattr *tb[NL80211_ATTR_MAX + 1];
@@ -751,6 +797,7 @@
 struct features_info {
 	u8 *flags;
 	size_t flags_len;
+	struct wpa_driver_capa *capa;
 };
 
 
@@ -776,6 +823,19 @@
 			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;
@@ -810,6 +870,7 @@
 	}
 
 	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;
@@ -819,8 +880,14 @@
 
 	if (check_feature(QCA_WLAN_VENDOR_FEATURE_SUPPORT_HW_MODE_ANY, &info))
 		drv->capa.flags |= WPA_DRIVER_FLAGS_SUPPORT_HW_MODE_ANY;
+
+	if (check_feature(QCA_WLAN_VENDOR_FEATURE_OFFCHANNEL_SIMULTANEOUS,
+			  &info))
+		drv->capa.flags |= WPA_DRIVER_FLAGS_OFFCHANNEL_SIMULTANEOUS;
 }
 
+#endif /* CONFIG_DRIVER_NL80211_QCA */
+
 
 int wpa_driver_nl80211_capa(struct wpa_driver_nl80211_data *drv)
 {
@@ -901,9 +968,21 @@
 	if (!drv->use_monitor && !info.data_tx_status)
 		drv->capa.flags &= ~WPA_DRIVER_FLAGS_EAPOL_TX_STATUS;
 
+#ifdef CONFIG_DRIVER_NL80211_QCA
 	qca_nl80211_check_dfs_capa(drv);
 	qca_nl80211_get_features(drv);
 
+	/*
+	 * To enable offchannel simultaneous support in wpa_supplicant, the
+	 * underlying driver needs to support the same along with offchannel TX.
+	 * Offchannel TX support is needed since remain_on_channel and
+	 * action_tx use some common data structures and hence cannot be
+	 * scheduled simultaneously.
+	 */
+	if (!(drv->capa.flags & WPA_DRIVER_FLAGS_OFFCHANNEL_TX))
+		drv->capa.flags &= ~WPA_DRIVER_FLAGS_OFFCHANNEL_SIMULTANEOUS;
+#endif /* CONFIG_DRIVER_NL80211_QCA */
+
 	return 0;
 }
 
@@ -912,6 +991,7 @@
 	u16 *num_modes;
 	struct hostapd_hw_modes *modes;
 	int last_mode, last_chan_idx;
+	int failed;
 };
 
 static void phy_info_ht_capa(struct hostapd_hw_modes *mode, struct nlattr *capa,
@@ -1029,7 +1109,7 @@
 				   mode->num_channels + new_channels,
 				   sizeof(struct hostapd_channel_data));
 	if (!channel)
-		return NL_SKIP;
+		return NL_STOP;
 
 	mode->channels = channel;
 	mode->num_channels += new_channels;
@@ -1075,7 +1155,7 @@
 
 	mode->rates = os_calloc(mode->num_rates, sizeof(int));
 	if (!mode->rates)
-		return NL_SKIP;
+		return NL_STOP;
 
 	idx = 0;
 
@@ -1104,8 +1184,10 @@
 		mode = os_realloc_array(phy_info->modes,
 					*phy_info->num_modes + 1,
 					sizeof(*mode));
-		if (!mode)
-			return NL_SKIP;
+		if (!mode) {
+			phy_info->failed = 1;
+			return NL_STOP;
+		}
 		phy_info->modes = mode;
 
 		mode = &phy_info->modes[*(phy_info->num_modes)];
@@ -1141,11 +1223,12 @@
 	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)
+	if (ret == NL_OK)
+		ret = phy_info_rates(mode, tb_band[NL80211_BAND_ATTR_RATES]);
+	if (ret != NL_OK) {
+		phy_info->failed = 1;
 		return ret;
-	ret = phy_info_rates(mode, tb_band[NL80211_BAND_ATTR_RATES]);
-	if (ret != NL_OK)
-		return ret;
+	}
 
 	return NL_OK;
 }
@@ -1360,7 +1443,7 @@
 
 
 static void nl80211_set_vht_mode(struct hostapd_hw_modes *mode, int start,
-				 int end)
+				 int end, int max_bw)
 {
 	int c;
 
@@ -1377,6 +1460,32 @@
 
 		if (chan->freq - 70 >= start && chan->freq + 10 <= end)
 			chan->flag |= HOSTAPD_CHAN_VHT_70_10;
+
+		if (max_bw >= 160) {
+			if (chan->freq - 10 >= start && chan->freq + 150 <= end)
+				chan->flag |= HOSTAPD_CHAN_VHT_10_150;
+
+			if (chan->freq - 30 >= start && chan->freq + 130 <= end)
+				chan->flag |= HOSTAPD_CHAN_VHT_30_130;
+
+			if (chan->freq - 50 >= start && chan->freq + 110 <= end)
+				chan->flag |= HOSTAPD_CHAN_VHT_50_110;
+
+			if (chan->freq - 70 >= start && chan->freq + 90 <= end)
+				chan->flag |= HOSTAPD_CHAN_VHT_70_90;
+
+			if (chan->freq - 90 >= start && chan->freq + 70 <= end)
+				chan->flag |= HOSTAPD_CHAN_VHT_90_70;
+
+			if (chan->freq - 110 >= start && chan->freq + 50 <= end)
+				chan->flag |= HOSTAPD_CHAN_VHT_110_50;
+
+			if (chan->freq - 130 >= start && chan->freq + 30 <= end)
+				chan->flag |= HOSTAPD_CHAN_VHT_130_30;
+
+			if (chan->freq - 150 >= start && chan->freq + 10 <= end)
+				chan->flag |= HOSTAPD_CHAN_VHT_150_10;
+		}
 	}
 }
 
@@ -1407,7 +1516,7 @@
 		if (!results->modes[m].vht_capab)
 			continue;
 
-		nl80211_set_vht_mode(&results->modes[m], start, end);
+		nl80211_set_vht_mode(&results->modes[m], start, end, max_bw);
 	}
 }
 
@@ -1545,6 +1654,7 @@
 		.num_modes = num_modes,
 		.modes = NULL,
 		.last_mode = -1,
+		.failed = 0,
 	};
 
 	*num_modes = 0;
@@ -1561,6 +1671,16 @@
 
 	if (send_and_recv_msgs(drv, msg, phy_info_handler, &result) == 0) {
 		nl80211_set_regulatory_flags(drv, &result);
+		if (result.failed) {
+			int i;
+
+			for (i = 0; result.modes && i < *num_modes; i++) {
+				os_free(result.modes[i].channels);
+				os_free(result.modes[i].rates);
+			}
+			os_free(result.modes);
+			return NULL;
+		}
 		return wpa_driver_nl80211_postprocess_modes(result.modes,
 							    num_modes);
 	}
diff --git a/src/drivers/driver_nl80211_event.c b/src/drivers/driver_nl80211_event.c
index 7b0f721..bd16edb 100644
--- a/src/drivers/driver_nl80211_event.c
+++ b/src/drivers/driver_nl80211_event.c
@@ -268,7 +268,8 @@
 			       struct nlattr *authorized,
 			       struct nlattr *key_replay_ctr,
 			       struct nlattr *ptk_kck,
-			       struct nlattr *ptk_kek)
+			       struct nlattr *ptk_kek,
+			       struct nlattr *subnet_status)
 {
 	union wpa_event_data event;
 	const u8 *ssid;
@@ -284,6 +285,8 @@
 		return;
 	}
 
+	drv->connect_reassoc = 0;
+
 	status_code = status ? nla_get_u16(status) : WLAN_STATUS_SUCCESS;
 
 	if (cmd == NL80211_CMD_CONNECT) {
@@ -334,9 +337,9 @@
 		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);
+			ssid = 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]);
@@ -367,6 +370,17 @@
 		event.assoc_info.ptk_kek_len = nla_len(ptk_kek);
 	}
 
+	if (subnet_status) {
+		/*
+		 * At least for now, this is only available from
+		 * QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_SUBNET_STATUS and that
+		 * attribute has the same values 0, 1, 2 as are used in the
+		 * variable here, so no mapping between different values are
+		 * needed.
+		 */
+		event.assoc_info.subnet_status = nla_get_u8(subnet_status);
+	}
+
 	wpa_supplicant_event(drv->ctx, EVENT_ASSOC, &event);
 }
 
@@ -560,9 +574,10 @@
 		rx_freq = drv->last_mgmt_freq = event.rx_mgmt.freq;
 	}
 	wpa_printf(MSG_DEBUG,
-		   "nl80211: RX frame sa=" MACSTR
+		   "nl80211: RX frame da=" MACSTR " sa=" MACSTR " bssid=" 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,
+		   MAC2STR(mgmt->da), MAC2STR(mgmt->sa), MAC2STR(mgmt->bssid),
+		   rx_freq, ssi_signal, fc,
 		   le_to_host16(mgmt->seq_ctrl), stype, fc2str(fc),
 		   (unsigned int) len);
 	event.rx_mgmt.frame = frame;
@@ -639,10 +654,39 @@
 			 * 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.
+			 * In case of locally generated event clear
+			 * ignore_next_local_deauth as well, to avoid next local
+			 * deauth event be wrongly ignored.
 			 */
-			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));
+			if (!os_memcmp(mgmt->sa, drv->first_bss->addr,
+				       ETH_ALEN)) {
+				wpa_printf(MSG_DEBUG,
+					   "nl80211: Received a locally generated deauth event. Clear ignore_next_local_deauth flag");
+				drv->ignore_next_local_deauth = 0;
+			} else {
+				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->capa.flags & WPA_DRIVER_FLAGS_SME) &&
+		    drv->connect_reassoc && drv->associated &&
+		    os_memcmp(bssid, drv->prev_bssid, ETH_ALEN) == 0 &&
+		    os_memcmp(bssid, drv->auth_attempt_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 connecting with " MACSTR,
+				    MAC2STR(bssid),
+				    MAC2STR(drv->auth_attempt_bssid));
 			return;
 		}
 
@@ -679,13 +723,15 @@
 				mgmt->u.disassoc.variable;
 		}
 	} else {
+		event.deauth_info.locally_generated =
+			!os_memcmp(mgmt->sa, drv->first_bss->addr, ETH_ALEN);
 		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;
+			if (event.deauth_info.locally_generated)
+				drv->ignore_next_local_deauth = 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) {
@@ -968,7 +1014,7 @@
 
 
 static void send_scan_event(struct wpa_driver_nl80211_data *drv, int aborted,
-			    struct nlattr *tb[])
+			    struct nlattr *tb[], int external_scan)
 {
 	union wpa_event_data event;
 	struct nlattr *nl;
@@ -978,7 +1024,7 @@
 	int freqs[MAX_REPORT_FREQS];
 	int num_freqs = 0;
 
-	if (drv->scan_for_auth) {
+	if (!external_scan && drv->scan_for_auth) {
 		drv->scan_for_auth = 0;
 		wpa_printf(MSG_DEBUG, "nl80211: Scan results for missing "
 			   "cfg80211 BSS entry");
@@ -989,6 +1035,8 @@
 	os_memset(&event, 0, sizeof(event));
 	info = &event.scan_info;
 	info->aborted = aborted;
+	info->external_scan = external_scan;
+	info->nl_scan_event = 1;
 
 	if (tb[NL80211_ATTR_SCAN_SSIDS]) {
 		nla_for_each_nested(nl, tb[NL80211_ATTR_SCAN_SSIDS], rem) {
@@ -1004,7 +1052,7 @@
 		}
 	}
 	if (tb[NL80211_ATTR_SCAN_FREQUENCIES]) {
-		char msg[200], *pos, *end;
+		char msg[300], *pos, *end;
 		int res;
 
 		pos = msg;
@@ -1109,7 +1157,7 @@
 		return;
 
 	addr = nla_data(tb[NL80211_ATTR_MAC]);
-	wpa_printf(MSG_DEBUG, "nl80211: New peer candidate" MACSTR,
+	wpa_printf(MSG_DEBUG, "nl80211: New peer candidate " MACSTR,
 		   MAC2STR(addr));
 
 	os_memset(&data, 0, sizeof(data));
@@ -1154,6 +1202,7 @@
 
 
 static void nl80211_del_station_event(struct wpa_driver_nl80211_data *drv,
+				      struct i802_bss *bss,
 				      struct nlattr **tb)
 {
 	u8 *addr;
@@ -1166,7 +1215,7 @@
 		   MAC2STR(addr));
 
 	if (is_ap_interface(drv->nlmode) && drv->device_ap_sme) {
-		drv_event_disassoc(drv->ctx, addr);
+		drv_event_disassoc(bss->ctx, addr);
 		return;
 	}
 
@@ -1175,7 +1224,7 @@
 
 	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);
+	wpa_supplicant_event(bss->ctx, EVENT_IBSS_PEER_LOST, &data);
 }
 
 
@@ -1444,6 +1493,8 @@
 }
 
 
+#ifdef CONFIG_DRIVER_NL80211_QCA
+
 static void qca_nl80211_avoid_freq(struct wpa_driver_nl80211_data *drv,
 				   const u8 *data, size_t len)
 {
@@ -1596,7 +1647,8 @@
 			   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]);
+			   tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_PTK_KEK],
+			   tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_SUBNET_STATUS]);
 }
 
 
@@ -1686,6 +1738,140 @@
 }
 
 
+static void qca_nl80211_scan_trigger_event(struct wpa_driver_nl80211_data *drv,
+					   u8 *data, size_t len)
+{
+	struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_SCAN_MAX + 1];
+	u64 cookie = 0;
+	union wpa_event_data event;
+	struct scan_info *info;
+
+	if (nla_parse(tb, QCA_WLAN_VENDOR_ATTR_SCAN_MAX,
+		      (struct nlattr *) data, len, NULL) ||
+	    !tb[QCA_WLAN_VENDOR_ATTR_SCAN_COOKIE])
+		return;
+
+	cookie = nla_get_u64(tb[QCA_WLAN_VENDOR_ATTR_SCAN_COOKIE]);
+	if (cookie != drv->vendor_scan_cookie) {
+		/* External scan trigger event, ignore */
+		return;
+	}
+
+	/* Cookie match, own scan */
+	os_memset(&event, 0, sizeof(event));
+	info = &event.scan_info;
+	info->external_scan = 0;
+	info->nl_scan_event = 0;
+
+	drv->scan_state = SCAN_STARTED;
+	wpa_supplicant_event(drv->ctx, EVENT_SCAN_STARTED, &event);
+}
+
+
+static void send_vendor_scan_event(struct wpa_driver_nl80211_data *drv,
+				   int aborted, struct nlattr *tb[],
+				   int external_scan)
+{
+	union wpa_event_data event;
+	struct nlattr *nl;
+	int rem;
+	struct scan_info *info;
+	int freqs[MAX_REPORT_FREQS];
+	int num_freqs = 0;
+
+	os_memset(&event, 0, sizeof(event));
+	info = &event.scan_info;
+	info->aborted = aborted;
+	info->external_scan = external_scan;
+
+	if (tb[QCA_WLAN_VENDOR_ATTR_SCAN_SSIDS]) {
+		nla_for_each_nested(nl,
+				    tb[QCA_WLAN_VENDOR_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[QCA_WLAN_VENDOR_ATTR_SCAN_FREQUENCIES]) {
+		char msg[300], *pos, *end;
+		int res;
+
+		pos = msg;
+		end = pos + sizeof(msg);
+		*pos = '\0';
+
+		nla_for_each_nested(nl,
+				    tb[QCA_WLAN_VENDOR_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 qca_nl80211_scan_done_event(struct wpa_driver_nl80211_data *drv,
+					u8 *data, size_t len)
+{
+	struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_SCAN_MAX + 1];
+	u64 cookie = 0;
+	enum scan_status status;
+	int external_scan;
+
+	if (nla_parse(tb, QCA_WLAN_VENDOR_ATTR_SCAN_MAX,
+		      (struct nlattr *) data, len, NULL) ||
+	    !tb[QCA_WLAN_VENDOR_ATTR_SCAN_STATUS] ||
+	    !tb[QCA_WLAN_VENDOR_ATTR_SCAN_COOKIE])
+		return;
+
+	status = nla_get_u8(tb[QCA_WLAN_VENDOR_ATTR_SCAN_STATUS]);
+	if (status >= VENDOR_SCAN_STATUS_MAX)
+		return; /* invalid status */
+
+	cookie = nla_get_u64(tb[QCA_WLAN_VENDOR_ATTR_SCAN_COOKIE]);
+	if (cookie != drv->vendor_scan_cookie) {
+		/* Event from an external scan, get scan results */
+		external_scan = 1;
+	} else {
+		external_scan = 0;
+		if (status == VENDOR_SCAN_STATUS_NEW_RESULTS)
+			drv->scan_state = SCAN_COMPLETED;
+		else
+			drv->scan_state = SCAN_ABORTED;
+
+		eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, drv,
+				     drv->ctx);
+		drv->vendor_scan_cookie = 0;
+		drv->last_scan_cmd = 0;
+	}
+
+	send_vendor_scan_event(drv, (status == VENDOR_SCAN_STATUS_ABORTED), tb,
+			       external_scan);
+}
+
+#endif /* CONFIG_DRIVER_NL80211_QCA */
+
+
 static void nl80211_vendor_event_qca(struct wpa_driver_nl80211_data *drv,
 				     u32 subcmd, u8 *data, size_t len)
 {
@@ -1693,6 +1879,7 @@
 	case QCA_NL80211_VENDOR_SUBCMD_TEST:
 		wpa_hexdump(MSG_DEBUG, "nl80211: QCA test event", data, len);
 		break;
+#ifdef CONFIG_DRIVER_NL80211_QCA
 	case QCA_NL80211_VENDOR_SUBCMD_AVOID_FREQUENCY:
 		qca_nl80211_avoid_freq(drv, data, len);
 		break;
@@ -1709,6 +1896,13 @@
 	case QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_RADAR_DETECTED:
 		qca_nl80211_dfs_offload_radar_event(drv, subcmd, data, len);
 		break;
+	case QCA_NL80211_VENDOR_SUBCMD_TRIGGER_SCAN:
+		qca_nl80211_scan_trigger_event(drv, data, len);
+		break;
+	case QCA_NL80211_VENDOR_SUBCMD_SCAN_DONE:
+		qca_nl80211_scan_done_event(drv, data, len);
+		break;
+#endif /* CONFIG_DRIVER_NL80211_QCA */
 	default:
 		wpa_printf(MSG_DEBUG,
 			   "nl80211: Ignore unsupported QCA vendor event %u",
@@ -1831,6 +2025,7 @@
 {
 	struct wpa_driver_nl80211_data *drv = bss->drv;
 	union wpa_event_data data;
+	int external_scan_event = 0;
 
 	wpa_printf(MSG_DEBUG, "nl80211: Drv Event %d (%s) received for %s",
 		   cmd, nl80211_command_to_string(cmd), bss->ifname);
@@ -1883,28 +2078,38 @@
 	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);
+		if (drv->last_scan_cmd == NL80211_CMD_TRIGGER_SCAN) {
+			drv->scan_state = SCAN_COMPLETED;
+			eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout,
+					     drv, drv->ctx);
+			drv->last_scan_cmd = 0;
+		} else {
+			external_scan_event = 1;
+		}
+		send_scan_event(drv, 0, tb, external_scan_event);
 		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);
+		send_scan_event(drv, 0, tb, 0);
 		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);
+		if (drv->last_scan_cmd == NL80211_CMD_TRIGGER_SCAN) {
+			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);
+			drv->last_scan_cmd = 0;
+		} else {
+			external_scan_event = 1;
+		}
+		send_scan_event(drv, 1, tb, external_scan_event);
 		break;
 	case NL80211_CMD_AUTHENTICATE:
 	case NL80211_CMD_ASSOCIATE:
@@ -1927,7 +2132,7 @@
 				   tb[NL80211_ATTR_MAC],
 				   tb[NL80211_ATTR_REQ_IE],
 				   tb[NL80211_ATTR_RESP_IE],
-				   NULL, NULL, NULL, NULL);
+				   NULL, NULL, NULL, NULL, NULL);
 		break;
 	case NL80211_CMD_CH_SWITCH_NOTIFY:
 		mlme_event_ch_switch(drv,
@@ -1972,7 +2177,7 @@
 		nl80211_new_station_event(drv, bss, tb);
 		break;
 	case NL80211_CMD_DEL_STATION:
-		nl80211_del_station_event(drv, tb);
+		nl80211_del_station_event(drv, bss, tb);
 		break;
 	case NL80211_CMD_SET_REKEY_OFFLOAD:
 		nl80211_rekey_offload_event(drv, tb);
diff --git a/src/drivers/driver_nl80211_scan.c b/src/drivers/driver_nl80211_scan.c
index f3d45e5..c089891 100644
--- a/src/drivers/driver_nl80211_scan.c
+++ b/src/drivers/driver_nl80211_scan.c
@@ -1,5 +1,6 @@
 /*
  * Driver interaction with Linux nl80211/cfg80211 - Scanning
+ * Copyright(c) 2015 Intel Deutschland GmbH
  * Copyright (c) 2002-2014, Jouni Malinen <j@w1.fi>
  * Copyright (c) 2007, Johannes Berg <johannes@sipsolutions.net>
  * Copyright (c) 2009-2010, Atheros Communications
@@ -14,6 +15,8 @@
 #include "utils/common.h"
 #include "utils/eloop.h"
 #include "common/ieee802_11_defs.h"
+#include "common/ieee802_11_common.h"
+#include "common/qca-vendor.h"
 #include "driver_nl80211.h"
 
 
@@ -93,12 +96,20 @@
 void wpa_driver_nl80211_scan_timeout(void *eloop_ctx, void *timeout_ctx)
 {
 	struct wpa_driver_nl80211_data *drv = eloop_ctx;
+
+	wpa_printf(MSG_DEBUG, "nl80211: Scan timeout - try to abort it");
+	if (!wpa_driver_nl80211_abort_scan(drv->first_bss))
+		return;
+
+	wpa_printf(MSG_DEBUG, "nl80211: Failed to abort scan");
+
 	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_printf(MSG_DEBUG, "nl80211: Try to get scan results");
 	wpa_supplicant_event(timeout_ctx, EVENT_SCAN_RESULTS, NULL);
 }
 
@@ -131,6 +142,8 @@
 				goto fail;
 		}
 		nla_nest_end(msg, ssids);
+	} else {
+		wpa_printf(MSG_DEBUG, "nl80211: Passive scan requested");
 	}
 
 	if (params->extra_ies) {
@@ -221,6 +234,9 @@
 	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;
@@ -249,6 +265,13 @@
 			goto fail;
 	}
 
+	if (params->bssid) {
+		wpa_printf(MSG_DEBUG, "nl80211: Scan for a specific BSSID: "
+			   MACSTR, MAC2STR(params->bssid));
+		if (nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, params->bssid))
+			goto fail;
+	}
+
 	ret = send_and_recv_msgs(drv, msg, NULL, NULL);
 	msg = NULL;
 	if (ret) {
@@ -294,6 +317,7 @@
 	eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, drv, drv->ctx);
 	eloop_register_timeout(timeout, 0, wpa_driver_nl80211_scan_timeout,
 			       drv, drv->ctx);
+	drv->last_scan_cmd = NL80211_CMD_TRIGGER_SCAN;
 
 fail:
 	nlmsg_free(msg);
@@ -301,16 +325,82 @@
 }
 
 
+static int
+nl80211_sched_scan_add_scan_plans(struct wpa_driver_nl80211_data *drv,
+				  struct nl_msg *msg,
+				  struct wpa_driver_scan_params *params)
+{
+	struct nlattr *plans;
+	struct sched_scan_plan *scan_plans = params->sched_scan_plans;
+	unsigned int i;
+
+	plans = nla_nest_start(msg, NL80211_ATTR_SCHED_SCAN_PLANS);
+	if (!plans)
+		return -1;
+
+	for (i = 0; i < params->sched_scan_plans_num; i++) {
+		struct nlattr *plan = nla_nest_start(msg, i + 1);
+
+		if (!plan)
+			return -1;
+
+		if (!scan_plans[i].interval ||
+		    scan_plans[i].interval >
+		    drv->capa.max_sched_scan_plan_interval) {
+			wpa_printf(MSG_DEBUG,
+				   "nl80211: sched scan plan no. %u: Invalid interval: %u",
+				   i, scan_plans[i].interval);
+			return -1;
+		}
+
+		if (nla_put_u32(msg, NL80211_SCHED_SCAN_PLAN_INTERVAL,
+				scan_plans[i].interval))
+			return -1;
+
+		if (scan_plans[i].iterations >
+		    drv->capa.max_sched_scan_plan_iterations) {
+			wpa_printf(MSG_DEBUG,
+				   "nl80211: sched scan plan no. %u: Invalid number of iterations: %u",
+				   i, scan_plans[i].iterations);
+			return -1;
+		}
+
+		if (scan_plans[i].iterations &&
+		    nla_put_u32(msg, NL80211_SCHED_SCAN_PLAN_ITERATIONS,
+				scan_plans[i].iterations))
+			return -1;
+
+		nla_nest_end(msg, plan);
+
+		/*
+		 * All the scan plans must specify the number of iterations
+		 * except the last plan, which will run infinitely. So if the
+		 * number of iterations is not specified, this ought to be the
+		 * last scan plan.
+		 */
+		if (!scan_plans[i].iterations)
+			break;
+	}
+
+	if (i != params->sched_scan_plans_num - 1) {
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: All sched scan plans but the last must specify number of iterations");
+		return -1;
+	}
+
+	nla_nest_end(msg, plans);
+	return 0;
+}
+
+
 /**
  * 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 wpa_driver_scan_params *params)
 {
 	struct i802_bss *bss = priv;
 	struct wpa_driver_nl80211_data *drv = bss->drv;
@@ -325,11 +415,27 @@
 		return android_pno_start(bss, params);
 #endif /* ANDROID */
 
+	if (!params->sched_scan_plans_num ||
+	    params->sched_scan_plans_num > drv->capa.max_sched_scan_plans) {
+		wpa_printf(MSG_ERROR,
+			   "nl80211: Invalid number of sched scan plans: %u",
+			   params->sched_scan_plans_num);
+		return -1;
+	}
+
 	msg = nl80211_scan_common(bss, NL80211_CMD_START_SCHED_SCAN, params);
-	if (!msg ||
-	    nla_put_u32(msg, NL80211_ATTR_SCHED_SCAN_INTERVAL, interval))
+	if (!msg)
 		goto fail;
 
+	if (drv->capa.max_sched_scan_plan_iterations) {
+		if (nl80211_sched_scan_add_scan_plans(drv, msg, params))
+			goto fail;
+	} else {
+		if (nla_put_u32(msg, NL80211_ATTR_SCHED_SCAN_INTERVAL,
+				params->sched_scan_plans[0].interval * 1000))
+			goto fail;
+	}
+
 	if ((drv->num_filter_ssids &&
 	    (int) drv->num_filter_ssids <= drv->capa.max_match_sets) ||
 	    params->filter_rssi) {
@@ -392,8 +498,7 @@
 		goto fail;
 	}
 
-	wpa_printf(MSG_DEBUG, "nl80211: Sched scan requested (ret=%d) - "
-		   "scan interval %d msec", ret, interval);
+	wpa_printf(MSG_DEBUG, "nl80211: Sched scan requested (ret=%d)", ret);
 
 fail:
 	nlmsg_free(msg);
@@ -433,28 +538,6 @@
 }
 
 
-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)
 {
@@ -464,7 +547,7 @@
 	if (drv->filter_ssids == NULL)
 		return 0;
 
-	ssid = nl80211_get_ie(ie, ie_len, WLAN_EID_SSID);
+	ssid = get_ie(ie, ie_len, WLAN_EID_SSID);
 	if (ssid == NULL)
 		return 1;
 
@@ -625,9 +708,9 @@
 		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);
+		s1 = get_ie((u8 *) (res->res[i] + 1),
+			    res->res[i]->ie_len, WLAN_EID_SSID);
+		s2 = 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;
@@ -778,3 +861,213 @@
 
 	wpa_scan_results_free(res);
 }
+
+
+int wpa_driver_nl80211_abort_scan(void *priv)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	int ret;
+	struct nl_msg *msg;
+
+	wpa_printf(MSG_DEBUG, "nl80211: Abort scan");
+	msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_ABORT_SCAN);
+	ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+	if (ret) {
+		wpa_printf(MSG_DEBUG, "nl80211: Abort scan failed: ret=%d (%s)",
+			   ret, strerror(-ret));
+	}
+
+	return ret;
+}
+
+
+#ifdef CONFIG_DRIVER_NL80211_QCA
+
+static int scan_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_VENDOR_DATA]) {
+		struct nlattr *nl_vendor = tb[NL80211_ATTR_VENDOR_DATA];
+		struct nlattr *tb_vendor[QCA_WLAN_VENDOR_ATTR_SCAN_MAX + 1];
+
+		nla_parse(tb_vendor, QCA_WLAN_VENDOR_ATTR_SCAN_MAX,
+			  nla_data(nl_vendor), nla_len(nl_vendor), NULL);
+
+		if (tb_vendor[QCA_WLAN_VENDOR_ATTR_SCAN_COOKIE])
+			*cookie = nla_get_u64(
+				tb_vendor[QCA_WLAN_VENDOR_ATTR_SCAN_COOKIE]);
+	}
+
+	return NL_SKIP;
+}
+
+
+/**
+ * wpa_driver_nl80211_vendor_scan - Request the driver to initiate a vendor 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_vendor_scan(struct i802_bss *bss,
+				   struct wpa_driver_scan_params *params)
+{
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	struct nl_msg *msg = NULL;
+	struct nlattr *attr;
+	size_t i;
+	u32 scan_flags = 0;
+	int ret = -1;
+	u64 cookie = 0;
+
+	wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: vendor scan request");
+	drv->scan_for_auth = 0;
+
+	if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
+	    nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
+	    nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
+			QCA_NL80211_VENDOR_SUBCMD_TRIGGER_SCAN) )
+		goto fail;
+
+	attr = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA);
+	if (attr == NULL)
+		goto fail;
+
+	if (params->num_ssids) {
+		struct nlattr *ssids;
+
+		ssids = nla_nest_start(msg, QCA_WLAN_VENDOR_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, QCA_WLAN_VENDOR_ATTR_SCAN_IE,
+			    params->extra_ies_len, params->extra_ies))
+			goto fail;
+	}
+
+	if (params->freqs) {
+		struct nlattr *freqs;
+
+		freqs = nla_nest_start(msg,
+				       QCA_WLAN_VENDOR_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->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, QCA_WLAN_VENDOR_ATTR_SCAN_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, QCA_WLAN_VENDOR_ATTR_SCAN_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;
+
+	if (params->p2p_probe) {
+		struct nlattr *rates;
+
+		wpa_printf(MSG_DEBUG, "nl80211: P2P probe - mask SuppRates");
+
+		rates = nla_nest_start(msg,
+				       QCA_WLAN_VENDOR_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, QCA_WLAN_VENDOR_ATTR_SCAN_TX_NO_CCK_RATE))
+			goto fail;
+	}
+
+	nla_nest_end(msg, attr);
+
+	ret = send_and_recv_msgs(drv, msg, scan_cookie_handler, &cookie);
+	msg = NULL;
+	if (ret) {
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: Vendor scan trigger failed: ret=%d (%s)",
+			   ret, strerror(-ret));
+		goto fail;
+	}
+
+	drv->vendor_scan_cookie = cookie;
+	drv->scan_state = SCAN_REQUESTED;
+
+	wpa_printf(MSG_DEBUG,
+		   "nl80211: Vendor scan requested (ret=%d) - scan timeout 30 seconds, scan cookie:0x%llx",
+		   ret, (long long unsigned int) cookie);
+	eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, drv, drv->ctx);
+	eloop_register_timeout(30, 0, wpa_driver_nl80211_scan_timeout,
+			       drv, drv->ctx);
+	drv->last_scan_cmd = NL80211_CMD_VENDOR;
+
+fail:
+	nlmsg_free(msg);
+	return ret;
+}
+
+#endif /* CONFIG_DRIVER_NL80211_QCA */
diff --git a/src/drivers/driver_privsep.c b/src/drivers/driver_privsep.c
index 1cfc15d..43d4193 100644
--- a/src/drivers/driver_privsep.c
+++ b/src/drivers/driver_privsep.c
@@ -161,11 +161,11 @@
 		return NULL;
 	}
 
-	while (results->num < (size_t) num && pos + sizeof(int) < end) {
+	while (results->num < (size_t) num && end - pos > (int) sizeof(int)) {
 		int len;
 		os_memcpy(&len, pos, sizeof(int));
 		pos += sizeof(int);
-		if (len < 0 || len > 10000 || pos + len > end)
+		if (len < 0 || len > 10000 || len > end - pos)
 			break;
 
 		r = os_malloc(len);
@@ -220,6 +220,56 @@
 }
 
 
+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)
 {
@@ -309,6 +359,32 @@
 }
 
 
+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)
@@ -468,6 +544,9 @@
 	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);
@@ -503,6 +582,9 @@
 		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);
@@ -703,6 +785,10 @@
 	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;
 }
 
@@ -735,6 +821,7 @@
 	.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,
diff --git a/src/drivers/driver_wext.c b/src/drivers/driver_wext.c
index 01defdf..791cd5d 100644
--- a/src/drivers/driver_wext.c
+++ b/src/drivers/driver_wext.c
@@ -422,7 +422,7 @@
 
 
 static void wpa_driver_wext_event_wireless(struct wpa_driver_wext_data *drv,
-					   char *data, int len)
+					   char *data, unsigned int len)
 {
 	struct iw_event iwe_buf, *iwe = &iwe_buf;
 	char *pos, *end, *custom, *buf;
@@ -430,13 +430,13 @@
 	pos = data;
 	end = data + len;
 
-	while (pos + IW_EV_LCP_LEN <= end) {
+	while ((size_t) (end - pos) >= IW_EV_LCP_LEN) {
 		/* 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)
+		if (iwe->len <= IW_EV_LCP_LEN || iwe->len > end - pos)
 			return;
 
 		custom = pos + IW_EV_POINT_LEN;
@@ -480,7 +480,7 @@
 			}
 			break;
 		case IWEVMICHAELMICFAILURE:
-			if (custom + iwe->u.data.length > end) {
+			if (iwe->u.data.length > end - custom) {
 				wpa_printf(MSG_DEBUG, "WEXT: Invalid "
 					   "IWEVMICHAELMICFAILURE length");
 				return;
@@ -489,7 +489,7 @@
 				drv->ctx, custom, iwe->u.data.length);
 			break;
 		case IWEVCUSTOM:
-			if (custom + iwe->u.data.length > end) {
+			if (iwe->u.data.length > end - custom) {
 				wpa_printf(MSG_DEBUG, "WEXT: Invalid "
 					   "IWEVCUSTOM length");
 				return;
@@ -508,7 +508,7 @@
 					     NULL);
 			break;
 		case IWEVASSOCREQIE:
-			if (custom + iwe->u.data.length > end) {
+			if (iwe->u.data.length > end - custom) {
 				wpa_printf(MSG_DEBUG, "WEXT: Invalid "
 					   "IWEVASSOCREQIE length");
 				return;
@@ -517,7 +517,7 @@
 				drv, custom, iwe->u.data.length);
 			break;
 		case IWEVASSOCRESPIE:
-			if (custom + iwe->u.data.length > end) {
+			if (iwe->u.data.length > end - custom) {
 				wpa_printf(MSG_DEBUG, "WEXT: Invalid "
 					   "IWEVASSOCRESPIE length");
 				return;
@@ -526,7 +526,7 @@
 				drv, custom, iwe->u.data.length);
 			break;
 		case IWEVPMKIDCAND:
-			if (custom + iwe->u.data.length > end) {
+			if (iwe->u.data.length > end - custom) {
 				wpa_printf(MSG_DEBUG, "WEXT: Invalid "
 					   "IWEVPMKIDCAND length");
 				return;
@@ -1220,7 +1220,7 @@
 			       char *end)
 {
 	int ssid_len = iwe->u.essid.length;
-	if (custom + ssid_len > end)
+	if (ssid_len > end - custom)
 		return;
 	if (iwe->u.essid.flags &&
 	    ssid_len > 0 &&
@@ -1316,7 +1316,7 @@
 	size_t clen;
 
 	clen = iwe->len;
-	if (custom + clen > end)
+	if (clen > (size_t) (end - custom))
 		return;
 	maxrate = 0;
 	while (((ssize_t) clen) >= (ssize_t) sizeof(struct iw_param)) {
@@ -1369,7 +1369,7 @@
 	u8 *tmp;
 
 	clen = iwe->u.data.length;
-	if (custom + clen > end)
+	if (clen > (size_t) (end - custom))
 		return;
 
 	if (clen > 7 && os_strncmp(custom, "wpa_ie=", 7) == 0) {
@@ -1441,8 +1441,8 @@
 	/* 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)
+	while (pos && end - pos > 1) {
+		if (2 + pos[1] > end - pos)
 			break;
 		if (pos[0] == WLAN_EID_SSID)
 			ssid_ie = pos;
@@ -1530,11 +1530,11 @@
 	end = (char *) res_buf + len;
 	os_memset(&data, 0, sizeof(data));
 
-	while (pos + IW_EV_LCP_LEN <= end) {
+	while ((size_t) (end - pos) >= IW_EV_LCP_LEN) {
 		/* 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)
+		if (iwe->len <= IW_EV_LCP_LEN || iwe->len > end - pos)
 			break;
 
 		custom = pos + IW_EV_POINT_LEN;
diff --git a/src/drivers/driver_wired.c b/src/drivers/driver_wired.c
index f95f3cc..15e82df 100644
--- a/src/drivers/driver_wired.c
+++ b/src/drivers/driver_wired.c
@@ -8,6 +8,11 @@
  */
 
 #include "includes.h"
+
+#include "common.h"
+#include "eloop.h"
+#include "driver.h"
+
 #include <sys/ioctl.h>
 #include <net/if.h>
 #ifdef __linux__
@@ -23,10 +28,6 @@
 #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 */
diff --git a/src/drivers/drivers.mak b/src/drivers/drivers.mak
index 9434078..c1bfff1 100644
--- a/src/drivers/drivers.mak
+++ b/src/drivers/drivers.mak
@@ -30,6 +30,9 @@
 DRV_OBJS += ../src/drivers/driver_nl80211_monitor.o
 DRV_OBJS += ../src/drivers/driver_nl80211_scan.o
 DRV_OBJS += ../src/utils/radiotap.o
+ifdef CONFIG_DRIVER_NL80211_QCA
+DRV_CFLAGS += -DCONFIG_DRIVER_NL80211_QCA
+endif
 NEED_SME=y
 NEED_AP_MLME=y
 NEED_NETLINK=y
@@ -54,11 +57,15 @@
   ifdef CONFIG_LIBNL_TINY
     DRV_LIBS += -lnl-tiny
   else
-    DRV_LIBS += -lnl
+    ifndef CONFIG_OSX
+      DRV_LIBS += -lnl
+    endif
   endif
 
   ifdef CONFIG_LIBNL20
-    DRV_LIBS += -lnl-genl
+    ifndef CONFIG_LIBNL_TINY
+      DRV_LIBS += -lnl-genl
+    endif
     DRV_CFLAGS += -DCONFIG_LIBNL20
   endif
 endif
diff --git a/src/drivers/drivers.mk b/src/drivers/drivers.mk
index 8da4c53..0444e52 100644
--- a/src/drivers/drivers.mk
+++ b/src/drivers/drivers.mk
@@ -26,6 +26,9 @@
 DRV_OBJS += src/drivers/driver_nl80211_monitor.c
 DRV_OBJS += src/drivers/driver_nl80211_scan.c
 DRV_OBJS += src/utils/radiotap.c
+ifdef CONFIG_DRIVER_NL80211_QCA
+DRV_CFLAGS += -DCONFIG_DRIVER_NL80211_QCA
+endif
 NEED_SME=y
 NEED_AP_MLME=y
 NEED_NETLINK=y
@@ -48,7 +51,9 @@
   endif
 
   ifdef CONFIG_LIBNL20
-    DRV_LIBS += -lnl-genl
+    ifndef CONFIG_LIBNL_TINY
+      DRV_LIBS += -lnl-genl
+    endif
     DRV_CFLAGS += -DCONFIG_LIBNL20
   endif
 endif
diff --git a/src/drivers/linux_ioctl.c b/src/drivers/linux_ioctl.c
index 837971d..e21147a 100644
--- a/src/drivers/linux_ioctl.c
+++ b/src/drivers/linux_ioctl.c
@@ -219,3 +219,26 @@
 	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/src/drivers/linux_ioctl.h b/src/drivers/linux_ioctl.h
index c03fe6e..6de4d9b 100644
--- a/src/drivers/linux_ioctl.h
+++ b/src/drivers/linux_ioctl.h
@@ -18,5 +18,6 @@
 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/src/drivers/nl80211_copy.h b/src/drivers/nl80211_copy.h
index ae16ba9..7758969 100644
--- a/src/drivers/nl80211_copy.h
+++ b/src/drivers/nl80211_copy.h
@@ -10,6 +10,7 @@
  * Copyright 2008, 2009 Luis R. Rodriguez <lrodriguez@atheros.com>
  * Copyright 2008 Jouni Malinen <jouni.malinen@atheros.com>
  * Copyright 2008 Colin McCabe <colin@cozybit.com>
+ * Copyright 2015	Intel Deutschland GmbH
  *
  * Permission to use, copy, modify, and/or distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
@@ -328,7 +329,15 @@
  *	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.
+ *	intervals and certain number of cycles, as specified by
+ *	%NL80211_ATTR_SCHED_SCAN_PLANS. If %NL80211_ATTR_SCHED_SCAN_PLANS is
+ *	not specified and only %NL80211_ATTR_SCHED_SCAN_INTERVAL is specified,
+ *	scheduled scan will run in an infinite loop with the specified interval.
+ *	These attributes are mutually exculsive,
+ *	i.e. NL80211_ATTR_SCHED_SCAN_INTERVAL must not be passed if
+ *	NL80211_ATTR_SCHED_SCAN_PLANS is defined.
+ *	If for some reason scheduled scan is aborted by the driver, all scan
+ *	plans are canceled (including scan plans that did not start yet).
  *	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
@@ -811,6 +820,10 @@
  *	as an event to indicate changes for devices with wiphy-specific regdom
  *	management.
  *
+ * @NL80211_CMD_ABORT_SCAN: Stop an ongoing scan. Returns -ENOENT if a scan is
+ *	not running. The driver indicates the status of the scan through
+ *	cfg80211_scan_done().
+ *
  * @NL80211_CMD_MAX: highest used command number
  * @__NL80211_CMD_AFTER_LAST: internal use
  */
@@ -997,6 +1010,8 @@
 
 	NL80211_CMD_WIPHY_REG_CHANGE,
 
+	NL80211_CMD_ABORT_SCAN,
+
 	/* add new commands above here */
 
 	/* used to define NL80211_CMD_MAX below */
@@ -1754,12 +1769,31 @@
  *	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_SCHED_SCAN_DELAY: delay before the first cycle of a
+ *	scheduled scan is started.  Or the delay before a WoWLAN
+ *	net-detect scan is started, counting from the moment the
+ *	system is suspended.  This value is a u32, in seconds.
 
  * @NL80211_ATTR_REG_INDOOR: flag attribute, if set indicates that the device
  *      is operating in an indoor environment.
  *
+ * @NL80211_ATTR_MAX_NUM_SCHED_SCAN_PLANS: maximum number of scan plans for
+ *	scheduled scan supported by the device (u32), a wiphy attribute.
+ * @NL80211_ATTR_MAX_SCAN_PLAN_INTERVAL: maximum interval (in seconds) for
+ *	a scan plan (u32), a wiphy attribute.
+ * @NL80211_ATTR_MAX_SCAN_PLAN_ITERATIONS: maximum number of iterations in
+ *	a scan plan (u32), a wiphy attribute.
+ * @NL80211_ATTR_SCHED_SCAN_PLANS: a list of scan plans for scheduled scan.
+ *	Each scan plan defines the number of scan iterations and the interval
+ *	between scans. The last scan plan will always run infinitely,
+ *	thus it must not specify the number of iterations, only the interval
+ *	between scans. The scan plans are executed sequentially.
+ *	Each scan plan is a nested attribute of &enum nl80211_sched_scan_plan.
+ * @NL80211_ATTR_PBSS: flag attribute. If set it means operate
+ *	in a PBSS. Specified in %NL80211_CMD_CONNECT to request
+ *	connecting to a PCP, and in %NL80211_CMD_START_AP to start
+ *	a PCP instead of AP. Relevant for DMG networks only.
+ *
  * @NUM_NL80211_ATTR: total number of nl80211_attrs available
  * @NL80211_ATTR_MAX: highest attribute number currently defined
  * @__NL80211_ATTR_AFTER_LAST: internal use
@@ -2129,6 +2163,13 @@
 
 	NL80211_ATTR_REG_INDOOR,
 
+	NL80211_ATTR_MAX_NUM_SCHED_SCAN_PLANS,
+	NL80211_ATTR_MAX_SCAN_PLAN_INTERVAL,
+	NL80211_ATTR_MAX_SCAN_PLAN_ITERATIONS,
+	NL80211_ATTR_SCHED_SCAN_PLANS,
+
+	NL80211_ATTR_PBSS,
+
 	/* add attributes here, update the policy in nl80211.c */
 
 	__NL80211_ATTR_AFTER_LAST,
@@ -2619,16 +2660,17 @@
  *	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
+ * @NL80211_FREQUENCY_ATTR_IR_CONCURRENT: IR 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)
+ *	band), and IEEE80211_CHAN_RADAR is not set. Instantiating a GO or TDLS
+ *	off-channel on a channel that has the IR_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 or TDLS
+ *	off-channel 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). Using this
+ *	attribute for IR is disallowed for master interfaces (IBSS, AP).
  * @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
@@ -2640,7 +2682,7 @@
  * 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.
+ * NL80211_FREQUENCY_ATTR_IR_CONCURRENT.
  */
 enum nl80211_frequency_attr {
 	__NL80211_FREQUENCY_ATTR_INVALID,
@@ -2658,7 +2700,7 @@
 	NL80211_FREQUENCY_ATTR_NO_160MHZ,
 	NL80211_FREQUENCY_ATTR_DFS_CAC_TIME,
 	NL80211_FREQUENCY_ATTR_INDOOR_ONLY,
-	NL80211_FREQUENCY_ATTR_GO_CONCURRENT,
+	NL80211_FREQUENCY_ATTR_IR_CONCURRENT,
 	NL80211_FREQUENCY_ATTR_NO_20MHZ,
 	NL80211_FREQUENCY_ATTR_NO_10MHZ,
 
@@ -2671,6 +2713,8 @@
 #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
+#define NL80211_FREQUENCY_ATTR_GO_CONCURRENT \
+					NL80211_FREQUENCY_ATTR_IR_CONCURRENT
 
 /**
  * enum nl80211_bitrate_attr - bitrate attributes
@@ -2829,7 +2873,7 @@
  * @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_IR_CONCURRENT: See &NL80211_FREQUENCY_ATTR_IR_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
@@ -2846,7 +2890,7 @@
 	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_IR_CONCURRENT	= 1<<12,
 	NL80211_RRF_NO_HT40MINUS	= 1<<13,
 	NL80211_RRF_NO_HT40PLUS		= 1<<14,
 	NL80211_RRF_NO_80MHZ		= 1<<15,
@@ -2858,6 +2902,7 @@
 #define NL80211_RRF_NO_IR		NL80211_RRF_NO_IR
 #define NL80211_RRF_NO_HT40		(NL80211_RRF_NO_HT40MINUS |\
 					 NL80211_RRF_NO_HT40PLUS)
+#define NL80211_RRF_GO_CONCURRENT	NL80211_RRF_IR_CONCURRENT
 
 /* For backport compatibility with older userspace */
 #define NL80211_RRF_NO_IR_ALL		(NL80211_RRF_NO_IR | __NL80211_RRF_NO_IBSS)
@@ -3359,6 +3404,9 @@
  *	(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_LAST_SEEN_BOOTTIME: CLOCK_BOOTTIME timestamp when this entry
+ *	was last updated by a received frame. The value is expected to be
+ *	accurate to about 10ms. (u64, nanoseconds)
  * @__NL80211_BSS_AFTER_LAST: internal
  * @NL80211_BSS_MAX: highest BSS attribute
  */
@@ -3378,6 +3426,7 @@
 	NL80211_BSS_CHAN_WIDTH,
 	NL80211_BSS_BEACON_TSF,
 	NL80211_BSS_PRESP_DATA,
+	NL80211_BSS_LAST_SEEN_BOOTTIME,
 
 	/* keep last */
 	__NL80211_BSS_AFTER_LAST,
@@ -4584,4 +4633,28 @@
 	NL80211_TDLS_PEER_WMM = 1<<2,
 };
 
+/**
+ * enum nl80211_sched_scan_plan - scanning plan for scheduled scan
+ * @__NL80211_SCHED_SCAN_PLAN_INVALID: attribute number 0 is reserved
+ * @NL80211_SCHED_SCAN_PLAN_INTERVAL: interval between scan iterations. In
+ *	seconds (u32).
+ * @NL80211_SCHED_SCAN_PLAN_ITERATIONS: number of scan iterations in this
+ *	scan plan (u32). The last scan plan must not specify this attribute
+ *	because it will run infinitely. A value of zero is invalid as it will
+ *	make the scan plan meaningless.
+ * @NL80211_SCHED_SCAN_PLAN_MAX: highest scheduled scan plan attribute number
+ *	currently defined
+ * @__NL80211_SCHED_SCAN_PLAN_AFTER_LAST: internal use
+ */
+enum nl80211_sched_scan_plan {
+	__NL80211_SCHED_SCAN_PLAN_INVALID,
+	NL80211_SCHED_SCAN_PLAN_INTERVAL,
+	NL80211_SCHED_SCAN_PLAN_ITERATIONS,
+
+	/* keep last */
+	__NL80211_SCHED_SCAN_PLAN_AFTER_LAST,
+	NL80211_SCHED_SCAN_PLAN_MAX =
+		__NL80211_SCHED_SCAN_PLAN_AFTER_LAST - 1
+};
+
 #endif /* __LINUX_NL80211_H */
diff --git a/src/drivers/rfkill.c b/src/drivers/rfkill.c
index 45b26c4..4d4d1b4 100644
--- a/src/drivers/rfkill.c
+++ b/src/drivers/rfkill.c
@@ -8,6 +8,7 @@
 
 #include "includes.h"
 #include <fcntl.h>
+#include <limits.h>
 
 #include "utils/common.h"
 #include "utils/eloop.h"
@@ -47,6 +48,7 @@
 	struct rfkill_config *cfg;
 	int fd;
 	int blocked;
+	uint32_t idx;
 };
 
 
@@ -69,12 +71,13 @@
 			   (int) len, RFKILL_EVENT_SIZE_V1);
 		return;
 	}
+	if (event.op != RFKILL_OP_CHANGE || event.idx != rfkill->idx)
+		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");
@@ -102,11 +105,23 @@
 	struct rfkill_data *rfkill;
 	struct rfkill_event event;
 	ssize_t len;
+	char *phy = NULL, *rfk_phy;
+	char buf[24 + IFNAMSIZ + 1];
+	char buf2[31 + 11 + 1];
+	int found = 0;
 
 	rfkill = os_zalloc(sizeof(*rfkill));
 	if (rfkill == NULL)
 		return NULL;
 
+	os_snprintf(buf, sizeof(buf), "/sys/class/net/%s/phy80211",
+		    cfg->ifname);
+	phy = realpath(buf, NULL);
+	if (!phy) {
+		wpa_printf(MSG_INFO, "rfkill: Cannot get wiphy information");
+		goto fail;
+	}
+
 	rfkill->cfg = cfg;
 	rfkill->fd = open("/dev/rfkill", O_RDONLY);
 	if (rfkill->fd < 0) {
@@ -136,13 +151,27 @@
 				   (int) len, RFKILL_EVENT_SIZE_V1);
 			continue;
 		}
+		if (event.op != RFKILL_OP_ADD ||
+		    event.type != RFKILL_TYPE_WLAN)
+			continue;
+
+		os_snprintf(buf2, sizeof(buf2),
+			    "/sys/class/rfkill/rfkill%d/device", event.idx);
+		rfk_phy = realpath(buf2, NULL);
+		if (!rfk_phy)
+			goto fail2;
+		found = os_strcmp(phy, rfk_phy) == 0;
+		free(rfk_phy);
+
+		if (!found)
+			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;
+
+		rfkill->idx = event.idx;
 		if (event.hard) {
 			wpa_printf(MSG_INFO, "rfkill: WLAN hard blocked");
 			rfkill->blocked = 1;
@@ -150,8 +179,13 @@
 			wpa_printf(MSG_INFO, "rfkill: WLAN soft blocked");
 			rfkill->blocked = 1;
 		}
+		break;
 	}
 
+	if (!found)
+		goto fail2;
+
+	free(phy);
 	eloop_register_read_sock(rfkill->fd, rfkill_receive, rfkill, NULL);
 
 	return rfkill;
@@ -160,6 +194,8 @@
 	close(rfkill->fd);
 fail:
 	os_free(rfkill);
+	/* use standard free function to match realpath() */
+	free(phy);
 	return NULL;
 }
 
diff --git a/src/eap_common/eap_eke_common.c b/src/eap_common/eap_eke_common.c
index 4dfdb3f..6217468 100644
--- a/src/eap_common/eap_eke_common.c
+++ b/src/eap_common/eap_eke_common.c
@@ -44,9 +44,7 @@
 	int dhlen;
 
 	dhlen = eap_eke_dh_len(dhgroup);
-	if (dhlen < 0)
-		return -1;
-	if (encr != EAP_EKE_ENCR_AES128_CBC)
+	if (dhlen < 0 || encr != EAP_EKE_ENCR_AES128_CBC)
 		return -1;
 	return AES_BLOCK_SIZE + dhlen;
 }
@@ -166,14 +164,11 @@
 	size_t pub_len, i;
 
 	generator = eap_eke_dh_generator(group);
-	if (generator < 0 || generator > 255)
+	dh = eap_eke_dh_group(group);
+	if (generator < 0 || generator > 255 || !dh)
 		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;
@@ -411,11 +406,8 @@
 	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)
+	if (sess->encr != EAP_EKE_ENCR_AES128_CBC || !dh)
 		return -1;
 
 	/* Decrypt peer DHComponent */
@@ -635,6 +627,7 @@
 
 	if (*prot_len < block_size + data_len + pad + icv_len) {
 		wpa_printf(MSG_INFO, "EAP-EKE: Not enough room for Prot() data");
+		return -1;
 	}
 	pos = prot;
 
@@ -653,10 +646,8 @@
 		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)
+	if (aes_128_cbc_encrypt(sess->ke, iv, e, data_len + pad) < 0 ||
+	    eap_eke_mac(sess->mac, sess->ki, e, data_len + pad, pos) < 0)
 		return -1;
 	pos += icv_len;
 
@@ -684,9 +675,8 @@
 	else
 		return -1;
 
-	if (prot_len < 2 * block_size + icv_len)
-		return -1;
-	if ((prot_len - icv_len) % block_size)
+	if (prot_len < 2 * block_size + icv_len ||
+	    (prot_len - icv_len) % block_size)
 		return -1;
 
 	if (eap_eke_mac(sess->mac, sess->ki, prot + block_size,
@@ -737,22 +727,14 @@
 	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)
+	if (sess->prf_len < 0 || sess->nonce_len < 0 || sess->auth_len < 0 ||
+	    sess->dhcomp_len < 0 || sess->pnonce_len < 0 ||
+	    sess->pnonce_ps_len < 0)
 		return -1;
 
 	return 0;
diff --git a/src/eap_common/eap_fast_common.c b/src/eap_common/eap_fast_common.c
index 151cc78..e8587fd 100644
--- a/src/eap_common/eap_fast_common.c
+++ b/src/eap_common/eap_fast_common.c
@@ -111,22 +111,24 @@
 }
 
 
-void eap_fast_derive_eap_msk(const u8 *simck, u8 *msk)
+int 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);
+	if (sha1_t_prf(simck, EAP_FAST_SIMCK_LEN,
+		       "Session Key Generating Function", (u8 *) "", 0,
+		       msk, EAP_FAST_KEY_LEN) < 0)
+		return -1;
 	wpa_hexdump_key(MSG_DEBUG, "EAP-FAST: Derived key (MSK)",
 			msk, EAP_FAST_KEY_LEN);
+	return 0;
 }
 
 
-void eap_fast_derive_eap_emsk(const u8 *simck, u8 *emsk)
+int eap_fast_derive_eap_emsk(const u8 *simck, u8 *emsk)
 {
 	/*
 	 * RFC 4851, Section 5.4: EAP Master Session Key Genreration
@@ -134,11 +136,13 @@
 	 *        "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);
+	if (sha1_t_prf(simck, EAP_FAST_SIMCK_LEN,
+		       "Extended Session Key Generating Function", (u8 *) "", 0,
+		       emsk, EAP_EMSK_LEN) < 0)
+		return -1;
 	wpa_hexdump_key(MSG_DEBUG, "EAP-FAST: Derived key (EMSK)",
 			emsk, EAP_EMSK_LEN);
+	return 0;
 }
 
 
diff --git a/src/eap_common/eap_fast_common.h b/src/eap_common/eap_fast_common.h
index d59a845..6756dd2 100644
--- a/src/eap_common/eap_fast_common.h
+++ b/src/eap_common/eap_fast_common.h
@@ -99,8 +99,8 @@
 				   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_derive_eap_msk(const u8 *simck, u8 *msk);
+int 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);
 
diff --git a/src/eap_common/eap_gpsk_common.c b/src/eap_common/eap_gpsk_common.c
index 8c7ae27..b081879 100644
--- a/src/eap_common/eap_gpsk_common.c
+++ b/src/eap_common/eap_gpsk_common.c
@@ -92,7 +92,8 @@
 	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);
+		if (hmac_sha256_vector(psk, 32, 2, addr, vlen, hash))
+			return -1;
 		clen = left > hashlen ? hashlen : left;
 		os_memcpy(opos, hash, clen);
 		opos += clen;
@@ -534,8 +535,7 @@
 		break;
 #ifdef EAP_GPSK_SHA256
 	case EAP_GPSK_CIPHER_SHA256:
-		hmac_sha256(sk, sk_len, data, len, mic);
-		ret = 0;
+		ret = hmac_sha256(sk, sk_len, data, len, mic);
 		break;
 #endif /* EAP_GPSK_SHA256 */
 	default:
@@ -545,5 +545,8 @@
 		break;
 	}
 
+	if (ret)
+		wpa_printf(MSG_DEBUG, "EAP-GPSK: Could not compute MIC");
+
 	return ret;
 }
diff --git a/src/eap_common/eap_pax_common.c b/src/eap_common/eap_pax_common.c
index 0e80ef5..a11bce8 100644
--- a/src/eap_common/eap_pax_common.c
+++ b/src/eap_common/eap_pax_common.c
@@ -57,7 +57,8 @@
 	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);
+		if (hmac_sha1_vector(key, key_len, 3, addr, len, mac) < 0)
+			return -1;
 		os_memcpy(pos, mac, clen);
 		pos += clen;
 		left -= clen;
@@ -106,7 +107,8 @@
 	len[2] = data3_len;
 
 	count = (data1 ? 1 : 0) + (data2 ? 1 : 0) + (data3 ? 1 : 0);
-	hmac_sha1_vector(key, key_len, count, addr, len, hash);
+	if (hmac_sha1_vector(key, key_len, count, addr, len, hash) < 0)
+		return -1;
 	os_memcpy(mac, hash, EAP_PAX_MAC_LEN);
 
 	return 0;
diff --git a/src/eap_common/eap_pwd_common.c b/src/eap_common/eap_pwd_common.c
index 4d27623..67f8f70 100644
--- a/src/eap_common/eap_pwd_common.c
+++ b/src/eap_common/eap_pwd_common.c
@@ -115,6 +115,26 @@
         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:
 		wpa_printf(MSG_INFO, "EAP-pwd: unsupported group %d", num);
 		return -1;
diff --git a/src/eap_common/eap_sake_common.c b/src/eap_common/eap_sake_common.c
index c22e43e..8819541 100644
--- a/src/eap_common/eap_sake_common.c
+++ b/src/eap_common/eap_sake_common.c
@@ -121,7 +121,7 @@
 		attr->next_tmpid_len = len;
 		break;
 	case EAP_SAKE_AT_MSK_LIFE:
-		wpa_printf(MSG_DEBUG, "EAP-SAKE: Parse: AT_IV");
+		wpa_printf(MSG_DEBUG, "EAP-SAKE: Parse: AT_MSK_LIFE");
 		if (len != 4) {
 			wpa_printf(MSG_DEBUG, "EAP-SAKE: Invalid "
 				   "AT_MSK_LIFE payload length %d", len);
diff --git a/src/eap_common/ikev2_common.c b/src/eap_common/ikev2_common.c
index d60358c..90fb89e 100644
--- a/src/eap_common/ikev2_common.c
+++ b/src/eap_common/ikev2_common.c
@@ -62,13 +62,15 @@
 	case AUTH_HMAC_SHA1_96:
 		if (key_len != 20)
 			return -1;
-		hmac_sha1(key, key_len, data, data_len, tmphash);
+		if (hmac_sha1(key, key_len, data, data_len, tmphash) < 0)
+			return -1;
 		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);
+		if (hmac_md5(key, key_len, data, data_len, tmphash) < 0)
+			return -1;
 		os_memcpy(hash, tmphash, 12);
 		break;
 	default:
@@ -98,16 +100,13 @@
 {
 	switch (alg) {
 	case PRF_HMAC_SHA1:
-		hmac_sha1_vector(key, key_len, num_elem, addr, len, hash);
-		break;
+		return hmac_sha1_vector(key, key_len, num_elem, addr, len,
+					hash);
 	case PRF_HMAC_MD5:
-		hmac_md5_vector(key, key_len, num_elem, addr, len, hash);
-		break;
+		return hmac_md5_vector(key, key_len, num_elem, addr, len, hash);
 	default:
 		return -1;
 	}
-
-	return 0;
 }
 
 
diff --git a/src/eap_peer/eap.c b/src/eap_peer/eap.c
index 1dbe003..9110ca5 100644
--- a/src/eap_peer/eap.c
+++ b/src/eap_peer/eap.c
@@ -48,6 +48,8 @@
 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 void eap_sm_request(struct eap_sm *sm, enum wpa_ctrl_req_type field,
+			   const char *msg, size_t msglen);
 
 
 
@@ -188,6 +190,14 @@
 	 */
 	eapol_set_bool(sm, EAPOL_eapResp, FALSE);
 	eapol_set_bool(sm, EAPOL_eapNoResp, FALSE);
+	/*
+	 * RFC 4137 does not reset ignore here, but since it is possible for
+	 * some method code paths to end up not setting ignore=FALSE, clear the
+	 * value here to avoid issues if a previous authentication attempt
+	 * failed with ignore=TRUE being left behind in the last
+	 * m.check(eapReqData) operation.
+	 */
+	sm->ignore = 0;
 	sm->num_rounds = 0;
 	sm->prev_failure = 0;
 	sm->expected_failure = 0;
@@ -312,11 +322,14 @@
 	wpa_printf(MSG_DEBUG, "EAP: Initialize selected EAP method: "
 		   "vendor %u method %u (%s)",
 		   sm->reqVendor, method, sm->m->name);
-	if (reinit)
+	if (reinit) {
 		sm->eap_method_priv = sm->m->init_for_reauth(
 			sm, sm->eap_method_priv);
-	else
+	} else {
+		sm->waiting_ext_cert_check = 0;
+		sm->ext_cert_check = 0;
 		sm->eap_method_priv = sm->m->init(sm);
+	}
 
 	if (sm->eap_method_priv == NULL) {
 		struct eap_peer_config *config = eap_get_config(sm);
@@ -584,7 +597,7 @@
 	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, EAP_ERP_TYPE_REAUTH,
+	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)
@@ -708,7 +721,7 @@
 	wpabuf_free(sm->lastRespData);
 	if (sm->eapRespData) {
 		if (sm->workaround)
-			os_memcpy(sm->last_md5, sm->req_md5, 16);
+			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);
@@ -914,12 +927,12 @@
 
 	duplicate = (sm->reqId == sm->lastId) && sm->rxReq;
 	if (sm->workaround && duplicate &&
-	    os_memcmp(sm->req_md5, sm->last_md5, 16) != 0) {
+	    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 MD5 hash as
+		 * unfortunately, such implementations exist. Use SHA1 hash as
 		 * an extra verification for the packets being duplicate to
 		 * workaround these issues.
 		 */
@@ -1373,13 +1386,10 @@
 	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
@@ -1393,24 +1403,20 @@
 		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 */
 }
 
+#endif /* PCSC_FUNCS */
+
 
 /**
  * eap_sm_buildIdentity - Build EAP-Identity/Response for the current network
@@ -1453,23 +1459,27 @@
 				  identity, identity_len);
 	}
 
-	if (identity == NULL) {
-		wpa_printf(MSG_WARNING, "EAP: buildIdentity: identity "
-			   "configuration was not available");
-		if (config->pcsc) {
+	if (config->pcsc) {
+#ifdef PCSC_FUNCS
+		if (!identity) {
 			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);
+			wpa_hexdump_ascii(MSG_DEBUG,
+					  "permanent identity from IMSI",
+					  identity, identity_len);
+		} else if (eap_sm_set_scard_pin(sm, config) < 0) {
 			return NULL;
 		}
-	} else if (config->pcsc) {
-		if (eap_sm_set_scard_pin(sm, config) < 0)
-			return NULL;
+#else /* PCSC_FUNCS */
+		return NULL;
+#endif /* PCSC_FUNCS */
+	} else if (!identity) {
+		wpa_printf(MSG_WARNING,
+			"EAP: buildIdentity: identity configuration was not available");
+		eap_sm_request_identity(sm);
+		return NULL;
 	}
 
 	resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_IDENTITY, identity_len,
@@ -1510,15 +1520,9 @@
 
 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;
+	return eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_NOTIFICATION, 0,
+			EAP_CODE_RESPONSE, id);
 }
 
 
@@ -1765,7 +1769,7 @@
 	if (sm->workaround) {
 		const u8 *addr[1];
 		addr[0] = wpabuf_head(req);
-		md5_vector(1, addr, &plen, sm->req_md5);
+		sha1_vector(1, addr, &plen, sm->req_sha1);
 	}
 
 	switch (hdr->code) {
@@ -1850,6 +1854,11 @@
 	case TLS_CERT_CHAIN_SUCCESS:
 		eap_notify_status(sm, "remote certificate verification",
 				  "success");
+		if (sm->ext_cert_check) {
+			sm->waiting_ext_cert_check = 1;
+			eap_sm_request(sm, WPA_CTRL_REQ_EXT_CERT_CHECK,
+				       NULL, 0);
+		}
 		break;
 	case TLS_CERT_CHAIN_FAILURE:
 		wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_TLS_CERT_ERROR
@@ -2172,10 +2181,10 @@
 #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)
 {
+#if defined(CONFIG_CTRL_IFACE) || !defined(CONFIG_NO_STDOUT_DEBUG)
 	struct eap_peer_config *config;
 	const char *txt = NULL;
 	char *tmp;
@@ -2224,16 +2233,17 @@
 	case WPA_CTRL_REQ_SIM:
 		txt = msg;
 		break;
+	case WPA_CTRL_REQ_EXT_CERT_CHECK:
+		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)
 {
diff --git a/src/eap_peer/eap_aka.c b/src/eap_peer/eap_aka.c
index dc9e8cc..0bac62d 100644
--- a/src/eap_peer/eap_aka.c
+++ b/src/eap_peer/eap_aka.c
@@ -1492,7 +1492,6 @@
 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");
@@ -1511,10 +1510,7 @@
 	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;
+	return eap_peer_method_register(eap);
 }
 
 
@@ -1522,7 +1518,6 @@
 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,
@@ -1542,10 +1537,6 @@
 	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;
+	return eap_peer_method_register(eap);
 }
 #endif /* EAP_AKA_PRIME */
diff --git a/src/eap_peer/eap_config.h b/src/eap_peer/eap_config.h
index 2b1a1d5..39ddcff 100644
--- a/src/eap_peer/eap_config.h
+++ b/src/eap_peer/eap_config.h
@@ -739,6 +739,20 @@
 	 * erp - Whether EAP Re-authentication Protocol (ERP) is enabled
 	 */
 	int erp;
+
+	/**
+	 * pending_ext_cert_check - External server certificate check status
+	 *
+	 * This field should not be set in configuration step. It is only used
+	 * internally when control interface is used to request external
+	 * validation of server certificate chain.
+	 */
+	enum {
+		NO_CHECK = 0,
+		PENDING_CHECK,
+		EXT_CERT_CHECK_GOOD,
+		EXT_CERT_CHECK_BAD,
+	} pending_ext_cert_check;
 };
 
 
diff --git a/src/eap_peer/eap_eke.c b/src/eap_peer/eap_eke.c
index dfbda56..f899f65 100644
--- a/src/eap_peer/eap_eke.c
+++ b/src/eap_peer/eap_eke.c
@@ -452,6 +452,7 @@
 	/* DHComponent_P = Encr(key, y_p) */
 	rpos = wpabuf_put(resp, data->sess.dhcomp_len);
 	if (eap_eke_dhcomp(&data->sess, key, pub, rpos) < 0) {
+		wpabuf_free(resp);
 		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,
@@ -770,7 +771,6 @@
 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");
@@ -785,8 +785,5 @@
 	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;
+	return eap_peer_method_register(eap);
 }
diff --git a/src/eap_peer/eap_fast.c b/src/eap_peer/eap_fast.c
index f636e74..f03cd4a 100644
--- a/src/eap_peer/eap_fast.c
+++ b/src/eap_peer/eap_fast.c
@@ -1,6 +1,6 @@
 /*
  * EAP peer method: EAP-FAST (RFC 4851)
- * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi>
+ * 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.
@@ -67,6 +67,7 @@
 	int simck_idx;
 
 	struct wpabuf *pending_phase2_req;
+	struct wpabuf *pending_resp;
 };
 
 
@@ -112,8 +113,8 @@
 }
 
 
-static int eap_fast_parse_phase1(struct eap_fast_data *data,
-				 const char *phase1)
+static void eap_fast_parse_phase1(struct eap_fast_data *data,
+				  const char *phase1)
 {
 	const char *pos;
 
@@ -139,8 +140,6 @@
 		wpa_printf(MSG_DEBUG, "EAP-FAST: Using binary format for PAC "
 			   "list");
 	}
-
-	return 0;
 }
 
 
@@ -158,10 +157,8 @@
 	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 (config->phase1)
+		eap_fast_parse_phase1(data, config->phase1);
 
 	if (eap_peer_select_phase2_methods(config, "auth=",
 					   &data->phase2_types,
@@ -254,14 +251,16 @@
 	os_memset(data->emsk, 0, EAP_EMSK_LEN);
 	os_free(data->session_id);
 	wpabuf_free(data->pending_phase2_req);
+	wpabuf_free(data->pending_resp);
 	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);
+	if (eap_fast_derive_eap_msk(data->simck, data->key_data) < 0 ||
+	    eap_fast_derive_eap_emsk(data->simck, data->emsk) < 0)
+		return -1;
 	data->success = 1;
 	return 0;
 }
@@ -1096,7 +1095,7 @@
 	/* Parse TLVs from the decrypted Phase 2 data */
 	pos = wpabuf_mhead(decrypted);
 	end = pos + wpabuf_len(decrypted);
-	while (pos + 4 < end) {
+	while (end - pos > 4) {
 		mandatory = pos[0] & 0x80;
 		tlv_type = WPA_GET_BE16(pos) & 0x3fff;
 		pos += 2;
@@ -1443,7 +1442,7 @@
 static int eap_fast_set_provisioning_ciphers(struct eap_sm *sm,
 					     struct eap_fast_data *data)
 {
-	u8 ciphers[5];
+	u8 ciphers[7];
 	int count = 0;
 
 	if (data->provisioning_allowed & EAP_FAST_PROV_UNAUTH) {
@@ -1455,7 +1454,9 @@
 	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_AES256_SHA;
 		ciphers[count++] = TLS_CIPHER_RSA_DHE_AES128_SHA;
+		ciphers[count++] = TLS_CIPHER_AES256_SHA;
 		ciphers[count++] = TLS_CIPHER_AES128_SHA;
 		ciphers[count++] = TLS_CIPHER_RC4_SHA;
 	}
@@ -1567,11 +1568,54 @@
 			res = 1;
 		}
 	} else {
+		if (sm->waiting_ext_cert_check && data->pending_resp) {
+			struct eap_peer_config *config = eap_get_config(sm);
+
+			if (config->pending_ext_cert_check ==
+			    EXT_CERT_CHECK_GOOD) {
+				wpa_printf(MSG_DEBUG,
+					   "EAP-FAST: External certificate check succeeded - continue handshake");
+				resp = data->pending_resp;
+				data->pending_resp = NULL;
+				sm->waiting_ext_cert_check = 0;
+				return resp;
+			}
+
+			if (config->pending_ext_cert_check ==
+			    EXT_CERT_CHECK_BAD) {
+				wpa_printf(MSG_DEBUG,
+					   "EAP-FAST: External certificate check failed - force authentication failure");
+				ret->methodState = METHOD_DONE;
+				ret->decision = DECISION_FAIL;
+				sm->waiting_ext_cert_check = 0;
+				return NULL;
+			}
+
+			wpa_printf(MSG_DEBUG,
+				   "EAP-FAST: Continuing to wait external server certificate validation");
+			return NULL;
+		}
+
 		/* Continue processing TLS handshake (phase 1). */
 		res = eap_peer_tls_process_helper(sm, &data->ssl,
 						  EAP_TYPE_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 (sm->waiting_ext_cert_check) {
+			wpa_printf(MSG_DEBUG,
+				   "EAP-FAST: Waiting external server certificate validation");
+			wpabuf_free(data->pending_resp);
+			data->pending_resp = resp;
+			return NULL;
+		}
 
 		if (tls_connection_established(sm->ssl_ctx, data->ssl.conn)) {
 			char cipher[80];
@@ -1637,6 +1681,8 @@
 	data->key_block_p = NULL;
 	wpabuf_free(data->pending_phase2_req);
 	data->pending_phase2_req = NULL;
+	wpabuf_free(data->pending_resp);
+	data->pending_resp = NULL;
 }
 
 
@@ -1714,7 +1760,7 @@
 	struct eap_fast_data *data = priv;
 	u8 *id;
 
-	if (!data->success)
+	if (!data->success || !data->session_id)
 		return NULL;
 
 	id = os_malloc(data->id_len);
@@ -1750,7 +1796,6 @@
 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");
@@ -1771,8 +1816,5 @@
 #endif
 	eap->get_emsk = eap_fast_get_emsk;
 
-	ret = eap_peer_method_register(eap);
-	if (ret)
-		eap_peer_method_free(eap);
-	return ret;
+	return eap_peer_method_register(eap);
 }
diff --git a/src/eap_peer/eap_fast_pac.c b/src/eap_peer/eap_fast_pac.c
index 89e604e..c815860 100644
--- a/src/eap_peer/eap_fast_pac.c
+++ b/src/eap_peer/eap_fast_pac.c
@@ -455,7 +455,8 @@
 	}
 
 	if (pac) {
-		err = "PAC block not terminated with END";
+		if (!err)
+			err = "PAC block not terminated with END";
 		eap_fast_free_pac(pac);
 	}
 
@@ -709,7 +710,7 @@
 	pos = pac->pac_info;
 	end = pos + pac->pac_info_len;
 
-	while (pos + 4 < end) {
+	while (end - pos > 4) {
 		type = WPA_GET_BE16(pos);
 		pos += 2;
 		len = WPA_GET_BE16(pos);
@@ -801,8 +802,10 @@
 	while (pos < end) {
 		u16 val;
 
-		if (end - pos < 2 + EAP_FAST_PAC_KEY_LEN + 2 + 2)
+		if (end - pos < 2 + EAP_FAST_PAC_KEY_LEN + 2 + 2) {
+			pac = NULL;
 			goto parse_fail;
+		}
 
 		pac = os_zalloc(sizeof(*pac));
 		if (pac == NULL)
diff --git a/src/eap_peer/eap_gpsk.c b/src/eap_peer/eap_gpsk.c
index 902b4ba..177cbcc 100644
--- a/src/eap_peer/eap_gpsk.c
+++ b/src/eap_peer/eap_gpsk.c
@@ -771,7 +771,6 @@
 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");
@@ -786,8 +785,5 @@
 	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;
+	return eap_peer_method_register(eap);
 }
diff --git a/src/eap_peer/eap_gtc.c b/src/eap_peer/eap_gtc.c
index 9f3cfbd..a519a78 100644
--- a/src/eap_peer/eap_gtc.c
+++ b/src/eap_peer/eap_gtc.c
@@ -127,7 +127,6 @@
 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");
@@ -138,8 +137,5 @@
 	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;
+	return eap_peer_method_register(eap);
 }
diff --git a/src/eap_peer/eap_i.h b/src/eap_peer/eap_i.h
index 5f8b5fa..6ab2483 100644
--- a/src/eap_peer/eap_i.h
+++ b/src/eap_peer/eap_i.h
@@ -338,9 +338,9 @@
 	Boolean rxResp /* LEAP only */;
 	Boolean leap_done;
 	Boolean peap_done;
-	u8 req_md5[16]; /* MD5() of the current EAP packet */
-	u8 last_md5[16]; /* MD5() of the previously received EAP packet; used
-			  * in duplicate request detection. */
+	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;
@@ -366,6 +366,8 @@
 	int external_sim;
 
 	unsigned int expected_failure:1;
+	unsigned int ext_cert_check:1;
+	unsigned int waiting_ext_cert_check:1;
 
 	struct dl_list erp_keys; /* struct eap_erp_key */
 };
diff --git a/src/eap_peer/eap_ikev2.c b/src/eap_peer/eap_ikev2.c
index b5ef71b..390f0ec 100644
--- a/src/eap_peer/eap_ikev2.c
+++ b/src/eap_peer/eap_ikev2.c
@@ -513,7 +513,6 @@
 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,
@@ -529,8 +528,5 @@
 	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;
+	return eap_peer_method_register(eap);
 }
diff --git a/src/eap_peer/eap_leap.c b/src/eap_peer/eap_leap.c
index e0f8bcf..ff6fa4a 100644
--- a/src/eap_peer/eap_leap.c
+++ b/src/eap_peer/eap_leap.c
@@ -393,7 +393,6 @@
 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");
@@ -406,8 +405,5 @@
 	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;
+	return eap_peer_method_register(eap);
 }
diff --git a/src/eap_peer/eap_md5.c b/src/eap_peer/eap_md5.c
index d06befa..efae8de 100644
--- a/src/eap_peer/eap_md5.c
+++ b/src/eap_peer/eap_md5.c
@@ -102,7 +102,6 @@
 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");
@@ -113,8 +112,5 @@
 	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;
+	return eap_peer_method_register(eap);
 }
diff --git a/src/eap_peer/eap_methods.c b/src/eap_peer/eap_methods.c
index 1bdd81e..9747954 100644
--- a/src/eap_peer/eap_methods.c
+++ b/src/eap_peer/eap_methods.c
@@ -18,6 +18,8 @@
 
 static struct eap_method *eap_methods = NULL;
 
+static void eap_peer_method_free(struct eap_method *method);
+
 
 /**
  * eap_peer_get_eap_method - Get EAP method based on type number
@@ -295,7 +297,7 @@
  * 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)
+static void eap_peer_method_free(struct eap_method *method)
 {
 	os_free(method);
 }
@@ -303,26 +305,31 @@
 
 /**
  * eap_peer_method_register - Register an EAP peer method
- * @method: EAP method to register
+ * @method: EAP method to register from eap_peer_method_alloc()
  * 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.
+ * supported EAP method. The caller must not free the allocated method data
+ * regardless of the return value.
  */
 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)
+	    method->version != EAP_PEER_METHOD_INTERFACE_VERSION) {
+		eap_peer_method_free(method);
 		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)
+		    os_strcmp(m->name, method->name) == 0) {
+			eap_peer_method_free(method);
 			return -2;
+		}
 		last = m;
 	}
 
diff --git a/src/eap_peer/eap_methods.h b/src/eap_peer/eap_methods.h
index e35c919..b96b211 100644
--- a/src/eap_peer/eap_methods.h
+++ b/src/eap_peer/eap_methods.h
@@ -16,7 +16,6 @@
 
 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);
 
 
diff --git a/src/eap_peer/eap_mschapv2.c b/src/eap_peer/eap_mschapv2.c
index 9e486e7..ce2227d 100644
--- a/src/eap_peer/eap_mschapv2.c
+++ b/src/eap_peer/eap_mschapv2.c
@@ -511,6 +511,11 @@
 	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;
@@ -628,6 +633,7 @@
 fail:
 	wpabuf_free(resp);
 	return NULL;
+#endif /* CONFIG_NO_RC4 */
 }
 
 
@@ -874,7 +880,6 @@
 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,
@@ -888,8 +893,5 @@
 	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;
+	return eap_peer_method_register(eap);
 }
diff --git a/src/eap_peer/eap_otp.c b/src/eap_peer/eap_otp.c
index 9ac744a..0ab4c79 100644
--- a/src/eap_peer/eap_otp.c
+++ b/src/eap_peer/eap_otp.c
@@ -83,7 +83,6 @@
 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");
@@ -94,8 +93,5 @@
 	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;
+	return eap_peer_method_register(eap);
 }
diff --git a/src/eap_peer/eap_pax.c b/src/eap_peer/eap_pax.c
index c920bcd..5f0b7fb 100644
--- a/src/eap_peer/eap_pax.c
+++ b/src/eap_peer/eap_pax.c
@@ -276,10 +276,10 @@
 	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) {
+	if (eap_pax_mac(data->mac_id, data->ck, EAP_PAX_CK_LEN,
+			data->rand.r.y, EAP_PAX_RAND_LEN,
+			(u8 *) data->cid, data->cid_len, NULL, 0, mac) < 0 ||
+	    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)",
@@ -306,9 +306,12 @@
 	/* 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);
+	if (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) < 0) {
+		wpabuf_free(resp);
+		return NULL;
+	}
 	wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ICV", rpos, EAP_PAX_ICV_LEN);
 
 	data->state = PAX_DONE;
@@ -472,9 +475,13 @@
 		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);
+	if (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) < 0) {
+		os_free(key);
+		return NULL;
+	}
 
 	return key;
 }
@@ -493,10 +500,13 @@
 		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);
+	if (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) < 0) {
+		os_free(key);
+		return NULL;
+	}
 
 	return key;
 }
@@ -525,7 +535,6 @@
 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");
@@ -540,8 +549,5 @@
 	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;
+	return eap_peer_method_register(eap);
 }
diff --git a/src/eap_peer/eap_peap.c b/src/eap_peer/eap_peap.c
index 4f68fce..45ba381 100644
--- a/src/eap_peer/eap_peap.c
+++ b/src/eap_peer/eap_peap.c
@@ -1,6 +1,6 @@
 /*
  * EAP peer method: EAP-PEAP (draft-josefsson-pppext-eap-tls-eap-10.txt)
- * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi>
+ * 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.
@@ -59,6 +59,7 @@
 	size_t id_len;
 
 	struct wpabuf *pending_phase2_req;
+	struct wpabuf *pending_resp;
 	enum { NO_BINDING, OPTIONAL_BINDING, REQUIRE_BINDING } crypto_binding;
 	int crypto_binding_used;
 	u8 binding_nonce[32];
@@ -69,8 +70,8 @@
 };
 
 
-static int eap_peap_parse_phase1(struct eap_peap_data *data,
-				 const char *phase1)
+static void eap_peap_parse_phase1(struct eap_peap_data *data,
+				  const char *phase1)
 {
 	const char *pos;
 
@@ -125,8 +126,6 @@
 		wpa_printf(MSG_DEBUG, "EAP-PEAP: SoH version 2 enabled");
 	}
 #endif /* EAP_TNC */
-
-	return 0;
 }
 
 
@@ -144,11 +143,8 @@
 	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 (config && config->phase1)
+		eap_peap_parse_phase1(data, config->phase1);
 
 	if (eap_peer_select_phase2_methods(config, "auth=",
 					   &data->phase2_types,
@@ -191,6 +187,7 @@
 	eap_peap_free_key(data);
 	os_free(data->session_id);
 	wpabuf_free(data->pending_phase2_req);
+	wpabuf_free(data->pending_resp);
 	os_free(data);
 }
 
@@ -256,6 +253,7 @@
 {
 	u8 *tk;
 	u8 isk[32], imck[60];
+	int resumed;
 
 	/*
 	 * Tunnel key (TK) is the first 60 octets of the key generated by
@@ -266,8 +264,12 @@
 		return -1;
 	wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: TK", tk, 60);
 
-	if (data->reauth &&
-	    tls_connection_resumed(sm->ssl_ctx, data->ssl.conn)) {
+	resumed = tls_connection_resumed(sm->ssl_ctx, data->ssl.conn);
+	wpa_printf(MSG_DEBUG,
+		   "EAP-PEAP: CMK derivation - reauth=%d resumed=%d phase2_eap_started=%d phase2_success=%d",
+		   data->reauth, resumed, data->phase2_eap_started,
+		   data->phase2_success);
+	if (data->reauth && !data->phase2_eap_started && resumed) {
 		/* Fast-connect: IPMK|CMK = TK */
 		os_memcpy(data->ipmk, tk, 40);
 		wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: IPMK from TK",
@@ -337,7 +339,8 @@
 		    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);
+	if (hmac_sha1_vector(data->cmk, 20, 2, addr, len, mac) < 0)
+		return -1;
 	wpa_hexdump(MSG_MSGDUMP, "EAP-PEAP: Compound_MAC", mac, SHA1_MAC_LEN);
 	data->crypto_binding_used = 1;
 
@@ -648,6 +651,7 @@
 					if (*resp == NULL) {
 						ret->methodState = METHOD_DONE;
 						ret->decision = DECISION_FAIL;
+						wpabuf_free(buf);
 						return -1;
 					}
 					wpabuf_put_buf(*resp, buf);
@@ -1006,11 +1010,56 @@
 	    !data->resuming) {
 		res = eap_peap_decrypt(sm, data, ret, req, &msg, &resp);
 	} else {
+		if (sm->waiting_ext_cert_check && data->pending_resp) {
+			struct eap_peer_config *config = eap_get_config(sm);
+
+			if (config->pending_ext_cert_check ==
+			    EXT_CERT_CHECK_GOOD) {
+				wpa_printf(MSG_DEBUG,
+					   "EAP-PEAP: External certificate check succeeded - continue handshake");
+				resp = data->pending_resp;
+				data->pending_resp = NULL;
+				sm->waiting_ext_cert_check = 0;
+				return resp;
+			}
+
+			if (config->pending_ext_cert_check ==
+			    EXT_CERT_CHECK_BAD) {
+				wpa_printf(MSG_DEBUG,
+					   "EAP-PEAP: External certificate check failed - force authentication failure");
+				ret->methodState = METHOD_DONE;
+				ret->decision = DECISION_FAIL;
+				sm->waiting_ext_cert_check = 0;
+				return NULL;
+			}
+
+			wpa_printf(MSG_DEBUG,
+				   "EAP-PEAP: Continuing to wait external server certificate validation");
+			return NULL;
+		}
+
 		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 (sm->waiting_ext_cert_check) {
+			wpa_printf(MSG_DEBUG,
+				   "EAP-PEAP: Waiting external server certificate validation");
+			wpabuf_free(data->pending_resp);
+			data->pending_resp = resp;
+			return NULL;
+		}
+
 		if (tls_connection_established(sm->ssl_ctx, data->ssl.conn)) {
 			char *label;
 			wpa_printf(MSG_DEBUG,
@@ -1116,6 +1165,8 @@
 	struct eap_peap_data *data = priv;
 	wpabuf_free(data->pending_phase2_req);
 	data->pending_phase2_req = NULL;
+	wpabuf_free(data->pending_resp);
+	data->pending_resp = NULL;
 	data->crypto_binding_used = 0;
 }
 
@@ -1230,7 +1281,6 @@
 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");
@@ -1248,8 +1298,5 @@
 	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;
+	return eap_peer_method_register(eap);
 }
diff --git a/src/eap_peer/eap_psk.c b/src/eap_peer/eap_psk.c
index f012663..ac18c15 100644
--- a/src/eap_peer/eap_psk.c
+++ b/src/eap_peer/eap_psk.c
@@ -480,7 +480,6 @@
 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");
@@ -495,8 +494,5 @@
 	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;
+	return eap_peer_method_register(eap);
 }
diff --git a/src/eap_peer/eap_pwd.c b/src/eap_peer/eap_pwd.c
index cd7a2ab..d2bc981 100644
--- a/src/eap_peer/eap_pwd.c
+++ b/src/eap_peer/eap_pwd.c
@@ -288,6 +288,12 @@
 	}
 
 	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 {
@@ -307,6 +313,7 @@
 
 		password = pwhashhash;
 		password_len = sizeof(pwhashhash);
+#endif /* CONFIG_FIPS */
 	} else {
 		password = data->password;
 		password_len = data->password_len;
@@ -411,7 +418,6 @@
 		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)) {
@@ -548,6 +554,7 @@
 	os_free(element);
 	BN_clear_free(x);
 	BN_clear_free(y);
+	BN_clear_free(mask);
 	BN_clear_free(cofactor);
 	EC_POINT_clear_free(K);
 	EC_POINT_clear_free(point);
@@ -767,7 +774,8 @@
 	wpabuf_put_data(data->outbuf, conf, SHA256_MAC_LEN);
 
 fin:
-	bin_clear_free(cruft, BN_num_bytes(data->grp->prime));
+	if (data->grp)
+		bin_clear_free(cruft, BN_num_bytes(data->grp->prime));
 	BN_clear_free(x);
 	BN_clear_free(y);
 	if (data->outbuf == NULL) {
@@ -1046,7 +1054,6 @@
 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");
@@ -1061,8 +1068,5 @@
 	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;
+	return eap_peer_method_register(eap);
 }
diff --git a/src/eap_peer/eap_sake.c b/src/eap_peer/eap_sake.c
index c4f9843..80f4667 100644
--- a/src/eap_peer/eap_sake.c
+++ b/src/eap_peer/eap_sake.c
@@ -494,7 +494,6 @@
 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");
@@ -509,8 +508,5 @@
 	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;
+	return eap_peer_method_register(eap);
 }
diff --git a/src/eap_peer/eap_sim.c b/src/eap_peer/eap_sim.c
index 99a2816..b97c95d 100644
--- a/src/eap_peer/eap_sim.c
+++ b/src/eap_peer/eap_sim.c
@@ -249,6 +249,7 @@
 			return eap_sim_ext_sim_req(sm, data);
 	}
 
+#ifdef PCSC_FUNCS
 	if (conf->pcsc) {
 		if (scard_gsm_auth(sm->scard_ctx, data->rand[0],
 				   data->sres[0], data->kc[0]) ||
@@ -263,6 +264,7 @@
 		}
 		return 0;
 	}
+#endif /* PCSC_FUNCS */
 
 #ifdef CONFIG_SIM_SIMULATOR
 	if (conf->password) {
@@ -1135,7 +1137,7 @@
 	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);
+		eap_sim_deinit(sm, data);
 		return NULL;
 	}
 	data->num_id_req = 0;
@@ -1235,7 +1237,6 @@
 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");
@@ -1254,8 +1255,5 @@
 	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;
+	return eap_peer_method_register(eap);
 }
diff --git a/src/eap_peer/eap_tls.c b/src/eap_peer/eap_tls.c
index d81b1cf..ca2354f 100644
--- a/src/eap_peer/eap_tls.c
+++ b/src/eap_peer/eap_tls.c
@@ -1,6 +1,6 @@
 /*
  * EAP peer method: EAP-TLS (RFC 2716)
- * Copyright (c) 2004-2008, 2012, Jouni Malinen <j@w1.fi>
+ * 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.
@@ -25,6 +25,7 @@
 	size_t id_len;
 	void *ssl_ctx;
 	u8 eap_type;
+	struct wpabuf *pending_resp;
 };
 
 
@@ -142,6 +143,7 @@
 	eap_peer_tls_ssl_deinit(sm, &data->ssl);
 	eap_tls_free_key(data);
 	os_free(data->session_id);
+	wpabuf_free(data->pending_resp);
 	os_free(data);
 }
 
@@ -156,20 +158,6 @@
 	ret->methodState = METHOD_DONE;
 	ret->decision = DECISION_FAIL;
 
-	if (res == -1) {
-		struct eap_peer_config *config = eap_get_config(sm);
-		if (config) {
-			/*
-			 * The TLS handshake failed. So better forget the old
-			 * PIN. It may be wrong, we cannot be sure but trying
-			 * the wrong one again might block it on the card--so
-			 * better ask the user again.
-			 */
-			os_free(config->pin);
-			config->pin = NULL;
-		}
-	}
-
 	if (resp) {
 		/*
 		 * This is likely an alert message, so send it instead of just
@@ -230,6 +218,32 @@
 	struct eap_tls_data *data = priv;
 	struct wpabuf msg;
 
+	if (sm->waiting_ext_cert_check && data->pending_resp) {
+		struct eap_peer_config *config = eap_get_config(sm);
+
+		if (config->pending_ext_cert_check == EXT_CERT_CHECK_GOOD) {
+			wpa_printf(MSG_DEBUG,
+				   "EAP-TLS: External certificate check succeeded - continue handshake");
+			resp = data->pending_resp;
+			data->pending_resp = NULL;
+			sm->waiting_ext_cert_check = 0;
+			return resp;
+		}
+
+		if (config->pending_ext_cert_check == EXT_CERT_CHECK_BAD) {
+			wpa_printf(MSG_DEBUG,
+				   "EAP-TLS: External certificate check failed - force authentication failure");
+			ret->methodState = METHOD_DONE;
+			ret->decision = DECISION_FAIL;
+			sm->waiting_ext_cert_check = 0;
+			return NULL;
+		}
+
+		wpa_printf(MSG_DEBUG,
+			   "EAP-TLS: Continuing to wait external server certificate validation");
+		return NULL;
+	}
+
 	pos = eap_peer_tls_process_init(sm, &data->ssl, data->eap_type, ret,
 					reqData, &left, &flags);
 	if (pos == NULL)
@@ -251,6 +265,14 @@
 		return eap_tls_failure(sm, data, ret, res, resp, id);
 	}
 
+	if (sm->waiting_ext_cert_check) {
+		wpa_printf(MSG_DEBUG,
+			   "EAP-TLS: Waiting external server certificate validation");
+		wpabuf_free(data->pending_resp);
+		data->pending_resp = resp;
+		return NULL;
+	}
+
 	if (tls_connection_established(data->ssl_ctx, data->ssl.conn))
 		eap_tls_success(sm, data, ret);
 
@@ -272,6 +294,10 @@
 
 static void eap_tls_deinit_for_reauth(struct eap_sm *sm, void *priv)
 {
+	struct eap_tls_data *data = priv;
+
+	wpabuf_free(data->pending_resp);
+	data->pending_resp = NULL;
 }
 
 
@@ -364,7 +390,6 @@
 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");
@@ -383,10 +408,7 @@
 	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;
+	return eap_peer_method_register(eap);
 }
 
 
@@ -394,7 +416,6 @@
 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,
@@ -413,10 +434,7 @@
 	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;
+	return eap_peer_method_register(eap);
 }
 #endif /* EAP_UNAUTH_TLS */
 
@@ -425,7 +443,6 @@
 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,
@@ -445,9 +462,6 @@
 	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;
+	return eap_peer_method_register(eap);
 }
 #endif /* CONFIG_HS20 */
diff --git a/src/eap_peer/eap_tls_common.c b/src/eap_peer/eap_tls_common.c
index fef7fdb..406c162 100644
--- a/src/eap_peer/eap_tls_common.c
+++ b/src/eap_peer/eap_tls_common.c
@@ -68,6 +68,10 @@
 		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"))
@@ -76,6 +80,10 @@
 		params->flags |= TLS_CONN_DISABLE_TLSv1_2;
 	if (os_strstr(txt, "tls_disable_tlsv1_2=0"))
 		params->flags &= ~TLS_CONN_DISABLE_TLSv1_2;
+	if (os_strstr(txt, "tls_ext_cert_check=1"))
+		params->flags |= TLS_CONN_EXT_CERT_CHECK;
+	if (os_strstr(txt, "tls_ext_cert_check=0"))
+		params->flags &= ~TLS_CONN_EXT_CERT_CHECK;
 }
 
 
@@ -173,6 +181,8 @@
 
 	params->openssl_ciphers = config->openssl_ciphers;
 
+	sm->ext_cert_check = !!(params->flags & TLS_CONN_EXT_CERT_CHECK);
+
 	return 0;
 }
 
@@ -186,8 +196,10 @@
 
 	if (config->ocsp)
 		params->flags |= TLS_CONN_REQUEST_OCSP;
-	if (config->ocsp == 2)
+	if (config->ocsp >= 2)
 		params->flags |= TLS_CONN_REQUIRE_OCSP;
+	if (config->ocsp == 3)
+		params->flags |= TLS_CONN_REQUIRE_OCSP_ALL;
 	data->conn = tls_connection_init(data->ssl_ctx);
 	if (data->conn == NULL) {
 		wpa_printf(MSG_INFO, "SSL: Failed to initialize new TLS "
@@ -343,13 +355,11 @@
 				    struct eap_ssl_data *data, u8 eap_type,
 				    size_t *len)
 {
-	struct tls_keys keys;
+	struct tls_random keys;
 	u8 *out;
 
-	if (tls_connection_get_keys(sm->ssl_ctx, data->conn, &keys))
-		return NULL;
-
-	if (keys.client_random == NULL || keys.server_random == NULL)
+	if (tls_connection_get_random(sm->ssl_ctx, data->conn, &keys) ||
+	    keys.client_random == NULL || keys.server_random == NULL)
 		return NULL;
 
 	*len = 1 + keys.client_random_len + keys.server_random_len;
@@ -678,12 +688,18 @@
 	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");
+			   "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 (data->tls_out == NULL || wpabuf_len(data->tls_out) == 0) {
+	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
@@ -749,20 +765,24 @@
 int eap_peer_tls_status(struct eap_sm *sm, struct eap_ssl_data *data,
 			char *buf, size_t buflen, int verbose)
 {
-	char name[128];
+	char version[20], name[128];
 	int len = 0, ret;
 
-	if (tls_get_cipher(data->ssl_ctx, data->conn, name, sizeof(name)) == 0)
-	{
-		ret = os_snprintf(buf + len, buflen - len,
-				  "EAP TLS cipher=%s\n"
-				  "tls_session_reused=%d\n",
-				  name, tls_connection_resumed(data->ssl_ctx,
-							       data->conn));
-		if (os_snprintf_error(buflen - len, ret))
-			return len;
-		len += 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;
 }
@@ -1021,6 +1041,9 @@
 		if (vendor == EAP_VENDOR_IETF && method == EAP_TYPE_NONE) {
 			wpa_printf(MSG_ERROR, "TLS: Unsupported Phase2 EAP "
 				   "method '%s'", start);
+			os_free(methods);
+			os_free(buf);
+			return -1;
 		} else {
 			num_methods++;
 			_methods = os_realloc_array(methods, num_methods,
diff --git a/src/eap_peer/eap_tnc.c b/src/eap_peer/eap_tnc.c
index 25b9f12..726221e 100644
--- a/src/eap_peer/eap_tnc.c
+++ b/src/eap_peer/eap_tnc.c
@@ -10,6 +10,7 @@
 
 #include "common.h"
 #include "eap_i.h"
+#include "eap_config.h"
 #include "tncc.h"
 
 
@@ -35,12 +36,16 @@
 static void * eap_tnc_init(struct eap_sm *sm)
 {
 	struct eap_tnc_data *data;
+	struct eap_peer_config *config = eap_get_config(sm);
 
 	data = os_zalloc(sizeof(*data));
 	if (data == NULL)
 		return NULL;
 	data->state = WAIT_START;
-	data->fragment_size = 1300;
+	if (config && config->fragment_size)
+		data->fragment_size = config->fragment_size;
+	else
+		data->fragment_size = 1300;
 	data->tncc = tncc_init();
 	if (data->tncc == NULL) {
 		os_free(data);
@@ -345,11 +350,6 @@
 	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));
@@ -410,7 +410,6 @@
 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");
@@ -421,8 +420,5 @@
 	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;
+	return eap_peer_method_register(eap);
 }
diff --git a/src/eap_peer/eap_ttls.c b/src/eap_peer/eap_ttls.c
index 25e3cba..92f94dc 100644
--- a/src/eap_peer/eap_ttls.c
+++ b/src/eap_peer/eap_ttls.c
@@ -1,6 +1,6 @@
 /*
  * EAP peer method: EAP-TTLS (RFC 5281)
- * Copyright (c) 2004-2011, Jouni Malinen <j@w1.fi>
+ * 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.
@@ -35,6 +35,7 @@
 	void *phase2_priv;
 	int phase2_success;
 	int phase2_start;
+	EapDecision decision_succ;
 
 	enum phase2_types {
 		EAP_TTLS_PHASE2_EAP,
@@ -58,6 +59,7 @@
 	size_t id_len;
 
 	struct wpabuf *pending_phase2_req;
+	struct wpabuf *pending_resp;
 
 #ifdef EAP_TNC
 	int ready_for_tnc;
@@ -70,6 +72,7 @@
 {
 	struct eap_ttls_data *data;
 	struct eap_peer_config *config = eap_get_config(sm);
+	int selected_non_eap;
 	char *selected;
 
 	data = os_zalloc(sizeof(*data));
@@ -77,26 +80,67 @@
 		return NULL;
 	data->ttls_version = EAP_TTLS_VERSION;
 	selected = "EAP";
+	selected_non_eap = 0;
 	data->phase2_type = EAP_TTLS_PHASE2_EAP;
 
+	/*
+	 * Either one auth= type or one or more autheap= methods can be
+	 * specified.
+	 */
 	if (config && config->phase2) {
+		const char *token, *last = NULL;
+
+		while ((token = cstr_token(config->phase2, " \t", &last))) {
+			if (os_strncmp(token, "auth=", 5) != 0)
+				continue;
+			token += 5;
+
+			if (last - token == 8 &&
+			    os_strncmp(token, "MSCHAPV2", 8) == 0) {
+				selected = "MSCHAPV2";
+				data->phase2_type = EAP_TTLS_PHASE2_MSCHAPV2;
+			} else if (last - token == 6 &&
+				   os_strncmp(token, "MSCHAP", 6) == 0) {
+				selected = "MSCHAP";
+				data->phase2_type = EAP_TTLS_PHASE2_MSCHAP;
+			} else if (last - token == 3 &&
+				   os_strncmp(token, "PAP", 3) == 0) {
+				selected = "PAP";
+				data->phase2_type = EAP_TTLS_PHASE2_PAP;
+			} else if (last - token == 4 &&
+				   os_strncmp(token, "CHAP", 4) == 0) {
+				selected = "CHAP";
+				data->phase2_type = EAP_TTLS_PHASE2_CHAP;
+			} else {
+				wpa_printf(MSG_ERROR,
+					   "EAP-TTLS: Unsupported Phase2 type '%s'",
+					   token);
+				eap_ttls_deinit(sm, data);
+				return NULL;
+			}
+
+			if (selected_non_eap) {
+				wpa_printf(MSG_ERROR,
+					   "EAP-TTLS: Only one Phase2 type can be specified");
+				eap_ttls_deinit(sm, data);
+				return NULL;
+			}
+
+			selected_non_eap = 1;
+		}
+
 		if (os_strstr(config->phase2, "autheap=")) {
+			if (selected_non_eap) {
+				wpa_printf(MSG_ERROR,
+					   "EAP-TTLS: Both auth= and autheap= params cannot be specified");
+				eap_ttls_deinit(sm, data);
+				return NULL;
+			}
 			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) {
@@ -153,6 +197,7 @@
 	eap_ttls_free_key(data);
 	os_free(data->session_id);
 	wpabuf_free(data->pending_phase2_req);
+	wpabuf_free(data->pending_resp);
 	os_free(data);
 }
 
@@ -175,7 +220,8 @@
 	}
 
 	avp->avp_code = host_to_be32(avp_code);
-	avp->avp_length = host_to_be32((flags << 24) | (u32) (hdrlen + len));
+	avp->avp_length = host_to_be32(((u32) flags << 24) |
+				       (u32) (hdrlen + len));
 
 	return avphdr + hdrlen;
 }
@@ -253,11 +299,13 @@
 }
 
 
+#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,
@@ -428,6 +476,10 @@
 					    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;
@@ -510,6 +562,7 @@
 	wpa_printf(MSG_ERROR, "EAP-TTLS: MSCHAPv2 not included in the build");
 	return -1;
 #endif /* EAP_MSCHAPv2 */
+#endif /* CONFIG_FIPS */
 }
 
 
@@ -518,6 +571,10 @@
 					  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;
@@ -592,6 +649,7 @@
 	ret->decision = DECISION_COND_SUCC;
 
 	return 0;
+#endif /* CONFIG_FIPS */
 }
 
 
@@ -654,6 +712,10 @@
 					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;
@@ -722,6 +784,7 @@
 	ret->decision = DECISION_COND_SUCC;
 
 	return 0;
+#endif /* CONFIG_FIPS */
 }
 
 
@@ -1390,9 +1453,50 @@
 {
 	int res;
 
+	if (sm->waiting_ext_cert_check && data->pending_resp) {
+		struct eap_peer_config *config = eap_get_config(sm);
+
+		if (config->pending_ext_cert_check == EXT_CERT_CHECK_GOOD) {
+			wpa_printf(MSG_DEBUG,
+				   "EAP-TTLS: External certificate check succeeded - continue handshake");
+			*out_data = data->pending_resp;
+			data->pending_resp = NULL;
+			sm->waiting_ext_cert_check = 0;
+			return 0;
+		}
+
+		if (config->pending_ext_cert_check == EXT_CERT_CHECK_BAD) {
+			wpa_printf(MSG_DEBUG,
+				   "EAP-TTLS: External certificate check failed - force authentication failure");
+			ret->methodState = METHOD_DONE;
+			ret->decision = DECISION_FAIL;
+			sm->waiting_ext_cert_check = 0;
+			return 0;
+		}
+
+		wpa_printf(MSG_DEBUG,
+			   "EAP-TTLS: Continuing to wait external server certificate validation");
+		return 0;
+	}
+
 	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 (sm->waiting_ext_cert_check) {
+		wpa_printf(MSG_DEBUG,
+			   "EAP-TTLS: Waiting external server certificate validation");
+		wpabuf_free(data->pending_resp);
+		data->pending_resp = *out_data;
+		*out_data = NULL;
+		return 0;
+	}
 
 	if (tls_connection_established(sm->ssl_ctx, data->ssl.conn)) {
 		wpa_printf(MSG_DEBUG, "EAP-TTLS: TLS done, proceed to "
@@ -1444,6 +1548,7 @@
 			wpa_printf(MSG_DEBUG, "EAP-TTLS: Authentication "
 				   "completed successfully");
 			data->phase2_success = 1;
+			data->decision_succ = ret->decision;
 #ifdef EAP_TNC
 			if (!data->ready_for_tnc && !data->tnc_started) {
 				/*
@@ -1461,6 +1566,18 @@
 			wpa_printf(MSG_DEBUG, "EAP-TTLS: Authentication "
 				   "completed successfully (MAY_CONT)");
 			data->phase2_success = 1;
+			data->decision_succ = ret->decision;
+	} else if (data->decision_succ != DECISION_FAIL &&
+		   data->phase2_success &&
+		   !data->ssl.tls_out) {
+		/*
+		 * This is needed to cover the case where the final Phase 2
+		 * message gets fragmented since fragmentation clears
+		 * decision back to FAIL.
+		 */
+		wpa_printf(MSG_DEBUG,
+			   "EAP-TTLS: Restore success decision after fragmented frame sent completely");
+		ret->decision = data->decision_succ;
 	}
 }
 
@@ -1533,6 +1650,9 @@
 	struct eap_ttls_data *data = priv;
 	wpabuf_free(data->pending_phase2_req);
 	data->pending_phase2_req = NULL;
+	wpabuf_free(data->pending_resp);
+	data->pending_resp = NULL;
+	data->decision_succ = DECISION_FAIL;
 #ifdef EAP_TNC
 	data->ready_for_tnc = 0;
 	data->tnc_started = 0;
@@ -1671,7 +1791,6 @@
 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");
@@ -1690,8 +1809,5 @@
 	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;
+	return eap_peer_method_register(eap);
 }
diff --git a/src/eap_peer/eap_vendor_test.c b/src/eap_peer/eap_vendor_test.c
index b61057e..16e3c39 100644
--- a/src/eap_peer/eap_vendor_test.c
+++ b/src/eap_peer/eap_vendor_test.c
@@ -169,7 +169,6 @@
 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,
@@ -183,8 +182,5 @@
 	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;
+	return eap_peer_method_register(eap);
 }
diff --git a/src/eap_peer/eap_wsc.c b/src/eap_peer/eap_wsc.c
index 7ce0a53..d140c88 100644
--- a/src/eap_peer/eap_wsc.c
+++ b/src/eap_peer/eap_wsc.c
@@ -17,7 +17,7 @@
 
 
 struct eap_wsc_data {
-	enum { WAIT_START, MESG, FRAG_ACK, WAIT_FRAG_ACK, DONE, FAIL } state;
+	enum { WAIT_START, MESG, WAIT_FRAG_ACK, FAIL } state;
 	int registrar;
 	struct wpabuf *in_buf;
 	struct wpabuf *out_buf;
@@ -36,12 +36,8 @@
 		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:
@@ -557,6 +553,9 @@
 		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;
@@ -576,7 +575,6 @@
 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,
@@ -588,8 +586,5 @@
 	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;
+	return eap_peer_method_register(eap);
 }
diff --git a/src/eap_peer/ikev2.c b/src/eap_peer/ikev2.c
index 55ab72a..ca6502e 100644
--- a/src/eap_peer/ikev2.c
+++ b/src/eap_peer/ikev2.c
@@ -128,7 +128,7 @@
 
 	t = (const struct ikev2_transform *) pos;
 	transform_len = WPA_GET_BE16(t->transform_length);
-	if (transform_len < (int) sizeof(*t) || pos + transform_len > end) {
+	if (transform_len < (int) sizeof(*t) || transform_len > end - pos) {
 		wpa_printf(MSG_INFO, "IKEV2: Invalid transform length %d",
 			   transform_len);
 		return -1;
@@ -248,7 +248,7 @@
 
 	ppos = (const u8 *) (p + 1);
 	pend = pos + proposal_len;
-	if (ppos + p->spi_size > pend) {
+	if (p->spi_size > pend - ppos) {
 		wpa_printf(MSG_INFO, "IKEV2: Not enough room for SPI "
 			   "in proposal");
 		return -1;
diff --git a/src/eap_peer/tncc.c b/src/eap_peer/tncc.c
index 7ca956e..9965513 100644
--- a/src/eap_peer/tncc.c
+++ b/src/eap_peer/tncc.c
@@ -694,6 +694,8 @@
 	enum tncc_process_res res = TNCCS_PROCESS_OK_NO_RECOMMENDATION;
 	int recommendation_msg = 0;
 
+	wpa_hexdump_ascii(MSG_MSGDUMP, "TNC: Received IF-TNCCS message",
+			  msg, len);
 	buf = dup_binstr(msg, len);
 	if (buf == NULL)
 		return TNCCS_PROCESS_ERROR;
diff --git a/src/eap_server/eap.h b/src/eap_server/eap.h
index 09be581..69eaab8 100644
--- a/src/eap_server/eap.h
+++ b/src/eap_server/eap.h
@@ -131,6 +131,7 @@
 	const u8 *server_id;
 	size_t server_id_len;
 	int erp;
+	unsigned int tls_session_lifetime;
 
 #ifdef CONFIG_TESTING_OPTIONS
 	u32 tls_test_flags;
diff --git a/src/eap_server/eap_i.h b/src/eap_server/eap_i.h
index 978c879..c90443d 100644
--- a/src/eap_server/eap_i.h
+++ b/src/eap_server/eap_i.h
@@ -210,6 +210,7 @@
 	Boolean initiate_reauth_start_sent;
 	Boolean try_initiate_reauth;
 	int erp;
+	unsigned int tls_session_lifetime;
 
 #ifdef CONFIG_TESTING_OPTIONS
 	u32 tls_test_flags;
diff --git a/src/eap_server/eap_methods.h b/src/eap_server/eap_methods.h
index 0baa327..3bf1495 100644
--- a/src/eap_server/eap_methods.h
+++ b/src/eap_server/eap_methods.h
@@ -15,7 +15,6 @@
 						    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);
diff --git a/src/eap_server/eap_server.c b/src/eap_server/eap_server.c
index 6651229..84ecafc 100644
--- a/src/eap_server/eap_server.c
+++ b/src/eap_server/eap_server.c
@@ -96,7 +96,8 @@
 		plen += 2 + domain_len;
 	}
 
-	msg = eap_msg_alloc(EAP_VENDOR_IETF, EAP_ERP_TYPE_REAUTH_START, plen,
+	msg = eap_msg_alloc(EAP_VENDOR_IETF,
+			    (EapType) EAP_ERP_TYPE_REAUTH_START, plen,
 			    EAP_CODE_INITIATE, id);
 	if (msg == NULL)
 		return NULL;
@@ -714,8 +715,8 @@
 	plen = 1 + 2 + 2 + os_strlen(nai);
 	if (hash_len)
 		plen += 1 + hash_len;
-	msg = eap_msg_alloc(EAP_VENDOR_IETF, EAP_ERP_TYPE_REAUTH, plen,
-			    EAP_CODE_FINISH, id);
+	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);
@@ -799,7 +800,7 @@
 
 	sm->rxInitiate = FALSE;
 
-	pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_ERP_TYPE_REAUTH,
+	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");
@@ -1246,6 +1247,17 @@
 			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:
 		/*
@@ -1853,6 +1865,7 @@
 	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;
diff --git a/src/eap_server/eap_server_aka.c b/src/eap_server/eap_server_aka.c
index db9b6aa..a8bb5ea 100644
--- a/src/eap_server/eap_server_aka.c
+++ b/src/eap_server/eap_server_aka.c
@@ -1319,7 +1319,6 @@
 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");
@@ -1337,10 +1336,7 @@
 	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;
+	return eap_server_method_register(eap);
 }
 
 
@@ -1348,7 +1344,6 @@
 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,
@@ -1367,10 +1362,6 @@
 	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;
+	return eap_server_method_register(eap);
 }
 #endif /* EAP_SERVER_AKA_PRIME */
diff --git a/src/eap_server/eap_server_eke.c b/src/eap_server/eap_server_eke.c
index ba82be9..1eba8f5 100644
--- a/src/eap_server/eap_server_eke.c
+++ b/src/eap_server/eap_server_eke.c
@@ -792,7 +792,6 @@
 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");
@@ -810,8 +809,5 @@
 	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;
+	return eap_server_method_register(eap);
 }
diff --git a/src/eap_server/eap_server_fast.c b/src/eap_server/eap_server_fast.c
index 6745100..6993159 100644
--- a/src/eap_server/eap_server_fast.c
+++ b/src/eap_server/eap_server_fast.c
@@ -180,42 +180,47 @@
 			buf, end - buf);
 
 	pos = buf;
-	while (pos + 1 < end) {
-		if (pos + 2 + pos[1] > end)
+	while (end - pos > 1) {
+		u8 id, elen;
+
+		id = *pos++;
+		elen = *pos++;
+		if (elen > end - pos)
 			break;
 
-		switch (*pos) {
+		switch (id) {
 		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]);
+			if (elen != EAP_FAST_PAC_KEY_LEN) {
+				wpa_printf(MSG_DEBUG,
+					   "EAP-FAST: Invalid PAC-Key length %d",
+					   elen);
 				os_free(buf);
 				return -1;
 			}
-			pac_key = pos + 2;
+			pac_key = pos;
 			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) {
+			if (elen != 4) {
 				wpa_printf(MSG_DEBUG, "EAP-FAST: Invalid "
 					   "PAC-Key lifetime length %d",
-					   pos[1]);
+					   elen);
 				os_free(buf);
 				return -1;
 			}
-			lifetime = WPA_GET_BE32(pos + 2);
+			lifetime = WPA_GET_BE32(pos);
 			break;
 		case PAC_OPAQUE_TYPE_IDENTITY:
-			identity = pos + 2;
-			identity_len = pos[1];
+			identity = pos;
+			identity_len = elen;
 			break;
 		}
 
-		pos += 2 + pos[1];
+		pos += elen;
 	}
 done:
 
@@ -407,11 +412,13 @@
 static void * eap_fast_init(struct eap_sm *sm)
 {
 	struct eap_fast_data *data;
-	u8 ciphers[5] = {
+	u8 ciphers[7] = {
 		TLS_CIPHER_ANON_DH_AES128_SHA,
 		TLS_CIPHER_AES128_SHA,
 		TLS_CIPHER_RSA_DHE_AES128_SHA,
 		TLS_CIPHER_RC4_SHA,
+		TLS_CIPHER_RSA_DHE_AES256_SHA,
+		TLS_CIPHER_AES256_SHA,
 		TLS_CIPHER_NONE
 	};
 
@@ -428,7 +435,7 @@
 	}
 	data->state = START;
 
-	if (eap_server_tls_ssl_init(sm, &data->ssl, 0)) {
+	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;
@@ -1134,7 +1141,7 @@
 
 	pos = wpabuf_mhead(data);
 	end = pos + wpabuf_len(data);
-	while (pos + 4 < end) {
+	while (end - pos > 4) {
 		mandatory = pos[0] & 0x80;
 		tlv_type = WPA_GET_BE16(pos) & 0x3fff;
 		pos += 2;
@@ -1559,7 +1566,10 @@
 	if (eapKeyData == NULL)
 		return NULL;
 
-	eap_fast_derive_eap_msk(data->simck, eapKeyData);
+	if (eap_fast_derive_eap_msk(data->simck, eapKeyData) < 0) {
+		os_free(eapKeyData);
+		return NULL;
+	}
 	*len = EAP_FAST_KEY_LEN;
 
 	return eapKeyData;
@@ -1578,7 +1588,10 @@
 	if (eapKeyData == NULL)
 		return NULL;
 
-	eap_fast_derive_eap_emsk(data->simck, eapKeyData);
+	if (eap_fast_derive_eap_emsk(data->simck, eapKeyData) < 0) {
+		os_free(eapKeyData);
+		return NULL;
+	}
 	*len = EAP_EMSK_LEN;
 
 	return eapKeyData;
@@ -1607,7 +1620,6 @@
 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");
@@ -1625,8 +1637,5 @@
 	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;
+	return eap_server_method_register(eap);
 }
diff --git a/src/eap_server/eap_server_gpsk.c b/src/eap_server/eap_server_gpsk.c
index 50f15c3..94e74ec 100644
--- a/src/eap_server/eap_server_gpsk.c
+++ b/src/eap_server/eap_server_gpsk.c
@@ -631,7 +631,6 @@
 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");
@@ -649,8 +648,5 @@
 	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;
+	return eap_server_method_register(eap);
 }
diff --git a/src/eap_server/eap_server_gtc.c b/src/eap_server/eap_server_gtc.c
index 98ac3c6..193a851 100644
--- a/src/eap_server/eap_server_gtc.c
+++ b/src/eap_server/eap_server_gtc.c
@@ -202,7 +202,6 @@
 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");
@@ -217,8 +216,5 @@
 	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;
+	return eap_server_method_register(eap);
 }
diff --git a/src/eap_server/eap_server_identity.c b/src/eap_server/eap_server_identity.c
index 4501533..1b1db53 100644
--- a/src/eap_server/eap_server_identity.c
+++ b/src/eap_server/eap_server_identity.c
@@ -157,7 +157,6 @@
 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,
@@ -174,8 +173,5 @@
 	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;
+	return eap_server_method_register(eap);
 }
diff --git a/src/eap_server/eap_server_ikev2.c b/src/eap_server/eap_server_ikev2.c
index 16e6276..3a249d1 100644
--- a/src/eap_server/eap_server_ikev2.c
+++ b/src/eap_server/eap_server_ikev2.c
@@ -550,7 +550,6 @@
 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,
@@ -569,8 +568,5 @@
 	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;
+	return eap_server_method_register(eap);
 }
diff --git a/src/eap_server/eap_server_md5.c b/src/eap_server/eap_server_md5.c
index 71e8d59..cf5ceb1 100644
--- a/src/eap_server/eap_server_md5.c
+++ b/src/eap_server/eap_server_md5.c
@@ -153,7 +153,6 @@
 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");
@@ -168,8 +167,5 @@
 	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;
+	return eap_server_method_register(eap);
 }
diff --git a/src/eap_server/eap_server_methods.c b/src/eap_server/eap_server_methods.c
index 9e9dc93..79ed344 100644
--- a/src/eap_server/eap_server_methods.c
+++ b/src/eap_server/eap_server_methods.c
@@ -87,7 +87,7 @@
  * 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)
+static void eap_server_method_free(struct eap_method *method)
 {
 	os_free(method);
 }
@@ -95,26 +95,31 @@
 
 /**
  * eap_server_method_register - Register an EAP server method
- * @method: EAP method to register
+ * @method: EAP method to register from eap_server_method_alloc()
  * 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.
+ * supported EAP method. The caller must not free the allocated method data
+ * regardless of the return value.
  */
 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)
+	    method->version != EAP_SERVER_METHOD_INTERFACE_VERSION) {
+		eap_server_method_free(method);
 		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)
+		    os_strcmp(m->name, method->name) == 0) {
+			eap_server_method_free(method);
 			return -2;
+		}
 		last = m;
 	}
 
diff --git a/src/eap_server/eap_server_mschapv2.c b/src/eap_server/eap_server_mschapv2.c
index 98d74e0..460cd9c 100644
--- a/src/eap_server/eap_server_mschapv2.c
+++ b/src/eap_server/eap_server_mschapv2.c
@@ -571,7 +571,6 @@
 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,
@@ -588,8 +587,5 @@
 	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;
+	return eap_server_method_register(eap);
 }
diff --git a/src/eap_server/eap_server_pax.c b/src/eap_server/eap_server_pax.c
index 0e6b4a0..782b8c3 100644
--- a/src/eap_server/eap_server_pax.c
+++ b/src/eap_server/eap_server_pax.c
@@ -565,7 +565,6 @@
 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");
@@ -583,8 +582,5 @@
 	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;
+	return eap_server_method_register(eap);
 }
diff --git a/src/eap_server/eap_server_peap.c b/src/eap_server/eap_server_peap.c
index 3848f30..18d31b5 100644
--- a/src/eap_server/eap_server_peap.c
+++ b/src/eap_server/eap_server_peap.c
@@ -95,6 +95,37 @@
 		   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);
 }
 
 
@@ -151,7 +182,7 @@
 	data->state = START;
 	data->crypto_binding = OPTIONAL_BINDING;
 
-	if (eap_server_tls_ssl_init(sm, &data->ssl, 0)) {
+	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;
@@ -304,6 +335,18 @@
 		return -1;
 	wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: TK", tk, 60);
 
+	if (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);
+		os_free(tk);
+		return 0;
+	}
+
 	eap_peap_get_isk(data, isk, sizeof(isk));
 	wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: ISK", isk, sizeof(isk));
 
@@ -326,7 +369,6 @@
 
 	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);
@@ -708,10 +750,12 @@
 		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)
+			if (data->tlv_request == TLV_REQ_SUCCESS) {
 				eap_peap_state(data, SUCCESS);
-			else
+				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 "
@@ -1094,6 +1138,7 @@
 		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:
@@ -1159,6 +1204,7 @@
 		break;
 	case SUCCESS_REQ:
 		eap_peap_state(data, SUCCESS);
+		eap_peap_valid_session(sm, data);
 		break;
 	case FAILURE_REQ:
 		eap_peap_state(data, FAILURE);
@@ -1175,10 +1221,66 @@
 			     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_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_req_success(sm, data);
+	if (data->state == SUCCESS_REQ)
+		tls_connection_set_success_data_resumed(data->ssl.conn);
 }
 
 
@@ -1261,7 +1363,6 @@
 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");
@@ -1278,8 +1379,5 @@
 	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;
+	return eap_server_method_register(eap);
 }
diff --git a/src/eap_server/eap_server_psk.c b/src/eap_server/eap_server_psk.c
index 12b5d25..857d421 100644
--- a/src/eap_server/eap_server_psk.c
+++ b/src/eap_server/eap_server_psk.c
@@ -510,7 +510,6 @@
 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");
@@ -528,8 +527,5 @@
 	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;
+	return eap_server_method_register(eap);
 }
diff --git a/src/eap_server/eap_server_pwd.c b/src/eap_server/eap_server_pwd.c
index 9f787ab..64bf708 100644
--- a/src/eap_server/eap_server_pwd.c
+++ b/src/eap_server/eap_server_pwd.c
@@ -178,8 +178,13 @@
 		return;
 	}
 
-	/* an lfsr is good enough to generate unpredictable tokens */
-	data->token = os_random();
+	if (os_get_random((u8 *) &data->token, sizeof(data->token)) < 0) {
+		wpabuf_free(data->outbuf);
+		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);
@@ -1094,7 +1099,6 @@
 int eap_server_pwd_register(void)
 {
 	struct eap_method *eap;
-	int ret;
 	struct timeval tp;
 	struct timezone tz;
 	u32 sr;
@@ -1121,9 +1125,6 @@
 	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;
+	return eap_server_method_register(eap);
 }
 
diff --git a/src/eap_server/eap_server_sake.c b/src/eap_server/eap_server_sake.c
index de70777..84d0e0b 100644
--- a/src/eap_server/eap_server_sake.c
+++ b/src/eap_server/eap_server_sake.c
@@ -520,7 +520,6 @@
 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");
@@ -538,8 +537,5 @@
 	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;
+	return eap_server_method_register(eap);
 }
diff --git a/src/eap_server/eap_server_sim.c b/src/eap_server/eap_server_sim.c
index ddfb71c..3a6ed79 100644
--- a/src/eap_server/eap_server_sim.c
+++ b/src/eap_server/eap_server_sim.c
@@ -846,7 +846,6 @@
 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");
@@ -864,8 +863,5 @@
 	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;
+	return eap_server_method_register(eap);
 }
diff --git a/src/eap_server/eap_server_tls.c b/src/eap_server/eap_server_tls.c
index 58cfe8a..7249858 100644
--- a/src/eap_server/eap_server_tls.c
+++ b/src/eap_server/eap_server_tls.c
@@ -48,6 +48,23 @@
 		   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);
 }
 
 
@@ -60,7 +77,7 @@
 		return NULL;
 	data->state = START;
 
-	if (eap_server_tls_ssl_init(sm, &data->ssl, 1)) {
+	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;
@@ -82,7 +99,7 @@
 		return NULL;
 	data->state = START;
 
-	if (eap_server_tls_ssl_init(sm, &data->ssl, 0)) {
+	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;
@@ -104,7 +121,8 @@
 		return NULL;
 	data->state = START;
 
-	if (eap_server_tls_ssl_init(sm, &data->ssl, 0)) {
+	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;
@@ -183,6 +201,7 @@
 		 * 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;
@@ -234,10 +253,41 @@
 			    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)
+	    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);
 }
 
 
@@ -325,7 +375,6 @@
 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");
@@ -343,10 +392,7 @@
 	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;
+	return eap_server_method_register(eap);
 }
 
 
@@ -354,7 +400,6 @@
 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,
@@ -373,10 +418,7 @@
 	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;
+	return eap_server_method_register(eap);
 }
 #endif /* EAP_SERVER_UNAUTH_TLS */
 
@@ -385,7 +427,6 @@
 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,
@@ -404,9 +445,6 @@
 	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;
+	return eap_server_method_register(eap);
 }
 #endif /* CONFIG_HS20 */
diff --git a/src/eap_server/eap_server_tls_common.c b/src/eap_server/eap_server_tls_common.c
index 23498c9..05677b7 100644
--- a/src/eap_server/eap_server_tls_common.c
+++ b/src/eap_server/eap_server_tls_common.c
@@ -44,8 +44,11 @@
 
 
 int eap_server_tls_ssl_init(struct eap_sm *sm, struct eap_ssl_data *data,
-			    int verify_peer)
+			    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;
@@ -68,7 +71,13 @@
 #endif /* CONFIG_TESTING_OPTIONS */
 #endif /* CONFIG_TLS_INTERNAL */
 
-	if (tls_connection_set_verify(sm->ssl_ctx, data->conn, verify_peer)) {
+	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);
@@ -133,10 +142,10 @@
 				      struct eap_ssl_data *data, u8 eap_type,
 				      size_t *len)
 {
-	struct tls_keys keys;
+	struct tls_random keys;
 	u8 *out;
 
-	if (tls_connection_get_keys(sm->ssl_ctx, data->conn, &keys))
+	if (tls_connection_get_random(sm->ssl_ctx, data->conn, &keys))
 		return NULL;
 
 	if (keys.client_random == NULL || keys.server_random == NULL)
diff --git a/src/eap_server/eap_server_tnc.c b/src/eap_server/eap_server_tnc.c
index 21bd26f..b568558 100644
--- a/src/eap_server/eap_server_tnc.c
+++ b/src/eap_server/eap_server_tnc.c
@@ -554,7 +554,6 @@
 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");
@@ -569,8 +568,5 @@
 	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;
+	return eap_server_method_register(eap);
 }
diff --git a/src/eap_server/eap_server_ttls.c b/src/eap_server/eap_server_ttls.c
index 31c67e8..a53633f 100644
--- a/src/eap_server/eap_server_ttls.c
+++ b/src/eap_server/eap_server_ttls.c
@@ -71,6 +71,36 @@
 		   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);
 }
 
 
@@ -317,7 +347,7 @@
 	data->ttls_version = EAP_TTLS_VERSION;
 	data->state = START;
 
-	if (eap_server_tls_ssl_init(sm, &data->ssl, 0)) {
+	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;
@@ -518,6 +548,7 @@
 
 	wpa_printf(MSG_DEBUG, "EAP-TTLS/PAP: Correct user password");
 	eap_ttls_state(data, SUCCESS);
+	eap_ttls_valid_session(sm, data);
 }
 
 
@@ -576,6 +607,7 @@
 	    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);
@@ -643,6 +675,7 @@
 	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",
@@ -906,6 +939,7 @@
 		break;
 	case PHASE2_METHOD:
 		eap_ttls_state(data, SUCCESS);
+		eap_ttls_valid_session(sm, data);
 		break;
 	case FAILURE:
 		break;
@@ -1129,6 +1163,7 @@
 			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");
@@ -1155,10 +1190,64 @@
 			     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_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);
 }
 
 
@@ -1246,7 +1335,6 @@
 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");
@@ -1264,8 +1352,5 @@
 	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;
+	return eap_server_method_register(eap);
 }
diff --git a/src/eap_server/eap_server_vendor_test.c b/src/eap_server/eap_server_vendor_test.c
index 30f600d..9639977 100644
--- a/src/eap_server/eap_server_vendor_test.c
+++ b/src/eap_server/eap_server_vendor_test.c
@@ -168,7 +168,6 @@
 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,
@@ -185,8 +184,5 @@
 	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;
+	return eap_server_method_register(eap);
 }
diff --git a/src/eap_server/eap_server_wsc.c b/src/eap_server/eap_server_wsc.c
index 9d9c28d..7d9d285 100644
--- a/src/eap_server/eap_server_wsc.c
+++ b/src/eap_server/eap_server_wsc.c
@@ -488,7 +488,6 @@
 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,
@@ -505,8 +504,5 @@
 	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;
+	return eap_server_method_register(eap);
 }
diff --git a/src/eap_server/eap_sim_db.c b/src/eap_server/eap_sim_db.c
index acf5435..d84c3d2 100644
--- a/src/eap_server/eap_sim_db.c
+++ b/src/eap_server/eap_sim_db.c
@@ -66,6 +66,7 @@
 	struct eap_sim_pseudonym *pseudonyms;
 	struct eap_sim_reauth *reauths;
 	struct eap_sim_db_pending *pending;
+	unsigned int eap_sim_db_timeout;
 #ifdef CONFIG_SQLITE
 	sqlite3 *sqlite_db;
 	char db_tmp_identity[100];
@@ -76,6 +77,10 @@
 };
 
 
+static void eap_sim_db_del_timeout(void *eloop_ctx, void *user_ctx);
+static void eap_sim_db_query_timeout(void *eloop_ctx, void *user_ctx);
+
+
 #ifdef CONFIG_SQLITE
 
 static int db_table_exists(sqlite3 *db, const char *name)
@@ -397,6 +402,57 @@
 }
 
 
+static void eap_sim_db_free_pending(struct eap_sim_db_data *data,
+				    struct eap_sim_db_pending *entry)
+{
+	eloop_cancel_timeout(eap_sim_db_query_timeout, data, entry);
+	eloop_cancel_timeout(eap_sim_db_del_timeout, data, entry);
+	os_free(entry);
+}
+
+
+static void eap_sim_db_del_pending(struct eap_sim_db_data *data,
+				   struct eap_sim_db_pending *entry)
+{
+	struct eap_sim_db_pending **pp = &data->pending;
+
+	while (*pp != NULL) {
+		if (*pp == entry) {
+			*pp = entry->next;
+			eap_sim_db_free_pending(data, entry);
+			return;
+		}
+		pp = &(*pp)->next;
+	}
+}
+
+
+static void eap_sim_db_del_timeout(void *eloop_ctx, void *user_ctx)
+{
+	struct eap_sim_db_data *data = eloop_ctx;
+	struct eap_sim_db_pending *entry = user_ctx;
+
+	wpa_printf(MSG_DEBUG, "EAP-SIM DB: Delete query timeout for %p", entry);
+	eap_sim_db_del_pending(data, entry);
+}
+
+
+static void eap_sim_db_query_timeout(void *eloop_ctx, void *user_ctx)
+{
+	struct eap_sim_db_data *data = eloop_ctx;
+	struct eap_sim_db_pending *entry = user_ctx;
+
+	/*
+	 * Report failure and allow some time for EAP server to process it
+	 * before deleting the query.
+	 */
+	wpa_printf(MSG_DEBUG, "EAP-SIM DB: Query timeout for %p", entry);
+	entry->state = FAILURE;
+	data->get_complete_cb(data->ctx, entry->cb_session_ctx);
+	eloop_register_timeout(1, 0, eap_sim_db_del_timeout, data, entry);
+}
+
+
 static void eap_sim_db_sim_resp_auth(struct eap_sim_db_data *data,
 				     const char *imsi, char *buf)
 {
@@ -472,7 +528,7 @@
 
 parse_fail:
 	wpa_printf(MSG_DEBUG, "EAP-SIM DB: Failed to parse response string");
-	os_free(entry);
+	eap_sim_db_free_pending(data, entry);
 }
 
 
@@ -563,7 +619,7 @@
 
 parse_fail:
 	wpa_printf(MSG_DEBUG, "EAP-SIM DB: Failed to parse response string");
-	os_free(entry);
+	eap_sim_db_free_pending(data, entry);
 }
 
 
@@ -690,12 +746,13 @@
 /**
  * eap_sim_db_init - Initialize EAP-SIM DB / authentication gateway interface
  * @config: Configuration data (e.g., file name)
+ * @db_timeout: Database lookup timeout
  * @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,
+eap_sim_db_init(const char *config, unsigned int db_timeout,
 		void (*get_complete_cb)(void *ctx, void *session_ctx),
 		void *ctx)
 {
@@ -709,6 +766,7 @@
 	data->sock = -1;
 	data->get_complete_cb = get_complete_cb;
 	data->ctx = ctx;
+	data->eap_sim_db_timeout = db_timeout;
 	data->fname = os_strdup(config);
 	if (data->fname == NULL)
 		goto fail;
@@ -796,7 +854,7 @@
 	while (pending) {
 		prev_pending = pending;
 		pending = pending->next;
-		os_free(prev_pending);
+		eap_sim_db_free_pending(data, prev_pending);
 	}
 
 	os_free(data);
@@ -833,11 +891,11 @@
 }
 
 
-static void eap_sim_db_expire_pending(struct eap_sim_db_data *data)
+static void eap_sim_db_expire_pending(struct eap_sim_db_data *data,
+				      struct eap_sim_db_pending *entry)
 {
-	/* 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 */
+	eloop_register_timeout(data->eap_sim_db_timeout, 0,
+			       eap_sim_db_query_timeout, data, entry);
 }
 
 
@@ -891,7 +949,7 @@
 		if (entry->state == FAILURE) {
 			wpa_printf(MSG_DEBUG, "EAP-SIM DB: Pending entry -> "
 				   "failure");
-			os_free(entry);
+			eap_sim_db_free_pending(data, entry);
 			return EAP_SIM_DB_FAILURE;
 		}
 
@@ -911,7 +969,7 @@
 		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);
+		eap_sim_db_free_pending(data, entry);
 		return num_chal;
 	}
 
@@ -945,7 +1003,8 @@
 	entry->cb_session_ctx = cb_session_ctx;
 	entry->state = PENDING;
 	eap_sim_db_add_pending(data, entry);
-	eap_sim_db_expire_pending(data);
+	eap_sim_db_expire_pending(data, entry);
+	wpa_printf(MSG_DEBUG, "EAP-SIM DB: Added query %p", entry);
 
 	return EAP_SIM_DB_PENDING;
 }
@@ -1356,7 +1415,7 @@
 	entry = eap_sim_db_get_pending(data, imsi, 1);
 	if (entry) {
 		if (entry->state == FAILURE) {
-			os_free(entry);
+			eap_sim_db_free_pending(data, entry);
 			wpa_printf(MSG_DEBUG, "EAP-SIM DB: Failure");
 			return EAP_SIM_DB_FAILURE;
 		}
@@ -1375,7 +1434,7 @@
 		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);
+		eap_sim_db_free_pending(data, entry);
 		return 0;
 	}
 
@@ -1406,7 +1465,8 @@
 	entry->cb_session_ctx = cb_session_ctx;
 	entry->state = PENDING;
 	eap_sim_db_add_pending(data, entry);
-	eap_sim_db_expire_pending(data);
+	eap_sim_db_expire_pending(data, entry);
+	wpa_printf(MSG_DEBUG, "EAP-SIM DB: Added query %p", entry);
 
 	return EAP_SIM_DB_PENDING;
 }
diff --git a/src/eap_server/eap_sim_db.h b/src/eap_server/eap_sim_db.h
index 53a1a7c..ca900b9 100644
--- a/src/eap_server/eap_sim_db.h
+++ b/src/eap_server/eap_sim_db.h
@@ -31,7 +31,7 @@
 struct eap_sim_db_data;
 
 struct eap_sim_db_data *
-eap_sim_db_init(const char *config,
+eap_sim_db_init(const char *config, unsigned int db_timeout,
 		void (*get_complete_cb)(void *ctx, void *session_ctx),
 		void *ctx);
 
diff --git a/src/eap_server/eap_tls_common.h b/src/eap_server/eap_tls_common.h
index ddf90b8..dc943eb 100644
--- a/src/eap_server/eap_tls_common.h
+++ b/src/eap_server/eap_tls_common.h
@@ -70,7 +70,7 @@
 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 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);
diff --git a/src/eap_server/ikev2.c b/src/eap_server/ikev2.c
index 632598f..5385cd8 100644
--- a/src/eap_server/ikev2.c
+++ b/src/eap_server/ikev2.c
@@ -133,7 +133,7 @@
 
 	t = (const struct ikev2_transform *) pos;
 	transform_len = WPA_GET_BE16(t->transform_length);
-	if (transform_len < (int) sizeof(*t) || pos + transform_len > end) {
+	if (transform_len < (int) sizeof(*t) || transform_len > end - pos) {
 		wpa_printf(MSG_INFO, "IKEV2: Invalid transform length %d",
 			   transform_len);
 		return -1;
@@ -221,7 +221,7 @@
 
 	p = (const struct ikev2_proposal *) pos;
 	proposal_len = WPA_GET_BE16(p->proposal_length);
-	if (proposal_len < (int) sizeof(*p) || pos + proposal_len > end) {
+	if (proposal_len < (int) sizeof(*p) || proposal_len > end - pos) {
 		wpa_printf(MSG_INFO, "IKEV2: Invalid proposal length %d",
 			   proposal_len);
 		return -1;
@@ -256,7 +256,7 @@
 
 	ppos = (const u8 *) (p + 1);
 	pend = pos + proposal_len;
-	if (ppos + p->spi_size > pend) {
+	if (p->spi_size > pend - ppos) {
 		wpa_printf(MSG_INFO, "IKEV2: Not enough room for SPI "
 			   "in proposal");
 		return -1;
diff --git a/src/eapol_auth/eapol_auth_sm.c b/src/eapol_auth/eapol_auth_sm.c
index 3b0c2e4..ff673bb 100644
--- a/src/eapol_auth/eapol_auth_sm.c
+++ b/src/eapol_auth/eapol_auth_sm.c
@@ -1,6 +1,6 @@
 /*
  * IEEE 802.1X-2004 Authenticator - EAPOL state machine
- * Copyright (c) 2002-2014, Jouni Malinen <j@w1.fi>
+ * 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.
@@ -198,6 +198,18 @@
 {
 	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;
 }
 
 
@@ -835,6 +847,7 @@
 	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);
@@ -853,10 +866,13 @@
 		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;
+#ifndef CONFIG_NO_RADIUS
+	if (radius_gen_session_id((u8 *) &sm->acct_multi_session_id,
+				  sizeof(sm->acct_multi_session_id)) < 0) {
+		eapol_auth_free(sm);
+		return NULL;
+	}
+#endif /* CONFIG_NO_RADIUS */
 
 	return sm;
 }
@@ -871,6 +887,9 @@
 	eloop_cancel_timeout(eapol_sm_step_cb, sm, NULL);
 	if (sm->eap)
 		eap_server_sm_deinit(sm->eap);
+
+	wpabuf_free(sm->radius_cui);
+	os_free(sm->identity);
 	os_free(sm);
 }
 
@@ -1080,6 +1099,87 @@
 }
 
 
+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)
 {
@@ -1148,6 +1248,7 @@
 	}
 	dst->erp_send_reauth_start = src->erp_send_reauth_start;
 	dst->erp = src->erp;
+	dst->tls_session_lifetime = src->tls_session_lifetime;
 
 	return 0;
 
@@ -1176,7 +1277,6 @@
 					     struct eapol_auth_cb *cb)
 {
 	struct eapol_authenticator *eapol;
-	struct os_time now;
 
 	eapol = os_zalloc(sizeof(*eapol));
 	if (eapol == NULL)
@@ -1205,12 +1305,6 @@
 	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;
 }
 
diff --git a/src/eapol_auth/eapol_auth_sm.h b/src/eapol_auth/eapol_auth_sm.h
index ebed19a..e1974e4 100644
--- a/src/eapol_auth/eapol_auth_sm.h
+++ b/src/eapol_auth/eapol_auth_sm.h
@@ -1,6 +1,6 @@
 /*
  * IEEE 802.1X-2004 Authenticator - EAPOL state machine
- * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
+ * 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.
@@ -27,6 +27,7 @@
 	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;
@@ -94,5 +95,8 @@
 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/src/eapol_auth/eapol_auth_sm_i.h b/src/eapol_auth/eapol_auth_sm_i.h
index a29b49c..04386b2 100644
--- a/src/eapol_auth/eapol_auth_sm_i.h
+++ b/src/eapol_auth/eapol_auth_sm_i.h
@@ -30,9 +30,6 @@
 
 	u8 *default_wep_key;
 	u8 default_wep_key_idx;
-
-	u32 acct_multi_session_id_hi;
-	u32 acct_multi_session_id_lo;
 };
 
 
@@ -162,12 +159,6 @@
 	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 */
@@ -179,8 +170,7 @@
 
 	int remediation;
 
-	u32 acct_multi_session_id_hi;
-	u32 acct_multi_session_id_lo;
+	u64 acct_multi_session_id;
 };
 
 #endif /* EAPOL_AUTH_SM_I_H */
diff --git a/src/eapol_supp/eapol_supp_sm.c b/src/eapol_supp/eapol_supp_sm.c
index eb8c5bb..65460fc 100644
--- a/src/eapol_supp/eapol_supp_sm.c
+++ b/src/eapol_supp/eapol_supp_sm.c
@@ -244,7 +244,8 @@
 
 SM_STATE(SUPP_PAE, CONNECTING)
 {
-	int send_start = sm->SUPP_PAE_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)
@@ -313,6 +314,16 @@
 {
 	SM_ENTRY(SUPP_PAE, RESTART);
 	sm->eapRestart = TRUE;
+	if (sm->altAccept) {
+		/*
+		 * Prevent EAP peer state machine from failing due to prior
+		 * external EAP success notification (altSuccess=TRUE in the
+		 * IDLE state could result in a transition to the FAILURE state.
+		 */
+		wpa_printf(MSG_DEBUG, "EAPOL: Clearing prior altAccept TRUE");
+		sm->eapSuccess = FALSE;
+		sm->altAccept = FALSE;
+	}
 }
 
 
@@ -653,7 +664,9 @@
 	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;
@@ -747,6 +760,13 @@
 		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);
@@ -755,6 +775,7 @@
 			 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
diff --git a/src/fst/Makefile b/src/fst/Makefile
new file mode 100644
index 0000000..9c41962
--- /dev/null
+++ b/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/src/fst/fst.c b/src/fst/fst.c
new file mode 100644
index 0000000..40430e2
--- /dev/null
+++ b/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, FALSE))
+		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/src/fst/fst.h b/src/fst/fst.h
new file mode 100644
index 0000000..0c0e435
--- /dev/null
+++ b/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/src/fst/fst_ctrl_aux.c b/src/fst/fst_ctrl_aux.c
new file mode 100644
index 0000000..b632827
--- /dev/null
+++ b/src/fst/fst_ctrl_aux.c
@@ -0,0 +1,70 @@
+/*
+ * 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/src/fst/fst_ctrl_aux.h b/src/fst/fst_ctrl_aux.h
new file mode 100644
index 0000000..e2133f5
--- /dev/null
+++ b/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/src/fst/fst_ctrl_defs.h b/src/fst/fst_ctrl_defs.h
new file mode 100644
index 0000000..6735389
--- /dev/null
+++ b/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/src/fst/fst_ctrl_iface.c b/src/fst/fst_ctrl_iface.c
new file mode 100644
index 0000000..7820e58
--- /dev/null
+++ b/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, TRUE)) {
+				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/src/fst/fst_ctrl_iface.h b/src/fst/fst_ctrl_iface.h
new file mode 100644
index 0000000..4d0cd9f
--- /dev/null
+++ b/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/src/fst/fst_defs.h b/src/fst/fst_defs.h
new file mode 100644
index 0000000..8ddcc61
--- /dev/null
+++ b/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/src/fst/fst_group.c b/src/fst/fst_group.c
new file mode 100644
index 0000000..d6157b1
--- /dev/null
+++ b/src/fst/fst_group.c
@@ -0,0 +1,437 @@
+/*
+ * 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;
+
+
+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;
+
+	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,
+							   FALSE) &&
+				    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/src/fst/fst_group.h b/src/fst/fst_group.h
new file mode 100644
index 0000000..3a87c0b
--- /dev/null
+++ b/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/src/fst/fst_iface.c b/src/fst/fst_iface.c
new file mode 100644
index 0000000..35e83cb
--- /dev/null
+++ b/src/fst/fst_iface.c
@@ -0,0 +1,80 @@
+/*
+ * 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,
+			       Boolean mb_only)
+{
+	struct fst_get_peer_ctx *ctx;
+	const u8 *a = fst_iface_get_peer_first(iface, &ctx, mb_only);
+
+	for (; a != NULL; a = fst_iface_get_peer_next(iface, &ctx, mb_only))
+		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/src/fst/fst_iface.h b/src/fst/fst_iface.h
new file mode 100644
index 0000000..0eb2732
--- /dev/null
+++ b/src/fst/fst_iface.h
@@ -0,0 +1,136 @@
+/*
+ * 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,
+			       Boolean mb_only);
+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/src/fst/fst_internal.h b/src/fst/fst_internal.h
new file mode 100644
index 0000000..9fe32b8
--- /dev/null
+++ b/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/src/fst/fst_session.c b/src/fst/fst_session.c
new file mode 100644
index 0000000..449e304
--- /dev/null
+++ b/src/fst/fst_session.c
@@ -0,0 +1,1630 @@
+/*
+ * 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)
+{
+	/* Action frames sometimes get delayed. Use relaxed timeout (2*) */
+	eloop_register_timeout(0, 2 * 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 (fst_session_is_ready_pending(s) &&
+		    /* waiting for Setup Response */
+		    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;
+		}
+
+		/*
+		 * State is SETUP_COMPLETION (either in transition or not) or
+		 * TRANSITION_DONE (in transition).
+		 * Setup Request arriving in this state could mean:
+		 * 1. peer sent it before receiving our Setup Request (race
+		 *    condition)
+		 * 2. peer didn't receive our Setup Response. Peer is retrying
+		 *    after STT timeout
+		 * 3. peer's FST state machines are out of sync due to some
+		 *    other reason
+		 *
+		 * We will reset our session and create a new one instead.
+		 */
+
+		fst_printf_session(s, MSG_WARNING,
+			"resetting due to FST request");
+
+		/*
+		 * If FST Setup Request arrived with the same FSTS ID as one we
+		 * initialized before, 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);
+	}
+
+	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 = {
+			.reject_code = 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,
+				    FALSE)) {
+		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,
+				    FALSE)) {
+		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, FALSE)) {
+		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/src/fst/fst_session.h b/src/fst/fst_session.h
new file mode 100644
index 0000000..1162de4
--- /dev/null
+++ b/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/src/l2_packet/l2_packet_linux.c b/src/l2_packet/l2_packet_linux.c
index 41de2f8..a7a300e 100644
--- a/src/l2_packet/l2_packet_linux.c
+++ b/src/l2_packet/l2_packet_linux.c
@@ -30,11 +30,14 @@
 	int l2_hdr; /* whether to include layer 2 (Ethernet) header data
 		     * buffers */
 
+#ifndef CONFIG_NO_LINUX_PACKET_SOCKET_WAR
 	/* For working around Linux packet socket behavior and regression. */
 	int fd_br_rx;
-	int last_from_br;
+	int last_from_br, last_from_br_prev;
 	u8 last_hash[SHA1_MAC_LEN];
-	unsigned int num_rx, num_rx_br;
+	u8 last_hash_prev[SHA1_MAC_LEN];
+	unsigned int num_rx_br;
+#endif /* CONFIG_NO_LINUX_PACKET_SOCKET_WAR */
 };
 
 /* Generated by 'sudo tcpdump -s 3000 -dd greater 278 and ip and udp and
@@ -127,7 +130,6 @@
 	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,
@@ -141,6 +143,7 @@
 	wpa_printf(MSG_DEBUG, "%s: src=" MACSTR " len=%d",
 		   __func__, MAC2STR(ll.sll_addr), (int) res);
 
+#ifndef CONFIG_NO_LINUX_PACKET_SOCKET_WAR
 	if (l2->fd_br_rx >= 0) {
 		u8 hash[SHA1_MAC_LEN];
 		const u8 *addr[1];
@@ -169,14 +172,24 @@
 				   __func__);
 			return;
 		}
+		if (l2->last_from_br_prev &&
+		    os_memcmp(hash, l2->last_hash_prev, SHA1_MAC_LEN) == 0) {
+			wpa_printf(MSG_DEBUG, "%s: Drop duplicate RX(prev)",
+				   __func__);
+			return;
+		}
+		os_memcpy(l2->last_hash_prev, l2->last_hash, SHA1_MAC_LEN);
+		l2->last_from_br_prev = l2->last_from_br;
 		os_memcpy(l2->last_hash, hash, SHA1_MAC_LEN);
 	}
 
 	l2->last_from_br = 0;
+#endif /* CONFIG_NO_LINUX_PACKET_SOCKET_WAR */
 	l2->rx_callback(l2->rx_callback_ctx, ll.sll_addr, buf, res);
 }
 
 
+#ifndef CONFIG_NO_LINUX_PACKET_SOCKET_WAR
 static void l2_packet_receive_br(int sock, void *eloop_ctx, void *sock_ctx)
 {
 	struct l2_packet_data *l2 = eloop_ctx;
@@ -202,6 +215,11 @@
 	wpa_printf(MSG_DEBUG, "%s: src=" MACSTR " len=%d",
 		   __func__, MAC2STR(ll.sll_addr), (int) res);
 
+	if (os_memcmp(ll.sll_addr, l2->own_addr, ETH_ALEN) == 0) {
+		wpa_printf(MSG_DEBUG, "%s: Drop RX of own frame", __func__);
+		return;
+	}
+
 	addr[0] = buf;
 	len[0] = res;
 	sha1_vector(1, addr, len, hash);
@@ -210,10 +228,18 @@
 		wpa_printf(MSG_DEBUG, "%s: Drop duplicate RX", __func__);
 		return;
 	}
+	if (!l2->last_from_br_prev &&
+	    os_memcmp(hash, l2->last_hash_prev, SHA1_MAC_LEN) == 0) {
+		wpa_printf(MSG_DEBUG, "%s: Drop duplicate RX(prev)", __func__);
+		return;
+	}
+	os_memcpy(l2->last_hash_prev, l2->last_hash, SHA1_MAC_LEN);
+	l2->last_from_br_prev = l2->last_from_br;
 	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);
 }
+#endif /* CONFIG_NO_LINUX_PACKET_SOCKET_WAR */
 
 
 struct l2_packet_data * l2_packet_init(
@@ -233,7 +259,9 @@
 	l2->rx_callback = rx_callback;
 	l2->rx_callback_ctx = rx_callback_ctx;
 	l2->l2_hdr = l2_hdr;
+#ifndef CONFIG_NO_LINUX_PACKET_SOCKET_WAR
 	l2->fd_br_rx = -1;
+#endif /* CONFIG_NO_LINUX_PACKET_SOCKET_WAR */
 
 	l2->fd = socket(PF_PACKET, l2_hdr ? SOCK_RAW : SOCK_DGRAM,
 			htons(protocol));
@@ -289,6 +317,7 @@
 	void *rx_callback_ctx, int l2_hdr)
 {
 	struct l2_packet_data *l2;
+#ifndef CONFIG_NO_LINUX_PACKET_SOCKET_WAR
 	struct sock_filter ethertype_sock_filter_insns[] = {
 		/* Load ethertype */
 		BPF_STMT(BPF_LD | BPF_H | BPF_ABS, 2 * ETH_ALEN),
@@ -304,12 +333,14 @@
 		.filter = ethertype_sock_filter_insns,
 	};
 	struct sockaddr_ll ll;
+#endif /* CONFIG_NO_LINUX_PACKET_SOCKET_WAR */
 
 	l2 = l2_packet_init(br_ifname, own_addr, protocol, rx_callback,
 			    rx_callback_ctx, l2_hdr);
 	if (!l2)
 		return NULL;
 
+#ifndef CONFIG_NO_LINUX_PACKET_SOCKET_WAR
 	/*
 	 * The Linux packet socket behavior has changed over the years and there
 	 * is an inconvenient regression in it that breaks RX for a specific
@@ -357,6 +388,7 @@
 	}
 
 	eloop_register_read_sock(l2->fd_br_rx, l2_packet_receive_br, l2, NULL);
+#endif /* CONFIG_NO_LINUX_PACKET_SOCKET_WAR */
 
 	return l2;
 }
@@ -372,10 +404,12 @@
 		close(l2->fd);
 	}
 
+#ifndef CONFIG_NO_LINUX_PACKET_SOCKET_WAR
 	if (l2->fd_br_rx >= 0) {
 		eloop_unregister_read_sock(l2->fd_br_rx);
 		close(l2->fd_br_rx);
 	}
+#endif /* CONFIG_NO_LINUX_PACKET_SOCKET_WAR */
 
 	os_free(l2);
 }
diff --git a/src/l2_packet/l2_packet_pcap.c b/src/l2_packet/l2_packet_pcap.c
index bb4f4a3..423c099 100644
--- a/src/l2_packet/l2_packet_pcap.c
+++ b/src/l2_packet/l2_packet_pcap.c
@@ -312,6 +312,18 @@
 }
 
 
+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)
diff --git a/src/p2p/p2p.c b/src/p2p/p2p.c
index b87ff96..6942c85 100644
--- a/src/p2p/p2p.c
+++ b/src/p2p/p2p.c
@@ -10,6 +10,7 @@
 
 #include "common.h"
 #include "eloop.h"
+#include "common/defs.h"
 #include "common/ieee802_11_defs.h"
 #include "common/ieee802_11_common.h"
 #include "common/wpa_ctrl.h"
@@ -48,9 +49,8 @@
 #define P2P_PEER_EXPIRATION_AGE 60
 #endif /* P2P_PEER_EXPIRATION_AGE */
 
-#define P2P_PEER_EXPIRATION_INTERVAL (P2P_PEER_EXPIRATION_AGE / 2)
 
-static void p2p_expire_peers(struct p2p_data *p2p)
+void p2p_expire_peers(struct p2p_data *p2p)
 {
 	struct p2p_device *dev, *n;
 	struct os_reltime now;
@@ -103,15 +103,6 @@
 }
 
 
-static void p2p_expiration_timeout(void *eloop_ctx, void *timeout_ctx)
-{
-	struct p2p_data *p2p = eloop_ctx;
-	p2p_expire_peers(p2p);
-	eloop_register_timeout(P2P_PEER_EXPIRATION_INTERVAL, 0,
-			       p2p_expiration_timeout, p2p, NULL);
-}
-
-
 static const char * p2p_state_txt(int state)
 {
 	switch (state) {
@@ -455,8 +446,9 @@
 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';
+	p2p_copy_filter_devname(dev->info.device_name,
+				sizeof(dev->info.device_name),
+				cli->dev_name, cli->dev_name_len);
 	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);
@@ -646,11 +638,11 @@
 
 	end = ies + ies_len;
 
-	for (pos = ies; pos + 1 < end; pos += len) {
+	for (pos = ies; end - pos > 1; pos += len) {
 		id = *pos++;
 		len = *pos++;
 
-		if (pos + len > end)
+		if (len > end - pos)
 			break;
 
 		if (id != WLAN_EID_VENDOR_SPECIFIC || len < 3)
@@ -1142,7 +1134,7 @@
 	if (adv_len >= sizeof(str_buf))
 		return 0;
 
-	for (i = 0; str[i] && i < adv_len; i++) {
+	for (i = 0; i < adv_len; i++) {
 		if (str[i] >= 'A' && str[i] <= 'Z')
 			str_buf[i] = str[i] - 'A' + 'a';
 		else
@@ -1230,9 +1222,14 @@
 
 	p2p->start_after_scan = P2P_AFTER_SCAN_NOTHING;
 	p2p_clear_timeout(p2p);
+	if (p2p->pending_listen_freq) {
+		p2p_dbg(p2p, "Clear pending_listen_freq for p2p_find");
+		p2p->pending_listen_freq = 0;
+	}
 	p2p->cfg->stop_listen(p2p->cfg->cb_ctx);
 	p2p->find_type = type;
 	p2p_device_clear_reported(p2p);
+	os_memset(p2p->sd_query_no_ack, 0, ETH_ALEN);
 	p2p_set_state(p2p, P2P_SEARCH);
 	p2p->search_delay = search_delay;
 	p2p->in_search_delay = 0;
@@ -1469,7 +1466,7 @@
 
 
 /**
- * p2p_prepare_channel - Select operating channel for GO Negotiation
+ * p2p_prepare_channel - Select operating channel for GO Negotiation or P2PS PD
  * @p2p: P2P module context from p2p_init()
  * @dev: Selected peer device
  * @force_freq: Forced frequency in MHz or 0 if not forced
@@ -1478,9 +1475,9 @@
  * 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.
+ * Negotiation prior to having received peer information or for P2PS PD
+ * signalling. 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)
@@ -2038,8 +2035,23 @@
 
 	dev = p2p_get_device(p2p, addr);
 	if (dev) {
-		if (dev->country[0] == 0 && msg.listen_channel)
-			os_memcpy(dev->country, msg.listen_channel, 3);
+		if (msg.listen_channel) {
+			int freq;
+
+			if (dev->country[0] == 0)
+				os_memcpy(dev->country, msg.listen_channel, 3);
+
+			freq = p2p_channel_to_freq(msg.listen_channel[3],
+						   msg.listen_channel[4]);
+
+			if (freq > 0 && dev->listen_freq != freq) {
+				p2p_dbg(p2p,
+					"Updated peer " MACSTR " Listen channel (Probe Request): %d -> %d MHz",
+					MAC2STR(addr), dev->listen_freq, freq);
+				dev->listen_freq = freq;
+			}
+		}
+
 		os_get_reltime(&dev->last_seen);
 		p2p_parse_free(&msg);
 		return; /* already known */
@@ -2689,13 +2701,14 @@
 
 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 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])
+	if (!p2p || !adv_str || !adv_str[0] || !cpt_priority)
 		return -1;
 
 	if (!(config_methods & p2p->cfg->config_methods)) {
@@ -2724,6 +2737,11 @@
 	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);
@@ -2762,8 +2780,9 @@
 
 inserted:
 	p2p_dbg(p2p,
-		"Added ASP advertisement adv_id=0x%x config_methods=0x%x svc_state=0x%x adv_str='%s'",
-		adv_id, adv_data->config_methods, svc_state, adv_str);
+		"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;
 }
@@ -2919,9 +2938,6 @@
 
 	dl_list_init(&p2p->devices);
 
-	eloop_register_timeout(P2P_PEER_EXPIRATION_INTERVAL, 0,
-			       p2p_expiration_timeout, p2p, NULL);
-
 	p2p->go_timeout = 100;
 	p2p->client_timeout = 20;
 	p2p->num_p2p_sd_queries = 0;
@@ -2950,8 +2966,6 @@
 	wpabuf_free(p2p->wfd_coupled_sink_info);
 #endif /* CONFIG_WIFI_DISPLAY */
 
-	eloop_cancel_timeout(p2p_expiration_timeout, p2p, NULL);
-	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);
@@ -2978,6 +2992,8 @@
 void p2p_flush(struct p2p_data *p2p)
 {
 	struct p2p_device *dev, *prev;
+
+	p2p_ext_listen(p2p, 0, 0);
 	p2p_stop_find(p2p);
 	dl_list_for_each_safe(dev, prev, &p2p->devices, struct p2p_device,
 			      list) {
@@ -2987,6 +3003,7 @@
 	p2p_free_sd_queries(p2p);
 	os_free(p2p->after_scan_tx);
 	p2p->after_scan_tx = NULL;
+	p2p->ssid_set = 0;
 }
 
 
@@ -3163,13 +3180,18 @@
 
 static int p2p_pre_find_operation(struct p2p_data *p2p, struct p2p_device *dev)
 {
+	int res;
+
 	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)
+	res = p2p_start_sd(p2p, dev);
+	if (res == -2)
+		return -2;
+	if (res == 0)
 		return 1;
 
 	if (dev->req_config_methods &&
@@ -3189,7 +3211,7 @@
 void p2p_continue_find(struct p2p_data *p2p)
 {
 	struct p2p_device *dev;
-	int found;
+	int found, res;
 
 	p2p_set_state(p2p, P2P_SEARCH);
 
@@ -3202,10 +3224,13 @@
 		}
 		if (!found)
 			continue;
-		if (p2p_pre_find_operation(p2p, dev) > 0) {
+		res = p2p_pre_find_operation(p2p, dev);
+		if (res > 0) {
 			p2p->last_p2p_find_oper = dev;
 			return;
 		}
+		if (res == -2)
+			goto skip_sd;
 	}
 
 	/*
@@ -3213,14 +3238,19 @@
 	 * iteration device.
 	 */
 	dl_list_for_each(dev, &p2p->devices, struct p2p_device, list) {
-		if (p2p_pre_find_operation(p2p, dev) > 0) {
+		res = p2p_pre_find_operation(p2p, dev);
+		if (res > 0) {
 			p2p->last_p2p_find_oper = dev;
 			return;
 		}
+		if (res == -2)
+			goto skip_sd;
 		if (dev == p2p->last_p2p_find_oper)
 			break;
 	}
 
+skip_sd:
+	os_memset(p2p->sd_query_no_ack, 0, ETH_ALEN);
 	p2p_listen_in_find(p2p, 1);
 }
 
@@ -3232,8 +3262,17 @@
 	p2p->pending_action_state = P2P_NO_PENDING_ACTION;
 
 	if (!success) {
-		if (p2p->sd_peer)
+		if (p2p->sd_peer) {
+			if (is_zero_ether_addr(p2p->sd_query_no_ack)) {
+				os_memcpy(p2p->sd_query_no_ack,
+					  p2p->sd_peer->info.p2p_device_addr,
+					  ETH_ALEN);
+				p2p_dbg(p2p,
+					"First SD Query no-ACK in this search iteration: "
+					MACSTR, MAC2STR(p2p->sd_query_no_ack));
+			}
 			p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
+		}
 		p2p->sd_peer = NULL;
 		if (p2p->state != P2P_IDLE)
 			p2p_continue_find(p2p);
@@ -3332,6 +3371,43 @@
 	}
 
 	/*
+	 * If after PD Request the peer doesn't expect to receive PD Response
+	 * the PD Request ACK indicates a completion of the current PD. This
+	 * happens only on the advertiser side sending the follow-on PD Request
+	 * with the status different than 12 (Success: accepted by user).
+	 */
+	if (p2p->p2ps_prov && !p2p->p2ps_prov->pd_seeker &&
+	    p2p->p2ps_prov->status != P2P_SC_SUCCESS_DEFERRED) {
+		p2p_dbg(p2p, "P2PS PD completion on Follow-on PD Request ACK");
+
+		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 (p2p->cfg->p2ps_prov_complete) {
+			p2p->cfg->p2ps_prov_complete(
+				p2p->cfg->cb_ctx,
+				p2p->p2ps_prov->status,
+				p2p->p2ps_prov->adv_mac,
+				p2p->p2ps_prov->adv_mac,
+				p2p->p2ps_prov->session_mac,
+				NULL, p2p->p2ps_prov->adv_id,
+				p2p->p2ps_prov->session_id,
+				0, 0, NULL, 0, 0, 0,
+				NULL, NULL, 0, 0, NULL, 0);
+		}
+
+		if (p2p->user_initiated_pd)
+			p2p_reset_pending_pd(p2p);
+
+		p2ps_prov_free(p2p);
+		return;
+	}
+
+	/*
 	 * This postponing, of resetting pending_action_state, needs to be
 	 * done only for user initiated PD requests and not internal ones.
 	 */
@@ -3405,9 +3481,11 @@
 		 * operation was started.
 		 */
 		p2p_dbg(p2p, "Ignore old scan result for " MACSTR
-			" (rx_time=%u.%06u)",
+			" (rx_time=%u.%06u find_start=%u.%06u)",
 			MAC2STR(bssid), (unsigned int) rx_time->sec,
-			(unsigned int) rx_time->usec);
+			(unsigned int) rx_time->usec,
+			(unsigned int) p2p->find_start.sec,
+			(unsigned int) p2p->find_start.usec);
 		return 0;
 	}
 
@@ -3432,7 +3510,8 @@
 }
 
 
-void p2p_scan_ie(struct p2p_data *p2p, struct wpabuf *ies, const u8 *dev_id)
+void p2p_scan_ie(struct p2p_data *p2p, struct wpabuf *ies, const u8 *dev_id,
+		 unsigned int bands)
 {
 	u8 dev_capab;
 	u8 *len;
@@ -3466,6 +3545,9 @@
 		p2p_buf_add_ext_listen_timing(ies, p2p->ext_listen_period,
 					      p2p->ext_listen_interval);
 
+	if (bands & BAND_60_GHZ)
+		p2p_buf_add_device_info(ies, p2p, NULL);
+
 	if (p2p->p2ps_seek && p2p->p2ps_seek_count)
 		p2p_buf_add_service_hash(ies, p2p);
 
@@ -5389,3 +5471,20 @@
 		"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/src/p2p/p2p.h b/src/p2p/p2p.h
index 67b8bdb..0feafd3 100644
--- a/src/p2p/p2p.h
+++ b/src/p2p/p2p.h
@@ -21,11 +21,17 @@
 #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
+#define P2P_MAX_REG_CLASSES 15
 
 /**
  * P2P_MAX_REG_CLASS_CHANNELS - Maximum number of channels per regulatory class
@@ -93,6 +99,10 @@
 
 	int vht;
 
+	u8 max_oper_chwidth;
+
+	unsigned int vht_center_freq2;
+
 	/**
 	 * ssid - SSID of the group
 	 */
@@ -156,6 +166,11 @@
 
 struct p2ps_provision {
 	/**
+	 * pd_seeker - P2PS provision discovery seeker role
+	 */
+	unsigned int pd_seeker:1;
+
+	/**
 	 * status - Remote returned provisioning status code
 	 */
 	int status;
@@ -196,6 +211,33 @@
 	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];
+
+	/**
+	 * force_freq - The only allowed channel frequency in MHz or 0.
+	 */
+	unsigned int force_freq;
+
+	/**
+	 * pref_freq - Preferred operating frequency in MHz or 0.
+	 */
+	unsigned int pref_freq;
+
+	/**
 	 * info - Vendor defined extra Provisioning information
 	 */
 	char info[0];
@@ -235,6 +277,23 @@
 	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];
@@ -955,18 +1014,21 @@
 
 	/**
 	 * 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 intended GO P2P Device Address
+	 * @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 *ret_ssid, size_t *ret_ssid_len,
+				    u8 *intended_iface_addr);
 
 	/**
 	 * Get information about a possible local GO role
@@ -976,6 +1038,8 @@
 	 * @ssid_len: Buffer for returning length of @ssid
 	 * @group_iface: Buffer for returning whether a separate group interface
 	 *	would be used
+	 * @freq: Variable for returning the current operating frequency of a
+	 *	currently running P2P GO.
 	 * Returns: 1 if GO info found, 0 otherwise
 	 *
 	 * This is used to compose New Group settings (SSID, and intended
@@ -983,7 +1047,8 @@
 	 * 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);
+			   u8 *ssid, size_t *ssid_len, int *group_iface,
+			   unsigned int *freq);
 
 	/**
 	 * remove_stale_groups - Remove stale P2PS groups
@@ -1007,7 +1072,10 @@
 				   u8 conncap, int passwd_id,
 				   const u8 *persist_ssid,
 				   size_t persist_ssid_size, int response_done,
-				   int prov_start, const char *session_info);
+				   int prov_start, const char *session_info,
+				   const u8 *feat_cap, size_t feat_cap_len,
+				   unsigned int freq, const u8 *group_ssid,
+				   size_t group_ssid_len);
 
 	/**
 	 * prov_disc_resp_cb - Callback for indicating completion of PD Response
@@ -1021,14 +1089,34 @@
 
 	/**
 	 * p2ps_group_capability - Determine group capability
+	 * @ctx: Callback context from cb_ctx
+	 * @incoming: Peer requested roles, expressed with P2PS_SETUP_* bitmap.
+	 * @role: Local roles, expressed with P2PS_SETUP_* bitmap.
+	 * @force_freq: Variable for returning forced frequency for the group.
+	 * @pref_freq: Variable for returning preferred frequency for the group.
+	 * Returns: P2PS_SETUP_* bitmap of group capability result.
 	 *
-	 * 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.
+	 * This function can be used to determine group capability and
+	 * frequencies based on information from P2PS PD exchange and the
+	 * current state of ongoing groups and driver capabilities.
 	 */
-	u8 (*p2ps_group_capability)(void *ctx, u8 incoming, u8 role);
+	u8 (*p2ps_group_capability)(void *ctx, u8 incoming, u8 role,
+				    unsigned int *force_freq,
+				    unsigned int *pref_freq);
+
+	/**
+	 * 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);
 };
 
 
@@ -1628,6 +1716,12 @@
 	int freq;
 
 	/**
+	 * ip_addr_alloc - Whether IP address allocation within 4-way handshake
+	 *	is supported
+	 */
+	int ip_addr_alloc;
+
+	/**
 	 * cb_ctx - Context to use with callback functions
 	 */
 	void *cb_ctx;
@@ -1814,8 +1908,10 @@
  * @p2p: P2P module context from p2p_init()
  * @ies: Buffer for writing P2P IE
  * @dev_id: Device ID to search for or %NULL for any
+ * @bands: Frequency bands used in the scan (enum wpa_radio_work_band bitmap)
  */
-void p2p_scan_ie(struct p2p_data *p2p, struct wpabuf *ies, const u8 *dev_id);
+void p2p_scan_ie(struct p2p_data *p2p, struct wpabuf *ies, const u8 *dev_id,
+		 unsigned int bands);
 
 /**
  * p2p_scan_ie_buf_len - Get maximum buffer length needed for p2p_scan_ie
@@ -2248,9 +2344,33 @@
 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);
+			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/src/p2p/p2p_build.c b/src/p2p/p2p_build.c
index c733543..793d28b 100644
--- a/src/p2p/p2p_build.c
+++ b/src/p2p/p2p_build.c
@@ -10,6 +10,7 @@
 
 #include "common.h"
 #include "common/ieee802_11_defs.h"
+#include "common/qca-vendor.h"
 #include "wps/wps_i.h"
 #include "p2p_i.h"
 
@@ -109,6 +110,44 @@
 }
 
 
+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)
 {
diff --git a/src/p2p/p2p_go_neg.c b/src/p2p/p2p_go_neg.c
index 19f1daa..9f0b3f3 100644
--- a/src/p2p/p2p_go_neg.c
+++ b/src/p2p/p2p_go_neg.c
@@ -38,7 +38,7 @@
 {
 	const u8 *pos, *end;
 	struct p2p_channels *ch;
-	size_t channels;
+	u8 channels;
 	struct p2p_channels intersection;
 
 	ch = &dev->channels;
@@ -58,14 +58,14 @@
 	}
 	pos += 3;
 
-	while (pos + 2 < end) {
+	while (end - pos > 2) {
 		struct p2p_reg_class *cl = &ch->reg_class[ch->reg_classes];
 		cl->reg_class = *pos++;
-		if (pos + 1 + pos[0] > end) {
+		channels = *pos++;
+		if (channels > end - pos) {
 			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);
@@ -185,6 +185,9 @@
 				      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)
@@ -312,7 +315,7 @@
 			       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) {
+	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,
@@ -381,7 +384,7 @@
 	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 };
+	const int op_classes_vht[] = { 128, 129, 130, 0 };
 
 	if (p2p->own_freq_preference > 0 &&
 	    p2p_freq_to_channel(p2p->own_freq_preference,
@@ -542,6 +545,195 @@
 }
 
 
+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)
 {
@@ -709,6 +901,14 @@
 			return;
 		}
 
+		if (dev->go_neg_req_sent &&
+		    (dev->flags & P2P_DEV_PEER_WAITING_RESPONSE)) {
+			p2p_dbg(p2p,
+				"Do not reply since peer is waiting for us to start a new GO Negotiation 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");
@@ -799,6 +999,12 @@
 		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];
@@ -854,7 +1060,7 @@
 			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) {
+			    wpabuf_head(resp), wpabuf_len(resp), 100) < 0) {
 		p2p_dbg(p2p, "Failed to send Action frame");
 	}
 
@@ -1062,6 +1268,11 @@
 		dev->client_timeout = msg.config_timeout[1];
 	}
 
+	if (msg.wfd_subelems) {
+		wpabuf_free(dev->info.wfd_subelems);
+		dev->info.wfd_subelems = wpabuf_dup(msg.wfd_subelems);
+	}
+
 	if (!msg.operating_channel && !go) {
 		/*
 		 * Note: P2P Client may omit Operating Channel attribute to
@@ -1150,6 +1361,13 @@
 	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);
 
@@ -1181,7 +1399,7 @@
 
 	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) {
+			    wpabuf_len(dev->go_neg_conf), 50) < 0) {
 		p2p_dbg(p2p, "Failed to send Action frame");
 		p2p_go_neg_failed(p2p, -1);
 		p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
diff --git a/src/p2p/p2p_group.c b/src/p2p/p2p_group.c
index 41ca99f..eac73ef 100644
--- a/src/p2p/p2p_group.c
+++ b/src/p2p/p2p_group.c
@@ -155,7 +155,8 @@
 		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;
+	if (group->cfg->ip_addr_alloc)
+		group_capab |= P2P_GROUP_CAPAB_IP_ADDR_ALLOCATION;
 	p2p_buf_add_capability(ie, dev_capab, group_capab);
 }
 
@@ -296,14 +297,14 @@
 	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) {
+	while (end - pos >= 3) {
 		u8 id;
 		u16 len;
 
 		id = *pos++;
 		len = WPA_GET_BE16(pos);
 		pos += 2;
-		if (pos + len > end)
+		if (len > end - pos)
 			break;
 
 		switch (id) {
@@ -1071,3 +1072,43 @@
 			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/src/p2p/p2p_i.h b/src/p2p/p2p_i.h
index a1042d2..47524d4 100644
--- a/src/p2p/p2p_i.h
+++ b/src/p2p/p2p_i.h
@@ -53,6 +53,9 @@
 	 * 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];
 
@@ -305,6 +308,18 @@
 	 */
 	int num_p2p_sd_queries;
 
+	/**
+	 * sd_query_no_ack - The first peer (Dev Addr) that did not ACK SD Query
+	 *
+	 * This is used to track the first peer that did not ACK an SD Query
+	 * within a single P2P Search iteration. All zeros address means no such
+	 * peer was yet seen. This information is used to allow a new Listen and
+	 * Search phases to be once every pending SD Query has been sent once to
+	 * each peer instead of looping all pending attempts continuously until
+	 * running out of retry maximums.
+	 */
+	u8 sd_query_no_ack[ETH_ALEN];
+
 	/* GO Negotiation data */
 
 	/**
@@ -535,6 +550,9 @@
 	u16 authorized_oob_dev_pw_id;
 
 	struct wpabuf **vendor_elem;
+
+	unsigned int pref_freq_list[P2P_MAX_PREF_CHANNELS];
+	unsigned int num_pref_freq;
 };
 
 /**
@@ -637,6 +655,9 @@
 	const u8 *persistent_dev;
 	const u8 *persistent_ssid;
 	size_t persistent_ssid_len;
+
+	const u8 *pref_freq_list;
+	size_t pref_freq_list_len;
 };
 
 
@@ -682,6 +703,8 @@
 			      u8 *op_channel);
 
 /* p2p_parse.c */
+void p2p_copy_filter_devname(char *dst, size_t dst_len,
+			     const void *src, size_t src_len);
 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);
@@ -763,6 +786,8 @@
 				       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,
@@ -792,6 +817,8 @@
 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,
diff --git a/src/p2p/p2p_invitation.c b/src/p2p/p2p_invitation.c
index f5454f7..bbba001 100644
--- a/src/p2p/p2p_invitation.c
+++ b/src/p2p/p2p_invitation.c
@@ -85,6 +85,9 @@
 	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);
@@ -281,7 +284,7 @@
 
 		if (!p2p_channels_includes(&intersection, reg_class, channel))
 		{
-			p2p_dbg(p2p, "forced freq %d MHz not in the supported channels interaction",
+			p2p_dbg(p2p, "forced freq %d MHz not in the supported channels intersection",
 				op_freq);
 			status = P2P_SC_FAIL_NO_COMMON_CHANNELS;
 			goto fail;
@@ -343,6 +346,12 @@
 			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) {
@@ -409,7 +418,7 @@
 	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) {
+			    wpabuf_head(resp), wpabuf_len(resp), 50) < 0) {
 		p2p_dbg(p2p, "Failed to send Action frame");
 	}
 
@@ -534,6 +543,12 @@
 				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);
diff --git a/src/p2p/p2p_parse.c b/src/p2p/p2p_parse.c
index 980dddf..5d2299c 100644
--- a/src/p2p/p2p_parse.c
+++ b/src/p2p/p2p_parse.c
@@ -15,11 +15,29 @@
 #include "p2p_i.h"
 
 
+void p2p_copy_filter_devname(char *dst, size_t dst_len,
+			     const void *src, size_t src_len)
+{
+	size_t i;
+
+	if (src_len >= dst_len)
+		src_len = dst_len - 1;
+	os_memcpy(dst, src, src_len);
+	dst[src_len] = '\0';
+	for (i = 0; i < src_len; i++) {
+		if (dst[i] == '\0')
+			break;
+		if (is_ctrl_char(dst[i]))
+			dst[i] = '_';
+	}
+}
+
+
 static int p2p_parse_attribute(u8 id, const u8 *data, u16 len,
 			       struct p2p_message *msg)
 {
 	const u8 *pos;
-	size_t i, nlen;
+	u16 nlen;
 	char devtype[WPS_DEV_TYPE_BUFSIZE];
 
 	switch (id) {
@@ -149,21 +167,14 @@
 		pos += 2;
 		nlen = WPA_GET_BE16(pos);
 		pos += 2;
-		if (data + len - pos < (int) nlen ||
-		    nlen > WPS_DEV_NAME_MAX_LEN) {
+		if (nlen > data + len - pos || nlen > WPS_DEV_NAME_MAX_LEN) {
 			wpa_printf(MSG_DEBUG, "P2P: Invalid Device Name "
-				   "length %d (buf len %d)", (int) nlen,
+				   "length %u (buf len %d)", 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] = '_';
-		}
+		p2p_copy_filter_devname(msg->device_name,
+					sizeof(msg->device_name), pos, nlen);
 		wpa_printf(MSG_DEBUG, "P2P: * Device Info: addr " MACSTR
 			   " primary device type %s device name '%s' "
 			   "config methods 0x%x",
@@ -548,6 +559,9 @@
 	}
 #endif /* CONFIG_WIFI_DISPLAY */
 
+	msg->pref_freq_list = elems.pref_freq_list;
+	msg->pref_freq_list_len = elems.pref_freq_list_len;
+
 	return 0;
 }
 
@@ -634,49 +648,48 @@
 	gend = gi + gi_len;
 	while (g < gend) {
 		struct p2p_client_info *cli;
-		const u8 *t, *cend;
-		int count;
+		const u8 *cend;
+		u16 count;
+		u8 len;
 
 		cli = &info->client[info->num_clients];
-		cend = g + 1 + g[0];
-		if (cend > gend)
+		len = *g++;
+		if (len > gend - g || len < 2 * ETH_ALEN + 1 + 2 + 8 + 1)
 			return -1; /* invalid data */
+		cend = g + len;
 		/* 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];
+		cli->p2p_device_addr = g;
+		g += ETH_ALEN;
+		cli->p2p_interface_addr = g;
+		g += ETH_ALEN;
+		cli->dev_capab = *g++;
 
-		if (t + 1 + 2 + 8 + 1 > cend)
-			return -1; /* invalid data */
+		cli->config_methods = WPA_GET_BE16(g);
+		g += 2;
+		cli->pri_dev_type = g;
+		g += 8;
 
-		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)
+		/* g at Number of Secondary Device Types */
+		len = *g++;
+		if (8 * len > cend - g)
 			return -1; /* invalid data */
-		cli->sec_dev_types = t;
-		t += 8 * cli->num_sec_dev_types;
+		cli->num_sec_dev_types = len;
+		cli->sec_dev_types = g;
+		g += 8 * len;
 
-		/* t at Device Name in WPS TLV format */
-		if (t + 2 + 2 > cend)
+		/* g at Device Name in WPS TLV format */
+		if (cend - g < 2 + 2)
 			return -1; /* invalid data */
-		if (WPA_GET_BE16(t) != ATTR_DEV_NAME)
+		if (WPA_GET_BE16(g) != ATTR_DEV_NAME)
 			return -1; /* invalid Device Name TLV */
-		t += 2;
-		count = WPA_GET_BE16(t);
-		t += 2;
-		if (count > cend - t)
+		g += 2;
+		count = WPA_GET_BE16(g);
+		g += 2;
+		if (count > cend - g)
 			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 = (const char *) g;
 		cli->dev_name_len = count;
 
 		g = cend;
diff --git a/src/p2p/p2p_pd.c b/src/p2p/p2p_pd.c
index 86558f7..93a0535 100644
--- a/src/p2p/p2p_pd.c
+++ b/src/p2p/p2p_pd.c
@@ -40,24 +40,38 @@
 }
 
 
-static void p2ps_add_new_group_info(struct p2p_data *p2p, struct wpabuf *buf)
+static void p2ps_add_new_group_info(struct p2p_data *p2p,
+				    struct p2p_device *dev,
+				    struct wpabuf *buf)
 {
 	int found;
 	u8 intended_addr[ETH_ALEN];
 	u8 ssid[SSID_MAX_LEN];
 	size_t ssid_len;
 	int group_iface;
+	unsigned int force_freq;
 
 	if (!p2p->cfg->get_go_info)
 		return;
 
 	found = p2p->cfg->get_go_info(
 		p2p->cfg->cb_ctx, intended_addr, ssid,
-		&ssid_len, &group_iface);
+		&ssid_len, &group_iface, &force_freq);
 	if (found) {
+		if (force_freq > 0) {
+			p2p->p2ps_prov->force_freq = force_freq;
+			p2p->p2ps_prov->pref_freq = 0;
+
+			if (dev)
+				p2p_prepare_channel(p2p, dev, force_freq, 0, 0);
+		}
 		p2p_buf_add_group_id(buf, p2p->cfg->dev_addr,
 				     ssid, ssid_len);
-		p2p_buf_add_intended_addr(buf, intended_addr);
+
+		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);
@@ -82,76 +96,82 @@
 				  struct wpabuf *buf, u16 config_methods)
 {
 	struct p2ps_provision *prov = p2p->p2ps_prov;
-	u8 feat_cap_mask[] = { 1, 0 };
+	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];
+	int follow_on_req_fail = prov->status >= 0 &&
+		prov->status != P2P_SC_SUCCESS_DEFERRED;
 
 	/* 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 (!follow_on_req_fail &&
+	    (prov->conncap & (P2PS_SETUP_GROUP_OWNER | P2PS_SETUP_NEW)))
+		p2ps_add_new_group_info(p2p, dev, 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);
-	}
+	if (!follow_on_req_fail) {
+		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;
+		if (shared_group ||
+		    (prov->conncap & (P2PS_SETUP_CLIENT | P2PS_SETUP_NEW)))
+			p2p_buf_add_channel_list(buf, p2p->cfg->country,
+						 &p2p->channels);
 
-		p2p_go_select_channel(p2p, dev, &tmp);
-
-		if (p2p->op_reg_class && p2p->op_channel)
+		if ((shared_group && !is_zero_ether_addr(intended_addr)) ||
+		    (prov->conncap & (P2PS_SETUP_GROUP_OWNER | P2PS_SETUP_NEW)))
 			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])
+	if (prov->status < 0 && prov->info[0])
 		p2p_buf_add_session_info(buf, prov->info);
 
-	p2p_buf_add_connection_capability(buf, prov->conncap);
+	if (!follow_on_req_fail)
+		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);
-	}
+	if (!follow_on_req_fail) {
+		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_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(feat_cap_mask),
-				       feat_cap_mask);
+	p2p_buf_add_feature_capability(buf, sizeof(fcap), (const u8 *) &fcap);
 
-	if (shared_group)
+	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);
+	}
 }
 
 
@@ -232,7 +252,9 @@
 						const u8 *group_id,
 						size_t group_id_len,
 						const u8 *persist_ssid,
-						size_t persist_ssid_len)
+						size_t persist_ssid_len,
+						const u8 *fcap,
+						u16 fcap_len)
 {
 	struct wpabuf *buf;
 	size_t extra = 0;
@@ -270,10 +292,14 @@
 
 	/* Add P2P IE for P2PS */
 	if (p2p->p2ps_prov && p2p->p2ps_prov->adv_id == adv_id) {
-		u8 feat_cap_mask[] = { 1, 0 };
 		u8 *len = p2p_buf_add_ie_hdr(buf);
 		struct p2ps_provision *prov = p2p->p2ps_prov;
 		u8 group_capab;
+		u8 conncap = 0;
+
+		if (status == P2P_SC_SUCCESS ||
+		    status == P2P_SC_SUCCESS_DEFERRED)
+			conncap = prov->conncap;
 
 		if (!status && prov->status != -1)
 			status = prov->status;
@@ -290,33 +316,33 @@
 				       group_capab);
 		p2p_buf_add_device_info(buf, p2p, NULL);
 
-		if (persist_ssid && p2p->cfg->get_persistent_group &&
+		if (persist_ssid && p2p->cfg->get_persistent_group && dev &&
 		    (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);
-			if (persist)
+				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);
+		if (!persist && (conncap & P2PS_SETUP_GROUP_OWNER))
+			p2ps_add_new_group_info(p2p, dev, 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 (persist || (conncap & P2PS_SETUP_GROUP_OWNER)) {
 			if (p2p->op_reg_class && p2p->op_channel)
 				p2p_buf_add_operating_channel(
 					buf, p2p->cfg->country,
@@ -329,23 +355,25 @@
 					p2p->cfg->op_channel);
 		}
 
-		p2p_buf_add_channel_list(buf, p2p->cfg->country,
-					 &p2p->cfg->channels);
+		if (persist ||
+		    (conncap & (P2PS_SETUP_CLIENT | P2PS_SETUP_GROUP_OWNER)))
+			p2p_buf_add_channel_list(buf, p2p->cfg->country,
+						 &p2p->channels);
 
-		if (!persist && (status == P2P_SC_SUCCESS ||
-				 status == P2P_SC_SUCCESS_DEFERRED))
-			p2p_buf_add_connection_capability(buf, prov->conncap);
+		if (!persist && conncap)
+			p2p_buf_add_connection_capability(buf, conncap);
 
 		p2p_buf_add_advertisement_id(buf, adv_id, prov->adv_mac);
 
-		p2p_buf_add_config_timeout(buf, p2p->go_timeout,
-					   p2p->client_timeout);
+		if (persist ||
+		    (conncap & (P2PS_SETUP_CLIENT | P2PS_SETUP_GROUP_OWNER)))
+			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, sizeof(feat_cap_mask),
-					       feat_cap_mask);
+		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);
@@ -400,6 +428,129 @@
 }
 
 
+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;
+}
+
+
+/* Check if the message contains a valid P2PS PD Request */
+static int p2ps_validate_pd_req(struct p2p_data *p2p, struct p2p_message *msg,
+				const u8 *addr)
+{
+	u8 group_id = 0;
+	u8 intended_addr = 0;
+	u8 operating_channel = 0;
+	u8 channel_list = 0;
+	u8 config_timeout = 0;
+	u8 listen_channel = 0;
+
+#define P2PS_PD_REQ_CHECK(_val, _attr) \
+do { \
+	if ((_val) && !msg->_attr) { \
+		p2p_dbg(p2p, "Not P2PS PD Request. Missing %s", #_attr); \
+		return -1; \
+	} \
+} while (0)
+
+	P2PS_PD_REQ_CHECK(1, adv_id);
+	P2PS_PD_REQ_CHECK(1, session_id);
+	P2PS_PD_REQ_CHECK(1, session_mac);
+	P2PS_PD_REQ_CHECK(1, adv_mac);
+	P2PS_PD_REQ_CHECK(1, capability);
+	P2PS_PD_REQ_CHECK(1, p2p_device_info);
+	P2PS_PD_REQ_CHECK(1, feature_cap);
+
+	/*
+	 * We don't need to check Connection Capability, Persistent Group,
+	 * and related attributes for follow-on PD Request with a status
+	 * other than SUCCESS_DEFERRED.
+	 */
+	if (msg->status && *msg->status != P2P_SC_SUCCESS_DEFERRED)
+		return 0;
+
+	P2PS_PD_REQ_CHECK(1, conn_cap);
+
+	/*
+	 * Note 1: A feature capability attribute structure can be changed
+	 * in the future. The assumption is that such modifications are
+	 * backward compatible, therefore we allow processing of msg.feature_cap
+	 * exceeding the size of the p2ps_feature_capab structure.
+	 * Note 2: Verification 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->feature_cap_len < sizeof(struct p2ps_feature_capab)) {
+		p2p_dbg(p2p, "P2PS: Invalid feature capability len");
+		return -1;
+	}
+
+	switch (*msg->conn_cap) {
+	case P2PS_SETUP_NEW:
+		group_id = 1;
+		intended_addr = 1;
+		operating_channel = 1;
+		channel_list = 1;
+		config_timeout = 1;
+		listen_channel = 1;
+		break;
+	case P2PS_SETUP_CLIENT:
+		channel_list = 1;
+		listen_channel = 1;
+		break;
+	case P2PS_SETUP_GROUP_OWNER:
+		group_id = 1;
+		intended_addr = 1;
+		operating_channel = 1;
+		break;
+	case P2PS_SETUP_NEW | P2PS_SETUP_GROUP_OWNER:
+		group_id = 1;
+		operating_channel = 1;
+		intended_addr = 1;
+		channel_list = 1;
+		config_timeout = 1;
+		break;
+	case P2PS_SETUP_CLIENT | P2PS_SETUP_GROUP_OWNER:
+		group_id = 1;
+		intended_addr = 1;
+		operating_channel = 1;
+		channel_list = 1;
+		config_timeout = 1;
+		break;
+	default:
+		p2p_dbg(p2p, "Invalid P2PS PD connection capability");
+		return -1;
+	}
+
+	if (msg->persistent_dev) {
+		channel_list = 1;
+		config_timeout = 1;
+		if (os_memcmp(msg->persistent_dev, addr, ETH_ALEN) == 0) {
+			intended_addr = 1;
+			operating_channel = 1;
+		}
+	}
+
+	P2PS_PD_REQ_CHECK(group_id, group_id);
+	P2PS_PD_REQ_CHECK(intended_addr, intended_addr);
+	P2PS_PD_REQ_CHECK(operating_channel, operating_channel);
+	P2PS_PD_REQ_CHECK(channel_list, channel_list);
+	P2PS_PD_REQ_CHECK(config_timeout, config_timeout);
+	P2PS_PD_REQ_CHECK(listen_channel, listen_channel);
+
+#undef P2PS_PD_REQ_CHECK
+
+	return 0;
+}
+
+
 void p2p_process_prov_disc_req(struct p2p_data *p2p, const u8 *sa,
 			       const u8 *data, size_t len, int rx_freq)
 {
@@ -413,11 +564,16 @@
 	u8 conncap = P2PS_SETUP_NEW;
 	u8 auto_accept = 0;
 	u32 session_id = 0;
-	u8 session_mac[ETH_ALEN];
-	u8 adv_mac[ETH_ALEN];
-	u8 group_mac[ETH_ALEN];
+	u8 session_mac[ETH_ALEN] = { 0, 0, 0, 0, 0, 0 };
+	u8 adv_mac[ETH_ALEN] = { 0, 0, 0, 0, 0, 0 };
+	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 = NULL;
+	u8 remote_conncap;
+	u16 method;
 
 	if (p2p_parse(data, len, &msg))
 		return;
@@ -425,6 +581,7 @@
 	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)) {
@@ -435,256 +592,431 @@
 				   0)) {
 			p2p_dbg(p2p, "Provision Discovery Request add device failed "
 				MACSTR, MAC2STR(sa));
+			goto out;
+		}
+
+		if (!dev) {
+			dev = p2p_get_device(p2p, sa);
+			if (!dev) {
+				p2p_dbg(p2p,
+					"Provision Discovery device not found "
+					MACSTR, MAC2STR(sa));
+				goto out;
+			}
 		}
 	} else if (msg.wfd_subelems) {
 		wpabuf_free(dev->info.wfd_subelems);
 		dev->info.wfd_subelems = wpabuf_dup(msg.wfd_subelems);
 	}
 
-	if (!(msg.wps_config_methods &
-	      (WPS_CONFIG_DISPLAY | WPS_CONFIG_KEYPAD |
-	       WPS_CONFIG_PUSHBUTTON | WPS_CONFIG_P2PS))) {
-		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");
+	if (!msg.adv_id) {
+		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;
 		}
-	}
 
-	if (dev) {
-		dev->flags &= ~(P2P_DEV_PD_PEER_DISPLAY |
-				P2P_DEV_PD_PEER_KEYPAD |
-				P2P_DEV_PD_PEER_P2PS);
+		/* Legacy (non-P2PS) - Unknown groups allowed for P2PS */
+		if (msg.group_id) {
+			size_t i;
 
-		/* 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);
+			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 (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;
-	}
+	} else {
+		allowed_config_methods |= WPS_CONFIG_P2PS;
 
-	reject = P2P_SC_SUCCESS;
+		/*
+		 * Set adv_id here, so in case of an error, a P2PS PD Response
+		 * will be sent.
+		 */
+		adv_id = WPA_GET_LE32(msg.adv_id);
+		if (p2ps_validate_pd_req(p2p, &msg, sa) < 0) {
+			reject = P2P_SC_FAIL_INVALID_PARAMS;
+			goto out;
+		}
 
-	os_memset(session_mac, 0, ETH_ALEN);
-	os_memset(adv_mac, 0, ETH_ALEN);
-	os_memset(group_mac, 0, ETH_ALEN);
-
-	if (msg.adv_id && msg.session_id && msg.session_mac && msg.adv_mac &&
-	    (msg.status || msg.conn_cap)) {
-		u8 remote_conncap;
-
-		if (msg.intended_addr)
-			os_memcpy(group_mac, msg.intended_addr, ETH_ALEN);
+		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);
+		/*
+		 * We need to verify a P2PS config methog in an initial PD
+		 * request or in a follow-on PD request with the status
+		 * SUCCESS_DEFERRED.
+		 */
+		if ((!msg.status || *msg.status == P2P_SC_SUCCESS_DEFERRED) &&
+		    !(msg.wps_config_methods & allowed_config_methods)) {
+			p2p_dbg(p2p,
+				"Unsupported Config Methods in Provision Discovery Request");
+			goto out;
+		}
 
-			p2p_dbg(p2p, "Conncap: local:%d remote:%d result:%d",
-				auto_accept, remote_conncap, conncap);
+		/*
+		 * TODO: since we don't support multiple PD, reject PD request
+		 * if we are in the middle of P2PS PD with some other peer
+		 */
+	}
 
-			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;
-			}
+	dev->flags &= ~(P2P_DEV_PD_PEER_DISPLAY |
+			P2P_DEV_PD_PEER_KEYPAD |
+			P2P_DEV_PD_PEER_P2PS);
 
-			if (msg.wps_config_methods & WPS_CONFIG_KEYPAD) {
-				p2p_dbg(p2p, "Keypad - always defer");
-				auto_accept = 0;
-			}
+	if (msg.wps_config_methods & WPS_CONFIG_DISPLAY) {
+		p2p_dbg(p2p, "Peer " MACSTR
+			" requested us to show a PIN on display", MAC2STR(sa));
+		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));
+		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));
+		dev->flags |= P2P_DEV_PD_PEER_P2PS;
+		passwd_id = DEV_PW_P2PS_DEFAULT;
+	}
 
-			if (auto_accept || reject != P2P_SC_SUCCESS) {
-				struct p2ps_provision *tmp;
+	/* 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 (reject == P2P_SC_SUCCESS && !conncap) {
-					reject =
-						P2P_SC_FAIL_INCOMPATIBLE_PARAMS;
-				}
+	reject = P2P_SC_SUCCESS;
 
-				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;
-				}
+	/*
+	 * End of a legacy P2P PD Request processing, from this point continue
+	 * with P2PS one.
+	 */
+	if (!msg.adv_id)
+		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;
-				}
+	remote_conncap = conncap;
 
-				if (reject != P2P_SC_SUCCESS)
-					goto out;
-			}
-		} else if (!msg.status) {
+	if (!msg.status) {
+		unsigned int forced_freq, pref_freq;
+
+		if (os_memcmp(p2p->cfg->dev_addr, msg.adv_mac, ETH_ALEN)) {
+			p2p_dbg(p2p,
+				"P2PS PD adv mac does not match the local one");
 			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;
+		p2ps_adv = p2p_service_p2ps_id(p2p, adv_id);
+		if (!p2ps_adv) {
+			p2p_dbg(p2p, "P2PS PD invalid adv_id=0x%X", adv_id);
+			reject = P2P_SC_FAIL_INCOMPATIBLE_PARAMS;
+			goto out;
+		}
+		p2p_dbg(p2p, "adv_id: 0x%X, p2ps_adv: %p", adv_id, p2ps_adv);
 
-			if (!conncap) {
-				reject = P2P_SC_FAIL_INCOMPATIBLE_PARAMS;
-				goto out;
+		auto_accept = p2ps_adv->auto_accept;
+		conncap = p2p->cfg->p2ps_group_capability(p2p->cfg->cb_ctx,
+							  conncap, auto_accept,
+							  &forced_freq,
+							  &pref_freq);
+
+		p2p_dbg(p2p, "Conncap: local:%d remote:%d result:%d",
+			auto_accept, remote_conncap, conncap);
+
+		p2p_prepare_channel(p2p, dev, forced_freq, pref_freq, 0);
+
+		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 ((remote_conncap & (P2PS_SETUP_NEW | P2PS_SETUP_CLIENT) ||
+		     msg.persistent_dev) && conncap != P2PS_SETUP_NEW &&
+		    msg.channel_list && msg.channel_list_len &&
+		    p2p_peer_channels_check(p2p, &p2p->channels, dev,
+					    msg.channel_list,
+					    msg.channel_list_len) < 0) {
+			p2p_dbg(p2p,
+				"No common channels - force deferred flow");
+			auto_accept = 0;
+		}
+
+		if (((remote_conncap & P2PS_SETUP_GROUP_OWNER) ||
+		     msg.persistent_dev) && msg.operating_channel) {
+			struct p2p_channels intersect;
+
+			/*
+			 * There are cases where only the operating channel is
+			 * provided. This requires saving the channel as the
+			 * supported channel list, and verifying that it is
+			 * supported.
+			 */
+			if (dev->channels.reg_classes == 0 ||
+			    !p2p_channels_includes(&dev->channels,
+						   msg.operating_channel[3],
+						   msg.operating_channel[4])) {
+				struct p2p_channels *ch = &dev->channels;
+
+				os_memset(ch, 0, sizeof(*ch));
+				ch->reg_class[0].reg_class =
+					msg.operating_channel[3];
+				ch->reg_class[0].channel[0] =
+					msg.operating_channel[4];
+				ch->reg_class[0].channels = 1;
+				ch->reg_classes = 1;
 			}
 
+			p2p_channels_intersect(&p2p->channels, &dev->channels,
+					       &intersect);
+
+			if (intersect.reg_classes == 0) {
+				p2p_dbg(p2p,
+					"No common channels - force deferred flow");
+				auto_accept = 0;
+			}
+		}
+
+		if (auto_accept || reject != P2P_SC_SUCCESS) {
+			struct p2ps_provision *tmp;
+
 			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);
-
-				/*
-				 * 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;
-
-				/* Reject this "Deferred Accept* if incompatible
-				 * conncap or method */
-				if (!conncap ||
-				    !(msg.wps_config_methods & method))
-					reject =
-						P2P_SC_FAIL_INCOMPATIBLE_PARAMS;
-				else
-					reject = P2P_SC_SUCCESS;
-
-				p2p->p2ps_prov->status = reject;
-				p2p->p2ps_prov->conncap = conncap;
+			tmp->force_freq = forced_freq;
+			tmp->pref_freq = pref_freq;
+			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;
 		}
 	}
 
+	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;
+	}
+
+	/* Not a P2PS Follow-on PD */
+	if (!msg.status)
+		goto out;
+
+	if (*msg.status && *msg.status != P2P_SC_SUCCESS_DEFERRED) {
+		reject = *msg.status;
+		goto out;
+	}
+
+	if (*msg.status != P2P_SC_SUCCESS_DEFERRED || !p2p->p2ps_prov)
+		goto out;
+
+	if (p2p->p2ps_prov->adv_id != adv_id ||
+	    os_memcmp(p2p->p2ps_prov->adv_mac, msg.adv_mac, ETH_ALEN)) {
+		p2p_dbg(p2p,
+			"P2PS Follow-on PD with mismatch Advertisement ID/MAC");
+		goto out;
+	}
+
+	if (p2p->p2ps_prov->session_id != session_id ||
+	    os_memcmp(p2p->p2ps_prov->session_mac, msg.session_mac, ETH_ALEN)) {
+		p2p_dbg(p2p, "P2PS Follow-on PD with mismatch Session ID/MAC");
+		goto out;
+	}
+
+	method = p2p->p2ps_prov->method;
+
+	conncap = p2p->cfg->p2ps_group_capability(p2p->cfg->cb_ctx,
+						  remote_conncap,
+						  p2p->p2ps_prov->conncap,
+						  &p2p->p2ps_prov->force_freq,
+						  &p2p->p2ps_prov->pref_freq);
+
+	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);
+
+	p2p_prepare_channel(p2p, dev, p2p->p2ps_prov->force_freq,
+			    p2p->p2ps_prov->pref_freq, 0);
+
+	/*
+	 * 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 if ((remote_conncap & (P2PS_SETUP_NEW | P2PS_SETUP_CLIENT) ||
+		    msg.persistent_dev) && conncap != P2PS_SETUP_NEW &&
+		   msg.channel_list && msg.channel_list_len &&
+		   p2p_peer_channels_check(p2p, &p2p->channels, dev,
+					   msg.channel_list,
+					   msg.channel_list_len) < 0) {
+		p2p_dbg(p2p,
+			"No common channels in Follow-On Provision Discovery Request");
+		reject = P2P_SC_FAIL_NO_COMMON_CHANNELS;
+	} else {
+		reject = P2P_SC_SUCCESS;
+	}
+
+	dev->oper_freq = 0;
+	if (reject == P2P_SC_SUCCESS || reject == P2P_SC_SUCCESS_DEFERRED) {
+		u8 tmp;
+
+		if (msg.operating_channel)
+			dev->oper_freq =
+				p2p_channel_to_freq(msg.operating_channel[3],
+						    msg.operating_channel[4]);
+
+		if ((conncap & P2PS_SETUP_GROUP_OWNER) &&
+		    p2p_go_select_channel(p2p, dev, &tmp) < 0)
+			reject = P2P_SC_FAIL_NO_COMMON_CHANNELS;
+	}
+
+	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);
-	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);
+	/*
+	 * Send PD Response for an initial PD Request or for follow-on
+	 * PD Request with P2P_SC_SUCCESS_DEFERRED status.
+	 */
+	if (!msg.status || *msg.status == P2P_SC_SUCCESS_DEFERRED) {
+		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) {
+			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),
+				    50) < 0)
+			p2p_dbg(p2p, "Failed to send Action frame");
+		else
+			p2p->send_action_in_progress = 1;
+
+		wpabuf_free(resp);
+	}
+
+	if (!dev) {
+		p2p_parse_free(&msg);
+		return;
+	}
+
+	freq = 0;
+	if (reject == P2P_SC_SUCCESS && conncap == P2PS_SETUP_GROUP_OWNER) {
+		freq = p2p_channel_to_freq(p2p->op_reg_class,
+					   p2p->op_channel);
+		if (freq < 0)
+			freq = 0;
+	}
 
 	if (!p2p->cfg->p2ps_prov_complete) {
 		/* Don't emit anything */
@@ -696,7 +1028,8 @@
 					     NULL, adv_id, session_id,
 					     0, 0, msg.persistent_ssid,
 					     msg.persistent_ssid_len,
-					     0, 0, NULL);
+					     0, 0, NULL, NULL, 0, freq,
+					     NULL, 0);
 	} else if (msg.status && *msg.status == P2P_SC_SUCCESS_DEFERRED &&
 		   p2p->p2ps_prov) {
 		p2p->p2ps_prov->status = reject;
@@ -709,7 +1042,8 @@
 						     session_id, conncap, 0,
 						     msg.persistent_ssid,
 						     msg.persistent_ssid_len, 0,
-						     0, NULL);
+						     0, NULL, NULL, 0, freq,
+						     NULL, 0);
 		else
 			p2p->cfg->p2ps_prov_complete(p2p->cfg->cb_ctx,
 						     *msg.status,
@@ -719,7 +1053,10 @@
 						     passwd_id,
 						     msg.persistent_ssid,
 						     msg.persistent_ssid_len, 0,
-						     0, NULL);
+						     0, NULL,
+						     (const u8 *) &resp_fcap,
+						     sizeof(resp_fcap), freq,
+						     NULL, 0);
 	} 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,
@@ -728,7 +1065,9 @@
 					     passwd_id,
 					     msg.persistent_ssid,
 					     msg.persistent_ssid_len,
-					     0, 0, NULL);
+					     0, 0, NULL,
+					     (const u8 *) &resp_fcap,
+					     sizeof(resp_fcap), freq, NULL, 0);
 	} else if (msg.status) {
 	} else if (auto_accept && reject == P2P_SC_SUCCESS) {
 		p2p->cfg->p2ps_prov_complete(p2p->cfg->cb_ctx, P2P_SC_SUCCESS,
@@ -737,7 +1076,13 @@
 					     conncap, passwd_id,
 					     msg.persistent_ssid,
 					     msg.persistent_ssid_len,
-					     0, 0, NULL);
+					     0, 0, NULL,
+					     (const u8 *) &resp_fcap,
+					     sizeof(resp_fcap), freq,
+					     msg.group_id ?
+					     msg.group_id + ETH_ALEN : NULL,
+					     msg.group_id ?
+					     msg.group_id_len - ETH_ALEN : 0);
 	} else if (reject == P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE &&
 		   (!msg.session_info || !msg.session_info_len)) {
 		p2p->p2ps_prov->method = msg.wps_config_methods;
@@ -748,7 +1093,9 @@
 					     conncap, passwd_id,
 					     msg.persistent_ssid,
 					     msg.persistent_ssid_len,
-					     0, 1, NULL);
+					     0, 1, NULL,
+					     (const u8 *) &resp_fcap,
+					     sizeof(resp_fcap), freq, NULL, 0);
 	} 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);
@@ -764,14 +1111,46 @@
 				adv_mac, session_mac, group_mac, adv_id,
 				session_id, conncap, passwd_id,
 				msg.persistent_ssid, msg.persistent_ssid_len,
-				0, 1, buf);
+				0, 1, buf,
+				(const u8 *) &resp_fcap, sizeof(resp_fcap),
+				freq, NULL, 0);
 
 			os_free(buf);
 		}
 	}
 
-	if (reject == P2P_SC_SUCCESS && p2p->cfg->prov_disc_req) {
+	/*
+	 * 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,
@@ -783,10 +1162,133 @@
 					0,
 					msg.group_id, msg.group_id_len);
 	}
+
+	if (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)
 {
@@ -794,24 +1296,26 @@
 	struct p2p_device *dev;
 	u16 report_config_methods = 0, req_config_methods;
 	u8 status = P2P_SC_SUCCESS;
-	int success = 0;
 	u32 adv_id = 0;
 	u8 conncap = P2PS_SETUP_NEW;
 	u8 adv_mac[ETH_ALEN];
-	u8 group_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;
 
-	if (msg.intended_addr)
-		os_memcpy(group_mac, msg.intended_addr, ETH_ALEN);
-	else
-		os_memset(group_mac, 0, ETH_ALEN);
+	group_mac = msg.intended_addr;
 
 	if (msg.adv_mac)
 		os_memcpy(adv_mac, msg.adv_mac, ETH_ALEN);
@@ -845,6 +1349,9 @@
 			" with no pending request", MAC2STR(sa));
 		p2p_parse_free(&msg);
 		return;
+	} else if (msg.wfd_subelems) {
+		wpabuf_free(dev->info.wfd_subelems);
+		dev->info.wfd_subelems = wpabuf_dup(msg.wfd_subelems);
 	}
 
 	if (dev->dialog_token != msg.dialog_token) {
@@ -859,6 +1366,8 @@
 		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.
@@ -907,30 +1416,82 @@
 		passwd_id = DEV_PW_P2PS_DEFAULT;
 	}
 
-	if ((msg.conn_cap || msg.persistent_dev) &&
-	    msg.adv_id &&
-	    (status == P2P_SC_SUCCESS || status == P2P_SC_SUCCESS_DEFERRED) &&
+	if ((status == P2P_SC_SUCCESS || status == P2P_SC_SUCCESS_DEFERRED) &&
 	    p2p->p2ps_prov) {
+		dev->oper_freq = 0;
+
+		/*
+		 * Save the reported channel list and operating frequency.
+		 * Note that the specification mandates that the responder
+		 * should include in the channel list only channels reported by
+		 * the initiator, so this is only a sanity check, and if this
+		 * fails the flow would continue, although it would probably
+		 * fail. Same is true for the operating channel.
+		 */
+		if (msg.channel_list && msg.channel_list_len &&
+		    p2p_peer_channels_check(p2p, &p2p->channels, dev,
+					    msg.channel_list,
+					    msg.channel_list_len) < 0)
+			p2p_dbg(p2p, "P2PS PD Response - no common channels");
+
+		if (msg.operating_channel) {
+			if (p2p_channels_includes(&p2p->channels,
+						  msg.operating_channel[3],
+						  msg.operating_channel[4]) &&
+			    p2p_channels_includes(&dev->channels,
+						  msg.operating_channel[3],
+						  msg.operating_channel[4])) {
+				dev->oper_freq =
+					p2p_channel_to_freq(
+						msg.operating_channel[3],
+						msg.operating_channel[4]);
+			} else {
+				p2p_dbg(p2p,
+					"P2PS PD Response - invalid operating channel");
+			}
+		}
+
 		if (p2p->cfg->p2ps_prov_complete) {
+			int freq = 0;
+
+			if (conncap == P2PS_SETUP_GROUP_OWNER) {
+				u8 tmp;
+
+				/*
+				 * Re-select the operating channel as it is
+				 * possible that original channel is no longer
+				 * valid. This should not really fail.
+				 */
+				if (p2p_go_select_channel(p2p, dev, &tmp) < 0)
+					p2p_dbg(p2p,
+						"P2PS PD channel selection failed");
+
+				freq = p2p_channel_to_freq(p2p->op_reg_class,
+							   p2p->op_channel);
+				if (freq < 0)
+					freq = 0;
+			}
+
 			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.persistent_ssid_len, 1, 0, NULL,
+				msg.feature_cap, msg.feature_cap_len, freq,
+				msg.group_id ? msg.group_id + ETH_ALEN : NULL,
+				msg.group_id ? msg.group_id_len - ETH_ALEN : 0);
 		}
 		p2ps_prov_free(p2p);
-	}
-
-	if (status != P2P_SC_SUCCESS &&
-	    status != P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE &&
-	    status != P2P_SC_SUCCESS_DEFERRED && p2p->p2ps_prov) {
+	} 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);
+				0, 0, NULL, 0, 1, 0, NULL, NULL, 0, 0, NULL, 0);
 		p2ps_prov_free(p2p);
 	}
 
@@ -966,13 +1527,12 @@
 					p2p->cfg->cb_ctx, sa,
 					P2P_PROV_DISC_INFO_UNAVAILABLE,
 					adv_id, adv_mac, NULL);
-	} else if (msg.wps_config_methods != dev->req_config_methods ||
-		   status != P2P_SC_SUCCESS) {
+	} 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, 0,
-						 NULL, NULL);
+						 P2P_PROV_DISC_REJECTED,
+						 adv_id, adv_mac, NULL);
 		p2p_parse_free(&msg);
 		p2ps_prov_free(p2p);
 		goto out;
@@ -980,9 +1540,10 @@
 
 	/* 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);
-	success = 1;
 
 out:
 	dev->req_config_methods = 0;
@@ -994,7 +1555,28 @@
 		p2p_connect_send(p2p, dev);
 		return;
 	}
-	if (success && p2p->cfg->prov_disc_resp)
+
+	/*
+	 * 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);
 
@@ -1058,6 +1640,10 @@
 			"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);
+
+		if (p2p_prepare_channel(p2p, dev, p2p->p2ps_prov->force_freq,
+					p2p->p2ps_prov->pref_freq, 1) < 0)
+			return -1;
 	}
 
 	req = p2p_build_prov_disc_req(p2p, dev, join);
diff --git a/src/p2p/p2p_sd.c b/src/p2p/p2p_sd.c
index 1a2af04..a8bc5ba 100644
--- a/src/p2p/p2p_sd.c
+++ b/src/p2p/p2p_sd.c
@@ -28,11 +28,11 @@
 	pos = wpabuf_head(wfd);
 	end = pos + wpabuf_len(wfd);
 
-	while (pos + 3 <= end) {
+	while (end - pos >= 3) {
 		subelem = *pos++;
 		len = WPA_GET_BE16(pos);
 		pos += 2;
-		if (pos + len > end)
+		if (len > end - pos)
 			break;
 
 		if (subelem == WFD_SUBELEM_DEVICE_INFO && len >= 6) {
@@ -288,6 +288,14 @@
 	query = p2p_pending_sd_req(p2p, dev);
 	if (query == NULL)
 		return -1;
+	if (p2p->state == P2P_SEARCH &&
+	    os_memcmp(p2p->sd_query_no_ack, dev->info.p2p_device_addr,
+		      ETH_ALEN) == 0) {
+		p2p_dbg(p2p, "Do not start Service Discovery with " MACSTR
+			" due to it being the first no-ACK peer in this search iteration",
+			MAC2STR(dev->info.p2p_device_addr));
+		return -2;
+	}
 
 	p2p_dbg(p2p, "Start Service Discovery with " MACSTR,
 		MAC2STR(dev->info.p2p_device_addr));
@@ -355,11 +363,11 @@
 	pos++;
 
 	slen = *pos++;
-	next = pos + slen;
-	if (next > end || slen < 2) {
+	if (slen > end - pos || slen < 2) {
 		p2p_dbg(p2p, "Invalid IE in GAS Initial Request");
 		return;
 	}
+	next = pos + slen;
 	pos++; /* skip QueryRespLenLimit and PAME-BI */
 
 	if (*pos != ACCESS_NETWORK_QUERY_PROTOCOL) {
@@ -370,16 +378,16 @@
 
 	pos = next;
 	/* Query Request */
-	if (pos + 2 > end)
+	if (end - pos < 2)
 		return;
 	slen = WPA_GET_LE16(pos);
 	pos += 2;
-	if (pos + slen > end)
+	if (slen > end - pos)
 		return;
 	end = pos + slen;
 
 	/* ANQP Query Request */
-	if (pos + 4 > end)
+	if (end - pos < 4)
 		return;
 	if (WPA_GET_LE16(pos) != ANQP_VENDOR_SPECIFIC) {
 		p2p_dbg(p2p, "Unsupported ANQP Info ID %u", WPA_GET_LE16(pos));
@@ -389,7 +397,7 @@
 
 	slen = WPA_GET_LE16(pos);
 	pos += 2;
-	if (pos + slen > end || slen < 3 + 1) {
+	if (slen > end - pos || slen < 3 + 1) {
 		p2p_dbg(p2p, "Invalid ANQP Query Request length");
 		return;
 	}
@@ -401,7 +409,7 @@
 	}
 	pos += 4;
 
-	if (pos + 2 > end)
+	if (end - pos < 2)
 		return;
 	update_indic = WPA_GET_LE16(pos);
 	p2p_dbg(p2p, "Service Update Indicator: %u", update_indic);
@@ -417,9 +425,16 @@
 		     u8 dialog_token, const struct wpabuf *resp_tlvs)
 {
 	struct wpabuf *resp;
+	size_t max_len;
+
+	/*
+	 * In the 60 GHz, we have a smaller maximum frame length for management
+	 * frames.
+	 */
+	max_len = (freq > 56160) ? 928 : 1400;
 
 	/* TODO: fix the length limit to match with the maximum frame length */
-	if (wpabuf_len(resp_tlvs) > 1400) {
+	if (wpabuf_len(resp_tlvs) > max_len) {
 		p2p_dbg(p2p, "SD response long enough to require fragmentation");
 		if (p2p->sd_resp) {
 			/*
@@ -512,11 +527,11 @@
 	pos++;
 
 	slen = *pos++;
-	next = pos + slen;
-	if (next > end || slen < 2) {
+	if (slen > end - pos || slen < 2) {
 		p2p_dbg(p2p, "Invalid IE in GAS Initial Response");
 		return;
 	}
+	next = pos + slen;
 	pos++; /* skip QueryRespLenLimit and PAME-BI */
 
 	if (*pos != ACCESS_NETWORK_QUERY_PROTOCOL) {
@@ -527,14 +542,14 @@
 
 	pos = next;
 	/* Query Response */
-	if (pos + 2 > end) {
+	if (end - pos < 2) {
 		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) {
+	if (slen > end - pos) {
 		p2p_dbg(p2p, "Not enough Query Response data");
 		return;
 	}
@@ -552,7 +567,7 @@
 	}
 
 	/* ANQP Query Response */
-	if (pos + 4 > end)
+	if (end - pos < 4)
 		return;
 	if (WPA_GET_LE16(pos) != ANQP_VENDOR_SPECIFIC) {
 		p2p_dbg(p2p, "Unsupported ANQP Info ID %u", WPA_GET_LE16(pos));
@@ -562,7 +577,7 @@
 
 	slen = WPA_GET_LE16(pos);
 	pos += 2;
-	if (pos + slen > end || slen < 3 + 1) {
+	if (slen > end - pos || slen < 3 + 1) {
 		p2p_dbg(p2p, "Invalid ANQP Query Response length");
 		return;
 	}
@@ -574,7 +589,7 @@
 	}
 	pos += 4;
 
-	if (pos + 2 > end)
+	if (end - pos < 2)
 		return;
 	update_indic = WPA_GET_LE16(pos);
 	p2p_dbg(p2p, "Service Update Indicator: %u", update_indic);
@@ -606,7 +621,7 @@
 {
 	struct wpabuf *resp;
 	u8 dialog_token;
-	size_t frag_len;
+	size_t frag_len, max_len;
 	int more = 0;
 
 	wpa_hexdump(MSG_DEBUG, "P2P: RX GAS Comeback Request", data, len);
@@ -630,9 +645,14 @@
 		return;
 	}
 
+	/*
+	 * In the 60 GHz, we have a smaller maximum frame length for management
+	 * frames.
+	 */
+	max_len = (rx_freq > 56160) ? 928 : 1400;
 	frag_len = wpabuf_len(p2p->sd_resp) - p2p->sd_resp_pos;
-	if (frag_len > 1400) {
-		frag_len = 1400;
+	if (frag_len > max_len) {
+		frag_len = max_len;
 		more = 1;
 	}
 	resp = p2p_build_gas_comeback_resp(dialog_token, WLAN_STATUS_SUCCESS,
@@ -727,11 +747,11 @@
 	pos++;
 
 	slen = *pos++;
-	next = pos + slen;
-	if (next > end || slen < 2) {
+	if (slen > end - pos || slen < 2) {
 		p2p_dbg(p2p, "Invalid IE in GAS Comeback Response");
 		return;
 	}
+	next = pos + slen;
 	pos++; /* skip QueryRespLenLimit and PAME-BI */
 
 	if (*pos != ACCESS_NETWORK_QUERY_PROTOCOL) {
@@ -742,14 +762,14 @@
 
 	pos = next;
 	/* Query Response */
-	if (pos + 2 > end) {
+	if (end - pos < 2) {
 		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) {
+	if (slen > end - pos) {
 		p2p_dbg(p2p, "Not enough Query Response data");
 		return;
 	}
@@ -768,7 +788,7 @@
 	}
 
 	/* ANQP Query Response */
-	if (pos + 4 > end)
+	if (end - pos < 4)
 		return;
 	if (WPA_GET_LE16(pos) != ANQP_VENDOR_SPECIFIC) {
 		p2p_dbg(p2p, "Unsupported ANQP Info ID %u", WPA_GET_LE16(pos));
@@ -783,7 +803,7 @@
 		p2p_dbg(p2p, "Invalid ANQP Query Response length");
 		return;
 	}
-	if (pos + 4 > end)
+	if (end - pos < 4)
 		return;
 
 	if (WPA_GET_BE32(pos) != P2P_IE_VENDOR_TYPE) {
@@ -793,7 +813,7 @@
 	}
 	pos += 4;
 
-	if (pos + 2 > end)
+	if (end - pos < 2)
 		return;
 	p2p->sd_rx_update_indic = WPA_GET_LE16(pos);
 	p2p_dbg(p2p, "Service Update Indicator: %u", p2p->sd_rx_update_indic);
diff --git a/src/p2p/p2p_utils.c b/src/p2p/p2p_utils.c
index eee3c5a..2e2aa8a 100644
--- a/src/p2p/p2p_utils.c
+++ b/src/p2p/p2p_utils.c
@@ -9,6 +9,7 @@
 #include "includes.h"
 
 #include "common.h"
+#include "common/defs.h"
 #include "common/ieee802_11_common.h"
 #include "p2p_i.h"
 
@@ -67,59 +68,11 @@
  */
 int p2p_freq_to_channel(unsigned int freq, u8 *op_class, u8 *channel)
 {
-	/* TODO: more operating classes */
-	if (freq >= 2412 && freq <= 2472) {
-		if ((freq - 2407) % 5)
-			return -1;
+	if (ieee80211_freq_to_channel_ext(freq, 0, 0, op_class, channel) ==
+	    NUM_HOSTAPD_MODES)
+		return -1;
 
-		*op_class = 81; /* 2.407 GHz, channels 1..13 */
-		*channel = (freq - 2407) / 5;
-		return 0;
-	}
-
-	if (freq == 2484) {
-		*op_class = 82; /* channel 14 */
-		*channel = 14;
-		return 0;
-	}
-
-	if (freq >= 5180 && freq <= 5240) {
-		if ((freq - 5000) % 5)
-			return -1;
-
-		*op_class = 115; /* 5 GHz, channels 36..48 */
-		*channel = (freq - 5000) / 5;
-		return 0;
-	}
-
-	if (freq >= 5745 && freq <= 5805) {
-		if ((freq - 5000) % 5)
-			return -1;
-
-		*op_class = 124; /* 5 GHz, channels 149..161 */
-		*channel = (freq - 5000) / 5;
-		return 0;
-	}
-
-	if (freq >= 5745 && freq <= 5845) {
-		if ((freq - 5000) % 5)
-			return -1;
-
-		*op_class = 125; /* 5 GHz, channels 149..169 */
-		*channel = (freq - 5000) / 5;
-		return 0;
-	}
-
-	if (freq >= 58320 && freq <= 64800) {
-		if ((freq - 58320) % 2160)
-			return -1;
-
-		*op_class = 180; /* 60 GHz, channels 1..4 */
-		*channel = (freq - 56160) / 2160;
-		return 0;
-	}
-
-	return -1;
+	return 0;
 }
 
 
@@ -506,12 +459,22 @@
 			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;
 		}
 	}
diff --git a/src/radius/radius.c b/src/radius/radius.c
index 1ebfd11..da978db 100644
--- a/src/radius/radius.c
+++ b/src/radius/radius.c
@@ -1,6 +1,6 @@
 /*
  * RADIUS message processing
- * Copyright (c) 2002-2009, 2011-2014, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2002-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.
@@ -173,6 +173,8 @@
 	{ 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_SERVICE_TYPE, "Service-Type", RADIUS_ATTR_INT32 },
+	{ RADIUS_ATTR_FRAMED_IP_ADDRESS, "Framed-IP-Address", RADIUS_ATTR_IP },
 	{ 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 },
@@ -214,6 +216,7 @@
 	  RADIUS_ATTR_INT32 },
 	{ RADIUS_ATTR_EVENT_TIMESTAMP, "Event-Timestamp",
 	  RADIUS_ATTR_INT32 },
+	{ RADIUS_ATTR_EGRESS_VLANID, "EGRESS-VLANID", RADIUS_ATTR_HEXDUMP },
 	{ 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",
@@ -703,7 +706,7 @@
 
 		attr = (struct radius_attr_hdr *) pos;
 
-		if (pos + attr->length > end || attr->length < sizeof(*attr))
+		if (attr->length > end - pos || attr->length < sizeof(*attr))
 			goto fail;
 
 		/* TODO: check that attr->length is suitable for attr->type */
@@ -892,25 +895,11 @@
 
 /* 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)
+ */
+int radius_msg_make_authenticator(struct radius_msg *msg)
 {
-	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);
+	return os_get_random((u8 *) &msg->hdr->authenticator,
+			     sizeof(msg->hdr->authenticator));
 }
 
 
@@ -1210,7 +1199,9 @@
 	vhdr = (struct radius_attr_vendor *) pos;
 	vhdr->vendor_type = RADIUS_VENDOR_ATTR_MS_MPPE_SEND_KEY;
 	pos = (u8 *) (vhdr + 1);
-	salt = os_random() | 0x8000;
+	if (os_get_random((u8 *) &salt, sizeof(salt)) < 0)
+		return 0;
+	salt |= 0x8000;
 	WPA_PUT_BE16(pos, salt);
 	pos += 2;
 	encrypt_ms_key(send_key, send_key_len, salt, req_authenticator, secret,
@@ -1422,12 +1413,30 @@
 };
 
 
+static int cmp_int(const void *a, const void *b)
+{
+	int x, y;
+
+	x = *((int *) a);
+	y = *((int *) b);
+	return (x - y);
+}
+
+
 /**
  * radius_msg_get_vlanid - Parse RADIUS attributes for VLAN tunnel information
+ * The k tagged vlans found are sorted by vlan_id and stored in the first k
+ * items of tagged.
+ *
  * @msg: RADIUS message
- * Returns: VLAN ID for the first tunnel configuration or 0 if none is found
+ * @untagged: Pointer to store untagged vid
+ * @numtagged: Size of tagged
+ * @tagged: Pointer to store tagged list
+ *
+ * Returns: 0 if neither tagged nor untagged configuration is found, 1 otherwise
  */
-int radius_msg_get_vlanid(struct radius_msg *msg)
+int radius_msg_get_vlanid(struct radius_msg *msg, int *untagged, int numtagged,
+			  int *tagged)
 {
 	struct radius_tunnel_attrs tunnel[RADIUS_TUNNEL_TAGS], *tun;
 	size_t i;
@@ -1435,8 +1444,12 @@
 	const u8 *data;
 	char buf[10];
 	size_t dlen;
+	int j, taggedidx = 0, vlan_id;
 
 	os_memset(&tunnel, 0, sizeof(tunnel));
+	for (j = 0; j < numtagged; j++)
+		tagged[j] = 0;
+	*untagged = 0;
 
 	for (i = 0; i < msg->attr_used; i++) {
 		attr = radius_get_attr_hdr(msg, i);
@@ -1473,21 +1486,44 @@
 				break;
 			os_memcpy(buf, data, dlen);
 			buf[dlen] = '\0';
+			vlan_id = atoi(buf);
+			if (vlan_id <= 0)
+				break;
 			tun->tag_used++;
-			tun->vlanid = atoi(buf);
+			tun->vlanid = vlan_id;
+			break;
+		case RADIUS_ATTR_EGRESS_VLANID: /* RFC 4675 */
+			if (attr->length != 6)
+				break;
+			vlan_id = WPA_GET_BE24(data + 1);
+			if (vlan_id <= 0)
+				break;
+			if (data[0] == 0x32)
+				*untagged = vlan_id;
+			else if (data[0] == 0x31 && tagged &&
+				 taggedidx < numtagged)
+				tagged[taggedidx++] = vlan_id;
 			break;
 		}
 	}
 
+	/* Use tunnel with the lowest tag for untagged VLAN id */
 	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;
+		    tun->vlanid > 0) {
+			*untagged = tun->vlanid;
+			break;
+		}
 	}
 
+	if (taggedidx)
+		qsort(tagged, taggedidx, sizeof(int), cmp_int);
+
+	if (*untagged > 0 || taggedidx)
+		return 1;
 	return 0;
 }
 
@@ -1669,3 +1705,14 @@
 
 	return 0;
 }
+
+
+int radius_gen_session_id(u8 *id, size_t len)
+{
+	/*
+	 * Acct-Session-Id and Acct-Multi-Session-Id should be globally and
+	 * temporarily unique. A high quality random number is required
+	 * therefore. This could be be improved by switching to a GUID.
+	 */
+	return os_get_random(id, len);
+}
diff --git a/src/radius/radius.h b/src/radius/radius.h
index 5977339..cba2b91 100644
--- a/src/radius/radius.h
+++ b/src/radius/radius.h
@@ -1,6 +1,6 @@
 /*
  * RADIUS message processing
- * Copyright (c) 2002-2009, 2012, 2014, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2002-2009, 2012, 2014-2015, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -52,6 +52,8 @@
        RADIUS_ATTR_USER_PASSWORD = 2,
        RADIUS_ATTR_NAS_IP_ADDRESS = 4,
        RADIUS_ATTR_NAS_PORT = 5,
+       RADIUS_ATTR_SERVICE_TYPE = 6,
+       RADIUS_ATTR_FRAMED_IP_ADDRESS = 8,
        RADIUS_ATTR_FRAMED_MTU = 12,
        RADIUS_ATTR_REPLY_MESSAGE = 18,
        RADIUS_ATTR_STATE = 24,
@@ -79,6 +81,7 @@
        RADIUS_ATTR_ACCT_INPUT_GIGAWORDS = 52,
        RADIUS_ATTR_ACCT_OUTPUT_GIGAWORDS = 53,
        RADIUS_ATTR_EVENT_TIMESTAMP = 55,
+       RADIUS_ATTR_EGRESS_VLANID = 56,
        RADIUS_ATTR_NAS_PORT_TYPE = 61,
        RADIUS_ATTR_TUNNEL_TYPE = 64,
        RADIUS_ATTR_TUNNEL_MEDIUM_TYPE = 65,
@@ -108,6 +111,9 @@
 };
 
 
+/* Service-Type values (RFC 2865, 5.6) */
+#define RADIUS_SERVICE_TYPE_FRAMED 2
+
 /* Termination-Action */
 #define RADIUS_TERMINATION_ACTION_DEFAULT 0
 #define RADIUS_TERMINATION_ACTION_RADIUS_REQUEST 1
@@ -250,8 +256,7 @@
 			       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);
+int radius_msg_make_authenticator(struct radius_msg *msg);
 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);
@@ -274,7 +279,8 @@
 				  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);
+int radius_msg_get_vlanid(struct radius_msg *msg, int *untagged, int numtagged,
+			  int *tagged);
 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);
@@ -319,4 +325,6 @@
 
 u8 radius_msg_find_unlisted_attr(struct radius_msg *msg, u8 *attrs);
 
+int radius_gen_session_id(u8 *id, size_t len);
+
 #endif /* RADIUS_H */
diff --git a/src/radius/radius_client.c b/src/radius/radius_client.c
index 693f61e..a4edd5f 100644
--- a/src/radius/radius_client.c
+++ b/src/radius/radius_client.c
@@ -226,6 +226,16 @@
 	 * next_radius_identifier - Next RADIUS message identifier to use
 	 */
 	u8 next_radius_identifier;
+
+	/**
+	 * interim_error_cb - Interim accounting error callback
+	 */
+	void (*interim_error_cb)(const u8 *addr, void *ctx);
+
+	/**
+	 * interim_error_cb_ctx - interim_error_cb() context data
+	 */
+	void *interim_error_cb_ctx;
 };
 
 
@@ -297,6 +307,25 @@
 }
 
 
+/**
+ * radius_client_set_interim_erro_cb - Register an interim acct error callback
+ * @radius: RADIUS client context from radius_client_init()
+ * @addr: Station address from the failed message
+ * @cb: Handler for interim accounting errors
+ * @ctx: Context pointer for handler callbacks
+ *
+ * This function is used to register a handler for processing failed
+ * transmission attempts of interim accounting update messages.
+ */
+void radius_client_set_interim_error_cb(struct radius_client_data *radius,
+					void (*cb)(const u8 *addr, void *ctx),
+					void *ctx)
+{
+	radius->interim_error_cb = cb;
+	radius->interim_error_cb_ctx = ctx;
+}
+
+
 /*
  * Returns >0 if message queue was flushed (i.e., the message that triggered
  * the error is not available anymore)
@@ -308,7 +337,7 @@
 	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) {
+	    _errno == EBADF || _errno == ENETUNREACH || _errno == EACCES) {
 		hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS,
 			       HOSTAPD_LEVEL_INFO,
 			       "Send failed - maybe interface status changed -"
@@ -336,6 +365,8 @@
 	int s;
 	struct wpabuf *buf;
 	size_t prev_num_msgs;
+	u8 *acct_delay_time;
+	size_t acct_delay_time_len;
 
 	if (entry->msg_type == RADIUS_ACCT ||
 	    entry->msg_type == RADIUS_ACCT_INTERIM) {
@@ -371,12 +402,52 @@
 			conf->auth_server->retransmissions++;
 		}
 	}
+
+	if (entry->msg_type == RADIUS_ACCT_INTERIM) {
+		wpa_printf(MSG_DEBUG,
+			   "RADIUS: Failed to transmit interim accounting update to "
+			   MACSTR " - drop message and request a new update",
+			   MAC2STR(entry->addr));
+		if (radius->interim_error_cb)
+			radius->interim_error_cb(entry->addr,
+						 radius->interim_error_cb_ctx);
+		return 1;
+	}
+
 	if (s < 0) {
 		wpa_printf(MSG_INFO,
 			   "RADIUS: No valid socket for retransmission");
 		return 1;
 	}
 
+	if (entry->msg_type == RADIUS_ACCT &&
+	    radius_msg_get_attr_ptr(entry->msg, RADIUS_ATTR_ACCT_DELAY_TIME,
+				    &acct_delay_time, &acct_delay_time_len,
+				    NULL) == 0 &&
+	    acct_delay_time_len == 4) {
+		struct radius_hdr *hdr;
+		u32 delay_time;
+
+		/*
+		 * Need to assign a new identifier since attribute contents
+		 * changes.
+		 */
+		hdr = radius_msg_get_hdr(entry->msg);
+		hdr->identifier = radius_client_get_id(radius);
+
+		/* Update Acct-Delay-Time to show wait time in queue */
+		delay_time = now - entry->first_try;
+		WPA_PUT_BE32(acct_delay_time, delay_time);
+
+		wpa_printf(MSG_DEBUG,
+			   "RADIUS: Updated Acct-Delay-Time to %u for retransmission",
+			   delay_time);
+		radius_msg_finish_acct(entry->msg, entry->shared_secret,
+				       entry->shared_secret_len);
+		if (radius->conf->msg_dumps)
+			radius_msg_dump(entry->msg);
+	}
+
 	/* retransmit; remove entry if too many attempts */
 	entry->attempts++;
 	hostapd_logger(radius->ctx, entry->addr, HOSTAPD_MODULE_RADIUS,
@@ -407,7 +478,6 @@
 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;
@@ -476,10 +546,10 @@
 			       (long int) (first - now.sec));
 	}
 
-	if (auth_failover && conf->num_auth_servers > 1)
+	if (auth_failover)
 		radius_client_auth_failover(radius);
 
-	if (acct_failover && conf->num_acct_servers > 1)
+	if (acct_failover)
 		radius_client_acct_failover(radius);
 }
 
@@ -625,39 +695,6 @@
 }
 
 
-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()
@@ -669,16 +706,19 @@
  * 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.
+ * message will not be retransmitted. Instead, a callback is used to indicate
+ * that the transmission failed for the specific station @addr so that a new
+ * interim accounting update message can be generated with up-to-date session
+ * data instead of trying to resend old information.
  *
  * 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.
+ * (RADIUS_CLIENT_MAX_RETRIES) is reached. No such retries are used with
+ * RADIUS_ACCT_INTERIM, i.e., such a pending message is removed from the queue
+ * automatically on transmission failure.
  *
  * 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.
+ * can be removed with radius_client_flush_auth().
  */
 int radius_client_send(struct radius_client_data *radius,
 		       struct radius_msg *msg, RadiusType msg_type,
@@ -691,11 +731,6 @@
 	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);
@@ -1015,6 +1050,9 @@
 	int sel_sock;
 	struct radius_msg_list *entry;
 	struct hostapd_radius_servers *conf = radius->conf;
+	struct sockaddr_in disconnect_addr = {
+		.sin_family = AF_UNSPEC,
+	};
 
 	hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS,
 		       HOSTAPD_LEVEL_INFO,
@@ -1023,6 +1061,12 @@
 		       hostapd_ip_txt(&nserv->addr, abuf, sizeof(abuf)),
 		       nserv->port);
 
+	if (oserv && oserv == nserv) {
+		/* Reconnect to same server, flush */
+		if (auth)
+			radius_client_flush(radius, 1);
+	}
+
 	if (oserv && oserv != nserv &&
 	    (nserv->shared_secret_len != oserv->shared_secret_len ||
 	     os_memcmp(nserv->shared_secret, oserv->shared_secret,
@@ -1125,6 +1169,11 @@
 		}
 	}
 
+	/* Force a reconnect by disconnecting the socket first */
+	if (connect(sel_sock, (struct sockaddr *) &disconnect_addr,
+		    sizeof(disconnect_addr)) < 0)
+		wpa_printf(MSG_INFO, "disconnect[radius]: %s", strerror(errno));
+
 	if (connect(sel_sock, addr, addrlen) < 0) {
 		wpa_printf(MSG_INFO, "connect[radius]: %s", strerror(errno));
 		return -1;
diff --git a/src/radius/radius_client.h b/src/radius/radius_client.h
index 3db16aa..8ca0874 100644
--- a/src/radius/radius_client.h
+++ b/src/radius/radius_client.h
@@ -241,6 +241,9 @@
 			    const u8 *shared_secret, size_t shared_secret_len,
 			    void *data),
 			   void *data);
+void radius_client_set_interim_error_cb(struct radius_client_data *radius,
+					void (*cb)(const u8 *addr, void *ctx),
+					void *ctx);
 int radius_client_send(struct radius_client_data *radius,
 		       struct radius_msg *msg,
 		       RadiusType msg_type, const u8 *addr);
diff --git a/src/radius/radius_das.c b/src/radius/radius_das.c
index 39ceea8..b7d991b 100644
--- a/src/radius/radius_das.c
+++ b/src/radius/radius_das.c
@@ -245,7 +245,7 @@
 				  (u8 *) &val, 4);
 	if (res == 4) {
 		u32 timestamp = ntohl(val);
-		if ((unsigned int) abs(now.sec - timestamp) >
+		if ((unsigned int) abs((int) (now.sec - timestamp)) >
 		    das->time_window) {
 			wpa_printf(MSG_DEBUG, "DAS: Unacceptable "
 				   "Event-Timestamp (%u; local time %u) in "
diff --git a/src/radius/radius_server.c b/src/radius/radius_server.c
index bdb7e42..744283c 100644
--- a/src/radius/radius_server.c
+++ b/src/radius/radius_server.c
@@ -265,6 +265,8 @@
 
 	struct dl_list erp_keys; /* struct eap_server_erp_key */
 
+	unsigned int tls_session_lifetime;
+
 	/**
 	 * wps - Wi-Fi Protected Setup context
 	 *
@@ -688,6 +690,7 @@
 	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);
@@ -1745,6 +1748,7 @@
 	}
 	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 =
diff --git a/src/radius/radius_server.h b/src/radius/radius_server.h
index ca4e38c..7a25802 100644
--- a/src/radius/radius_server.h
+++ b/src/radius/radius_server.h
@@ -170,6 +170,8 @@
 
 	const char *erp_domain;
 
+	unsigned int tls_session_lifetime;
+
 	/**
 	 * wps - Wi-Fi Protected Setup context
 	 *
diff --git a/src/rsn_supp/pmksa_cache.c b/src/rsn_supp/pmksa_cache.c
index ef7b683..3d8d122 100644
--- a/src/rsn_supp/pmksa_cache.c
+++ b/src/rsn_supp/pmksa_cache.c
@@ -15,7 +15,7 @@
 #include "wpa_i.h"
 #include "pmksa_cache.h"
 
-#ifdef IEEE8021X_EAPOL
+#if defined(IEEE8021X_EAPOL) && !defined(CONFIG_NO_WPA)
 
 static const int pmksa_cache_max_entries = 32;
 
@@ -109,6 +109,7 @@
  * @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)
+ * @pmkid: Calculated PMKID
  * @kck: Key confirmation key or %NULL if not yet derived
  * @kck_len: KCK length in bytes
  * @aa: Authenticator address
@@ -124,13 +125,13 @@
  */
 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 *pmkid, 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)
+	if (pmk_len > PMK_LEN_MAX)
 		return NULL;
 
 	if (wpa_key_mgmt_suite_b(akmp) && !kck)
@@ -141,7 +142,9 @@
 		return NULL;
 	os_memcpy(entry->pmk, pmk, pmk_len);
 	entry->pmk_len = pmk_len;
-	if (akmp == WPA_KEY_MGMT_IEEE8021X_SUITE_B_192)
+	if (pmkid)
+		os_memcpy(entry->pmkid, pmkid, PMKID_LEN);
+	else 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);
@@ -344,7 +347,7 @@
 	struct rsn_pmksa_cache_entry *new_entry;
 
 	new_entry = pmksa_cache_add(pmksa, old_entry->pmk, old_entry->pmk_len,
-				    NULL, 0,
+				    NULL, NULL, 0,
 				    aa, pmksa->sm->own_addr,
 				    old_entry->network_ctx, old_entry->akmp);
 	if (new_entry == NULL)
diff --git a/src/rsn_supp/pmksa_cache.h b/src/rsn_supp/pmksa_cache.h
index f8e040e..daede6d 100644
--- a/src/rsn_supp/pmksa_cache.h
+++ b/src/rsn_supp/pmksa_cache.h
@@ -15,7 +15,7 @@
 struct rsn_pmksa_cache_entry {
 	struct rsn_pmksa_cache_entry *next;
 	u8 pmkid[PMKID_LEN];
-	u8 pmk[PMK_LEN];
+	u8 pmk[PMK_LEN_MAX];
 	size_t pmk_len;
 	os_time_t expiration;
 	int akmp; /* WPA_KEY_MGMT_* */
@@ -44,7 +44,7 @@
 	PMKSA_EXPIRE,
 };
 
-#ifdef IEEE8021X_EAPOL
+#if defined(IEEE8021X_EAPOL) && !defined(CONFIG_NO_WPA)
 
 struct rsn_pmksa_cache *
 pmksa_cache_init(void (*free_cb)(struct rsn_pmksa_cache_entry *entry,
@@ -57,7 +57,7 @@
 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 *pmkid, 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);
@@ -105,7 +105,7 @@
 
 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 *pmkid, const u8 *kck, size_t kck_len,
 		const u8 *aa, const u8 *spa, void *network_ctx, int akmp)
 {
 	return NULL;
diff --git a/src/rsn_supp/preauth.c b/src/rsn_supp/preauth.c
index c6534af..4c9a4fb 100644
--- a/src/rsn_supp/preauth.c
+++ b/src/rsn_supp/preauth.c
@@ -18,7 +18,7 @@
 #include "wpa_i.h"
 
 
-#ifdef IEEE8021X_EAPOL
+#if defined(IEEE8021X_EAPOL) && !defined(CONFIG_NO_WPA)
 
 #define PMKID_CANDIDATE_PRIO_SCAN 1000
 
@@ -93,7 +93,7 @@
 			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,
+			pmksa_cache_add(sm->pmksa, pmk, pmk_len, NULL,
 					NULL, 0,
 					sm->preauth_bssid, sm->own_addr,
 					sm->network_ctx,
@@ -538,4 +538,4 @@
 	return sm->preauth_eapol != NULL;
 }
 
-#endif /* IEEE8021X_EAPOL */
+#endif /* IEEE8021X_EAPOL && !CONFIG_NO_WPA */
diff --git a/src/rsn_supp/preauth.h b/src/rsn_supp/preauth.h
index 277f066..8caf3ee 100644
--- a/src/rsn_supp/preauth.h
+++ b/src/rsn_supp/preauth.h
@@ -11,7 +11,7 @@
 
 struct wpa_scan_results;
 
-#ifdef IEEE8021X_EAPOL
+#if defined(IEEE8021X_EAPOL) && !defined(CONFIG_NO_WPA)
 
 void pmksa_candidate_free(struct wpa_sm *sm);
 int rsn_preauth_init(struct wpa_sm *sm, const u8 *dst,
@@ -27,7 +27,7 @@
 			   int verbose);
 int rsn_preauth_in_progress(struct wpa_sm *sm);
 
-#else /* IEEE8021X_EAPOL */
+#else /* IEEE8021X_EAPOL && !CONFIG_NO_WPA */
 
 static inline void pmksa_candidate_free(struct wpa_sm *sm)
 {
@@ -74,6 +74,6 @@
 	return 0;
 }
 
-#endif /* IEEE8021X_EAPOL */
+#endif /* IEEE8021X_EAPOL && !CONFIG_NO_WPA */
 
 #endif /* PREAUTH_H */
diff --git a/src/rsn_supp/tdls.c b/src/rsn_supp/tdls.c
index 6b1df71..e424168 100644
--- a/src/rsn_supp/tdls.c
+++ b/src/rsn_supp/tdls.c
@@ -12,6 +12,7 @@
 #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"
@@ -626,9 +627,15 @@
 	 */
 
 	if (peer->initiator) {
+		u8 addr[ETH_ALEN];
+
 		wpa_printf(MSG_DEBUG, "TDLS: TPK lifetime expired for " MACSTR
 			   " - try to renew", MAC2STR(peer->addr));
-		wpa_tdls_start(sm, peer->addr);
+		/* cache the peer address before do_teardown */
+		os_memcpy(addr, peer->addr, ETH_ALEN);
+		wpa_tdls_do_teardown(sm, peer,
+				     WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED);
+		wpa_tdls_start(sm, addr);
 	} else {
 		wpa_printf(MSG_DEBUG, "TDLS: TPK lifetime expired for " MACSTR
 			   " - tear down", MAC2STR(peer->addr));
@@ -2169,6 +2176,14 @@
 			   "ignore TPK M2 from " MACSTR, MAC2STR(src_addr));
 		return -1;
 	}
+
+	if (peer->tpk_success) {
+		wpa_printf(MSG_INFO, "TDLS: Ignore incoming TPK M2 retry, from "
+			   MACSTR " as TPK M3 was already sent",
+			   MAC2STR(src_addr));
+		return 0;
+	}
+
 	wpa_tdls_tpk_retry_timeout_cancel(sm, peer, WLAN_TDLS_SETUP_REQUEST);
 
 	if (len < 3 + 2 + 1) {
@@ -2324,7 +2339,7 @@
 		    kde.ftie, sizeof(*ftie));
 	ftie = (struct wpa_tdls_ftie *) kde.ftie;
 
-	if (!os_memcmp(peer->inonce, ftie->Snonce, WPA_NONCE_LEN) == 0) {
+	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 */
@@ -2385,7 +2400,7 @@
 	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;
+		goto error_no_msg;
 
 	if (!peer->tpk_success) {
 		/*
@@ -2406,6 +2421,7 @@
 error:
 	wpa_tdls_send_error(sm, src_addr, WLAN_TDLS_SETUP_CONFIRM, dtoken, 1,
 			    status);
+error_no_msg:
 	wpa_tdls_disable_peer_link(sm, peer);
 	return -1;
 }
@@ -2502,13 +2518,13 @@
 		goto error;
 	}
 
-	if (!os_memcmp(peer->rnonce, ftie->Anonce, WPA_NONCE_LEN) == 0) {
+	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) {
+	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;
@@ -2859,14 +2875,14 @@
 }
 
 
-static int wpa_tdls_prohibited(struct wpa_eapol_ie_parse *elems)
+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 wpa_eapol_ie_parse *elems)
+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);
@@ -2875,12 +2891,13 @@
 
 void wpa_tdls_ap_ies(struct wpa_sm *sm, const u8 *ies, size_t len)
 {
-	struct wpa_eapol_ie_parse elems;
+	struct ieee802_11_elems elems;
 
 	sm->tdls_prohibited = 0;
 	sm->tdls_chan_switch_prohibited = 0;
 
-	if (ies == NULL || wpa_supplicant_parse_ies(ies, len, &elems) < 0 ||
+	if (ies == NULL ||
+	    ieee802_11_parse_elems(ies, len, &elems, 0) == ParseFailed ||
 	    elems.ext_capab == NULL || elems.ext_capab_len < 2 + 5)
 		return;
 
@@ -2896,9 +2913,10 @@
 
 void wpa_tdls_assoc_resp_ies(struct wpa_sm *sm, const u8 *ies, size_t len)
 {
-	struct wpa_eapol_ie_parse elems;
+	struct ieee802_11_elems elems;
 
-	if (ies == NULL || wpa_supplicant_parse_ies(ies, len, &elems) < 0 ||
+	if (ies == NULL ||
+	    ieee802_11_parse_elems(ies, len, &elems, 0) == ParseFailed ||
 	    elems.ext_capab == NULL || elems.ext_capab_len < 2 + 5)
 		return;
 
diff --git a/src/rsn_supp/wpa.c b/src/rsn_supp/wpa.c
index faffe36..3c47879 100644
--- a/src/rsn_supp/wpa.c
+++ b/src/rsn_supp/wpa.c
@@ -1,6 +1,7 @@
 /*
  * WPA Supplicant - WPA state machine and EAPOL-Key processing
  * Copyright (c) 2003-2015, Jouni Malinen <j@w1.fi>
+ * Copyright(c) 2015 Intel Deutschland GmbH
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -23,6 +24,9 @@
 #include "peerkey.h"
 
 
+static const u8 null_rsc[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
+
+
 /**
  * wpa_eapol_key_send - Send WPA/RSN EAPOL-Key message
  * @sm: Pointer to WPA state machine data from wpa_sm_init()
@@ -34,11 +38,13 @@
  * @msg: EAPOL-Key message
  * @msg_len: Length of message
  * @key_mic: Pointer to the buffer to which the EAPOL-Key MIC is written
+ * Returns: >= 0 on success, < 0 on failure
  */
-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_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 ret = -1;
 	size_t mic_len = wpa_mic_len(sm->key_mgmt);
 
 	if (is_zero_ether_addr(dest) && is_zero_ether_addr(sm->bssid)) {
@@ -69,10 +75,11 @@
 	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);
+	ret = wpa_sm_ether_send(sm, dest, proto, msg, msg_len);
 	eapol_sm_notify_tx_eapol_key(sm->eapol);
 out:
 	os_free(msg);
+	return ret;
 }
 
 
@@ -124,7 +131,7 @@
 		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;
+		key_info |= WPA_KEY_INFO_MIC | WPA_KEY_INFO_SECURE;
 	if (error)
 		key_info |= WPA_KEY_INFO_ERROR;
 	if (pairwise)
@@ -206,15 +213,21 @@
 #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 (sm->key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B_192)
+			pmk_len = PMK_LEN_SUITE_B_192;
+		else
+			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;
+			if (pmk_len == PMK_LEN) {
+				/*
+				 * 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];
@@ -236,7 +249,7 @@
 			    !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,
+						     sm->pmk, pmk_len, NULL,
 						     NULL, 0,
 						     src_addr, sm->own_addr,
 						     sm->network_ctx,
@@ -257,7 +270,7 @@
 				 * much we can do here without knowing what
 				 * exactly caused the server to misbehave.
 				 */
-				wpa_dbg(sm->ctx->msg_ctx, MSG_INFO,
+				wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
 					"RSN: PMKID mismatch - authentication server may have derived different MSK?!");
 				return -1;
 			}
@@ -318,7 +331,7 @@
  * @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
+ * Returns: >= 0 on success, < 0 on failure
  */
 int wpa_supplicant_send_2_of_4(struct wpa_sm *sm, const unsigned char *dst,
 			       const struct wpa_eapol_key *key,
@@ -351,13 +364,12 @@
 		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,
+		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,
@@ -409,10 +421,8 @@
 	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;
+	return wpa_eapol_key_send(sm, ptk->kck, ptk->kck_len, ver, dst,
+				  ETH_P_EAPOL, rbuf, rlen, key_mic);
 }
 
 
@@ -500,6 +510,7 @@
 		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;
@@ -525,7 +536,7 @@
 #endif /* CONFIG_P2P */
 
 	if (wpa_supplicant_send_2_of_4(sm, sm->bssid, key, ver, sm->snonce,
-				       kde, kde_len, ptk))
+				       kde, kde_len, ptk) < 0)
 		goto failed;
 
 	os_free(kde_buf);
@@ -603,7 +614,12 @@
 	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_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+			"WPA: Do not re-install same PTK to the driver");
+		return 0;
+	}
 
 	wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
 		"WPA: Installing PTK to the driver");
@@ -643,6 +659,7 @@
 
 	/* 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);
@@ -753,12 +770,43 @@
 }
 
 
+static int wpa_supplicant_rsc_relaxation(const struct wpa_sm *sm,
+					 const u8 *rsc)
+{
+	int rsclen;
+
+	if (!sm->wpa_rsc_relaxation)
+		return 0;
+
+	rsclen = wpa_cipher_rsc_len(sm->group_cipher);
+
+	/*
+	 * Try to detect RSC (endian) corruption issue where the AP sends
+	 * the RSC bytes in EAPOL-Key message in the wrong order, both if
+	 * it's actually a 6-byte field (as it should be) and if it treats
+	 * it as an 8-byte field.
+	 * An AP model known to have this bug is the Sapido RB-1632.
+	 */
+	if (rsclen == 6 && ((rsc[5] && !rsc[0]) || rsc[6] || rsc[7])) {
+		wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+			"RSC %02x%02x%02x%02x%02x%02x%02x%02x is likely bogus, using 0",
+			rsc[0], rsc[1], rsc[2], rsc[3],
+			rsc[4], rsc[5], rsc[6], rsc[7]);
+
+		return 1;
+	}
+
+	return 0;
+}
+
+
 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;
+	const u8 *key_rsc;
 
 	/*
 	 * IEEE Std 802.11i-2004 - 8.5.2 EAPOL-Key frames - Figure 43x
@@ -784,11 +832,15 @@
 	os_memcpy(gd.gtk, gtk, gtk_len);
 	gd.gtk_len = gtk_len;
 
+	key_rsc = key->key_rsc;
+	if (wpa_supplicant_rsc_relaxation(sm, key->key_rsc))
+		key_rsc = null_rsc;
+
 	if (sm->group_cipher != WPA_CIPHER_GTK_NOT_USED &&
 	    (wpa_supplicant_check_group_cipher(sm, sm->group_cipher,
 					       gtk_len, gtk_len,
 					       &gd.key_rsc_len, &gd.alg) ||
-	     wpa_supplicant_install_gtk(sm, &gd, key->key_rsc))) {
+	     wpa_supplicant_install_gtk(sm, &gd, key_rsc))) {
 		wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
 			"RSN: Failed to install GTK");
 		os_memset(&gd, 0, sizeof(gd));
@@ -989,8 +1041,8 @@
 	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)
+		while (end - pos > 2) {
+			if (2 + pos[1] > end - pos)
 				break;
 			switch (*pos) {
 			case WLAN_EID_MOBILITY_DOMAIN:
@@ -1086,7 +1138,7 @@
  * @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
+ * Returns: >= 0 on success, < 0 on failure
  */
 int wpa_supplicant_send_4_of_4(struct wpa_sm *sm, const unsigned char *dst,
 			       const struct wpa_eapol_key *key,
@@ -1126,10 +1178,8 @@
 		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;
+	return wpa_eapol_key_send(sm, ptk->kck, ptk->kck_len, ver, dst,
+				  ETH_P_EAPOL, rbuf, rlen, key_mic);
 }
 
 
@@ -1202,7 +1252,7 @@
 #endif /* CONFIG_P2P */
 
 	if (wpa_supplicant_send_4_of_4(sm, sm->bssid, key, ver, key_info,
-				       &sm->ptk)) {
+				       &sm->ptk) < 0) {
 		goto failed;
 	}
 
@@ -1247,7 +1297,7 @@
 	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,
+		sa = pmksa_cache_add(sm->pmksa, sm->pmk, sm->pmk_len, NULL,
 				     sm->ptk.kck, sm->ptk.kck_len,
 				     sm->bssid, sm->own_addr,
 				     sm->network_ctx, sm->key_mgmt);
@@ -1292,8 +1342,8 @@
 					      &gd->key_rsc_len, &gd->alg))
 		return -1;
 
-	wpa_hexdump(MSG_DEBUG, "RSN: received GTK in group key handshake",
-		    ie.gtk, ie.gtk_len);
+	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)));
@@ -1344,6 +1394,11 @@
 	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,
@@ -1361,6 +1416,7 @@
 			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,
@@ -1431,10 +1487,8 @@
 		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;
+	return wpa_eapol_key_send(sm, sm->ptk.kck, sm->ptk.kck_len, ver,
+				  sm->bssid, ETH_P_EAPOL, rbuf, rlen, key_mic);
 }
 
 
@@ -1447,6 +1501,7 @@
 	u16 key_info;
 	int rekey, ret;
 	struct wpa_gtk_data gd;
+	const u8 *key_rsc;
 
 	if (!sm->msg_3_of_4_ok) {
 		wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
@@ -1477,8 +1532,12 @@
 	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))
+	key_rsc = key->key_rsc;
+	if (wpa_supplicant_rsc_relaxation(sm, key->key_rsc))
+		key_rsc = null_rsc;
+
+	if (wpa_supplicant_install_gtk(sm, &gd, key_rsc) ||
+	    wpa_supplicant_send_2_of_2(sm, key, ver, key_info) < 0)
 		goto failed;
 	os_memset(&gd, 0, sizeof(gd));
 
@@ -1575,6 +1634,11 @@
 	/* 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);
@@ -1585,6 +1649,7 @@
 			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 ||
@@ -1605,14 +1670,14 @@
 		}
 		if (aes_unwrap(sm->ptk.kek, sm->ptk.kek_len, *key_data_len / 8,
 			       key_data, buf)) {
-			os_free(buf);
+			bin_clear_free(buf, *key_data_len);
 			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);
+		bin_clear_free(buf, *key_data_len);
 		WPA_PUT_BE16(key->key_data_length, *key_data_len);
 	} else {
 		wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
@@ -2225,6 +2290,9 @@
 #ifdef CONFIG_IEEE80211R
 	os_free(sm->assoc_resp_ies);
 #endif /* CONFIG_IEEE80211R */
+#ifdef CONFIG_TESTING_OPTIONS
+	wpabuf_free(sm->test_assoc_ie);
+#endif /* CONFIG_TESTING_OPTIONS */
 	os_free(sm);
 }
 
@@ -2323,12 +2391,13 @@
  * @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
+ * @pmkid: Calculated PMKID
  * @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)
+		    const u8 *pmkid, const u8 *bssid)
 {
 	if (sm == NULL)
 		return;
@@ -2343,7 +2412,7 @@
 #endif /* CONFIG_IEEE80211R */
 
 	if (bssid) {
-		pmksa_cache_add(sm->pmksa, pmk, pmk_len, NULL, 0,
+		pmksa_cache_add(sm->pmksa, pmk, pmk_len, pmkid, NULL, 0,
 				bssid, sm->own_addr,
 				sm->network_ctx, sm->key_mgmt);
 	}
@@ -2427,6 +2496,7 @@
 			sm->ssid_len = 0;
 		sm->wpa_ptk_rekey = config->wpa_ptk_rekey;
 		sm->p2p = config->p2p;
+		sm->wpa_rsc_relaxation = config->wpa_rsc_relaxation;
 	} else {
 		sm->network_ctx = NULL;
 		sm->peerkey_enabled = 0;
@@ -2437,6 +2507,7 @@
 		sm->ssid_len = 0;
 		sm->wpa_ptk_rekey = 0;
 		sm->p2p = 0;
+		sm->wpa_rsc_relaxation = 0;
 	}
 }
 
@@ -2624,6 +2695,17 @@
 	if (sm == NULL)
 		return -1;
 
+#ifdef CONFIG_TESTING_OPTIONS
+	if (sm->test_assoc_ie) {
+		wpa_printf(MSG_DEBUG,
+			   "TESTING: Replace association WPA/RSN IE");
+		if (*wpa_ie_len < wpabuf_len(sm->test_assoc_ie))
+			return -1;
+		os_memcpy(wpa_ie, wpabuf_head(sm->test_assoc_ie),
+			  wpabuf_len(sm->test_assoc_ie));
+		res = wpabuf_len(sm->test_assoc_ie);
+	} else
+#endif /* CONFIG_TESTING_OPTIONS */
 	res = wpa_gen_wpa_ie(sm, wpa_ie, *wpa_ie_len);
 	if (res < 0)
 		return -1;
@@ -2963,3 +3045,12 @@
 	}
 	sm->ptk_set = 1;
 }
+
+
+#ifdef CONFIG_TESTING_OPTIONS
+void wpa_sm_set_test_assoc_ie(struct wpa_sm *sm, struct wpabuf *buf)
+{
+	wpabuf_free(sm->test_assoc_ie);
+	sm->test_assoc_ie = buf;
+}
+#endif /* CONFIG_TESTING_OPTIONS */
diff --git a/src/rsn_supp/wpa.h b/src/rsn_supp/wpa.h
index e163b70..c89799a 100644
--- a/src/rsn_supp/wpa.h
+++ b/src/rsn_supp/wpa.h
@@ -104,6 +104,7 @@
 	size_t ssid_len;
 	int wpa_ptk_rekey;
 	int p2p;
+	int wpa_rsc_relaxation;
 };
 
 #ifndef CONFIG_NO_WPA
@@ -113,7 +114,7 @@
 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);
+		    const u8 *pmkid, 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);
@@ -180,7 +181,8 @@
 }
 
 static inline void wpa_sm_set_pmk(struct wpa_sm *sm, const u8 *pmk,
-				  size_t pmk_len)
+				  size_t pmk_len, const u8 *pmkid,
+				  const u8 *bssid)
 {
 }
 
@@ -320,7 +322,8 @@
 }
 
 static inline void wpa_sm_set_ptk_kck_kek(struct wpa_sm *sm, const u8 *ptk_kck,
-					  const u8 *ptk_kek)
+					  size_t ptk_kck_len,
+					  const u8 *ptk_kek, size_t ptk_kek_len)
 {
 }
 
@@ -417,5 +420,6 @@
 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);
+void wpa_sm_set_test_assoc_ie(struct wpa_sm *sm, struct wpabuf *buf);
 
 #endif /* WPA_H */
diff --git a/src/rsn_supp/wpa_ft.c b/src/rsn_supp/wpa_ft.c
index 06dea05..205793e 100644
--- a/src/rsn_supp/wpa_ft.c
+++ b/src/rsn_supp/wpa_ft.c
@@ -168,9 +168,7 @@
 	pos = (u8 *) (rsnie + 1);
 
 	/* Group Suite Selector */
-	if (sm->group_cipher != WPA_CIPHER_CCMP &&
-	    sm->group_cipher != WPA_CIPHER_GCMP &&
-	    sm->group_cipher != WPA_CIPHER_TKIP) {
+	if (!wpa_cipher_valid_group(sm->group_cipher)) {
 		wpa_printf(MSG_WARNING, "FT: Invalid group cipher (%d)",
 			   sm->group_cipher);
 		os_free(buf);
diff --git a/src/rsn_supp/wpa_i.h b/src/rsn_supp/wpa_i.h
index 965a9c1..f653ba6 100644
--- a/src/rsn_supp/wpa_i.h
+++ b/src/rsn_supp/wpa_i.h
@@ -19,11 +19,12 @@
  * struct wpa_sm - Internal WPA state machine data
  */
 struct wpa_sm {
-	u8 pmk[PMK_LEN];
+	u8 pmk[PMK_LEN_MAX];
 	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;
@@ -60,6 +61,7 @@
 	size_t ssid_len;
 	int wpa_ptk_rekey;
 	int p2p;
+	int wpa_rsc_relaxation;
 
 	u8 own_addr[ETH_ALEN];
 	const char *ifname;
@@ -132,6 +134,10 @@
 #ifdef CONFIG_P2P
 	u8 p2p_ip_addr[3 * 4];
 #endif /* CONFIG_P2P */
+
+#ifdef CONFIG_TESTING_OPTIONS
+	struct wpabuf *test_assoc_ie;
+#endif /* CONFIG_TESTING_OPTIONS */
 };
 
 
@@ -342,16 +348,14 @@
 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_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,
diff --git a/src/rsn_supp/wpa_ie.c b/src/rsn_supp/wpa_ie.c
index 0c37b35..c44844e 100644
--- a/src/rsn_supp/wpa_ie.c
+++ b/src/rsn_supp/wpa_ie.c
@@ -378,7 +378,7 @@
 		return 0;
 	}
 
-	if (pos + 1 + RSN_SELECTOR_LEN < end &&
+	if (1 + RSN_SELECTOR_LEN < end - pos &&
 	    pos[1] >= RSN_SELECTOR_LEN + PMKID_LEN &&
 	    RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_PMKID) {
 		ie->pmkid = pos + 2 + RSN_SELECTOR_LEN;
@@ -491,13 +491,13 @@
 	int ret = 0;
 
 	os_memset(ie, 0, sizeof(*ie));
-	for (pos = buf, end = pos + len; pos + 1 < end; pos += 2 + pos[1]) {
+	for (pos = buf, end = pos + len; end - pos > 1; pos += 2 + pos[1]) {
 		if (pos[0] == 0xdd &&
 		    ((pos == buf + len - 1) || pos[1] == 0)) {
 			/* Ignore padding */
 			break;
 		}
-		if (pos + 2 + pos[1] > end) {
+		if (2 + pos[1] > end - pos) {
 			wpa_printf(MSG_DEBUG, "WPA: EAPOL-Key Key Data "
 				   "underflow (ie=%d len=%d pos=%d)",
 				   pos[0], pos[1], (int) (pos - buf));
diff --git a/src/tls/Makefile b/src/tls/Makefile
index 27cdfca..52a890a 100644
--- a/src/tls/Makefile
+++ b/src/tls/Makefile
@@ -24,6 +24,7 @@
 	tlsv1_client.o \
 	tlsv1_client_read.o \
 	tlsv1_client_write.o \
+	tlsv1_client_ocsp.o \
 	tlsv1_common.o \
 	tlsv1_cred.o \
 	tlsv1_record.o \
diff --git a/src/tls/asn1.h b/src/tls/asn1.h
index 7475007..6bd7df5 100644
--- a/src/tls/asn1.h
+++ b/src/tls/asn1.h
@@ -20,6 +20,7 @@
 #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_EMBEDDED_PDV	0x0B /* 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 */
@@ -35,7 +36,8 @@
 #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_TAG_CHARACTERSTRING	0x1D /* not yet parsed */
+#define ASN1_TAG_BMPSTRING	0x1E /* not yet parsed */
 
 #define ASN1_CLASS_UNIVERSAL		0
 #define ASN1_CLASS_APPLICATION		1
diff --git a/src/tls/pkcs5.c b/src/tls/pkcs5.c
index 8a93483..a2ad83b 100644
--- a/src/tls/pkcs5.c
+++ b/src/tls/pkcs5.c
@@ -1,6 +1,6 @@
 /*
  * PKCS #5 (Password-based Encryption)
- * Copyright (c) 2009, Jouni Malinen <j@w1.fi>
+ * 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.
@@ -11,6 +11,7 @@
 #include "common.h"
 #include "crypto/crypto.h"
 #include "crypto/md5.h"
+#include "crypto/sha1.h"
 #include "asn1.h"
 #include "pkcs5.h"
 
@@ -18,30 +19,261 @@
 struct pkcs5_params {
 	enum pkcs5_alg {
 		PKCS5_ALG_UNKNOWN,
-		PKCS5_ALG_MD5_DES_CBC
+		PKCS5_ALG_MD5_DES_CBC,
+		PKCS5_ALG_PBES2,
+		PKCS5_ALG_SHA1_3DES_CBC,
 	} alg;
-	u8 salt[8];
+	u8 salt[64];
 	size_t salt_len;
 	unsigned int iter_count;
+	enum pbes2_enc_alg {
+		PBES2_ENC_ALG_UNKNOWN,
+		PBES2_ENC_ALG_DES_EDE3_CBC,
+	} enc_alg;
+	u8 iv[8];
+	size_t iv_len;
 };
 
 
+static int oid_is_rsadsi(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 pkcs5_is_oid(struct asn1_oid *oid, unsigned long alg)
+{
+	return oid->len == 7 &&
+		oid_is_rsadsi(oid) &&
+		oid->oid[4] == 1 /* pkcs */ &&
+		oid->oid[5] == 5 /* pkcs-5 */ &&
+		oid->oid[6] == alg;
+}
+
+
+static int enc_alg_is_oid(struct asn1_oid *oid, unsigned long alg)
+{
+	return oid->len == 6 &&
+		oid_is_rsadsi(oid) &&
+		oid->oid[4] == 3 /* encryptionAlgorithm */ &&
+		oid->oid[5] == alg;
+}
+
+
+static int pkcs12_is_pbe_oid(struct asn1_oid *oid, unsigned long alg)
+{
+	return oid->len == 8 &&
+		oid_is_rsadsi(oid) &&
+		oid->oid[4] == 1 /* pkcs */ &&
+		oid->oid[5] == 12 /* pkcs-12 */ &&
+		oid->oid[6] == 1 /* pkcs-12PbeIds */ &&
+		oid->oid[7] == alg;
+}
+
+
 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 */)
+	if (pkcs5_is_oid(oid, 3)) /* pbeWithMD5AndDES-CBC (PBES1) */
 		return PKCS5_ALG_MD5_DES_CBC;
-
+	if (pkcs12_is_pbe_oid(oid, 3)) /* pbeWithSHAAnd3-KeyTripleDES-CBC */
+		return PKCS5_ALG_SHA1_3DES_CBC;
+	if (pkcs5_is_oid(oid, 13)) /* id-PBES2 (PBES2) */
+		return PKCS5_ALG_PBES2;
 	return PKCS5_ALG_UNKNOWN;
 }
 
 
+static int pkcs5_get_params_pbes2(struct pkcs5_params *params, const u8 *pos,
+				  const u8 *enc_alg_end)
+{
+	struct asn1_hdr hdr;
+	const u8 *end, *kdf_end;
+	struct asn1_oid oid;
+	char obuf[80];
+
+	/*
+	 * RFC 2898, Ch. A.4
+	 *
+	 * PBES2-params ::= SEQUENCE {
+	 *     keyDerivationFunc AlgorithmIdentifier {{PBES2-KDFs}},
+	 *     encryptionScheme AlgorithmIdentifier {{PBES2-Encs}} }
+	 *
+	 * PBES2-KDFs ALGORITHM-IDENTIFIER ::=
+	 *     { {PBKDF2-params IDENTIFIED BY id-PBKDF2}, ... }
+	 */
+
+	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 (PBES2-params) - found class %d tag 0x%x",
+			   hdr.class, hdr.tag);
+		return -1;
+	}
+	pos = hdr.payload;
+	end = hdr.payload + hdr.length;
+
+	if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+	    hdr.class != ASN1_CLASS_UNIVERSAL ||
+	    hdr.tag != ASN1_TAG_SEQUENCE) {
+		wpa_printf(MSG_DEBUG,
+			   "PKCS #5: Expected SEQUENCE (keyDerivationFunc) - found class %d tag 0x%x",
+			   hdr.class, hdr.tag);
+		return -1;
+	}
+
+	pos = hdr.payload;
+	kdf_end = end = hdr.payload + hdr.length;
+
+	if (asn1_get_oid(pos, end - pos, &oid, &pos)) {
+		wpa_printf(MSG_DEBUG,
+			   "PKCS #5: Failed to parse OID (keyDerivationFunc algorithm)");
+		return -1;
+	}
+
+	asn1_oid_to_str(&oid, obuf, sizeof(obuf));
+	wpa_printf(MSG_DEBUG, "PKCS #5: PBES2 keyDerivationFunc algorithm %s",
+		   obuf);
+	if (!pkcs5_is_oid(&oid, 12)) /* id-PBKDF2 */ {
+		wpa_printf(MSG_DEBUG,
+			   "PKCS #5: Unsupported PBES2 keyDerivationFunc algorithm %s",
+			   obuf);
+		return -1;
+	}
+
+	/*
+	 * RFC 2898, C.
+	 *
+	 * PBKDF2-params ::= SEQUENCE {
+	 *     salt CHOICE {
+	 *       specified OCTET STRING,
+	 *       otherSource AlgorithmIdentifier {{PBKDF2-SaltSources}}
+	 *     },
+	 *     iterationCount INTEGER (1..MAX),
+	 *     keyLength INTEGER (1..MAX) OPTIONAL,
+	 *     prf AlgorithmIdentifier {{PBKDF2-PRFs}} DEFAULT
+	 *     algid-hmacWithSHA1
+	 * }
+	 */
+
+	if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+	    hdr.class != ASN1_CLASS_UNIVERSAL ||
+	    hdr.tag != ASN1_TAG_SEQUENCE) {
+		wpa_printf(MSG_DEBUG,
+			   "PKCS #5: Expected SEQUENCE (PBKDF2-params) - found class %d tag 0x%x",
+			   hdr.class, hdr.tag);
+		return -1;
+	}
+
+	pos = hdr.payload;
+	end = hdr.payload + hdr.length;
+
+	/* For now, only support the salt CHOICE specified (OCTET STRING) */
+	if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+	    hdr.class != ASN1_CLASS_UNIVERSAL ||
+	    hdr.tag != ASN1_TAG_OCTETSTRING ||
+	    hdr.length > sizeof(params->salt)) {
+		wpa_printf(MSG_DEBUG,
+			   "PKCS #5: Expected OCTET STRING (salt.specified) - 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;
+	}
+
+	/* For now, ignore optional keyLength and prf */
+
+	pos = kdf_end;
+
+	/* encryptionScheme AlgorithmIdentifier {{PBES2-Encs}} */
+
+	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 (encryptionScheme) - found class %d tag 0x%x",
+			   hdr.class, hdr.tag);
+		return -1;
+	}
+
+	pos = hdr.payload;
+	end = hdr.payload + hdr.length;
+
+	if (asn1_get_oid(pos, end - pos, &oid, &pos)) {
+		wpa_printf(MSG_DEBUG,
+			   "PKCS #5: Failed to parse OID (encryptionScheme algorithm)");
+		return -1;
+	}
+
+	asn1_oid_to_str(&oid, obuf, sizeof(obuf));
+	wpa_printf(MSG_DEBUG, "PKCS #5: PBES2 encryptionScheme algorithm %s",
+		   obuf);
+	if (enc_alg_is_oid(&oid, 7)) {
+		params->enc_alg = PBES2_ENC_ALG_DES_EDE3_CBC;
+	} else {
+		wpa_printf(MSG_DEBUG,
+			   "PKCS #5: Unsupported PBES2 encryptionScheme algorithm %s",
+			   obuf);
+		return -1;
+	}
+
+	/*
+	 * RFC 2898, B.2.2:
+	 * The parameters field associated with this OID in an
+	 * AlgorithmIdentifier shall have type OCTET STRING (SIZE(8)),
+	 * specifying the initialization vector for CBC mode.
+	 */
+	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 OCTET STRING (SIZE(8)) (IV) - found class %d tag 0x%x size %d",
+			   hdr.class, hdr.tag, hdr.length);
+		return -1;
+	}
+	os_memcpy(params->iv, hdr.payload, hdr.length);
+	params->iv_len = hdr.length;
+	wpa_hexdump(MSG_DEBUG, "PKCS #5: IV", params->iv, params->iv_len);
+
+	return 0;
+}
+
+
 static int pkcs5_get_params(const u8 *enc_alg, size_t enc_alg_len,
 			    struct pkcs5_params *params)
 {
@@ -71,11 +303,23 @@
 		return -1;
 	}
 
+	if (params->alg == PKCS5_ALG_PBES2)
+		return pkcs5_get_params_pbes2(params, pos, enc_alg_end);
+
+	/* PBES1 */
+
 	/*
 	 * PKCS#5, Section 8
 	 * PBEParameter ::= SEQUENCE {
 	 *   salt OCTET STRING SIZE(8),
 	 *   iterationCount INTEGER }
+	 *
+	 * Note: The same implementation can be used to parse the PKCS #12
+	 * version described in RFC 7292, C:
+	 * pkcs-12PbeParams ::= SEQUENCE {
+	 *     salt        OCTET STRING,
+	 *     iterations  INTEGER
+	 * }
 	 */
 
 	if (asn1_get_next(pos, enc_alg_end - pos, &hdr) < 0 ||
@@ -89,11 +333,11 @@
 	pos = hdr.payload;
 	end = hdr.payload + hdr.length;
 
-	/* salt OCTET STRING SIZE(8) */
+	/* salt OCTET STRING SIZE(8) (PKCS #5) or OCTET STRING (PKCS #12) */
 	if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
 	    hdr.class != ASN1_CLASS_UNIVERSAL ||
 	    hdr.tag != ASN1_TAG_OCTETSTRING ||
-	    hdr.length != 8) {
+	    hdr.length > sizeof(params->salt)) {
 		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);
@@ -136,6 +380,174 @@
 }
 
 
+static struct crypto_cipher *
+pkcs5_crypto_init_pbes2(struct pkcs5_params *params, const char *passwd)
+{
+	u8 key[24];
+
+	if (params->enc_alg != PBES2_ENC_ALG_DES_EDE3_CBC ||
+	    params->iv_len != 8)
+		return NULL;
+
+	wpa_hexdump_ascii_key(MSG_DEBUG, "PKCS #5: PBES2 password for PBKDF2",
+			      passwd, os_strlen(passwd));
+	wpa_hexdump(MSG_DEBUG, "PKCS #5: PBES2 salt for PBKDF2",
+		    params->salt, params->salt_len);
+	wpa_printf(MSG_DEBUG, "PKCS #5: PBES2 PBKDF2 iterations: %u",
+		   params->iter_count);
+	if (pbkdf2_sha1(passwd, params->salt, params->salt_len,
+			params->iter_count, key, sizeof(key)) < 0)
+		return NULL;
+	wpa_hexdump_key(MSG_DEBUG, "PKCS #5: DES EDE3 key", key, sizeof(key));
+	wpa_hexdump(MSG_DEBUG, "PKCS #5: DES IV", params->iv, params->iv_len);
+
+	return crypto_cipher_init(CRYPTO_CIPHER_ALG_3DES, params->iv,
+				  key, sizeof(key));
+}
+
+
+static void add_byte_array_mod(u8 *a, const u8 *b, size_t len)
+{
+	size_t i;
+	unsigned int carry = 0;
+
+	for (i = len - 1; i < len; i--) {
+		carry = carry + a[i] + b[i];
+		a[i] = carry & 0xff;
+		carry >>= 8;
+	}
+}
+
+
+static int pkcs12_key_gen(const u8 *pw, size_t pw_len, const u8 *salt,
+			  size_t salt_len, u8 id, unsigned int iter,
+			  size_t out_len, u8 *out)
+{
+	unsigned int u, v, S_len, P_len, i;
+	u8 *D = NULL, *I = NULL, *B = NULL, *pos;
+	int res = -1;
+
+	/* RFC 7292, B.2 */
+	u = SHA1_MAC_LEN;
+	v = 64;
+
+	/* D = copies of ID */
+	D = os_malloc(v);
+	if (!D)
+		goto done;
+	os_memset(D, id, v);
+
+	/* S = copies of salt; P = copies of password, I = S || P */
+	S_len = v * ((salt_len + v - 1) / v);
+	P_len = v * ((pw_len + v - 1) / v);
+	I = os_malloc(S_len + P_len);
+	if (!I)
+		goto done;
+	pos = I;
+	if (salt_len) {
+		for (i = 0; i < S_len; i++)
+			*pos++ = salt[i % salt_len];
+	}
+	if (pw_len) {
+		for (i = 0; i < P_len; i++)
+			*pos++ = pw[i % pw_len];
+	}
+
+	B = os_malloc(v);
+	if (!B)
+		goto done;
+
+	for (;;) {
+		u8 hash[SHA1_MAC_LEN];
+		const u8 *addr[2];
+		size_t len[2];
+
+		addr[0] = D;
+		len[0] = v;
+		addr[1] = I;
+		len[1] = S_len + P_len;
+		if (sha1_vector(2, addr, len, hash) < 0)
+			goto done;
+
+		addr[0] = hash;
+		len[0] = SHA1_MAC_LEN;
+		for (i = 1; i < iter; i++) {
+			if (sha1_vector(1, addr, len, hash) < 0)
+				goto done;
+		}
+
+		if (out_len <= u) {
+			os_memcpy(out, hash, out_len);
+			res = 0;
+			goto done;
+		}
+
+		os_memcpy(out, hash, u);
+		out += u;
+		out_len -= u;
+
+		/* I_j = (I_j + B + 1) mod 2^(v*8) */
+		/* B = copies of Ai (final hash value) */
+		for (i = 0; i < v; i++)
+			B[i] = hash[i % u];
+		inc_byte_array(B, v);
+		for (i = 0; i < S_len + P_len; i += v)
+			add_byte_array_mod(&I[i], B, v);
+	}
+
+done:
+	os_free(B);
+	os_free(I);
+	os_free(D);
+	return res;
+}
+
+
+#define PKCS12_ID_ENC 1
+#define PKCS12_ID_IV 2
+#define PKCS12_ID_MAC 3
+
+static struct crypto_cipher *
+pkcs12_crypto_init_sha1(struct pkcs5_params *params, const char *passwd)
+{
+	unsigned int i;
+	u8 *pw;
+	size_t pw_len;
+	u8 key[24];
+	u8 iv[8];
+
+	if (params->alg != PKCS5_ALG_SHA1_3DES_CBC)
+		return NULL;
+
+	pw_len = passwd ? os_strlen(passwd) : 0;
+	pw = os_malloc(2 * (pw_len + 1));
+	if (!pw)
+		return NULL;
+	if (pw_len) {
+		for (i = 0; i <= pw_len; i++)
+			WPA_PUT_BE16(&pw[2 * i], passwd[i]);
+		pw_len = 2 * (pw_len + 1);
+	}
+
+	if (pkcs12_key_gen(pw, pw_len, params->salt, params->salt_len,
+			   PKCS12_ID_ENC, params->iter_count,
+			   sizeof(key), key) < 0 ||
+	    pkcs12_key_gen(pw, pw_len, params->salt, params->salt_len,
+			   PKCS12_ID_IV, params->iter_count,
+			   sizeof(iv), iv) < 0) {
+		os_free(pw);
+		return NULL;
+	}
+
+	os_free(pw);
+
+	wpa_hexdump_key(MSG_DEBUG, "PKCS #12: DES key", key, sizeof(key));
+	wpa_hexdump_key(MSG_DEBUG, "PKCS #12: DES IV", iv, sizeof(iv));
+
+	return crypto_cipher_init(CRYPTO_CIPHER_ALG_3DES, iv, key, sizeof(key));
+}
+
+
 static struct crypto_cipher * pkcs5_crypto_init(struct pkcs5_params *params,
 						const char *passwd)
 {
@@ -144,6 +556,12 @@
 	const u8 *addr[2];
 	size_t len[2];
 
+	if (params->alg == PKCS5_ALG_PBES2)
+		return pkcs5_crypto_init_pbes2(params, passwd);
+
+	if (params->alg == PKCS5_ALG_SHA1_3DES_CBC)
+		return pkcs12_crypto_init_sha1(params, passwd);
+
 	if (params->alg != PKCS5_ALG_MD5_DES_CBC)
 		return NULL;
 
diff --git a/src/tls/tlsv1_client.c b/src/tls/tlsv1_client.c
index 533286c..9bc0d21 100644
--- a/src/tls/tlsv1_client.c
+++ b/src/tls/tlsv1_client.c
@@ -1,6 +1,6 @@
 /*
  * TLS v1.0/v1.1/v1.2 client (RFC 2246, RFC 4346, RFC 5246)
- * Copyright (c) 2006-2014, Jouni Malinen <j@w1.fi>
+ * 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.
@@ -11,6 +11,7 @@
 #include "common.h"
 #include "crypto/sha1.h"
 #include "crypto/tls.h"
+#include "x509v3.h"
 #include "tlsv1_common.h"
 #include "tlsv1_record.h"
 #include "tlsv1_client.h"
@@ -110,7 +111,6 @@
 		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
@@ -494,6 +494,7 @@
 	tlsv1_client_free_dh(conn);
 	tlsv1_cred_free(conn->cred);
 	wpabuf_free(conn->partial_input);
+	x509_certificate_chain_free(conn->server_cert);
 	os_free(conn);
 }
 
@@ -691,18 +692,16 @@
 	if (data == NULL || data_len == 0)
 		return 0;
 
-	pos = conn->client_hello_ext = os_malloc(6 + data_len);
+	pos = conn->client_hello_ext = os_malloc(4 + 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;
+	conn->client_hello_ext_len = 4 + data_len;
 
 	if (ext_type == TLS_EXT_PAC_OPAQUE) {
 		conn->session_ticket_included = 1;
@@ -714,12 +713,12 @@
 
 
 /**
- * tlsv1_client_get_keys - Get master key and random data from TLS connection
+ * tlsv1_client_get_random - Get random data from TLS connection
  * @conn: TLSv1 client connection data from tlsv1_client_init()
- * @keys: Structure of key/random data (filled on success)
+ * @keys: Structure of random data (filled on success)
  * Returns: 0 on success, -1 on failure
  */
-int tlsv1_client_get_keys(struct tlsv1_client *conn, struct tls_keys *keys)
+int tlsv1_client_get_random(struct tlsv1_client *conn, struct tls_random *keys)
 {
 	os_memset(keys, 0, sizeof(*keys));
 	if (conn->state == CLIENT_HELLO)
@@ -813,9 +812,14 @@
 }
 
 
-void tlsv1_client_set_time_checks(struct tlsv1_client *conn, int enabled)
+/**
+ * tlsv1_client_set_flags - Set connection flags
+ * @conn: TLSv1 client connection data from tlsv1_client_init()
+ * @flags: TLS_CONN_* bitfield
+ */
+void tlsv1_client_set_flags(struct tlsv1_client *conn, unsigned int flags)
 {
-	conn->disable_time_checks = !enabled;
+	conn->flags = flags;
 }
 
 
@@ -828,3 +832,38 @@
 	conn->session_ticket_cb = cb;
 	conn->session_ticket_cb_ctx = ctx;
 }
+
+
+void tlsv1_client_set_cb(struct tlsv1_client *conn,
+			 void (*event_cb)(void *ctx, enum tls_event ev,
+					  union tls_event_data *data),
+			 void *cb_ctx,
+			 int cert_in_cb)
+{
+	conn->event_cb = event_cb;
+	conn->cb_ctx = cb_ctx;
+	conn->cert_in_cb = !!cert_in_cb;
+}
+
+
+int tlsv1_client_get_version(struct tlsv1_client *conn, char *buf,
+			     size_t buflen)
+{
+	if (!conn)
+		return -1;
+	switch (conn->rl.tls_version) {
+	case TLS_VERSION_1:
+		os_strlcpy(buf, "TLSv1", buflen);
+		break;
+	case TLS_VERSION_1_1:
+		os_strlcpy(buf, "TLSv1.1", buflen);
+		break;
+	case TLS_VERSION_1_2:
+		os_strlcpy(buf, "TLSv1.2", buflen);
+		break;
+	default:
+		return -1;
+	}
+
+	return 0;
+}
diff --git a/src/tls/tlsv1_client.h b/src/tls/tlsv1_client.h
index 8ec85f1..40fa6c7 100644
--- a/src/tls/tlsv1_client.h
+++ b/src/tls/tlsv1_client.h
@@ -36,12 +36,12 @@
 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_keys(struct tlsv1_client *conn, struct tls_keys *keys);
+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);
+void tlsv1_client_set_flags(struct tlsv1_client *conn, unsigned int flags);
 
 typedef int (*tlsv1_client_session_ticket_cb)
 (void *ctx, const u8 *ticket, size_t len, const u8 *client_random,
@@ -51,4 +51,12 @@
 					tlsv1_client_session_ticket_cb cb,
 					void *ctx);
 
+void tlsv1_client_set_cb(struct tlsv1_client *conn,
+			 void (*event_cb)(void *ctx, enum tls_event ev,
+					  union tls_event_data *data),
+			 void *cb_ctx,
+			 int cert_in_cb);
+int tlsv1_client_get_version(struct tlsv1_client *conn, char *buf,
+			     size_t buflen);
+
 #endif /* TLSV1_CLIENT_H */
diff --git a/src/tls/tlsv1_client_i.h b/src/tls/tlsv1_client_i.h
index 55fdcf8..12ec8df 100644
--- a/src/tls/tlsv1_client_i.h
+++ b/src/tls/tlsv1_client_i.h
@@ -29,11 +29,14 @@
 	u8 alert_level;
 	u8 alert_description;
 
+	unsigned int flags; /* TLS_CONN_* bitfield */
+
 	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;
+	unsigned int cert_in_cb:1;
+	unsigned int ocsp_resp_received:1;
 
 	struct crypto_public_key *server_rsa_key;
 
@@ -64,6 +67,12 @@
 	void *session_ticket_cb_ctx;
 
 	struct wpabuf *partial_input;
+
+	void (*event_cb)(void *ctx, enum tls_event ev,
+			 union tls_event_data *data);
+	void *cb_ctx;
+
+	struct x509_certificate *server_cert;
 };
 
 
@@ -81,4 +90,11 @@
 				   const u8 *buf, size_t *len,
 				   u8 **out_data, size_t *out_len);
 
+enum tls_ocsp_result {
+	TLS_OCSP_NO_RESPONSE, TLS_OCSP_INVALID, TLS_OCSP_GOOD, TLS_OCSP_REVOKED
+};
+
+enum tls_ocsp_result tls_process_ocsp_response(struct tlsv1_client *conn,
+					       const u8 *resp, size_t len);
+
 #endif /* TLSV1_CLIENT_I_H */
diff --git a/src/tls/tlsv1_client_ocsp.c b/src/tls/tlsv1_client_ocsp.c
new file mode 100644
index 0000000..1d7b68c
--- /dev/null
+++ b/src/tls/tlsv1_client_ocsp.c
@@ -0,0 +1,803 @@
+/*
+ * TLSv1 client - OCSP
+ * 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 "includes.h"
+
+#include "common.h"
+#include "crypto/tls.h"
+#include "crypto/sha1.h"
+#include "asn1.h"
+#include "x509v3.h"
+#include "tlsv1_common.h"
+#include "tlsv1_record.h"
+#include "tlsv1_client.h"
+#include "tlsv1_client_i.h"
+
+
+/* RFC 6960, 4.2.1: OCSPResponseStatus ::= ENUMERATED */
+enum ocsp_response_status {
+	OCSP_RESP_STATUS_SUCCESSFUL = 0,
+	OCSP_RESP_STATUS_MALFORMED_REQ = 1,
+	OCSP_RESP_STATUS_INT_ERROR = 2,
+	OCSP_RESP_STATUS_TRY_LATER = 3,
+	/* 4 not used */
+	OCSP_RESP_STATUS_SIG_REQUIRED = 5,
+	OCSP_RESP_STATUS_UNAUTHORIZED = 6,
+};
+
+
+static int is_oid_basic_ocsp_resp(struct asn1_oid *oid)
+{
+	return oid->len == 10 &&
+		oid->oid[0] == 1 /* iso */ &&
+		oid->oid[1] == 3 /* identified-organization */ &&
+		oid->oid[2] == 6 /* dod */ &&
+		oid->oid[3] == 1 /* internet */ &&
+		oid->oid[4] == 5 /* security */ &&
+		oid->oid[5] == 5 /* mechanisms */ &&
+		oid->oid[6] == 7 /* id-pkix */ &&
+		oid->oid[7] == 48 /* id-ad */ &&
+		oid->oid[8] == 1 /* id-pkix-ocsp */ &&
+		oid->oid[9] == 1 /* id-pkix-ocsp-basic */;
+}
+
+
+static int ocsp_responder_id_match(struct x509_certificate *signer,
+				   struct x509_name *name, const u8 *key_hash)
+{
+	if (key_hash) {
+		u8 hash[SHA1_MAC_LEN];
+		const u8 *addr[1] = { signer->public_key };
+		size_t len[1] = { signer->public_key_len };
+
+		if (sha1_vector(1, addr, len, hash) < 0)
+			return 0;
+		return os_memcmp(hash, key_hash, SHA1_MAC_LEN) == 0;
+	}
+
+	return x509_name_compare(&signer->subject, name) == 0;
+}
+
+
+static unsigned int ocsp_hash_data(struct asn1_oid *alg, const u8 *data,
+				   size_t data_len, u8 *hash)
+{
+	const u8 *addr[1] = { data };
+	size_t len[1] = { data_len };
+	char buf[100];
+
+	if (x509_sha1_oid(alg)) {
+		if (sha1_vector(1, addr, len, hash) < 0)
+			return 0;
+		wpa_hexdump(MSG_MSGDUMP, "OCSP: Hash (SHA1)", hash, 20);
+		return 20;
+	}
+
+	if (x509_sha256_oid(alg)) {
+		if (sha256_vector(1, addr, len, hash) < 0)
+			return 0;
+		wpa_hexdump(MSG_MSGDUMP, "OCSP: Hash (SHA256)", hash, 32);
+		return 32;
+	}
+
+	if (x509_sha384_oid(alg)) {
+		if (sha384_vector(1, addr, len, hash) < 0)
+			return 0;
+		wpa_hexdump(MSG_MSGDUMP, "OCSP: Hash (SHA384)", hash, 48);
+		return 48;
+	}
+
+	if (x509_sha512_oid(alg)) {
+		if (sha512_vector(1, addr, len, hash) < 0)
+			return 0;
+		wpa_hexdump(MSG_MSGDUMP, "OCSP: Hash (SHA512)", hash, 64);
+		return 64;
+	}
+
+
+	asn1_oid_to_str(alg, buf, sizeof(buf));
+	wpa_printf(MSG_DEBUG, "OCSP: Could not calculate hash with alg %s",
+		   buf);
+	return 0;
+}
+
+
+static int tls_process_ocsp_single_response(struct tlsv1_client *conn,
+					    struct x509_certificate *cert,
+					    struct x509_certificate *issuer,
+					    const u8 *resp, size_t len,
+					    enum tls_ocsp_result *res)
+{
+	struct asn1_hdr hdr;
+	const u8 *pos, *end;
+	struct x509_algorithm_identifier alg;
+	const u8 *name_hash, *key_hash;
+	size_t name_hash_len, key_hash_len;
+	const u8 *serial_number;
+	size_t serial_number_len;
+	u8 hash[64];
+	unsigned int hash_len;
+	unsigned int cert_status;
+	os_time_t update;
+	struct os_time now;
+
+	wpa_hexdump(MSG_MSGDUMP, "OCSP: SingleResponse", resp, len);
+
+	/*
+	 * SingleResponse ::= SEQUENCE {
+	 *    certID                       CertID,
+	 *    certStatus                   CertStatus,
+	 *    thisUpdate                   GeneralizedTime,
+	 *    nextUpdate         [0]       EXPLICIT GeneralizedTime OPTIONAL,
+	 *    singleExtensions   [1]       EXPLICIT Extensions OPTIONAL }
+	 */
+
+	/* CertID ::= SEQUENCE */
+	if (asn1_get_next(resp, len, &hdr) < 0 ||
+	    hdr.class != ASN1_CLASS_UNIVERSAL ||
+	    hdr.tag != ASN1_TAG_SEQUENCE) {
+		wpa_printf(MSG_DEBUG,
+			   "OCSP: Expected SEQUENCE (CertID) - found class %d tag 0x%x",
+			   hdr.class, hdr.tag);
+		return -1;
+	}
+	pos = hdr.payload;
+	end = hdr.payload + hdr.length;
+
+	/*
+	 * CertID ::= SEQUENCE {
+	 *    hashAlgorithm           AlgorithmIdentifier,
+	 *    issuerNameHash          OCTET STRING,
+	 *    issuerKeyHash           OCTET STRING,
+	 *    serialNumber            CertificateSerialNumber }
+	 */
+
+	/* hashAlgorithm  AlgorithmIdentifier */
+	if (x509_parse_algorithm_identifier(pos, end - pos, &alg, &pos))
+		return -1;
+
+	/* issuerNameHash  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,
+			   "OCSP: Expected OCTET STRING (issuerNameHash) - found class %d tag 0x%x",
+			   hdr.class, hdr.tag);
+		return -1;
+	}
+	name_hash = hdr.payload;
+	name_hash_len = hdr.length;
+	wpa_hexdump(MSG_DEBUG, "OCSP: issuerNameHash",
+		    name_hash, name_hash_len);
+	pos = hdr.payload + hdr.length;
+
+	wpa_hexdump(MSG_DEBUG, "OCSP: Issuer subject DN",
+		    issuer->subject_dn, issuer->subject_dn_len);
+	hash_len = ocsp_hash_data(&alg.oid, issuer->subject_dn,
+				  issuer->subject_dn_len, hash);
+	if (hash_len == 0 || name_hash_len != hash_len ||
+	    os_memcmp(name_hash, hash, hash_len) != 0) {
+		wpa_printf(MSG_DEBUG, "OCSP: issuerNameHash mismatch");
+		wpa_hexdump(MSG_DEBUG, "OCSP: Calculated issuerNameHash",
+			    hash, hash_len);
+		return -1;
+	}
+
+	/* issuerKeyHash  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,
+			   "OCSP: Expected OCTET STRING (issuerKeyHash) - found class %d tag 0x%x",
+			   hdr.class, hdr.tag);
+		return -1;
+	}
+	key_hash = hdr.payload;
+	key_hash_len = hdr.length;
+	wpa_hexdump(MSG_DEBUG, "OCSP: issuerKeyHash", key_hash, key_hash_len);
+	pos = hdr.payload + hdr.length;
+
+	hash_len = ocsp_hash_data(&alg.oid, issuer->public_key,
+				  issuer->public_key_len, hash);
+	if (hash_len == 0 || key_hash_len != hash_len ||
+	    os_memcmp(key_hash, hash, hash_len) != 0) {
+		wpa_printf(MSG_DEBUG, "OCSP: issuerKeyHash mismatch");
+		wpa_hexdump(MSG_DEBUG, "OCSP: Calculated issuerKeyHash",
+			    hash, hash_len);
+		return -1;
+	}
+
+	/* serialNumber CertificateSerialNumber ::= INTEGER */
+	if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+	    hdr.class != ASN1_CLASS_UNIVERSAL ||
+	    hdr.tag != ASN1_TAG_INTEGER ||
+	    hdr.length < 1 || hdr.length > X509_MAX_SERIAL_NUM_LEN) {
+		wpa_printf(MSG_DEBUG, "OCSP: No INTEGER tag found for serialNumber; class=%d tag=0x%x length=%u",
+			   hdr.class, hdr.tag, hdr.length);
+		return -1;
+	}
+	serial_number = hdr.payload;
+	serial_number_len = hdr.length;
+	while (serial_number_len > 0 && serial_number[0] == 0) {
+		serial_number++;
+		serial_number_len--;
+	}
+	wpa_hexdump(MSG_MSGDUMP, "OCSP: serialNumber", serial_number,
+		    serial_number_len);
+
+	if (serial_number_len != cert->serial_number_len ||
+	    os_memcmp(serial_number, cert->serial_number,
+		      serial_number_len) != 0) {
+		wpa_printf(MSG_DEBUG, "OCSP: serialNumber mismatch");
+		return -1;
+	}
+
+	pos = end;
+	end = resp + len;
+
+	/* certStatus CertStatus ::= CHOICE */
+	if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+	    hdr.class != ASN1_CLASS_CONTEXT_SPECIFIC) {
+		wpa_printf(MSG_DEBUG,
+			   "OCSP: Expected CHOICE (CertStatus) - found class %d tag 0x%x",
+			   hdr.class, hdr.tag);
+		return -1;
+	}
+	cert_status = hdr.tag;
+	wpa_printf(MSG_DEBUG, "OCSP: certStatus=%u", cert_status);
+	wpa_hexdump(MSG_DEBUG, "OCSP: CertStatus additional data",
+		    hdr.payload, hdr.length);
+	pos = hdr.payload + hdr.length;
+
+	os_get_time(&now);
+	/* thisUpdate  GeneralizedTime */
+	if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+	    hdr.class != ASN1_CLASS_UNIVERSAL ||
+	    hdr.tag != ASN1_TAG_GENERALIZEDTIME ||
+	    x509_parse_time(hdr.payload, hdr.length, hdr.tag, &update) < 0) {
+		wpa_printf(MSG_DEBUG, "OCSP: Failed to parse thisUpdate");
+		return -1;
+	}
+	wpa_printf(MSG_DEBUG, "OCSP: thisUpdate %lu", (unsigned long) update);
+	pos = hdr.payload + hdr.length;
+	if ((unsigned long) now.sec < (unsigned long) update) {
+		wpa_printf(MSG_DEBUG,
+			   "OCSP: thisUpdate time in the future (response not yet valid)");
+		return -1;
+	}
+
+	/* nextUpdate  [0]  EXPLICIT GeneralizedTime OPTIONAL */
+	if (pos < end) {
+		if (asn1_get_next(pos, end - pos, &hdr) < 0)
+			return -1;
+		if (hdr.class == ASN1_CLASS_CONTEXT_SPECIFIC && hdr.tag == 0) {
+			const u8 *next = hdr.payload + hdr.length;
+
+			if (asn1_get_next(hdr.payload, hdr.length, &hdr) < 0 ||
+			    hdr.class != ASN1_CLASS_UNIVERSAL ||
+			    hdr.tag != ASN1_TAG_GENERALIZEDTIME ||
+			    x509_parse_time(hdr.payload, hdr.length, hdr.tag,
+					    &update) < 0) {
+				wpa_printf(MSG_DEBUG,
+					   "OCSP: Failed to parse nextUpdate");
+				return -1;
+			}
+			wpa_printf(MSG_DEBUG, "OCSP: nextUpdate %lu",
+				   (unsigned long) update);
+			pos = next;
+			if ((unsigned long) now.sec > (unsigned long) update) {
+				wpa_printf(MSG_DEBUG, "OCSP: nextUpdate time in the past (response has expired)");
+				return -1;
+			}
+		}
+	}
+
+	/* singleExtensions  [1]  EXPLICIT Extensions OPTIONAL */
+	if (pos < end) {
+		wpa_hexdump(MSG_MSGDUMP, "OCSP: singleExtensions",
+			    pos, end - pos);
+		/* Ignore for now */
+	}
+
+	if (cert_status == 0 /* good */)
+		*res = TLS_OCSP_GOOD;
+	else if (cert_status == 1 /* revoked */)
+		*res = TLS_OCSP_REVOKED;
+	else
+		return -1;
+	return 0;
+}
+
+
+static enum tls_ocsp_result
+tls_process_ocsp_responses(struct tlsv1_client *conn,
+			   struct x509_certificate *cert,
+			   struct x509_certificate *issuer, const u8 *resp,
+			   size_t len)
+{
+	struct asn1_hdr hdr;
+	const u8 *pos, *end;
+	enum tls_ocsp_result res;
+
+	pos = resp;
+	end = resp + len;
+	while (pos < end) {
+		/* SingleResponse ::= SEQUENCE */
+		if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+		    hdr.class != ASN1_CLASS_UNIVERSAL ||
+		    hdr.tag != ASN1_TAG_SEQUENCE) {
+			wpa_printf(MSG_DEBUG,
+				   "OCSP: Expected SEQUENCE (SingleResponse) - found class %d tag 0x%x",
+				   hdr.class, hdr.tag);
+			return TLS_OCSP_INVALID;
+		}
+		if (tls_process_ocsp_single_response(conn, cert, issuer,
+						     hdr.payload, hdr.length,
+						     &res) == 0)
+			return res;
+		pos = hdr.payload + hdr.length;
+	}
+
+	wpa_printf(MSG_DEBUG,
+		   "OCSP: Did not find a response matching the server certificate");
+	return TLS_OCSP_NO_RESPONSE;
+}
+
+
+static enum tls_ocsp_result
+tls_process_basic_ocsp_response(struct tlsv1_client *conn,
+				struct x509_certificate *srv_cert,
+				const u8 *resp, size_t len)
+{
+	struct asn1_hdr hdr;
+	const u8 *pos, *end;
+	const u8 *resp_data, *sign_value, *key_hash = NULL, *responses;
+	const u8 *resp_data_signed;
+	size_t resp_data_len, sign_value_len, responses_len;
+	size_t resp_data_signed_len;
+	struct x509_algorithm_identifier alg;
+	struct x509_certificate *certs = NULL, *last_cert = NULL;
+	struct x509_certificate *issuer, *signer;
+	struct x509_name name; /* used if key_hash == NULL */
+	char buf[100];
+	os_time_t produced_at;
+	enum tls_ocsp_result res;
+
+	wpa_hexdump(MSG_MSGDUMP, "OCSP: BasicOCSPResponse", resp, len);
+
+	os_memset(&name, 0, sizeof(name));
+
+	/*
+	 * RFC 6960, 4.2.1:
+	 * BasicOCSPResponse       ::= SEQUENCE {
+	 *    tbsResponseData      ResponseData,
+	 *    signatureAlgorithm   AlgorithmIdentifier,
+	 *    signature            BIT STRING,
+	 *    certs            [0] EXPLICIT SEQUENCE OF Certificate OPTIONAL }
+	 */
+
+	if (asn1_get_next(resp, len, &hdr) < 0 ||
+	    hdr.class != ASN1_CLASS_UNIVERSAL ||
+	    hdr.tag != ASN1_TAG_SEQUENCE) {
+		wpa_printf(MSG_DEBUG,
+			   "OCSP: Expected SEQUENCE (BasicOCSPResponse) - found class %d tag 0x%x",
+			   hdr.class, hdr.tag);
+		return TLS_OCSP_INVALID;
+	}
+	pos = hdr.payload;
+	end = hdr.payload + hdr.length;
+
+	/* ResponseData ::= SEQUENCE */
+	if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+	    hdr.class != ASN1_CLASS_UNIVERSAL ||
+	    hdr.tag != ASN1_TAG_SEQUENCE) {
+		wpa_printf(MSG_DEBUG,
+			   "OCSP: Expected SEQUENCE (ResponseData) - found class %d tag 0x%x",
+			   hdr.class, hdr.tag);
+		return TLS_OCSP_INVALID;
+	}
+	resp_data = hdr.payload;
+	resp_data_len = hdr.length;
+	resp_data_signed = pos;
+	pos = hdr.payload + hdr.length;
+	resp_data_signed_len = pos - resp_data_signed;
+
+	/* signatureAlgorithm  AlgorithmIdentifier */
+	if (x509_parse_algorithm_identifier(pos, end - pos, &alg, &pos))
+		return TLS_OCSP_INVALID;
+
+	/* signature  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,
+			   "OCSP: Expected BITSTRING (signature) - found class %d tag 0x%x",
+			   hdr.class, hdr.tag);
+		return TLS_OCSP_INVALID;
+	}
+	if (hdr.length < 1)
+		return TLS_OCSP_INVALID;
+	pos = hdr.payload;
+	if (*pos) {
+		wpa_printf(MSG_DEBUG, "OCSP: 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.
+		 */
+		return TLS_OCSP_INVALID;
+	}
+	sign_value = pos + 1;
+	sign_value_len = hdr.length - 1;
+	pos += hdr.length;
+	wpa_hexdump(MSG_MSGDUMP, "OCSP: signature", sign_value, sign_value_len);
+
+	/* certs  [0] EXPLICIT SEQUENCE OF Certificate OPTIONAL */
+	if (pos < end) {
+		if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+		    hdr.class != ASN1_CLASS_CONTEXT_SPECIFIC ||
+		    hdr.tag != 0) {
+			wpa_printf(MSG_DEBUG,
+				   "OCSP: Expected [0] EXPLICIT (certs) - found class %d tag 0x%x",
+				   hdr.class, hdr.tag);
+			return TLS_OCSP_INVALID;
+		}
+		wpa_hexdump(MSG_MSGDUMP, "OCSP: certs",
+			    hdr.payload, hdr.length);
+		pos = hdr.payload;
+		end = hdr.payload + hdr.length;
+		while (pos < end) {
+			struct x509_certificate *cert;
+
+			if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+			    hdr.class != ASN1_CLASS_UNIVERSAL ||
+			    hdr.tag != ASN1_TAG_SEQUENCE) {
+				wpa_printf(MSG_DEBUG,
+					   "OCSP: Expected SEQUENCE (Certificate) - found class %d tag 0x%x",
+					   hdr.class, hdr.tag);
+				goto fail;
+			}
+
+			cert = x509_certificate_parse(hdr.payload, hdr.length);
+			if (!cert)
+				goto fail;
+			if (last_cert) {
+				last_cert->next = cert;
+				last_cert = cert;
+			} else {
+				last_cert = certs = cert;
+			}
+			pos = hdr.payload + hdr.length;
+		}
+	}
+
+	/*
+	 * ResponseData ::= SEQUENCE {
+	 *    version              [0] EXPLICIT Version DEFAULT v1,
+	 *    responderID              ResponderID,
+	 *    producedAt               GeneralizedTime,
+	 *    responses                SEQUENCE OF SingleResponse,
+	 *    responseExtensions   [1] EXPLICIT Extensions OPTIONAL }
+	 */
+	pos = resp_data;
+	end = resp_data + resp_data_len;
+	wpa_hexdump(MSG_MSGDUMP, "OCSP: ResponseData", pos, end - pos);
+
+	/*
+	 * version [0] EXPLICIT Version DEFAULT v1
+	 * Version ::= INTEGER { v1(0) }
+	 */
+	if (asn1_get_next(pos, end - pos, &hdr) < 0 &&
+	    hdr.class == ASN1_CLASS_CONTEXT_SPECIFIC &&
+	    hdr.tag == 0) {
+		if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+		    hdr.class != ASN1_CLASS_UNIVERSAL ||
+		    hdr.tag != ASN1_TAG_INTEGER ||
+		    hdr.length != 1) {
+			wpa_printf(MSG_DEBUG,
+				   "OCSP: No INTEGER (len=1) tag found for version field - found class %d tag 0x%x length %d",
+				   hdr.class, hdr.tag, hdr.length);
+			goto fail;
+		}
+		wpa_printf(MSG_DEBUG, "OCSP: ResponseData version %u",
+			   hdr.payload[0]);
+		if (hdr.payload[0] != 0) {
+			wpa_printf(MSG_DEBUG,
+				   "OCSP: Unsupported ResponseData version %u",
+				   hdr.payload[0]);
+			goto no_resp;
+		}
+		pos = hdr.payload + hdr.length;
+	} else {
+		wpa_printf(MSG_DEBUG,
+			   "OCSP: Default ResponseData version (v1)");
+	}
+
+	/*
+	 * ResponderID ::= CHOICE {
+	 *    byName              [1] Name,
+	 *    byKey               [2] KeyHash }
+	 */
+	if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+	    hdr.class != ASN1_CLASS_CONTEXT_SPECIFIC) {
+		wpa_printf(MSG_DEBUG,
+			   "OCSP: Expected CHOICE (ResponderID) - found class %d tag 0x%x",
+			   hdr.class, hdr.tag);
+		goto fail;
+	}
+
+	if (hdr.tag == 1) {
+		/* Name */
+		if (x509_parse_name(hdr.payload, hdr.length, &name, &pos) < 0)
+			goto fail;
+		x509_name_string(&name, buf, sizeof(buf));
+		wpa_printf(MSG_DEBUG, "OCSP: ResponderID byName Name: %s", buf);
+	} else if (hdr.tag == 2) {
+		/* KeyHash ::= OCTET STRING */
+		if (asn1_get_next(hdr.payload, hdr.length, &hdr) < 0 ||
+		    hdr.class != ASN1_CLASS_UNIVERSAL ||
+		    hdr.tag != ASN1_TAG_OCTETSTRING) {
+			wpa_printf(MSG_DEBUG,
+				   "OCSP: Expected OCTET STRING (KeyHash) - found class %d tag 0x%x",
+				   hdr.class, hdr.tag);
+			goto fail;
+		}
+		key_hash = hdr.payload;
+		wpa_hexdump(MSG_DEBUG, "OCSP: ResponderID byKey KeyHash",
+			    key_hash, hdr.length);
+		if (hdr.length != SHA1_MAC_LEN) {
+			wpa_printf(MSG_DEBUG,
+				   "OCSP: Unexpected byKey KeyHash length %u - expected %u for SHA-1",
+				   hdr.length, SHA1_MAC_LEN);
+			goto fail;
+		}
+		pos = hdr.payload + hdr.length;
+	} else {
+		wpa_printf(MSG_DEBUG, "OCSP: Unexpected ResponderID CHOICE %u",
+			   hdr.tag);
+		goto fail;
+	}
+
+	/* producedAt  GeneralizedTime */
+	if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+	    hdr.class != ASN1_CLASS_UNIVERSAL ||
+	    hdr.tag != ASN1_TAG_GENERALIZEDTIME ||
+	    x509_parse_time(hdr.payload, hdr.length, hdr.tag,
+			    &produced_at) < 0) {
+		wpa_printf(MSG_DEBUG, "OCSP: Failed to parse producedAt");
+		goto fail;
+	}
+	wpa_printf(MSG_DEBUG, "OCSP: producedAt %lu",
+		   (unsigned long) produced_at);
+	pos = hdr.payload + hdr.length;
+
+	/* responses  SEQUENCE OF SingleResponse */
+	if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+	    hdr.class != ASN1_CLASS_UNIVERSAL ||
+	    hdr.tag != ASN1_TAG_SEQUENCE) {
+		wpa_printf(MSG_DEBUG,
+			   "OCSP: Expected SEQUENCE (responses) - found class %d tag 0x%x",
+			   hdr.class, hdr.tag);
+		goto fail;
+	}
+	responses = hdr.payload;
+	responses_len = hdr.length;
+	wpa_hexdump(MSG_MSGDUMP, "OCSP: responses", responses, responses_len);
+	pos = hdr.payload + hdr.length;
+
+	if (pos < end) {
+		/* responseExtensions  [1] EXPLICIT Extensions OPTIONAL */
+		wpa_hexdump(MSG_MSGDUMP, "OCSP: responseExtensions",
+			    pos, end - pos);
+		/* Ignore for now. */
+	}
+
+	if (!srv_cert) {
+		wpa_printf(MSG_DEBUG,
+			   "OCSP: Server certificate not known - cannot check OCSP response");
+		goto no_resp;
+	}
+
+	if (srv_cert->next) {
+		/* Issuer has already been verified in the chain */
+		issuer = srv_cert->next;
+	} else {
+		/* Find issuer from the set of trusted certificates */
+		for (issuer = conn->cred ? conn->cred->trusted_certs : NULL;
+		     issuer; issuer = issuer->next) {
+			if (x509_name_compare(&srv_cert->issuer,
+					      &issuer->subject) == 0)
+				break;
+		}
+	}
+	if (!issuer) {
+		wpa_printf(MSG_DEBUG,
+			   "OCSP: Server certificate issuer not known - cannot check OCSP response");
+		goto no_resp;
+	}
+
+	if (ocsp_responder_id_match(issuer, &name, key_hash)) {
+		wpa_printf(MSG_DEBUG,
+			   "OCSP: Server certificate issuer certificate matches ResponderID");
+		signer = issuer;
+	} else {
+		for (signer = certs; signer; signer = signer->next) {
+			if (!ocsp_responder_id_match(signer, &name, key_hash) ||
+			    x509_name_compare(&srv_cert->issuer,
+					      &issuer->subject) != 0 ||
+			    !(signer->ext_key_usage &
+			      X509_EXT_KEY_USAGE_OCSP) ||
+			    x509_certificate_check_signature(issuer, signer) <
+			    0)
+				continue;
+			wpa_printf(MSG_DEBUG,
+				   "OCSP: An extra certificate from the response matches ResponderID and is trusted as an OCSP signer");
+			break;
+		}
+		if (!signer) {
+			wpa_printf(MSG_DEBUG,
+				   "OCSP: Could not find OCSP signer certificate");
+			goto no_resp;
+		}
+	}
+
+	x509_free_name(&name);
+	os_memset(&name, 0, sizeof(name));
+	x509_certificate_chain_free(certs);
+	certs = NULL;
+
+	if (x509_check_signature(signer, &alg, sign_value, sign_value_len,
+				 resp_data_signed, resp_data_signed_len) < 0) {
+		    wpa_printf(MSG_DEBUG, "OCSP: Invalid signature");
+		    return TLS_OCSP_INVALID;
+	}
+
+	res = tls_process_ocsp_responses(conn, srv_cert, issuer,
+					 responses, responses_len);
+	if (res == TLS_OCSP_REVOKED)
+		srv_cert->ocsp_revoked = 1;
+	else if (res == TLS_OCSP_GOOD)
+		srv_cert->ocsp_good = 1;
+	return res;
+
+no_resp:
+	x509_free_name(&name);
+	x509_certificate_chain_free(certs);
+	return TLS_OCSP_NO_RESPONSE;
+
+fail:
+	x509_free_name(&name);
+	x509_certificate_chain_free(certs);
+	return TLS_OCSP_INVALID;
+}
+
+
+enum tls_ocsp_result tls_process_ocsp_response(struct tlsv1_client *conn,
+					       const u8 *resp, size_t len)
+{
+	struct asn1_hdr hdr;
+	const u8 *pos, *end;
+	u8 resp_status;
+	struct asn1_oid oid;
+	char obuf[80];
+	struct x509_certificate *cert;
+	enum tls_ocsp_result res = TLS_OCSP_NO_RESPONSE;
+	enum tls_ocsp_result res_first = res;
+
+	wpa_hexdump(MSG_MSGDUMP, "TLSv1: OCSPResponse", resp, len);
+
+	/*
+	 * RFC 6960, 4.2.1:
+	 * OCSPResponse ::= SEQUENCE {
+	 *    responseStatus  OCSPResponseStatus,
+	 *    responseBytes   [0] EXPLICIT ResponseBytes OPTIONAL }
+	 */
+
+	if (asn1_get_next(resp, len, &hdr) < 0 ||
+	    hdr.class != ASN1_CLASS_UNIVERSAL ||
+	    hdr.tag != ASN1_TAG_SEQUENCE) {
+		wpa_printf(MSG_DEBUG,
+			   "OCSP: Expected SEQUENCE (OCSPResponse) - found class %d tag 0x%x",
+			   hdr.class, hdr.tag);
+		return TLS_OCSP_INVALID;
+	}
+	pos = hdr.payload;
+	end = hdr.payload + hdr.length;
+
+	/* OCSPResponseStatus ::= ENUMERATED */
+	if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+	    hdr.class != ASN1_CLASS_UNIVERSAL ||
+	    hdr.tag != ASN1_TAG_ENUMERATED ||
+	    hdr.length != 1) {
+		wpa_printf(MSG_DEBUG,
+			   "OCSP: Expected ENUMERATED (responseStatus) - found class %d tag 0x%x length %u",
+			   hdr.class, hdr.tag, hdr.length);
+		return TLS_OCSP_INVALID;
+	}
+	resp_status = hdr.payload[0];
+	wpa_printf(MSG_DEBUG, "OCSP: responseStatus %u", resp_status);
+	pos = hdr.payload + hdr.length;
+	if (resp_status != OCSP_RESP_STATUS_SUCCESSFUL) {
+		wpa_printf(MSG_DEBUG, "OCSP: No stapling result");
+		return TLS_OCSP_NO_RESPONSE;
+	}
+
+	/* responseBytes   [0] EXPLICIT ResponseBytes OPTIONAL */
+	if (pos == end)
+		return TLS_OCSP_NO_RESPONSE;
+
+	if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+	    hdr.class != ASN1_CLASS_CONTEXT_SPECIFIC ||
+	    hdr.tag != 0) {
+		wpa_printf(MSG_DEBUG,
+			   "OCSP: Expected [0] EXPLICIT (responseBytes) - found class %d tag 0x%x",
+			   hdr.class, hdr.tag);
+		return TLS_OCSP_INVALID;
+	}
+
+	/*
+	 * ResponseBytes ::= SEQUENCE {
+	 *     responseType   OBJECT IDENTIFIER,
+	 *     response       OCTET STRING }
+	 */
+
+	if (asn1_get_next(hdr.payload, hdr.length, &hdr) < 0 ||
+	    hdr.class != ASN1_CLASS_UNIVERSAL ||
+	    hdr.tag != ASN1_TAG_SEQUENCE) {
+		wpa_printf(MSG_DEBUG,
+			   "OCSP: Expected SEQUENCE (ResponseBytes) - found class %d tag 0x%x",
+			   hdr.class, hdr.tag);
+		return TLS_OCSP_INVALID;
+	}
+	pos = hdr.payload;
+	end = hdr.payload + hdr.length;
+
+	/* responseType   OBJECT IDENTIFIER */
+	if (asn1_get_oid(pos, end - pos, &oid, &pos)) {
+		wpa_printf(MSG_DEBUG,
+			   "OCSP: Failed to parse OID (responseType)");
+		return TLS_OCSP_INVALID;
+	}
+	asn1_oid_to_str(&oid, obuf, sizeof(obuf));
+	wpa_printf(MSG_DEBUG, "OCSP: responseType %s", obuf);
+	if (!is_oid_basic_ocsp_resp(&oid)) {
+		wpa_printf(MSG_DEBUG, "OCSP: Ignore unsupported response type");
+		return TLS_OCSP_NO_RESPONSE;
+	}
+
+	/* response       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,
+			   "OCSP: Expected OCTET STRING (response) - found class %d tag 0x%x",
+			   hdr.class, hdr.tag);
+		return TLS_OCSP_INVALID;
+	}
+
+	cert = conn->server_cert;
+	while (cert) {
+		if (!cert->ocsp_good && !cert->ocsp_revoked) {
+			char sbuf[128];
+
+			x509_name_string(&cert->subject, sbuf, sizeof(sbuf));
+			wpa_printf(MSG_DEBUG,
+				   "OCSP: Trying to find certificate status for %s",
+				   sbuf);
+
+			res = tls_process_basic_ocsp_response(conn, cert,
+							      hdr.payload,
+							      hdr.length);
+			if (cert == conn->server_cert)
+				res_first = res;
+		}
+		if (res == TLS_OCSP_REVOKED || cert->issuer_trusted)
+			break;
+		cert = cert->next;
+	}
+	return res == TLS_OCSP_REVOKED ? res : res_first;
+}
diff --git a/src/tls/tlsv1_client_read.c b/src/tls/tlsv1_client_read.c
index 9ce9680..244c3cb 100644
--- a/src/tls/tlsv1_client_read.c
+++ b/src/tls/tlsv1_client_read.c
@@ -1,6 +1,6 @@
 /*
  * TLSv1 client - read handshake message
- * Copyright (c) 2006-2014, Jouni Malinen <j@w1.fi>
+ * 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.
@@ -27,6 +27,54 @@
 					 const u8 *in_data, size_t *in_len);
 
 
+static int tls_version_disabled(struct tlsv1_client *conn, u16 ver)
+{
+	return (((conn->flags & TLS_CONN_DISABLE_TLSv1_0) &&
+		 ver == TLS_VERSION_1) ||
+		((conn->flags & TLS_CONN_DISABLE_TLSv1_1) &&
+		 ver == TLS_VERSION_1_1) ||
+		((conn->flags & TLS_CONN_DISABLE_TLSv1_2) &&
+		 ver == TLS_VERSION_1_2));
+}
+
+
+static int tls_process_server_hello_extensions(struct tlsv1_client *conn,
+					       const u8 *pos, size_t len)
+{
+	const u8 *end = pos + len;
+
+	wpa_hexdump(MSG_MSGDUMP, "TLSv1: ServerHello extensions",
+		    pos, len);
+	while (pos < end) {
+		u16 ext, elen;
+
+		if (end - pos < 4) {
+			wpa_printf(MSG_INFO, "TLSv1: Truncated ServerHello extension header");
+			return -1;
+		}
+
+		ext = WPA_GET_BE16(pos);
+		pos += 2;
+		elen = WPA_GET_BE16(pos);
+		pos += 2;
+
+		if (elen > end - pos) {
+			wpa_printf(MSG_INFO, "TLSv1: Truncated ServerHello extension");
+			return -1;
+		}
+
+		wpa_printf(MSG_DEBUG, "TLSv1: ServerHello ExtensionType %u",
+			   ext);
+		wpa_hexdump(MSG_DEBUG, "TLSv1: ServerHello extension data",
+			    pos, elen);
+
+		pos += elen;
+	}
+
+	return 0;
+}
+
+
 static int tls_process_server_hello(struct tlsv1_client *conn, u8 ct,
 				    const u8 *in_data, size_t *in_len)
 {
@@ -76,7 +124,8 @@
 	if (end - pos < 2)
 		goto decode_error;
 	tls_version = WPA_GET_BE16(pos);
-	if (!tls_version_ok(tls_version)) {
+	if (!tls_version_ok(tls_version) ||
+	    tls_version_disabled(conn, 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,
@@ -165,8 +214,24 @@
 	}
 	pos++;
 
+	if (end - pos >= 2) {
+		u16 ext_len;
+
+		ext_len = WPA_GET_BE16(pos);
+		pos += 2;
+		if (end - pos < ext_len) {
+			wpa_printf(MSG_INFO,
+				   "TLSv1: Invalid ServerHello extension length: %u (left: %u)",
+				   ext_len, (unsigned int) (end - pos));
+			goto decode_error;
+		}
+
+		if (tls_process_server_hello_extensions(conn, pos, ext_len))
+			goto decode_error;
+		pos += ext_len;
+	}
+
 	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;
@@ -211,6 +276,73 @@
 }
 
 
+static void tls_peer_cert_event(struct tlsv1_client *conn, int depth,
+				struct x509_certificate *cert)
+{
+	union tls_event_data ev;
+	struct wpabuf *cert_buf = NULL;
+#ifdef CONFIG_SHA256
+	u8 hash[32];
+#endif /* CONFIG_SHA256 */
+	char subject[128];
+
+	if (!conn->event_cb)
+		return;
+
+	os_memset(&ev, 0, sizeof(ev));
+	if (conn->cred->cert_probe || conn->cert_in_cb) {
+		cert_buf = wpabuf_alloc_copy(cert->cert_start,
+					     cert->cert_len);
+		ev.peer_cert.cert = cert_buf;
+	}
+#ifdef CONFIG_SHA256
+	if (cert_buf) {
+		const u8 *addr[1];
+		size_t len[1];
+		addr[0] = wpabuf_head(cert_buf);
+		len[0] = wpabuf_len(cert_buf);
+		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;
+	x509_name_string(&cert->subject, subject, sizeof(subject));
+	ev.peer_cert.subject = subject;
+
+	conn->event_cb(conn->cb_ctx, TLS_PEER_CERTIFICATE, &ev);
+	wpabuf_free(cert_buf);
+}
+
+
+static void tls_cert_chain_failure_event(struct tlsv1_client *conn, int depth,
+					 struct x509_certificate *cert,
+					 enum tls_fail_reason reason,
+					 const char *reason_txt)
+{
+	struct wpabuf *cert_buf = NULL;
+	union tls_event_data ev;
+	char subject[128];
+
+	if (!conn->event_cb || !cert)
+		return;
+
+	os_memset(&ev, 0, sizeof(ev));
+	ev.cert_fail.depth = depth;
+	x509_name_string(&cert->subject, subject, sizeof(subject));
+	ev.peer_cert.subject = subject;
+	ev.cert_fail.reason = reason;
+	ev.cert_fail.reason_txt = reason_txt;
+	cert_buf = wpabuf_alloc_copy(cert->cert_start,
+				     cert->cert_len);
+	ev.cert_fail.cert = cert_buf;
+	conn->event_cb(conn->cb_ctx, TLS_CERT_CHAIN_FAILURE, &ev);
+	wpabuf_free(cert_buf);
+}
+
+
 static int tls_process_certificate(struct tlsv1_client *conn, u8 ct,
 				   const u8 *in_data, size_t *in_len)
 {
@@ -354,6 +486,8 @@
 			return -1;
 		}
 
+		tls_peer_cert_event(conn, idx, cert);
+
 		if (last == NULL)
 			chain = cert;
 		else
@@ -364,31 +498,99 @@
 		pos += cert_len;
 	}
 
-	if (conn->cred &&
-	    x509_certificate_chain_validate(conn->cred->trusted_certs, chain,
-					    &reason, conn->disable_time_checks)
-	    < 0) {
+	if (conn->cred && conn->cred->server_cert_only && chain) {
+		u8 hash[SHA256_MAC_LEN];
+		char buf[128];
+
+		wpa_printf(MSG_DEBUG,
+			   "TLSv1: Validate server certificate hash");
+		x509_name_string(&chain->subject, buf, sizeof(buf));
+		wpa_printf(MSG_DEBUG, "TLSv1: 0: %s", buf);
+		if (sha256_vector(1, &chain->cert_start, &chain->cert_len,
+				  hash) < 0 ||
+		    os_memcmp(conn->cred->srv_cert_hash, hash,
+			      SHA256_MAC_LEN) != 0) {
+			wpa_printf(MSG_DEBUG,
+				   "TLSv1: Server certificate hash mismatch");
+			wpa_hexdump(MSG_MSGDUMP, "TLSv1: SHA256 hash",
+				    hash, SHA256_MAC_LEN);
+			if (conn->event_cb) {
+				union tls_event_data ev;
+
+				os_memset(&ev, 0, sizeof(ev));
+				ev.cert_fail.reason = TLS_FAIL_UNSPECIFIED;
+				ev.cert_fail.reason_txt =
+					"Server certificate mismatch";
+				ev.cert_fail.subject = buf;
+				conn->event_cb(conn->cb_ctx,
+					       TLS_CERT_CHAIN_FAILURE, &ev);
+			}
+			tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+				  TLS_ALERT_BAD_CERTIFICATE);
+			x509_certificate_chain_free(chain);
+			return -1;
+		}
+	} else if (conn->cred && conn->cred->cert_probe) {
+		wpa_printf(MSG_DEBUG,
+			   "TLSv1: Reject server certificate on probe-only rune");
+		if (conn->event_cb) {
+			union tls_event_data ev;
+			char buf[128];
+
+			os_memset(&ev, 0, sizeof(ev));
+			ev.cert_fail.reason = TLS_FAIL_SERVER_CHAIN_PROBE;
+			ev.cert_fail.reason_txt =
+				"Server certificate chain probe";
+			if (chain) {
+				x509_name_string(&chain->subject, buf,
+						 sizeof(buf));
+				ev.cert_fail.subject = buf;
+			}
+			conn->event_cb(conn->cb_ctx, TLS_CERT_CHAIN_FAILURE,
+				       &ev);
+		}
+		tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+			  TLS_ALERT_BAD_CERTIFICATE);
+		x509_certificate_chain_free(chain);
+		return -1;
+	} else if (conn->cred && conn->cred->ca_cert_verify &&
+		   x509_certificate_chain_validate(
+			   conn->cred->trusted_certs, chain, &reason,
+			   !!(conn->flags & TLS_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;
+			tls_cert_chain_failure_event(
+				conn, 0, chain, TLS_FAIL_BAD_CERTIFICATE,
+				"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;
+			tls_cert_chain_failure_event(
+				conn, 0, chain, TLS_FAIL_REVOKED,
+				"certificate revoked");
 			break;
 		case X509_VALIDATE_CERTIFICATE_EXPIRED:
 			tls_reason = TLS_ALERT_CERTIFICATE_EXPIRED;
+			tls_cert_chain_failure_event(
+				conn, 0, chain, TLS_FAIL_EXPIRED,
+				"certificate has expired or is not yet valid");
 			break;
 		case X509_VALIDATE_CERTIFICATE_UNKNOWN:
 			tls_reason = TLS_ALERT_CERTIFICATE_UNKNOWN;
 			break;
 		case X509_VALIDATE_UNKNOWN_CA:
 			tls_reason = TLS_ALERT_UNKNOWN_CA;
+			tls_cert_chain_failure_event(
+				conn, 0, chain, TLS_FAIL_UNTRUSTED,
+				"unknown CA");
 			break;
 		default:
 			tls_reason = TLS_ALERT_BAD_CERTIFICATE;
@@ -399,7 +601,25 @@
 		return -1;
 	}
 
-	x509_certificate_chain_free(chain);
+	if (conn->cred && !conn->cred->server_cert_only && chain &&
+	    (chain->extensions_present & X509_EXT_EXT_KEY_USAGE) &&
+	    !(chain->ext_key_usage &
+	      (X509_EXT_KEY_USAGE_ANY | X509_EXT_KEY_USAGE_SERVER_AUTH))) {
+		tls_cert_chain_failure_event(
+			conn, 0, chain, TLS_FAIL_BAD_CERTIFICATE,
+			"certificate not allowed for server authentication");
+		tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+			  TLS_ALERT_BAD_CERTIFICATE);
+		x509_certificate_chain_free(chain);
+		return -1;
+	}
+
+	if (conn->flags & TLS_CONN_REQUEST_OCSP) {
+		x509_certificate_chain_free(conn->server_cert);
+		conn->server_cert = chain;
+	} else {
+		x509_certificate_chain_free(chain);
+	}
 
 	*in_len = end - in_data;
 
@@ -507,7 +727,7 @@
 	server_params_end = pos;
 
 	if (key_exchange == TLS_KEY_X_DHE_RSA) {
-		u8 hash[MD5_MAC_LEN + SHA1_MAC_LEN];
+		u8 hash[64];
 		int hlen;
 
 		if (conn->rl.tls_version == TLS_VERSION_1_2) {
@@ -524,18 +744,21 @@
 			 */
 			if (end - pos < 2)
 				goto fail;
-			if (pos[0] != TLS_HASH_ALG_SHA256 ||
+			if ((pos[0] != TLS_HASH_ALG_SHA256 &&
+			     pos[0] != TLS_HASH_ALG_SHA384 &&
+			     pos[0] != TLS_HASH_ALG_SHA512) ||
 			    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->rl.tls_version, pos[0],
+				conn->client_random,
 				conn->server_random, server_params,
 				server_params_end - server_params, hash);
+			pos += 2;
 #else /* CONFIG_TLSV12 */
 			goto fail;
 #endif /* CONFIG_TLSV12 */
@@ -567,6 +790,229 @@
 }
 
 
+static enum tls_ocsp_result
+tls_process_certificate_status_ocsp_response(struct tlsv1_client *conn,
+					     const u8 *pos, size_t len)
+{
+	const u8 *end = pos + len;
+	u32 ocsp_resp_len;
+
+	/* opaque OCSPResponse<1..2^24-1>; */
+	if (end - pos < 3) {
+		wpa_printf(MSG_INFO, "TLSv1: Too short OCSPResponse");
+		tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR);
+		return TLS_OCSP_INVALID;
+	}
+	ocsp_resp_len = WPA_GET_BE24(pos);
+	pos += 3;
+	if (end - pos < ocsp_resp_len) {
+		wpa_printf(MSG_INFO, "TLSv1: Truncated OCSPResponse");
+		tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR);
+		return TLS_OCSP_INVALID;
+	}
+
+	return tls_process_ocsp_response(conn, pos, ocsp_resp_len);
+}
+
+
+static int tls_process_certificate_status(struct tlsv1_client *conn, u8 ct,
+					   const u8 *in_data, size_t *in_len)
+{
+	const u8 *pos, *end;
+	size_t left, len;
+	u8 type, status_type;
+	enum tls_ocsp_result res;
+	struct x509_certificate *cert;
+	int depth;
+
+	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 CertificateStatus (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 CertificateStatus 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_STATUS) {
+		wpa_printf(MSG_DEBUG,
+			   "TLSv1: Received unexpected handshake message %d (expected CertificateStatus)",
+			   type);
+		tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+			  TLS_ALERT_UNEXPECTED_MESSAGE);
+		return -1;
+	}
+
+	wpa_printf(MSG_DEBUG, "TLSv1: Received CertificateStatus");
+
+	/*
+	 * struct {
+	 *     CertificateStatusType status_type;
+	 *     select (status_type) {
+	 *         case ocsp: OCSPResponse;
+	 *         case ocsp_multi: OCSPResponseList;
+	 *     } response;
+	 * } CertificateStatus;
+	 */
+	if (end - pos < 1) {
+		wpa_printf(MSG_INFO, "TLSv1: Too short CertificateStatus");
+		tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR);
+		return -1;
+	}
+	status_type = *pos++;
+	wpa_printf(MSG_DEBUG, "TLSv1: CertificateStatus status_type %u",
+		   status_type);
+
+	if (status_type == 1 /* ocsp */) {
+		res = tls_process_certificate_status_ocsp_response(
+			conn, pos, end - pos);
+	} else if (status_type == 2 /* ocsp_multi */) {
+		int good = 0, revoked = 0;
+		u32 resp_len;
+
+		res = TLS_OCSP_NO_RESPONSE;
+
+		/*
+		 * opaque OCSPResponse<0..2^24-1>;
+		 *
+		 * struct {
+		 *   OCSPResponse ocsp_response_list<1..2^24-1>;
+		 * } OCSPResponseList;
+		 */
+		if (end - pos < 3) {
+			wpa_printf(MSG_DEBUG,
+				   "TLSv1: Truncated OCSPResponseList");
+			res = TLS_OCSP_INVALID;
+			goto done;
+		}
+		resp_len = WPA_GET_BE24(pos);
+		pos += 3;
+		if (end - pos < resp_len) {
+			wpa_printf(MSG_DEBUG,
+				   "TLSv1: Truncated OCSPResponseList(len=%u)",
+				   resp_len);
+			res = TLS_OCSP_INVALID;
+			goto done;
+		}
+		end = pos + resp_len;
+
+		while (end - pos >= 3) {
+			resp_len = WPA_GET_BE24(pos);
+			pos += 3;
+			if (resp_len > end - pos) {
+				wpa_printf(MSG_DEBUG,
+					   "TLSv1: Truncated OCSPResponse(len=%u; left=%d) in ocsp_multi",
+					   resp_len, (int) (end - pos));
+				res = TLS_OCSP_INVALID;
+				break;
+			}
+			if (!resp_len)
+				continue; /* Skip an empty response */
+			res = tls_process_certificate_status_ocsp_response(
+				conn, pos - 3, resp_len + 3);
+			if (res == TLS_OCSP_REVOKED)
+				revoked++;
+			else if (res == TLS_OCSP_GOOD)
+				good++;
+			pos += resp_len;
+		}
+
+		if (revoked)
+			res = TLS_OCSP_REVOKED;
+		else if (good)
+			res = TLS_OCSP_GOOD;
+	} else {
+		wpa_printf(MSG_DEBUG,
+			   "TLSv1: Ignore unsupported CertificateStatus");
+		goto skip;
+	}
+
+done:
+	if (res == TLS_OCSP_REVOKED) {
+		tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+			  TLS_ALERT_CERTIFICATE_REVOKED);
+		for (cert = conn->server_cert, depth = 0; cert;
+		     cert = cert->next, depth++) {
+			if (cert->ocsp_revoked) {
+				tls_cert_chain_failure_event(
+					conn, depth, cert, TLS_FAIL_REVOKED,
+					"certificate revoked");
+			}
+		}
+		return -1;
+	}
+
+	if (conn->flags & TLS_CONN_REQUIRE_OCSP_ALL) {
+		/*
+		 * Verify that each certificate on the chain that is not part
+		 * of the trusted certificates has a good status. If not,
+		 * terminate handshake.
+		 */
+		for (cert = conn->server_cert, depth = 0; cert;
+		     cert = cert->next, depth++) {
+			if (!cert->ocsp_good) {
+				tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+					  TLS_ALERT_BAD_CERTIFICATE_STATUS_RESPONSE);
+				tls_cert_chain_failure_event(
+					conn, depth, cert,
+					TLS_FAIL_UNSPECIFIED,
+					"bad certificate status response");
+				return -1;
+			}
+			if (cert->issuer_trusted)
+				break;
+		}
+	}
+
+	if ((conn->flags & TLS_CONN_REQUIRE_OCSP) && res != TLS_OCSP_GOOD) {
+		tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+			  res == TLS_OCSP_INVALID ? TLS_ALERT_DECODE_ERROR :
+			  TLS_ALERT_BAD_CERTIFICATE_STATUS_RESPONSE);
+		if (conn->server_cert)
+			tls_cert_chain_failure_event(
+				conn, 0, conn->server_cert,
+				TLS_FAIL_UNSPECIFIED,
+				"bad certificate status response");
+		return -1;
+	}
+
+	conn->ocsp_resp_received = 1;
+
+skip:
+	*in_len = end - in_data;
+
+	conn->state = SERVER_KEY_EXCHANGE;
+
+	return 0;
+}
+
+
 static int tls_process_server_key_exchange(struct tlsv1_client *conn, u8 ct,
 					   const u8 *in_data, size_t *in_len)
 {
@@ -608,6 +1054,10 @@
 
 	end = pos + len;
 
+	if ((conn->flags & TLS_CONN_REQUEST_OCSP) &&
+	    type == TLS_HANDSHAKE_TYPE_CERTIFICATE_STATUS)
+		return tls_process_certificate_status(conn, ct, in_data,
+						      in_len);
 	if (type == TLS_HANDSHAKE_TYPE_CERTIFICATE_REQUEST)
 		return tls_process_certificate_request(conn, ct, in_data,
 						       in_len);
@@ -617,7 +1067,9 @@
 	if (type != TLS_HANDSHAKE_TYPE_SERVER_KEY_EXCHANGE) {
 		wpa_printf(MSG_DEBUG, "TLSv1: Received unexpected handshake "
 			   "message %d (expected ServerKeyExchange/"
-			   "CertificateRequest/ServerHelloDone)", type);
+			   "CertificateRequest/ServerHelloDone%s)", type,
+			   (conn->flags & TLS_CONN_REQUEST_OCSP) ?
+			   "/CertificateStatus" : "");
 		tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
 			  TLS_ALERT_UNEXPECTED_MESSAGE);
 		return -1;
@@ -771,6 +1223,15 @@
 
 	wpa_printf(MSG_DEBUG, "TLSv1: Received ServerHelloDone");
 
+	if ((conn->flags & TLS_CONN_REQUIRE_OCSP) &&
+	    !conn->ocsp_resp_received) {
+		wpa_printf(MSG_INFO,
+			   "TLSv1: No OCSP response received - reject handshake");
+		tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+			  TLS_ALERT_BAD_CERTIFICATE_STATUS_RESPONSE);
+		return -1;
+	}
+
 	*in_len = end - in_data;
 
 	conn->state = CLIENT_KEY_EXCHANGE;
diff --git a/src/tls/tlsv1_client_write.c b/src/tls/tlsv1_client_write.c
index d192f44..04d895e 100644
--- a/src/tls/tlsv1_client_write.c
+++ b/src/tls/tlsv1_client_write.c
@@ -1,6 +1,6 @@
 /*
  * TLSv1 client - write handshake message
- * Copyright (c) 2006-2014, Jouni Malinen <j@w1.fi>
+ * 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.
@@ -47,8 +47,28 @@
 	u8 *hello, *end, *pos, *hs_length, *hs_start, *rhdr;
 	struct os_time now;
 	size_t len, i;
+	u8 *ext_start;
+	u16 tls_version = TLS_VERSION;
 
-	wpa_printf(MSG_DEBUG, "TLSv1: Send ClientHello");
+	/* Pick the highest locally enabled TLS version */
+#ifdef CONFIG_TLSV12
+	if ((conn->flags & TLS_CONN_DISABLE_TLSv1_2) &&
+	    tls_version == TLS_VERSION_1_2)
+		tls_version = TLS_VERSION_1_1;
+#endif /* CONFIG_TLSV12 */
+#ifdef CONFIG_TLSV11
+	if ((conn->flags & TLS_CONN_DISABLE_TLSv1_1) &&
+	    tls_version == TLS_VERSION_1_1)
+		tls_version = TLS_VERSION_1;
+#endif /* CONFIG_TLSV11 */
+	if ((conn->flags & TLS_CONN_DISABLE_TLSv1_0) &&
+	    tls_version == TLS_VERSION_1) {
+		wpa_printf(MSG_INFO, "TLSv1: No TLS version allowed");
+		return NULL;
+	}
+
+	wpa_printf(MSG_DEBUG, "TLSv1: Send ClientHello (ver %s)",
+		   tls_version_str(tls_version));
 	*out_len = 0;
 
 	os_get_time(&now);
@@ -61,7 +81,7 @@
 	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;
+	len = 150 + conn->num_cipher_suites * 2 + conn->client_hello_ext_len;
 	hello = os_malloc(len);
 	if (hello == NULL)
 		return NULL;
@@ -81,7 +101,7 @@
 	pos += 3;
 	/* body - ClientHello */
 	/* ProtocolVersion client_version */
-	WPA_PUT_BE16(pos, TLS_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);
@@ -101,12 +121,124 @@
 	*pos++ = 1;
 	*pos++ = TLS_COMPRESSION_NULL;
 
+	/* Extension */
+	ext_start = pos;
+	pos += 2;
+
+#ifdef CONFIG_TLSV12
+	if (conn->rl.tls_version >= TLS_VERSION_1_2) {
+		/*
+		 * Add signature_algorithms extension since we support only
+		 * SHA256 (and not the default SHA1) with TLSv1.2.
+		 */
+		/* ExtensionsType extension_type = signature_algorithms(13) */
+		WPA_PUT_BE16(pos, TLS_EXT_SIGNATURE_ALGORITHMS);
+		pos += 2;
+		/* opaque extension_data<0..2^16-1> length */
+		WPA_PUT_BE16(pos, 8);
+		pos += 2;
+		/* supported_signature_algorithms<2..2^16-2> length */
+		WPA_PUT_BE16(pos, 6);
+		pos += 2;
+		/* supported_signature_algorithms */
+		*pos++ = TLS_HASH_ALG_SHA512;
+		*pos++ = TLS_SIGN_ALG_RSA;
+		*pos++ = TLS_HASH_ALG_SHA384;
+		*pos++ = TLS_SIGN_ALG_RSA;
+		*pos++ = TLS_HASH_ALG_SHA256;
+		*pos++ = TLS_SIGN_ALG_RSA;
+	}
+#endif /* CONFIG_TLSV12 */
+
 	if (conn->client_hello_ext) {
 		os_memcpy(pos, conn->client_hello_ext,
 			  conn->client_hello_ext_len);
 		pos += conn->client_hello_ext_len;
 	}
 
+	if (conn->flags & TLS_CONN_REQUEST_OCSP) {
+		wpa_printf(MSG_DEBUG,
+			   "TLSv1: Add status_request extension for OCSP stapling");
+		/* ExtensionsType extension_type = status_request(5) */
+		WPA_PUT_BE16(pos, TLS_EXT_STATUS_REQUEST);
+		pos += 2;
+		/* opaque extension_data<0..2^16-1> length */
+		WPA_PUT_BE16(pos, 5);
+		pos += 2;
+
+		/*
+		 * RFC 6066, 8:
+		 * struct {
+		 *     CertificateStatusType status_type;
+		 *     select (status_type) {
+		 *         case ocsp: OCSPStatusRequest;
+		 *     } request;
+		 * } CertificateStatusRequest;
+		 *
+		 * enum { ocsp(1), (255) } CertificateStatusType;
+		 */
+		*pos++ = 1; /* status_type = ocsp(1) */
+
+		/*
+		 * struct {
+		 *     ResponderID responder_id_list<0..2^16-1>;
+		 *     Extensions  request_extensions;
+		 * } OCSPStatusRequest;
+		 *
+		 * opaque ResponderID<1..2^16-1>;
+		 * opaque Extensions<0..2^16-1>;
+		 */
+		WPA_PUT_BE16(pos, 0); /* responder_id_list(empty) */
+		pos += 2;
+		WPA_PUT_BE16(pos, 0); /* request_extensions(empty) */
+		pos += 2;
+
+		wpa_printf(MSG_DEBUG,
+			   "TLSv1: Add status_request_v2 extension for OCSP stapling");
+		/* ExtensionsType extension_type = status_request_v2(17) */
+		WPA_PUT_BE16(pos, TLS_EXT_STATUS_REQUEST_V2);
+		pos += 2;
+		/* opaque extension_data<0..2^16-1> length */
+		WPA_PUT_BE16(pos, 7);
+		pos += 2;
+
+		/*
+		 * RFC 6961, 2.2:
+		 * struct {
+		 *     CertificateStatusType status_type;
+		 *     uint16 request_length;
+		 *     select (status_type) {
+		 *         case ocsp: OCSPStatusRequest;
+		 *         case ocsp_multi: OCSPStatusRequest;
+		 *     } request;
+		 * } CertificateStatusRequestItemV2;
+		 *
+		 * enum { ocsp(1), ocsp_multi(2), (255) } CertificateStatusType;
+		 *
+		 * struct {
+		 * CertificateStatusRequestItemV2
+		 *     certificate_status_req_list<1..2^16-1>;
+		 * } CertificateStatusRequestListV2;
+		 */
+
+		/* certificate_status_req_list<1..2^16-1> */
+		WPA_PUT_BE16(pos, 5);
+		pos += 2;
+
+		/* CertificateStatusRequestItemV2 */
+		*pos++ = 2; /* status_type = ocsp_multi(2) */
+		/* OCSPStatusRequest as shown above for v1 */
+		WPA_PUT_BE16(pos, 0); /* responder_id_list(empty) */
+		pos += 2;
+		WPA_PUT_BE16(pos, 0); /* request_extensions(empty) */
+		pos += 2;
+	}
+
+	if (pos == ext_start + 2)
+		pos -= 2; /* no extensions */
+	else
+		WPA_PUT_BE16(ext_start, pos - ext_start - 2);
+
 	WPA_PUT_BE24(hs_length, pos - hs_length - 3);
 	tls_verify_hash_add(&conn->verify, hs_start, pos - hs_start);
 
@@ -134,6 +266,11 @@
 	struct x509_certificate *cert;
 
 	pos = *msgpos;
+	if (TLS_RECORD_HEADER_LEN + 1 + 3 + 3 > end - pos) {
+		tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+			  TLS_ALERT_INTERNAL_ERROR);
+		return -1;
+	}
 
 	wpa_printf(MSG_DEBUG, "TLSv1: Send Certificate");
 	rhdr = pos;
@@ -154,7 +291,7 @@
 	pos += 3;
 	cert = conn->cred ? conn->cred->cert : NULL;
 	while (cert) {
-		if (pos + 3 + cert->cert_len > end) {
+		if (3 + cert->cert_len > (size_t) (end - pos)) {
 			wpa_printf(MSG_DEBUG, "TLSv1: Not enough buffer space "
 				   "for Certificate (cert_len=%lu left=%lu)",
 				   (unsigned long) cert->cert_len,
@@ -265,9 +402,16 @@
 	wpa_hexdump(MSG_DEBUG, "TLSv1: DH Yc (client's public value)",
 		    dh_yc, dh_yc_len);
 
+	if (end - *pos < 2) {
+		tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+			  TLS_ALERT_INTERNAL_ERROR);
+		os_free(csecret);
+		os_free(dh_yc);
+		return -1;
+	}
 	WPA_PUT_BE16(*pos, dh_yc_len);
 	*pos += 2;
-	if (*pos + dh_yc_len > end) {
+	if (dh_yc_len > (size_t) (end - *pos)) {
 		wpa_printf(MSG_DEBUG, "TLSv1: Not enough room in the "
 			   "message buffer for Yc");
 		tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
@@ -789,6 +933,8 @@
 
 	wpa_printf(MSG_DEBUG, "TLSv1: Session resumption completed "
 		   "successfully");
+	if (!conn->session_resumed && conn->use_session_ticket)
+		conn->session_resumed = 1;
 	conn->state = ESTABLISHED;
 
 	return msg;
diff --git a/src/tls/tlsv1_common.c b/src/tls/tlsv1_common.c
index dabc12a..6b28417 100644
--- a/src/tls/tlsv1_common.c
+++ b/src/tls/tlsv1_common.c
@@ -335,7 +335,7 @@
 
 
 #ifdef CONFIG_TLSV12
-int tlsv12_key_x_server_params_hash(u16 tls_version,
+int tlsv12_key_x_server_params_hash(u16 tls_version, u8 hash_alg,
 				    const u8 *client_random,
 				    const u8 *server_random,
 				    const u8 *server_params,
@@ -343,14 +343,30 @@
 {
 	size_t hlen;
 	struct crypto_hash *ctx;
+	enum crypto_hash_alg alg;
 
-	ctx = crypto_hash_init(CRYPTO_HASH_ALG_SHA256, NULL, 0);
+	switch (hash_alg) {
+	case TLS_HASH_ALG_SHA256:
+		alg = CRYPTO_HASH_ALG_SHA256;
+		hlen = SHA256_MAC_LEN;
+		break;
+	case TLS_HASH_ALG_SHA384:
+		alg = CRYPTO_HASH_ALG_SHA384;
+		hlen = 48;
+		break;
+	case TLS_HASH_ALG_SHA512:
+		alg = CRYPTO_HASH_ALG_SHA512;
+		hlen = 64;
+		break;
+	default:
+		return -1;
+	}
+	ctx = crypto_hash_init(alg, 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;
 
@@ -469,6 +485,21 @@
 			wpa_printf(MSG_DEBUG, "TLSv1.2: DigestAlgorithn = SHA-256");
 			decrypted = buf + 19;
 			buflen -= 19;
+		} else if (buflen >= 19 + 48 &&
+		    os_memcmp(buf, "\x30\x41\x30\x0d\x06\x09\x60\x86\x48\x01"
+			      "\x65\x03\x04\x02\x02\x05\x00\x04\x30", 19) == 0)
+		{
+			wpa_printf(MSG_DEBUG, "TLSv1.2: DigestAlgorithn = SHA-384");
+			decrypted = buf + 19;
+			buflen -= 19;
+		} else if (buflen >= 19 + 64 &&
+		    os_memcmp(buf, "\x30\x51\x30\x0d\x06\x09\x60\x86\x48\x01"
+			      "\x65\x03\x04\x02\x03\x05\x00\x04\x40", 19) == 0)
+		{
+			wpa_printf(MSG_DEBUG, "TLSv1.2: DigestAlgorithn = SHA-512");
+			decrypted = buf + 19;
+			buflen -= 19;
+
 		} else {
 			wpa_printf(MSG_DEBUG, "TLSv1.2: Unrecognized DigestInfo");
 			os_free(buf);
diff --git a/src/tls/tlsv1_common.h b/src/tls/tlsv1_common.h
index 26e68af..e30b15a 100644
--- a/src/tls/tlsv1_common.h
+++ b/src/tls/tlsv1_common.h
@@ -169,6 +169,8 @@
 #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_SIGNATURE_ALGORITHMS		13 /* RFC 5246 */
+#define TLS_EXT_STATUS_REQUEST_V2		17 /* RFC 6961 */
 #define TLS_EXT_SESSION_TICKET			35 /* RFC 4507 */
 
 #define TLS_EXT_PAC_OPAQUE TLS_EXT_SESSION_TICKET /* EAP-FAST terminology */
@@ -257,7 +259,8 @@
 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,
+int tlsv12_key_x_server_params_hash(u16 tls_version, u8 hash_Alg,
+				    const u8 *client_random,
 				    const u8 *server_random,
 				    const u8 *server_params,
 				    size_t server_params_len, u8 *hash);
diff --git a/src/tls/tlsv1_cred.c b/src/tls/tlsv1_cred.c
index 1ea6827..52c1ae0 100644
--- a/src/tls/tlsv1_cred.c
+++ b/src/tls/tlsv1_cred.c
@@ -1,6 +1,6 @@
 /*
  * TLSv1 credentials
- * Copyright (c) 2006-2009, Jouni Malinen <j@w1.fi>
+ * 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.
@@ -11,6 +11,9 @@
 #include "common.h"
 #include "base64.h"
 #include "crypto/crypto.h"
+#include "crypto/sha1.h"
+#include "pkcs5.h"
+#include "pkcs8.h"
 #include "x509v3.h"
 #include "tlsv1_cred.h"
 
@@ -33,6 +36,8 @@
 	crypto_private_key_free(cred->key);
 	os_free(cred->dh_p);
 	os_free(cred->dh_g);
+	os_free(cred->ocsp_stapling_response);
+	os_free(cred->ocsp_stapling_response_multi);
 	os_free(cred);
 }
 
@@ -190,6 +195,43 @@
 		      const u8 *cert_blob, size_t cert_blob_len,
 		      const char *path)
 {
+	if (cert && os_strncmp(cert, "hash://", 7) == 0) {
+		const char *pos = cert + 7;
+		if (os_strncmp(pos, "server/sha256/", 14) != 0) {
+			wpa_printf(MSG_DEBUG,
+				   "TLSv1: Unsupported ca_cert hash value '%s'",
+				   cert);
+			return -1;
+		}
+		pos += 14;
+		if (os_strlen(pos) != 32 * 2) {
+			wpa_printf(MSG_DEBUG,
+				   "TLSv1: Unexpected SHA256 hash length in ca_cert '%s'",
+				   cert);
+			return -1;
+		}
+		if (hexstr2bin(pos, cred->srv_cert_hash, 32) < 0) {
+			wpa_printf(MSG_DEBUG,
+				   "TLSv1: Invalid SHA256 hash value in ca_cert '%s'",
+				   cert);
+			return -1;
+		}
+		cred->server_cert_only = 1;
+		cred->ca_cert_verify = 0;
+		wpa_printf(MSG_DEBUG,
+			   "TLSv1: Checking only server certificate match");
+		return 0;
+	}
+
+	if (cert && os_strncmp(cert, "probe://", 8) == 0) {
+		cred->cert_probe = 1;
+		cred->ca_cert_verify = 0;
+		wpa_printf(MSG_DEBUG, "TLSv1: Only probe server certificate");
+		return 0;
+	}
+
+	cred->ca_cert_verify = cert || cert_blob || path;
+
 	if (tlsv1_set_cert_chain(&cred->trusted_certs, cert,
 				 cert_blob, cert_blob_len) < 0)
 		return -1;
@@ -288,6 +330,735 @@
 }
 
 
+#ifdef PKCS12_FUNCS
+
+static int oid_is_rsadsi(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 pkcs12_is_bagtype_oid(struct asn1_oid *oid, unsigned long type)
+{
+	return oid->len == 9 &&
+		oid_is_rsadsi(oid) &&
+		oid->oid[4] == 1 /* pkcs */ &&
+		oid->oid[5] == 12 /* pkcs-12 */ &&
+		oid->oid[6] == 10 &&
+		oid->oid[7] == 1 /* bagtypes */ &&
+		oid->oid[8] == type;
+}
+
+
+static int is_oid_pkcs7(struct asn1_oid *oid)
+{
+	return 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] == 7 /* pkcs-7 */;
+}
+
+
+static int is_oid_pkcs7_data(struct asn1_oid *oid)
+{
+	return is_oid_pkcs7(oid) && oid->oid[6] == 1 /* data */;
+}
+
+
+static int is_oid_pkcs7_enc_data(struct asn1_oid *oid)
+{
+	return is_oid_pkcs7(oid) && oid->oid[6] == 6 /* encryptedData */;
+}
+
+
+static int is_oid_pkcs9(struct asn1_oid *oid)
+{
+	return oid->len >= 6 &&
+		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] == 9 /* pkcs-9 */;
+}
+
+
+static int is_oid_pkcs9_friendly_name(struct asn1_oid *oid)
+{
+	return oid->len == 7 && is_oid_pkcs9(oid) &&
+		oid->oid[6] == 20;
+}
+
+
+static int is_oid_pkcs9_local_key_id(struct asn1_oid *oid)
+{
+	return oid->len == 7 && is_oid_pkcs9(oid) &&
+		oid->oid[6] == 21;
+}
+
+
+static int is_oid_pkcs9_x509_cert(struct asn1_oid *oid)
+{
+	return oid->len == 8 && is_oid_pkcs9(oid) &&
+		oid->oid[6] == 22 /* certTypes */ &&
+		oid->oid[7] == 1 /* x509Certificate */;
+}
+
+
+static int pkcs12_keybag(struct tlsv1_credentials *cred,
+			 const u8 *buf, size_t len)
+{
+	/* TODO */
+	return 0;
+}
+
+
+static int pkcs12_pkcs8_keybag(struct tlsv1_credentials *cred,
+			       const u8 *buf, size_t len,
+			       const char *passwd)
+{
+	struct crypto_private_key *key;
+
+	/* PKCS8ShroudedKeyBag ::= EncryptedPrivateKeyInfo */
+	key = pkcs8_enc_key_import(buf, len, passwd);
+	if (!key)
+		return -1;
+
+	wpa_printf(MSG_DEBUG,
+		   "PKCS #12: Successfully decrypted PKCS8ShroudedKeyBag");
+	crypto_private_key_free(cred->key);
+	cred->key = key;
+
+	return 0;
+}
+
+
+static int pkcs12_certbag(struct tlsv1_credentials *cred,
+			  const u8 *buf, size_t len)
+{
+	struct asn1_hdr hdr;
+	struct asn1_oid oid;
+	char obuf[80];
+	const u8 *pos, *end;
+
+	/*
+	 * CertBag ::= SEQUENCE {
+	 *     certId      BAG-TYPE.&id   ({CertTypes}),
+	 *     certValue   [0] EXPLICIT BAG-TYPE.&Type ({CertTypes}{@certId})
+	 * }
+	 */
+
+	if (asn1_get_next(buf, len, &hdr) < 0 ||
+	    hdr.class != ASN1_CLASS_UNIVERSAL ||
+	    hdr.tag != ASN1_TAG_SEQUENCE) {
+		wpa_printf(MSG_DEBUG,
+			   "PKCS #12: Expected SEQUENCE (CertBag) - found class %d tag 0x%x",
+			   hdr.class, hdr.tag);
+		return -1;
+	}
+
+	pos = hdr.payload;
+	end = hdr.payload + hdr.length;
+
+	if (asn1_get_oid(pos, end - pos, &oid, &pos)) {
+		wpa_printf(MSG_DEBUG,
+			   "PKCS #12: Failed to parse OID (certId)");
+		return -1;
+	}
+
+	asn1_oid_to_str(&oid, obuf, sizeof(obuf));
+	wpa_printf(MSG_DEBUG, "PKCS #12: certId %s", obuf);
+
+	if (!is_oid_pkcs9_x509_cert(&oid)) {
+		wpa_printf(MSG_DEBUG,
+			   "PKCS #12: Ignored unsupported certificate type (certId %s)",
+			   obuf);
+	}
+
+	if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+	    hdr.class != ASN1_CLASS_CONTEXT_SPECIFIC ||
+	    hdr.tag != 0) {
+		wpa_printf(MSG_DEBUG,
+			   "PKCS #12: Expected [0] EXPLICIT (certValue) - found class %d tag 0x%x",
+			   hdr.class, hdr.tag);
+		return -1;
+	}
+
+	if (asn1_get_next(hdr.payload, hdr.length, &hdr) < 0 ||
+	    hdr.class != ASN1_CLASS_UNIVERSAL ||
+	    hdr.tag != ASN1_TAG_OCTETSTRING) {
+		wpa_printf(MSG_DEBUG,
+			   "PKCS #12: Expected OCTET STRING (x509Certificate) - found class %d tag 0x%x",
+			   hdr.class, hdr.tag);
+		return -1;
+	}
+
+	wpa_hexdump(MSG_DEBUG, "PKCS #12: x509Certificate",
+		    hdr.payload, hdr.length);
+	if (cred->cert) {
+		struct x509_certificate *cert;
+
+		wpa_printf(MSG_DEBUG, "PKCS #12: Ignore extra certificate");
+		cert = x509_certificate_parse(hdr.payload, hdr.length);
+		if (!cert) {
+			wpa_printf(MSG_DEBUG,
+				   "PKCS #12: Failed to parse x509Certificate");
+			return 0;
+		}
+		x509_certificate_chain_free(cert);
+
+		return 0;
+	}
+	return tlsv1_set_cert(cred, NULL, hdr.payload, hdr.length);
+}
+
+
+static int pkcs12_parse_attr_friendly_name(const u8 *pos, const u8 *end)
+{
+	struct asn1_hdr hdr;
+
+	/*
+	 * RFC 2985, 5.5.1:
+	 * friendlyName ATTRIBUTE ::= {
+	 *         WITH SYNTAX BMPString (SIZE(1..pkcs-9-ub-friendlyName))
+	 *         EQUALITY MATCHING RULE caseIgnoreMatch
+	 *         SINGLE VALUE TRUE
+	 *          ID pkcs-9-at-friendlyName
+	 * }
+	 */
+	if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+	    hdr.class != ASN1_CLASS_UNIVERSAL ||
+	    hdr.tag != ASN1_TAG_BMPSTRING) {
+		wpa_printf(MSG_DEBUG,
+			   "PKCS #12: Expected BMPSTRING (friendlyName) - found class %d tag 0x%x",
+			   hdr.class, hdr.tag);
+		return 0;
+	}
+	wpa_hexdump_ascii(MSG_DEBUG, "PKCS #12: friendlyName",
+			  hdr.payload, hdr.length);
+	return 0;
+}
+
+
+static int pkcs12_parse_attr_local_key_id(const u8 *pos, const u8 *end)
+{
+	struct asn1_hdr hdr;
+
+	/*
+	 * RFC 2985, 5.5.2:
+	 * localKeyId ATTRIBUTE ::= {
+	 *         WITH SYNTAX OCTET STRING
+	 *         EQUALITY MATCHING RULE octetStringMatch
+	 *         SINGLE VALUE TRUE
+	 *         ID pkcs-9-at-localKeyId
+	 * }
+	 */
+	if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+	    hdr.class != ASN1_CLASS_UNIVERSAL ||
+	    hdr.tag != ASN1_TAG_OCTETSTRING) {
+		wpa_printf(MSG_DEBUG,
+			   "PKCS #12: Expected OCTET STRING (localKeyID) - found class %d tag 0x%x",
+			   hdr.class, hdr.tag);
+		return -1;
+	}
+	wpa_hexdump_key(MSG_DEBUG, "PKCS #12: localKeyID",
+			hdr.payload, hdr.length);
+	return 0;
+}
+
+
+static int pkcs12_parse_attr(const u8 *pos, size_t len)
+{
+	const u8 *end = pos + len;
+	struct asn1_hdr hdr;
+	struct asn1_oid a_oid;
+	char obuf[80];
+
+	/*
+	 * PKCS12Attribute ::= SEQUENCE {
+	 * attrId      ATTRIBUTE.&id ({PKCS12AttrSet}),
+	 * attrValues  SET OF ATTRIBUTE.&Type ({PKCS12AttrSet}{@attrId})
+	 * }
+	 */
+
+	if (asn1_get_oid(pos, end - pos, &a_oid, &pos)) {
+		wpa_printf(MSG_DEBUG, "PKCS #12: Failed to parse OID (attrId)");
+		return -1;
+	}
+
+	asn1_oid_to_str(&a_oid, obuf, sizeof(obuf));
+	wpa_printf(MSG_DEBUG, "PKCS #12: attrId %s", obuf);
+
+	if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+	    hdr.class != ASN1_CLASS_UNIVERSAL ||
+	    hdr.tag != ASN1_TAG_SET) {
+		wpa_printf(MSG_DEBUG,
+			   "PKCS #12: Expected SET (attrValues) - found class %d tag 0x%x",
+			   hdr.class, hdr.tag);
+		return -1;
+	}
+	wpa_hexdump_key(MSG_MSGDUMP, "PKCS #12: attrValues",
+			hdr.payload, hdr.length);
+	pos = hdr.payload;
+	end = hdr.payload + hdr.length;
+
+	if (is_oid_pkcs9_friendly_name(&a_oid))
+		return pkcs12_parse_attr_friendly_name(pos, end);
+	if (is_oid_pkcs9_local_key_id(&a_oid))
+		return pkcs12_parse_attr_local_key_id(pos, end);
+
+	wpa_printf(MSG_DEBUG, "PKCS #12: Ignore unknown attribute");
+	return 0;
+}
+
+
+static int pkcs12_safebag(struct tlsv1_credentials *cred,
+			  const u8 *buf, size_t len, const char *passwd)
+{
+	struct asn1_hdr hdr;
+	struct asn1_oid oid;
+	char obuf[80];
+	const u8 *pos = buf, *end = buf + len;
+	const u8 *value;
+	size_t value_len;
+
+	wpa_hexdump_key(MSG_MSGDUMP, "PKCS #12: SafeBag", buf, len);
+
+	/* BAG-TYPE ::= TYPE-IDENTIFIER */
+	if (asn1_get_oid(pos, end - pos, &oid, &pos)) {
+		wpa_printf(MSG_DEBUG,
+			   "PKCS #12: Failed to parse OID (BAG-TYPE)");
+		return -1;
+	}
+
+	asn1_oid_to_str(&oid, obuf, sizeof(obuf));
+	wpa_printf(MSG_DEBUG, "PKCS #12: BAG-TYPE %s", obuf);
+
+	if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+	    hdr.class != ASN1_CLASS_CONTEXT_SPECIFIC ||
+	    hdr.tag != 0) {
+		wpa_printf(MSG_DEBUG,
+			   "PKCS #12: Expected [0] EXPLICIT (bagValue) - found class %d tag 0x%x",
+			   hdr.class, hdr.tag);
+		return 0;
+	}
+	value = hdr.payload;
+	value_len = hdr.length;
+	wpa_hexdump_key(MSG_MSGDUMP, "PKCS #12: bagValue", value, value_len);
+	pos = hdr.payload + hdr.length;
+
+	if (pos < end) {
+		/* bagAttributes  SET OF PKCS12Attribute OPTIONAL */
+		if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+		    hdr.class != ASN1_CLASS_UNIVERSAL ||
+		    hdr.tag != ASN1_TAG_SET) {
+			wpa_printf(MSG_DEBUG,
+				   "PKCS #12: Expected SET (bagAttributes) - found class %d tag 0x%x",
+				   hdr.class, hdr.tag);
+			return -1;
+		}
+		wpa_hexdump_key(MSG_MSGDUMP, "PKCS #12: bagAttributes",
+				hdr.payload, hdr.length);
+
+		pos = hdr.payload;
+		end = hdr.payload + hdr.length;
+		while (pos < end) {
+			/* PKCS12Attribute ::= SEQUENCE */
+			if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+			    hdr.class != ASN1_CLASS_UNIVERSAL ||
+			    hdr.tag != ASN1_TAG_SEQUENCE) {
+				wpa_printf(MSG_DEBUG,
+					   "PKCS #12: Expected SEQUENCE (PKCS12Attribute) - found class %d tag 0x%x",
+					   hdr.class, hdr.tag);
+				return -1;
+			}
+			if (pkcs12_parse_attr(hdr.payload, hdr.length) < 0)
+				return -1;
+			pos = hdr.payload + hdr.length;
+		}
+	}
+
+	if (pkcs12_is_bagtype_oid(&oid, 1))
+		return pkcs12_keybag(cred, value, value_len);
+	if (pkcs12_is_bagtype_oid(&oid, 2))
+		return pkcs12_pkcs8_keybag(cred, value, value_len, passwd);
+	if (pkcs12_is_bagtype_oid(&oid, 3))
+		return pkcs12_certbag(cred, value, value_len);
+
+	wpa_printf(MSG_DEBUG, "PKCS #12: Ignore unsupported BAG-TYPE");
+	return 0;
+}
+
+
+static int pkcs12_safecontents(struct tlsv1_credentials *cred,
+			       const u8 *buf, size_t len,
+			       const char *passwd)
+{
+	struct asn1_hdr hdr;
+	const u8 *pos, *end;
+
+	/* SafeContents ::= SEQUENCE OF SafeBag */
+	if (asn1_get_next(buf, len, &hdr) < 0 ||
+	    hdr.class != ASN1_CLASS_UNIVERSAL ||
+	    hdr.tag != ASN1_TAG_SEQUENCE) {
+		wpa_printf(MSG_DEBUG,
+			   "PKCS #12: Expected SEQUENCE (SafeContents) - found class %d tag 0x%x",
+			   hdr.class, hdr.tag);
+		return -1;
+	}
+	pos = hdr.payload;
+	end = hdr.payload + hdr.length;
+
+	/*
+	 * SafeBag ::= SEQUENCE {
+	 *   bagId          BAG-TYPE.&id ({PKCS12BagSet})
+	 *   bagValue       [0] EXPLICIT BAG-TYPE.&Type({PKCS12BagSet}{@bagId}),
+	 *   bagAttributes  SET OF PKCS12Attribute OPTIONAL
+	 * }
+	 */
+
+	while (pos < end) {
+		if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+		    hdr.class != ASN1_CLASS_UNIVERSAL ||
+		    hdr.tag != ASN1_TAG_SEQUENCE) {
+			wpa_printf(MSG_DEBUG,
+				   "PKCS #12: Expected SEQUENCE (SafeBag) - found class %d tag 0x%x",
+				   hdr.class, hdr.tag);
+			return -1;
+		}
+		if (pkcs12_safebag(cred, hdr.payload, hdr.length, passwd) < 0)
+			return -1;
+		pos = hdr.payload + hdr.length;
+	}
+
+	return 0;
+}
+
+
+static int pkcs12_parse_content_data(struct tlsv1_credentials *cred,
+				     const u8 *pos, const u8 *end,
+				     const char *passwd)
+{
+	struct asn1_hdr hdr;
+
+	/* Data ::= 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 #12: Expected OCTET STRING (Data) - found class %d tag 0x%x",
+			   hdr.class, hdr.tag);
+		return -1;
+	}
+
+	wpa_hexdump(MSG_MSGDUMP, "PKCS #12: Data", hdr.payload, hdr.length);
+
+	return pkcs12_safecontents(cred, hdr.payload, hdr.length, passwd);
+}
+
+
+static int pkcs12_parse_content_enc_data(struct tlsv1_credentials *cred,
+					 const u8 *pos, const u8 *end,
+					 const char *passwd)
+{
+	struct asn1_hdr hdr;
+	struct asn1_oid oid;
+	char buf[80];
+	const u8 *enc_alg;
+	u8 *data;
+	size_t enc_alg_len, data_len;
+	int res = -1;
+
+	/*
+	 * EncryptedData ::= SEQUENCE {
+	 *   version Version,
+	 *   encryptedContentInfo EncryptedContentInfo }
+	 */
+	if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+	    hdr.class != ASN1_CLASS_UNIVERSAL ||
+	    hdr.tag != ASN1_TAG_SEQUENCE) {
+		wpa_printf(MSG_DEBUG,
+			   "PKCS #12: Expected SEQUENCE (EncryptedData) - found class %d tag 0x%x",
+			   hdr.class, hdr.tag);
+		return 0;
+	}
+	pos = hdr.payload;
+
+	/* 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 #12: No INTEGER tag found for version; class=%d tag=0x%x",
+			   hdr.class, hdr.tag);
+		return -1;
+	}
+	if (hdr.length != 1 || hdr.payload[0] != 0) {
+		wpa_printf(MSG_DEBUG, "PKCS #12: Unrecognized PKCS #7 version");
+		return -1;
+	}
+	pos = hdr.payload + hdr.length;
+
+	wpa_hexdump(MSG_MSGDUMP, "PKCS #12: EncryptedContentInfo",
+		    pos, end - pos);
+
+	/*
+	 * EncryptedContentInfo ::= SEQUENCE {
+	 *   contentType ContentType,
+	 *   contentEncryptionAlgorithm ContentEncryptionAlgorithmIdentifier,
+	 *   encryptedContent [0] IMPLICIT EncryptedContent 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 #12: Expected SEQUENCE (EncryptedContentInfo) - found class %d tag 0x%x",
+			   hdr.class, hdr.tag);
+		return -1;
+	}
+
+	pos = hdr.payload;
+	end = pos + hdr.length;
+
+	/* ContentType ::= OBJECT IDENTIFIER */
+	if (asn1_get_oid(pos, end - pos, &oid, &pos)) {
+		wpa_printf(MSG_DEBUG,
+			   "PKCS #12: Could not find OBJECT IDENTIFIER (contentType)");
+		return -1;
+	}
+	asn1_oid_to_str(&oid, buf, sizeof(buf));
+	wpa_printf(MSG_DEBUG, "PKCS #12: EncryptedContentInfo::contentType %s",
+		   buf);
+
+	if (!is_oid_pkcs7_data(&oid)) {
+		wpa_printf(MSG_DEBUG,
+			   "PKCS #12: Unsupported EncryptedContentInfo::contentType %s",
+			   buf);
+		return 0;
+	}
+
+	/* ContentEncryptionAlgorithmIdentifier ::= AlgorithmIdentifier */
+	if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+	    hdr.class != ASN1_CLASS_UNIVERSAL ||
+	    hdr.tag != ASN1_TAG_SEQUENCE) {
+		wpa_printf(MSG_DEBUG, "PKCS #12: Expected SEQUENCE (ContentEncryptionAlgorithmIdentifier) - found class %d tag 0x%x",
+			   hdr.class, hdr.tag);
+		return -1;
+	}
+	enc_alg = hdr.payload;
+	enc_alg_len = hdr.length;
+	pos = hdr.payload + hdr.length;
+
+	if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+	    hdr.class != ASN1_CLASS_CONTEXT_SPECIFIC ||
+	    hdr.tag != 0) {
+		wpa_printf(MSG_DEBUG,
+			   "PKCS #12: Expected [0] IMPLICIT (encryptedContent) - found class %d tag 0x%x",
+			   hdr.class, hdr.tag);
+		return -1;
+	}
+
+	/* EncryptedContent ::= OCTET STRING */
+	data = pkcs5_decrypt(enc_alg, enc_alg_len, hdr.payload, hdr.length,
+			     passwd, &data_len);
+	if (data) {
+		wpa_hexdump_key(MSG_MSGDUMP,
+				"PKCS #12: Decrypted encryptedContent",
+				data, data_len);
+		res = pkcs12_safecontents(cred, data, data_len, passwd);
+		os_free(data);
+	}
+
+	return res;
+}
+
+
+static int pkcs12_parse_content(struct tlsv1_credentials *cred,
+				const u8 *buf, size_t len,
+				const char *passwd)
+{
+	const u8 *pos = buf;
+	const u8 *end = buf + len;
+	struct asn1_oid oid;
+	char txt[80];
+	struct asn1_hdr hdr;
+
+	wpa_hexdump(MSG_MSGDUMP, "PKCS #12: ContentInfo", buf, len);
+
+	if (asn1_get_oid(pos, end - pos, &oid, &pos)) {
+		wpa_printf(MSG_DEBUG,
+			   "PKCS #12: Could not find OBJECT IDENTIFIER (contentType)");
+		return 0;
+	}
+
+	asn1_oid_to_str(&oid, txt, sizeof(txt));
+	wpa_printf(MSG_DEBUG, "PKCS #12: contentType %s", txt);
+
+	if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+	    hdr.class != ASN1_CLASS_CONTEXT_SPECIFIC ||
+	    hdr.tag != 0) {
+		wpa_printf(MSG_DEBUG,
+			   "PKCS #12: Expected [0] EXPLICIT (content) - found class %d tag 0x%x",
+			   hdr.class, hdr.tag);
+		return 0;
+	}
+	pos = hdr.payload;
+
+	if (is_oid_pkcs7_data(&oid))
+		return pkcs12_parse_content_data(cred, pos, end, passwd);
+	if (is_oid_pkcs7_enc_data(&oid))
+		return pkcs12_parse_content_enc_data(cred, pos, end, passwd);
+
+	wpa_printf(MSG_DEBUG, "PKCS #12: Ignored unsupported contentType %s",
+		   txt);
+
+	return 0;
+}
+
+
+static int pkcs12_parse(struct tlsv1_credentials *cred,
+			const u8 *key, size_t len, const char *passwd)
+{
+	struct asn1_hdr hdr;
+	const u8 *pos, *end;
+	struct asn1_oid oid;
+	char buf[80];
+
+	/*
+	 * PFX ::= SEQUENCE {
+	 *     version     INTEGER {v3(3)}(v3,...),
+	 *     authSafe    ContentInfo,
+	 *     macData     MacData OPTIONAL
+	 * }
+	 */
+
+	if (asn1_get_next(key, len, &hdr) < 0 ||
+	    hdr.class != ASN1_CLASS_UNIVERSAL ||
+	    hdr.tag != ASN1_TAG_SEQUENCE) {
+		wpa_printf(MSG_DEBUG,
+			   "PKCS #12: Expected SEQUENCE (PFX) - found class %d tag 0x%x; assume PKCS #12 not used",
+			   hdr.class, hdr.tag);
+		return -1;
+	}
+
+	pos = hdr.payload;
+	end = pos + hdr.length;
+
+	if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+	    hdr.class != ASN1_CLASS_UNIVERSAL || hdr.tag != ASN1_TAG_INTEGER) {
+		wpa_printf(MSG_DEBUG,
+			   "PKCS #12: No INTEGER tag found for version; class=%d tag=0x%x",
+			   hdr.class, hdr.tag);
+		return -1;
+	}
+	if (hdr.length != 1 || hdr.payload[0] != 3) {
+		wpa_printf(MSG_DEBUG, "PKCS #12: Unrecognized version");
+		return -1;
+	}
+	pos = hdr.payload + hdr.length;
+
+	/*
+	 * ContentInfo ::= SEQUENCE {
+	 *   contentType ContentType,
+	 *   content [0] EXPLICIT ANY DEFINED BY contentType 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 #12: Expected SEQUENCE (authSafe) - found class %d tag 0x%x; assume PKCS #12 not used",
+			   hdr.class, hdr.tag);
+		return -1;
+	}
+
+	pos = hdr.payload;
+	end = pos + hdr.length;
+
+	/* ContentType ::= OBJECT IDENTIFIER */
+	if (asn1_get_oid(pos, end - pos, &oid, &pos)) {
+		wpa_printf(MSG_DEBUG,
+			   "PKCS #12: Could not find OBJECT IDENTIFIER (contentType); assume PKCS #12 not used");
+		return -1;
+	}
+	asn1_oid_to_str(&oid, buf, sizeof(buf));
+	wpa_printf(MSG_DEBUG, "PKCS #12: contentType %s", buf);
+	if (!is_oid_pkcs7_data(&oid)) {
+		wpa_printf(MSG_DEBUG, "PKCS #12: Unsupported contentType %s",
+			   buf);
+		return -1;
+	}
+
+	if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+	    hdr.class != ASN1_CLASS_CONTEXT_SPECIFIC ||
+	    hdr.tag != 0) {
+		wpa_printf(MSG_DEBUG,
+			   "PKCS #12: Expected [0] EXPLICIT (content) - found class %d tag 0x%x; assume PKCS #12 not used",
+			   hdr.class, hdr.tag);
+		return -1;
+	}
+
+	pos = hdr.payload;
+
+	/* Data ::= 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 #12: Expected OCTET STRING (Data) - found class %d tag 0x%x; assume PKCS #12 not used",
+			   hdr.class, hdr.tag);
+		return -1;
+	}
+
+	/*
+	 * AuthenticatedSafe ::= SEQUENCE OF ContentInfo
+	 *     -- Data if unencrypted
+	 *     -- EncryptedData if password-encrypted
+	 *     -- EnvelopedData if public key-encrypted
+	 */
+	wpa_hexdump(MSG_MSGDUMP, "PKCS #12: Data content",
+		    hdr.payload, hdr.length);
+
+	if (asn1_get_next(hdr.payload, hdr.length, &hdr) < 0 ||
+	    hdr.class != ASN1_CLASS_UNIVERSAL ||
+	    hdr.tag != ASN1_TAG_SEQUENCE) {
+		wpa_printf(MSG_DEBUG,
+			   "PKCS #12: Expected SEQUENCE within Data content - found class %d tag 0x%x; assume PKCS #12 not used",
+			   hdr.class, hdr.tag);
+		return -1;
+	}
+
+	pos = hdr.payload;
+	end = pos + hdr.length;
+
+	while (end > pos) {
+		if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+		    hdr.class != ASN1_CLASS_UNIVERSAL ||
+		    hdr.tag != ASN1_TAG_SEQUENCE) {
+			wpa_printf(MSG_DEBUG,
+				   "PKCS #12: Expected SEQUENCE (ContentInfo) - found class %d tag 0x%x; assume PKCS #12 not used",
+				   hdr.class, hdr.tag);
+			return -1;
+		}
+		if (pkcs12_parse_content(cred, hdr.payload, hdr.length,
+					 passwd) < 0)
+			return -1;
+
+		pos = hdr.payload + hdr.length;
+	}
+
+	return 0;
+}
+
+#endif /* PKCS12_FUNCS */
+
+
 static int tlsv1_set_key(struct tlsv1_credentials *cred,
 			 const u8 *key, size_t len, const char *passwd)
 {
@@ -296,6 +1067,10 @@
 		cred->key = tlsv1_set_key_pem(key, len);
 	if (cred->key == NULL)
 		cred->key = tlsv1_set_key_enc_pem(key, len, passwd);
+#ifdef PKCS12_FUNCS
+	if (!cred->key)
+		pkcs12_parse(cred, key, len, passwd);
+#endif /* PKCS12_FUNCS */
 	if (cred->key == NULL) {
 		wpa_printf(MSG_INFO, "TLSv1: Failed to parse private key");
 		return -1;
diff --git a/src/tls/tlsv1_cred.h b/src/tls/tlsv1_cred.h
index 68fbdc9..716e93c 100644
--- a/src/tls/tlsv1_cred.h
+++ b/src/tls/tlsv1_cred.h
@@ -14,11 +14,19 @@
 	struct x509_certificate *cert;
 	struct crypto_private_key *key;
 
+	unsigned int cert_probe:1;
+	unsigned int ca_cert_verify:1;
+	unsigned int server_cert_only:1;
+	u8 srv_cert_hash[32];
+
 	/* Diffie-Hellman parameters */
 	u8 *dh_p; /* prime */
 	size_t dh_p_len;
 	u8 *dh_g; /* generator */
 	size_t dh_g_len;
+
+	char *ocsp_stapling_response;
+	char *ocsp_stapling_response_multi;
 };
 
 
diff --git a/src/tls/tlsv1_server.c b/src/tls/tlsv1_server.c
index 4df756f..ba47337 100644
--- a/src/tls/tlsv1_server.c
+++ b/src/tls/tlsv1_server.c
@@ -610,12 +610,12 @@
 
 
 /**
- * tlsv1_server_get_keys - Get master key and random data from TLS connection
+ * tlsv1_server_get_random - Get random data from TLS connection
  * @conn: TLSv1 server connection data from tlsv1_server_init()
- * @keys: Structure of key/random data (filled on success)
+ * @keys: Structure of random data (filled on success)
  * Returns: 0 on success, -1 on failure
  */
-int tlsv1_server_get_keys(struct tlsv1_server *conn, struct tls_keys *keys)
+int tlsv1_server_get_random(struct tlsv1_server *conn, struct tls_random *keys)
 {
 	os_memset(keys, 0, sizeof(*keys));
 	if (conn->state == CLIENT_HELLO)
diff --git a/src/tls/tlsv1_server.h b/src/tls/tlsv1_server.h
index b2b28d1..10e7699 100644
--- a/src/tls/tlsv1_server.h
+++ b/src/tls/tlsv1_server.h
@@ -32,7 +32,7 @@
 			    size_t buflen);
 int tlsv1_server_shutdown(struct tlsv1_server *conn);
 int tlsv1_server_resumed(struct tlsv1_server *conn);
-int tlsv1_server_get_keys(struct tlsv1_server *conn, struct tls_keys *keys);
+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);
diff --git a/src/tls/tlsv1_server_i.h b/src/tls/tlsv1_server_i.h
index 96d79b3..29c6678 100644
--- a/src/tls/tlsv1_server_i.h
+++ b/src/tls/tlsv1_server_i.h
@@ -55,6 +55,9 @@
 	void *log_cb_ctx;
 
 	int use_session_ticket;
+	unsigned int status_request:1;
+	unsigned int status_request_v2:1;
+	unsigned int status_request_multi:1;
 
 	u8 *dh_secret;
 	size_t dh_secret_len;
diff --git a/src/tls/tlsv1_server_read.c b/src/tls/tlsv1_server_read.c
index 0f237ba..4aa8a01 100644
--- a/src/tls/tlsv1_server_read.c
+++ b/src/tls/tlsv1_server_read.c
@@ -46,6 +46,78 @@
 }
 
 
+static void tls_process_status_request_item(struct tlsv1_server *conn,
+					    const u8 *req, size_t req_len)
+{
+	const u8 *pos, *end;
+	u8 status_type;
+
+	pos = req;
+	end = req + req_len;
+
+	/*
+	 * RFC 6961, 2.2:
+	 * struct {
+	 *   CertificateStatusType status_type;
+	 *   uint16 request_length;
+	 *   select (status_type) {
+	 *     case ocsp: OCSPStatusRequest;
+	 *     case ocsp_multi: OCSPStatusRequest;
+	 *   } request;
+	 * } CertificateStatusRequestItemV2;
+	 *
+	 * enum { ocsp(1), ocsp_multi(2), (255) } CertificateStatusType;
+	 */
+
+	if (end - pos < 1)
+		return; /* Truncated data */
+
+	status_type = *pos++;
+	wpa_printf(MSG_DEBUG, "TLSv1: CertificateStatusType %u", status_type);
+	if (status_type != 1 && status_type != 2)
+		return; /* Unsupported status type */
+	/*
+	 * For now, only OCSP stapling is supported, so ignore the specific
+	 * request, if any.
+	 */
+	wpa_hexdump(MSG_DEBUG, "TLSv1: OCSPStatusRequest", pos, end - pos);
+
+	if (status_type == 2)
+		conn->status_request_multi = 1;
+}
+
+
+static void tls_process_status_request_v2(struct tlsv1_server *conn,
+					  const u8 *ext, size_t ext_len)
+{
+	const u8 *pos, *end;
+
+	conn->status_request_v2 = 1;
+
+	pos = ext;
+	end = ext + ext_len;
+
+	/*
+	 * RFC 6961, 2.2:
+	 * struct {
+	 *   CertificateStatusRequestItemV2
+	 *                    certificate_status_req_list<1..2^16-1>;
+	 * } CertificateStatusRequestListV2;
+	 */
+
+	while (end - pos >= 2) {
+		u16 len;
+
+		len = WPA_GET_BE16(pos);
+		pos += 2;
+		if (len > end - pos)
+			break; /* Truncated data */
+		tls_process_status_request_item(conn, pos, len);
+		pos += len;
+	}
+}
+
+
 static int tls_process_client_hello(struct tlsv1_server *conn, u8 ct,
 				    const u8 *in_data, size_t *in_len)
 {
@@ -267,6 +339,11 @@
 						  ext_len);
 					conn->session_ticket_len = ext_len;
 				}
+			} else if (ext_type == TLS_EXT_STATUS_REQUEST) {
+				conn->status_request = 1;
+			} else if (ext_type == TLS_EXT_STATUS_REQUEST_V2) {
+				tls_process_status_request_v2(conn, pos,
+							      ext_len);
 			}
 
 			pos += ext_len;
@@ -471,6 +548,15 @@
 		return -1;
 	}
 
+	if (chain && (chain->extensions_present & X509_EXT_EXT_KEY_USAGE) &&
+	    !(chain->ext_key_usage &
+	      (X509_EXT_KEY_USAGE_ANY | X509_EXT_KEY_USAGE_CLIENT_AUTH))) {
+		tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+				   TLS_ALERT_BAD_CERTIFICATE);
+		x509_certificate_chain_free(chain);
+		return -1;
+	}
+
 	x509_certificate_chain_free(chain);
 
 	*in_len = end - in_data;
diff --git a/src/tls/tlsv1_server_write.c b/src/tls/tlsv1_server_write.c
index 15e6692..bdc6c11 100644
--- a/src/tls/tlsv1_server_write.c
+++ b/src/tls/tlsv1_server_write.c
@@ -42,7 +42,7 @@
 static int tls_write_server_hello(struct tlsv1_server *conn,
 				  u8 **msgpos, u8 *end)
 {
-	u8 *pos, *rhdr, *hs_start, *hs_length;
+	u8 *pos, *rhdr, *hs_start, *hs_length, *ext_start;
 	struct os_time now;
 	size_t rlen;
 
@@ -97,6 +97,32 @@
 	/* CompressionMethod compression_method */
 	*pos++ = TLS_COMPRESSION_NULL;
 
+	/* Extension */
+	ext_start = pos;
+	pos += 2;
+
+	if (conn->status_request) {
+		/* Add a status_request extension with empty extension_data */
+		/* ExtensionsType extension_type = status_request(5) */
+		WPA_PUT_BE16(pos, TLS_EXT_STATUS_REQUEST);
+		pos += 2;
+		/* opaque extension_data<0..2^16-1> length */
+		WPA_PUT_BE16(pos, 0);
+		pos += 2;
+	}
+
+	if (conn->status_request_v2) {
+		/*
+		  Add a status_request_v2 extension with empty extension_data
+		*/
+		/* ExtensionsType extension_type = status_request_v2(17) */
+		WPA_PUT_BE16(pos, TLS_EXT_STATUS_REQUEST_V2);
+		pos += 2;
+		/* opaque extension_data<0..2^16-1> length */
+		WPA_PUT_BE16(pos, 0);
+		pos += 2;
+	}
+
 	if (conn->session_ticket && conn->session_ticket_cb) {
 		int res = conn->session_ticket_cb(
 			conn->session_ticket_cb_ctx,
@@ -133,6 +159,11 @@
 		 */
 	}
 
+	if (pos == ext_start + 2)
+		pos -= 2; /* no extensions */
+	else
+		WPA_PUT_BE16(ext_start, pos - ext_start - 2);
+
 	WPA_PUT_BE24(hs_length, pos - hs_length - 3);
 	tls_verify_hash_add(&conn->verify, hs_start, pos - hs_start);
 
@@ -168,6 +199,11 @@
 	}
 
 	pos = *msgpos;
+	if (TLS_RECORD_HEADER_LEN + 1 + 3 + 3 > end - pos) {
+		tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+				   TLS_ALERT_INTERNAL_ERROR);
+		return -1;
+	}
 
 	tlsv1_server_log(conn, "Send Certificate");
 	rhdr = pos;
@@ -188,7 +224,7 @@
 	pos += 3;
 	cert = conn->cred->cert;
 	while (cert) {
-		if (pos + 3 + cert->cert_len > end) {
+		if (3 + cert->cert_len > (size_t) (end - pos)) {
 			wpa_printf(MSG_DEBUG, "TLSv1: Not enough buffer space "
 				   "for Certificate (cert_len=%lu left=%lu)",
 				   (unsigned long) cert->cert_len,
@@ -239,6 +275,93 @@
 }
 
 
+static int tls_write_server_certificate_status(struct tlsv1_server *conn,
+					       u8 **msgpos, u8 *end,
+					       int ocsp_multi,
+					       char *ocsp_resp,
+					       size_t ocsp_resp_len)
+{
+	u8 *pos, *rhdr, *hs_start, *hs_length;
+	size_t rlen;
+
+	if (!ocsp_resp) {
+		 /*
+		  * Client did not request certificate status or there is no
+		  * matching response cached.
+		  */
+		return 0;
+	}
+
+	pos = *msgpos;
+	if (TLS_RECORD_HEADER_LEN + 1 + 3 + 1 + 3 + ocsp_resp_len >
+	    (unsigned int) (end - pos)) {
+		tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+				   TLS_ALERT_INTERNAL_ERROR);
+		return -1;
+	}
+
+	tlsv1_server_log(conn, "Send CertificateStatus (multi=%d)", ocsp_multi);
+	rhdr = pos;
+	pos += TLS_RECORD_HEADER_LEN;
+
+	/* opaque fragment[TLSPlaintext.length] */
+
+	/* Handshake */
+	hs_start = pos;
+	/* HandshakeType msg_type */
+	*pos++ = TLS_HANDSHAKE_TYPE_CERTIFICATE_STATUS;
+	/* uint24 length (to be filled) */
+	hs_length = pos;
+	pos += 3;
+
+	/* body - CertificateStatus
+	 *
+	 * struct {
+	 *     CertificateStatusType status_type;
+	 *     select (status_type) {
+	 *         case ocsp: OCSPResponse;
+	 *         case ocsp_multi: OCSPResponseList;
+	 *     } response;
+	 * } CertificateStatus;
+	 *
+	 * opaque OCSPResponse<1..2^24-1>;
+	 *
+	 * struct {
+	 *   OCSPResponse ocsp_response_list<1..2^24-1>;
+	 * } OCSPResponseList;
+	 */
+
+	/* CertificateStatusType status_type */
+	if (ocsp_multi)
+		*pos++ = 2; /* ocsp_multi(2) */
+	else
+		*pos++ = 1; /* ocsp(1) */
+	/* uint24 length of OCSPResponse */
+	WPA_PUT_BE24(pos, ocsp_resp_len);
+	pos += 3;
+	os_memcpy(pos, ocsp_resp, ocsp_resp_len);
+	pos += ocsp_resp_len;
+
+	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)
 {
@@ -371,7 +494,7 @@
 	/* body - ServerDHParams */
 	server_params = pos;
 	/* dh_p */
-	if (pos + 2 + dh_p_len > end) {
+	if (2 + dh_p_len > (size_t) (end - pos)) {
 		wpa_printf(MSG_DEBUG, "TLSv1: Not enough buffer space for "
 			   "dh_p");
 		tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
@@ -385,7 +508,7 @@
 	pos += dh_p_len;
 
 	/* dh_g */
-	if (pos + 2 + conn->cred->dh_g_len > end) {
+	if (2 + conn->cred->dh_g_len > (size_t) (end - pos)) {
 		wpa_printf(MSG_DEBUG, "TLSv1: Not enough buffer space for "
 			   "dh_g");
 		tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
@@ -399,7 +522,7 @@
 	pos += conn->cred->dh_g_len;
 
 	/* dh_Ys */
-	if (pos + 2 + dh_ys_len > end) {
+	if (2 + dh_ys_len > (size_t) (end - pos)) {
 		wpa_printf(MSG_DEBUG, "TLSv1: Not enough buffer space for "
 			   "dh_Ys");
 		tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
@@ -443,7 +566,8 @@
 		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->rl.tls_version, TLS_HASH_ALG_SHA256,
+				conn->client_random,
 				conn->server_random, server_params,
 				pos - server_params, hash + 19);
 
@@ -457,7 +581,7 @@
 			 *   SignatureAlgorithm signature;
 			 * } SignatureAndHashAlgorithm;
 			 */
-			if (hlen < 0 || pos + 2 > end) {
+			if (hlen < 0 || end - pos < 2) {
 				tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
 						   TLS_ALERT_INTERNAL_ERROR);
 				return -1;
@@ -804,24 +928,46 @@
 {
 	u8 *msg, *end, *pos;
 	size_t msglen;
+	int ocsp_multi = 0;
+	char *ocsp_resp = NULL;
+	size_t ocsp_resp_len = 0;
 
 	*out_len = 0;
 
-	msglen = 1000 + tls_server_cert_chain_der_len(conn);
+	if (conn->status_request_multi &&
+	    conn->cred->ocsp_stapling_response_multi) {
+		ocsp_resp = os_readfile(
+			conn->cred->ocsp_stapling_response_multi,
+			&ocsp_resp_len);
+		ocsp_multi = 1;
+	} else if ((conn->status_request || conn->status_request_v2) &&
+		   conn->cred->ocsp_stapling_response) {
+		ocsp_resp = os_readfile(conn->cred->ocsp_stapling_response,
+					&ocsp_resp_len);
+	}
+	if (!ocsp_resp)
+		ocsp_resp_len = 0;
+
+	msglen = 1000 + tls_server_cert_chain_der_len(conn) + ocsp_resp_len;
 
 	msg = os_malloc(msglen);
-	if (msg == NULL)
+	if (msg == NULL) {
+		os_free(ocsp_resp);
 		return NULL;
+	}
 
 	pos = msg;
 	end = msg + msglen;
 
 	if (tls_write_server_hello(conn, &pos, end) < 0) {
 		os_free(msg);
+		os_free(ocsp_resp);
 		return NULL;
 	}
 
 	if (conn->use_session_ticket) {
+		os_free(ocsp_resp);
+
 		/* 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) {
@@ -838,12 +984,16 @@
 
 	/* Full handshake */
 	if (tls_write_server_certificate(conn, &pos, end) < 0 ||
+	    tls_write_server_certificate_status(conn, &pos, end, ocsp_multi,
+						ocsp_resp, ocsp_resp_len) < 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);
+		os_free(ocsp_resp);
 		return NULL;
 	}
+	os_free(ocsp_resp);
 
 	*out_len = pos - msg;
 
diff --git a/src/tls/x509v3.c b/src/tls/x509v3.c
index b51dfcd..75f222c 100644
--- a/src/tls/x509v3.c
+++ b/src/tls/x509v3.c
@@ -1,6 +1,6 @@
 /*
  * X.509v3 certificate parsing and processing (RFC 3280 profile)
- * Copyright (c) 2006-2011, Jouni Malinen <j@w1.fi>
+ * 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.
@@ -14,7 +14,7 @@
 #include "x509v3.h"
 
 
-static void x509_free_name(struct x509_name *name)
+void x509_free_name(struct x509_name *name)
 {
 	size_t i;
 
@@ -55,6 +55,7 @@
 	x509_free_name(&cert->subject);
 	os_free(cert->public_key);
 	os_free(cert->sign_value);
+	os_free(cert->subject_dn);
 	os_free(cert);
 }
 
@@ -177,9 +178,9 @@
 }
 
 
-static int x509_parse_algorithm_identifier(
-	const u8 *buf, size_t len,
-	struct x509_algorithm_identifier *id, const u8 **next)
+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;
@@ -199,12 +200,11 @@
 			   hdr.class, hdr.tag);
 		return -1;
 	}
+	if (hdr.length > buf + len - hdr.payload)
+		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))
@@ -243,7 +243,7 @@
 	}
 	pos = hdr.payload;
 
-	if (pos + hdr.length > end)
+	if (hdr.length > end - pos)
 		return -1;
 	end = pos + hdr.length;
 	*next = end;
@@ -289,8 +289,8 @@
 }
 
 
-static int x509_parse_name(const u8 *buf, size_t len, struct x509_name *name,
-			   const u8 **next)
+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;
@@ -319,7 +319,7 @@
 	}
 	pos = hdr.payload;
 
-	if (pos + hdr.length > buf + len)
+	if (hdr.length > buf + len - pos)
 		return -1;
 
 	end = *next = pos + hdr.length;
@@ -537,8 +537,7 @@
 }
 
 
-static int x509_parse_time(const u8 *buf, size_t len, u8 asn1_tag,
-			   os_time_t *val)
+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;
@@ -677,7 +676,7 @@
 	pos = hdr.payload;
 	plen = hdr.length;
 
-	if (pos + plen > buf + len)
+	if (plen > (size_t) (buf + len - pos))
 		return -1;
 
 	*next = pos + plen;
@@ -721,6 +720,15 @@
 }
 
 
+static int x509_any_ext_key_usage_oid(struct asn1_oid *oid)
+{
+	return oid->len == 6 &&
+		x509_id_ce_oid(oid) &&
+		oid->oid[3] == 37 /* extKeyUsage */ &&
+		oid->oid[4] == 0 /* anyExtendedKeyUsage */;
+}
+
+
 static int x509_parse_ext_key_usage(struct x509_certificate *cert,
 				    const u8 *pos, size_t len)
 {
@@ -801,7 +809,7 @@
 		}
 		cert->ca = hdr.payload[0];
 
-		if (hdr.payload + hdr.length == pos + len) {
+		if (hdr.length == pos + len - hdr.payload) {
 			wpa_printf(MSG_DEBUG, "X509: BasicConstraints - cA=%d",
 				   cert->ca);
 			return 0;
@@ -1074,6 +1082,112 @@
 }
 
 
+static int x509_id_pkix_oid(struct asn1_oid *oid)
+{
+	return oid->len >= 7 &&
+		oid->oid[0] == 1 /* iso */ &&
+		oid->oid[1] == 3 /* identified-organization */ &&
+		oid->oid[2] == 6 /* dod */ &&
+		oid->oid[3] == 1 /* internet */ &&
+		oid->oid[4] == 5 /* security */ &&
+		oid->oid[5] == 5 /* mechanisms */ &&
+		oid->oid[6] == 7 /* id-pkix */;
+}
+
+
+static int x509_id_kp_oid(struct asn1_oid *oid)
+{
+	/* id-kp */
+	return oid->len >= 8 &&
+		x509_id_pkix_oid(oid) &&
+		oid->oid[7] == 3 /* id-kp */;
+}
+
+
+static int x509_id_kp_server_auth_oid(struct asn1_oid *oid)
+{
+	/* id-kp */
+	return oid->len == 9 &&
+		x509_id_kp_oid(oid) &&
+		oid->oid[8] == 1 /* id-kp-serverAuth */;
+}
+
+
+static int x509_id_kp_client_auth_oid(struct asn1_oid *oid)
+{
+	/* id-kp */
+	return oid->len == 9 &&
+		x509_id_kp_oid(oid) &&
+		oid->oid[8] == 2 /* id-kp-clientAuth */;
+}
+
+
+static int x509_id_kp_ocsp_oid(struct asn1_oid *oid)
+{
+	/* id-kp */
+	return oid->len == 9 &&
+		x509_id_kp_oid(oid) &&
+		oid->oid[8] == 9 /* id-kp-OCSPSigning */;
+}
+
+
+static int x509_parse_ext_ext_key_usage(struct x509_certificate *cert,
+					const u8 *pos, size_t len)
+{
+	struct asn1_hdr hdr;
+	const u8 *end;
+	struct asn1_oid oid;
+
+	/*
+	 * ExtKeyUsageSyntax ::= SEQUENCE SIZE (1..MAX) OF KeyPurposeId
+	 *
+	 * KeyPurposeId ::= OBJECT IDENTIFIER
+	 */
+
+	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 "
+			   "(ExtKeyUsageSyntax) - found class %d tag 0x%x",
+			   hdr.class, hdr.tag);
+		return -1;
+	}
+	if (hdr.length > pos + len - hdr.payload)
+		return -1;
+	pos = hdr.payload;
+	end = pos + hdr.length;
+
+	wpa_hexdump(MSG_MSGDUMP, "X509: ExtKeyUsageSyntax", pos, end - pos);
+
+	while (pos < end) {
+		char buf[80];
+
+		if (asn1_get_oid(pos, end - pos, &oid, &pos))
+			return -1;
+		if (x509_any_ext_key_usage_oid(&oid)) {
+			os_strlcpy(buf, "anyExtendedKeyUsage", sizeof(buf));
+			cert->ext_key_usage |= X509_EXT_KEY_USAGE_ANY;
+		} else if (x509_id_kp_server_auth_oid(&oid)) {
+			os_strlcpy(buf, "id-kp-serverAuth", sizeof(buf));
+			cert->ext_key_usage |= X509_EXT_KEY_USAGE_SERVER_AUTH;
+		} else if (x509_id_kp_client_auth_oid(&oid)) {
+			os_strlcpy(buf, "id-kp-clientAuth", sizeof(buf));
+			cert->ext_key_usage |= X509_EXT_KEY_USAGE_CLIENT_AUTH;
+		} else if (x509_id_kp_ocsp_oid(&oid)) {
+			os_strlcpy(buf, "id-kp-OCSPSigning", sizeof(buf));
+			cert->ext_key_usage |= X509_EXT_KEY_USAGE_OCSP;
+		} else {
+			asn1_oid_to_str(&oid, buf, sizeof(buf));
+		}
+		wpa_printf(MSG_DEBUG, "ExtKeyUsage KeyPurposeId: %s", buf);
+	}
+
+	cert->extensions_present |= X509_EXT_EXT_KEY_USAGE;
+
+	return 0;
+}
+
+
 static int x509_parse_extension_data(struct x509_certificate *cert,
 				     struct asn1_oid *oid,
 				     const u8 *pos, size_t len)
@@ -1085,7 +1199,6 @@
 	 * 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]) {
@@ -1097,6 +1210,8 @@
 		return x509_parse_ext_issuer_alt_name(cert, pos, len);
 	case 19: /* id-ce-basicConstraints */
 		return x509_parse_ext_basic_constraints(cert, pos, len);
+	case 37: /* id-ce-extKeyUsage */
+		return x509_parse_ext_ext_key_usage(cert, pos, len);
 	default:
 		return 1;
 	}
@@ -1224,6 +1339,7 @@
 	size_t left;
 	char sbuf[128];
 	unsigned long value;
+	const u8 *subject_dn;
 
 	/* tbsCertificate TBSCertificate ::= SEQUENCE */
 	if (asn1_get_next(buf, len, &hdr) < 0 ||
@@ -1287,21 +1403,23 @@
 
 	/* serialNumber CertificateSerialNumber ::= INTEGER */
 	if (hdr.class != ASN1_CLASS_UNIVERSAL ||
-	    hdr.tag != ASN1_TAG_INTEGER) {
+	    hdr.tag != ASN1_TAG_INTEGER ||
+	    hdr.length < 1 || hdr.length > X509_MAX_SERIAL_NUM_LEN) {
 		wpa_printf(MSG_DEBUG, "X509: No INTEGER tag found for "
-			   "serialNumber; class=%d tag=0x%x",
-			   hdr.class, hdr.tag);
+			   "serialNumber; class=%d tag=0x%x length=%u",
+			   hdr.class, hdr.tag, hdr.length);
 		return -1;
 	}
 
-	pos = hdr.payload;
-	left = hdr.length;
-	while (left) {
-		cert->serial_number <<= 8;
-		cert->serial_number |= *pos++;
-		left--;
+	pos = hdr.payload + hdr.length;
+	while (hdr.length > 0 && hdr.payload[0] == 0) {
+		hdr.payload++;
+		hdr.length--;
 	}
-	wpa_printf(MSG_MSGDUMP, "X509: serialNumber %lu", cert->serial_number);
+	os_memcpy(cert->serial_number, hdr.payload, hdr.length);
+	cert->serial_number_len = hdr.length;
+	wpa_hexdump(MSG_MSGDUMP, "X509: serialNumber", cert->serial_number,
+		    cert->serial_number_len);
 
 	/* signature AlgorithmIdentifier */
 	if (x509_parse_algorithm_identifier(pos, end - pos, &cert->signature,
@@ -1319,8 +1437,14 @@
 		return -1;
 
 	/* subject Name */
+	subject_dn = pos;
 	if (x509_parse_name(pos, end - pos, &cert->subject, &pos))
 		return -1;
+	cert->subject_dn = os_malloc(pos - subject_dn);
+	if (!cert->subject_dn)
+		return -1;
+	cert->subject_dn_len = pos - subject_dn;
+	os_memcpy(cert->subject_dn, subject_dn, cert->subject_dn_len);
 	x509_name_string(&cert->subject, sbuf, sizeof(sbuf));
 	wpa_printf(MSG_MSGDUMP, "X509: subject %s", sbuf);
 
@@ -1437,7 +1561,7 @@
 }
 
 
-static int x509_sha1_oid(struct asn1_oid *oid)
+int x509_sha1_oid(struct asn1_oid *oid)
 {
 	return oid->len == 6 &&
 		oid->oid[0] == 1 /* iso */ &&
@@ -1449,7 +1573,7 @@
 }
 
 
-static int x509_sha256_oid(struct asn1_oid *oid)
+static int x509_sha2_oid(struct asn1_oid *oid)
 {
 	return oid->len == 9 &&
 		oid->oid[0] == 2 /* joint-iso-itu-t */ &&
@@ -1459,11 +1583,31 @@
 		oid->oid[4] == 101 /* gov */ &&
 		oid->oid[5] == 3 /* csor */ &&
 		oid->oid[6] == 4 /* nistAlgorithm */ &&
-		oid->oid[7] == 2 /* hashAlgs */ &&
+		oid->oid[7] == 2 /* hashAlgs */;
+}
+
+
+int x509_sha256_oid(struct asn1_oid *oid)
+{
+	return x509_sha2_oid(oid) &&
 		oid->oid[8] == 1 /* sha256 */;
 }
 
 
+int x509_sha384_oid(struct asn1_oid *oid)
+{
+	return x509_sha2_oid(oid) &&
+		oid->oid[8] == 2 /* sha384 */;
+}
+
+
+int x509_sha512_oid(struct asn1_oid *oid)
+{
+	return x509_sha2_oid(oid) &&
+		oid->oid[8] == 3 /* sha512 */;
+}
+
+
 /**
  * x509_certificate_parse - Parse a X.509 certificate in DER format
  * @buf: Pointer to the X.509 certificate in DER format
@@ -1503,12 +1647,12 @@
 	}
 	pos = hdr.payload;
 
-	if (pos + hdr.length > end) {
+	if (hdr.length > end - pos) {
 		x509_certificate_free(cert);
 		return NULL;
 	}
 
-	if (pos + hdr.length < end) {
+	if (hdr.length < end - pos) {
 		wpa_hexdump(MSG_MSGDUMP, "X509: Ignoring extra data after DER "
 			    "encoded certificate",
 			    pos + hdr.length, end - (pos + hdr.length));
@@ -1582,18 +1726,31 @@
 int x509_certificate_check_signature(struct x509_certificate *issuer,
 				     struct x509_certificate *cert)
 {
+	return x509_check_signature(issuer, &cert->signature,
+				    cert->sign_value, cert->sign_value_len,
+				    cert->tbs_cert_start, cert->tbs_cert_len);
+}
+
+
+int x509_check_signature(struct x509_certificate *issuer,
+			 struct x509_algorithm_identifier *signature,
+			 const u8 *sign_value, size_t sign_value_len,
+			 const u8 *signed_data, size_t signed_data_len)
+{
 	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];
+	u8 hash[64];
 	size_t hash_len;
+	const u8 *addr[1] = { signed_data };
+	size_t len[1] = { signed_data_len };
 
-	if (!x509_pkcs_oid(&cert->signature.oid) ||
-	    cert->signature.oid.len != 7 ||
-	    cert->signature.oid.oid[5] != 1 /* pkcs-1 */) {
+	if (!x509_pkcs_oid(&signature->oid) ||
+	    signature->oid.len != 7 ||
+	    signature->oid.oid[5] != 1 /* pkcs-1 */) {
 		wpa_printf(MSG_DEBUG, "X509: Unrecognized signature "
 			   "algorithm");
 		return -1;
@@ -1604,15 +1761,15 @@
 	if (pk == NULL)
 		return -1;
 
-	data_len = cert->sign_value_len;
+	data_len = 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,
+	if (crypto_public_key_decrypt_pkcs1(pk, sign_value,
+					    sign_value_len, data,
 					    &data_len) < 0) {
 		wpa_printf(MSG_DEBUG, "X509: Failed to decrypt signature");
 		crypto_public_key_free(pk);
@@ -1675,12 +1832,11 @@
 	}
 
 	if (x509_sha1_oid(&oid)) {
-		if (cert->signature.oid.oid[6] !=
-		    5 /* sha-1WithRSAEncryption */) {
+		if (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]);
+				   signature->oid.oid[6]);
 			os_free(data);
 			return -1;
 		}
@@ -1688,12 +1844,36 @@
 	}
 
 	if (x509_sha256_oid(&oid)) {
-		if (cert->signature.oid.oid[6] !=
+		if (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]);
+				   signature->oid.oid[6]);
+			os_free(data);
+			return -1;
+		}
+		goto skip_digest_oid;
+	}
+
+	if (x509_sha384_oid(&oid)) {
+		if (signature->oid.oid[6] != 12 /* sha384WithRSAEncryption */) {
+			wpa_printf(MSG_DEBUG, "X509: digestAlgorithm SHA384 "
+				   "does not match with certificate "
+				   "signatureAlgorithm (%lu)",
+				   signature->oid.oid[6]);
+			os_free(data);
+			return -1;
+		}
+		goto skip_digest_oid;
+	}
+
+	if (x509_sha512_oid(&oid)) {
+		if (signature->oid.oid[6] != 13 /* sha512WithRSAEncryption */) {
+			wpa_printf(MSG_DEBUG, "X509: digestAlgorithm SHA512 "
+				   "does not match with certificate "
+				   "signatureAlgorithm (%lu)",
+				   signature->oid.oid[6]);
 			os_free(data);
 			return -1;
 		}
@@ -1707,12 +1887,11 @@
 	}
 	switch (oid.oid[5]) {
 	case 5: /* md5 */
-		if (cert->signature.oid.oid[6] != 4 /* md5WithRSAEncryption */)
-		{
+		if (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]);
+				   signature->oid.oid[6]);
 			os_free(data);
 			return -1;
 		}
@@ -1743,34 +1922,41 @@
 	wpa_hexdump(MSG_MSGDUMP, "X509: Decrypted Digest",
 		    hdr.payload, hdr.length);
 
-	switch (cert->signature.oid.oid[6]) {
+	switch (signature->oid.oid[6]) {
 	case 4: /* md5WithRSAEncryption */
-		md5_vector(1, &cert->tbs_cert_start, &cert->tbs_cert_len,
-			   hash);
+		md5_vector(1, addr, 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);
+		sha1_vector(1, addr, 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);
+		sha256_vector(1, addr, len, hash);
 		hash_len = 32;
 		wpa_hexdump(MSG_MSGDUMP, "X509: Certificate hash (SHA256)",
 			    hash, hash_len);
 		break;
-	case 2: /* md2WithRSAEncryption */
 	case 12: /* sha384WithRSAEncryption */
+		sha384_vector(1, addr, len, hash);
+		hash_len = 48;
+		wpa_hexdump(MSG_MSGDUMP, "X509: Certificate hash (SHA384)",
+			    hash, hash_len);
+		break;
 	case 13: /* sha512WithRSAEncryption */
+		sha512_vector(1, addr, len, hash);
+		hash_len = 64;
+		wpa_hexdump(MSG_MSGDUMP, "X509: Certificate hash (SHA512)",
+			    hash, hash_len);
+		break;
+	case 2: /* md2WithRSAEncryption */
 	default:
 		wpa_printf(MSG_INFO, "X509: Unsupported certificate signature "
-			   "algorithm (%lu)", cert->signature.oid.oid[6]);
+			   "algorithm (%lu)", signature->oid.oid[6]);
 		os_free(data);
 		return -1;
 	}
@@ -1852,6 +2038,7 @@
 	os_get_time(&now);
 
 	for (cert = chain, idx = 0; cert; cert = cert->next, idx++) {
+		cert->issuer_trusted = 0;
 		x509_name_string(&cert->subject, buf, sizeof(buf)); 
 		wpa_printf(MSG_DEBUG, "X509: %lu: %s", idx, buf);
 
@@ -1937,6 +2124,7 @@
 
 			wpa_printf(MSG_DEBUG, "X509: Trusted certificate "
 				   "found to complete the chain");
+			cert->issuer_trusted = 1;
 			chain_trusted = 1;
 		}
 	}
diff --git a/src/tls/x509v3.h b/src/tls/x509v3.h
index 91a35ba..7df8e2a 100644
--- a/src/tls/x509v3.h
+++ b/src/tls/x509v3.h
@@ -45,13 +45,18 @@
 	struct asn1_oid rid; /* registeredID */
 };
 
+#define X509_MAX_SERIAL_NUM_LEN 20
+
 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;
+	u8 serial_number[X509_MAX_SERIAL_NUM_LEN];
+	size_t serial_number_len;
 	struct x509_algorithm_identifier signature;
 	struct x509_name issuer;
 	struct x509_name subject;
+	u8 *subject_dn;
+	size_t subject_dn_len;
 	os_time_t not_before;
 	os_time_t not_after;
 	struct x509_algorithm_identifier public_key_alg;
@@ -68,6 +73,7 @@
 #define X509_EXT_KEY_USAGE			(1 << 2)
 #define X509_EXT_SUBJECT_ALT_NAME		(1 << 3)
 #define X509_EXT_ISSUER_ALT_NAME		(1 << 4)
+#define X509_EXT_EXT_KEY_USAGE			(1 << 5)
 
 	/* BasicConstraints */
 	int ca; /* cA */
@@ -85,6 +91,13 @@
 #define X509_KEY_USAGE_ENCIPHER_ONLY		(1 << 7)
 #define X509_KEY_USAGE_DECIPHER_ONLY		(1 << 8)
 
+	/* ExtKeyUsage */
+	unsigned long ext_key_usage;
+#define X509_EXT_KEY_USAGE_ANY			(1 << 0)
+#define X509_EXT_KEY_USAGE_SERVER_AUTH		(1 << 1)
+#define X509_EXT_KEY_USAGE_CLIENT_AUTH		(1 << 2)
+#define X509_EXT_KEY_USAGE_OCSP			(1 << 3)
+
 	/*
 	 * The DER format certificate follows struct x509_certificate. These
 	 * pointers point to that buffer.
@@ -93,6 +106,11 @@
 	size_t cert_len;
 	const u8 *tbs_cert_start;
 	size_t tbs_cert_len;
+
+	/* Meta data used for certificate validation */
+	unsigned int ocsp_good:1;
+	unsigned int ocsp_revoked:1;
+	unsigned int issuer_trusted:1;
 };
 
 enum {
@@ -106,10 +124,21 @@
 };
 
 void x509_certificate_free(struct x509_certificate *cert);
+int x509_parse_algorithm_identifier(const u8 *buf, size_t len,
+				    struct x509_algorithm_identifier *id,
+				    const u8 **next);
+int x509_parse_name(const u8 *buf, size_t len, struct x509_name *name,
+		    const u8 **next);
+int x509_parse_time(const u8 *buf, size_t len, u8 asn1_tag, os_time_t *val);
 struct x509_certificate * x509_certificate_parse(const u8 *buf, size_t len);
+void x509_free_name(struct x509_name *name);
 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_check_signature(struct x509_certificate *issuer,
+			 struct x509_algorithm_identifier *signature,
+			 const u8 *sign_value, size_t sign_value_len,
+			 const u8 *signed_data, size_t signed_data_len);
 int x509_certificate_check_signature(struct x509_certificate *issuer,
 				     struct x509_certificate *cert);
 int x509_certificate_chain_validate(struct x509_certificate *trusted,
@@ -120,4 +149,9 @@
 			     struct x509_name *name);
 int x509_certificate_self_signed(struct x509_certificate *cert);
 
+int x509_sha1_oid(struct asn1_oid *oid);
+int x509_sha256_oid(struct asn1_oid *oid);
+int x509_sha384_oid(struct asn1_oid *oid);
+int x509_sha512_oid(struct asn1_oid *oid);
+
 #endif /* X509V3_H */
diff --git a/src/utils/browser-android.c b/src/utils/browser-android.c
index 9ce1a5c..71a1652 100644
--- a/src/utils/browser-android.c
+++ b/src/utils/browser-android.c
@@ -95,7 +95,7 @@
 
 	if (pid == 0) {
 		/* run the external command in the child process */
-		char *argv[9];
+		char *argv[7];
 
 		argv[0] = "browser-android";
 		argv[1] = "start";
@@ -103,9 +103,7 @@
 		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;
+		argv[6] = NULL;
 
 		execv("/system/bin/am", argv);
 		wpa_printf(MSG_ERROR, "execv: %s", strerror(errno));
diff --git a/src/utils/browser-wpadebug.c b/src/utils/browser-wpadebug.c
index 5fc40fa..59ba4d1 100644
--- a/src/utils/browser-wpadebug.c
+++ b/src/utils/browser-wpadebug.c
@@ -96,7 +96,7 @@
 
 	if (pid == 0) {
 		/* run the external command in the child process */
-		char *argv[12];
+		char *argv[14];
 
 		argv[0] = "browser-wpadebug";
 		argv[1] = "start";
@@ -109,7 +109,9 @@
 		argv[8] = "-e";
 		argv[9] = "w1.fi.wpadebug.URL";
 		argv[10] = (void *) url;
-		argv[11] = NULL;
+		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));
diff --git a/src/utils/common.c b/src/utils/common.c
index 5cf0d57..9c7d0d4 100644
--- a/src/utils/common.c
+++ b/src/utils/common.c
@@ -86,7 +86,7 @@
 		return -1;
 
 	/* check for optional mask */
-	if (*r == '\0' || isspace(*r)) {
+	if (*r == '\0' || isspace((unsigned char) *r)) {
 		/* no mask specified, assume default */
 		os_memset(mask, 0xff, ETH_ALEN);
 	} else if (maskable && *r == '/') {
@@ -498,7 +498,7 @@
 			*txt++ = 't';
 			break;
 		default:
-			if (data[i] >= 32 && data[i] <= 127) {
+			if (data[i] >= 32 && data[i] <= 126) {
 				*txt++ = data[i];
 			} else {
 				txt += os_snprintf(txt, end - txt, "\\x%02x",
@@ -973,6 +973,48 @@
 
 
 /**
+ * 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
@@ -982,25 +1024,12 @@
  */
 char * str_token(char *str, const char *delim, char **context)
 {
-	char *end, *pos = str;
+	char *token = (char *) cstr_token(str, delim, (const char **) context);
 
-	if (*context)
-		pos = *context;
+	if (token && **context)
+		*(*context)++ = '\0';
 
-	while (*pos && os_strchr(delim, *pos))
-		pos++;
-	if (!*pos)
-		return NULL;
-
-	end = pos + 1;
-	while (*end && !os_strchr(delim, *end))
-		end++;
-
-	if (*end)
-		*end++ = '\0';
-
-	*context = end;
-	return pos;
+	return token;
 }
 
 
diff --git a/src/utils/common.h b/src/utils/common.h
index 88318f5..6f0de69 100644
--- a/src/utils/common.h
+++ b/src/utils/common.h
@@ -53,16 +53,6 @@
 }
 #endif /* __APPLE__ */
 
-#ifdef CONFIG_TI_COMPILER
-#define __BIG_ENDIAN 4321
-#define __LITTLE_ENDIAN 1234
-#ifdef __big_endian__
-#define __BYTE_ORDER __BIG_ENDIAN
-#else
-#define __BYTE_ORDER __LITTLE_ENDIAN
-#endif
-#endif /* CONFIG_TI_COMPILER */
-
 #ifdef CONFIG_NATIVE_WINDOWS
 #include <winsock.h>
 
@@ -110,22 +100,6 @@
 #define WPA_TYPES_DEFINED
 #endif /* __vxworks */
 
-#ifdef CONFIG_TI_COMPILER
-#ifdef _LLONG_AVAILABLE
-typedef unsigned long long u64;
-#else
-/*
- * TODO: 64-bit variable not available. Using long as a workaround to test the
- * build, but this will likely not work for all operations.
- */
-typedef unsigned long u64;
-#endif
-typedef unsigned int u32;
-typedef unsigned short u16;
-typedef unsigned char u8;
-#define WPA_TYPES_DEFINED
-#endif /* CONFIG_TI_COMPILER */
-
 #ifndef WPA_TYPES_DEFINED
 #ifdef CONFIG_USE_INTTYPES_H
 #include <inttypes.h>
@@ -262,7 +236,7 @@
 
 static inline u32 WPA_GET_BE32(const u8 *a)
 {
-	return (a[0] << 24) | (a[1] << 16) | (a[2] << 8) | a[3];
+	return ((u32) a[0] << 24) | (a[1] << 16) | (a[2] << 8) | a[3];
 }
 
 static inline void WPA_PUT_BE32(u8 *a, u32 val)
@@ -275,7 +249,7 @@
 
 static inline u32 WPA_GET_LE32(const u8 *a)
 {
-	return (a[3] << 24) | (a[2] << 16) | (a[1] << 8) | a[0];
+	return ((u32) a[3] << 24) | (a[2] << 16) | (a[1] << 8) | a[0];
 }
 
 static inline void WPA_PUT_LE32(u8 *a, u32 val)
@@ -339,6 +313,9 @@
 #ifndef ETH_P_ALL
 #define ETH_P_ALL 0x0003
 #endif
+#ifndef ETH_P_IP
+#define ETH_P_IP 0x0800
+#endif
 #ifndef ETH_P_80211_ENCAP
 #define ETH_P_80211_ENCAP 0x890d /* TDLS comes under this category */
 #endif
@@ -433,7 +410,7 @@
 #endif
 
 #ifndef BIT
-#define BIT(x) (1 << (x))
+#define BIT(x) (1U << (x))
 #endif
 
 /*
@@ -518,6 +495,11 @@
 	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"
@@ -549,6 +531,7 @@
 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);
diff --git a/src/utils/eloop.c b/src/utils/eloop.c
index 4a565eb..436bc8c 100644
--- a/src/utils/eloop.c
+++ b/src/utils/eloop.c
@@ -18,7 +18,12 @@
 #error Do not define both of poll and epoll
 #endif
 
-#if !defined(CONFIG_ELOOP_POLL) && !defined(CONFIG_ELOOP_EPOLL)
+#if defined(CONFIG_ELOOP_POLL) && defined(CONFIG_ELOOP_KQUEUE)
+#error Do not define both of poll and kqueue
+#endif
+
+#if !defined(CONFIG_ELOOP_POLL) && !defined(CONFIG_ELOOP_EPOLL) && \
+    !defined(CONFIG_ELOOP_KQUEUE)
 #define CONFIG_ELOOP_SELECT
 #endif
 
@@ -30,6 +35,10 @@
 #include <sys/epoll.h>
 #endif /* CONFIG_ELOOP_EPOLL */
 
+#ifdef CONFIG_ELOOP_KQUEUE
+#include <sys/event.h>
+#endif /* CONFIG_ELOOP_KQUEUE */
+
 struct eloop_sock {
 	int sock;
 	void *eloop_data;
@@ -61,11 +70,8 @@
 struct eloop_sock_table {
 	int count;
 	struct eloop_sock *table;
-#ifdef CONFIG_ELOOP_EPOLL
 	eloop_event_type type;
-#else /* CONFIG_ELOOP_EPOLL */
 	int changed;
-#endif /* CONFIG_ELOOP_EPOLL */
 };
 
 struct eloop_data {
@@ -78,13 +84,20 @@
 	struct pollfd *pollfds;
 	struct pollfd **pollfds_map;
 #endif /* CONFIG_ELOOP_POLL */
+#if defined(CONFIG_ELOOP_EPOLL) || defined(CONFIG_ELOOP_KQUEUE)
+	int max_fd;
+	struct eloop_sock *fd_table;
+#endif /* CONFIG_ELOOP_EPOLL || CONFIG_ELOOP_KQUEUE */
 #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 */
+#ifdef CONFIG_ELOOP_KQUEUE
+	int kqueuefd;
+	int kqueue_nevents;
+	struct kevent *kqueue_events;
+#endif /* CONFIG_ELOOP_KQUEUE */
 	struct eloop_sock_table readers;
 	struct eloop_sock_table writers;
 	struct eloop_sock_table exceptions;
@@ -152,14 +165,24 @@
 #ifdef CONFIG_ELOOP_EPOLL
 	eloop.epollfd = epoll_create1(0);
 	if (eloop.epollfd < 0) {
-		wpa_printf(MSG_ERROR, "%s: epoll_create1 failed. %s\n",
+		wpa_printf(MSG_ERROR, "%s: epoll_create1 failed. %s",
 			   __func__, strerror(errno));
 		return -1;
 	}
+#endif /* CONFIG_ELOOP_EPOLL */
+#ifdef CONFIG_ELOOP_KQUEUE
+	eloop.kqueuefd = kqueue();
+	if (eloop.kqueuefd < 0) {
+		wpa_printf(MSG_ERROR, "%s: kqueue failed: %s",
+			   __func__, strerror(errno));
+		return -1;
+	}
+#endif /* CONFIG_ELOOP_KQUEUE */
+#if defined(CONFIG_ELOOP_EPOLL) || defined(CONFIG_ELOOP_KQUEUE)
 	eloop.readers.type = EVENT_TYPE_READ;
 	eloop.writers.type = EVENT_TYPE_WRITE;
 	eloop.exceptions.type = EVENT_TYPE_EXCEPTION;
-#endif /* CONFIG_ELOOP_EPOLL */
+#endif /* CONFIG_ELOOP_EPOLL || CONFIG_ELOOP_KQUEUE */
 #ifdef WPA_TRACE
 	signal(SIGSEGV, eloop_sigsegv_handler);
 #endif /* WPA_TRACE */
@@ -167,15 +190,80 @@
 }
 
 
+#ifdef CONFIG_ELOOP_EPOLL
+static int eloop_sock_queue(int sock, eloop_event_type type)
+{
+	struct epoll_event ev;
+
+	os_memset(&ev, 0, sizeof(ev));
+	switch (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",
+			   __func__, sock, strerror(errno));
+		return -1;
+	}
+	return 0;
+}
+#endif /* CONFIG_ELOOP_EPOLL */
+
+
+#ifdef CONFIG_ELOOP_KQUEUE
+static int eloop_sock_queue(int sock, eloop_event_type type)
+{
+	int filter;
+	struct kevent ke;
+
+	switch (type) {
+	case EVENT_TYPE_READ:
+		filter = EVFILT_READ;
+		break;
+	case EVENT_TYPE_WRITE:
+		filter = EVFILT_WRITE;
+		break;
+	default:
+		filter = 0;
+	}
+	EV_SET(&ke, sock, filter, EV_ADD, 0, 0, 0);
+	if (kevent(eloop.kqueuefd, &ke, 1, NULL, 0, NULL) == -1) {
+		wpa_printf(MSG_ERROR, "%s: kevent(ADD) for fd=%d failed: %s",
+			   __func__, sock, strerror(errno));
+		return -1;
+	}
+	return 0;
+}
+#endif /* CONFIG_ELOOP_KQUEUE */
+
+
 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;
+	struct epoll_event *temp_events;
 #endif /* CONFIG_ELOOP_EPOLL */
+#ifdef CONFIG_ELOOP_KQUEUE
+	struct kevent *temp_events;
+#endif /* CONFIG_ELOOP_EPOLL */
+#if defined(CONFIG_ELOOP_EPOLL) || defined(CONFIG_ELOOP_KQUEUE)
+	struct eloop_sock *temp_table;
+	int next;
+#endif /* CONFIG_ELOOP_EPOLL || CONFIG_ELOOP_KQUEUE */
 	struct eloop_sock *tmp;
 	int new_max_sock;
 
@@ -211,26 +299,28 @@
 		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,
+#if defined(CONFIG_ELOOP_EPOLL) || defined(CONFIG_ELOOP_KQUEUE)
+	if (new_max_sock >= eloop.max_fd) {
+		next = eloop.max_fd == 0 ? 16 : eloop.max_fd * 2;
+		temp_table = os_realloc_array(eloop.fd_table, next,
 					      sizeof(struct eloop_sock));
 		if (temp_table == NULL)
 			return -1;
 
-		eloop.epoll_max_fd = next;
-		eloop.epoll_table = temp_table;
+		eloop.max_fd = next;
+		eloop.fd_table = temp_table;
 	}
+#endif /* CONFIG_ELOOP_EPOLL || CONFIG_ELOOP_KQUEUE */
 
+#ifdef CONFIG_ELOOP_EPOLL
 	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));
+			wpa_printf(MSG_ERROR, "%s: malloc for epoll failed: %s",
+				   __func__, strerror(errno));
 			return -1;
 		}
 
@@ -238,6 +328,22 @@
 		eloop.epoll_events = temp_events;
 	}
 #endif /* CONFIG_ELOOP_EPOLL */
+#ifdef CONFIG_ELOOP_KQUEUE
+	if (eloop.count + 1 > eloop.kqueue_nevents) {
+		next = eloop.kqueue_nevents == 0 ? 8 : eloop.kqueue_nevents * 2;
+		temp_events = os_malloc(next * sizeof(*temp_events));
+		if (!temp_events) {
+			wpa_printf(MSG_ERROR,
+				   "%s: malloc for kqueue failed: %s",
+				   __func__, strerror(errno));
+			return -1;
+		}
+
+		os_free(eloop.kqueue_events);
+		eloop.kqueue_events = temp_events;
+		eloop.kqueue_nevents = next;
+	}
+#endif /* CONFIG_ELOOP_KQUEUE */
 
 	eloop_trace_sock_remove_ref(table);
 	tmp = os_realloc_array(table->table, table->count + 1,
@@ -256,38 +362,15 @@
 	table->table = tmp;
 	eloop.max_sock = new_max_sock;
 	eloop.count++;
-#ifndef CONFIG_ELOOP_EPOLL
 	table->changed = 1;
-#endif /* CONFIG_ELOOP_EPOLL */
 	eloop_trace_sock_add_ref(table);
 
-#ifdef CONFIG_ELOOP_EPOLL
-	os_memset(&ev, 0, sizeof(ev));
-	switch (table->type) {
-	case EVENT_TYPE_READ:
-		ev.events = EPOLLIN;
-		break;
-	case EVENT_TYPE_WRITE:
-		ev.events = EPOLLOUT;
-		break;
-	/*
-	 * Exceptions are always checked when using epoll, but I suppose it's
-	 * possible that someone registered a socket *only* for exception
-	 * handling.
-	 */
-	case EVENT_TYPE_EXCEPTION:
-		ev.events = EPOLLERR | EPOLLHUP;
-		break;
-	}
-	ev.data.fd = sock;
-	if (epoll_ctl(eloop.epollfd, EPOLL_CTL_ADD, sock, &ev) < 0) {
-		wpa_printf(MSG_ERROR, "%s: epoll_ctl(ADD) for fd=%d "
-			   "failed. %s\n", __func__, sock, strerror(errno));
+#if defined(CONFIG_ELOOP_EPOLL) || defined(CONFIG_ELOOP_KQUEUE)
+	if (eloop_sock_queue(sock, table->type) < 0)
 		return -1;
-	}
-	os_memcpy(&eloop.epoll_table[sock], &table->table[table->count - 1],
+	os_memcpy(&eloop.fd_table[sock], &table->table[table->count - 1],
 		  sizeof(struct eloop_sock));
-#endif /* CONFIG_ELOOP_EPOLL */
+#endif /* CONFIG_ELOOP_EPOLL || CONFIG_ELOOP_KQUEUE */
 	return 0;
 }
 
@@ -295,6 +378,9 @@
 static void eloop_sock_table_remove_sock(struct eloop_sock_table *table,
                                          int sock)
 {
+#ifdef CONFIG_ELOOP_KQUEUE
+	struct kevent ke;
+#endif /* CONFIG_ELOOP_KQUEUE */
 	int i;
 
 	if (table == NULL || table->table == NULL || table->count == 0)
@@ -314,18 +400,25 @@
 	}
 	table->count--;
 	eloop.count--;
-#ifndef CONFIG_ELOOP_EPOLL
 	table->changed = 1;
-#endif /* CONFIG_ELOOP_EPOLL */
 	eloop_trace_sock_add_ref(table);
 #ifdef CONFIG_ELOOP_EPOLL
 	if (epoll_ctl(eloop.epollfd, EPOLL_CTL_DEL, sock, NULL) < 0) {
-		wpa_printf(MSG_ERROR, "%s: epoll_ctl(DEL) for fd=%d "
-			   "failed. %s\n", __func__, sock, strerror(errno));
+		wpa_printf(MSG_ERROR, "%s: epoll_ctl(DEL) for fd=%d failed: %s",
+			   __func__, sock, strerror(errno));
 		return;
 	}
-	os_memset(&eloop.epoll_table[sock], 0, sizeof(struct eloop_sock));
+	os_memset(&eloop.fd_table[sock], 0, sizeof(struct eloop_sock));
 #endif /* CONFIG_ELOOP_EPOLL */
+#ifdef CONFIG_ELOOP_KQUEUE
+	EV_SET(&ke, sock, 0, EV_DELETE, 0, 0, 0);
+	if (kevent(eloop.kqueuefd, &ke, 1, NULL, 0, NULL) < 0) {
+		wpa_printf(MSG_ERROR, "%s: kevent(DEL) for fd=%d failed: %s",
+			   __func__, sock, strerror(errno));
+		return;
+	}
+	os_memset(&eloop.fd_table[sock], 0, sizeof(struct eloop_sock));
+#endif /* CONFIG_ELOOP_KQUEUE */
 }
 
 
@@ -518,16 +611,81 @@
 	int i;
 
 	for (i = 0; i < nfds; i++) {
-		table = &eloop.epoll_table[events[i].data.fd];
+		table = &eloop.fd_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 */
 
 
+#ifdef CONFIG_ELOOP_KQUEUE
+
+static void eloop_sock_table_dispatch(struct kevent *events, int nfds)
+{
+	struct eloop_sock *table;
+	int i;
+
+	for (i = 0; i < nfds; i++) {
+		table = &eloop.fd_table[events[i].ident];
+		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;
+	}
+}
+
+
+static int eloop_sock_table_requeue(struct eloop_sock_table *table)
+{
+	int i, r;
+
+	r = 0;
+	for (i = 0; i < table->count && table->table; i++) {
+		if (eloop_sock_queue(table->table[i].sock, table->type) == -1)
+			r = -1;
+	}
+	return r;
+}
+
+#endif /* CONFIG_ELOOP_KQUEUE */
+
+
+int eloop_sock_requeue(void)
+{
+	int r = 0;
+
+#ifdef CONFIG_ELOOP_KQUEUE
+	close(eloop.kqueuefd);
+	eloop.kqueuefd = kqueue();
+	if (eloop.kqueuefd < 0) {
+		wpa_printf(MSG_ERROR, "%s: kqueue failed: %s",
+			   __func__, strerror(errno));
+		return -1;
+	}
+
+	if (eloop_sock_table_requeue(&eloop.readers) < 0)
+		r = -1;
+	if (eloop_sock_table_requeue(&eloop.writers) < 0)
+		r = -1;
+	if (eloop_sock_table_requeue(&eloop.exceptions) < 0)
+		r = -1;
+#endif /* CONFIG_ELOOP_KQUEUE */
+
+	return r;
+}
+
+
 static void eloop_sock_table_destroy(struct eloop_sock_table *table)
 {
 	if (table) {
@@ -908,6 +1066,9 @@
 #ifdef CONFIG_ELOOP_EPOLL
 	int timeout_ms = -1;
 #endif /* CONFIG_ELOOP_EPOLL */
+#ifdef CONFIG_ELOOP_KQUEUE
+	struct timespec ts;
+#endif /* CONFIG_ELOOP_KQUEUE */
 	int res;
 	struct os_reltime tv, now;
 
@@ -923,6 +1084,20 @@
 	       (!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) {
@@ -938,6 +1113,10 @@
 			_tv.tv_sec = tv.sec;
 			_tv.tv_usec = tv.usec;
 #endif /* CONFIG_ELOOP_SELECT */
+#ifdef CONFIG_ELOOP_KQUEUE
+			ts.tv_sec = tv.sec;
+			ts.tv_nsec = tv.usec * 1000L;
+#endif /* CONFIG_ELOOP_KQUEUE */
 		}
 
 #ifdef CONFIG_ELOOP_POLL
@@ -963,6 +1142,15 @@
 					 eloop.count, timeout_ms);
 		}
 #endif /* CONFIG_ELOOP_EPOLL */
+#ifdef CONFIG_ELOOP_KQUEUE
+		if (eloop.count == 0) {
+			res = 0;
+		} else {
+			res = kevent(eloop.kqueuefd, NULL, 0,
+				     eloop.kqueue_events, eloop.kqueue_nevents,
+				     timeout ? &ts : NULL);
+		}
+#endif /* CONFIG_ELOOP_KQUEUE */
 		if (res < 0 && errno != EINTR && errno != 0) {
 			wpa_printf(MSG_ERROR, "eloop: %s: %s",
 #ifdef CONFIG_ELOOP_POLL
@@ -974,11 +1162,21 @@
 #ifdef CONFIG_ELOOP_EPOLL
 				   "epoll"
 #endif /* CONFIG_ELOOP_EPOLL */
+#ifdef CONFIG_ELOOP_KQUEUE
+				   "kqueue"
+#endif /* CONFIG_ELOOP_EKQUEUE */
+
 				   , 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);
@@ -998,6 +1196,19 @@
 		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,
@@ -1011,6 +1222,9 @@
 #ifdef CONFIG_ELOOP_EPOLL
 		eloop_sock_table_dispatch(eloop.epoll_events, res);
 #endif /* CONFIG_ELOOP_EPOLL */
+#ifdef CONFIG_ELOOP_KQUEUE
+		eloop_sock_table_dispatch(eloop.kqueue_events, res);
+#endif /* CONFIG_ELOOP_KQUEUE */
 	}
 
 	eloop.terminate = 0;
@@ -1063,17 +1277,23 @@
 	os_free(eloop.pollfds);
 	os_free(eloop.pollfds_map);
 #endif /* CONFIG_ELOOP_POLL */
+#if defined(CONFIG_ELOOP_EPOLL) || defined(CONFIG_ELOOP_KQUEUE)
+	os_free(eloop.fd_table);
+#endif /* CONFIG_ELOOP_EPOLL || CONFIG_ELOOP_KQUEUE */
 #ifdef CONFIG_ELOOP_EPOLL
-	os_free(eloop.epoll_table);
 	os_free(eloop.epoll_events);
 	close(eloop.epollfd);
 #endif /* CONFIG_ELOOP_EPOLL */
+#ifdef CONFIG_ELOOP_KQUEUE
+	os_free(eloop.kqueue_events);
+	close(eloop.kqueuefd);
+#endif /* CONFIG_ELOOP_KQUEUE */
 }
 
 
 int eloop_terminated(void)
 {
-	return eloop.terminate;
+	return eloop.terminate || eloop.pending_terminate;
 }
 
 
@@ -1106,6 +1326,17 @@
 	FD_SET(sock, &rfds);
 	select(sock + 1, &rfds, NULL, NULL, NULL);
 #endif /* defined(CONFIG_ELOOP_SELECT) || defined(CONFIG_ELOOP_EPOLL) */
+#ifdef CONFIG_ELOOP_KQUEUE
+	int kfd;
+	struct kevent ke1, ke2;
+
+	kfd = kqueue();
+	if (kfd == -1)
+		return;
+	EV_SET(&ke1, sock, EVFILT_READ, EV_ADD | EV_ONESHOT, 0, 0, 0);
+	kevent(kfd, &ke1, 1, &ke2, 1, NULL);
+	close(kfd);
+#endif /* CONFIG_ELOOP_KQUEUE */
 }
 
 #ifdef CONFIG_ELOOP_SELECT
diff --git a/src/utils/eloop.h b/src/utils/eloop.h
index 07b8c0d..97af16f 100644
--- a/src/utils/eloop.h
+++ b/src/utils/eloop.h
@@ -313,6 +313,14 @@
 				   void *user_data);
 
 /**
+ * eloop_sock_requeue - Requeue sockets
+ *
+ * Requeue sockets after forking because some implementations require this,
+ * such as epoll and kqueue.
+ */
+int eloop_sock_requeue(void);
+
+/**
  * eloop_run - Start the event loop
  *
  * Start the event loop and continue running as long as there are any
diff --git a/src/utils/eloop_win.c b/src/utils/eloop_win.c
index de47fb2..9c8b12b 100644
--- a/src/utils/eloop_win.c
+++ b/src/utils/eloop_win.c
@@ -692,3 +692,9 @@
 	WSAEventSelect(sock, event, 0);
 	WSACloseEvent(event);
 }
+
+
+int eloop_sock_requeue(void)
+{
+	return 0;
+}
diff --git a/src/utils/http_curl.c b/src/utils/http_curl.c
index 653eb54..d594398 100644
--- a/src/utils/http_curl.c
+++ b/src/utils/http_curl.c
@@ -26,6 +26,9 @@
 #include "common.h"
 #include "xml-utils.h"
 #include "http-utils.h"
+#ifdef EAP_TLS_OPENSSL
+#include "crypto/tls_openssl.h"
+#endif /* EAP_TLS_OPENSSL */
 
 
 struct http_ctx {
@@ -421,6 +424,28 @@
 
 IMPLEMENT_ASN1_FUNCTIONS(LogotypeExtn);
 
+#ifdef OPENSSL_IS_BORINGSSL
+#define sk_LogotypeInfo_num(st) \
+sk_num(CHECKED_CAST(_STACK *, STACK_OF(LogotypeInfo) *, (st)))
+#define sk_LogotypeInfo_value(st, i) (LogotypeInfo *) \
+sk_value(CHECKED_CAST(_STACK *, const STACK_OF(LogotypeInfo) *, (st)), (i))
+#define sk_LogotypeImage_num(st) \
+sk_num(CHECKED_CAST(_STACK *, STACK_OF(LogotypeImage) *, (st)))
+#define sk_LogotypeImage_value(st, i) (LogotypeImage *) \
+sk_value(CHECKED_CAST(_STACK *, const STACK_OF(LogotypeImage) *, (st)), (i))
+#define sk_LogotypeAudio_num(st) \
+sk_num(CHECKED_CAST(_STACK *, STACK_OF(LogotypeAudio) *, (st)))
+#define sk_LogotypeAudio_value(st, i) (LogotypeAudio *) \
+sk_value(CHECK_CAST(_STACK *, const STACK_OF(LogotypeAudio) *, (st)), (i))
+#define sk_HashAlgAndValue_num(st) \
+sk_num(CHECKED_CAST(_STACK *, STACK_OF(HashAlgAndValue) *, (st)))
+#define sk_HashAlgAndValue_value(st, i) (HashAlgAndValue *) \
+sk_value(CHECKED_CAST(_STACK *, const STACK_OF(HashAlgAndValue) *, (st)), (i))
+#define sk_ASN1_IA5STRING_num(st) \
+sk_num(CHECKED_CAST(_STACK *, STACK_OF(ASN1_IA5STRING) *, (st)))
+#define sk_ASN1_IA5STRING_value(st, i) (ASN1_IA5STRING *) \
+sk_value(CHECKED_CAST(_STACK *, const STACK_OF(ASN1_IA5STRING) *, (st)), (i))
+#else /* OPENSSL_IS_BORINGSSL */
 #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))
@@ -431,6 +456,7 @@
 #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))
+#endif /* OPENSSL_IS_BORINGSSL */
 
 
 static void add_logo(struct http_ctx *ctx, struct http_cert *hcert,
@@ -618,13 +644,25 @@
 	} else {
 		BIO_printf(out, "%*stype: default (1)\n", indent, "");
 	}
+	val = ASN1_INTEGER_get(info->fileSize);
+	BIO_printf(out, "%*sfileSize: %ld\n", indent, "", val);
 	val = ASN1_INTEGER_get(info->xSize);
 	BIO_printf(out, "%*sxSize: %ld\n", indent, "", val);
 	val = ASN1_INTEGER_get(info->ySize);
 	BIO_printf(out, "%*sySize: %ld\n", indent, "", val);
 	if (info->resolution) {
-		BIO_printf(out, "%*sresolution\n", indent, "");
-		/* TODO */
+		BIO_printf(out, "%*sresolution [%d]\n", indent, "",
+			   info->resolution->type);
+		switch (info->resolution->type) {
+		case 0:
+			val = ASN1_INTEGER_get(info->resolution->d.numBits);
+			BIO_printf(out, "%*snumBits: %ld\n", indent, "", val);
+			break;
+		case 1:
+			val = ASN1_INTEGER_get(info->resolution->d.tableSize);
+			BIO_printf(out, "%*stableSize: %ld\n", indent, "", val);
+			break;
+		}
 	}
 	if (info->language) {
 		BIO_printf(out, "%*slanguage: ", indent, "");
@@ -819,8 +857,10 @@
 	os_memset(hcert, 0, sizeof(*hcert));
 
 	*names = X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL);
-	if (*names)
+	if (*names) {
 		add_alt_names(ctx, hcert, *names);
+		sk_GENERAL_NAME_pop_free(*names, GENERAL_NAME_free);
+	}
 
 	add_logotype_ext(ctx, hcert, cert);
 }
@@ -981,6 +1021,26 @@
 	if (depth == 0 && preverify_ok && validate_server_cert(ctx, cert) < 0)
 		return 0;
 
+#ifdef OPENSSL_IS_BORINGSSL
+	if (depth == 0 && ctx->ocsp != NO_OCSP && preverify_ok) {
+		enum ocsp_result res;
+
+		res = check_ocsp_resp(ssl_ctx, ssl, cert, ctx->peer_issuer,
+				      ctx->peer_issuer_issuer);
+		if (res == OCSP_REVOKED) {
+			preverify_ok = 0;
+			wpa_printf(MSG_INFO, "OCSP: certificate revoked");
+			if (err == X509_V_OK)
+				X509_STORE_CTX_set_error(
+					x509_ctx, X509_V_ERR_CERT_REVOKED);
+		} else if (res != OCSP_GOOD && (ctx->ocsp == MANDATORY_OCSP)) {
+			preverify_ok = 0;
+			wpa_printf(MSG_INFO,
+				   "OCSP: bad certificate status response");
+		}
+	}
+#endif /* OPENSSL_IS_BORINGSSL */
+
 	if (!preverify_ok)
 		ctx->last_err = "TLS validation failed";
 
@@ -1156,6 +1216,7 @@
 		wpa_printf(MSG_INFO, "OpenSSL: Could not find current server certificate from OCSP response%s",
 			   (ctx->ocsp == MANDATORY_OCSP) ? "" :
 			   " (OCSP not required)");
+		OCSP_CERTID_free(id);
 		OCSP_BASICRESP_free(basic);
 		OCSP_RESPONSE_free(rsp);
 		if (ctx->ocsp == MANDATORY_OCSP)
@@ -1163,6 +1224,7 @@
 			ctx->last_err = "Could not find current server certificate from OCSP response";
 		return (ctx->ocsp == MANDATORY_OCSP) ? 0 : 1;
 	}
+	OCSP_CERTID_free(id);
 
 	if (!OCSP_check_validity(this_update, next_update, 5 * 60, -1)) {
 		tls_show_errors(__func__, "OpenSSL: OCSP status times invalid");
@@ -1273,6 +1335,16 @@
 #ifdef EAP_TLS_OPENSSL
 		curl_easy_setopt(curl, CURLOPT_SSL_CTX_FUNCTION, curl_cb_ssl);
 		curl_easy_setopt(curl, CURLOPT_SSL_CTX_DATA, ctx);
+#ifdef OPENSSL_IS_BORINGSSL
+		/* For now, using the CURLOPT_SSL_VERIFYSTATUS option only
+		 * with BoringSSL since the OpenSSL specific callback hack to
+		 * enable OCSP is not available with BoringSSL. The OCSP
+		 * implementation within libcurl is not sufficient for the
+		 * Hotspot 2.0 OSU needs, so cannot use this with OpenSSL.
+		 */
+		if (ctx->ocsp != NO_OCSP)
+			curl_easy_setopt(curl, CURLOPT_SSL_VERIFYSTATUS, 1L);
+#endif /* OPENSSL_IS_BORINGSSL */
 #endif /* EAP_TLS_OPENSSL */
 	} else {
 		curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);
diff --git a/src/utils/includes.h b/src/utils/includes.h
index 6c6ec87..75513fc 100644
--- a/src/utils/includes.h
+++ b/src/utils/includes.h
@@ -17,26 +17,22 @@
 #include "build_config.h"
 
 #include <stdlib.h>
+#include <stddef.h>
 #include <stdio.h>
 #include <stdarg.h>
 #include <string.h>
 #ifndef _WIN32_WCE
-#ifndef CONFIG_TI_COMPILER
 #include <signal.h>
 #include <sys/types.h>
-#endif /* CONFIG_TI_COMPILER */
 #include <errno.h>
 #endif /* _WIN32_WCE */
 #include <ctype.h>
 
-#ifndef CONFIG_TI_COMPILER
 #ifndef _MSC_VER
 #include <unistd.h>
 #endif /* _MSC_VER */
-#endif /* CONFIG_TI_COMPILER */
 
 #ifndef CONFIG_NATIVE_WINDOWS
-#ifndef CONFIG_TI_COMPILER
 #include <sys/socket.h>
 #include <netinet/in.h>
 #include <arpa/inet.h>
@@ -44,7 +40,6 @@
 #include <sys/uio.h>
 #include <sys/time.h>
 #endif /* __vxworks */
-#endif /* CONFIG_TI_COMPILER */
 #endif /* CONFIG_NATIVE_WINDOWS */
 
 #endif /* INCLUDES_H */
diff --git a/src/utils/os.h b/src/utils/os.h
index 8913854..9e496fb 100644
--- a/src/utils/os.h
+++ b/src/utils/os.h
@@ -247,11 +247,11 @@
 int os_file_exists(const char *fname);
 
 /**
- * os_fsync - Sync a file's (for a given stream) state with storage device
+ * 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_fsync(FILE *stream);
+int os_fdatasync(FILE *stream);
 
 /**
  * os_zalloc - Allocate and zero memory
@@ -653,4 +653,12 @@
 #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/src/utils/os_internal.c b/src/utils/os_internal.c
index b8fb2db..ed6eb3c 100644
--- a/src/utils/os_internal.c
+++ b/src/utils/os_internal.c
@@ -243,7 +243,7 @@
 }
 
 
-int os_fsync(FILE *stream)
+int os_fdatasync(FILE *stream)
 {
 	return 0;
 }
diff --git a/src/utils/os_none.c b/src/utils/os_none.c
index 96d243d..0c3214d 100644
--- a/src/utils/os_none.c
+++ b/src/utils/os_none.c
@@ -102,7 +102,7 @@
 }
 
 
-int os_fsync(FILE *stream)
+int os_fdatasync(FILE *stream)
 {
 	return 0;
 }
diff --git a/src/utils/os_unix.c b/src/utils/os_unix.c
index ac73f7a..0118d98 100644
--- a/src/utils/os_unix.c
+++ b/src/utils/os_unix.c
@@ -17,6 +17,12 @@
 #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"
 
@@ -36,7 +42,7 @@
 	struct dl_list list;
 	size_t len;
 	WPA_TRACE_INFO
-};
+} __attribute__((aligned(16)));
 
 #endif /* WPA_TRACE */
 
@@ -63,6 +69,7 @@
 
 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)
@@ -95,6 +102,23 @@
 			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__ */
 }
 
 
@@ -226,6 +250,9 @@
 	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");
@@ -345,6 +372,7 @@
 	if (total)
 		wpa_printf(MSG_INFO, "MEMLEAK: total %lu bytes",
 			   (unsigned long) total);
+	wpa_trace_deinit();
 #endif /* WPA_TRACE */
 }
 
@@ -415,10 +443,21 @@
 }
 
 
-int os_fsync(FILE *stream)
+int os_fdatasync(FILE *stream)
 {
-	if (!fflush(stream))
+	if (!fflush(stream)) {
+#ifdef __linux__
+		return fdatasync(fileno(stream));
+#else /* !__linux__ */
+#ifdef F_FULLFSYNC
+		/* OS X does not implement fdatasync(). */
+		return fcntl(fileno(stream), F_FULLFSYNC);
+#else /* F_FULLFSYNC */
 		return fsync(fileno(stream));
+#endif /* F_FULLFSYNC */
+#endif /* __linux__ */
+	}
+
 	return -1;
 }
 
@@ -556,6 +595,78 @@
 	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)
diff --git a/src/utils/os_win32.c b/src/utils/os_win32.c
index 890abf4..dea27b9 100644
--- a/src/utils/os_win32.c
+++ b/src/utils/os_win32.c
@@ -216,18 +216,18 @@
 }
 
 
-int os_fsync(FILE *stream)
+int os_fdatasync(FILE *stream)
 {
-	HANDLE hFile;
+	HANDLE h;
 
 	if (stream == NULL)
 		return -1;
 
-	hFile = _get_osfhandle(_fileno(stream));
-	if (hFile == INVALID_HANDLE_VALUE)
+	h = (HANDLE) _get_osfhandle(_fileno(stream));
+	if (h == INVALID_HANDLE_VALUE)
 		return -1;
 
-	if (!FlushFileBuffers(hFile))
+	if (!FlushFileBuffers(h))
 		return -1;
 
 	return 0;
diff --git a/src/utils/pcsc_funcs.c b/src/utils/pcsc_funcs.c
index 6f5ea93..2f1157b 100644
--- a/src/utils/pcsc_funcs.c
+++ b/src/utils/pcsc_funcs.c
@@ -275,7 +275,7 @@
 	pos++;
 	if (pos >= end)
 		return -1;
-	if ((pos + pos[0]) < end)
+	if (pos[0] < end - pos)
 		end = pos + 1 + pos[0];
 	pos++;
 	wpa_hexdump(MSG_DEBUG, "SCARD: file header FSP template",
@@ -1385,7 +1385,7 @@
 		end = buf + len;
 
 		/* RES */
-		if (pos[0] > RES_MAX_LEN || pos + pos[0] > end) {
+		if (pos[0] > RES_MAX_LEN || pos[0] > end - pos) {
 			wpa_printf(MSG_DEBUG, "SCARD: Invalid RES");
 			return -1;
 		}
@@ -1395,7 +1395,7 @@
 		wpa_hexdump(MSG_DEBUG, "SCARD: RES", res, *res_len);
 
 		/* CK */
-		if (pos[0] != CK_LEN || pos + CK_LEN > end) {
+		if (pos[0] != CK_LEN || CK_LEN > end - pos) {
 			wpa_printf(MSG_DEBUG, "SCARD: Invalid CK");
 			return -1;
 		}
@@ -1405,7 +1405,7 @@
 		wpa_hexdump(MSG_DEBUG, "SCARD: CK", ck, CK_LEN);
 
 		/* IK */
-		if (pos[0] != IK_LEN || pos + IK_LEN > end) {
+		if (pos[0] != IK_LEN || IK_LEN > end - pos) {
 			wpa_printf(MSG_DEBUG, "SCARD: Invalid IK");
 			return -1;
 		}
diff --git a/src/utils/radiotap.c b/src/utils/radiotap.c
index f8f815a..c9a5023 100644
--- a/src/utils/radiotap.c
+++ b/src/utils/radiotap.c
@@ -123,13 +123,13 @@
 
 	/* find payload start allowing for extended bitmap(s) */
 
-	if (iterator->_bitmap_shifter & (1<<IEEE80211_RADIOTAP_EXT)) {
+	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) &
-					(1 << IEEE80211_RADIOTAP_EXT)) {
+		       BIT(IEEE80211_RADIOTAP_EXT)) {
 			iterator->_arg += sizeof(uint32_t);
 
 			/*
diff --git a/src/utils/trace.c b/src/utils/trace.c
index 8484d27..d72cf60 100644
--- a/src/utils/trace.c
+++ b/src/utils/trace.c
@@ -366,4 +366,13 @@
 	}
 }
 
+
+void wpa_trace_deinit(void)
+{
+#ifdef WPA_TRACE_BFD
+	free(syms);
+	syms = NULL;
+#endif /* WPA_TRACE_BFD */
+}
+
 #endif /* WPA_TRACE */
diff --git a/src/utils/trace.h b/src/utils/trace.h
index 43ed86c..d1636de 100644
--- a/src/utils/trace.h
+++ b/src/utils/trace.h
@@ -66,4 +66,6 @@
 
 #endif /* WPA_TRACE_BFD */
 
+void wpa_trace_deinit(void);
+
 #endif /* TRACE_H */
diff --git a/src/utils/utils_module_tests.c b/src/utils/utils_module_tests.c
index b2c7e08..41511b9 100644
--- a/src/utils/utils_module_tests.c
+++ b/src/utils/utils_module_tests.c
@@ -14,6 +14,8 @@
 #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 {
@@ -44,6 +46,7 @@
 	char buf[100];
 	u8 bin[100];
 	int errors = 0;
+	int array[10];
 
 	wpa_printf(MSG_INFO, "printf encode/decode tests");
 
@@ -92,9 +95,24 @@
 	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;
@@ -336,7 +354,7 @@
 
 static int common_tests(void)
 {
-	char buf[3];
+	char buf[3], longbuf[100];
 	u8 addr[ETH_ALEN] = { 1, 2, 3, 4, 5, 6 };
 	u8 bin[3];
 	int errors = 0;
@@ -409,6 +427,11 @@
 		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;
@@ -418,6 +441,403 @@
 }
 
 
+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;
@@ -430,6 +850,10 @@
 	    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;
 
diff --git a/src/utils/wpa_debug.c b/src/utils/wpa_debug.c
index 3c26301..f7acf6b 100644
--- a/src/utils/wpa_debug.c
+++ b/src/utils/wpa_debug.c
@@ -148,7 +148,7 @@
 		strtok_r(line, " ", &tmp2);
 		tmp_path = strtok_r(NULL, " ", &tmp2);
 		fstype = strtok_r(NULL, " ", &tmp2);
-		if (strcmp(fstype, "debugfs") == 0) {
+		if (fstype && strcmp(fstype, "debugfs") == 0) {
 			path = tmp_path;
 			break;
 		}
@@ -517,16 +517,18 @@
 {
 #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;
-	}
+	char *tmp;
+
+	if (!last_path)
+		return 0; /* logfile not used */
+
+	tmp = os_strdup(last_path);
+	if (!tmp)
+		return -1;
+
+	wpa_debug_close_file();
+	rv = wpa_debug_open_file(tmp);
+	os_free(tmp);
 	return rv;
 #else /* CONFIG_DEBUG_FILE */
 	return 0;
@@ -819,3 +821,42 @@
 	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/src/utils/wpa_debug.h b/src/utils/wpa_debug.h
index 87bd7fa..17d8f96 100644
--- a/src/utils/wpa_debug.h
+++ b/src/utils/wpa_debug.h
@@ -364,4 +364,7 @@
 #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/src/utils/wpabuf.c b/src/utils/wpabuf.c
index 7aafa0a..11e7323 100644
--- a/src/utils/wpabuf.c
+++ b/src/utils/wpabuf.c
@@ -17,7 +17,7 @@
 
 struct wpabuf_trace {
 	unsigned int magic;
-};
+} __attribute__((aligned(8)));
 
 static struct wpabuf_trace * wpabuf_get_trace(const struct wpabuf *buf)
 {
diff --git a/src/utils/wpabuf.h b/src/utils/wpabuf.h
index c3ef1ba..9cd8a07 100644
--- a/src/utils/wpabuf.h
+++ b/src/utils/wpabuf.h
@@ -81,7 +81,7 @@
 
 static inline const u8 * wpabuf_head_u8(const struct wpabuf *buf)
 {
-	return wpabuf_head(buf);
+	return (const u8 *) wpabuf_head(buf);
 }
 
 /**
@@ -96,42 +96,42 @@
 
 static inline u8 * wpabuf_mhead_u8(struct wpabuf *buf)
 {
-	return wpabuf_mhead(buf);
+	return (u8 *) wpabuf_mhead(buf);
 }
 
 static inline void wpabuf_put_u8(struct wpabuf *buf, u8 data)
 {
-	u8 *pos = wpabuf_put(buf, 1);
+	u8 *pos = (u8 *) wpabuf_put(buf, 1);
 	*pos = data;
 }
 
 static inline void wpabuf_put_le16(struct wpabuf *buf, u16 data)
 {
-	u8 *pos = wpabuf_put(buf, 2);
+	u8 *pos = (u8 *) 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);
+	u8 *pos = (u8 *) 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);
+	u8 *pos = (u8 *) 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);
+	u8 *pos = (u8 *) 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);
+	u8 *pos = (u8 *) wpabuf_put(buf, 4);
 	WPA_PUT_BE32(pos, data);
 }
 
diff --git a/src/utils/xml_libxml2.c b/src/utils/xml_libxml2.c
index c928394..7b6d276 100644
--- a/src/utils/xml_libxml2.c
+++ b/src/utils/xml_libxml2.c
@@ -212,6 +212,8 @@
 	xmlDocSetRootElement(doc, n);
 	xmlDocDumpFormatMemory(doc, &buf, &bufsiz, 0);
 	xmlFreeDoc(doc);
+	if (!buf)
+		return NULL;
 	pos = (char *) buf;
 	if (strncmp(pos, "<?xml", 5) == 0) {
 		pos = strchr(pos, '>');
diff --git a/src/wps/http_client.c b/src/wps/http_client.c
index 0290013..cdf3a51 100644
--- a/src/wps/http_client.c
+++ b/src/wps/http_client.c
@@ -85,15 +85,16 @@
 {
 	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) wpabuf_len(c->req) - c->req_pos);
+		   (unsigned long) send_len);
 
-	res = send(c->sd, wpabuf_head_u8(c->req) + c->req_pos,
-		   wpabuf_len(c->req) - c->req_pos, 0);
+	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));
@@ -102,12 +103,11 @@
 		return;
 	}
 
-	if ((size_t) res < wpabuf_len(c->req) - c->req_pos) {
+	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) wpabuf_len(c->req) - c->req_pos -
-			   res);
+			   (unsigned long) send_len - res);
 		c->req_pos += res;
 		return;
 	}
@@ -146,24 +146,20 @@
 	c->cb_ctx = cb_ctx;
 
 	c->sd = socket(AF_INET, SOCK_STREAM, 0);
-	if (c->sd < 0) {
-		http_client_free(c);
-		return NULL;
-	}
+	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));
-		http_client_free(c);
-		return NULL;
+		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));
-			http_client_free(c);
-			return NULL;
+			goto fail;
 		}
 
 		/*
@@ -173,20 +169,18 @@
 	}
 
 	if (eloop_register_sock(c->sd, EVENT_TYPE_WRITE, http_client_tx_ready,
-				c, NULL)) {
-		http_client_free(c);
-		return NULL;
-	}
-
-	if (eloop_register_timeout(HTTP_CLIENT_TIMEOUT_SEC, 0,
-				   http_client_timeout, c, NULL)) {
-		http_client_free(c);
-		return NULL;
-	}
+				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;
 }
 
 
diff --git a/src/wps/http_server.c b/src/wps/http_server.c
index ac088c4..507abe8 100644
--- a/src/wps/http_server.c
+++ b/src/wps/http_server.c
@@ -277,11 +277,9 @@
 			   "%s", srv->port, strerror(errno));
 		goto fail;
 	}
-	if (listen(srv->fd, 10 /* max backlog */) < 0)
-		goto fail;
-	if (fcntl(srv->fd, F_SETFL, O_NONBLOCK) < 0)
-		goto fail;
-	if (eloop_register_sock(srv->fd, EVENT_TYPE_READ, http_server_cb,
+	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;
 
diff --git a/src/wps/httpread.c b/src/wps/httpread.c
index 180b572..7a2ba50 100644
--- a/src/wps/httpread.c
+++ b/src/wps/httpread.c
@@ -278,8 +278,6 @@
 			}
 		}
 		*uri = 0;       /* null terminate */
-		while (isgraph(*hbp))
-			hbp++;
 		while (*hbp == ' ' || *hbp == '\t')
 			hbp++;
 		/* get version */
@@ -506,10 +504,13 @@
 			    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) {
+			    new_alloc_nbytes > h->max_bytes +
+			    HTTPREAD_BODYBUF_DELTA) {
 				wpa_printf(MSG_DEBUG,
-					   "httpread: Unacceptable body length %d",
-					   new_alloc_nbytes);
+					   "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))
diff --git a/src/wps/ndef.c b/src/wps/ndef.c
index 8d1ce1e..bb3c055 100644
--- a/src/wps/ndef.c
+++ b/src/wps/ndef.c
@@ -45,9 +45,14 @@
 			return -1;
 		record->payload_length = *pos++;
 	} else {
+		u32 len;
+
 		if (size < 6)
 			return -1;
-		record->payload_length = ntohl(*(u32 *)pos);
+		len = WPA_GET_BE32(pos);
+		if (len > size - 6 || len > 20000)
+			return -1;
+		record->payload_length = len;
 		pos += sizeof(u32);
 	}
 
@@ -68,7 +73,8 @@
 	pos += record->payload_length;
 
 	record->total_length = pos - data;
-	if (record->total_length > size)
+	if (record->total_length > size ||
+	    record->total_length < record->payload_length)
 		return -1;
 	return 0;
 }
diff --git a/src/wps/wps.c b/src/wps/wps.c
index 498f11f..7c6dcb2 100644
--- a/src/wps/wps.c
+++ b/src/wps/wps.c
@@ -19,6 +19,10 @@
 int wps_version_number = 0x20;
 int wps_testing_dummy_cred = 0;
 int wps_corrupt_pkhash = 0;
+int wps_force_auth_types_in_use = 0;
+u16 wps_force_auth_types = 0;
+int wps_force_encr_types_in_use = 0;
+u16 wps_force_encr_types = 0;
 #endif /* CONFIG_WPS_TESTING */
 
 
@@ -355,16 +359,16 @@
 int wps_ap_priority_compar(const struct wpabuf *wps_a,
 			   const struct wpabuf *wps_b)
 {
-	struct wps_parse_attr attr_a, attr_b;
+	struct wps_parse_attr attr;
 	int sel_a, sel_b;
 
-	if (wps_a == NULL || wps_parse_msg(wps_a, &attr_a) < 0)
+	if (wps_a == NULL || wps_parse_msg(wps_a, &attr) < 0)
 		return 1;
-	if (wps_b == NULL || wps_parse_msg(wps_b, &attr_b) < 0)
-		return -1;
+	sel_a = attr.selected_registrar && *attr.selected_registrar != 0;
 
-	sel_a = attr_a.selected_registrar && *attr_a.selected_registrar != 0;
-	sel_b = attr_b.selected_registrar && *attr_b.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;
diff --git a/src/wps/wps.h b/src/wps/wps.h
index 2c91d16..2505d2d 100644
--- a/src/wps/wps.h
+++ b/src/wps/wps.h
@@ -1,6 +1,6 @@
 /*
  * Wi-Fi Protected Setup
- * Copyright (c) 2007-2013, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2007-2016, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -664,6 +664,16 @@
 	u16 encr_types;
 
 	/**
+	 * encr_types_rsn - Enabled encryption types for RSN (WPS_ENCR_*)
+	 */
+	u16 encr_types_rsn;
+
+	/**
+	 * encr_types_wpa - Enabled encryption types for WPA (WPS_ENCR_*)
+	 */
+	u16 encr_types_wpa;
+
+	/**
 	 * auth_types - Authentication types (bit field of WPS_AUTH_*)
 	 */
 	u16 auth_types;
@@ -827,7 +837,7 @@
 
 unsigned int wps_pin_checksum(unsigned int pin);
 unsigned int wps_pin_valid(unsigned int pin);
-unsigned int wps_generate_pin(void);
+int wps_generate_pin(unsigned int *pin);
 int wps_pin_str_valid(const char *pin);
 void wps_free_pending_msgs(struct upnp_pending_message *msgs);
 
diff --git a/src/wps/wps_attr_build.c b/src/wps/wps_attr_build.c
index b689357..748620f 100644
--- a/src/wps/wps_attr_build.c
+++ b/src/wps/wps_attr_build.c
@@ -1,6 +1,6 @@
 /*
  * Wi-Fi Protected Setup - attribute building
- * Copyright (c) 2008, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2008-2016, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -298,7 +298,16 @@
 	auth_types &= ~WPS_AUTH_WPA;
 	auth_types &= ~WPS_AUTH_WPA2;
 	auth_types &= ~WPS_AUTH_SHARED;
-	wpa_printf(MSG_DEBUG, "WPS:  * Authentication Type Flags");
+#ifdef CONFIG_WPS_TESTING
+	if (wps_force_auth_types_in_use) {
+		wpa_printf(MSG_DEBUG,
+			   "WPS: Testing - replace auth type 0x%x with 0x%x",
+			   auth_types, wps_force_auth_types);
+		auth_types = wps_force_auth_types;
+	}
+#endif /* CONFIG_WPS_TESTING */
+	wpa_printf(MSG_DEBUG, "WPS:  * Authentication Type Flags (0x%x)",
+		   auth_types);
 	wpabuf_put_be16(msg, ATTR_AUTH_TYPE_FLAGS);
 	wpabuf_put_be16(msg, 2);
 	wpabuf_put_be16(msg, auth_types);
@@ -310,7 +319,16 @@
 {
 	u16 encr_types = WPS_ENCR_TYPES;
 	encr_types &= ~WPS_ENCR_WEP;
-	wpa_printf(MSG_DEBUG, "WPS:  * Encryption Type Flags");
+#ifdef CONFIG_WPS_TESTING
+	if (wps_force_encr_types_in_use) {
+		wpa_printf(MSG_DEBUG,
+			   "WPS: Testing - replace encr type 0x%x with 0x%x",
+			   encr_types, wps_force_encr_types);
+		encr_types = wps_force_encr_types;
+	}
+#endif /* CONFIG_WPS_TESTING */
+	wpa_printf(MSG_DEBUG, "WPS:  * Encryption Type Flags (0x%x)",
+		   encr_types);
 	wpabuf_put_be16(msg, ATTR_ENCR_TYPE_FLAGS);
 	wpabuf_put_be16(msg, 2);
 	wpabuf_put_be16(msg, encr_types);
diff --git a/src/wps/wps_attr_parse.c b/src/wps/wps_attr_parse.c
index 11a967b..756d57e 100644
--- a/src/wps/wps_attr_parse.c
+++ b/src/wps/wps_attr_parse.c
@@ -83,10 +83,10 @@
 	const u8 *end = pos + len;
 	u8 id, elen;
 
-	while (pos + 2 <= end) {
+	while (end - pos >= 2) {
 		id = *pos++;
 		elen = *pos++;
-		if (pos + elen > end)
+		if (elen > end - pos)
 			break;
 		if (wps_set_vendor_ext_wfa_subelem(attr, id, elen, pos) < 0)
 			return -1;
diff --git a/src/wps/wps_attr_parse.h b/src/wps/wps_attr_parse.h
index 82c4739..8188fe9 100644
--- a/src/wps/wps_attr_parse.h
+++ b/src/wps/wps_attr_parse.h
@@ -59,43 +59,44 @@
 
 	/* variable length fields */
 	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 *public_key;
-	size_t public_key_len;
 	const u8 *encr_settings;
-	size_t encr_settings_len;
 	const u8 *ssid; /* <= 32 octets */
-	size_t ssid_len;
 	const u8 *network_key; /* <= 64 octets */
-	size_t network_key_len;
 	const u8 *authorized_macs; /* <= 30 octets */
-	size_t authorized_macs_len;
 	const u8 *sec_dev_type_list; /* <= 128 octets */
-	size_t sec_dev_type_list_len;
 	const u8 *oob_dev_password; /* 38..54 octets */
-	size_t oob_dev_password_len;
+	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
-	const u8 *cred[MAX_CRED_COUNT];
-	size_t cred_len[MAX_CRED_COUNT];
-	size_t num_cred;
-
 #define MAX_REQ_DEV_TYPE_COUNT 10
-	const u8 *req_dev_type[MAX_REQ_DEV_TYPE_COUNT];
-	size_t num_req_dev_type;
 
+	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];
-	size_t vendor_ext_len[MAX_WPS_PARSE_VENDOR_EXT];
-	size_t num_vendor_ext;
 };
 
 int wps_parse_msg(const struct wpabuf *msg, struct wps_parse_attr *attr);
diff --git a/src/wps/wps_common.c b/src/wps/wps_common.c
index 16d466e..c6a1cfd 100644
--- a/src/wps/wps_common.c
+++ b/src/wps/wps_common.c
@@ -235,20 +235,18 @@
  * wps_generate_pin - Generate a random PIN
  * Returns: Eight digit PIN (i.e., including the checksum digit)
  */
-unsigned int wps_generate_pin(void)
+int wps_generate_pin(unsigned int *pin)
 {
 	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;
-	}
+	if (random_get_bytes((unsigned char *) &val, sizeof(val)) < 0)
+		return -1;
 	val %= 10000000;
 
 	/* Append checksum digit */
-	return val * 10 + wps_pin_checksum(val);
+	*pin = val * 10 + wps_pin_checksum(val);
+	return 0;
 }
 
 
@@ -528,7 +526,7 @@
 {
 	u16 methods = 0;
 
-	if (str == NULL) {
+	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;
diff --git a/src/wps/wps_defs.h b/src/wps/wps_defs.h
index a23b979..301864d 100644
--- a/src/wps/wps_defs.h
+++ b/src/wps/wps_defs.h
@@ -14,6 +14,10 @@
 extern int wps_version_number;
 extern int wps_testing_dummy_cred;
 extern int wps_corrupt_pkhash;
+extern int wps_force_auth_types_in_use;
+extern u16 wps_force_auth_types;
+extern int wps_force_encr_types_in_use;
+extern u16 wps_force_encr_types;
 #define WPS_VERSION wps_version_number
 
 #else /* CONFIG_WPS_TESTING */
diff --git a/src/wps/wps_enrollee.c b/src/wps/wps_enrollee.c
index 89957b1..9321b72 100644
--- a/src/wps/wps_enrollee.c
+++ b/src/wps/wps_enrollee.c
@@ -759,7 +759,7 @@
 
 
 static int wps_process_creds(struct wps_data *wps, const u8 *cred[],
-			     size_t cred_len[], size_t num_cred, int wps2)
+			     u16 cred_len[], unsigned int num_cred, int wps2)
 {
 	size_t i;
 	int ok = 0;
@@ -799,6 +799,7 @@
 				     struct wpabuf *attrs, int wps2)
 {
 	struct wps_credential cred;
+	int ret = 0;
 
 	if (!wps->wps->ap)
 		return 0;
@@ -877,10 +878,10 @@
 	if (wps->wps->cred_cb) {
 		cred.cred_attr = wpabuf_head(attrs);
 		cred.cred_attr_len = wpabuf_len(attrs);
-		wps->wps->cred_cb(wps->wps->cb_ctx, &cred);
+		ret = wps->wps->cred_cb(wps->wps->cb_ctx, &cred);
 	}
 
-	return 0;
+	return ret;
 }
 
 
diff --git a/src/wps/wps_er.c b/src/wps/wps_er.c
index 078ff72..b840acd 100644
--- a/src/wps/wps_er.c
+++ b/src/wps/wps_er.c
@@ -1649,11 +1649,15 @@
 	case HTTP_CLIENT_OK:
 		wpa_printf(MSG_DEBUG, "WPS ER: PutMessage OK");
 		reply = http_client_get_body(c);
-		if (reply == NULL)
+		if (reply)
+			msg = os_zalloc(wpabuf_len(reply) + 1);
+		if (msg == NULL) {
+			if (ap->wps) {
+				wps_deinit(ap->wps);
+				ap->wps = NULL;
+			}
 			break;
-		msg = os_zalloc(wpabuf_len(reply) + 1);
-		if (msg == NULL)
-			break;
+		}
 		os_memcpy(msg, wpabuf_head(reply), wpabuf_len(reply));
 		break;
 	case HTTP_CLIENT_FAILED:
@@ -1709,21 +1713,30 @@
 	url = http_client_url_parse(ap->control_url, &dst, &path);
 	if (url == NULL) {
 		wpa_printf(MSG_DEBUG, "WPS ER: Failed to parse controlURL");
-		return;
+		goto fail;
 	}
 
 	buf = wps_er_soap_hdr(msg, "PutMessage", "NewInMessage", path, &dst,
 			      &len_ptr, &body_ptr);
 	os_free(url);
 	if (buf == NULL)
-		return;
+		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)
+	if (ap->http == NULL) {
 		wpabuf_free(buf);
+		goto fail;
+	}
+	return;
+
+fail:
+	if (ap->wps) {
+		wps_deinit(ap->wps);
+		ap->wps = NULL;
+	}
 }
 
 
diff --git a/src/wps/wps_er_ssdp.c b/src/wps/wps_er_ssdp.c
index e381fec..280b2b3 100644
--- a/src/wps/wps_er_ssdp.c
+++ b/src/wps/wps_er_ssdp.c
@@ -78,9 +78,7 @@
 			if (os_strstr(start, "ssdp:byebye"))
 				byebye = 1;
 		} else if (os_strncasecmp(start, "CACHE-CONTROL:", 14) == 0) {
-			start += 9;
-			while (*start == ' ')
-				start++;
+			start += 14;
 			pos2 = os_strstr(start, "max-age=");
 			if (pos2 == NULL)
 				continue;
diff --git a/src/wps/wps_registrar.c b/src/wps/wps_registrar.c
index 8bcf2b3..25e88d5 100644
--- a/src/wps/wps_registrar.c
+++ b/src/wps/wps_registrar.c
@@ -1,6 +1,6 @@
 /*
  * Wi-Fi Protected Setup - Registrar
- * Copyright (c) 2008-2013, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2008-2016, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -1606,6 +1606,9 @@
 	wps->cred.ssid_len = wps->wps->ssid_len;
 
 	/* Select the best authentication and encryption type */
+	wpa_printf(MSG_DEBUG,
+		   "WPS: Own auth types 0x%x - masked Enrollee auth types 0x%x",
+		   wps->wps->auth_types, wps->auth_type);
 	if (wps->auth_type & WPS_AUTH_WPA2PSK)
 		wps->auth_type = WPS_AUTH_WPA2PSK;
 	else if (wps->auth_type & WPS_AUTH_WPAPSK)
@@ -1619,6 +1622,14 @@
 	}
 	wps->cred.auth_type = wps->auth_type;
 
+	wpa_printf(MSG_DEBUG,
+		   "WPS: Own encr types 0x%x (rsn: 0x%x, wpa: 0x%x) - masked Enrollee encr types 0x%x",
+		   wps->wps->encr_types, wps->wps->encr_types_rsn,
+		   wps->wps->encr_types_wpa, wps->encr_type);
+	if (wps->wps->ap && wps->auth_type == WPS_AUTH_WPA2PSK)
+		wps->encr_type &= wps->wps->encr_types_rsn;
+	else if (wps->wps->ap && wps->auth_type == WPS_AUTH_WPAPSK)
+		wps->encr_type &= wps->wps->encr_types_wpa;
 	if (wps->auth_type == WPS_AUTH_WPA2PSK ||
 	    wps->auth_type == WPS_AUTH_WPAPSK) {
 		if (wps->encr_type & WPS_ENCR_AES)
@@ -2343,6 +2354,23 @@
 
 	wpa_printf(MSG_DEBUG, "WPS: Enrollee Authentication Type flags 0x%x",
 		   auth_types);
+#ifdef WPS_WORKAROUNDS
+	/*
+	 * Some deployed implementations seem to advertise incorrect information
+	 * in this attribute. A value of 0x1b (WPA2 + WPA + WPAPSK + OPEN, but
+	 * no WPA2PSK) has been reported to be used. Add WPA2PSK to the list to
+	 * avoid issues with building Credentials that do not use the strongest
+	 * actually supported authentication option (that device does support
+	 * WPA2PSK even when it does not claim it here).
+	 */
+	if ((auth_types &
+	     (WPS_AUTH_WPA2 | WPS_AUTH_WPA2PSK | WPS_AUTH_WPAPSK)) ==
+	    (WPS_AUTH_WPA2 | WPS_AUTH_WPAPSK)) {
+		wpa_printf(MSG_DEBUG,
+			   "WPS: Workaround - assume Enrollee supports WPA2PSK based on claimed WPA2 support");
+		auth_types |= WPS_AUTH_WPA2PSK;
+	}
+#endif /* WPS_WORKAROUNDS */
 	wps->auth_type = wps->wps->auth_types & auth_types;
 	if (wps->auth_type == 0) {
 		wpa_printf(MSG_DEBUG, "WPS: No match in supported "
@@ -2605,13 +2633,16 @@
 		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;
-			sha256_vector(1, addr, &attr->public_key_len, hash);
+			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) {
diff --git a/src/wps/wps_upnp.c b/src/wps/wps_upnp.c
index 933d734..0c458c6 100644
--- a/src/wps/wps_upnp.c
+++ b/src/wps/wps_upnp.c
@@ -695,6 +695,7 @@
 	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);
@@ -743,8 +744,10 @@
 		subscription_destroy(s);
 		return NULL;
 	}
-	wpa_printf(MSG_DEBUG, "WPS UPnP: Subscription %p started with %s",
-		   s, callback_urls);
+	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;
@@ -1079,6 +1082,7 @@
 void upnp_wps_device_deinit(struct upnp_wps_device_sm *sm, void *priv)
 {
 	struct upnp_wps_device_interface *iface;
+	struct upnp_wps_peer *peer;
 
 	if (!sm)
 		return;
@@ -1099,8 +1103,13 @@
 					    iface->wps->registrar);
 	dl_list_del(&iface->list);
 
-	if (iface->peer.wps)
-		wps_deinit(iface->peer.wps);
+	while ((peer = dl_list_first(&iface->peers, struct upnp_wps_peer,
+				     list))) {
+		if (peer->wps)
+			wps_deinit(peer->wps);
+		dl_list_del(&peer->list);
+		os_free(peer);
+	}
 	os_free(iface->ctx->ap_pin);
 	os_free(iface->ctx);
 	os_free(iface);
@@ -1138,6 +1147,7 @@
 	}
 	wpa_printf(MSG_DEBUG, "WPS UPnP: Init interface instance %p", iface);
 
+	dl_list_init(&iface->peers);
 	iface->ctx = ctx;
 	iface->wps = wps;
 	iface->priv = priv;
diff --git a/src/wps/wps_upnp.h b/src/wps/wps_upnp.h
index 87b7ab1..b6f6df5 100644
--- a/src/wps/wps_upnp.h
+++ b/src/wps/wps_upnp.h
@@ -11,11 +11,14 @@
 #ifndef WPS_UPNP_H
 #define WPS_UPNP_H
 
+#include "utils/list.h"
+
 struct upnp_wps_device_sm;
 struct wps_context;
 struct wps_data;
 
 struct upnp_wps_peer {
+	struct dl_list list;
 	struct wps_data *wps;
 };
 
diff --git a/src/wps/wps_upnp_ap.c b/src/wps/wps_upnp_ap.c
index 2949f14..cca3905 100644
--- a/src/wps/wps_upnp_ap.c
+++ b/src/wps/wps_upnp_ap.c
@@ -34,10 +34,8 @@
 
 	wpa_hexdump_buf(MSG_MSGDUMP, "WPS: SetSelectedRegistrar attributes",
 			msg);
-	if (wps_validate_upnp_set_selected_registrar(msg) < 0)
-		return -1;
-
-	if (wps_parse_msg(msg, &attr) < 0)
+	if (wps_validate_upnp_set_selected_registrar(msg) < 0 ||
+	    wps_parse_msg(msg, &attr) < 0)
 		return -1;
 
 	s->reg = reg;
diff --git a/src/wps/wps_upnp_event.c b/src/wps/wps_upnp_event.c
index 2c8ed4f..94aae75 100644
--- a/src/wps/wps_upnp_event.c
+++ b/src/wps/wps_upnp_event.c
@@ -276,11 +276,9 @@
 	 * 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))
-		return -1;
-	if (s->current_event)
-		return -1;
-	if (dl_list_empty(&s->event_queue))
+	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);
diff --git a/src/wps/wps_upnp_i.h b/src/wps/wps_upnp_i.h
index f289fe6..6a7c627 100644
--- a/src/wps/wps_upnp_i.h
+++ b/src/wps/wps_upnp_i.h
@@ -109,8 +109,7 @@
 	struct wps_context *wps;
 	void *priv;
 
-	/* FIX: maintain separate structures for each UPnP peer */
-	struct upnp_wps_peer peer;
+	struct dl_list peers; /* active UPnP peer sessions */
 };
 
 /*
diff --git a/src/wps/wps_upnp_ssdp.c b/src/wps/wps_upnp_ssdp.c
index 26a740d..968fc03 100644
--- a/src/wps/wps_upnp_ssdp.c
+++ b/src/wps/wps_upnp_ssdp.c
@@ -139,7 +139,7 @@
 	uuid_bin2str(iface->wps->uuid, uuid_string, sizeof(uuid_string));
 	msg = wpabuf_alloc(800); /* more than big enough */
 	if (msg == NULL)
-		goto fail;
+		return NULL;
 	switch (a->type) {
 	case ADVERTISE_UP:
 	case ADVERTISE_DOWN:
@@ -213,10 +213,6 @@
 		*islast = 1;
 
 	return msg;
-
-fail:
-	wpabuf_free(msg);
-	return NULL;
 }
 
 
@@ -744,11 +740,9 @@
 	int sd;
 
 	sd = socket(AF_INET, SOCK_DGRAM, 0);
-	if (sd < 0)
-		goto fail;
-	if (fcntl(sd, F_SETFL, O_NONBLOCK) != 0)
-		goto fail;
-	if (setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)))
+	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;
@@ -760,9 +754,8 @@
 	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)))
-		goto fail;
-	if (setsockopt(sd, IPPROTO_IP, IP_MULTICAST_TTL,
+		       (char *) &mcast_addr, sizeof(mcast_addr)) ||
+	    setsockopt(sd, IPPROTO_IP, IP_MULTICAST_TTL,
 		       &ttl, sizeof(ttl)))
 		goto fail;
 
diff --git a/src/wps/wps_upnp_web.c b/src/wps/wps_upnp_web.c
index b1cf571..7548e84 100644
--- a/src/wps/wps_upnp_web.c
+++ b/src/wps/wps_upnp_web.c
@@ -300,7 +300,8 @@
  * 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 http_request *hreq,
+				     const char *filename)
 {
 	struct wpabuf *buf; /* output buffer, allocated */
 	char *put_length_here;
@@ -409,6 +410,15 @@
 }
 
 
+static void wps_upnp_peer_del(struct upnp_wps_peer *peer)
+{
+	dl_list_del(&peer->list);
+	if (peer->wps)
+		wps_deinit(peer->wps);
+	os_free(peer);
+}
+
+
 static enum http_reply_code
 web_process_get_device_info(struct upnp_wps_device_sm *sm,
 			    struct wpabuf **reply, const char **replyname)
@@ -426,7 +436,9 @@
 	if (!iface || iface->ctx->ap_pin == NULL)
 		return HTTP_INTERNAL_SERVER_ERROR;
 
-	peer = &iface->peer;
+	peer = os_zalloc(sizeof(*peer));
+	if (!peer)
+		return HTTP_INTERNAL_SERVER_ERROR;
 
 	/*
 	 * Request for DeviceInfo, i.e., M1 TLVs. This is a start of WPS
@@ -436,9 +448,6 @@
 	 * 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;
@@ -455,8 +464,22 @@
 		*reply = NULL;
 	if (*reply == NULL) {
 		wpa_printf(MSG_INFO, "WPS UPnP: Failed to get DeviceInfo");
+		os_free(peer);
 		return HTTP_INTERNAL_SERVER_ERROR;
 	}
+
+	if (dl_list_len(&iface->peers) > 3) {
+		struct upnp_wps_peer *old;
+
+		old = dl_list_first(&iface->peers, struct upnp_wps_peer, list);
+		if (old) {
+			wpa_printf(MSG_DEBUG, "WPS UPnP: Drop oldest active session");
+			wps_upnp_peer_del(old);
+		}
+	}
+	dl_list_add_tail(&iface->peers, &peer->list);
+	/* TODO: Could schedule a timeout to free the entry */
+
 	*replyname = name;
 	return HTTP_OK;
 }
@@ -472,6 +495,8 @@
 	enum wps_process_res res;
 	enum wsc_op_code op_code;
 	struct upnp_wps_device_interface *iface;
+	struct wps_parse_attr attr;
+	struct upnp_wps_peer *tmp, *peer;
 
 	iface = dl_list_first(&sm->interfaces,
 			      struct upnp_wps_device_interface, list);
@@ -487,11 +512,56 @@
 	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)
+
+	if (wps_parse_msg(msg, &attr)) {
+		wpa_printf(MSG_DEBUG,
+			   "WPS UPnP: Could not parse PutMessage - NewInMessage");
+		wpabuf_free(msg);
+		return HTTP_BAD_REQUEST;
+	}
+
+	/* Find a matching active peer session */
+	peer = NULL;
+	dl_list_for_each(tmp, &iface->peers, struct upnp_wps_peer, list) {
+		if (!tmp->wps)
+			continue;
+		if (attr.enrollee_nonce &&
+		    os_memcmp(tmp->wps->nonce_e, attr.enrollee_nonce,
+			      WPS_NONCE_LEN) != 0)
+			continue; /* Enrollee nonce mismatch */
+		if (attr.msg_type &&
+		    *attr.msg_type != WPS_M2 &&
+		    *attr.msg_type != WPS_M2D &&
+		    attr.registrar_nonce &&
+		    os_memcmp(tmp->wps->nonce_r, attr.registrar_nonce,
+			      WPS_NONCE_LEN) != 0)
+			continue; /* Registrar nonce mismatch */
+		peer = tmp;
+		break;
+	}
+	if (!peer) {
+		/*
+		  Try to use the first entry in case message could work with
+		 * it. The actual handler function will reject this, if needed.
+		 * This maintains older behavior where only a single peer entry
+		 * was supported.
+		 */
+		peer = dl_list_first(&iface->peers, struct upnp_wps_peer, list);
+	}
+	if (!peer || !peer->wps) {
+		wpa_printf(MSG_DEBUG, "WPS UPnP: No active peer entry found");
+		wpabuf_free(msg);
+		return HTTP_BAD_REQUEST;
+	}
+
+	res = wps_process_msg(peer->wps, WSC_UPnP, msg);
+	if (res == WPS_FAILURE) {
 		*reply = NULL;
-	else
-		*reply = wps_get_msg(iface->peer.wps, &op_code);
+		wpa_printf(MSG_DEBUG, "WPS UPnP: Drop active peer session");
+		wps_upnp_peer_del(peer);
+	} else {
+		*reply = wps_get_msg(peer->wps, &op_code);
+	}
 	wpabuf_free(msg);
 	if (*reply == NULL)
 		return HTTP_INTERNAL_SERVER_ERROR;
@@ -1003,6 +1073,8 @@
 				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 */
@@ -1214,18 +1286,25 @@
 	}
 
 	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 %s",
-				   s, (sa && sa->domain_and_port) ?
+			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");
+			wpa_printf(MSG_INFO,
+				   "WPS UPnP: Could not find matching subscription to unsubscribe (SID %s)",
+				   str);
 			ret = HTTP_PRECONDITION_FAILED;
 			goto send_msg;
 		}
diff --git a/wpa_supplicant/Android.mk b/wpa_supplicant/Android.mk
index 4b45f8c..f65076c 100644
--- a/wpa_supplicant/Android.mk
+++ b/wpa_supplicant/Android.mk
@@ -39,7 +39,7 @@
 
 # Use Android specific directory for control interface sockets
 L_CFLAGS += -DCONFIG_CTRL_IFACE_CLIENT_DIR=\"/data/misc/wifi/sockets\"
-L_CFLAGS += -DCONFIG_CTRL_IFACE_DIR=\"/data/system/wpa_supplicant\"
+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\"
@@ -269,6 +269,7 @@
 ifdef CONFIG_IBSS_RSN
 NEED_RSN_AUTHENTICATOR=y
 L_CFLAGS += -DCONFIG_IBSS_RSN
+L_CFLAGS += -DCONFIG_NO_VLAN
 OBJS += ibss_rsn.c
 endif
 
@@ -315,6 +316,22 @@
 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
@@ -369,7 +386,6 @@
 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
@@ -380,7 +396,6 @@
 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
@@ -395,7 +410,6 @@
 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
@@ -409,11 +423,12 @@
 else
 L_CFLAGS += -DEAP_TTLS
 OBJS += src/eap_peer/eap_ttls.c
-OBJS_h += src/eap_server/eap_server_ttls.c
 endif
-MS_FUNCS=y
 TLS_FUNCS=y
+ifndef CONFIG_FIPS
+MS_FUNCS=y
 CHAP=y
+endif
 CONFIG_IEEE8021X_EAPOL=y
 endif
 
@@ -425,7 +440,6 @@
 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
@@ -448,7 +462,6 @@
 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
@@ -462,7 +475,6 @@
 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
@@ -487,7 +499,6 @@
 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
@@ -515,7 +526,6 @@
 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
@@ -532,7 +542,6 @@
 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
@@ -558,7 +567,6 @@
 
 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
@@ -573,7 +581,6 @@
 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
@@ -588,7 +595,6 @@
 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
@@ -601,7 +607,6 @@
 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
@@ -614,7 +619,6 @@
 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
@@ -627,7 +631,6 @@
 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
@@ -640,12 +643,12 @@
 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
@@ -662,7 +665,6 @@
 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
@@ -725,8 +727,6 @@
 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
@@ -742,7 +742,6 @@
 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
@@ -752,8 +751,6 @@
 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
@@ -809,6 +806,9 @@
 ifdef CONFIG_WNM
 OBJS += src/ap/wnm_ap.c
 endif
+ifdef CONFIG_MBO
+OBJS += src/ap/mbo_ap.c
+endif
 ifdef CONFIG_CTRL_IFACE
 OBJS += src/ap/ctrl_iface_ap.c
 endif
@@ -825,6 +825,11 @@
 endif
 endif
 
+ifdef CONFIG_MBO
+OBJS += mbo.c
+L_CFLAGS += -DCONFIG_MBO
+endif
+
 ifdef NEED_AP_MLME
 OBJS += src/ap/wmm.c
 OBJS += src/ap/ap_list.c
@@ -860,34 +865,10 @@
 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
+ifdef CONFIG_ACS
+L_CFLAGS += -DCONFIG_ACS
+OBJS += src/ap/acs.c
+LIBS += -lm
 endif
 
 ifdef CONFIG_PCSC
@@ -941,7 +922,6 @@
 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
@@ -966,6 +946,7 @@
 ifdef TLS_FUNCS
 L_CFLAGS += -DEAP_TLS_OPENSSL
 OBJS += src/crypto/tls_openssl.c
+OBJS += src/crypto/tls_openssl_ocsp.c
 LIBS += -lssl
 endif
 OBJS += src/crypto/crypto_openssl.c
@@ -973,6 +954,8 @@
 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
@@ -1012,6 +995,7 @@
 OBJS += src/tls/tlsv1_client.c
 OBJS += src/tls/tlsv1_client_write.c
 OBJS += src/tls/tlsv1_client_read.c
+OBJS += src/tls/tlsv1_client_ocsp.c
 OBJS += src/tls/asn1.c
 OBJS += src/tls/rsa.c
 OBJS += src/tls/x509v3.c
@@ -1065,6 +1049,8 @@
 CONFIG_INTERNAL_MD4=y
 CONFIG_INTERNAL_MD5=y
 CONFIG_INTERNAL_SHA256=y
+CONFIG_INTERNAL_SHA384=y
+CONFIG_INTERNAL_SHA512=y
 CONFIG_INTERNAL_RC4=y
 CONFIG_INTERNAL_DH_GROUP5=y
 endif
@@ -1117,6 +1103,20 @@
 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
@@ -1139,7 +1139,7 @@
 endif
 ifdef NEED_AES_WRAP
 NEED_AES_ENC=y
-ifneq ($(CONFIG_TLS), openssl)
+ifdef NEED_INTERNAL_AES_WRAP
 AESOBJS += src/crypto/aes-wrap.c
 endif
 endif
@@ -1215,11 +1215,17 @@
 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
@@ -1231,16 +1237,26 @@
 ifdef CONFIG_INTERNAL_SHA256
 SHA256OBJS += src/crypto/sha256-internal.c
 endif
+ifdef CONFIG_INTERNAL_SHA384
+L_CFLAGS += -DCONFIG_INTERNAL_SHA384
+SHA256OBJS += src/crypto/sha384-internal.c
+endif
+ifdef CONFIG_INTERNAL_SHA512
+L_CFLAGS += -DCONFIG_INTERNAL_SHA512
+SHA256OBJS += src/crypto/sha512-internal.c
+endif
 ifdef NEED_TLS_PRF_SHA256
 SHA256OBJS += src/crypto/sha256-tlsprf.c
 endif
 ifdef NEED_HMAC_SHA256_KDF
+L_CFLAGS += -DCONFIG_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
@@ -1276,6 +1292,7 @@
 L_CFLAGS += -DCONFIG_CTRL_IFACE
 ifeq ($(CONFIG_CTRL_IFACE), unix)
 L_CFLAGS += -DCONFIG_CTRL_IFACE_UNIX
+OBJS += src/common/ctrl_iface_common.c
 endif
 ifeq ($(CONFIG_CTRL_IFACE), udp)
 L_CFLAGS += -DCONFIG_CTRL_IFACE_UDP
@@ -1299,12 +1316,6 @@
 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
 
@@ -1320,12 +1331,6 @@
 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
@@ -1340,7 +1345,16 @@
 
 OBJS += $(DBUS_OBJS)
 L_CFLAGS += $(DBUS_CFLAGS)
-LIBS += $(DBUS_LIBS)
+
+ifdef CONFIG_CTRL_IFACE_BINDER
+BINDER=y
+L_CFLAGS += -DCONFIG_BINDER -DCONFIG_CTRL_IFACE_BINDER
+OBJS += binder/binder.cpp binder/binder_manager.cpp
+OBJS += binder/supplicant.cpp binder/iface.cpp
+OBJS += binder/fi/w1/wpa_supplicant/ISupplicant.aidl
+OBJS += binder/fi/w1/wpa_supplicant/ISupplicantCallbacks.aidl
+OBJS += binder/fi/w1/wpa_supplicant/IIface.aidl
+endif
 
 ifdef CONFIG_READLINE
 OBJS_c += src/utils/edit_readline.c
@@ -1486,12 +1500,6 @@
 
 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
@@ -1582,6 +1590,13 @@
 LOCAL_CFLAGS := $(L_CFLAGS)
 LOCAL_SRC_FILES := $(OBJS)
 LOCAL_C_INCLUDES := $(INCLUDES)
+ifeq ($(DBUS), y)
+LOCAL_SHARED_LIBRARIES += libdbus
+endif
+ifeq ($(BINDER), y)
+LOCAL_AIDL_INCLUDES := $(LOCAL_PATH)/binder frameworks/native/aidl/binder
+LOCAL_SHARED_LIBRARIES += libutils libbinder
+endif
 include $(BUILD_EXECUTABLE)
 
 ########################
diff --git a/wpa_supplicant/ChangeLog b/wpa_supplicant/ChangeLog
index 1ac79b4..facd90e 100644
--- a/wpa_supplicant/ChangeLog
+++ b/wpa_supplicant/ChangeLog
@@ -1,5 +1,68 @@
 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)
diff --git a/wpa_supplicant/Makefile b/wpa_supplicant/Makefile
index 05d8e0a..550d44b 100644
--- a/wpa_supplicant/Makefile
+++ b/wpa_supplicant/Makefile
@@ -6,6 +6,17 @@
 CFLAGS = -MMD -O2 -Wall -g
 endif
 
+ifdef LIBS
+# If LIBS is set with some global build system defaults, clone those for
+# LIBS_c and LIBS_p to cover wpa_passphrase and wpa_cli as well.
+ifndef LIBS_c
+LIBS_c := $(LIBS)
+endif
+ifndef LIBS_p
+LIBS_p := $(LIBS)
+endif
+endif
+
 export LIBDIR ?= /usr/local/lib/
 export INCDIR ?= /usr/local/include/
 export BINDIR ?= /usr/local/sbin/
@@ -17,6 +28,16 @@
 
 -include .config
 
+ifndef CONFIG_NO_GITVER
+# Add VERSION_STR postfix for builds from a git repository
+ifeq ($(wildcard ../.git),../.git)
+GITVER := $(shell git describe --dirty=+)
+ifneq ($(GITVER),)
+CFLAGS += -DGIT_VERSION_STR_POSTFIX=\"-$(GITVER)\"
+endif
+endif
+endif
+
 ifdef CONFIG_TESTING_OPTIONS
 CFLAGS += -DCONFIG_TESTING_OPTIONS
 CONFIG_WPS_TESTING=y
@@ -131,12 +152,15 @@
 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
@@ -146,6 +170,10 @@
 CFLAGS += -DCONFIG_ELOOP_EPOLL
 endif
 
+ifdef CONFIG_ELOOP_KQUEUE
+CFLAGS += -DCONFIG_ELOOP_KQUEUE
+endif
+
 ifdef CONFIG_EAPOL_TEST
 CFLAGS += -Werror -DEAPOL_TEST
 endif
@@ -275,14 +303,23 @@
 NEED_RC4=y
 else
 CFLAGS += -DCONFIG_NO_WPA
+ifeq ($(CONFIG_TLS), internal)
+NEED_SHA1=y
+NEED_MD5=y
+endif
 endif
 
 ifdef CONFIG_IBSS_RSN
 NEED_RSN_AUTHENTICATOR=y
 CFLAGS += -DCONFIG_IBSS_RSN
+CFLAGS += -DCONFIG_NO_VLAN
 OBJS += ibss_rsn.o
 endif
 
+ifdef CONFIG_MATCH_IFACE
+CFLAGS += -DCONFIG_MATCH_IFACE
+endif
+
 ifdef CONFIG_P2P
 OBJS += p2p_supplicant.o
 OBJS += p2p_supplicant_sd.o
@@ -383,7 +420,6 @@
 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
@@ -394,7 +430,6 @@
 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
@@ -409,7 +444,6 @@
 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
@@ -423,11 +457,12 @@
 else
 CFLAGS += -DEAP_TTLS
 OBJS += ../src/eap_peer/eap_ttls.o
-OBJS_h += ../src/eap_server/eap_server_ttls.o
 endif
-MS_FUNCS=y
 TLS_FUNCS=y
+ifndef CONFIG_FIPS
+MS_FUNCS=y
 CHAP=y
+endif
 CONFIG_IEEE8021X_EAPOL=y
 endif
 
@@ -439,7 +474,6 @@
 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
@@ -462,7 +496,6 @@
 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
@@ -476,7 +509,6 @@
 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
@@ -501,7 +533,6 @@
 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
@@ -529,7 +560,6 @@
 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
@@ -546,7 +576,6 @@
 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
@@ -572,7 +601,6 @@
 
 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
@@ -587,7 +615,6 @@
 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
@@ -602,7 +629,6 @@
 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
@@ -615,7 +641,6 @@
 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
@@ -628,7 +653,6 @@
 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
@@ -641,7 +665,6 @@
 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
@@ -654,12 +677,12 @@
 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
@@ -676,7 +699,6 @@
 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
@@ -739,8 +761,6 @@
 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
@@ -756,7 +776,6 @@
 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
@@ -766,8 +785,6 @@
 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
@@ -836,6 +853,9 @@
 ifdef CONFIG_WNM
 OBJS += ../src/ap/wnm_ap.o
 endif
+ifdef CONFIG_MBO
+OBJS += ../src/ap/mbo_ap.o
+endif
 ifdef CONFIG_CTRL_IFACE
 OBJS += ../src/ap/ctrl_iface_ap.o
 endif
@@ -852,6 +872,11 @@
 endif
 endif
 
+ifdef CONFIG_MBO
+OBJS += mbo.o
+CFLAGS += -DCONFIG_MBO
+endif
+
 ifdef NEED_AP_MLME
 OBJS += ../src/ap/wmm.o
 OBJS += ../src/ap/ap_list.o
@@ -887,34 +912,10 @@
 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
+ifdef CONFIG_ACS
+CFLAGS += -DCONFIG_ACS
+OBJS += ../src/ap/acs.o
+LIBS += -lm
 endif
 
 ifdef CONFIG_PCSC
@@ -968,7 +969,6 @@
 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
@@ -993,6 +993,7 @@
 ifdef TLS_FUNCS
 CFLAGS += -DEAP_TLS_OPENSSL
 OBJS += ../src/crypto/tls_openssl.o
+OBJS += ../src/crypto/tls_openssl_ocsp.o
 LIBS += -lssl
 endif
 OBJS += ../src/crypto/crypto_openssl.o
@@ -1001,6 +1002,8 @@
 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
@@ -1041,6 +1044,7 @@
 OBJS += ../src/tls/tlsv1_client.o
 OBJS += ../src/tls/tlsv1_client_write.o
 OBJS += ../src/tls/tlsv1_client_read.o
+OBJS += ../src/tls/tlsv1_client_ocsp.o
 OBJS += ../src/tls/asn1.o
 OBJS += ../src/tls/rsa.o
 OBJS += ../src/tls/x509v3.o
@@ -1094,6 +1098,8 @@
 CONFIG_INTERNAL_MD4=y
 CONFIG_INTERNAL_MD5=y
 CONFIG_INTERNAL_SHA256=y
+CONFIG_INTERNAL_SHA384=y
+CONFIG_INTERNAL_SHA512=y
 CONFIG_INTERNAL_RC4=y
 CONFIG_INTERNAL_DH_GROUP5=y
 endif
@@ -1146,6 +1152,20 @@
 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
@@ -1171,7 +1191,7 @@
 endif
 ifdef NEED_AES_WRAP
 NEED_AES_ENC=y
-ifneq ($(CONFIG_TLS), openssl)
+ifdef NEED_INTERNAL_AES_WRAP
 AESOBJS += ../src/crypto/aes-wrap.o
 endif
 endif
@@ -1243,11 +1263,17 @@
 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
@@ -1259,16 +1285,26 @@
 ifdef CONFIG_INTERNAL_SHA256
 SHA256OBJS += ../src/crypto/sha256-internal.o
 endif
+ifdef CONFIG_INTERNAL_SHA384
+CFLAGS += -DCONFIG_INTERNAL_SHA384
+SHA256OBJS += ../src/crypto/sha384-internal.o
+endif
+ifdef CONFIG_INTERNAL_SHA512
+CFLAGS += -DCONFIG_INTERNAL_SHA512
+SHA256OBJS += ../src/crypto/sha512-internal.o
+endif
 ifdef NEED_TLS_PRF_SHA256
 SHA256OBJS += ../src/crypto/sha256-tlsprf.o
 endif
 ifdef NEED_HMAC_SHA256_KDF
+CFLAGS += -DCONFIG_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
@@ -1304,6 +1340,7 @@
 CFLAGS += -DCONFIG_CTRL_IFACE
 ifeq ($(CONFIG_CTRL_IFACE), unix)
 CFLAGS += -DCONFIG_CTRL_IFACE_UNIX
+OBJS += ../src/common/ctrl_iface_common.o
 endif
 ifeq ($(CONFIG_CTRL_IFACE), udp)
 CFLAGS += -DCONFIG_CTRL_IFACE_UDP
@@ -1383,7 +1420,7 @@
 
 ifdef CONFIG_READLINE
 OBJS_c += ../src/utils/edit_readline.o
-LIBS_c += -lncurses -lreadline
+LIBS_c += -lreadline -lncurses
 else
 ifdef CONFIG_WPA_CLI_EDIT
 OBJS_c += ../src/utils/edit.o
@@ -1414,6 +1451,10 @@
 CFLAGS += -DCONFIG_IPV6
 endif
 
+ifdef CONFIG_NO_LINUX_PACKET_SOCKET_WAR
+CFLAGS += -DCONFIG_NO_LINUX_PACKET_SOCKET_WAR
+endif
+
 ifdef NEED_BASE64
 OBJS += ../src/utils/base64.o
 endif
@@ -1540,12 +1581,6 @@
 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
@@ -1591,6 +1626,25 @@
 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)
+OBJS_nfc += $(FST_OBJS)
+endif
+
 ifndef LDO
 LDO=$(CC)
 endif
@@ -1644,9 +1698,11 @@
 
 LIBCTRL += ../src/common/wpa_ctrl.o
 LIBCTRL += ../src/utils/os_$(CONFIG_OS).o
+LIBCTRL += ../src/utils/common.o
 LIBCTRL += ../src/utils/wpa_debug.o
 LIBCTRLSO += ../src/common/wpa_ctrl.c
 LIBCTRLSO += ../src/utils/os_$(CONFIG_OS).c
+LIBCTRLSO += ../src/utils/common.c
 LIBCTRLSO += ../src/utils/wpa_debug.c
 
 libwpa_client.a: $(LIBCTRL)
@@ -1658,12 +1714,12 @@
 	@$(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)
+libwpa_test1: libwpa_test.o libwpa_client.a
+	$(Q)$(LDO) $(LDFLAGS) -o libwpa_test1 libwpa_test.o libwpa_client.a $(LIBS_c)
 	@$(E) "  LD " $@
 
-test_wpa: $(OBJS_wpa) $(OBJS_h)
-	$(Q)$(LDO) $(LDFLAGS) -o test_wpa $(OBJS_wpa) $(LIBS)
+libwpa_test2: libwpa_test.o libwpa_client.so
+	$(Q)$(LDO) $(LDFLAGS) -o libwpa_test2 libwpa_test.o -L. -lwpa_client $(LIBS_c)
 	@$(E) "  LD " $@
 
 nfc_pw_token: $(OBJS_nfc)
@@ -1748,17 +1804,6 @@
 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:
@@ -1779,5 +1824,6 @@
 	rm -rf lcov-html
 	rm -f libwpa_client.a
 	rm -f libwpa_client.so
+	rm -f libwpa_test1 libwpa_test2
 
 -include $(OBJS:%.o=%.d)
diff --git a/wpa_supplicant/README b/wpa_supplicant/README
index f9c65d2..11ab01a 100644
--- a/wpa_supplicant/README
+++ b/wpa_supplicant/README
@@ -1,7 +1,7 @@
 WPA Supplicant
 ==============
 
-Copyright (c) 2003-2015, Jouni Malinen <j@w1.fi> and contributors
+Copyright (c) 2003-2016, Jouni Malinen <j@w1.fi> and contributors
 All Rights Reserved.
 
 This program is licensed under the BSD license (the one with
@@ -72,11 +72,13 @@
   * EAP-TTLS/CHAP
   * EAP-SIM
   * EAP-AKA
+  * EAP-AKA'
   * EAP-PSK
   * EAP-PAX
   * EAP-SAKE
   * EAP-IKEv2
   * EAP-GPSK
+  * EAP-pwd
   * LEAP (note: requires special support from the driver for IEEE 802.11
 	  authentication)
   (following methods are supported, but since they do not generate keying
@@ -163,18 +165,12 @@
 
 
 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
+- OpenSSL (tested with 1.0.1 and 1.0.2 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
@@ -308,7 +304,7 @@
 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.
+TLSv1 implementation can be used for TLS functionality.
 
 CONFIG_IEEE8021X_EAPOL=y
 CONFIG_EAP_MD5=y
@@ -320,15 +316,17 @@
 CONFIG_EAP_OTP=y
 CONFIG_EAP_SIM=y
 CONFIG_EAP_AKA=y
+CONFIG_EAP_AKA_PRIME=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_EAP_PWD=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
+authentication algorithm (for EAP-SIM/EAP-AKA/EAP-AKA'). This requires pcsc-lite
 (http://www.linuxnet.com/) for smart card access.
 
 CONFIG_PCSC=y
@@ -409,10 +407,10 @@
 --------------------
 
 usage:
-  wpa_supplicant [-BddfhKLqqtuvwW] [-P<pid file>] [-g<global ctrl>] \
+  wpa_supplicant [-BddfhKLqqtuvW] [-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>] \
+        [-b<br_ifname> [-MN -i<ifname> -c<conf> [-C<ctrl>] [-D<driver>] \
         [-p<driver_param>] [-b<br_ifname>] [-m<P2P Device config file>] ...
 
 options:
@@ -435,8 +433,8 @@
   -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
+  -M = start describing matching interface
   -N = start describing new interface
   -m = Configuration file for the P2P Device
 
@@ -479,6 +477,22 @@
 	-c wpa2.conf -i wlan1 -D wext
 
 
+If the interfaces on which wpa_supplicant is to run are not known or do
+not exist, wpa_supplicant can match an interface when it arrives. Each
+matched interface is separated with -M argument and the -i argument now
+allows for pattern matching.
+
+As an example, the following command would start wpa_supplicant for a
+specific wired interface called lan0, any interface starting with wlan
+and lastly any other interface. Each match has its own configuration
+file, and for the wired interface a specific driver has also been given.
+
+wpa_supplicant \
+	-M -c wpa_wired.conf -ilan0 -D wired \
+	-M -c wpa1.conf -iwlan* \
+	-M -c wpa2.conf
+
+
 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:
@@ -500,7 +514,7 @@
 
 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
+network based on the order of network blocks in the configuration
 file, network security level (WPA/WPA2 is preferred), and signal
 strength.
 
@@ -792,7 +806,7 @@
 
 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
+default interface (-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
@@ -1008,8 +1022,8 @@
 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
+program terminating), wpa_supplicant will time out the radio work item
+and send "EXT-RADIO-WORK-TIMEOUT <id>" event to indicate that this has
 happened. "RADIO_WORK done <id>" can also be used to cancel items that
 have not yet been started.
 
diff --git a/wpa_supplicant/README-HS20 b/wpa_supplicant/README-HS20
index 161dc06..e4eed20 100644
--- a/wpa_supplicant/README-HS20
+++ b/wpa_supplicant/README-HS20
@@ -229,7 +229,7 @@
 #
 # 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
+#	provisioned 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.
 #
@@ -564,3 +564,68 @@
 <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
+
+
+Hotspot 2.0 Rel 2 online signup and OSEN
+----------------------------------------
+
+Following parameters can be used to create a network profile for
+link-layer protected Hotspot 2.0 online signup connection with
+OSEN. Note that ssid and identify (NAI) values need to be set based on
+the information for the selected provider in the OSU Providers list
+ANQP-element.
+
+network={
+    ssid="HS 2.0 OSU"
+    proto=OSEN
+    key_mgmt=OSEN
+    pairwise=CCMP
+    group=GTK_NOT_USED
+    eap=WFA-UNAUTH-TLS
+    identity="anonymous@example.com"
+    ca_cert="osu-ca.pem"
+    ocsp=2
+}
+
+
+Hotspot 2.0 connection with external network selection
+------------------------------------------------------
+
+When an component controlling wpa_supplicant takes care of Interworking
+network selection, following configuration and network profile
+parameters can be used to configure a temporary network profile for a
+Hotspot 2.0 connection (e.g., with SET, ADD_NETWORK, SET_NETWORK, and
+SELECT_NETWORK control interface commands):
+
+interworking=1
+hs20=1
+auto_interworking=0
+
+network={
+    ssid="test-hs20"
+    proto=RSN
+    key_mgmt=WPA-EAP
+    pairwise=CCMP
+    anonymous_identity="anonymous@example.com"
+    identity="hs20-test@example.com"
+    password="password"
+    ca_cert="ca.pem"
+    eap=TTLS
+    phase2="auth=MSCHAPV2"
+    update_identifier=54321
+    #ocsp=2
+}
+
+
+These parameters are set based on the PPS MO credential and/or NAI Realm
+list ANQP-element:
+
+anonymous_identity: Credential/UsernamePassword/Username with username part
+		    replaced with "anonymous"
+identity: Credential/UsernamePassword/Username
+password: Credential/UsernamePassword/Password
+update_identifier: PPS/UpdateIdentifier
+ca_cert: from the downloaded trust root based on PPS information
+eap: Credential/UsernamePassword/EAPMethod or NAI Realm list
+phase2: Credential/UsernamePassword/EAPMethod or NAI Realm list
+ocsp: Credential/CheckAAAServerCertStatus
diff --git a/wpa_supplicant/README-P2P b/wpa_supplicant/README-P2P
index 6a5b032..23ac7fa 100644
--- a/wpa_supplicant/README-P2P
+++ b/wpa_supplicant/README-P2P
@@ -151,6 +151,7 @@
 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]
+	[ssid=<hexdump>]
 
 Start P2P group formation with a discovered P2P peer. This includes
 optional group owner negotiation, group interface setup, provisioning,
@@ -195,11 +196,17 @@
 out whether the peer device is operating as a GO and if so, use
 join-a-group operation rather than GO Negotiation.
 
+"ssid=<hexdump>" can be used to specify the Group SSID for join
+operations. This allows the P2P Client interface to filter scan results
+based on SSID to avoid selecting an incorrect BSS entry in case the same
+P2P Device or Interface address have been used in multiple groups
+recently.
+
 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.
+The remaining parameters 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
@@ -281,7 +288,7 @@
 p2p_cancel
 
 Cancel an ongoing P2P group formation and joining-a-group related
-operation. This operations unauthorizes the specific peer device (if any
+operation. This operation 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
@@ -633,12 +640,17 @@
 Disable/enable managed P2P Device operations. This is disabled by
 default.
 
-p2p_set listen_channel <1/6/11>
+p2p_set listen_channel <channel> [<op_class>]
 
 Set P2P Listen channel. This is mainly meant for testing purposes and
 changing the Listen channel during normal operations can result in
 protocol failures.
 
+When specifying a social channel on the 2.4 GHz band (1/6/11) there is
+no need to specify the operating class since it defaults to 81.  When
+specifying a social channel on the 60 GHz band (2), specify the 60 GHz
+operating class (180).
+
 p2p_set ssid_postfix <postfix>
 
 Set postfix string to be added to the automatically generated P2P SSID
@@ -650,7 +662,7 @@
 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
+the group securely 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
diff --git a/wpa_supplicant/android.config b/wpa_supplicant/android.config
index 8d27bb2..0a8bf98 100644
--- a/wpa_supplicant/android.config
+++ b/wpa_supplicant/android.config
@@ -32,6 +32,9 @@
 #CONFIG_DRIVER_NL80211=y
 CONFIG_LIBNL20=y
 
+# QCA vendor extensions to nl80211
+CONFIG_DRIVER_NL80211_QCA=y
+
 # Driver interface for FreeBSD net80211 layer (e.g., Atheros driver)
 #CONFIG_DRIVER_BSD=y
 #CFLAGS += -I/usr/local/include
@@ -321,6 +324,10 @@
 # Add introspection support for new DBus control interface
 #CONFIG_CTRL_IFACE_DBUS_INTRO=y
 
+# Add support for Binder control interface
+# Only applicable for Android platforms.
+#CONFIG_CTRL_IFACE_BINDER=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).
@@ -476,4 +483,10 @@
 # External password backend for testing purposes (developer use)
 #CONFIG_EXT_PASSWORD_TEST=y
 
+# Enable Fast Session Transfer (FST)
+#CONFIG_FST=y
+
+# Support Multi Band Operation
+#CONFIG_MBO=y
+
 include $(wildcard $(LOCAL_PATH)/android_config_*.inc)
diff --git a/wpa_supplicant/ap.c b/wpa_supplicant/ap.c
index f3960c5..b133d03 100644
--- a/wpa_supplicant/ap.c
+++ b/wpa_supplicant/ap.c
@@ -56,12 +56,32 @@
 	if (!conf->secondary_channel)
 		goto no_vht;
 
-	center_chan = wpas_p2p_get_vht80_center(wpa_s, mode, channel);
+	switch (conf->vht_oper_chwidth) {
+	case VHT_CHANWIDTH_80MHZ:
+	case VHT_CHANWIDTH_80P80MHZ:
+		center_chan = wpas_p2p_get_vht80_center(wpa_s, mode, channel);
+		break;
+	case VHT_CHANWIDTH_160MHZ:
+		center_chan = wpas_p2p_get_vht160_center(wpa_s, mode, channel);
+		break;
+	default:
+		/*
+		 * conf->vht_oper_chwidth might not be set for non-P2P GO cases,
+		 * try oper_cwidth 160 MHz first then VHT 80 MHz, if 160 MHz is
+		 * not supported.
+		 */
+		conf->vht_oper_chwidth = VHT_CHANWIDTH_160MHZ;
+		center_chan = wpas_p2p_get_vht160_center(wpa_s, mode, channel);
+		if (!center_chan) {
+			conf->vht_oper_chwidth = VHT_CHANWIDTH_80MHZ;
+			center_chan = wpas_p2p_get_vht80_center(wpa_s, mode,
+								channel);
+		}
+		break;
+	}
 	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;
 
@@ -72,14 +92,24 @@
 	conf->vht_oper_centr_freq_seg0_idx =
 		conf->channel + conf->secondary_channel * 2;
 #endif /* CONFIG_P2P */
+	conf->vht_oper_chwidth = VHT_CHANWIDTH_USE_HT;
 }
 #endif /* CONFIG_IEEE80211N */
 
 
-void wpa_supplicant_conf_ap_ht(struct wpa_supplicant *wpa_s,
-			       struct wpa_ssid *ssid,
-			       struct hostapd_config *conf)
+int wpa_supplicant_conf_ap_ht(struct wpa_supplicant *wpa_s,
+			      struct wpa_ssid *ssid,
+			      struct hostapd_config *conf)
 {
+	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;
+	}
+
 	/* TODO: enable HT40 if driver supports it;
 	 * drop to 11b if driver does not support 11g */
 
@@ -142,7 +172,32 @@
 			}
 		}
 	}
+
+	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 */
+
+	return 0;
 }
 
 
@@ -156,15 +211,16 @@
 
 	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);
+	if (wpa_supplicant_conf_ap_ht(wpa_s, ssid, conf))
 		return -1;
-	}
 
-	wpa_supplicant_conf_ap_ht(wpa_s, ssid, conf);
+#ifdef CONFIG_ACS
+	if (ssid->acs) {
+		/* Setting channel to 0 in order to enable ACS */
+		conf->channel = 0;
+		wpa_printf(MSG_DEBUG, "Use automatic channel selection");
+	}
+#endif /* CONFIG_ACS */
 
 	if (ieee80211_is_dfs(ssid->frequency) && wpa_s->conf->country[0]) {
 		conf->ieee80211h = 1;
@@ -206,12 +262,12 @@
 	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,
+		os_memcpy(bss->ip_addr_go, wpa_s->p2pdev->conf->ip_addr_go, 4);
+		os_memcpy(bss->ip_addr_mask, wpa_s->p2pdev->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,
+			  wpa_s->p2pdev->conf->ip_addr_start, 4);
+		os_memcpy(bss->ip_addr_end, wpa_s->p2pdev->conf->ip_addr_end,
 			  4);
 	}
 #endif /* CONFIG_P2P */
@@ -274,13 +330,17 @@
 		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;
+	if (ssid->mode == WPAS_MODE_P2P_GO ||
+	    ssid->mode == WPAS_MODE_P2P_GROUP_FORMATION) {
+		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 */
 
@@ -393,6 +453,8 @@
 			wpabuf_dup(wpa_s->conf->ap_vendor_elements);
 	}
 
+	bss->pbss = ssid->pbss;
+
 	return 0;
 }
 
@@ -425,14 +487,14 @@
 	if (event == WPS_EV_FAIL) {
 		struct wps_event_fail *fail = &data->fail;
 
-		if (wpa_s->parent && wpa_s->parent != wpa_s &&
+		if (wpa_s->p2pdev && wpa_s->p2pdev != 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
+			wpa_msg(wpa_s->p2pdev, MSG_INFO, WPS_EVENT_FAIL
 				"msg=%d config_error=%d",
 				fail->msg, fail->config_error);
 		}
@@ -507,6 +569,11 @@
 {
 	struct wpa_supplicant *wpa_s = ctx;
 
+#ifdef CONFIG_ACS
+	if (wpa_s->current_ssid && wpa_s->current_ssid->acs)
+		wpa_s->assoc_freq = wpa_s->ap_iface->freq;
+#endif /* CONFIG_ACS */
+
 	wpa_supplicant_set_state(wpa_s, WPA_COMPLETED);
 
 	if (wpa_s->ap_configured_cb)
@@ -572,8 +639,8 @@
 		params.p2p = 1;
 #endif /* CONFIG_P2P */
 
-	if (wpa_s->parent->set_ap_uapsd)
-		params.uapsd = wpa_s->parent->ap_uapsd;
+	if (wpa_s->p2pdev->set_ap_uapsd)
+		params.uapsd = wpa_s->p2pdev->ap_uapsd;
 	else if (params.p2p && (wpa_s->drv_flags & WPA_DRIVER_FLAGS_AP_UAPSD))
 		params.uapsd = 1; /* mandatory for P2P GO */
 	else
@@ -604,6 +671,13 @@
 		return -1;
 	}
 
+	/* Use the maximum oper channel width if it's given. */
+	if (ssid->max_oper_chwidth)
+		conf->vht_oper_chwidth = ssid->max_oper_chwidth;
+
+	ieee80211_freq_to_chan(ssid->vht_center_freq2,
+			       &conf->vht_oper_centr_freq_seg1_idx);
+
 	os_memcpy(wpa_s->ap_iface->conf->wmm_ac_params,
 		  wpa_s->conf->wmm_ac_params,
 		  sizeof(wpa_s->conf->wmm_ac_params));
@@ -645,7 +719,7 @@
 		}
 
 		hapd_iface->bss[i]->msg_ctx = wpa_s;
-		hapd_iface->bss[i]->msg_ctx_parent = wpa_s->parent;
+		hapd_iface->bss[i]->msg_ctx_parent = wpa_s->p2pdev;
 		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;
@@ -841,7 +915,10 @@
 		return -1;
 
 	if (pin == NULL) {
-		unsigned int rpin = wps_generate_pin();
+		unsigned int rpin;
+
+		if (wps_generate_pin(&rpin) < 0)
+			return -1;
 		ret_len = os_snprintf(buf, buflen, "%08d", rpin);
 		if (os_snprintf_error(buflen, ret_len))
 			return -1;
@@ -907,7 +984,8 @@
 	if (wpa_s->ap_iface == NULL)
 		return NULL;
 	hapd = wpa_s->ap_iface->bss[0];
-	pin = wps_generate_pin();
+	if (wps_generate_pin(&pin) < 0)
+		return NULL;
 	os_snprintf(pin_txt, sizeof(pin_txt), "%08u", pin);
 	os_free(hapd->conf->ap_pin);
 	hapd->conf->ap_pin = os_strdup(pin_txt);
@@ -1182,7 +1260,10 @@
 		return;
 
 	wpa_s->assoc_freq = freq;
-	hostapd_event_ch_switch(wpa_s->ap_iface->bss[0], freq, ht, offset, width, cf1, cf1);
+	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);
 }
 
 
@@ -1239,8 +1320,8 @@
 	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) {
+	if (wpa_s->p2pdev->conf->wps_nfc_dh_pubkey == NULL ||
+	    wpa_s->p2pdev->conf->wps_nfc_dh_privkey == NULL) {
 		wpa_printf(MSG_DEBUG, "P2P: No NFC DH key known");
 		return -1;
 	}
@@ -1249,9 +1330,9 @@
 	wpabuf_free(wps->dh_pubkey);
 	wpabuf_free(wps->dh_privkey);
 	wps->dh_privkey = wpabuf_dup(
-		wpa_s->parent->conf->wps_nfc_dh_privkey);
+		wpa_s->p2pdev->conf->wps_nfc_dh_privkey);
 	wps->dh_pubkey = wpabuf_dup(
-		wpa_s->parent->conf->wps_nfc_dh_pubkey);
+		wpa_s->p2pdev->conf->wps_nfc_dh_pubkey);
 	if (wps->dh_privkey == NULL || wps->dh_pubkey == NULL) {
 		wps->dh_ctx = NULL;
 		wpabuf_free(wps->dh_pubkey);
@@ -1285,6 +1366,58 @@
 #endif /* CONFIG_CTRL_IFACE */
 
 
+int wpas_ap_pmksa_cache_list(struct wpa_supplicant *wpa_s, char *buf,
+			     size_t len)
+{
+	size_t reply_len = 0, i;
+	char ap_delimiter[] = "---- AP ----\n";
+	char mesh_delimiter[] = "---- mesh ----\n";
+	size_t dlen;
+
+	if (wpa_s->ap_iface) {
+		dlen = os_strlen(ap_delimiter);
+		if (dlen > len - reply_len)
+			return reply_len;
+		os_memcpy(&buf[reply_len], ap_delimiter, dlen);
+		reply_len += dlen;
+
+		for (i = 0; i < wpa_s->ap_iface->num_bss; i++) {
+			reply_len += hostapd_ctrl_iface_pmksa_list(
+				wpa_s->ap_iface->bss[i],
+				&buf[reply_len], len - reply_len);
+		}
+	}
+
+	if (wpa_s->ifmsh) {
+		dlen = os_strlen(mesh_delimiter);
+		if (dlen > len - reply_len)
+			return reply_len;
+		os_memcpy(&buf[reply_len], mesh_delimiter, dlen);
+		reply_len += dlen;
+
+		reply_len += hostapd_ctrl_iface_pmksa_list(
+			wpa_s->ifmsh->bss[0], &buf[reply_len],
+			len - reply_len);
+	}
+
+	return reply_len;
+}
+
+
+void wpas_ap_pmksa_cache_flush(struct wpa_supplicant *wpa_s)
+{
+	size_t i;
+
+	if (wpa_s->ap_iface) {
+		for (i = 0; i < wpa_s->ap_iface->num_bss; i++)
+			hostapd_ctrl_iface_pmksa_flush(wpa_s->ap_iface->bss[i]);
+	}
+
+	if (wpa_s->ifmsh)
+		hostapd_ctrl_iface_pmksa_flush(wpa_s->ifmsh->bss[0]);
+}
+
+
 #ifdef NEED_AP_MLME
 void wpas_event_dfs_radar_detected(struct wpa_supplicant *wpa_s,
 				   struct dfs_event *radar)
@@ -1346,3 +1479,10 @@
 				 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/wpa_supplicant/ap.h b/wpa_supplicant/ap.h
index 3f4151d..5a59ddc 100644
--- a/wpa_supplicant/ap.h
+++ b/wpa_supplicant/ap.h
@@ -76,12 +76,16 @@
 			   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 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);
 
+int wpas_ap_pmksa_cache_list(struct wpa_supplicant *wpa_s, char *buf,
+			     size_t len);
+void wpas_ap_pmksa_cache_flush(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,
@@ -93,4 +97,6 @@
 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/wpa_supplicant/autoscan.c b/wpa_supplicant/autoscan.c
index a2cf7a5..d12eb21 100644
--- a/wpa_supplicant/autoscan.c
+++ b/wpa_supplicant/autoscan.c
@@ -1,6 +1,7 @@
 /*
  * WPA Supplicant - auto scan
  * Copyright (c) 2012, Intel Corporation. All rights reserved.
+ * Copyright 2015	Intel Deutschland GmbH
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -50,6 +51,11 @@
 	size_t nlen;
 	int i;
 	const struct autoscan_ops *ops = NULL;
+	struct sched_scan_plan *scan_plans;
+
+	/* Give preference to scheduled scan plans if supported/configured */
+	if (wpa_s->sched_scan_plans)
+		return 0;
 
 	if (wpa_s->autoscan && wpa_s->autoscan_priv)
 		return 0;
@@ -79,11 +85,23 @@
 		return -1;
 	}
 
+	scan_plans = os_malloc(sizeof(*wpa_s->sched_scan_plans));
+	if (!scan_plans)
+		return -1;
+
 	wpa_s->autoscan_params = NULL;
 
 	wpa_s->autoscan_priv = ops->init(wpa_s, params);
-	if (wpa_s->autoscan_priv == NULL)
+	if (!wpa_s->autoscan_priv) {
+		os_free(scan_plans);
 		return -1;
+	}
+
+	scan_plans[0].interval = 5;
+	scan_plans[0].iterations = 0;
+	os_free(wpa_s->sched_scan_plans);
+	wpa_s->sched_scan_plans = scan_plans;
+	wpa_s->sched_scan_plans_num = 1;
 	wpa_s->autoscan = ops;
 
 	wpa_printf(MSG_DEBUG, "autoscan: Initialized module '%s' with "
@@ -116,7 +134,10 @@
 		wpa_s->autoscan_priv = NULL;
 
 		wpa_s->scan_interval = 5;
-		wpa_s->sched_scan_interval = 0;
+
+		os_free(wpa_s->sched_scan_plans);
+		wpa_s->sched_scan_plans = NULL;
+		wpa_s->sched_scan_plans_num = 0;
 	}
 }
 
@@ -134,7 +155,7 @@
 			return -1;
 
 		wpa_s->scan_interval = interval;
-		wpa_s->sched_scan_interval = interval;
+		wpa_s->sched_scan_plans[0].interval = interval;
 
 		request_scan(wpa_s);
 	}
diff --git a/wpa_supplicant/binder/binder.cpp b/wpa_supplicant/binder/binder.cpp
new file mode 100644
index 0000000..28f7a2b
--- /dev/null
+++ b/wpa_supplicant/binder/binder.cpp
@@ -0,0 +1,108 @@
+/*
+ * binder interface for wpa_supplicant daemon
+ * Copyright (c) 2004-2016, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2016, Roshan Pius <rpius@google.com>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include <binder/IPCThreadState.h>
+#include <binder/ProcessState.h>
+#include <binder/IServiceManager.h>
+
+#include "binder_manager.h"
+
+extern "C" {
+#include "utils/includes.h"
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "binder.h"
+#include "binder_i.h"
+}
+
+void wpas_binder_sock_handler(int sock, void *eloop_ctx, void *sock_ctx)
+{
+	struct wpa_global *global = (wpa_global *) eloop_ctx;
+	struct wpas_binder_priv *priv = (wpas_binder_priv *) sock_ctx;
+
+	wpa_printf(MSG_DEBUG, "Processing binder events on FD %d",
+		   priv->binder_fd);
+	android::IPCThreadState::self()->handlePolledCommands();
+}
+
+
+struct wpas_binder_priv * wpas_binder_init(struct wpa_global *global)
+{
+	struct wpas_binder_priv *priv;
+	wpa_supplicant_binder::BinderManager *binder_manager;
+
+	priv = (wpas_binder_priv *) os_zalloc(sizeof(*priv));
+	if (!priv)
+		return NULL;
+	priv->global = global;
+
+	android::ProcessState::self()->setThreadPoolMaxThreadCount(0);
+	android::IPCThreadState::self()->disableBackgroundScheduling(true);
+	android::IPCThreadState::self()->setupPolling(&priv->binder_fd);
+	wpa_printf(MSG_INFO, "Process binder events on FD %d", priv->binder_fd);
+	if (priv->binder_fd < 0)
+		goto err;
+	/* Look for read events from the binder socket in the eloop. */
+	if (eloop_register_read_sock(priv->binder_fd, wpas_binder_sock_handler,
+				     global, priv) < 0)
+		goto err;
+
+	binder_manager = wpa_supplicant_binder::BinderManager::getInstance();
+	if (!binder_manager)
+		goto err;
+	binder_manager->registerBinderService(global);
+	/* We may not need to store this binder manager reference in the
+	 * global data strucure because we've made it a singleton class. */
+	priv->binder_manager = (void *) binder_manager;
+
+	return priv;
+
+err:
+	wpas_binder_deinit(priv);
+	return NULL;
+}
+
+
+void wpas_binder_deinit(struct wpas_binder_priv *priv)
+{
+	if (!priv)
+		return;
+
+	wpa_supplicant_binder::BinderManager::destroyInstance();
+	eloop_unregister_read_sock(priv->binder_fd);
+	android::IPCThreadState::shutdown();
+}
+
+
+int wpas_binder_register_interface(struct wpa_supplicant *wpa_s)
+{
+	if (!wpa_s->global->binder)
+		return 1;
+
+	wpa_supplicant_binder::BinderManager *binder_manager =
+		wpa_supplicant_binder::BinderManager::getInstance();
+	if (!binder_manager)
+		return 1;
+
+	return binder_manager->registerInterface(wpa_s);
+}
+
+
+int wpas_binder_unregister_interface(struct wpa_supplicant *wpa_s)
+{
+	if (!wpa_s->global->binder)
+		return 1;
+
+	wpa_supplicant_binder::BinderManager *binder_manager =
+		wpa_supplicant_binder::BinderManager::getInstance();
+	if (!binder_manager)
+		return 1;
+
+	return binder_manager->unregisterInterface(wpa_s);
+}
diff --git a/wpa_supplicant/binder/binder.h b/wpa_supplicant/binder/binder.h
new file mode 100644
index 0000000..a165074
--- /dev/null
+++ b/wpa_supplicant/binder/binder.h
@@ -0,0 +1,46 @@
+/*
+ * binder interface for wpa_supplicant daemon
+ * Copyright (c) 2004-2016, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2016, Roshan Pius <rpius@google.com>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef BINDER_H
+#define BINDER_H
+
+#ifdef _cplusplus
+extern "C" {
+#endif /* _cplusplus */
+
+/**
+ * This is the binder RPC interface entry point to the wpa_supplicant core.
+ * This initializes the binder driver & BinderManager instance and then forwards
+ * all the notifcations from the supplicant core to the BinderManager.
+ */
+struct wpas_binder_priv;
+struct wpa_global;
+
+struct wpas_binder_priv * wpas_binder_init(struct wpa_global *global);
+void wpas_binder_deinit(struct wpas_binder_priv *priv);
+
+#ifdef CONFIG_CTRL_IFACE_BINDER
+int wpas_binder_register_interface(struct wpa_supplicant *wpa_s);
+int wpas_binder_unregister_interface(struct wpa_supplicant *wpa_s);
+#else /* CONFIG_CTRL_IFACE_BINDER */
+static inline int wpas_binder_register_interface(struct wpa_supplicant *wpa_s)
+{
+	return 0;
+}
+static inline int wpas_binder_unregister_interface(struct wpa_supplicant *wpa_s)
+{
+	return 0;
+}
+#endif /* CONFIG_CTRL_IFACE_BINDER */
+
+#ifdef _cplusplus
+}
+#endif /* _cplusplus */
+
+#endif /* BINDER_H */
diff --git a/wpa_supplicant/binder/binder_i.h b/wpa_supplicant/binder/binder_i.h
new file mode 100644
index 0000000..e8087b6
--- /dev/null
+++ b/wpa_supplicant/binder/binder_i.h
@@ -0,0 +1,27 @@
+/*
+ * binder interface for wpa_supplicant daemon
+ * Copyright (c) 2004-2016, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2016, Roshan Pius <rpius@google.com>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef BINDER_I_H
+#define BINDER_I_H
+
+#ifdef _cplusplus
+extern "C" {
+#endif // _cplusplus
+
+struct wpas_binder_priv {
+	int binder_fd;
+	struct wpa_global *global;
+	void *binder_manager;
+};
+
+#ifdef _cplusplus
+}
+#endif /* _cplusplus */
+
+#endif /* BINDER_I_H */
diff --git a/wpa_supplicant/binder/binder_manager.cpp b/wpa_supplicant/binder/binder_manager.cpp
new file mode 100644
index 0000000..728f4b7
--- /dev/null
+++ b/wpa_supplicant/binder/binder_manager.cpp
@@ -0,0 +1,107 @@
+/*
+ * binder interface for wpa_supplicant daemon
+ * Copyright (c) 2004-2016, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2016, Roshan Pius <rpius@google.com>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include <binder/IServiceManager.h>
+
+#include "binder_manager.h"
+
+extern "C" {
+#include "utils/includes.h"
+#include "utils/common.h"
+}
+
+namespace wpa_supplicant_binder {
+
+const char BinderManager::kBinderServiceName[] = "fi.w1.wpa_supplicant";
+BinderManager *BinderManager::instance_ = NULL;
+
+
+BinderManager * BinderManager::getInstance()
+{
+	if (!instance_)
+		instance_ = new BinderManager();
+	return instance_;
+}
+
+
+void BinderManager::destroyInstance()
+{
+	if (instance_)
+		delete instance_;
+	instance_ = NULL;
+}
+
+
+int BinderManager::registerBinderService(struct wpa_global *global)
+{
+	/* Create the main binder service object and register with
+	 * system service manager. */
+	supplicant_object_ = new Supplicant(global);
+	android::String16 service_name(kBinderServiceName);
+	android::defaultServiceManager()->addService(
+		service_name,
+		android::IInterface::asBinder(supplicant_object_));
+	return 0;
+}
+
+
+int BinderManager::registerInterface(struct wpa_supplicant *wpa_s)
+{
+	if (!wpa_s)
+		return 1;
+
+	/* Using the corresponding wpa_supplicant pointer as key to our
+	 * object map. */
+	const void *iface_key = wpa_s;
+
+	/* Return failure if we already have an object for that iface_key. */
+	if (iface_object_map_.find(iface_key) != iface_object_map_.end())
+		return 1;
+
+	iface_object_map_[iface_key] = new Iface(wpa_s);
+	if (!iface_object_map_[iface_key].get())
+		return 1;
+
+	wpa_s->binder_object_key = iface_key;
+
+	return 0;
+}
+
+
+int BinderManager::unregisterInterface(struct wpa_supplicant *wpa_s)
+{
+	if (!wpa_s || !wpa_s->binder_object_key)
+		return 1;
+
+	const void *iface_key = wpa_s;
+	if (iface_object_map_.find(iface_key) == iface_object_map_.end())
+		return 1;
+
+	/* Delete the corresponding iface object from our map. */
+	iface_object_map_.erase(iface_key);
+	wpa_s->binder_object_key = NULL;
+	return 0;
+}
+
+
+int BinderManager::getIfaceBinderObjectByKey(
+	const void *iface_object_key,
+	android::sp<fi::w1::wpa_supplicant::IIface> *iface_object)
+{
+	if (!iface_object_key || !iface_object)
+		return 1;
+
+	if (iface_object_map_.find(iface_object_key) == iface_object_map_.end())
+		return 1;
+
+	*iface_object = iface_object_map_[iface_object_key];
+	return 0;
+}
+
+} /* namespace wpa_supplicant_binder */
diff --git a/wpa_supplicant/binder/binder_manager.h b/wpa_supplicant/binder/binder_manager.h
new file mode 100644
index 0000000..687e740
--- /dev/null
+++ b/wpa_supplicant/binder/binder_manager.h
@@ -0,0 +1,59 @@
+/*
+ * binder interface for wpa_supplicant daemon
+ * Copyright (c) 2004-2016, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2016, Roshan Pius <rpius@google.com>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef BINDER_MANAGER_H
+#define BINDER_MANAGER_H
+
+#include <map>
+#include <string>
+
+#include "supplicant.h"
+#include "iface.h"
+
+struct wpa_global;
+struct wpa_supplicant;
+
+namespace wpa_supplicant_binder {
+
+/**
+ * BinderManager is responsible for managing the lifetime of all
+ * binder objects created by wpa_supplicant. This is a singleton
+ * class which is created by the supplicant core and can be used
+ * to get references to the binder objects.
+ */
+class BinderManager {
+public:
+	static const char kBinderServiceName[];
+
+	static BinderManager * getInstance();
+	static void destroyInstance();
+	int registerBinderService(struct wpa_global *global);
+	int registerInterface(struct wpa_supplicant *wpa_s);
+	int unregisterInterface(struct wpa_supplicant *wpa_s);
+	int getIfaceBinderObjectByKey(
+		const void *iface_object_key,
+		android::sp<fi::w1::wpa_supplicant::IIface> *iface_object);
+
+private:
+	BinderManager() = default;
+	~BinderManager() = default;
+
+	/* Singleton instance of this class. */
+	static BinderManager *instance_;
+	/* The main binder service object. */
+	android::sp<Supplicant> supplicant_object_;
+	/* Map of all the interface specific binder objects controlled by
+	 * wpa_supplicant. This map is keyed in by the corresponding
+	 * wpa_supplicant structure pointer. */
+	std::map<const void *, android::sp<Iface>> iface_object_map_;
+};
+
+} /* namespace wpa_supplicant_binder */
+
+#endif /* BINDER_MANAGER_H */
diff --git a/wpa_supplicant/binder/fi/w1/wpa_supplicant/IIface.aidl b/wpa_supplicant/binder/fi/w1/wpa_supplicant/IIface.aidl
new file mode 100644
index 0000000..ea11d42
--- /dev/null
+++ b/wpa_supplicant/binder/fi/w1/wpa_supplicant/IIface.aidl
@@ -0,0 +1,16 @@
+/*
+ * binder interface for wpa_supplicant daemon
+ * Copyright (c) 2004-2016, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2016, Roshan Pius <rpius@google.com>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+package fi.w1.wpa_supplicant;
+
+/**
+ * Interface exposed by wpa_supplicant for each network interface it controls.
+ */
+interface IIface {
+}
diff --git a/wpa_supplicant/binder/fi/w1/wpa_supplicant/ISupplicant.aidl b/wpa_supplicant/binder/fi/w1/wpa_supplicant/ISupplicant.aidl
new file mode 100644
index 0000000..1cbee20
--- /dev/null
+++ b/wpa_supplicant/binder/fi/w1/wpa_supplicant/ISupplicant.aidl
@@ -0,0 +1,59 @@
+/*
+ * WPA Supplicant - binder interface for wpa_supplicant daemon
+ * Copyright (c) 2004-2016, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2016, Roshan Pius <rpius@google.com>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+package fi.w1.wpa_supplicant;
+
+import android.os.PersistableBundle;
+import fi.w1.wpa_supplicant.IIface;
+
+/**
+ * Interface exposed by the wpa_supplicant binder service registered
+ * with the service manager with name: fi.w1.wpa_supplicant.
+ */
+interface ISupplicant {
+	/* Error values returned by the service to RPC method calls. */
+	const int ERROR_INVALID_ARGS = 1;
+	const int ERROR_UNKNOWN = 2;
+	const int ERROR_IFACE_EXISTS = 3;
+	const int ERROR_IFACE_UNKNOWN = 4;
+
+	/**
+	 * Registers a wireless interface in wpa_supplicant.
+	 *
+	 * @param args A dictionary with arguments used to add the interface to
+	 *             wpa_supplicant.
+	 * The dictionary may contain the following entries:
+	 *   Ifname(String) Name of the network interface to control, e.g.,
+	 *   wlan0.
+	 *   BridgeIfname(String) Name of the bridge interface to control, e.g.,
+	 *   br0.
+	 *   Driver(String) Driver name which the interface uses, e.g., nl80211.
+	 *   ConfigFile(String) Configuration file path.
+	 *
+	 * @return Binder object representing the interface.
+	 */
+	IIface CreateInterface(in PersistableBundle args);
+
+	/**
+	 * Deregisters a wireless interface from wpa_supplicant.
+	 *
+	 * @param ifname Name of the network interface, e.g., wlan0
+	 */
+	void RemoveInterface(in @utf8InCpp String ifname);
+
+	/**
+	 * Gets a binder object for the interface corresponding to ifname
+	 * which wpa_supplicant already controls.
+	 *
+	 * @param ifname Name of the network interface, e.g., wlan0
+	 *
+	 * @return Binder object representing the interface.
+	 */
+	IIface GetInterface(in @utf8InCpp String ifname);
+}
diff --git a/wpa_supplicant/binder/fi/w1/wpa_supplicant/ISupplicantCallbacks.aidl b/wpa_supplicant/binder/fi/w1/wpa_supplicant/ISupplicantCallbacks.aidl
new file mode 100644
index 0000000..d624d91
--- /dev/null
+++ b/wpa_supplicant/binder/fi/w1/wpa_supplicant/ISupplicantCallbacks.aidl
@@ -0,0 +1,20 @@
+/*
+ * binder interface for wpa_supplicant daemon
+ * Copyright (c) 2004-2016, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2016, Roshan Pius <rpius@google.com>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+package fi.w1.wpa_supplicant;
+
+import android.os.PersistableBundle;
+
+/**
+ * Callback Interface exposed by the wpa_supplicant service. Clients need
+ * to host an instance of this binder object and pass a reference of the object
+ * to wpa_supplicant via the registerCallbacksObject method.
+ */
+interface ISupplicantCallbacks {
+}
diff --git a/wpa_supplicant/binder/iface.cpp b/wpa_supplicant/binder/iface.cpp
new file mode 100644
index 0000000..af2548d
--- /dev/null
+++ b/wpa_supplicant/binder/iface.cpp
@@ -0,0 +1,19 @@
+/*
+ * binder interface for wpa_supplicant daemon
+ * Copyright (c) 2004-2016, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2016, Roshan Pius <rpius@google.com>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "iface.h"
+
+namespace wpa_supplicant_binder {
+
+Iface::Iface(struct wpa_supplicant *wpa_s)
+	: wpa_s_(wpa_s)
+{
+}
+
+} /* namespace wpa_supplicant_binder */
diff --git a/wpa_supplicant/binder/iface.h b/wpa_supplicant/binder/iface.h
new file mode 100644
index 0000000..acc8e55
--- /dev/null
+++ b/wpa_supplicant/binder/iface.h
@@ -0,0 +1,42 @@
+/*
+ * binder interface for wpa_supplicant daemon
+ * Copyright (c) 2004-2016, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2016, Roshan Pius <rpius@google.com>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef IFACE_H
+#define IFACE_H
+
+#include "fi/w1/wpa_supplicant/BnIface.h"
+
+extern "C" {
+#include "utils/includes.h"
+#include "utils/common.h"
+#include "../wpa_supplicant_i.h"
+}
+
+namespace wpa_supplicant_binder {
+
+/**
+ * Implementation of Iface binder object. Each unique binder
+ * object is used for control operations on a specific interface
+ * controlled by wpa_supplicant.
+ */
+class Iface : public fi::w1::wpa_supplicant::BnIface
+{
+public:
+	Iface(struct wpa_supplicant *wpa_s);
+	virtual ~Iface() = default;
+
+private:
+	/* Raw pointer to the structure maintained by the core for this
+	 * interface. */
+	struct wpa_supplicant *wpa_s_;
+};
+
+} /* namespace wpa_supplicant_binder */
+
+#endif /* IFACE_H */
diff --git a/wpa_supplicant/binder/supplicant.cpp b/wpa_supplicant/binder/supplicant.cpp
new file mode 100644
index 0000000..6844e5a
--- /dev/null
+++ b/wpa_supplicant/binder/supplicant.cpp
@@ -0,0 +1,125 @@
+/*
+ * binder interface for wpa_supplicant daemon
+ * Copyright (c) 2004-2016, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2016, Roshan Pius <rpius@google.com>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "binder_manager.h"
+#include "supplicant.h"
+
+namespace wpa_supplicant_binder {
+
+Supplicant::Supplicant(struct wpa_global *global)
+	: wpa_global_(global)
+{
+}
+
+
+android::binder::Status Supplicant::CreateInterface(
+	const android::os::PersistableBundle &params,
+	android::sp<fi::w1::wpa_supplicant::IIface> *aidl_return)
+{
+	android::String16 driver, ifname, confname, bridge_ifname;
+
+	/* Check if required Ifname argument is missing */
+	if (!params.getString(android::String16("Ifname"), &ifname))
+		return android::binder::Status::fromServiceSpecificError(
+			ERROR_INVALID_ARGS,
+			android::String8("Ifname missing in params."));
+	/* Retrieve the remaining params from the dictionary */
+	params.getString(android::String16("Driver"), &driver);
+	params.getString(android::String16("ConfigFile"), &confname);
+	params.getString(android::String16("BridgeIfname"), &bridge_ifname);
+
+	/*
+	 * Try to get the wpa_supplicant record for this iface, return
+	 * an error if we already control it.
+	 */
+	if (wpa_supplicant_get_iface(wpa_global_,
+				     android::String8(ifname).string()) != NULL)
+		return android::binder::Status::fromServiceSpecificError(
+			ERROR_IFACE_EXISTS,
+			android::String8("wpa_supplicant already controls this interface."));
+
+	android::binder::Status status;
+	struct wpa_supplicant *wpa_s = NULL;
+	struct wpa_interface iface;
+
+	os_memset(&iface, 0, sizeof(iface));
+	iface.driver = os_strdup(android::String8(driver).string());
+	iface.ifname = os_strdup(android::String8(ifname).string());
+	iface.confname = os_strdup(android::String8(confname).string());
+	iface.bridge_ifname = os_strdup(
+		android::String8(bridge_ifname).string());
+	/* Otherwise, have wpa_supplicant attach to it. */
+	wpa_s = wpa_supplicant_add_iface(wpa_global_, &iface, NULL);
+	/* The supplicant core creates a corresponding binder object via
+	 * BinderManager when |wpa_supplicant_add_iface| is called. */
+	if (!wpa_s || !wpa_s->binder_object_key) {
+		status = android::binder::Status::fromServiceSpecificError(
+			ERROR_UNKNOWN,
+			android::String8("wpa_supplicant couldn't grab this interface."));
+	} else {
+		BinderManager *binder_manager = BinderManager::getInstance();
+
+		if (!binder_manager ||
+		    binder_manager->getIfaceBinderObjectByKey(
+			    wpa_s->binder_object_key, aidl_return))
+			status = android::binder::Status::fromServiceSpecificError(
+				ERROR_UNKNOWN,
+				android::String8("wpa_supplicant encountered a binder error."));
+		else
+			status = android::binder::Status::ok();
+	}
+	os_free((void *) iface.driver);
+	os_free((void *) iface.ifname);
+	os_free((void *) iface.confname);
+	os_free((void *) iface.bridge_ifname);
+	return status;
+}
+
+
+android::binder::Status Supplicant::RemoveInterface(const std::string &ifname)
+{
+	struct wpa_supplicant *wpa_s;
+
+	wpa_s = wpa_supplicant_get_iface(wpa_global_, ifname.c_str());
+	if (!wpa_s || !wpa_s->binder_object_key)
+		return android::binder::Status::fromServiceSpecificError(
+			ERROR_IFACE_UNKNOWN,
+			android::String8("wpa_supplicant does not control this interface."));
+	if (wpa_supplicant_remove_iface(wpa_global_, wpa_s, 0))
+		return android::binder::Status::fromServiceSpecificError(
+			ERROR_UNKNOWN,
+			android::String8("wpa_supplicant couldn't remove this interface."));
+	return android::binder::Status::ok();
+}
+
+
+android::binder::Status Supplicant::GetInterface(
+	const std::string &ifname,
+	android::sp<fi::w1::wpa_supplicant::IIface> *aidl_return)
+{
+	struct wpa_supplicant *wpa_s;
+
+	wpa_s = wpa_supplicant_get_iface(wpa_global_, ifname.c_str());
+	if (!wpa_s || !wpa_s->binder_object_key)
+		return android::binder::Status::fromServiceSpecificError(
+			ERROR_IFACE_UNKNOWN,
+			android::String8("wpa_supplicant does not control this interface."));
+
+	BinderManager *binder_manager = BinderManager::getInstance();
+	if (!binder_manager ||
+	    binder_manager->getIfaceBinderObjectByKey(wpa_s->binder_object_key,
+						      aidl_return))
+		return android::binder::Status::fromServiceSpecificError(
+			ERROR_UNKNOWN,
+			android::String8("wpa_supplicant encountered a binder error."));
+
+	return android::binder::Status::ok();
+}
+
+} /* namespace wpa_supplicant_binder */
diff --git a/wpa_supplicant/binder/supplicant.h b/wpa_supplicant/binder/supplicant.h
new file mode 100644
index 0000000..b96f4e6
--- /dev/null
+++ b/wpa_supplicant/binder/supplicant.h
@@ -0,0 +1,57 @@
+/*
+ * binder interface for wpa_supplicant daemon
+ * Copyright (c) 2004-2016, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2016, Roshan Pius <rpius@google.com>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef SUPPLICANT_H
+#define SUPPLICANT_H
+
+#include "fi/w1/wpa_supplicant/BnSupplicant.h"
+#include "fi/w1/wpa_supplicant/IIface.h"
+#include "fi/w1/wpa_supplicant/ISupplicantCallbacks.h"
+
+extern "C" {
+#include "utils/includes.h"
+#include "utils/common.h"
+#include "../wpa_supplicant_i.h"
+}
+
+namespace wpa_supplicant_binder {
+
+/**
+ * Implementation of the supplicant binder object. This binder
+ * object is used core for global control operations on
+ * wpa_supplicant.
+ */
+class Supplicant : public fi::w1::wpa_supplicant::BnSupplicant
+{
+public:
+	Supplicant(struct wpa_global *global);
+	virtual ~Supplicant() = default;
+
+	android::binder::Status CreateInterface(
+		const android::os::PersistableBundle &params,
+		android::sp<fi::w1::wpa_supplicant::IIface> *aidl_return)
+		override;
+	android::binder::Status RemoveInterface(
+		const std::string &ifname) override;
+	android::binder::Status GetInterface(
+		const std::string &ifname,
+		android::sp<fi::w1::wpa_supplicant::IIface> *aidl_return)
+		override;
+
+private:
+	/* Raw pointer to the global structure maintained by the core. */
+	struct wpa_global *wpa_global_;
+	/* All the callback objects registered by the clients. */
+	std::vector<android::sp<fi::w1::wpa_supplicant::ISupplicantCallbacks>>
+		callbacks_;
+};
+
+} /* namespace wpa_supplicant_binder */
+
+#endif /* SUPPLICANT_H */
diff --git a/wpa_supplicant/bss.c b/wpa_supplicant/bss.c
index 8134562..a83ca10 100644
--- a/wpa_supplicant/bss.c
+++ b/wpa_supplicant/bss.c
@@ -19,11 +19,6 @@
 #include "bss.h"
 
 
-/**
- * WPA_BSS_EXPIRATION_PERIOD - Period of expiration run in seconds
- */
-#define WPA_BSS_EXPIRATION_PERIOD 10
-
 #define WPA_BSS_FREQ_CHANGED_FLAG	BIT(0)
 #define WPA_BSS_SIGNAL_CHANGED_FLAG	BIT(1)
 #define WPA_BSS_PRIVACY_CHANGED_FLAG	BIT(2)
@@ -65,6 +60,9 @@
 	anqp = os_zalloc(sizeof(*anqp));
 	if (anqp == NULL)
 		return NULL;
+#ifdef CONFIG_INTERWORKING
+	dl_list_init(&anqp->anqp_elems);
+#endif /* CONFIG_INTERWORKING */
 	anqp->users = 1;
 	return anqp;
 }
@@ -85,6 +83,7 @@
 
 #define ANQP_DUP(f) if (anqp->f) n->f = wpabuf_dup(anqp->f)
 #ifdef CONFIG_INTERWORKING
+	dl_list_init(&n->anqp_elems);
 	ANQP_DUP(capability_list);
 	ANQP_DUP(venue_name);
 	ANQP_DUP(network_auth_type);
@@ -146,6 +145,10 @@
  */
 static void wpa_bss_anqp_free(struct wpa_bss_anqp *anqp)
 {
+#ifdef CONFIG_INTERWORKING
+	struct wpa_bss_anqp_elem *elem;
+#endif /* CONFIG_INTERWORKING */
+
 	if (anqp == NULL)
 		return;
 
@@ -164,6 +167,13 @@
 	wpabuf_free(anqp->nai_realm);
 	wpabuf_free(anqp->anqp_3gpp);
 	wpabuf_free(anqp->domain_name);
+
+	while ((elem = dl_list_first(&anqp->anqp_elems,
+				     struct wpa_bss_anqp_elem, list))) {
+		dl_list_del(&elem->list);
+		wpabuf_free(elem->payload);
+		os_free(elem);
+	}
 #endif /* CONFIG_INTERWORKING */
 #ifdef CONFIG_HS20
 	wpabuf_free(anqp->hs20_capability_list);
@@ -311,10 +321,18 @@
 
 static int wpa_bss_in_use(struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
 {
-	return bss == wpa_s->current_bss ||
-		(!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));
+	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);
 }
 
 
@@ -390,8 +408,9 @@
 	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'",
-		bss->id, MAC2STR(bss->bssid), wpa_ssid_txt(ssid, ssid_len));
+		" 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;
 }
@@ -534,6 +553,9 @@
 	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);
@@ -777,7 +799,7 @@
 	struct wpa_bss *bss, *n;
 
 	os_get_reltime(&wpa_s->last_scan);
-	if (!new_scan)
+	if ((info && info->aborted) || !new_scan)
 		return; /* do not expire entries without new scan */
 
 	dl_list_for_each_safe(bss, n, &wpa_s->bss, struct wpa_bss, list) {
@@ -828,16 +850,6 @@
 }
 
 
-static void wpa_bss_timeout(void *eloop_ctx, void *timeout_ctx)
-{
-	struct wpa_supplicant *wpa_s = eloop_ctx;
-
-	wpa_bss_flush_by_age(wpa_s, wpa_s->conf->bss_expiration_age);
-	eloop_register_timeout(WPA_BSS_EXPIRATION_PERIOD, 0,
-			       wpa_bss_timeout, wpa_s, NULL);
-}
-
-
 /**
  * wpa_bss_init - Initialize BSS table
  * @wpa_s: Pointer to wpa_supplicant data
@@ -850,8 +862,6 @@
 {
 	dl_list_init(&wpa_s->bss);
 	dl_list_init(&wpa_s->bss_id);
-	eloop_register_timeout(WPA_BSS_EXPIRATION_PERIOD, 0,
-			       wpa_bss_timeout, wpa_s, NULL);
 	return 0;
 }
 
@@ -883,7 +893,6 @@
  */
 void wpa_bss_deinit(struct wpa_supplicant *wpa_s)
 {
-	eloop_cancel_timeout(wpa_bss_timeout, wpa_s, NULL);
 	wpa_bss_flush(wpa_s);
 }
 
@@ -1010,20 +1019,7 @@
  */
 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;
+	return get_ie((const u8 *) (bss + 1), bss->ie_len, ie);
 }
 
 
@@ -1043,8 +1039,8 @@
 	pos = (const u8 *) (bss + 1);
 	end = pos + bss->ie_len;
 
-	while (pos + 1 < end) {
-		if (pos + 2 + pos[1] > end)
+	while (end - pos > 1) {
+		if (2 + pos[1] > end - pos)
 			break;
 		if (pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 &&
 		    vendor_type == WPA_GET_BE32(&pos[2]))
@@ -1080,8 +1076,8 @@
 	pos += bss->ie_len;
 	end = pos + bss->beacon_ie_len;
 
-	while (pos + 1 < end) {
-		if (pos + 2 + pos[1] > end)
+	while (end - pos > 1) {
+		if (2 + pos[1] > end - pos)
 			break;
 		if (pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 &&
 		    vendor_type == WPA_GET_BE32(&pos[2]))
@@ -1116,8 +1112,8 @@
 	pos = (const u8 *) (bss + 1);
 	end = pos + bss->ie_len;
 
-	while (pos + 1 < end) {
-		if (pos + 2 + pos[1] > end)
+	while (end - pos > 1) {
+		if (2 + pos[1] > end - pos)
 			break;
 		if (pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 &&
 		    vendor_type == WPA_GET_BE32(&pos[2]))
@@ -1161,8 +1157,8 @@
 	pos += bss->ie_len;
 	end = pos + bss->beacon_ie_len;
 
-	while (pos + 1 < end) {
-		if (pos + 2 + pos[1] > end)
+	while (end - pos > 1) {
+		if (2 + pos[1] > end - pos)
 			break;
 		if (pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 &&
 		    vendor_type == WPA_GET_BE32(&pos[2]))
diff --git a/wpa_supplicant/bss.h b/wpa_supplicant/bss.h
index b215380..f7f72f3 100644
--- a/wpa_supplicant/bss.h
+++ b/wpa_supplicant/bss.h
@@ -19,6 +19,12 @@
 #define WPA_BSS_ASSOCIATED		BIT(5)
 #define WPA_BSS_ANQP_FETCH_TRIED	BIT(6)
 
+struct wpa_bss_anqp_elem {
+	struct dl_list list;
+	u16 infoid;
+	struct wpabuf *payload;
+};
+
 /**
  * struct wpa_bss_anqp - ANQP data for a BSS entry (struct wpa_bss)
  */
@@ -34,6 +40,7 @@
 	struct wpabuf *nai_realm;
 	struct wpabuf *anqp_3gpp;
 	struct wpabuf *domain_name;
+	struct dl_list anqp_elems; /* list of struct wpa_bss_anqp_elem */
 #endif /* CONFIG_INTERWORKING */
 #ifdef CONFIG_HS20
 	struct wpabuf *hs20_capability_list;
@@ -141,6 +148,17 @@
 	return bss->freq > 45000;
 }
 
+/**
+ * Test whether a BSS is a PBSS.
+ * This checks whether a BSS is a DMG-band PBSS. PBSS is used for P2P DMG
+ * network.
+ */
+static inline int bss_is_pbss(struct wpa_bss *bss)
+{
+	return bss_is_dmg(bss) &&
+		(bss->caps & IEEE80211_CAP_DMG_MASK) == IEEE80211_CAP_DMG_PBSS;
+}
+
 static inline void wpa_bss_update_level(struct wpa_bss *bss, int new_level)
 {
 	if (bss != NULL && new_level < 0)
diff --git a/wpa_supplicant/config.c b/wpa_supplicant/config.c
index 8a34e84..4376676 100644
--- a/wpa_supplicant/config.c
+++ b/wpa_supplicant/config.c
@@ -15,6 +15,7 @@
 #include "rsn_supp/wpa.h"
 #include "eap_peer/eap.h"
 #include "p2p/p2p.h"
+#include "fst/fst.h"
 #include "config.h"
 
 
@@ -31,7 +32,11 @@
 	/* Configuration variable name */
 	char *name;
 
-	/* Parser function for this variable */
+	/* Parser function for this variable. The parser functions return 0 or 1
+	 * to indicate success. Value 0 indicates that the parameter value may
+	 * have changed while value 1 means that the value did not change.
+	 * Error cases (failure to parse the string) are indicated by returning
+	 * -1. */
 	int (*parser)(const struct parse_data *data, struct wpa_ssid *ssid,
 		      int line, const char *value);
 
@@ -58,7 +63,7 @@
 				struct wpa_ssid *ssid,
 				int line, const char *value)
 {
-	size_t res_len, *dst_len;
+	size_t res_len, *dst_len, prev_len;
 	char **dst, *tmp;
 
 	if (os_strcmp(value, "NULL") == 0) {
@@ -104,6 +109,21 @@
 set:
 	dst = (char **) (((u8 *) ssid) + (long) data->param1);
 	dst_len = (size_t *) (((u8 *) ssid) + (long) data->param2);
+
+	if (data->param2)
+		prev_len = *dst_len;
+	else if (*dst)
+		prev_len = os_strlen(*dst);
+	else
+		prev_len = 0;
+	if ((*dst == NULL && tmp == NULL) ||
+	    (*dst && tmp && prev_len == res_len &&
+	     os_memcmp(*dst, tmp, res_len) == 0)) {
+		/* No change to the previously configured value */
+		os_free(tmp);
+		return 1;
+	}
+
 	os_free(*dst);
 	*dst = tmp;
 	if (data->param2)
@@ -189,6 +209,9 @@
 			   line, value);
 		return -1;
 	}
+
+	if (*dst == val)
+		return 1;
 	*dst = val;
 	wpa_printf(MSG_MSGDUMP, "%s=%d (0x%x)", data->name, *dst, *dst);
 
@@ -456,8 +479,10 @@
 		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;
+		    os_memcmp(ssid->passphrase, value, len) == 0) {
+			/* No change to the previously configured value */
+			return 1;
+		}
 		ssid->psk_set = 0;
 		str_clear_free(ssid->passphrase);
 		ssid->passphrase = dup_binstr(value, len);
@@ -568,6 +593,8 @@
 		errors++;
 	}
 
+	if (!errors && ssid->proto == val)
+		return 1;
 	wpa_printf(MSG_MSGDUMP, "proto: 0x%x", val);
 	ssid->proto = val;
 	return errors ? -1 : 0;
@@ -704,6 +731,8 @@
 		errors++;
 	}
 
+	if (!errors && ssid->key_mgmt == val)
+		return 1;
 	wpa_printf(MSG_MSGDUMP, "key_mgmt: 0x%x", val);
 	ssid->key_mgmt = val;
 	return errors ? -1 : 0;
@@ -898,6 +927,9 @@
 
 static int wpa_config_parse_cipher(int line, const char *value)
 {
+#ifdef CONFIG_NO_WPA
+	return -1;
+#else /* CONFIG_NO_WPA */
 	int val = wpa_parse_cipher(value);
 	if (val < 0) {
 		wpa_printf(MSG_ERROR, "Line %d: invalid cipher '%s'.",
@@ -910,12 +942,16 @@
 		return -1;
 	}
 	return val;
+#endif /* CONFIG_NO_WPA */
 }
 
 
 #ifndef NO_CONFIG_WRITE
 static char * wpa_config_write_cipher(int cipher)
 {
+#ifdef CONFIG_NO_WPA
+	return NULL;
+#else /* CONFIG_NO_WPA */
 	char *buf = os_zalloc(50);
 	if (buf == NULL)
 		return NULL;
@@ -926,6 +962,7 @@
 	}
 
 	return buf;
+#endif /* CONFIG_NO_WPA */
 }
 #endif /* NO_CONFIG_WRITE */
 
@@ -944,6 +981,8 @@
 		return -1;
 	}
 
+	if (ssid->pairwise_cipher == val)
+		return 1;
 	wpa_printf(MSG_MSGDUMP, "pairwise: 0x%x", val);
 	ssid->pairwise_cipher = val;
 	return 0;
@@ -980,6 +1019,8 @@
 		return -1;
 	}
 
+	if (ssid->group_cipher == val)
+		return 1;
 	wpa_printf(MSG_MSGDUMP, "group: 0x%x", val);
 	ssid->group_cipher = val;
 	return 0;
@@ -1041,6 +1082,8 @@
 		errors++;
 	}
 
+	if (!errors && ssid->auth_alg == val)
+		return 1;
 	wpa_printf(MSG_MSGDUMP, "auth_alg: 0x%x", val);
 	ssid->auth_alg = val;
 	return errors ? -1 : 0;
@@ -1295,6 +1338,32 @@
 	methods[num_methods].method = EAP_TYPE_NONE;
 	num_methods++;
 
+	if (!errors && ssid->eap.eap_methods) {
+		struct eap_method_type *prev_m;
+		size_t i, j, prev_methods, match = 0;
+
+		prev_m = ssid->eap.eap_methods;
+		for (i = 0; prev_m[i].vendor != EAP_VENDOR_IETF ||
+			     prev_m[i].method != EAP_TYPE_NONE; i++) {
+			/* Count the methods */
+		}
+		prev_methods = i + 1;
+
+		for (i = 0; prev_methods == num_methods && i < prev_methods;
+		     i++) {
+			for (j = 0; j < num_methods; j++) {
+				if (prev_m[i].vendor == methods[j].vendor &&
+				    prev_m[i].method == methods[j].method) {
+					match++;
+					break;
+				}
+			}
+		}
+		if (match == num_methods) {
+			os_free(methods);
+			return 1;
+		}
+	}
 	wpa_hexdump(MSG_MSGDUMP, "eap methods",
 		    (u8 *) methods, num_methods * sizeof(*methods));
 	os_free(ssid->eap.eap_methods);
@@ -1347,6 +1416,8 @@
 	u8 *hash;
 
 	if (os_strcmp(value, "NULL") == 0) {
+		if (!ssid->eap.password)
+			return 1; /* Already unset */
 		wpa_printf(MSG_DEBUG, "Unset configuration string 'password'");
 		bin_clear_free(ssid->eap.password, ssid->eap.password_len);
 		ssid->eap.password = NULL;
@@ -1410,6 +1481,12 @@
 
 	wpa_hexdump_key(MSG_MSGDUMP, data->name, hash, 16);
 
+	if (ssid->eap.password && ssid->eap.password_len == 16 &&
+	    os_memcmp(ssid->eap.password, hash, 16) == 0 &&
+	    (ssid->eap.flags & EAP_CONFIG_FLAGS_PASSWORD_NTHASH)) {
+		bin_clear_free(hash, 16);
+		return 1;
+	}
 	bin_clear_free(ssid->eap.password, ssid->eap.password_len);
 	ssid->eap.password = hash;
 	ssid->eap.password_len = 16;
@@ -1836,6 +1913,8 @@
 	{ FUNC(auth_alg) },
 	{ FUNC(scan_freq) },
 	{ FUNC(freq_list) },
+	{ INT_RANGE(max_oper_chwidth, VHT_CHANWIDTH_USE_HT,
+		    VHT_CHANWIDTH_80P80MHZ) },
 #ifdef IEEE8021X_EAPOL
 	{ FUNC(eap) },
 	{ STR_LENe(identity) },
@@ -1909,6 +1988,9 @@
 	{ INT_RANGE(mixed_cell, 0, 1) },
 	{ INT_RANGE(frequency, 0, 65000) },
 	{ INT_RANGE(fixed_freq, 0, 1) },
+#ifdef CONFIG_ACS
+	{ INT_RANGE(acs, 0, 1) },
+#endif /* CONFIG_ACS */
 #ifdef CONFIG_MESH
 	{ FUNC(mesh_basic_rates) },
 	{ INT(dot11MeshMaxRetries) },
@@ -1966,6 +2048,7 @@
 	{ INT(update_identifier) },
 #endif /* CONFIG_HS20 */
 	{ INT_RANGE(mac_addr, 0, 2) },
+	{ INT_RANGE(pbss, 0, 1) },
 };
 
 #undef OFFSET
@@ -2269,6 +2352,12 @@
 	os_free(config->osu_dir);
 	os_free(config->bgscan);
 	os_free(config->wowlan_triggers);
+	os_free(config->fst_group_id);
+	os_free(config->sched_scan_plans);
+#ifdef CONFIG_MBO
+	os_free(config->non_pref_chan);
+#endif /* CONFIG_MBO */
+
 	os_free(config);
 }
 
@@ -2451,7 +2540,8 @@
  * @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
+ * Returns: 0 on success with possible change in the value, 1 on success with
+ * no change to previously configured value, or -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
@@ -2472,7 +2562,8 @@
 		if (os_strcmp(var, field->name) != 0)
 			continue;
 
-		if (field->parser(field, ssid, line, value)) {
+		ret = field->parser(field, ssid, line, value);
+		if (ret < 0) {
 			if (line) {
 				wpa_printf(MSG_ERROR, "Line %d: failed to "
 					   "parse %s '%s'.", line, var, value);
@@ -3525,9 +3616,12 @@
 	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;
@@ -3545,6 +3639,11 @@
 	config->rand_addr_lifetime = DEFAULT_RAND_ADDR_LIFETIME;
 	config->key_mgmt_offload = DEFAULT_KEY_MGMT_OFFLOAD;
 	config->cert_in_cb = DEFAULT_CERT_IN_CB;
+	config->wpa_rsc_relaxation = DEFAULT_WPA_RSC_RELAXATION;
+
+#ifdef CONFIG_MBO
+	config->mbo_cell_capa = DEFAULT_MBO_CELL_CAPA;
+#endif /* CONFIG_MBO */
 
 	if (ctrl_interface)
 		config->ctrl_interface = os_strdup(ctrl_interface);
@@ -4138,6 +4237,7 @@
 	{ 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 },
@@ -4172,8 +4272,8 @@
 #endif /* CONFIG_WPS */
 #ifdef CONFIG_P2P
 	{ FUNC(sec_device_type), CFG_CHANGED_SEC_DEVICE_TYPE },
-	{ INT(p2p_listen_reg_class), 0 },
-	{ INT(p2p_listen_channel), 0 },
+	{ 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 },
@@ -4181,6 +4281,7 @@
 	{ 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 },
@@ -4244,6 +4345,18 @@
 	{ 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 */
+	{ INT_RANGE(wpa_rsc_relaxation, 0, 1), 0 },
+	{ STR(sched_scan_plans), CFG_CHANGED_SCHED_SCAN_PLANS },
+#ifdef CONFIG_MBO
+	{ STR(non_pref_chan), 0 },
+	{ INT_RANGE(mbo_cell_capa, MBO_CELL_CAPA_AVAILABLE,
+		    MBO_CELL_CAPA_NOT_SUPPORTED), 0 },
+#endif /*CONFIG_MBO */
 };
 
 #undef FUNC
@@ -4302,6 +4415,23 @@
 }
 
 
+int wpa_config_get_num_global_field_names(void)
+{
+	return NUM_GLOBAL_FIELDS;
+}
+
+
+const char * wpa_config_get_global_field_name(unsigned int i, int *no_var)
+{
+	if (i >= NUM_GLOBAL_FIELDS)
+		return NULL;
+
+	if (no_var)
+		*no_var = !global_fields[i].param1;
+	return global_fields[i].name;
+}
+
+
 int wpa_config_process_global(struct wpa_config *config, char *pos, int line)
 {
 	size_t i;
diff --git a/wpa_supplicant/config.h b/wpa_supplicant/config.h
index d8ca054..9a13f5f 100644
--- a/wpa_supplicant/config.h
+++ b/wpa_supplicant/config.h
@@ -18,6 +18,11 @@
 #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
@@ -34,6 +39,8 @@
 #define DEFAULT_KEY_MGMT_OFFLOAD 1
 #define DEFAULT_CERT_IN_CB 1
 #define DEFAULT_P2P_GO_CTWINDOW 0
+#define DEFAULT_WPA_RSC_RELAXATION 1
+#define DEFAULT_MBO_CELL_CAPA MBO_CELL_CAPA_NOT_SUPPORTED
 
 #include "config_ssid.h"
 #include "wps/wps.h"
@@ -326,6 +333,7 @@
 #define CFG_CHANGED_EXT_PW_BACKEND BIT(14)
 #define CFG_CHANGED_NFC_PASSWORD_TOKEN BIT(15)
 #define CFG_CHANGED_P2P_PASSPHRASE_LEN BIT(16)
+#define CFG_CHANGED_SCHED_SCAN_PLANS BIT(17)
 
 /**
  * struct wpa_config - wpa_supplicant configuration data
@@ -401,6 +409,11 @@
 	 * 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;
 
@@ -734,6 +747,39 @@
 	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.
+	 *
+	 * @P2P_GO_FREQ_MOVE_SCM_ECSA: Same as
+	 * P2P_GO_FREQ_MOVE_SCM_PEER_SUPPORTS but a transition is possible only
+	 * if all the group members advertise eCSA support.
+	 */
+	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_SCM_ECSA = 3,
+		P2P_GO_FREQ_MOVE_MAX = P2P_GO_FREQ_MOVE_SCM_ECSA,
+	} 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
@@ -1161,6 +1207,15 @@
 	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
@@ -1184,6 +1239,58 @@
 	 * 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;
+
+	 /**
+	  * wpa_rsc_relaxation - RSC relaxation on GTK installation
+	  *
+	  * Values:
+	  * 0 - use the EAPOL-Key RSC value on GTK installation
+	  * 1 - use the null RSC if a bogus RSC value is detected in message 3
+	  * of 4-Way Handshake or message 1 of Group Key Handshake.
+	  */
+	 int wpa_rsc_relaxation;
+
+	/**
+	 * sched_scan_plans - Scan plans for scheduled scan
+	 *
+	 * Each scan plan specifies the interval between scans and the number of
+	 * iterations. The last scan plan only specifies the scan interval and
+	 * will be run infinitely.
+	 *
+	 * format: <interval:iterations> <interval2:iterations2> ... <interval>
+	 */
+	 char *sched_scan_plans;
+
+#ifdef CONFIG_MBO
+	/**
+	 * non_pref_chan - Non-preferred channels list, separated by spaces.
+	 *
+	 * format: op_class:chan:preference:reason<:detail>
+	 * Detail is optional.
+	 */
+	char *non_pref_chan;
+
+	/**
+	 * mbo_cell_capa - Cellular capabilities for MBO
+	 */
+	enum mbo_cellular_capa mbo_cell_capa;
+#endif /* CONFIG_MBO */
 };
 
 
@@ -1242,6 +1349,9 @@
 /* Prototypes for common functions from config.c */
 int wpa_config_process_global(struct wpa_config *config, char *pos, int line);
 
+int wpa_config_get_num_global_field_names(void);
+
+const char * wpa_config_get_global_field_name(unsigned int i, int *no_var);
 
 /* Prototypes for backend specific functions from the selected config_*.c */
 
diff --git a/wpa_supplicant/config_file.c b/wpa_supplicant/config_file.c
index aeea70c..38061f1 100644
--- a/wpa_supplicant/config_file.c
+++ b/wpa_supplicant/config_file.c
@@ -747,10 +747,15 @@
 	INT(no_auto_peer);
 	INT(frequency);
 	INT(fixed_freq);
+#ifdef CONFIG_ACS
+	INT(acs);
+#endif /* CONFIG_ACS */
 	write_int(f, "proactive_key_caching", ssid->proactive_key_caching, -1);
 	INT(disabled);
 	INT(peerkey);
 	INT(mixed_cell);
+	INT(max_oper_chwidth);
+	INT(pbss);
 #ifdef CONFIG_IEEE80211W
 	write_int(f, "ieee80211w", ssid->ieee80211w,
 		  MGMT_FRAME_PROTECTION_DEFAULT);
@@ -1133,6 +1138,25 @@
 			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);
+	if (WPA_GET_BE32(config->ip_addr_go))
+		fprintf(f, "ip_addr_go=%u.%u.%u.%u\n",
+			config->ip_addr_go[0], config->ip_addr_go[1],
+			config->ip_addr_go[2], config->ip_addr_go[3]);
+	if (WPA_GET_BE32(config->ip_addr_mask))
+		fprintf(f, "ip_addr_mask=%u.%u.%u.%u\n",
+			config->ip_addr_mask[0], config->ip_addr_mask[1],
+			config->ip_addr_mask[2], config->ip_addr_mask[3]);
+	if (WPA_GET_BE32(config->ip_addr_start))
+		fprintf(f, "ip_addr_start=%u.%u.%u.%u\n",
+			config->ip_addr_start[0], config->ip_addr_start[1],
+			config->ip_addr_start[2], config->ip_addr_start[3]);
+	if (WPA_GET_BE32(config->ip_addr_end))
+		fprintf(f, "ip_addr_end=%u.%u.%u.%u\n",
+			config->ip_addr_end[0], config->ip_addr_end[1],
+			config->ip_addr_end[2], config->ip_addr_end[3]);
 #endif /* CONFIG_P2P */
 	if (config->country[0] && config->country[1]) {
 		fprintf(f, "country=%c%c\n",
@@ -1282,6 +1306,11 @@
 		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);
 
@@ -1291,6 +1320,21 @@
 
 	if (config->wps_priority)
 		fprintf(f, "wps_priority=%d\n", config->wps_priority);
+
+	if (config->wpa_rsc_relaxation != DEFAULT_WPA_RSC_RELAXATION)
+		fprintf(f, "wpa_rsc_relaxation=%d\n",
+			config->wpa_rsc_relaxation);
+
+	if (config->sched_scan_plans)
+		fprintf(f, "sched_scan_plans=%s\n", config->sched_scan_plans);
+
+#ifdef CONFIG_MBO
+	if (config->non_pref_chan)
+		fprintf(f, "non_pref_chan=%s\n", config->non_pref_chan);
+	if (config->mbo_cell_capa != DEFAULT_MBO_CELL_CAPA)
+		fprintf(f, "mbo_cell_capa=%u\n", config->mbo_cell_capa);
+#endif /* CONFIG_MBO */
+
 }
 
 #endif /* CONFIG_NO_CONFIG_WRITE */
@@ -1353,7 +1397,7 @@
 	}
 #endif /* CONFIG_NO_CONFIG_BLOBS */
 
-	os_fsync(f);
+	os_fdatasync(f);
 
 	fclose(f);
 
diff --git a/wpa_supplicant/config_ssid.h b/wpa_supplicant/config_ssid.h
index 1c63e51..eb7b87b 100644
--- a/wpa_supplicant/config_ssid.h
+++ b/wpa_supplicant/config_ssid.h
@@ -225,7 +225,9 @@
 	 *
 	 * 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.
+	 * 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;
 
@@ -358,6 +360,15 @@
 	} mode;
 
 	/**
+	 * pbss - Whether to use PBSS. Relevant to DMG networks only.
+	 * Used together with mode configuration. When mode is AP, it
+	 * means to start a PCP instead of a regular AP. When mode is INFRA it
+	 * means connect to a PCP instead of AP. P2P_GO and P2P_GROUP_FORMATION
+	 * modes must use PBSS in DMG network.
+	 */
+	int pbss;
+
+	/**
 	 * disabled - Whether this network is currently disabled
 	 *
 	 * 0 = this network can be used (default).
@@ -429,6 +440,18 @@
 	 */
 	int fixed_freq;
 
+#ifdef CONFIG_ACS
+	/**
+	 * ACS - Automatic Channel Selection for AP mode
+	 *
+	 * If present, it will be handled together with frequency.
+	 * frequency will be used to determine hardware mode only, when it is
+	 * used for both hardware mode and channel when used alone. This will
+	 * force the channel to be set to 0, thus enabling ACS.
+	 */
+	int acs;
+#endif /* CONFIG_ACS */
+
 	/**
 	 * mesh_basic_rates - BSS Basic rate set for mesh network
 	 *
@@ -447,6 +470,10 @@
 
 	int vht;
 
+	u8 max_oper_chwidth;
+
+	unsigned int vht_center_freq2;
+
 	/**
 	 * wpa_ptk_rekey - Maximum lifetime for PTK in seconds
 	 *
diff --git a/wpa_supplicant/ctrl_iface.c b/wpa_supplicant/ctrl_iface.c
index bab10ad..8574437 100644
--- a/wpa_supplicant/ctrl_iface.c
+++ b/wpa_supplicant/ctrl_iface.c
@@ -28,6 +28,8 @@
 #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"
@@ -53,6 +55,7 @@
 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,
+						  const char *input,
 						  char *buf, int len);
 static int * freq_range_to_channel_list(struct wpa_supplicant *wpa_s,
 					char *val);
@@ -369,6 +372,20 @@
 		wps_corrupt_pkhash = atoi(value);
 		wpa_printf(MSG_DEBUG, "WPS: Testing - wps_corrupt_pkhash=%d",
 			   wps_corrupt_pkhash);
+	} else if (os_strcasecmp(cmd, "wps_force_auth_types") == 0) {
+		if (value[0] == '\0') {
+			wps_force_auth_types_in_use = 0;
+		} else {
+			wps_force_auth_types = strtol(value, NULL, 0);
+			wps_force_auth_types_in_use = 1;
+		}
+	} else if (os_strcasecmp(cmd, "wps_force_encr_types") == 0) {
+		if (value[0] == '\0') {
+			wps_force_encr_types_in_use = 0;
+		} else {
+			wps_force_encr_types = strtol(value, NULL, 0);
+			wps_force_encr_types_in_use = 1;
+		}
 #endif /* CONFIG_WPS_TESTING */
 	} else if (os_strcasecmp(cmd, "ampdu") == 0) {
 		if (wpa_drv_ampdu(wpa_s, atoi(value)) < 0)
@@ -465,6 +482,8 @@
 		wpa_s->extra_roc_dur = atoi(value);
 	} else if (os_strcasecmp(cmd, "test_failure") == 0) {
 		wpa_s->test_failure = atoi(value);
+	} else if (os_strcasecmp(cmd, "p2p_go_csa_on_inv") == 0) {
+		wpa_s->p2p_go_csa_on_inv = !!atoi(value);
 #endif /* CONFIG_TESTING_OPTIONS */
 #ifndef CONFIG_NO_CONFIG_BLOBS
 	} else if (os_strcmp(cmd, "blob") == 0) {
@@ -472,6 +491,12 @@
 #endif /* CONFIG_NO_CONFIG_BLOBS */
 	} else if (os_strcasecmp(cmd, "setband") == 0) {
 		ret = wpas_ctrl_set_band(wpa_s, value);
+#ifdef CONFIG_MBO
+	} else if (os_strcasecmp(cmd, "non_pref_chan") == 0) {
+		ret = wpas_mbo_update_non_pref_chan(wpa_s, value);
+	} else if (os_strcasecmp(cmd, "mbo_cell_capa") == 0) {
+		wpas_mbo_update_cell_capa(wpa_s, atoi(value));
+#endif /* CONFIG_MBO */
 	} else {
 		value[-1] = '=';
 		ret = wpa_config_process_global(wpa_s->conf, cmd, -1);
@@ -938,7 +963,8 @@
 	if (os_strcmp(cmd, "any") == 0)
 		_bssid = NULL;
 	else if (os_strcmp(cmd, "get") == 0) {
-		ret = wps_generate_pin();
+		if (wps_generate_pin((unsigned int *) &ret) < 0)
+			return -1;
 		goto done;
 	} else if (hwaddr_aton(cmd, bssid)) {
 		wpa_printf(MSG_DEBUG, "CTRL_IFACE WPS_PIN: invalid BSSID '%s'",
@@ -2134,45 +2160,6 @@
 }
 
 
-static 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 "?";
-	}
-}
-
-
-static 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;
-}
-
-
 static int wpa_supplicant_ctrl_iface_log_level(struct wpa_supplicant *wpa_s,
 					       char *cmd, char *buf,
 					       size_t buflen)
@@ -2205,7 +2192,7 @@
 		}
 	}
 
-	if (cmd && os_strlen(cmd)) {
+	if (os_strlen(cmd)) {
 		int level = str_to_debug_level(cmd);
 		if (level < 0)
 			return -1;
@@ -2582,6 +2569,14 @@
 		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));
@@ -2732,6 +2727,40 @@
 	return 0;
 }
 
+
+static int wpa_supplicant_ctrl_iface_mesh_peer_remove(
+	struct wpa_supplicant *wpa_s, char *cmd)
+{
+	u8 addr[ETH_ALEN];
+
+	if (hwaddr_aton(cmd, addr) < 0)
+		return -1;
+
+	return wpas_mesh_peer_remove(wpa_s, addr);
+}
+
+
+static int wpa_supplicant_ctrl_iface_mesh_peer_add(
+	struct wpa_supplicant *wpa_s, char *cmd)
+{
+	u8 addr[ETH_ALEN];
+	int duration;
+	char *pos;
+
+	pos = os_strstr(cmd, " duration=");
+	if (pos) {
+		*pos = '\0';
+		duration = atoi(pos + 10);
+	} else {
+		duration = -1;
+	}
+
+	if (hwaddr_aton(cmd, addr))
+		return -1;
+
+	return wpas_mesh_peer_add(wpa_s, addr, duration);
+}
+
 #endif /* CONFIG_MESH */
 
 
@@ -2773,6 +2802,8 @@
 		}
 	}
 
+	wpa_s->scan_min_time.sec = 0;
+	wpa_s->scan_min_time.usec = 0;
 	wpa_supplicant_select_network(wpa_s, ssid);
 
 	return 0;
@@ -2810,6 +2841,8 @@
 			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;
@@ -2970,22 +3003,29 @@
 	struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid,
 	char *name, char *value)
 {
-	if (wpa_config_set(ssid, name, value, 0) < 0) {
+	int ret;
+
+	ret = wpa_config_set(ssid, name, value, 0);
+	if (ret < 0) {
 		wpa_printf(MSG_DEBUG, "CTRL_IFACE: Failed to set network "
 			   "variable '%s'", name);
 		return -1;
 	}
+	if (ret == 1)
+		return 0; /* No change to the previously configured value */
 
 	if (os_strcmp(name, "bssid") != 0 &&
-	    os_strcmp(name, "priority") != 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 (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 &&
@@ -3064,19 +3104,19 @@
 	*name++ = '\0';
 
 	id = atoi(cmd);
-	wpa_printf(MSG_DEBUG, "CTRL_IFACE: GET_NETWORK id=%d name='%s'",
+	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_DEBUG, "CTRL_IFACE: Could not find network "
+		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_DEBUG, "CTRL_IFACE: Failed to get network "
+		wpa_printf(MSG_EXCESSIVE, "CTRL_IFACE: Failed to get network "
 			   "variable '%s'", name);
 		return -1;
 	}
@@ -3094,7 +3134,8 @@
 
 
 static int wpa_supplicant_ctrl_iface_dup_network(
-	struct wpa_supplicant *wpa_s, char *cmd)
+	struct wpa_supplicant *wpa_s, char *cmd,
+	struct wpa_supplicant *dst_wpa_s)
 {
 	struct wpa_ssid *ssid_s, *ssid_d;
 	char *name, *id, *value;
@@ -3113,8 +3154,10 @@
 
 	id_s = atoi(cmd);
 	id_d = atoi(id);
-	wpa_printf(MSG_DEBUG, "CTRL_IFACE: DUP_NETWORK id=%d -> %d name='%s'",
-		   id_s, id_d, name);
+
+	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) {
@@ -3123,7 +3166,7 @@
 		return -1;
 	}
 
-	ssid_d = wpa_config_get_network(wpa_s->conf, id_d);
+	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);
@@ -3137,7 +3180,7 @@
 		return -1;
 	}
 
-	ret = wpa_supplicant_ctrl_iface_update_network(wpa_s, ssid_d, name,
+	ret = wpa_supplicant_ctrl_iface_update_network(dst_wpa_s, ssid_d, name,
 						       value);
 
 	os_free(value);
@@ -3948,6 +3991,24 @@
 	}
 #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 */
+
+#ifdef CONFIG_ACS
+	if (os_strcmp(field, "acs") == 0) {
+		res = os_snprintf(buf, buflen, "ACS");
+		if (os_snprintf_error(buflen, res))
+			return -1;
+		return res;
+	}
+#endif /* CONFIG_ACS */
+
 	wpa_printf(MSG_DEBUG, "CTRL_IFACE: Unknown GET_CAPABILITY field '%s'",
 		   field);
 
@@ -4197,9 +4258,10 @@
 	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 < 0 || ret >= end - pos)
+		if (ret >= end - pos)
 			return 0;
-		pos += ret;
+		if (ret > 0)
+			pos += ret;
 	}
 #endif /* CONFIG_WPS */
 
@@ -4243,6 +4305,8 @@
 #ifdef CONFIG_INTERWORKING
 	if ((mask & WPA_BSS_MASK_INTERNETW) && bss->anqp) {
 		struct wpa_bss_anqp *anqp = bss->anqp;
+		struct wpa_bss_anqp_elem *elem;
+
 		pos = anqp_add_hex(pos, end, "anqp_capability_list",
 				   anqp->capability_list);
 		pos = anqp_add_hex(pos, end, "anqp_venue_name",
@@ -4272,6 +4336,15 @@
 		pos = anqp_add_hex(pos, end, "hs20_osu_providers_list",
 				   anqp->hs20_osu_providers_list);
 #endif /* CONFIG_HS20 */
+
+		dl_list_for_each(elem, &anqp->anqp_elems,
+				 struct wpa_bss_anqp_elem, list) {
+			char title[20];
+
+			os_snprintf(title, sizeof(title), "anqp[%u]",
+				    elem->infoid);
+			pos = anqp_add_hex(pos, end, title, elem->payload);
+		}
 	}
 #endif /* CONFIG_INTERWORKING */
 
@@ -4300,6 +4373,15 @@
 		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))
@@ -4647,6 +4729,48 @@
 }
 
 
+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((unsigned char) *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;
@@ -4655,6 +4779,7 @@
 	char *info = NULL;
 	u8 role = P2PS_SETUP_NONE;
 	long long unsigned val;
+	int i;
 
 	pos = os_strstr(cmd, "info=");
 	if (pos) {
@@ -4713,6 +4838,18 @@
 	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) {
@@ -4789,11 +4926,37 @@
 	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 parse_freq(int chwidth, int freq2)
+{
+	if (freq2 < 0)
+		return -1;
+	if (freq2)
+		return VHT_CHANWIDTH_80P80MHZ;
+
+	switch (chwidth) {
+	case 0:
+	case 20:
+	case 40:
+		return VHT_CHANWIDTH_USE_HT;
+	case 80:
+		return VHT_CHANWIDTH_80MHZ;
+	case 160:
+		return VHT_CHANWIDTH_160MHZ;
+	default:
+		wpa_printf(MSG_DEBUG, "Unknown max oper bandwidth: %d",
+			   chwidth);
+		return -1;
+	}
+}
+
+
 static int p2p_ctrl_connect(struct wpa_supplicant *wpa_s, char *cmd,
 			    char *buf, size_t buflen)
 {
@@ -4810,7 +4973,9 @@
 	int go_intent = -1;
 	int freq = 0;
 	int pd;
-	int ht40, vht;
+	int ht40, vht, max_oper_chwidth, chwidth = 0, freq2 = 0;
+	u8 _group_ssid[SSID_MAX_LEN], *group_ssid = NULL;
+	size_t group_ssid_len = 0;
 
 	if (!wpa_s->global->p2p_init_wpa_s)
 		return -1;
@@ -4823,7 +4988,7 @@
 	/* <addr> <"pbc" | "pin" | PIN> [label|display|keypad|p2ps]
 	 * [persistent|persistent=<network id>]
 	 * [join] [auth] [go_intent=<0..15>] [freq=<in MHz>] [provdisc]
-	 * [ht40] [vht] [auto] */
+	 * [ht40] [vht] [auto] [ssid=<hexdump>] */
 
 	if (hwaddr_aton(cmd, addr))
 		return -1;
@@ -4871,6 +5036,34 @@
 			return -1;
 	}
 
+	pos2 = os_strstr(pos, " freq2=");
+	if (pos2)
+		freq2 = atoi(pos2 + 7);
+
+	pos2 = os_strstr(pos, " max_oper_chwidth=");
+	if (pos2)
+		chwidth = atoi(pos2 + 18);
+
+	max_oper_chwidth = parse_freq(chwidth, freq2);
+	if (max_oper_chwidth < 0)
+		return -1;
+
+	pos2 = os_strstr(pos, " ssid=");
+	if (pos2) {
+		char *end;
+
+		pos2 += 6;
+		end = os_strchr(pos2, ' ');
+		if (!end)
+			group_ssid_len = os_strlen(pos2) / 2;
+		else
+			group_ssid_len = (end - pos2) / 2;
+		if (group_ssid_len == 0 || group_ssid_len > SSID_MAX_LEN ||
+		    hexstr2bin(pos2, _group_ssid, group_ssid_len) < 0)
+			return -1;
+		group_ssid = _group_ssid;
+	}
+
 	if (os_strncmp(pos, "pin", 3) == 0) {
 		/* Request random PIN (to be displayed) and enable the PIN */
 		wps_method = WPS_PIN_DISPLAY;
@@ -4895,8 +5088,9 @@
 
 	new_pin = wpas_p2p_connect(wpa_s, addr, pin, wps_method,
 				   persistent_group, automatic, join,
-				   auth, go_intent, freq, persistent_id, pd,
-				   ht40, vht);
+				   auth, go_intent, freq, freq2, persistent_id,
+				   pd, ht40, vht, max_oper_chwidth,
+				   group_ssid, group_ssid_len);
 	if (new_pin == -2) {
 		os_memcpy(buf, "FAIL-CHANNEL-UNAVAILABLE\n", 25);
 		return 25;
@@ -5208,6 +5402,8 @@
 	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)
@@ -5280,6 +5476,19 @@
 	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;
@@ -5297,7 +5506,7 @@
 
 	return wpas_p2p_service_add_asp(wpa_s, auto_accept, adv_id, adv_str,
 					(u8) svc_state, (u16) config_methods,
-					svc_info);
+					svc_info, cpt_prio);
 }
 
 
@@ -5436,7 +5645,7 @@
 	struct wpa_ssid *ssid;
 	u8 *_peer = NULL, peer[ETH_ALEN];
 	int freq = 0, pref_freq = 0;
-	int ht40, vht;
+	int ht40, vht, max_oper_chwidth, chwidth = 0, freq2 = 0;
 
 	id = atoi(cmd);
 	pos = os_strstr(cmd, " peer=");
@@ -5474,8 +5683,20 @@
 	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);
+	pos = os_strstr(cmd, "freq2=");
+	if (pos)
+		freq2 = atoi(pos + 6);
+
+	pos = os_strstr(cmd, " max_oper_chwidth=");
+	if (pos)
+		chwidth = atoi(pos + 18);
+
+	max_oper_chwidth = parse_freq(chwidth, freq2);
+	if (max_oper_chwidth < 0)
+		return -1;
+
+	return wpas_p2p_invite(wpa_s, _peer, ssid, NULL, freq, freq2, ht40, vht,
+			       max_oper_chwidth, pref_freq);
 }
 
 
@@ -5522,7 +5743,8 @@
 
 
 static int p2p_ctrl_group_add_persistent(struct wpa_supplicant *wpa_s,
-					 int id, int freq, int ht40, int vht)
+					 int id, int freq, int vht_center_freq2,
+					 int ht40, int vht, int vht_chwidth)
 {
 	struct wpa_ssid *ssid;
 
@@ -5534,8 +5756,9 @@
 		return -1;
 	}
 
-	return wpas_p2p_group_add_persistent(wpa_s, ssid, 0, freq, 0, ht40, vht,
-					     NULL, 0);
+	return wpas_p2p_group_add_persistent(wpa_s, ssid, 0, freq,
+					     vht_center_freq2, 0, ht40, vht,
+					     vht_chwidth, NULL, 0, 0);
 }
 
 
@@ -5544,11 +5767,14 @@
 	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;
+	int max_oper_chwidth, chwidth = 0, freq2 = 0;
 	char *token, *context = NULL;
 
 	while ((token = str_token(cmd, " ", &context))) {
 		if (sscanf(token, "freq=%d", &freq) == 1 ||
-		    sscanf(token, "persistent=%d", &group_id) == 1) {
+		    sscanf(token, "freq2=%d", &freq2) == 1 ||
+		    sscanf(token, "persistent=%d", &group_id) == 1 ||
+		    sscanf(token, "max_oper_chwidth=%d", &chwidth) == 1) {
 			continue;
 		} else if (os_strcmp(token, "ht40") == 0) {
 			ht40 = 1;
@@ -5565,11 +5791,17 @@
 		}
 	}
 
+	max_oper_chwidth = parse_freq(chwidth, freq2);
+	if (max_oper_chwidth < 0)
+		return -1;
+
 	if (group_id >= 0)
 		return p2p_ctrl_group_add_persistent(wpa_s, group_id,
-						     freq, ht40, vht);
+						     freq, freq2, ht40, vht,
+						     max_oper_chwidth);
 
-	return wpas_p2p_group_add(wpa_s, persistent, freq, ht40, vht);
+	return wpas_p2p_group_add(wpa_s, persistent, freq, freq2, ht40, vht,
+				  max_oper_chwidth);
 }
 
 
@@ -5699,7 +5931,7 @@
 			   freq->min, freq->max);
 	}
 
-	wpas_p2p_update_channel_list(wpa_s);
+	wpas_p2p_update_channel_list(wpa_s, WPAS_P2P_CHANNEL_UPDATE_DISALLOW);
 	return 0;
 }
 
@@ -5728,8 +5960,15 @@
 	}
 
 	if (os_strcmp(cmd, "listen_channel") == 0) {
-		return p2p_set_listen_channel(wpa_s->global->p2p, 81,
-					      atoi(param), 1);
+		char *pos;
+		u8 channel, op_class;
+
+		channel = atoi(param);
+		pos = os_strchr(param, ' ');
+		op_class = pos ? atoi(pos) : 81;
+
+		return p2p_set_listen_channel(wpa_s->global->p2p, op_class,
+					      channel, 1);
 	}
 
 	if (os_strcmp(cmd, "ssid_postfix") == 0) {
@@ -5916,6 +6155,7 @@
 	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);
 }
@@ -6308,7 +6548,7 @@
 	if (subtypes == 0)
 		return -1;
 
-	return hs20_anqp_send_req(wpa_s, dst_addr, subtypes, NULL, 0);
+	return hs20_anqp_send_req(wpa_s, dst_addr, subtypes, NULL, 0, 0);
 }
 
 
@@ -6331,7 +6571,7 @@
 
 	ret = hs20_anqp_send_req(wpa_s, addr,
 				 BIT(HS20_STYPE_NAI_HOME_REALM_QUERY),
-				 buf, len);
+				 buf, len, 0);
 
 	os_free(buf);
 
@@ -6377,14 +6617,59 @@
 
 	ret = hs20_anqp_send_req(wpa_s, dst_addr,
 				 BIT(HS20_STYPE_NAI_HOME_REALM_QUERY),
-				 buf, len);
+				 buf, len, 0);
 	os_free(buf);
 
 	return ret;
 }
 
 
-static int hs20_icon_request(struct wpa_supplicant *wpa_s, char *cmd)
+static int get_hs20_icon(struct wpa_supplicant *wpa_s, char *cmd, char *reply,
+			 int buflen)
+{
+	u8 dst_addr[ETH_ALEN];
+	int used;
+	char *ctx = NULL, *icon, *poffset, *psize;
+
+	used = hwaddr_aton2(cmd, dst_addr);
+	if (used < 0)
+		return -1;
+	cmd += used;
+
+	icon = str_token(cmd, " ", &ctx);
+	poffset = str_token(cmd, " ", &ctx);
+	psize = str_token(cmd, " ", &ctx);
+	if (!icon || !poffset || !psize)
+		return -1;
+
+	wpa_s->fetch_osu_icon_in_progress = 0;
+	return hs20_get_icon(wpa_s, dst_addr, icon, atoi(poffset), atoi(psize),
+			     reply, buflen);
+}
+
+
+static int del_hs20_icon(struct wpa_supplicant *wpa_s, char *cmd)
+{
+	u8 dst_addr[ETH_ALEN];
+	int used;
+	char *icon;
+
+	if (!cmd[0])
+		return hs20_del_icon(wpa_s, NULL, NULL);
+
+	used = hwaddr_aton2(cmd, dst_addr);
+	if (used < 0)
+		return -1;
+
+	while (cmd[used] == ' ')
+		used++;
+	icon = cmd[used] ? &cmd[used] : NULL;
+
+	return hs20_del_icon(wpa_s, dst_addr, icon);
+}
+
+
+static int hs20_icon_request(struct wpa_supplicant *wpa_s, char *cmd, int inmem)
 {
 	u8 dst_addr[ETH_ALEN];
 	int used;
@@ -6400,7 +6685,7 @@
 
 	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));
+				  (u8 *) icon, os_strlen(icon), inmem);
 }
 
 #endif /* CONFIG_HS20 */
@@ -6490,14 +6775,27 @@
 
 static int wpas_ctrl_iface_wnm_bss_query(struct wpa_supplicant *wpa_s, char *cmd)
 {
-	int query_reason;
+	int query_reason, list = 0;
 
 	query_reason = atoi(cmd);
 
-	wpa_printf(MSG_DEBUG, "CTRL_IFACE: WNM_BSS_QUERY query_reason=%d",
-		   query_reason);
+	cmd = os_strchr(cmd, ' ');
+	if (cmd) {
+		cmd++;
+		if (os_strncmp(cmd, "list", 4) == 0) {
+			list = 1;
+		} else {
+			wpa_printf(MSG_DEBUG, "WNM Query: Invalid option %s",
+				   cmd);
+			return -1;
+		}
+	}
 
-	return wnm_send_bss_transition_mgmt_query(wpa_s, query_reason);
+	wpa_printf(MSG_DEBUG,
+		   "CTRL_IFACE: WNM_BSS_QUERY query_reason=%d%s",
+		   query_reason, list ? " candidate list" : "");
+
+	return wnm_send_bss_transition_mgmt_query(wpa_s, query_reason, list);
 }
 
 #endif /* CONFIG_WNM */
@@ -6562,6 +6860,75 @@
 }
 
 
+static int wpas_ctrl_iface_signal_monitor(struct wpa_supplicant *wpa_s,
+					  const char *cmd)
+{
+	const char *pos;
+	int threshold = 0;
+	int hysteresis = 0;
+
+	if (wpa_s->bgscan && wpa_s->bgscan_priv) {
+		wpa_printf(MSG_DEBUG,
+			   "Reject SIGNAL_MONITOR command - bgscan is active");
+		return -1;
+	}
+	pos = os_strstr(cmd, "THRESHOLD=");
+	if (pos)
+		threshold = atoi(pos + 10);
+	pos = os_strstr(cmd, "HYSTERESIS=");
+	if (pos)
+		hysteresis = atoi(pos + 11);
+	return wpa_drv_signal_monitor(wpa_s, threshold, hysteresis);
+}
+
+
+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)
 {
@@ -6619,13 +6986,13 @@
 
 	/* cmd: <vendor id> <subcommand id> [<hex formatted data>] */
 	vendor_id = strtoul(cmd, &pos, 16);
-	if (!isblank(*pos))
+	if (!isblank((unsigned char) *pos))
 		return -EINVAL;
 
 	subcmd = strtoul(pos, &pos, 10);
 
 	if (*pos != '\0') {
-		if (!isblank(*pos++))
+		if (!isblank((unsigned char) *pos++))
 			return -EINVAL;
 		data_len = os_strlen(pos);
 	}
@@ -6673,10 +7040,20 @@
 
 	wpa_dbg(wpa_s, MSG_DEBUG, "Flush all wpa_supplicant state");
 
+	wpas_abort_ongoing_scan(wpa_s);
+
+	if (wpa_s->wpa_state >= WPA_AUTHENTICATING) {
+		/*
+		 * Avoid possible auto connect re-connection on getting
+		 * disconnected due to state flush.
+		 */
+		wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED);
+	}
+
 #ifdef CONFIG_P2P
+	wpas_p2p_group_remove(p2p_wpa_s, "*");
 	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;
@@ -6684,13 +7061,17 @@
 	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;
+	p2p_wpa_s->global->pending_p2ps_group_freq = 0;
 #endif /* CONFIG_P2P */
 
 #ifdef CONFIG_WPS_TESTING
 	wps_version_number = 0x20;
 	wps_testing_dummy_cred = 0;
 	wps_corrupt_pkhash = 0;
+	wps_force_auth_types_in_use = 0;
+	wps_force_encr_types_in_use = 0;
 #endif /* CONFIG_WPS_TESTING */
 #ifdef CONFIG_WPS
 	wpa_s->wps_fragment_size = 0;
@@ -6748,7 +7129,10 @@
 	wpa_s->next_ssid = NULL;
 
 #ifdef CONFIG_INTERWORKING
+#ifdef CONFIG_HS20
 	hs20_cancel_fetch_osu(wpa_s);
+	hs20_del_icon(wpa_s, NULL, NULL);
+#endif /* CONFIG_HS20 */
 #endif /* CONFIG_INTERWORKING */
 
 	wpa_s->ext_mgmt_frame_handling = 0;
@@ -6756,6 +7140,8 @@
 #ifdef CONFIG_TESTING_OPTIONS
 	wpa_s->extra_roc_dur = 0;
 	wpa_s->test_failure = WPAS_TEST_FAILURE_NONE;
+	wpa_s->p2p_go_csa_on_inv = 0;
+	wpa_sm_set_test_assoc_ie(wpa_s->wpa, NULL);
 #endif /* CONFIG_TESTING_OPTIONS */
 
 	wpa_s->disconnected = 0;
@@ -6773,6 +7159,7 @@
 	}
 
 	eloop_cancel_timeout(wpas_network_reenabled, wpa_s, NULL);
+	wpa_s->wnmsleep_used = 0;
 }
 
 
@@ -7381,7 +7768,7 @@
 {
 	struct wpa_supplicant *wpa_s = ctx;
 	const struct ether_header *eth;
-	const struct iphdr *ip;
+	struct iphdr ip;
 	const u8 *pos;
 	unsigned int i;
 
@@ -7389,14 +7776,14 @@
 		return;
 
 	eth = (const struct ether_header *) buf;
-	ip = (const struct iphdr *) (eth + 1);
-	pos = (const u8 *) (ip + 1);
+	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)
+	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++) {
+	for (i = 0; i < HWSIM_IP_LEN - sizeof(ip); i++) {
 		if (*pos != (u8) i)
 			return;
 		pos++;
@@ -7411,6 +7798,8 @@
 					    char *cmd)
 {
 	int enabled = atoi(cmd);
+	char *pos;
+	const char *ifname;
 
 	if (!enabled) {
 		if (wpa_s->l2_test) {
@@ -7424,7 +7813,13 @@
 	if (wpa_s->l2_test)
 		return 0;
 
-	wpa_s->l2_test = l2_packet_init(wpa_s->ifname, wpa_s->own_addr,
+	pos = os_strstr(cmd, " ifname=");
+	if (pos)
+		ifname = pos + 8;
+	else
+		ifname = wpa_s->ifname;
+
+	wpa_s->l2_test = l2_packet_init(ifname, wpa_s->own_addr,
 					ETHERTYPE_IP, wpas_data_test_rx,
 					wpa_s, 1);
 	if (wpa_s->l2_test == NULL)
@@ -7443,7 +7838,7 @@
 	int used;
 	long int val;
 	u8 tos;
-	u8 buf[HWSIM_PACKETLEN];
+	u8 buf[2 + HWSIM_PACKETLEN];
 	struct ether_header *eth;
 	struct iphdr *ip;
 	u8 *dpos;
@@ -7471,7 +7866,7 @@
 		return -1;
 	tos = val;
 
-	eth = (struct ether_header *) buf;
+	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);
@@ -7483,14 +7878,14 @@
 	ip->tos = tos;
 	ip->tot_len = htons(HWSIM_IP_LEN);
 	ip->protocol = 1;
-	ip->saddr = htonl(192 << 24 | 168 << 16 | 1 << 8 | 1);
-	ip->daddr = htonl(192 << 24 | 168 << 16 | 1 << 8 | 2);
+	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,
+	if (l2_packet_send(wpa_s->l2_test, dst, ETHERTYPE_IP, &buf[2],
 			   HWSIM_PACKETLEN) < 0)
 		return -1;
 
@@ -7580,6 +7975,44 @@
 }
 
 
+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 */
+}
+
+
 static void wpas_ctrl_event_test_cb(void *eloop_ctx, void *timeout_ctx)
 {
 	struct wpa_supplicant *wpa_s = eloop_ctx;
@@ -7606,61 +8039,36 @@
 				      (void *) (intptr_t) count);
 }
 
-#endif /* CONFIG_TESTING_OPTIONS */
 
-
-static void wpas_ctrl_vendor_elem_update(struct wpa_supplicant *wpa_s)
+static int wpas_ctrl_test_assoc_ie(struct wpa_supplicant *wpa_s,
+				   const char *cmd)
 {
-	unsigned int i;
-	char buf[30];
+	struct wpabuf *buf;
+	size_t len;
 
-	wpa_printf(MSG_DEBUG, "Update vendor elements");
+	len = os_strlen(cmd);
+	if (len & 1)
+		return -1;
+	len /= 2;
 
-	for (i = 0; i < NUM_VENDOR_ELEM_FRAMES; i++) {
-		if (wpa_s->vendor_elem[i]) {
-			int res;
+	if (len == 0) {
+		buf = NULL;
+	} else {
+		buf = wpabuf_alloc(len);
+		if (buf == NULL)
+			return -1;
 
-			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]);
-			}
+		if (hexstr2bin(cmd, wpabuf_put(buf, len), len) < 0) {
+			wpabuf_free(buf);
+			return -1;
 		}
 	}
 
-#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 */
+	wpa_sm_set_test_assoc_ie(wpa_s->wpa, buf);
+	return 0;
 }
 
-
-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;
-	}
-}
+#endif /* CONFIG_TESTING_OPTIONS */
 
 
 static int wpas_ctrl_vendor_elem_add(struct wpa_supplicant *wpa_s, char *cmd)
@@ -7674,7 +8082,7 @@
 	frame = atoi(pos);
 	if (frame < 0 || frame >= NUM_VENDOR_ELEM_FRAMES)
 		return -1;
-	wpa_s = wpas_ctrl_vendor_elem_iface(wpa_s, frame);
+	wpa_s = wpas_vendor_elem(wpa_s, frame);
 
 	pos = os_strchr(pos, ' ');
 	if (pos == NULL)
@@ -7705,7 +8113,7 @@
 
 	if (wpa_s->vendor_elem[frame] == NULL) {
 		wpa_s->vendor_elem[frame] = buf;
-		wpas_ctrl_vendor_elem_update(wpa_s);
+		wpas_vendor_elem_update(wpa_s);
 		return 0;
 	}
 
@@ -7716,7 +8124,7 @@
 
 	wpabuf_put_buf(wpa_s->vendor_elem[frame], buf);
 	wpabuf_free(buf);
-	wpas_ctrl_vendor_elem_update(wpa_s);
+	wpas_vendor_elem_update(wpa_s);
 
 	return 0;
 }
@@ -7729,7 +8137,7 @@
 
 	if (frame < 0 || frame >= NUM_VENDOR_ELEM_FRAMES)
 		return -1;
-	wpa_s = wpas_ctrl_vendor_elem_iface(wpa_s, frame);
+	wpa_s = wpas_vendor_elem(wpa_s, frame);
 
 	if (wpa_s->vendor_elem[frame] == NULL)
 		return 0;
@@ -7747,12 +8155,12 @@
 	size_t len;
 	u8 *buf;
 	struct ieee802_11_elems elems;
-	u8 *ie, *end;
+	int res;
 
 	frame = atoi(pos);
 	if (frame < 0 || frame >= NUM_VENDOR_ELEM_FRAMES)
 		return -1;
-	wpa_s = wpas_ctrl_vendor_elem_iface(wpa_s, frame);
+	wpa_s = wpas_vendor_elem(wpa_s, frame);
 
 	pos = os_strchr(pos, ' ');
 	if (pos == NULL)
@@ -7762,7 +8170,7 @@
 	if (*pos == '*') {
 		wpabuf_free(wpa_s->vendor_elem[frame]);
 		wpa_s->vendor_elem[frame] = NULL;
-		wpas_ctrl_vendor_elem_update(wpa_s);
+		wpas_vendor_elem_update(wpa_s);
 		return 0;
 	}
 
@@ -7790,31 +8198,9 @@
 		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;
-	}
-
+	res = wpas_vendor_elem_remove(wpa_s, frame, buf, len);
 	os_free(buf);
-
-	return -1;
+	return res;
 }
 
 
@@ -7983,6 +8369,42 @@
 }
 
 
+static int wpas_ctrl_iface_pmksa(struct wpa_supplicant *wpa_s,
+				 char *buf, size_t buflen)
+{
+	size_t reply_len;
+
+	reply_len = wpa_sm_pmksa_cache_list(wpa_s->wpa, buf, buflen);
+#ifdef CONFIG_AP
+	reply_len += wpas_ap_pmksa_cache_list(wpa_s, &buf[reply_len],
+					      buflen - reply_len);
+#endif /* CONFIG_AP */
+	return reply_len;
+}
+
+
+static void wpas_ctrl_iface_pmksa_flush(struct wpa_supplicant *wpa_s)
+{
+	wpa_sm_pmksa_cache_flush(wpa_s->wpa, NULL);
+#ifdef CONFIG_AP
+	wpas_ap_pmksa_cache_flush(wpa_s);
+#endif /* CONFIG_AP */
+}
+
+
+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)
 {
@@ -8006,9 +8428,7 @@
 		wpa_hexdump_ascii_key(MSG_DEBUG, "RX ctrl_iface",
 				      (const u8 *) buf, os_strlen(buf));
 	} else {
-		int level = MSG_DEBUG;
-		if (os_strcmp(buf, "PING") == 0)
-			level = MSG_EXCESSIVE;
+		int level = wpas_ctrl_cmd_debug_level(buf);
 		wpa_dbg(wpa_s, level, "Control interface command '%s'", buf);
 	}
 
@@ -8043,10 +8463,9 @@
 		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);
+		reply_len = wpas_ctrl_iface_pmksa(wpa_s, reply, reply_size);
 	} else if (os_strcmp(buf, "PMKSA_FLUSH") == 0) {
-		wpa_sm_pmksa_cache_flush(wpa_s->wpa, NULL);
+		wpas_ctrl_iface_pmksa_flush(wpa_s);
 	} else if (os_strncmp(buf, "SET ", 4) == 0) {
 		if (wpa_supplicant_ctrl_iface_set(wpa_s, buf + 4))
 			reply_len = -1;
@@ -8214,6 +8633,12 @@
 		if (wpa_supplicant_ctrl_iface_mesh_group_remove(wpa_s,
 								buf + 18))
 			reply_len = -1;
+	} else if (os_strncmp(buf, "MESH_PEER_REMOVE ", 17) == 0) {
+		if (wpa_supplicant_ctrl_iface_mesh_peer_remove(wpa_s, buf + 17))
+			reply_len = -1;
+	} else if (os_strncmp(buf, "MESH_PEER_ADD ", 14) == 0) {
+		if (wpa_supplicant_ctrl_iface_mesh_peer_add(wpa_s, buf + 14))
+			reply_len = -1;
 #endif /* CONFIG_MESH */
 #ifdef CONFIG_P2P
 	} else if (os_strncmp(buf, "P2P_FIND ", 9) == 0) {
@@ -8366,7 +8791,15 @@
 		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)
+		if (hs20_icon_request(wpa_s, buf + 18, 0) < 0)
+			reply_len = -1;
+	} else if (os_strncmp(buf, "REQ_HS20_ICON ", 14) == 0) {
+		if (hs20_icon_request(wpa_s, buf + 14, 1) < 0)
+			reply_len = -1;
+	} else if (os_strncmp(buf, "GET_HS20_ICON ", 14) == 0) {
+		reply_len = get_hs20_icon(wpa_s, buf + 14, reply, reply_size);
+	} else if (os_strncmp(buf, "DEL_HS20_ICON ", 14) == 0) {
+		if (del_hs20_icon(wpa_s, buf + 14) < 0)
 			reply_len = -1;
 	} else if (os_strcmp(buf, "FETCH_OSU") == 0) {
 		if (hs20_fetch_osu(wpa_s) < 0)
@@ -8425,6 +8858,9 @@
 	} else if (os_strcmp(buf, "SCAN_RESULTS") == 0) {
 		reply_len = wpa_supplicant_ctrl_iface_scan_results(
 			wpa_s, reply, reply_size);
+	} else if (os_strcmp(buf, "ABORT_SCAN") == 0) {
+		if (wpas_abort_ongoing_scan(wpa_s) < 0)
+			reply_len = -1;
 	} else if (os_strncmp(buf, "SELECT_NETWORK ", 15) == 0) {
 		if (wpa_supplicant_ctrl_iface_select_network(wpa_s, buf + 15))
 			reply_len = -1;
@@ -8447,7 +8883,8 @@
 		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))
+		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(
@@ -8482,9 +8919,9 @@
 	} 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) {
+	} else if (os_strncmp(buf, "INTERFACES", 10) == 0) {
 		reply_len = wpa_supplicant_global_iface_interfaces(
-			wpa_s->global, reply, reply_size);
+			wpa_s->global, buf + 10, 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);
@@ -8565,6 +9002,9 @@
 	} 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, "SIGNAL_MONITOR", 14) == 0) {
+		if (wpas_ctrl_iface_signal_monitor(wpa_s, buf + 14))
+			reply_len = -1;
 	} else if (os_strncmp(buf, "PKTCNT_POLL", 11) == 0) {
 		reply_len = wpa_supplicant_pktcnt_poll(wpa_s, reply,
 						       reply_size);
@@ -8623,9 +9063,17 @@
 			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);
 	} else if (os_strncmp(buf, "EVENT_TEST ", 11) == 0) {
 		if (wpas_ctrl_event_test(wpa_s, buf + 11) < 0)
 			reply_len = -1;
+	} else if (os_strncmp(buf, "TEST_ASSOC_IE ", 14) == 0) {
+		if (wpas_ctrl_test_assoc_ie(wpa_s, buf + 14) < 0)
+			reply_len = -1;
 #endif /* CONFIG_TESTING_OPTIONS */
 	} else if (os_strncmp(buf, "VENDOR_ELEM_ADD ", 16) == 0) {
 		if (wpas_ctrl_vendor_elem_add(wpa_s, buf + 16) < 0)
@@ -8644,6 +9092,9 @@
 	} 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;
@@ -8667,10 +9118,11 @@
 	struct wpa_supplicant *wpa_s;
 	unsigned int create_iface = 0;
 	u8 mac_addr[ETH_ALEN];
+	enum wpa_driver_if_type type = WPA_IF_STATION;
 
 	/*
 	 * <ifname>TAB<confname>TAB<driver>TAB<ctrl_interface>TAB<driver_param>
-	 * TAB<bridge_ifname>[TAB<create>]
+	 * TAB<bridge_ifname>[TAB<create>[TAB<interface_type>]]
 	 */
 	wpa_printf(MSG_DEBUG, "CTRL_IFACE GLOBAL INTERFACE_ADD '%s'", cmd);
 
@@ -8738,9 +9190,22 @@
 		if (!extra[0])
 			break;
 
-		if (os_strcmp(extra, "create") == 0)
+		if (os_strcmp(extra, "create") == 0) {
 			create_iface = 1;
-		else {
+			if (!pos)
+				break;
+
+			if (os_strcmp(pos, "sta") == 0) {
+				type = WPA_IF_STATION;
+			} else if (os_strcmp(pos, "ap") == 0) {
+				type = WPA_IF_AP_BSS;
+			} else {
+				wpa_printf(MSG_DEBUG,
+					   "INTERFACE_ADD unsupported interface type: '%s'",
+					   pos);
+				return -1;
+			}
+		} else {
 			wpa_printf(MSG_DEBUG,
 				   "INTERFACE_ADD unsupported extra parameter: '%s'",
 				   extra);
@@ -8753,7 +9218,7 @@
 			   iface.ifname);
 		if (!global->ifaces)
 			return -1;
-		if (wpa_drv_if_add(global->ifaces, WPA_IF_STATION, iface.ifname,
+		if (wpa_drv_if_add(global->ifaces, type, iface.ifname,
 				   NULL, NULL, NULL, mac_addr, NULL) < 0) {
 			wpa_printf(MSG_ERROR,
 				   "CTRL_IFACE interface creation failed");
@@ -8862,18 +9327,31 @@
 
 
 static int wpa_supplicant_global_iface_interfaces(struct wpa_global *global,
+						  const char *input,
 						  char *buf, int len)
 {
 	int res;
 	char *pos, *end;
 	struct wpa_supplicant *wpa_s;
+	int show_ctrl = 0;
+
+	if (input)
+		show_ctrl = !!os_strstr(input, "ctrl");
 
 	wpa_s = global->ifaces;
 	pos = buf;
 	end = buf + len;
 
 	while (wpa_s) {
-		res = os_snprintf(pos, end - pos, "%s\n", wpa_s->ifname);
+		if (show_ctrl)
+			res = os_snprintf(pos, end - pos, "%s ctrl_iface=%s\n",
+					  wpa_s->ifname,
+					  wpa_s->conf->ctrl_interface ?
+					  wpa_s->conf->ctrl_interface : "N/A");
+		else
+			res = os_snprintf(pos, end - pos, "%s\n",
+					  wpa_s->ifname);
+
 		if (os_snprintf_error(end - pos, res)) {
 			*pos = '\0';
 			break;
@@ -9045,6 +9523,41 @@
 }
 
 
+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)
 {
@@ -9126,6 +9639,59 @@
 }
 
 
+#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)
 {
@@ -9174,9 +9740,21 @@
 	} 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) {
+	} else if (os_strncmp(buf, "INTERFACES", 10) == 0) {
 		reply_len = wpa_supplicant_global_iface_interfaces(
-			global, reply, reply_size);
+			global, buf + 10, 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) {
@@ -9197,6 +9775,9 @@
 #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))
diff --git a/wpa_supplicant/ctrl_iface_udp.c b/wpa_supplicant/ctrl_iface_udp.c
index 76f69f2..0dc0937 100644
--- a/wpa_supplicant/ctrl_iface_udp.c
+++ b/wpa_supplicant/ctrl_iface_udp.c
@@ -1,6 +1,6 @@
 /*
  * WPA Supplicant / UDP socket -based control interface
- * Copyright (c) 2004-2005, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2016, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -48,13 +48,33 @@
 	u8 cookie[COOKIE_LEN];
 };
 
+struct ctrl_iface_global_priv {
+	int sock;
+	struct wpa_ctrl_dst *ctrl_dst;
+	u8 cookie[COOKIE_LEN];
+};
 
-static void wpa_supplicant_ctrl_iface_send(struct ctrl_iface_priv *priv,
+
+static void wpa_supplicant_ctrl_iface_send(struct wpa_supplicant *wpa_s,
+					   const char *ifname, int sock,
+					   struct wpa_ctrl_dst **head,
 					   int level, const char *buf,
 					   size_t len);
 
 
-static int wpa_supplicant_ctrl_iface_attach(struct ctrl_iface_priv *priv,
+static void wpas_ctrl_iface_free_dst(struct wpa_ctrl_dst *dst)
+{
+	struct wpa_ctrl_dst *prev;
+
+	while (dst) {
+		prev = dst;
+		dst = dst->next;
+		os_free(prev);
+	}
+}
+
+
+static int wpa_supplicant_ctrl_iface_attach(struct wpa_ctrl_dst **head,
 #ifdef CONFIG_CTRL_IFACE_UDP_IPV6
 					    struct sockaddr_in6 *from,
 #else /* CONFIG_CTRL_IFACE_UDP_IPV6 */
@@ -73,8 +93,8 @@
 	os_memcpy(&dst->addr, from, sizeof(*from));
 	dst->addrlen = fromlen;
 	dst->debug_level = MSG_INFO;
-	dst->next = priv->ctrl_dst;
-	priv->ctrl_dst = dst;
+	dst->next = *head;
+	*head = 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)),
@@ -87,7 +107,7 @@
 }
 
 
-static int wpa_supplicant_ctrl_iface_detach(struct ctrl_iface_priv *priv,
+static int wpa_supplicant_ctrl_iface_detach(struct wpa_ctrl_dst **head,
 #ifdef CONFIG_CTRL_IFACE_UDP_IPV6
 					    struct sockaddr_in6 *from,
 #else /* CONFIG_CTRL_IFACE_UDP_IPV6 */
@@ -100,7 +120,7 @@
 	char addr[INET6_ADDRSTRLEN];
 #endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */
 
-	dst = priv->ctrl_dst;
+	dst = *head;
 	while (dst) {
 #ifdef CONFIG_CTRL_IFACE_UDP_IPV6
 		if (from->sin6_port == dst->addr.sin6_port &&
@@ -118,7 +138,7 @@
 				   ntohs(from->sin_port));
 #endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */
 			if (prev == NULL)
-				priv->ctrl_dst = dst->next;
+				*head = dst->next;
 			else
 				prev->next = dst->next;
 			os_free(dst);
@@ -282,14 +302,16 @@
 		pos++;
 
 	if (os_strcmp(pos, "ATTACH") == 0) {
-		if (wpa_supplicant_ctrl_iface_attach(priv, &from, fromlen))
+		if (wpa_supplicant_ctrl_iface_attach(&priv->ctrl_dst,
+						     &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))
+		if (wpa_supplicant_ctrl_iface_detach(&priv->ctrl_dst,
+						     &from, fromlen))
 			reply_len = 1;
 		else
 			reply_len = 2;
@@ -327,9 +349,28 @@
 					     const char *txt, size_t len)
 {
 	struct wpa_supplicant *wpa_s = ctx;
-	if (wpa_s == NULL || wpa_s->ctrl_iface == NULL)
+
+	if (!wpa_s)
 		return;
-	wpa_supplicant_ctrl_iface_send(wpa_s->ctrl_iface, level, txt, len);
+
+	if (type != WPA_MSG_NO_GLOBAL && wpa_s->global->ctrl_iface) {
+		struct ctrl_iface_global_priv *priv = wpa_s->global->ctrl_iface;
+
+		if (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);
+		}
+	}
+
+	if (type == WPA_MSG_ONLY_GLOBAL || !wpa_s->ctrl_iface)
+		return;
+
+	wpa_supplicant_ctrl_iface_send(wpa_s, NULL, wpa_s->ctrl_iface->sock,
+				       &wpa_s->ctrl_iface->ctrl_dst,
+				       level, txt, len);
 }
 
 
@@ -337,7 +378,9 @@
 wpa_supplicant_ctrl_iface_init(struct wpa_supplicant *wpa_s)
 {
 	struct ctrl_iface_priv *priv;
+	char port_str[40];
 	int port = WPA_CTRL_IFACE_PORT;
+	char *pos;
 #ifdef CONFIG_CTRL_IFACE_UDP_IPV6
 	struct sockaddr_in6 addr;
 	int domain = PF_INET6;
@@ -356,6 +399,17 @@
 	if (wpa_s->conf->ctrl_interface == NULL)
 		return priv;
 
+	pos = os_strstr(wpa_s->conf->ctrl_interface, "udp:");
+	if (pos) {
+		pos += 4;
+		port = atoi(pos);
+		if (port <= 0) {
+			wpa_printf(MSG_ERROR, "Invalid ctrl_iface UDP port: %s",
+				   wpa_s->conf->ctrl_interface);
+			goto fail;
+		}
+	}
+
 	priv->sock = socket(domain, SOCK_DGRAM, 0);
 	if (priv->sock < 0) {
 		wpa_printf(MSG_ERROR, "socket(PF_INET): %s", strerror(errno));
@@ -392,6 +446,15 @@
 		goto fail;
 	}
 
+	/* Update the ctrl_interface value to match the selected port */
+	os_snprintf(port_str, sizeof(port_str), "udp:%d", port);
+	os_free(wpa_s->conf->ctrl_interface);
+	wpa_s->conf->ctrl_interface = os_strdup(port_str);
+	if (!wpa_s->conf->ctrl_interface) {
+		wpa_msg(wpa_s, MSG_ERROR, "Failed to malloc ctrl_interface");
+		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 */
@@ -412,8 +475,6 @@
 
 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) {
@@ -430,22 +491,19 @@
 		priv->sock = -1;
 	}
 
-	dst = priv->ctrl_dst;
-	while (dst) {
-		prev = dst;
-		dst = dst->next;
-		os_free(prev);
-	}
+	wpas_ctrl_iface_free_dst(priv->ctrl_dst);
 	os_free(priv);
 }
 
 
-static void wpa_supplicant_ctrl_iface_send(struct ctrl_iface_priv *priv,
+static void wpa_supplicant_ctrl_iface_send(struct wpa_supplicant *wpa_s,
+					   const char *ifname, int sock,
+					   struct wpa_ctrl_dst **head,
 					   int level, const char *buf,
 					   size_t len)
 {
 	struct wpa_ctrl_dst *dst, *next;
-	char levelstr[10];
+	char levelstr[64];
 	int idx;
 	char *sbuf;
 	int llen;
@@ -453,11 +511,15 @@
 	char addr[INET6_ADDRSTRLEN];
 #endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */
 
-	dst = priv->ctrl_dst;
-	if (priv->sock < 0 || dst == NULL)
+	dst = *head;
+	if (sock < 0 || dst == NULL)
 		return;
 
-	os_snprintf(levelstr, sizeof(levelstr), "<%d>", level);
+	if (ifname)
+		os_snprintf(levelstr, sizeof(levelstr), "IFACE=%s <%d>",
+			    ifname, level);
+	else
+		os_snprintf(levelstr, sizeof(levelstr), "<%d>", level);
 
 	llen = os_strlen(levelstr);
 	sbuf = os_malloc(llen + len);
@@ -481,7 +543,7 @@
 				   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,
+			if (sendto(sock, sbuf, llen + len, 0,
 				   (struct sockaddr *) &dst->addr,
 				   sizeof(dst->addr)) < 0) {
 				wpa_printf(MSG_ERROR,
@@ -490,7 +552,7 @@
 				dst->errors++;
 				if (dst->errors > 10) {
 					wpa_supplicant_ctrl_iface_detach(
-						priv, &dst->addr,
+						head, &dst->addr,
 						dst->addrlen);
 				}
 			} else
@@ -513,12 +575,6 @@
 
 /* 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)
@@ -546,9 +602,13 @@
 	struct ctrl_iface_global_priv *priv = sock_ctx;
 	char buf[256], *pos;
 	int res;
+#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 = sizeof(from);
-	char *reply;
+	char *reply = NULL;
 	size_t reply_len;
 	u8 cookie[COOKIE_LEN];
 
@@ -561,6 +621,7 @@
 	}
 
 #ifndef CONFIG_CTRL_IFACE_UDP_REMOTE
+#ifndef 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
@@ -572,6 +633,7 @@
 			   "source %s", inet_ntoa(from.sin_addr));
 		return;
 	}
+#endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */
 #endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */
 
 	buf[res] = '\0';
@@ -603,17 +665,34 @@
 	while (*pos == ' ')
 		pos++;
 
-	reply = wpa_supplicant_global_ctrl_iface_process(global, pos,
-							 &reply_len);
+	if (os_strcmp(pos, "ATTACH") == 0) {
+		if (wpa_supplicant_ctrl_iface_attach(&priv->ctrl_dst,
+						     &from, fromlen))
+			reply_len = 1;
+		else
+			reply_len = 2;
+	} else if (os_strcmp(pos, "DETACH") == 0) {
+		if (wpa_supplicant_ctrl_iface_detach(&priv->ctrl_dst,
+						     &from, fromlen))
+			reply_len = 1;
+		else
+			reply_len = 2;
+	} else {
+		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) {
+	} 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);
 	}
 }
 
@@ -623,6 +702,7 @@
 {
 	struct ctrl_iface_global_priv *priv;
 	struct sockaddr_in addr;
+	char *pos;
 	int port = WPA_GLOBAL_CTRL_IFACE_PORT;
 
 	priv = os_zalloc(sizeof(*priv));
@@ -637,6 +717,17 @@
 	wpa_printf(MSG_DEBUG, "Global control interface '%s'",
 		   global->params.ctrl_interface);
 
+	pos = os_strstr(global->params.ctrl_interface, "udp:");
+	if (pos) {
+		pos += 4;
+		port = atoi(pos);
+		if (port <= 0) {
+			wpa_printf(MSG_ERROR, "Invalid global ctrl UDP port %s",
+				   global->params.ctrl_interface);
+			goto fail;
+		}
+	}
+
 	priv->sock = socket(PF_INET, SOCK_DGRAM, 0);
 	if (priv->sock < 0) {
 		wpa_printf(MSG_ERROR, "socket(PF_INET): %s", strerror(errno));
@@ -655,7 +746,7 @@
 	if (bind(priv->sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
 		port++;
 		if ((port - WPA_GLOBAL_CTRL_IFACE_PORT) <
-		    WPA_GLOBAL_CTRL_IFACE_PORT_LIMIT)
+		    WPA_GLOBAL_CTRL_IFACE_PORT_LIMIT && !pos)
 			goto try_again;
 		wpa_printf(MSG_ERROR, "bind(AF_INET): %s", strerror(errno));
 		goto fail;
@@ -668,6 +759,7 @@
 	eloop_register_read_sock(priv->sock,
 				 wpa_supplicant_global_ctrl_iface_receive,
 				 global, priv);
+	wpa_msg_register_cb(wpa_supplicant_ctrl_iface_msg_cb);
 
 	return priv;
 
@@ -686,5 +778,7 @@
 		eloop_unregister_read_sock(priv->sock);
 		close(priv->sock);
 	}
+
+	wpas_ctrl_iface_free_dst(priv->ctrl_dst);
 	os_free(priv);
 }
diff --git a/wpa_supplicant/ctrl_iface_unix.c b/wpa_supplicant/ctrl_iface_unix.c
index 160a6f0..4db712f 100644
--- a/wpa_supplicant/ctrl_iface_unix.c
+++ b/wpa_supplicant/ctrl_iface_unix.c
@@ -15,7 +15,6 @@
 #include <fcntl.h>
 #ifdef __linux__
 #include <sys/ioctl.h>
-#include <linux/sockios.h>
 #endif /* __linux__ */
 #ifdef ANDROID
 #include <cutils/sockets.h>
@@ -24,6 +23,7 @@
 #include "utils/common.h"
 #include "utils/eloop.h"
 #include "utils/list.h"
+#include "common/ctrl_iface_common.h"
 #include "eapol_supp/eapol_supp_sm.h"
 #include "config.h"
 #include "wpa_supplicant_i.h"
@@ -31,22 +31,6 @@
 
 /* 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;
@@ -95,7 +79,7 @@
 #ifdef __linux__
 	socklen_t optlen;
 	int sndbuf, outq;
-	int level = MSG_DEBUG;
+	int level = MSG_MSGDUMP;
 
 	if (len >= 5 && os_strncmp(buf, "PONG\n", 5) == 0)
 		level = MSG_EXCESSIVE;
@@ -105,7 +89,7 @@
 	if (getsockopt(sock, SOL_SOCKET, SO_SNDBUF, &sndbuf, &optlen) < 0)
 		sndbuf = -1;
 
-	if (ioctl(sock, SIOCOUTQ, &outq) < 0)
+	if (ioctl(sock, TIOCOUTQ, &outq) < 0)
 		outq = -1;
 
 	wpa_printf(level,
@@ -116,81 +100,29 @@
 
 
 static int wpa_supplicant_ctrl_iface_attach(struct dl_list *ctrl_dst,
-					    struct sockaddr_un *from,
+					    struct sockaddr_storage *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;
+	return ctrl_iface_attach(ctrl_dst, from, fromlen);
 }
 
 
 static int wpa_supplicant_ctrl_iface_detach(struct dl_list *ctrl_dst,
-					    struct sockaddr_un *from,
+					    struct sockaddr_storage *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;
+	return ctrl_iface_detach(ctrl_dst, from, fromlen);
 }
 
 
 static int wpa_supplicant_ctrl_iface_level(struct ctrl_iface_priv *priv,
-					   struct sockaddr_un *from,
+					   struct sockaddr_storage *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;
+	return ctrl_iface_level(&priv->ctrl_dst, from, fromlen, level);
 }
 
 
@@ -201,7 +133,7 @@
 	struct ctrl_iface_priv *priv = sock_ctx;
 	char buf[4096];
 	int res;
-	struct sockaddr_un from;
+	struct sockaddr_storage from;
 	socklen_t fromlen = sizeof(from);
 	char *reply = NULL, *reply_buf = NULL;
 	size_t reply_len = 0;
@@ -356,7 +288,7 @@
 	optlen = sizeof(sndbuf);
 	sndbuf = 0;
 	if (getsockopt(sock, SOL_SOCKET, SO_SNDBUF, &sndbuf, &optlen) < 0 ||
-	    ioctl(sock, SIOCOUTQ, &outq) < 0 ||
+	    ioctl(sock, TIOCOUTQ, &outq) < 0 ||
 	    sndbuf <= 0 || outq < 0)
 		return 0;
 	return outq > sndbuf / 2;
@@ -774,6 +706,53 @@
 	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;
@@ -869,8 +848,10 @@
 
 free_dst:
 	dl_list_for_each_safe(dst, prev, &priv->ctrl_dst, struct wpa_ctrl_dst,
-			      list)
+			      list) {
+		dl_list_del(&dst->list);
 		os_free(dst);
+	}
 	dl_list_for_each_safe(msg, prev_msg, &priv->msg_queue,
 			      struct ctrl_iface_msg, list) {
 		dl_list_del(&msg->list);
@@ -946,32 +927,31 @@
 
 	dl_list_for_each_safe(dst, next, ctrl_dst, struct wpa_ctrl_dst, list) {
 		int _errno;
-		char addr_txt[200];
+		char 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_DEBUG, "CTRL_IFACE monitor sent successfully to %s",
-				   addr_txt);
+			sockaddr_print(MSG_MSGDUMP,
+				       "CTRL_IFACE monitor sent successfully to",
+				       &dst->addr, dst->addrlen);
 			dst->errors = 0;
 			continue;
 		}
 
 		_errno = errno;
-		wpa_printf(MSG_DEBUG, "CTRL_IFACE monitor[%s]: %d - %s",
-			   addr_txt, errno, strerror(errno));
+		os_snprintf(txt, sizeof(txt), "CTRL_IFACE monitor: %d (%s) for",
+			    _errno, strerror(_errno));
+		sockaddr_print(MSG_DEBUG, txt, &dst->addr, dst->addrlen);
 		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);
+			sockaddr_print(MSG_INFO, "CTRL_IFACE: Detach monitor that cannot receive messages:",
+				       &dst->addr, dst->addrlen);
 			wpa_supplicant_ctrl_iface_detach(ctrl_dst, &dst->addr,
 							 dst->addrlen);
 		}
@@ -1005,9 +985,12 @@
 {
 	char buf[256];
 	int res;
-	struct sockaddr_un from;
+	struct sockaddr_storage from;
 	socklen_t fromlen = sizeof(from);
 
+	if (priv->sock == -1)
+		return;
+
 	for (;;) {
 		wpa_printf(MSG_DEBUG, "CTRL_IFACE - %s - wait for monitor to "
 			   "attach", priv->wpa_s->ifname);
@@ -1065,7 +1048,7 @@
 	struct ctrl_iface_global_priv *priv = sock_ctx;
 	char buf[4096];
 	int res;
-	struct sockaddr_un from;
+	struct sockaddr_storage from;
 	socklen_t fromlen = sizeof(from);
 	char *reply = NULL, *reply_buf = NULL;
 	size_t reply_len;
@@ -1374,8 +1357,10 @@
 	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)
+			      list) {
+		dl_list_del(&dst->list);
 		os_free(dst);
+	}
 	dl_list_for_each_safe(msg, prev_msg, &priv->msg_queue,
 			      struct ctrl_iface_msg, list) {
 		dl_list_del(&msg->list);
diff --git a/wpa_supplicant/dbus/dbus-wpa_supplicant.conf b/wpa_supplicant/dbus/dbus-wpa_supplicant.conf
index c091234..382dcb3 100644
--- a/wpa_supplicant/dbus/dbus-wpa_supplicant.conf
+++ b/wpa_supplicant/dbus/dbus-wpa_supplicant.conf
@@ -17,11 +17,9 @@
         <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/wpa_supplicant/dbus/dbus_common_i.h b/wpa_supplicant/dbus/dbus_common_i.h
index a551ccd..95eb4bc 100644
--- a/wpa_supplicant/dbus/dbus_common_i.h
+++ b/wpa_supplicant/dbus/dbus_common_i.h
@@ -13,6 +13,8 @@
 
 #include <dbus/dbus.h>
 
+struct wpa_dbus_property_desc;
+
 struct wpas_dbus_priv {
 	DBusConnection *con;
 	int should_dispatch;
@@ -20,9 +22,13 @@
 	u32 next_objid;
 	int dbus_new_initialized;
 
-#if defined(CONFIG_CTRL_IFACE_DBUS_NEW) && defined(CONFIG_AP)
+#if defined(CONFIG_CTRL_IFACE_DBUS_NEW)
+	struct wpa_dbus_property_desc *all_interface_properties;
+	int globals_start;
+#if defined(CONFIG_AP)
 	int dbus_noc_refcnt;
-#endif /* CONFIG_CTRL_IFACE_DBUS_NEW && CONFIG_AP */
+#endif /* CONFIG_AP */
+#endif /* CONFIG_CTRL_IFACE_DBUS_NEW */
 };
 
 #endif /* DBUS_COMMON_I_H */
diff --git a/wpa_supplicant/dbus/dbus_dict_helpers.c b/wpa_supplicant/dbus/dbus_dict_helpers.c
index a0c44eb..e4e9b8d 100644
--- a/wpa_supplicant/dbus/dbus_dict_helpers.c
+++ b/wpa_supplicant/dbus/dbus_dict_helpers.c
@@ -205,24 +205,6 @@
 
 
 /**
- * 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
@@ -317,62 +299,6 @@
 
 
 /**
- * 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
diff --git a/wpa_supplicant/dbus/dbus_dict_helpers.h b/wpa_supplicant/dbus/dbus_dict_helpers.h
index b068431..94a0efd 100644
--- a/wpa_supplicant/dbus/dbus_dict_helpers.h
+++ b/wpa_supplicant/dbus/dbus_dict_helpers.h
@@ -26,9 +26,6 @@
 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);
@@ -49,18 +46,6 @@
 					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);
diff --git a/wpa_supplicant/dbus/dbus_new.c b/wpa_supplicant/dbus/dbus_new.c
index 1959ea7..d894f6a 100644
--- a/wpa_supplicant/dbus/dbus_new.c
+++ b/wpa_supplicant/dbus/dbus_new.c
@@ -633,6 +633,10 @@
 	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
@@ -1203,7 +1207,7 @@
 					 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);
+			data->wpa_s->p2pdev, data->info->p2p_device_addr);
 		return 0;
 	}
 
@@ -1220,7 +1224,7 @@
 	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,
+		wpas_dbus_signal_peer_groups_changed(data->wpa_s->p2pdev,
 						     info->p2p_device_addr);
 		return;
 	}
@@ -1857,6 +1861,99 @@
 	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);
+	dbus_message_unref(msg);
+}
+
+
 #endif /* CONFIG_P2P */
 
 
@@ -1904,6 +2001,10 @@
 		prop = "DisconnectReason";
 		flush = TRUE;
 		break;
+	case WPAS_DBUS_PROP_ASSOC_STATUS_CODE:
+		prop = "AssocStatusCode";
+		flush = TRUE;
+		break;
 	default:
 		wpa_printf(MSG_ERROR, "dbus: %s: Unknown Property value %d",
 			   __func__, property);
@@ -2076,41 +2177,54 @@
 		  END_ARGS
 	  }
 	},
+	{ "ExpectDisconnect", WPAS_DBUS_NEW_INTERFACE,
+	  (WPADBusMethodHandler) wpas_dbus_handler_expect_disconnect,
+	  {
+		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
+	  wpas_dbus_setter_debug_level,
+	  NULL
 	},
 	{ "DebugTimestamp", WPAS_DBUS_NEW_INTERFACE, "b",
 	  wpas_dbus_getter_debug_timestamp,
-	  wpas_dbus_setter_debug_timestamp
+	  wpas_dbus_setter_debug_timestamp,
+	  NULL
 	},
 	{ "DebugShowKeys", WPAS_DBUS_NEW_INTERFACE, "b",
 	  wpas_dbus_getter_debug_show_keys,
-	  wpas_dbus_setter_debug_show_keys
+	  wpas_dbus_setter_debug_show_keys,
+	  NULL
 	},
 	{ "Interfaces", WPAS_DBUS_NEW_INTERFACE, "ao",
 	  wpas_dbus_getter_interfaces,
+	  NULL,
 	  NULL
 	},
 	{ "EapMethods", WPAS_DBUS_NEW_INTERFACE, "as",
 	  wpas_dbus_getter_eap_methods,
+	  NULL,
 	  NULL
 	},
 	{ "Capabilities", WPAS_DBUS_NEW_INTERFACE, "as",
 	  wpas_dbus_getter_global_capabilities,
+	  NULL,
 	  NULL
 	},
 #ifdef CONFIG_WIFI_DISPLAY
 	{ "WFDIEs", WPAS_DBUS_NEW_INTERFACE, "ay",
 	  wpas_dbus_getter_global_wfd_ies,
-	  wpas_dbus_setter_global_wfd_ies
+	  wpas_dbus_setter_global_wfd_ies,
+	  NULL
 	},
 #endif /* CONFIG_WIFI_DISPLAY */
-	{ NULL, NULL, NULL, NULL, NULL }
+	{ NULL, NULL, NULL, NULL, NULL, NULL }
 };
 
 static const struct wpa_dbus_signal_desc wpas_dbus_global_signals[] = {
@@ -2138,12 +2252,50 @@
 };
 
 
+static char * uscore_to_dbus(const char *uscore)
+{
+	const char *p = uscore;
+	char *str, *s;
+	dbus_bool_t last_was_uscore = TRUE;
+
+	s = str = os_zalloc(os_strlen(uscore) + 1);
+	if (!str)
+		return NULL;
+	while (p && *p) {
+		if (*p == '_') {
+			last_was_uscore = TRUE;
+		} else {
+			*s++ = last_was_uscore ? toupper(*p) : *p;
+			last_was_uscore = FALSE;
+		}
+		p++;
+	}
+
+	return str;
+}
+
+
+static int wpa_dbus_ctrl_iface_props_init(struct wpas_dbus_priv *priv);
+
+
+static void wpa_dbus_ctrl_iface_props_deinit(struct wpas_dbus_priv *priv)
+{
+	int idx = priv->globals_start;
+
+	/* Free all allocated property values */
+	while (priv->all_interface_properties[idx].dbus_property)
+		os_free((char *)
+			priv->all_interface_properties[idx++].dbus_property);
+	os_free((char *) priv->all_interface_properties);
+}
+
+
 /**
  * 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
+ * Initialize the dbus control interface for wpa_supplicant and start
  * receiving commands from external programs over the bus.
  */
 int wpas_dbus_ctrl_iface_init(struct wpas_dbus_priv *priv)
@@ -2151,11 +2303,18 @@
 	struct wpa_dbus_object_desc *obj_desc;
 	int ret;
 
+	ret = wpa_dbus_ctrl_iface_props_init(priv);
+	if (ret < 0) {
+		wpa_printf(MSG_ERROR,
+			   "dbus: Not enough memory to init interface properties");
+		return -1;
+	}
+
 	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;
+		goto error;
 	}
 
 	wpas_dbus_register(obj_desc, priv->global, NULL,
@@ -2168,31 +2327,36 @@
 	ret = wpa_dbus_ctrl_iface_init(priv, WPAS_DBUS_NEW_PATH,
 				       WPAS_DBUS_NEW_SERVICE,
 				       obj_desc);
-	if (ret < 0)
+	if (ret < 0) {
 		free_dbus_object_desc(obj_desc);
-	else
-		priv->dbus_new_initialized = 1;
+		goto error;
+	}
 
-	return ret;
+	priv->dbus_new_initialized = 1;
+	return 0;
+
+error:
+	wpa_dbus_ctrl_iface_props_deinit(priv);
+	return -1;
 }
 
 
 /**
  * wpas_dbus_ctrl_iface_deinit - Deinitialize dbus ctrl interface for
  * wpa_supplicant
- * @iface: Pointer to dbus private data from wpas_dbus_init()
+ * @priv: 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)
+void wpas_dbus_ctrl_iface_deinit(struct wpas_dbus_priv *priv)
 {
-	if (!iface->dbus_new_initialized)
+	if (!priv->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);
+	dbus_connection_unregister_object_path(priv->con, WPAS_DBUS_NEW_PATH);
+	wpa_dbus_ctrl_iface_props_deinit(priv);
 }
 
 
@@ -2205,13 +2369,15 @@
 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
+	  wpas_dbus_setter_network_properties,
+	  NULL
 	},
 	{ "Enabled", WPAS_DBUS_NEW_IFACE_NETWORK, "b",
 	  wpas_dbus_getter_enabled,
-	  wpas_dbus_setter_enabled
+	  wpas_dbus_setter_enabled,
+	  NULL
 	},
-	{ NULL, NULL, NULL, NULL, NULL }
+	{ NULL, NULL, NULL, NULL, NULL, NULL }
 };
 
 
@@ -2350,53 +2516,65 @@
 static const struct wpa_dbus_property_desc wpas_dbus_bss_properties[] = {
 	{ "SSID", WPAS_DBUS_NEW_IFACE_BSS, "ay",
 	  wpas_dbus_getter_bss_ssid,
+	  NULL,
 	  NULL
 	},
 	{ "BSSID", WPAS_DBUS_NEW_IFACE_BSS, "ay",
 	  wpas_dbus_getter_bss_bssid,
+	  NULL,
 	  NULL
 	},
 	{ "Privacy", WPAS_DBUS_NEW_IFACE_BSS, "b",
 	  wpas_dbus_getter_bss_privacy,
+	  NULL,
 	  NULL
 	},
 	{ "Mode", WPAS_DBUS_NEW_IFACE_BSS, "s",
 	  wpas_dbus_getter_bss_mode,
+	  NULL,
 	  NULL
 	},
 	{ "Signal", WPAS_DBUS_NEW_IFACE_BSS, "n",
 	  wpas_dbus_getter_bss_signal,
+	  NULL,
 	  NULL
 	},
 	{ "Frequency", WPAS_DBUS_NEW_IFACE_BSS, "q",
 	  wpas_dbus_getter_bss_frequency,
+	  NULL,
 	  NULL
 	},
 	{ "Rates", WPAS_DBUS_NEW_IFACE_BSS, "au",
 	  wpas_dbus_getter_bss_rates,
+	  NULL,
 	  NULL
 	},
 	{ "WPA", WPAS_DBUS_NEW_IFACE_BSS, "a{sv}",
 	  wpas_dbus_getter_bss_wpa,
+	  NULL,
 	  NULL
 	},
 	{ "RSN", WPAS_DBUS_NEW_IFACE_BSS, "a{sv}",
 	  wpas_dbus_getter_bss_rsn,
+	  NULL,
 	  NULL
 	},
 	{ "WPS", WPAS_DBUS_NEW_IFACE_BSS, "a{sv}",
 	  wpas_dbus_getter_bss_wps,
+	  NULL,
 	  NULL
 	},
 	{ "IEs", WPAS_DBUS_NEW_IFACE_BSS, "ay",
 	  wpas_dbus_getter_bss_ies,
+	  NULL,
 	  NULL
 	},
 	{ "Age", WPAS_DBUS_NEW_IFACE_BSS, "u",
 	  wpas_dbus_getter_bss_age,
+	  NULL,
 	  NULL
 	},
-	{ NULL, NULL, NULL, NULL, NULL }
+	{ NULL, NULL, NULL, NULL, NULL, NULL }
 };
 
 
@@ -2896,131 +3074,197 @@
 	  }
 	},
 #endif /* CONFIG_TDLS */
+	{ "VendorElemAdd", WPAS_DBUS_NEW_IFACE_INTERFACE,
+	  (WPADBusMethodHandler) wpas_dbus_handler_vendor_elem_add,
+	  {
+		  { "frame_id", "i", ARG_IN },
+		  { "ielems", "ay", ARG_IN },
+		  END_ARGS
+	  }
+	},
+	{ "VendorElemGet", WPAS_DBUS_NEW_IFACE_INTERFACE,
+	  (WPADBusMethodHandler) wpas_dbus_handler_vendor_elem_get,
+	  {
+		  { "frame_id", "i", ARG_IN },
+		  { "ielems", "ay", ARG_OUT },
+		  END_ARGS
+	  }
+	},
+	{ "VendorElemRem", WPAS_DBUS_NEW_IFACE_INTERFACE,
+	  (WPADBusMethodHandler) wpas_dbus_handler_vendor_elem_remove,
+	  {
+		  { "frame_id", "i", ARG_IN },
+		  { "ielems", "ay", ARG_IN },
+		  END_ARGS
+	  }
+	},
+#ifndef CONFIG_NO_CONFIG_WRITE
+	{ "SaveConfig", WPAS_DBUS_NEW_IFACE_INTERFACE,
+	  (WPADBusMethodHandler) wpas_dbus_handler_save_config,
+	  {
+		  END_ARGS
+	  }
+	},
+#endif /* CONFIG_NO_CONFIG_WRITE */
 	{ 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,
 	  NULL
 	},
 	{ "State", WPAS_DBUS_NEW_IFACE_INTERFACE, "s",
 	  wpas_dbus_getter_state,
+	  NULL,
 	  NULL
 	},
 	{ "Scanning", WPAS_DBUS_NEW_IFACE_INTERFACE, "b",
 	  wpas_dbus_getter_scanning,
+	  NULL,
 	  NULL
 	},
 	{ "ApScan", WPAS_DBUS_NEW_IFACE_INTERFACE, "u",
 	  wpas_dbus_getter_ap_scan,
-	  wpas_dbus_setter_ap_scan
+	  wpas_dbus_setter_ap_scan,
+	  NULL
 	},
 	{ "BSSExpireAge", WPAS_DBUS_NEW_IFACE_INTERFACE, "u",
 	  wpas_dbus_getter_bss_expire_age,
-	  wpas_dbus_setter_bss_expire_age
+	  wpas_dbus_setter_bss_expire_age,
+	  NULL
 	},
 	{ "BSSExpireCount", WPAS_DBUS_NEW_IFACE_INTERFACE, "u",
 	  wpas_dbus_getter_bss_expire_count,
-	  wpas_dbus_setter_bss_expire_count
+	  wpas_dbus_setter_bss_expire_count,
+	  NULL
 	},
 	{ "Country", WPAS_DBUS_NEW_IFACE_INTERFACE, "s",
 	  wpas_dbus_getter_country,
-	  wpas_dbus_setter_country
+	  wpas_dbus_setter_country,
+	  NULL
 	},
 	{ "Ifname", WPAS_DBUS_NEW_IFACE_INTERFACE, "s",
 	  wpas_dbus_getter_ifname,
+	  NULL,
 	  NULL
 	},
 	{ "Driver", WPAS_DBUS_NEW_IFACE_INTERFACE, "s",
 	  wpas_dbus_getter_driver,
+	  NULL,
 	  NULL
 	},
 	{ "BridgeIfname", WPAS_DBUS_NEW_IFACE_INTERFACE, "s",
 	  wpas_dbus_getter_bridge_ifname,
+	  NULL,
 	  NULL
 	},
 	{ "CurrentBSS", WPAS_DBUS_NEW_IFACE_INTERFACE, "o",
 	  wpas_dbus_getter_current_bss,
+	  NULL,
 	  NULL
 	},
 	{ "CurrentNetwork", WPAS_DBUS_NEW_IFACE_INTERFACE, "o",
 	  wpas_dbus_getter_current_network,
+	  NULL,
 	  NULL
 	},
 	{ "CurrentAuthMode", WPAS_DBUS_NEW_IFACE_INTERFACE, "s",
 	  wpas_dbus_getter_current_auth_mode,
+	  NULL,
 	  NULL
 	},
 	{ "Blobs", WPAS_DBUS_NEW_IFACE_INTERFACE, "a{say}",
 	  wpas_dbus_getter_blobs,
+	  NULL,
 	  NULL
 	},
 	{ "BSSs", WPAS_DBUS_NEW_IFACE_INTERFACE, "ao",
 	  wpas_dbus_getter_bsss,
+	  NULL,
 	  NULL
 	},
 	{ "Networks", WPAS_DBUS_NEW_IFACE_INTERFACE, "ao",
 	  wpas_dbus_getter_networks,
+	  NULL,
 	  NULL
 	},
 	{ "FastReauth", WPAS_DBUS_NEW_IFACE_INTERFACE, "b",
 	  wpas_dbus_getter_fast_reauth,
-	  wpas_dbus_setter_fast_reauth
+	  wpas_dbus_setter_fast_reauth,
+	  NULL
 	},
 	{ "ScanInterval", WPAS_DBUS_NEW_IFACE_INTERFACE, "i",
 	  wpas_dbus_getter_scan_interval,
-	  wpas_dbus_setter_scan_interval
+	  wpas_dbus_setter_scan_interval,
+	  NULL
 	},
 	{ "PKCS11EnginePath", WPAS_DBUS_NEW_IFACE_INTERFACE, "s",
 	  wpas_dbus_getter_pkcs11_engine_path,
+	  NULL,
 	  NULL
 	},
 	{ "PKCS11ModulePath", WPAS_DBUS_NEW_IFACE_INTERFACE, "s",
 	  wpas_dbus_getter_pkcs11_module_path,
+	  NULL,
 	  NULL
 	},
 #ifdef CONFIG_WPS
 	{ "ProcessCredentials", WPAS_DBUS_NEW_IFACE_WPS, "b",
 	  wpas_dbus_getter_process_credentials,
-	  wpas_dbus_setter_process_credentials
+	  wpas_dbus_setter_process_credentials,
+	  NULL
 	},
 	{ "ConfigMethods", WPAS_DBUS_NEW_IFACE_WPS, "s",
 	  wpas_dbus_getter_config_methods,
-	  wpas_dbus_setter_config_methods
+	  wpas_dbus_setter_config_methods,
+	  NULL
 	},
 #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
+	  wpas_dbus_setter_p2p_device_config,
+	  NULL
 	},
 	{ "Peers", WPAS_DBUS_NEW_IFACE_P2PDEVICE, "ao",
 	  wpas_dbus_getter_p2p_peers,
+	  NULL,
 	  NULL
 	},
 	{ "Role", WPAS_DBUS_NEW_IFACE_P2PDEVICE, "s",
 	  wpas_dbus_getter_p2p_role,
+	  NULL,
 	  NULL
 	},
 	{ "Group", WPAS_DBUS_NEW_IFACE_P2PDEVICE, "o",
 	  wpas_dbus_getter_p2p_group,
+	  NULL,
 	  NULL
 	},
 	{ "PeerGO", WPAS_DBUS_NEW_IFACE_P2PDEVICE, "o",
 	  wpas_dbus_getter_p2p_peergo,
+	  NULL,
 	  NULL
 	},
 	{ "PersistentGroups", WPAS_DBUS_NEW_IFACE_P2PDEVICE, "ao",
 	  wpas_dbus_getter_persistent_groups,
+	  NULL,
 	  NULL
 	},
 #endif /* CONFIG_P2P */
 	{ "DisconnectReason", WPAS_DBUS_NEW_IFACE_INTERFACE, "i",
 	  wpas_dbus_getter_disconnect_reason,
+	  NULL,
 	  NULL
 	},
-	{ NULL, NULL, NULL, NULL, NULL }
+	{ "AssocStatusCode", WPAS_DBUS_NEW_IFACE_INTERFACE, "i",
+	  wpas_dbus_getter_assoc_status_code,
+	  NULL,
+	  NULL
+	},
+	{ NULL, NULL, NULL, NULL, NULL, NULL }
 };
 
 static const struct wpa_dbus_signal_desc wpas_dbus_interface_signals[] = {
@@ -3172,6 +3416,12 @@
 		  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 },
@@ -3187,7 +3437,7 @@
 	{ "GONegotiationRequest", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
 	  {
 		  { "path", "o", ARG_OUT },
-		  { "dev_passwd_id", "i", ARG_OUT },
+		  { "dev_passwd_id", "q", ARG_OUT },
 		  { "device_go_intent", "y", ARG_OUT },
 		  END_ARGS
 	  }
@@ -3236,6 +3486,12 @@
 		  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,
@@ -3282,6 +3538,77 @@
 };
 
 
+static int wpa_dbus_ctrl_iface_props_init(struct wpas_dbus_priv *priv)
+{
+	size_t all_size;
+	unsigned int i, j, count, num_const, num_globals;
+	const char *global_name;
+	static const char * const ignored_globals[] = {
+		"bss_expiration_age", "bss_expiration_scan_count",
+		"ap_scan", "country", "fast_reauth",
+		"pkcs11_engine_path", "pkcs11_module_path"
+	};
+
+	/* wpas_dbus_interface_properties terminates with a NULL element */
+	num_const = ARRAY_SIZE(wpas_dbus_interface_properties) - 1;
+
+	num_globals = wpa_config_get_num_global_field_names();
+	priv->globals_start = num_const;
+
+	/* allocate enough for all properties + terminating NULL element */
+	all_size = (num_globals + num_const + 1) *
+		sizeof(wpas_dbus_interface_properties[0]);
+	priv->all_interface_properties = os_zalloc(all_size);
+	if (!priv->all_interface_properties) {
+		wpa_printf(MSG_ERROR,
+			   "dbus: Not enough memory for interface properties");
+		return -1;
+	}
+
+	/* Copy constant interface properties to the start of the array */
+	os_memcpy(priv->all_interface_properties,
+		  wpas_dbus_interface_properties,
+		  sizeof(wpas_dbus_interface_properties));
+
+	/* Dynamically construct interface global properties */
+	for (i = 0, count = num_const; i < num_globals; i++) {
+		struct wpa_dbus_property_desc *desc;
+		int no_var = 0;
+
+		/* ignore globals that are actually just methods */
+		global_name = wpa_config_get_global_field_name(i, &no_var);
+		if (no_var)
+			continue;
+		/* Ignore fields already explicitly exposed */
+		for (j = 0; j < ARRAY_SIZE(ignored_globals); j++) {
+			if (os_strcmp(global_name, ignored_globals[j]) == 0)
+				break;
+		}
+		if (j < ARRAY_SIZE(ignored_globals))
+			continue;
+
+		desc = &priv->all_interface_properties[count++];
+		desc->dbus_property = uscore_to_dbus(global_name);
+		if (!desc->dbus_property) {
+			wpa_printf(MSG_ERROR,
+				   "dbus: Not enough memory for D-Bus property name");
+			goto error;
+		}
+		desc->dbus_interface = WPAS_DBUS_NEW_IFACE_INTERFACE;
+		desc->type = "s";
+		desc->getter = wpas_dbus_getter_iface_global;
+		desc->setter = wpas_dbus_setter_iface_global;
+		desc->data = global_name;
+	}
+
+	return 0;
+
+error:
+	wpa_dbus_ctrl_iface_props_deinit(priv);
+	return -1;
+}
+
+
 /**
  * wpas_dbus_register_interface - Register an interface with D-Bus
  * @wpa_s: wpa_supplicant interface structure
@@ -3289,7 +3616,6 @@
  */
 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;
@@ -3315,7 +3641,7 @@
 	}
 
 	wpas_dbus_register(obj_desc, wpa_s, NULL, wpas_dbus_interface_methods,
-			   wpas_dbus_interface_properties,
+			   ctrl_iface->all_interface_properties,
 			   wpas_dbus_interface_signals);
 
 	wpa_printf(MSG_DEBUG, "dbus: Register interface object '%s'",
@@ -3381,65 +3707,80 @@
 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,
 	  NULL
 	},
 	{ "Manufacturer", WPAS_DBUS_NEW_IFACE_P2P_PEER, "s",
 	  wpas_dbus_getter_p2p_peer_manufacturer,
+	  NULL,
 	  NULL
 	},
 	{ "ModelName", WPAS_DBUS_NEW_IFACE_P2P_PEER, "s",
 	  wpas_dbus_getter_p2p_peer_modelname,
+	  NULL,
 	  NULL
 	},
 	{ "ModelNumber", WPAS_DBUS_NEW_IFACE_P2P_PEER, "s",
 	  wpas_dbus_getter_p2p_peer_modelnumber,
+	  NULL,
 	  NULL
 	},
 	{ "SerialNumber", WPAS_DBUS_NEW_IFACE_P2P_PEER, "s",
 	  wpas_dbus_getter_p2p_peer_serialnumber,
+	  NULL,
 	  NULL
 	},
 	{ "PrimaryDeviceType", WPAS_DBUS_NEW_IFACE_P2P_PEER, "ay",
 	  wpas_dbus_getter_p2p_peer_primary_device_type,
+	  NULL,
 	  NULL
 	},
 	{ "config_method", WPAS_DBUS_NEW_IFACE_P2P_PEER, "q",
 	  wpas_dbus_getter_p2p_peer_config_method,
+	  NULL,
 	  NULL
 	},
 	{ "level", WPAS_DBUS_NEW_IFACE_P2P_PEER, "i",
 	  wpas_dbus_getter_p2p_peer_level,
+	  NULL,
 	  NULL
 	},
 	{ "devicecapability", WPAS_DBUS_NEW_IFACE_P2P_PEER, "y",
 	  wpas_dbus_getter_p2p_peer_device_capability,
+	  NULL,
 	  NULL
 	},
 	{ "groupcapability", WPAS_DBUS_NEW_IFACE_P2P_PEER, "y",
 	  wpas_dbus_getter_p2p_peer_group_capability,
+	  NULL,
 	  NULL
 	},
 	{ "SecondaryDeviceTypes", WPAS_DBUS_NEW_IFACE_P2P_PEER, "aay",
 	  wpas_dbus_getter_p2p_peer_secondary_device_types,
+	  NULL,
 	  NULL
 	},
 	{ "VendorExtension", WPAS_DBUS_NEW_IFACE_P2P_PEER, "aay",
 	  wpas_dbus_getter_p2p_peer_vendor_extension,
+	  NULL,
 	  NULL
 	},
 	{ "IEs", WPAS_DBUS_NEW_IFACE_P2P_PEER, "ay",
 	  wpas_dbus_getter_p2p_peer_ies,
+	  NULL,
 	  NULL
 	},
 	{ "DeviceAddress", WPAS_DBUS_NEW_IFACE_P2P_PEER, "ay",
 	  wpas_dbus_getter_p2p_peer_device_address,
+	  NULL,
 	  NULL
 	},
 	{ "Groups", WPAS_DBUS_NEW_IFACE_P2P_PEER, "ao",
 	  wpas_dbus_getter_p2p_peer_groups,
+	  NULL,
 	  NULL
 	},
-	{ NULL, NULL, NULL, NULL, NULL }
+	{ NULL, NULL, NULL, NULL, NULL, NULL }
 };
 
 static const struct wpa_dbus_signal_desc wpas_dbus_p2p_peer_signals[] = {
@@ -3697,41 +4038,50 @@
 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,
 	  NULL
 	},
 	{ "Group", WPAS_DBUS_NEW_IFACE_P2P_GROUP, "o",
 	  wpas_dbus_getter_p2p_group,
+	  NULL,
 	  NULL
 	},
 	{ "Role", WPAS_DBUS_NEW_IFACE_P2P_GROUP, "s",
 	  wpas_dbus_getter_p2p_role,
+	  NULL,
 	  NULL
 	},
 	{ "SSID", WPAS_DBUS_NEW_IFACE_P2P_GROUP, "ay",
 	  wpas_dbus_getter_p2p_group_ssid,
+	  NULL,
 	  NULL
 	},
 	{ "BSSID", WPAS_DBUS_NEW_IFACE_P2P_GROUP, "ay",
 	  wpas_dbus_getter_p2p_group_bssid,
+	  NULL,
 	  NULL
 	},
 	{ "Frequency", WPAS_DBUS_NEW_IFACE_P2P_GROUP, "q",
 	  wpas_dbus_getter_p2p_group_frequency,
+	  NULL,
 	  NULL
 	},
 	{ "Passphrase", WPAS_DBUS_NEW_IFACE_P2P_GROUP, "s",
 	  wpas_dbus_getter_p2p_group_passphrase,
+	  NULL,
 	  NULL
 	},
 	{ "PSK", WPAS_DBUS_NEW_IFACE_P2P_GROUP, "ay",
 	  wpas_dbus_getter_p2p_group_psk,
+	  NULL,
 	  NULL
 	},
 	{ "WPSVendorExtensions", WPAS_DBUS_NEW_IFACE_P2P_GROUP, "aay",
 	  wpas_dbus_getter_p2p_group_vendor_ext,
-	  wpas_dbus_setter_p2p_group_vendor_ext
+	  wpas_dbus_setter_p2p_group_vendor_ext,
+	  NULL
 	},
-	{ NULL, NULL, NULL, NULL, NULL }
+	{ NULL, NULL, NULL, NULL, NULL, NULL }
 };
 
 static const struct wpa_dbus_signal_desc wpas_dbus_p2p_group_signals[] = {
@@ -3858,9 +4208,10 @@
 	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
+	  wpas_dbus_setter_persistent_group_properties,
+	  NULL
 	},
-	{ NULL, NULL, NULL, NULL, NULL }
+	{ NULL, NULL, NULL, NULL, NULL, NULL }
 };
 
 /* No signals intended for persistent group objects */
diff --git a/wpa_supplicant/dbus/dbus_new.h b/wpa_supplicant/dbus/dbus_new.h
index 7503348..3ac66db 100644
--- a/wpa_supplicant/dbus/dbus_new.h
+++ b/wpa_supplicant/dbus/dbus_new.h
@@ -29,6 +29,7 @@
 	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 {
@@ -191,6 +192,8 @@
 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,
@@ -231,6 +234,10 @@
 				     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 */
 
@@ -400,6 +407,12 @@
 }
 
 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)
 {
@@ -532,6 +545,14 @@
 {
 }
 
+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/wpa_supplicant/dbus/dbus_new_handlers.c b/wpa_supplicant/dbus/dbus_new_handlers.c
index 1c04e92..da90ea1 100644
--- a/wpa_supplicant/dbus/dbus_new_handlers.c
+++ b/wpa_supplicant/dbus/dbus_new_handlers.c
@@ -435,7 +435,8 @@
 
 	for (i = 0; i < array_len; i++) {
 		if (!dbus_message_iter_append_basic(&array_iter, type,
-						    array + i * element_size)) {
+						    (const char *) array +
+						    i * element_size)) {
 			dbus_set_error(error, DBUS_ERROR_FAILED,
 				       "%s: failed to construct message 2.5",
 				       __func__);
@@ -711,9 +712,9 @@
  *
  * Getter for "DebugLevel" property.
  */
-dbus_bool_t wpas_dbus_getter_debug_level(DBusMessageIter *iter,
-					 DBusError *error,
-					 void *user_data)
+dbus_bool_t wpas_dbus_getter_debug_level(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
 {
 	const char *str;
 	int idx = wpa_debug_level;
@@ -737,9 +738,9 @@
  *
  * Getter for "DebugTimestamp" property.
  */
-dbus_bool_t wpas_dbus_getter_debug_timestamp(DBusMessageIter *iter,
-					     DBusError *error,
-					     void *user_data)
+dbus_bool_t wpas_dbus_getter_debug_timestamp(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
 {
 	return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_BOOLEAN,
 						&wpa_debug_timestamp, error);
@@ -756,9 +757,9 @@
  *
  * Getter for "DebugShowKeys" property.
  */
-dbus_bool_t wpas_dbus_getter_debug_show_keys(DBusMessageIter *iter,
-					     DBusError *error,
-					     void *user_data)
+dbus_bool_t wpas_dbus_getter_debug_show_keys(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
 {
 	return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_BOOLEAN,
 						&wpa_debug_show_keys, error);
@@ -774,8 +775,9 @@
  *
  * Setter for "DebugLevel" property.
  */
-dbus_bool_t wpas_dbus_setter_debug_level(DBusMessageIter *iter,
-					 DBusError *error, void *user_data)
+dbus_bool_t wpas_dbus_setter_debug_level(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
 {
 	struct wpa_global *global = user_data;
 	const char *str = NULL;
@@ -812,9 +814,9 @@
  *
  * Setter for "DebugTimestamp" property.
  */
-dbus_bool_t wpas_dbus_setter_debug_timestamp(DBusMessageIter *iter,
-					     DBusError *error,
-					     void *user_data)
+dbus_bool_t wpas_dbus_setter_debug_timestamp(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
 {
 	struct wpa_global *global = user_data;
 	dbus_bool_t val;
@@ -838,9 +840,9 @@
  *
  * Setter for "DebugShowKeys" property.
  */
-dbus_bool_t wpas_dbus_setter_debug_show_keys(DBusMessageIter *iter,
-					     DBusError *error,
-					     void *user_data)
+dbus_bool_t wpas_dbus_setter_debug_show_keys(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
 {
 	struct wpa_global *global = user_data;
 	dbus_bool_t val;
@@ -867,9 +869,9 @@
  * 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)
+dbus_bool_t wpas_dbus_getter_interfaces(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
 {
 	struct wpa_global *global = user_data;
 	struct wpa_supplicant *wpa_s;
@@ -912,8 +914,9 @@
  * 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)
+dbus_bool_t wpas_dbus_getter_eap_methods(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
 {
 	char **eap_methods;
 	size_t num_items = 0;
@@ -948,9 +951,9 @@
  * 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)
+dbus_bool_t wpas_dbus_getter_global_capabilities(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
 {
 	const char *capabilities[5] = { NULL, NULL, NULL, NULL, NULL };
 	size_t num_items = 0;
@@ -1341,6 +1344,7 @@
 			}
 
 			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(
@@ -1367,6 +1371,7 @@
 			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");
@@ -1578,6 +1583,27 @@
 
 
 /**
+ * wpas_dbus_handler_expect_disconnect - ExpectDisconnect
+ * @message: Pointer to incoming dbus message
+ * @global: %wpa_supplicant global data structure
+ * Returns: NULL
+ *
+ * Handler function for notifying system there will be a expected disconnect.
+ * This will prevent wpa_supplicant from adding blacklists upon next disconnect..
+ */
+DBusMessage * wpas_dbus_handler_expect_disconnect(DBusMessage *message,
+						  struct wpa_global *global)
+{
+	struct wpa_supplicant *wpa_s = global->ifaces;
+
+	for (; wpa_s; wpa_s = wpa_s->next)
+		if (wpa_s->wpa_state >= WPA_ASSOCIATED)
+			wpa_s->own_disconnect_req = 1;
+	return NULL;
+}
+
+
+/**
  * wpas_dbus_handler_reattach - Reattach to current AP
  * @message: Pointer to incoming dbus message
  * @wpa_s: wpa_supplicant structure for a network interface
@@ -1852,7 +1878,7 @@
 	os_free(iface);
 	return reply;
 #else /* IEEE8021X_EAPOL */
-	wpa_printf(MSG_DEBUG, "CTRL_IFACE: 802.1X not included");
+	wpa_printf(MSG_DEBUG, "dbus: 802.1X not included");
 	return wpas_dbus_error_unknown_error(message, "802.1X not included");
 #endif /* IEEE8021X_EAPOL */
 }
@@ -2269,6 +2295,35 @@
 #endif /* CONFIG_TDLS */
 
 
+#ifndef CONFIG_NO_CONFIG_WRITE
+/**
+ * wpas_dbus_handler_save_config - Save configuration to configuration file
+ * @message: Pointer to incoming dbus message
+ * @wpa_s: wpa_supplicant structure for a network interface
+ * Returns: NULL on Success, Otherwise errror message
+ *
+ * Handler function for "SaveConfig" method call of network interface.
+ */
+DBusMessage * wpas_dbus_handler_save_config(DBusMessage *message,
+					    struct wpa_supplicant *wpa_s)
+{
+	int ret;
+
+	if (!wpa_s->conf->update_config) {
+		return wpas_dbus_error_unknown_error(
+			message,
+			"Not allowed to update configuration (update_config=0)");
+	}
+
+	ret = wpa_config_write(wpa_s->confname, wpa_s->conf);
+	if (ret)
+		return wpas_dbus_error_unknown_error(
+			message, "Failed to update configuration");
+	return NULL;
+}
+#endif /* CONFIG_NO_CONFIG_WRITE */
+
+
 /**
  * wpas_dbus_handler_set_pkcs11_engine_and_module_path - Set PKCS #11 engine and module path
  * @message: Pointer to incoming dbus message
@@ -2336,8 +2391,9 @@
  *
  * Getter for "Capabilities" property of an interface.
  */
-dbus_bool_t wpas_dbus_getter_capabilities(DBusMessageIter *iter,
-					  DBusError *error, void *user_data)
+dbus_bool_t wpas_dbus_getter_capabilities(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
 {
 	struct wpa_supplicant *wpa_s = user_data;
 	struct wpa_driver_capa capa;
@@ -2589,6 +2645,7 @@
 	     !wpa_dbus_dict_string_array_add_element(
 		     &iter_array, "ap")) ||
 	    (res >= 0 && (capa.flags & WPA_DRIVER_FLAGS_P2P_CAPABLE) &&
+	     !wpa_s->conf->p2p_disabled &&
 	     !wpa_dbus_dict_string_array_add_element(
 		     &iter_array, "p2p")) ||
 	    !wpa_dbus_dict_end_string_array(&iter_dict,
@@ -2627,8 +2684,9 @@
  *
  * Getter for "State" property.
  */
-dbus_bool_t wpas_dbus_getter_state(DBusMessageIter *iter, DBusError *error,
-				   void *user_data)
+dbus_bool_t wpas_dbus_getter_state(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
 {
 	struct wpa_supplicant *wpa_s = user_data;
 	const char *str_state;
@@ -2667,8 +2725,9 @@
  *
  * Getter for "scanning" property.
  */
-dbus_bool_t wpas_dbus_getter_scanning(DBusMessageIter *iter, DBusError *error,
-				      void *user_data)
+dbus_bool_t wpas_dbus_getter_scanning(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
 {
 	struct wpa_supplicant *wpa_s = user_data;
 	dbus_bool_t scanning = wpa_s->scanning ? TRUE : FALSE;
@@ -2687,8 +2746,9 @@
  *
  * Getter function for "ApScan" property.
  */
-dbus_bool_t wpas_dbus_getter_ap_scan(DBusMessageIter *iter, DBusError *error,
-				     void *user_data)
+dbus_bool_t wpas_dbus_getter_ap_scan(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
 {
 	struct wpa_supplicant *wpa_s = user_data;
 	dbus_uint32_t ap_scan = wpa_s->conf->ap_scan;
@@ -2707,8 +2767,9 @@
  *
  * Setter function for "ApScan" property.
  */
-dbus_bool_t wpas_dbus_setter_ap_scan(DBusMessageIter *iter, DBusError *error,
-				     void *user_data)
+dbus_bool_t wpas_dbus_setter_ap_scan(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
 {
 	struct wpa_supplicant *wpa_s = user_data;
 	dbus_uint32_t ap_scan;
@@ -2736,9 +2797,9 @@
  *
  * Getter function for "FastReauth" property.
  */
-dbus_bool_t wpas_dbus_getter_fast_reauth(DBusMessageIter *iter,
-					 DBusError *error,
-					 void *user_data)
+dbus_bool_t wpas_dbus_getter_fast_reauth(
+	const struct wpa_dbus_property_desc *property_desc,
+	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;
@@ -2758,9 +2819,9 @@
  *
  * Setter function for "FastReauth" property.
  */
-dbus_bool_t wpas_dbus_setter_fast_reauth(DBusMessageIter *iter,
-				     DBusError *error,
-				     void *user_data)
+dbus_bool_t wpas_dbus_setter_fast_reauth(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
 {
 	struct wpa_supplicant *wpa_s = user_data;
 	dbus_bool_t fast_reauth;
@@ -2784,9 +2845,9 @@
  * 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)
+dbus_bool_t wpas_dbus_getter_disconnect_reason(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
 {
 	struct wpa_supplicant *wpa_s = user_data;
 	dbus_int32_t reason = wpa_s->disconnect_reason;
@@ -2797,6 +2858,27 @@
 
 
 /**
+ * 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(
+	const struct wpa_dbus_property_desc *property_desc,
+	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
@@ -2805,9 +2887,9 @@
  *
  * Getter function for "BSSExpireAge" property.
  */
-dbus_bool_t wpas_dbus_getter_bss_expire_age(DBusMessageIter *iter,
-					    DBusError *error,
-					    void *user_data)
+dbus_bool_t wpas_dbus_getter_bss_expire_age(
+	const struct wpa_dbus_property_desc *property_desc,
+	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;
@@ -2826,9 +2908,9 @@
  *
  * Setter function for "BSSExpireAge" property.
  */
-dbus_bool_t wpas_dbus_setter_bss_expire_age(DBusMessageIter *iter,
-					    DBusError *error,
-					    void *user_data)
+dbus_bool_t wpas_dbus_setter_bss_expire_age(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
 {
 	struct wpa_supplicant *wpa_s = user_data;
 	dbus_uint32_t expire_age;
@@ -2855,9 +2937,9 @@
  *
  * Getter function for "BSSExpireCount" property.
  */
-dbus_bool_t wpas_dbus_getter_bss_expire_count(DBusMessageIter *iter,
-					      DBusError *error,
-					      void *user_data)
+dbus_bool_t wpas_dbus_getter_bss_expire_count(
+	const struct wpa_dbus_property_desc *property_desc,
+	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;
@@ -2876,9 +2958,9 @@
  *
  * Setter function for "BSSExpireCount" property.
  */
-dbus_bool_t wpas_dbus_setter_bss_expire_count(DBusMessageIter *iter,
-					      DBusError *error,
-					      void *user_data)
+dbus_bool_t wpas_dbus_setter_bss_expire_count(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
 {
 	struct wpa_supplicant *wpa_s = user_data;
 	dbus_uint32_t expire_count;
@@ -2905,8 +2987,9 @@
  *
  * Getter function for "Country" property.
  */
-dbus_bool_t wpas_dbus_getter_country(DBusMessageIter *iter, DBusError *error,
-				     void *user_data)
+dbus_bool_t wpas_dbus_getter_country(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
 {
 	struct wpa_supplicant *wpa_s = user_data;
 	char country[3];
@@ -2930,8 +3013,9 @@
  *
  * Setter function for "Country" property.
  */
-dbus_bool_t wpas_dbus_setter_country(DBusMessageIter *iter, DBusError *error,
-				     void *user_data)
+dbus_bool_t wpas_dbus_setter_country(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
 {
 	struct wpa_supplicant *wpa_s = user_data;
 	const char *country;
@@ -2968,9 +3052,9 @@
  *
  * Getter function for "ScanInterval" property.
  */
-dbus_bool_t wpas_dbus_getter_scan_interval(DBusMessageIter *iter,
-					   DBusError *error,
-					   void *user_data)
+dbus_bool_t wpas_dbus_getter_scan_interval(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
 {
 	struct wpa_supplicant *wpa_s = user_data;
 	dbus_int32_t scan_interval = wpa_s->scan_interval;
@@ -2989,9 +3073,9 @@
  *
  * Setter function for "ScanInterval" property.
  */
-dbus_bool_t wpas_dbus_setter_scan_interval(DBusMessageIter *iter,
-					   DBusError *error,
-					   void *user_data)
+dbus_bool_t wpas_dbus_setter_scan_interval(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
 {
 	struct wpa_supplicant *wpa_s = user_data;
 	dbus_int32_t scan_interval;
@@ -3018,8 +3102,9 @@
  *
  * Getter for "Ifname" property.
  */
-dbus_bool_t wpas_dbus_getter_ifname(DBusMessageIter *iter, DBusError *error,
-				    void *user_data)
+dbus_bool_t wpas_dbus_getter_ifname(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
 {
 	struct wpa_supplicant *wpa_s = user_data;
 	const char *ifname = wpa_s->ifname;
@@ -3038,8 +3123,9 @@
  *
  * Getter for "Driver" property.
  */
-dbus_bool_t wpas_dbus_getter_driver(DBusMessageIter *iter, DBusError *error,
-				    void *user_data)
+dbus_bool_t wpas_dbus_getter_driver(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
 {
 	struct wpa_supplicant *wpa_s = user_data;
 	const char *driver;
@@ -3067,9 +3153,9 @@
  *
  * Getter for "CurrentBSS" property.
  */
-dbus_bool_t wpas_dbus_getter_current_bss(DBusMessageIter *iter,
-					 DBusError *error,
-					 void *user_data)
+dbus_bool_t wpas_dbus_getter_current_bss(
+	const struct wpa_dbus_property_desc *property_desc,
+	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;
@@ -3095,9 +3181,9 @@
  *
  * Getter for "CurrentNetwork" property.
  */
-dbus_bool_t wpas_dbus_getter_current_network(DBusMessageIter *iter,
-					     DBusError *error,
-					     void *user_data)
+dbus_bool_t wpas_dbus_getter_current_network(
+	const struct wpa_dbus_property_desc *property_desc,
+	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;
@@ -3123,9 +3209,9 @@
  *
  * Getter for "CurrentAuthMode" property.
  */
-dbus_bool_t wpas_dbus_getter_current_auth_mode(DBusMessageIter *iter,
-					       DBusError *error,
-					       void *user_data)
+dbus_bool_t wpas_dbus_getter_current_auth_mode(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
 {
 	struct wpa_supplicant *wpa_s = user_data;
 	const char *eap_mode;
@@ -3160,9 +3246,9 @@
  *
  * Getter for "BridgeIfname" property.
  */
-dbus_bool_t wpas_dbus_getter_bridge_ifname(DBusMessageIter *iter,
-					   DBusError *error,
-					   void *user_data)
+dbus_bool_t wpas_dbus_getter_bridge_ifname(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
 {
 	struct wpa_supplicant *wpa_s = user_data;
 	const char *bridge_ifname = wpa_s->bridge_ifname;
@@ -3181,8 +3267,9 @@
  *
  * Getter for "BSSs" property.
  */
-dbus_bool_t wpas_dbus_getter_bsss(DBusMessageIter *iter, DBusError *error,
-				  void *user_data)
+dbus_bool_t wpas_dbus_getter_bsss(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
 {
 	struct wpa_supplicant *wpa_s = user_data;
 	struct wpa_bss *bss;
@@ -3238,8 +3325,9 @@
  *
  * Getter for "Networks" property.
  */
-dbus_bool_t wpas_dbus_getter_networks(DBusMessageIter *iter, DBusError *error,
-				      void *user_data)
+dbus_bool_t wpas_dbus_getter_networks(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
 {
 	struct wpa_supplicant *wpa_s = user_data;
 	struct wpa_ssid *ssid;
@@ -3301,9 +3389,9 @@
  *
  * Getter for "PKCS11EnginePath" property.
  */
-dbus_bool_t wpas_dbus_getter_pkcs11_engine_path(DBusMessageIter *iter,
-						DBusError *error,
-						void *user_data)
+dbus_bool_t wpas_dbus_getter_pkcs11_engine_path(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
 {
 	struct wpa_supplicant *wpa_s = user_data;
 	const char *pkcs11_engine_path;
@@ -3326,9 +3414,9 @@
  *
  * Getter for "PKCS11ModulePath" property.
  */
-dbus_bool_t wpas_dbus_getter_pkcs11_module_path(DBusMessageIter *iter,
-						DBusError *error,
-						void *user_data)
+dbus_bool_t wpas_dbus_getter_pkcs11_module_path(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
 {
 	struct wpa_supplicant *wpa_s = user_data;
 	const char *pkcs11_module_path;
@@ -3351,8 +3439,9 @@
  *
  * Getter for "Blobs" property.
  */
-dbus_bool_t wpas_dbus_getter_blobs(DBusMessageIter *iter, DBusError *error,
-				   void *user_data)
+dbus_bool_t wpas_dbus_getter_blobs(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
 {
 	struct wpa_supplicant *wpa_s = user_data;
 	DBusMessageIter variant_iter, dict_iter, entry_iter, array_iter;
@@ -3404,6 +3493,79 @@
 }
 
 
+dbus_bool_t wpas_dbus_getter_iface_global(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
+{
+	struct wpa_supplicant *wpa_s = user_data;
+	int ret;
+	char buf[250];
+	char *p = buf;
+
+	if (!property_desc->data) {
+		dbus_set_error(error, DBUS_ERROR_INVALID_ARGS,
+			       "Unhandled interface property %s",
+			       property_desc->dbus_property);
+		return FALSE;
+	}
+
+	ret = wpa_config_get_value(property_desc->data, wpa_s->conf, buf,
+				   sizeof(buf));
+	if (ret < 0)
+		*p = '\0';
+
+	return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_STRING, &p,
+						error);
+}
+
+
+dbus_bool_t wpas_dbus_setter_iface_global(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
+{
+	struct wpa_supplicant *wpa_s = user_data;
+	const char *new_value = NULL;
+	char buf[250];
+	size_t combined_len;
+	int ret;
+
+	if (!wpas_dbus_simple_property_setter(iter, error, DBUS_TYPE_STRING,
+					      &new_value))
+		return FALSE;
+
+	combined_len = os_strlen(property_desc->data) + os_strlen(new_value) +
+		3;
+	if (combined_len >= sizeof(buf)) {
+		dbus_set_error(error, DBUS_ERROR_INVALID_ARGS,
+			       "Interface property %s value too large",
+			       property_desc->dbus_property);
+		return FALSE;
+	}
+
+	if (!new_value[0])
+		new_value = "NULL";
+
+	ret = os_snprintf(buf, combined_len, "%s=%s", property_desc->data,
+			  new_value);
+	if (os_snprintf_error(combined_len, ret)) {
+		dbus_set_error(error,  WPAS_DBUS_ERROR_UNKNOWN_ERROR,
+			       "Failed to construct new interface property %s",
+			       property_desc->dbus_property);
+		return FALSE;
+	}
+
+	if (wpa_config_process_global(wpa_s->conf, buf, -1)) {
+		dbus_set_error(error, DBUS_ERROR_INVALID_ARGS,
+			       "Failed to set interface property %s",
+			       property_desc->dbus_property);
+		return FALSE;
+	}
+
+	wpa_supplicant_update_config(wpa_s);
+	return TRUE;
+}
+
+
 static struct wpa_bss * get_bss_helper(struct bss_handler_args *args,
 				       DBusError *error, const char *func_name)
 {
@@ -3430,8 +3592,9 @@
  *
  * Getter for "BSSID" property.
  */
-dbus_bool_t wpas_dbus_getter_bss_bssid(DBusMessageIter *iter, DBusError *error,
-				       void *user_data)
+dbus_bool_t wpas_dbus_getter_bss_bssid(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
 {
 	struct bss_handler_args *args = user_data;
 	struct wpa_bss *res;
@@ -3455,8 +3618,9 @@
  *
  * Getter for "SSID" property.
  */
-dbus_bool_t wpas_dbus_getter_bss_ssid(DBusMessageIter *iter, DBusError *error,
-				      void *user_data)
+dbus_bool_t wpas_dbus_getter_bss_ssid(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
 {
 	struct bss_handler_args *args = user_data;
 	struct wpa_bss *res;
@@ -3480,8 +3644,9 @@
  *
  * Getter for "Privacy" property.
  */
-dbus_bool_t wpas_dbus_getter_bss_privacy(DBusMessageIter *iter,
-					 DBusError *error, void *user_data)
+dbus_bool_t wpas_dbus_getter_bss_privacy(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
 {
 	struct bss_handler_args *args = user_data;
 	struct wpa_bss *res;
@@ -3506,8 +3671,9 @@
  *
  * Getter for "Mode" property.
  */
-dbus_bool_t wpas_dbus_getter_bss_mode(DBusMessageIter *iter, DBusError *error,
-				      void *user_data)
+dbus_bool_t wpas_dbus_getter_bss_mode(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
 {
 	struct bss_handler_args *args = user_data;
 	struct wpa_bss *res;
@@ -3547,8 +3713,9 @@
  *
  * Getter for "Level" property.
  */
-dbus_bool_t wpas_dbus_getter_bss_signal(DBusMessageIter *iter,
-					DBusError *error, void *user_data)
+dbus_bool_t wpas_dbus_getter_bss_signal(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
 {
 	struct bss_handler_args *args = user_data;
 	struct wpa_bss *res;
@@ -3573,8 +3740,9 @@
  *
  * Getter for "Frequency" property.
  */
-dbus_bool_t wpas_dbus_getter_bss_frequency(DBusMessageIter *iter,
-					   DBusError *error, void *user_data)
+dbus_bool_t wpas_dbus_getter_bss_frequency(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
 {
 	struct bss_handler_args *args = user_data;
 	struct wpa_bss *res;
@@ -3605,8 +3773,9 @@
  *
  * Getter for "Rates" property.
  */
-dbus_bool_t wpas_dbus_getter_bss_rates(DBusMessageIter *iter,
-				       DBusError *error, void *user_data)
+dbus_bool_t wpas_dbus_getter_bss_rates(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
 {
 	struct bss_handler_args *args = user_data;
 	struct wpa_bss *res;
@@ -3645,9 +3814,9 @@
 }
 
 
-static dbus_bool_t wpas_dbus_get_bss_security_prop(DBusMessageIter *iter,
-						   struct wpa_ie_data *ie_data,
-						   DBusError *error)
+static dbus_bool_t wpas_dbus_get_bss_security_prop(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, struct wpa_ie_data *ie_data, DBusError *error)
 {
 	DBusMessageIter iter_dict, variant_iter;
 	const char *group;
@@ -3778,8 +3947,9 @@
  *
  * Getter for "WPA" property.
  */
-dbus_bool_t wpas_dbus_getter_bss_wpa(DBusMessageIter *iter, DBusError *error,
-				     void *user_data)
+dbus_bool_t wpas_dbus_getter_bss_wpa(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
 {
 	struct bss_handler_args *args = user_data;
 	struct wpa_bss *res;
@@ -3798,7 +3968,7 @@
 		return FALSE;
 	}
 
-	return wpas_dbus_get_bss_security_prop(iter, &wpa_data, error);
+	return wpas_dbus_get_bss_security_prop(property_desc, iter, &wpa_data, error);
 }
 
 
@@ -3811,8 +3981,9 @@
  *
  * Getter for "RSN" property.
  */
-dbus_bool_t wpas_dbus_getter_bss_rsn(DBusMessageIter *iter, DBusError *error,
-				     void *user_data)
+dbus_bool_t wpas_dbus_getter_bss_rsn(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
 {
 	struct bss_handler_args *args = user_data;
 	struct wpa_bss *res;
@@ -3831,7 +4002,7 @@
 		return FALSE;
 	}
 
-	return wpas_dbus_get_bss_security_prop(iter, &wpa_data, error);
+	return wpas_dbus_get_bss_security_prop(property_desc, iter, &wpa_data, error);
 }
 
 
@@ -3844,8 +4015,9 @@
  *
  * Getter for "WPS" property.
  */
-dbus_bool_t wpas_dbus_getter_bss_wps(DBusMessageIter *iter, DBusError *error,
-				     void *user_data)
+dbus_bool_t wpas_dbus_getter_bss_wps(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
 {
 	struct bss_handler_args *args = user_data;
 	struct wpa_bss *res;
@@ -3900,8 +4072,9 @@
  *
  * Getter for "IEs" property.
  */
-dbus_bool_t wpas_dbus_getter_bss_ies(DBusMessageIter *iter, DBusError *error,
-				     void *user_data)
+dbus_bool_t wpas_dbus_getter_bss_ies(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
 {
 	struct bss_handler_args *args = user_data;
 	struct wpa_bss *res;
@@ -3925,8 +4098,9 @@
  *
  * Getter for BSS age
  */
-dbus_bool_t wpas_dbus_getter_bss_age(DBusMessageIter *iter, DBusError *error,
-				     void *user_data)
+dbus_bool_t wpas_dbus_getter_bss_age(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
 {
 	struct bss_handler_args *args = user_data;
 	struct wpa_bss *res;
@@ -3954,8 +4128,9 @@
  *
  * Getter for "enabled" property of a configured network.
  */
-dbus_bool_t wpas_dbus_getter_enabled(DBusMessageIter *iter, DBusError *error,
-				     void *user_data)
+dbus_bool_t wpas_dbus_getter_enabled(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
 {
 	struct network_handler_args *net = user_data;
 	dbus_bool_t enabled = net->ssid->disabled ? FALSE : TRUE;
@@ -3974,8 +4149,9 @@
  *
  * Setter for "Enabled" property of a configured network.
  */
-dbus_bool_t wpas_dbus_setter_enabled(DBusMessageIter *iter, DBusError *error,
-				     void *user_data)
+dbus_bool_t wpas_dbus_setter_enabled(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
 {
 	struct network_handler_args *net = user_data;
 	struct wpa_supplicant *wpa_s;
@@ -4007,9 +4183,9 @@
  *
  * Getter for "Properties" property of a configured network.
  */
-dbus_bool_t wpas_dbus_getter_network_properties(DBusMessageIter *iter,
-						DBusError *error,
-						void *user_data)
+dbus_bool_t wpas_dbus_getter_network_properties(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
 {
 	struct network_handler_args *net = user_data;
 	DBusMessageIter	variant_iter, dict_iter;
@@ -4069,9 +4245,9 @@
  *
  * Setter for "Properties" property of a configured network.
  */
-dbus_bool_t wpas_dbus_setter_network_properties(DBusMessageIter *iter,
-						DBusError *error,
-						void *user_data)
+dbus_bool_t wpas_dbus_setter_network_properties(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
 {
 	struct network_handler_args *net = user_data;
 	struct wpa_ssid *ssid = net->ssid;
@@ -4209,3 +4385,147 @@
 }
 
 #endif /* CONFIG_AP */
+
+
+DBusMessage * wpas_dbus_handler_vendor_elem_add(DBusMessage *message,
+						struct wpa_supplicant *wpa_s)
+{
+	u8 *ielems;
+	int len;
+	struct ieee802_11_elems elems;
+	dbus_int32_t frame_id;
+	DBusMessageIter	iter, array;
+
+	dbus_message_iter_init(message, &iter);
+	dbus_message_iter_get_basic(&iter, &frame_id);
+	if (frame_id < 0 || frame_id >= NUM_VENDOR_ELEM_FRAMES) {
+		return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS,
+					      "Invalid ID");
+	}
+
+	dbus_message_iter_next(&iter);
+	dbus_message_iter_recurse(&iter, &array);
+	dbus_message_iter_get_fixed_array(&array, &ielems, &len);
+	if (!ielems || len == 0) {
+		return dbus_message_new_error(
+			message, DBUS_ERROR_INVALID_ARGS, "Invalid value");
+	}
+
+	if (ieee802_11_parse_elems(ielems, len, &elems, 0) == ParseFailed) {
+		return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS,
+					      "Parse error");
+	}
+
+	wpa_s = wpas_vendor_elem(wpa_s, frame_id);
+	if (!wpa_s->vendor_elem[frame_id]) {
+		wpa_s->vendor_elem[frame_id] = wpabuf_alloc_copy(ielems, len);
+		wpas_vendor_elem_update(wpa_s);
+		return NULL;
+	}
+
+	if (wpabuf_resize(&wpa_s->vendor_elem[frame_id], len) < 0) {
+		return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS,
+					      "Resize error");
+	}
+
+	wpabuf_put_data(wpa_s->vendor_elem[frame_id], ielems, len);
+	wpas_vendor_elem_update(wpa_s);
+	return NULL;
+}
+
+
+DBusMessage * wpas_dbus_handler_vendor_elem_get(DBusMessage *message,
+						struct wpa_supplicant *wpa_s)
+{
+	DBusMessage *reply;
+	DBusMessageIter	iter, array_iter;
+	dbus_int32_t frame_id;
+	const u8 *elem;
+	size_t elem_len;
+
+	dbus_message_iter_init(message, &iter);
+	dbus_message_iter_get_basic(&iter, &frame_id);
+
+	if (frame_id < 0 || frame_id >= NUM_VENDOR_ELEM_FRAMES) {
+		return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS,
+					      "Invalid ID");
+	}
+
+	wpa_s = wpas_vendor_elem(wpa_s, frame_id);
+	if (!wpa_s->vendor_elem[frame_id]) {
+		return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS,
+					      "ID value does not exist");
+	}
+
+	reply = dbus_message_new_method_return(message);
+	if (!reply)
+		return wpas_dbus_error_no_memory(message);
+
+	dbus_message_iter_init_append(reply, &iter);
+
+	elem = wpabuf_head_u8(wpa_s->vendor_elem[frame_id]);
+	elem_len = wpabuf_len(wpa_s->vendor_elem[frame_id]);
+
+	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,
+						  &elem, elem_len) ||
+	    !dbus_message_iter_close_container(&iter, &array_iter)) {
+		dbus_message_unref(reply);
+		reply = wpas_dbus_error_no_memory(message);
+	}
+
+	return reply;
+}
+
+
+DBusMessage * wpas_dbus_handler_vendor_elem_remove(DBusMessage *message,
+						   struct wpa_supplicant *wpa_s)
+{
+	u8 *ielems;
+	int len;
+	struct ieee802_11_elems elems;
+	DBusMessageIter	iter, array;
+	dbus_int32_t frame_id;
+
+	dbus_message_iter_init(message, &iter);
+	dbus_message_iter_get_basic(&iter, &frame_id);
+	if (frame_id < 0 || frame_id >= NUM_VENDOR_ELEM_FRAMES) {
+		return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS,
+					      "Invalid ID");
+	}
+
+	dbus_message_iter_next(&iter);
+	dbus_message_iter_recurse(&iter, &array);
+	dbus_message_iter_get_fixed_array(&array, &ielems, &len);
+	if (!ielems || len == 0) {
+		return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS,
+					      "Invalid value");
+	}
+
+	wpa_s = wpas_vendor_elem(wpa_s, frame_id);
+
+	if (len == 1 && *ielems == '*') {
+		wpabuf_free(wpa_s->vendor_elem[frame_id]);
+		wpa_s->vendor_elem[frame_id] = NULL;
+		wpas_vendor_elem_update(wpa_s);
+		return NULL;
+	}
+
+	if (!wpa_s->vendor_elem[frame_id]) {
+		return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS,
+					      "ID value does not exist");
+	}
+
+	if (ieee802_11_parse_elems(ielems, len, &elems, 0) == ParseFailed) {
+		return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS,
+					      "Parse error");
+	}
+
+	if (wpas_vendor_elem_remove(wpa_s, frame_id, ielems, len) == 0)
+		return NULL;
+
+	return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS,
+				      "Not found");
+}
diff --git a/wpa_supplicant/dbus/dbus_new_handlers.h b/wpa_supplicant/dbus/dbus_new_handlers.h
index 50f72ec..cd299c0 100644
--- a/wpa_supplicant/dbus/dbus_new_handlers.h
+++ b/wpa_supplicant/dbus/dbus_new_handlers.h
@@ -10,6 +10,8 @@
 #ifndef CTRL_IFACE_DBUS_NEW_HANDLERS_H
 #define CTRL_IFACE_DBUS_NEW_HANDLERS_H
 
+#include "dbus_new_helpers.h"
+
 struct network_handler_args {
 	struct wpa_supplicant *wpa_s;
 	struct wpa_ssid *ssid;
@@ -50,39 +52,20 @@
 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);
+DBusMessage * wpas_dbus_handler_expect_disconnect(DBusMessage *message,
+						  struct wpa_global *global);
 
-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);
+DECLARE_ACCESSOR(wpas_dbus_getter_debug_level);
+DECLARE_ACCESSOR(wpas_dbus_getter_debug_timestamp);
+DECLARE_ACCESSOR(wpas_dbus_getter_debug_show_keys);
+DECLARE_ACCESSOR(wpas_dbus_setter_debug_level);
+DECLARE_ACCESSOR(wpas_dbus_setter_debug_timestamp);
+DECLARE_ACCESSOR(wpas_dbus_setter_debug_show_keys);
+DECLARE_ACCESSOR(wpas_dbus_getter_interfaces);
+DECLARE_ACCESSOR(wpas_dbus_getter_eap_methods);
+DECLARE_ACCESSOR(wpas_dbus_getter_global_capabilities);
+DECLARE_ACCESSOR(wpas_dbus_getter_iface_global);
+DECLARE_ACCESSOR(wpas_dbus_setter_iface_global);
 
 DBusMessage * wpas_dbus_handler_scan(DBusMessage *message,
 				     struct wpa_supplicant *wpa_s);
@@ -146,150 +129,51 @@
 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_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);
+DECLARE_ACCESSOR(wpas_dbus_getter_capabilities);
+DECLARE_ACCESSOR(wpas_dbus_getter_state);
+DECLARE_ACCESSOR(wpas_dbus_getter_scanning);
+DECLARE_ACCESSOR(wpas_dbus_getter_ap_scan);
+DECLARE_ACCESSOR(wpas_dbus_setter_ap_scan);
+DECLARE_ACCESSOR(wpas_dbus_getter_fast_reauth);
+DECLARE_ACCESSOR(wpas_dbus_setter_fast_reauth);
+DECLARE_ACCESSOR(wpas_dbus_getter_disconnect_reason);
+DECLARE_ACCESSOR(wpas_dbus_getter_disassociate_reason);
+DECLARE_ACCESSOR(wpas_dbus_getter_assoc_status_code);
+DECLARE_ACCESSOR(wpas_dbus_getter_bss_expire_age);
+DECLARE_ACCESSOR(wpas_dbus_setter_bss_expire_age);
+DECLARE_ACCESSOR(wpas_dbus_getter_bss_expire_count);
+DECLARE_ACCESSOR(wpas_dbus_setter_bss_expire_count);
+DECLARE_ACCESSOR(wpas_dbus_getter_country);
+DECLARE_ACCESSOR(wpas_dbus_setter_country);
+DECLARE_ACCESSOR(wpas_dbus_getter_scan_interval);
+DECLARE_ACCESSOR(wpas_dbus_setter_scan_interval);
+DECLARE_ACCESSOR(wpas_dbus_getter_ifname);
+DECLARE_ACCESSOR(wpas_dbus_getter_driver);
+DECLARE_ACCESSOR(wpas_dbus_getter_bridge_ifname);
+DECLARE_ACCESSOR(wpas_dbus_getter_current_bss);
+DECLARE_ACCESSOR(wpas_dbus_getter_current_network);
+DECLARE_ACCESSOR(wpas_dbus_getter_current_auth_mode);
+DECLARE_ACCESSOR(wpas_dbus_getter_bsss);
+DECLARE_ACCESSOR(wpas_dbus_getter_networks);
+DECLARE_ACCESSOR(wpas_dbus_getter_pkcs11_engine_path);
+DECLARE_ACCESSOR(wpas_dbus_getter_pkcs11_module_path);
+DECLARE_ACCESSOR(wpas_dbus_getter_blobs);
+DECLARE_ACCESSOR(wpas_dbus_getter_bss_bssid);
+DECLARE_ACCESSOR(wpas_dbus_getter_bss_ssid);
+DECLARE_ACCESSOR(wpas_dbus_getter_bss_privacy);
+DECLARE_ACCESSOR(wpas_dbus_getter_bss_mode);
+DECLARE_ACCESSOR(wpas_dbus_getter_bss_signal);
+DECLARE_ACCESSOR(wpas_dbus_getter_bss_frequency);
+DECLARE_ACCESSOR(wpas_dbus_getter_bss_rates);
+DECLARE_ACCESSOR(wpas_dbus_getter_bss_wpa);
+DECLARE_ACCESSOR(wpas_dbus_getter_bss_rsn);
+DECLARE_ACCESSOR(wpas_dbus_getter_bss_wps);
+DECLARE_ACCESSOR(wpas_dbus_getter_bss_ies);
+DECLARE_ACCESSOR(wpas_dbus_getter_bss_age);
+DECLARE_ACCESSOR(wpas_dbus_getter_enabled);
+DECLARE_ACCESSOR(wpas_dbus_setter_enabled);
+DECLARE_ACCESSOR(wpas_dbus_getter_network_properties);
+DECLARE_ACCESSOR(wpas_dbus_setter_network_properties);
 
 DBusMessage * wpas_dbus_handler_wps_start(DBusMessage *message,
 					  struct wpa_supplicant *wpa_s);
@@ -297,20 +181,10 @@
 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);
+DECLARE_ACCESSOR(wpas_dbus_getter_process_credentials);
+DECLARE_ACCESSOR(wpas_dbus_setter_process_credentials);
+DECLARE_ACCESSOR(wpas_dbus_getter_config_methods);
+DECLARE_ACCESSOR(wpas_dbus_setter_config_methods);
 
 DBusMessage * wpas_dbus_handler_tdls_discover(DBusMessage *message,
 					      struct wpa_supplicant *wpa_s);
@@ -321,6 +195,16 @@
 DBusMessage * wpas_dbus_handler_tdls_teardown(DBusMessage *message,
 					      struct wpa_supplicant *wpa_s);
 
+DBusMessage * wpas_dbus_handler_vendor_elem_add(DBusMessage *message,
+						struct wpa_supplicant *wpa_s);
+DBusMessage * wpas_dbus_handler_vendor_elem_get(DBusMessage *message,
+						struct wpa_supplicant *wpa_s);
+DBusMessage * wpas_dbus_handler_vendor_elem_remove(
+	DBusMessage *message, struct wpa_supplicant *wpa_s);
+
+DBusMessage * wpas_dbus_handler_save_config(DBusMessage *message,
+					    struct wpa_supplicant *wpa_s);
+
 DBusMessage * wpas_dbus_error_invalid_args(DBusMessage *message,
 					   const char *arg);
 DBusMessage * wpas_dbus_error_unknown_error(DBusMessage *message,
diff --git a/wpa_supplicant/dbus/dbus_new_handlers_p2p.c b/wpa_supplicant/dbus/dbus_new_handlers_p2p.c
index e9d60df..73b9e20 100644
--- a/wpa_supplicant/dbus/dbus_new_handlers_p2p.c
+++ b/wpa_supplicant/dbus/dbus_new_handlers_p2p.c
@@ -364,13 +364,14 @@
 			goto inv_args;
 
 		if (wpas_p2p_group_add_persistent(wpa_s, ssid, 0, freq, 0, 0, 0,
-						  NULL, 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))
+	} else if (wpas_p2p_group_add(wpa_s, persistent_group, freq, 0, 0, 0,
+				      0))
 		goto inv_args;
 
 out:
@@ -582,7 +583,7 @@
 
 	new_pin = wpas_p2p_connect(wpa_s, addr, pin, wps_method,
 				   persistent_group, 0, join, authorize_only,
-				   go_intent, freq, -1, 0, 0, 0);
+				   go_intent, freq, 0, -1, 0, 0, 0, 0, NULL, 0);
 
 	if (new_pin >= 0) {
 		char npin[9];
@@ -733,8 +734,8 @@
 		if (ssid == NULL || ssid->disabled != 2)
 			goto err;
 
-		if (wpas_p2p_invite(wpa_s, peer_addr, ssid, NULL, 0, 0, 0, 0) <
-		    0) {
+		if (wpas_p2p_invite(wpa_s, peer_addr, ssid, NULL, 0, 0, 0, 0, 0,
+				    0) < 0) {
 			reply = wpas_dbus_error_unknown_error(
 				message,
 				"Failed to reinvoke a persistent group");
@@ -807,9 +808,9 @@
  * P2P Device property accessor methods.
  */
 
-dbus_bool_t wpas_dbus_getter_p2p_device_config(DBusMessageIter *iter,
-					       DBusError *error,
-					       void *user_data)
+dbus_bool_t wpas_dbus_getter_p2p_device_config(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
 {
 	struct wpa_supplicant *wpa_s = user_data;
 	DBusMessageIter variant_iter, dict_iter;
@@ -916,9 +917,9 @@
 }
 
 
-dbus_bool_t wpas_dbus_setter_p2p_device_config(DBusMessageIter *iter,
-					       DBusError *error,
-					       void *user_data)
+dbus_bool_t wpas_dbus_setter_p2p_device_config(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
 {
 	struct wpa_supplicant *wpa_s = user_data;
 	DBusMessageIter variant_iter, iter_dict;
@@ -944,7 +945,8 @@
 		if (os_strcmp(entry.key, "DeviceName") == 0) {
 			char *devname;
 
-			if (entry.type != DBUS_TYPE_STRING)
+			if (entry.type != DBUS_TYPE_STRING ||
+			    os_strlen(entry.str_value) > WPS_DEV_NAME_MAX_LEN)
 				goto error;
 
 			devname = os_strdup(entry.str_value);
@@ -1087,8 +1089,9 @@
 }
 
 
-dbus_bool_t wpas_dbus_getter_p2p_peers(DBusMessageIter *iter, DBusError *error,
-				       void *user_data)
+dbus_bool_t wpas_dbus_getter_p2p_peers(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
 {
 	struct wpa_supplicant *wpa_s = user_data;
 	struct p2p_data *p2p = wpa_s->global->p2p;
@@ -1201,8 +1204,9 @@
 }
 
 
-dbus_bool_t wpas_dbus_getter_p2p_role(DBusMessageIter *iter, DBusError *error,
-				      void *user_data)
+dbus_bool_t wpas_dbus_getter_p2p_role(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
 {
 	struct wpa_supplicant *wpa_s = user_data;
 	char *str;
@@ -1224,8 +1228,9 @@
 }
 
 
-dbus_bool_t wpas_dbus_getter_p2p_group(DBusMessageIter *iter, DBusError *error,
-				       void *user_data)
+dbus_bool_t wpas_dbus_getter_p2p_group(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
 {
 	struct wpa_supplicant *wpa_s = user_data;
 	char path_buf[WPAS_DBUS_OBJECT_PATH_MAX];
@@ -1243,8 +1248,9 @@
 }
 
 
-dbus_bool_t wpas_dbus_getter_p2p_peergo(DBusMessageIter *iter,
-					DBusError *error, void *user_data)
+dbus_bool_t wpas_dbus_getter_p2p_peergo(
+	const struct wpa_dbus_property_desc *property_desc,
+	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;
@@ -1271,9 +1277,9 @@
  * Peer object properties accessor methods
  */
 
-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_device_name(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
 {
 	struct peer_handler_args *peer_args = user_data;
 	const struct p2p_peer_info *info;
@@ -1309,9 +1315,9 @@
 }
 
 
-dbus_bool_t wpas_dbus_getter_p2p_peer_manufacturer(DBusMessageIter *iter,
-						   DBusError *error,
-						   void *user_data)
+dbus_bool_t wpas_dbus_getter_p2p_peer_manufacturer(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
 {
 	struct peer_handler_args *peer_args = user_data;
 	const struct p2p_peer_info *info;
@@ -1346,9 +1352,9 @@
 }
 
 
-dbus_bool_t wpas_dbus_getter_p2p_peer_modelname(DBusMessageIter *iter,
-						DBusError *error,
-						void *user_data)
+dbus_bool_t wpas_dbus_getter_p2p_peer_modelname(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
 {
 	struct peer_handler_args *peer_args = user_data;
 	const struct p2p_peer_info *info;
@@ -1383,9 +1389,9 @@
 }
 
 
-dbus_bool_t wpas_dbus_getter_p2p_peer_modelnumber(DBusMessageIter *iter,
-						  DBusError *error,
-						  void *user_data)
+dbus_bool_t wpas_dbus_getter_p2p_peer_modelnumber(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
 {
 	struct peer_handler_args *peer_args = user_data;
 	const struct p2p_peer_info *info;
@@ -1420,9 +1426,9 @@
 }
 
 
-dbus_bool_t wpas_dbus_getter_p2p_peer_serialnumber(DBusMessageIter *iter,
-						   DBusError *error,
-						   void *user_data)
+dbus_bool_t wpas_dbus_getter_p2p_peer_serialnumber(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
 {
 	struct peer_handler_args *peer_args = user_data;
 	const struct p2p_peer_info *info;
@@ -1458,6 +1464,7 @@
 
 
 dbus_bool_t wpas_dbus_getter_p2p_peer_primary_device_type(
+	const struct wpa_dbus_property_desc *property_desc,
 	DBusMessageIter *iter, DBusError *error, void *user_data)
 {
 	struct peer_handler_args *peer_args = user_data;
@@ -1483,9 +1490,9 @@
 }
 
 
-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_config_method(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
 {
 	struct peer_handler_args *peer_args = user_data;
 	const struct p2p_peer_info *info;
@@ -1508,9 +1515,9 @@
 }
 
 
-dbus_bool_t wpas_dbus_getter_p2p_peer_level(DBusMessageIter *iter,
-					    DBusError *error,
-					    void *user_data)
+dbus_bool_t wpas_dbus_getter_p2p_peer_level(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
 {
 	struct peer_handler_args *peer_args = user_data;
 	const struct p2p_peer_info *info;
@@ -1533,9 +1540,9 @@
 }
 
 
-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_device_capability(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
 {
 	struct peer_handler_args *peer_args = user_data;
 	const struct p2p_peer_info *info;
@@ -1558,9 +1565,9 @@
 }
 
 
-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_group_capability(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
 {
 	struct peer_handler_args *peer_args = user_data;
 	const struct p2p_peer_info *info;
@@ -1584,6 +1591,7 @@
 
 
 dbus_bool_t wpas_dbus_getter_p2p_peer_secondary_device_types(
+	const struct wpa_dbus_property_desc *property_desc,
 	DBusMessageIter *iter, DBusError *error, void *user_data)
 {
 	struct peer_handler_args *peer_args = user_data;
@@ -1649,9 +1657,9 @@
 }
 
 
-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_vendor_extension(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
 {
 	struct wpabuf *vendor_extension[P2P_MAX_WPS_VENDOR_EXT];
 	unsigned int i, num = 0;
@@ -1684,8 +1692,9 @@
 }
 
 
-dbus_bool_t wpas_dbus_getter_p2p_peer_ies(DBusMessageIter *iter,
-					  DBusError *error, void *user_data)
+dbus_bool_t wpas_dbus_getter_p2p_peer_ies(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
 {
 	struct peer_handler_args *peer_args = user_data;
 	const struct p2p_peer_info *info;
@@ -1709,9 +1718,9 @@
 }
 
 
-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_device_address(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
 {
 	struct peer_handler_args *peer_args = user_data;
 	const struct p2p_peer_info *info;
@@ -1774,9 +1783,9 @@
 }
 
 
-dbus_bool_t wpas_dbus_getter_p2p_peer_groups(DBusMessageIter *iter,
-					     DBusError *error,
-					     void *user_data)
+dbus_bool_t wpas_dbus_getter_p2p_peer_groups(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
 {
 	struct peer_handler_args *peer_args = user_data;
 	const struct p2p_peer_info *info;
@@ -1842,9 +1851,9 @@
  *
  * Getter for "PersistentGroups" property.
  */
-dbus_bool_t wpas_dbus_getter_persistent_groups(DBusMessageIter *iter,
-					       DBusError *error,
-					       void *user_data)
+dbus_bool_t wpas_dbus_getter_persistent_groups(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
 {
 	struct wpa_supplicant *wpa_s = user_data;
 	struct wpa_ssid *ssid;
@@ -1904,21 +1913,21 @@
  *
  * Getter for "Properties" property of a persistent group.
  */
-dbus_bool_t wpas_dbus_getter_persistent_group_properties(DBusMessageIter *iter,
-							 DBusError *error,
-							 void *user_data)
+dbus_bool_t wpas_dbus_getter_persistent_group_properties(
+	const struct wpa_dbus_property_desc *property_desc,
+	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);
+	return wpas_dbus_getter_network_properties(property_desc, iter, error, net);
 }
 
 
 /**
- * wpas_dbus_setter_persistent_group_properties - Get options for a persistent
+ * 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
@@ -1927,9 +1936,9 @@
  *
  * Setter for "Properties" property of a persistent group.
  */
-dbus_bool_t wpas_dbus_setter_persistent_group_properties(DBusMessageIter *iter,
-							 DBusError *error,
-							 void *user_data)
+dbus_bool_t wpas_dbus_setter_persistent_group_properties(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
 {
 	struct network_handler_args *net = user_data;
 	struct wpa_ssid *ssid = net->ssid;
@@ -2142,9 +2151,9 @@
  * Group object properties accessor methods
  */
 
-dbus_bool_t wpas_dbus_getter_p2p_group_members(DBusMessageIter *iter,
-					       DBusError *error,
-					       void *user_data)
+dbus_bool_t wpas_dbus_getter_p2p_group_members(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
 {
 	struct wpa_supplicant *wpa_s = user_data;
 	struct wpa_ssid *ssid;
@@ -2211,8 +2220,9 @@
 }
 
 
-dbus_bool_t wpas_dbus_getter_p2p_group_ssid(DBusMessageIter *iter,
-					    DBusError *error, void *user_data)
+dbus_bool_t wpas_dbus_getter_p2p_group_ssid(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
 {
 	struct wpa_supplicant *wpa_s = user_data;
 
@@ -2224,9 +2234,9 @@
 }
 
 
-dbus_bool_t wpas_dbus_getter_p2p_group_bssid(DBusMessageIter *iter,
-					     DBusError *error,
-					     void *user_data)
+dbus_bool_t wpas_dbus_getter_p2p_group_bssid(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
 {
 	struct wpa_supplicant *wpa_s = user_data;
 	u8 role = wpas_get_p2p_role(wpa_s);
@@ -2248,9 +2258,9 @@
 }
 
 
-dbus_bool_t wpas_dbus_getter_p2p_group_frequency(DBusMessageIter *iter,
-						 DBusError *error,
-						 void *user_data)
+dbus_bool_t wpas_dbus_getter_p2p_group_frequency(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
 {
 	struct wpa_supplicant *wpa_s = user_data;
 	u16 op_freq;
@@ -2271,9 +2281,9 @@
 }
 
 
-dbus_bool_t wpas_dbus_getter_p2p_group_passphrase(DBusMessageIter *iter,
-						  DBusError *error,
-						  void *user_data)
+dbus_bool_t wpas_dbus_getter_p2p_group_passphrase(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
 {
 	struct wpa_supplicant *wpa_s = user_data;
 	char *p_pass;
@@ -2292,8 +2302,9 @@
 }
 
 
-dbus_bool_t wpas_dbus_getter_p2p_group_psk(DBusMessageIter *iter,
-					   DBusError *error, void *user_data)
+dbus_bool_t wpas_dbus_getter_p2p_group_psk(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
 {
 	struct wpa_supplicant *wpa_s = user_data;
 	u8 *p_psk = NULL;
@@ -2313,9 +2324,9 @@
 }
 
 
-dbus_bool_t wpas_dbus_getter_p2p_group_vendor_ext(DBusMessageIter *iter,
-						  DBusError *error,
-						  void *user_data)
+dbus_bool_t wpas_dbus_getter_p2p_group_vendor_ext(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
 {
 	struct wpa_supplicant *wpa_s = user_data;
 	struct hostapd_data *hapd;
@@ -2348,9 +2359,9 @@
 }
 
 
-dbus_bool_t wpas_dbus_setter_p2p_group_vendor_ext(DBusMessageIter *iter,
-						  DBusError *error,
-						  void *user_data)
+dbus_bool_t wpas_dbus_setter_p2p_group_vendor_ext(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
 {
 	struct wpa_supplicant *wpa_s = user_data;
 	DBusMessageIter variant_iter, iter_dict, array_iter, sub;
@@ -2876,8 +2887,9 @@
 
 #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_getter_global_wfd_ies(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
 {
 	struct wpa_global *global = user_data;
 	struct wpabuf *ie;
@@ -2898,8 +2910,9 @@
 }
 
 
-dbus_bool_t wpas_dbus_setter_global_wfd_ies(DBusMessageIter *iter,
-					    DBusError *error, void *user_data)
+dbus_bool_t wpas_dbus_setter_global_wfd_ies(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
 {
 	struct wpa_global *global = user_data;
 	DBusMessageIter variant, array;
diff --git a/wpa_supplicant/dbus/dbus_new_handlers_p2p.h b/wpa_supplicant/dbus/dbus_new_handlers_p2p.h
index 2aecbbe..c4c0261 100644
--- a/wpa_supplicant/dbus/dbus_new_handlers_p2p.h
+++ b/wpa_supplicant/dbus/dbus_new_handlers_p2p.h
@@ -89,139 +89,50 @@
 /*
  * 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);
+DECLARE_ACCESSOR(wpas_dbus_setter_p2p_device_config);
+DECLARE_ACCESSOR(wpas_dbus_getter_p2p_device_config);
+DECLARE_ACCESSOR(wpas_dbus_getter_p2p_peers);
+DECLARE_ACCESSOR(wpas_dbus_getter_p2p_role);
+DECLARE_ACCESSOR(wpas_dbus_getter_p2p_group);
+DECLARE_ACCESSOR(wpas_dbus_getter_p2p_peergo);
 
 /*
  * 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);
+DECLARE_ACCESSOR(wpas_dbus_getter_p2p_peer_device_name);
+DECLARE_ACCESSOR(wpas_dbus_getter_p2p_peer_manufacturer);
+DECLARE_ACCESSOR(wpas_dbus_getter_p2p_peer_modelname);
+DECLARE_ACCESSOR(wpas_dbus_getter_p2p_peer_modelnumber);
+DECLARE_ACCESSOR(wpas_dbus_getter_p2p_peer_serialnumber);
+DECLARE_ACCESSOR(wpas_dbus_getter_p2p_peer_primary_device_type);
+DECLARE_ACCESSOR(wpas_dbus_getter_p2p_peer_config_method);
+DECLARE_ACCESSOR(wpas_dbus_getter_p2p_peer_level);
+DECLARE_ACCESSOR(wpas_dbus_getter_p2p_peer_device_capability);
+DECLARE_ACCESSOR(wpas_dbus_getter_p2p_peer_group_capability);
+DECLARE_ACCESSOR(wpas_dbus_getter_p2p_peer_secondary_device_types);
+DECLARE_ACCESSOR(wpas_dbus_getter_p2p_peer_vendor_extension);
+DECLARE_ACCESSOR(wpas_dbus_getter_p2p_peer_ies);
+DECLARE_ACCESSOR(wpas_dbus_getter_p2p_peer_device_address);
+DECLARE_ACCESSOR(wpas_dbus_getter_p2p_peer_groups);
 
 /*
  * 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);
+DECLARE_ACCESSOR(wpas_dbus_getter_p2p_group_members);
+DECLARE_ACCESSOR(wpas_dbus_getter_p2p_group_ssid);
+DECLARE_ACCESSOR(wpas_dbus_getter_p2p_group_bssid);
+DECLARE_ACCESSOR(wpas_dbus_getter_p2p_group_frequency);
+DECLARE_ACCESSOR(wpas_dbus_getter_p2p_group_passphrase);
+DECLARE_ACCESSOR(wpas_dbus_getter_p2p_group_psk);
+DECLARE_ACCESSOR(wpas_dbus_getter_p2p_group_vendor_ext);
+DECLARE_ACCESSOR(wpas_dbus_setter_p2p_group_vendor_ext);
 
 /*
  * 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);
+DECLARE_ACCESSOR(wpas_dbus_getter_persistent_groups);
+DECLARE_ACCESSOR(wpas_dbus_getter_persistent_group_properties);
+DECLARE_ACCESSOR(wpas_dbus_setter_persistent_group_properties);
 
 DBusMessage * wpas_dbus_handler_add_persistent_group(
 	DBusMessage *message, struct wpa_supplicant *wpa_s);
@@ -233,15 +144,8 @@
 	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);
-
+DECLARE_ACCESSOR(wpas_dbus_getter_global_wfd_ies);
+DECLARE_ACCESSOR(wpas_dbus_setter_global_wfd_ies);
 #endif /* CONFIG_WIFI_DISPLAY */
 
 #endif /* DBUS_NEW_HANDLERS_P2P_H */
diff --git a/wpa_supplicant/dbus/dbus_new_handlers_wps.c b/wpa_supplicant/dbus/dbus_new_handlers_wps.c
index b2251ba..1d5dd1c 100644
--- a/wpa_supplicant/dbus/dbus_new_handlers_wps.c
+++ b/wpa_supplicant/dbus/dbus_new_handlers_wps.c
@@ -349,9 +349,9 @@
  * 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)
+dbus_bool_t wpas_dbus_getter_process_credentials(
+	const struct wpa_dbus_property_desc *property_desc,
+	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;
@@ -371,9 +371,9 @@
  * 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)
+dbus_bool_t wpas_dbus_setter_process_credentials(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
 {
 	struct wpa_supplicant *wpa_s = user_data;
 	dbus_bool_t process_credentials, old_pc;
@@ -407,9 +407,9 @@
  * 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)
+dbus_bool_t wpas_dbus_getter_config_methods(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
 {
 	struct wpa_supplicant *wpa_s = user_data;
 	char *methods = wpa_s->conf->config_methods;
@@ -431,9 +431,9 @@
  * 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)
+dbus_bool_t wpas_dbus_setter_config_methods(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
 {
 	struct wpa_supplicant *wpa_s = user_data;
 	char *methods, *new_methods;
diff --git a/wpa_supplicant/dbus/dbus_new_helpers.c b/wpa_supplicant/dbus/dbus_new_helpers.c
index 45623f3..0115e32 100644
--- a/wpa_supplicant/dbus/dbus_new_helpers.c
+++ b/wpa_supplicant/dbus/dbus_new_helpers.c
@@ -46,7 +46,7 @@
 			goto error;
 
 		/* An error getting a property fails the request entirely */
-		if (!dsc->getter(&entry_iter, error, user_data)) {
+		if (!dsc->getter(dsc, &entry_iter, error, user_data)) {
 			wpa_printf(MSG_INFO,
 				   "dbus: %s dbus_interface=%s dbus_property=%s getter failed",
 				   __func__, dsc->dbus_interface,
@@ -176,7 +176,7 @@
 	dbus_message_iter_init_append(reply, &iter);
 
 	dbus_error_init(&error);
-	if (dsc->getter(&iter, &error, user_data) == FALSE) {
+	if (dsc->getter(dsc, &iter, &error, user_data) == FALSE) {
 		dbus_message_unref(reply);
 		reply = wpas_dbus_reply_new_from_error(
 			message, &error, DBUS_ERROR_FAILED,
@@ -213,7 +213,7 @@
 
 	/* Iter will now point to the property's new value */
 	dbus_error_init(&error);
-	if (dsc->setter(&iter, &error, user_data) == TRUE) {
+	if (dsc->setter(dsc, &iter, &error, user_data) == TRUE) {
 		/* Success */
 		reply = dbus_message_new_method_return(message);
 	} else {
@@ -627,7 +627,8 @@
 			return FALSE;
 
 		dbus_error_init(&error);
-		if (!dsc->getter(&entry_iter, &error, obj_dsc->user_data)) {
+		if (!dsc->getter(dsc, &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",
diff --git a/wpa_supplicant/dbus/dbus_new_helpers.h b/wpa_supplicant/dbus/dbus_new_helpers.h
index 6e2c1f1..7b63b28 100644
--- a/wpa_supplicant/dbus/dbus_new_helpers.h
+++ b/wpa_supplicant/dbus/dbus_new_helpers.h
@@ -16,9 +16,13 @@
 					      void *user_data);
 typedef void (*WPADBusArgumentFreeFunction)(void *handler_arg);
 
-typedef dbus_bool_t (*WPADBusPropertyAccessor)(DBusMessageIter *iter,
-					       DBusError *error,
-					       void *user_data);
+struct wpa_dbus_property_desc;
+typedef dbus_bool_t (*WPADBusPropertyAccessor)(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data);
+#define DECLARE_ACCESSOR(f) \
+dbus_bool_t f(const struct wpa_dbus_property_desc *property_desc, \
+	      DBusMessageIter *iter, DBusError *error, void *user_data)
 
 struct wpa_dbus_object_desc {
 	DBusConnection *connection;
@@ -89,6 +93,8 @@
 	WPADBusPropertyAccessor getter;
 	/* property setter function */
 	WPADBusPropertyAccessor setter;
+	/* other data */
+	const char *data;
 };
 
 
diff --git a/wpa_supplicant/dbus/dbus_new_introspect.c b/wpa_supplicant/dbus/dbus_new_introspect.c
index 6209c67..aee105b 100644
--- a/wpa_supplicant/dbus/dbus_new_introspect.c
+++ b/wpa_supplicant/dbus/dbus_new_introspect.c
@@ -38,7 +38,7 @@
 	if (!iface)
 		return NULL;
 	iface->dbus_interface = os_strdup(dbus_interface);
-	iface->xml = wpabuf_alloc(6000);
+	iface->xml = wpabuf_alloc(15000);
 	if (iface->dbus_interface == NULL || iface->xml == NULL) {
 		os_free(iface->dbus_interface);
 		wpabuf_free(iface->xml);
@@ -257,7 +257,7 @@
 	DBusMessage *reply;
 	struct wpabuf *xml;
 
-	xml = wpabuf_alloc(10000);
+	xml = wpabuf_alloc(20000);
 	if (xml == NULL)
 		return NULL;
 
diff --git a/wpa_supplicant/dbus/dbus_old_handlers.c b/wpa_supplicant/dbus/dbus_old_handlers.c
index 462c713..e8f62ef 100644
--- a/wpa_supplicant/dbus/dbus_old_handlers.c
+++ b/wpa_supplicant/dbus/dbus_old_handlers.c
@@ -809,10 +809,10 @@
 }
 
 
-static const char  const *dont_quote[] = {
+static const char * const dont_quote[] = {
 	"key_mgmt", "proto", "pairwise", "auth_alg", "group", "eap",
 	"opensc_engine_path", "pkcs11_engine_path", "pkcs11_module_path",
-	"bssid", NULL
+	"bssid", "scan_freq", "freq_list", NULL
 };
 
 
diff --git a/wpa_supplicant/defconfig b/wpa_supplicant/defconfig
index 7f627fd..1d05198 100644
--- a/wpa_supplicant/defconfig
+++ b/wpa_supplicant/defconfig
@@ -31,6 +31,9 @@
 # Driver interface for Linux drivers using the nl80211 kernel interface
 CONFIG_DRIVER_NL80211=y
 
+# QCA vendor extensions to nl80211
+#CONFIG_DRIVER_NL80211_QCA=y
+
 # driver_nl80211.c requires libnl. If you are compiling it yourself
 # you may need to point hostapd to your version of libnl.
 #
@@ -267,6 +270,9 @@
 # Should we use epoll instead of select? Select is used by default.
 #CONFIG_ELOOP_EPOLL=y
 
+# Should we use kqueue instead of select? Select is used by default.
+#CONFIG_ELOOP_KQUEUE=y
+
 # Select layer 2 packet implementation
 # linux = Linux packet socket (default)
 # pcap = libpcap/libdnet/WinPcap
@@ -276,6 +282,12 @@
 # none = Empty template
 #CONFIG_L2_PACKET=linux
 
+# Disable Linux packet socket workaround applicable for station interface
+# in a bridge for EAPOL frames. This should be uncommented only if the kernel
+# is known to not have the regression issue in packet socket behavior with
+# bridge interfaces (commit 'bridge: respect RFC2863 operational state')').
+#CONFIG_NO_LINUX_PACKET_SOCKET_WAR=y
+
 # PeerKey handshake for Station to Station Link (IEEE 802.11e DLS)
 CONFIG_PEERKEY=y
 
@@ -455,6 +467,9 @@
 # Hotspot 2.0
 #CONFIG_HS20=y
 
+# Enable interface matching in wpa_supplicant
+#CONFIG_MATCH_IFACE=y
+
 # Disable roaming in wpa_supplicant
 #CONFIG_NO_ROAMING=y
 
@@ -495,3 +510,41 @@
 #
 # 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
+
+# Automatic Channel Selection
+# This will allow wpa_supplicant to pick the channel automatically when channel
+# is set to "0".
+#
+# TODO: Extend parser to be able to parse "channel=acs_survey" as an alternative
+# to "channel=0". This would enable us to eventually add other ACS algorithms 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.
+#
+# TODO: In analogy to hostapd be able to customize the ACS survey algorithm with
+# a newly to create wpa_supplicant.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
+
+# Support Multi Band Operation
+#CONFIG_MBO=y
diff --git a/wpa_supplicant/doc/docbook/eapol_test.sgml b/wpa_supplicant/doc/docbook/eapol_test.sgml
index e9af6d9..3f22413 100644
--- a/wpa_supplicant/doc/docbook/eapol_test.sgml
+++ b/wpa_supplicant/doc/docbook/eapol_test.sgml
@@ -194,7 +194,7 @@
   </refsect1>
   <refsect1>
     <title>Legal</title>
-    <para>wpa_supplicant is copyright (c) 2003-2015,
+    <para>wpa_supplicant is copyright (c) 2003-2016,
     Jouni Malinen <email>j@w1.fi</email> and
     contributors.
     All Rights Reserved.</para>
diff --git a/wpa_supplicant/doc/docbook/wpa_background.sgml b/wpa_supplicant/doc/docbook/wpa_background.sgml
index afb8c3b..13c9f45 100644
--- a/wpa_supplicant/doc/docbook/wpa_background.sgml
+++ b/wpa_supplicant/doc/docbook/wpa_background.sgml
@@ -90,7 +90,7 @@
 
   <refsect1>
     <title>Legal</title>
-    <para>wpa_supplicant is copyright (c) 2003-2015,
+    <para>wpa_supplicant is copyright (c) 2003-2016,
     Jouni Malinen <email>j@w1.fi</email> and
     contributors.
     All Rights Reserved.</para>
diff --git a/wpa_supplicant/doc/docbook/wpa_cli.sgml b/wpa_supplicant/doc/docbook/wpa_cli.sgml
index 47947c1..15400f0 100644
--- a/wpa_supplicant/doc/docbook/wpa_cli.sgml
+++ b/wpa_supplicant/doc/docbook/wpa_cli.sgml
@@ -345,7 +345,7 @@
   </refsect1>
   <refsect1>
     <title>Legal</title>
-    <para>wpa_supplicant is copyright (c) 2003-2015,
+    <para>wpa_supplicant is copyright (c) 2003-2016,
     Jouni Malinen <email>j@w1.fi</email> and
     contributors.
     All Rights Reserved.</para>
diff --git a/wpa_supplicant/doc/docbook/wpa_gui.sgml b/wpa_supplicant/doc/docbook/wpa_gui.sgml
index 5f7b49d..352d3d2 100644
--- a/wpa_supplicant/doc/docbook/wpa_gui.sgml
+++ b/wpa_supplicant/doc/docbook/wpa_gui.sgml
@@ -91,7 +91,7 @@
   </refsect1>
   <refsect1>
     <title>Legal</title>
-    <para>wpa_supplicant is copyright (c) 2003-2015,
+    <para>wpa_supplicant is copyright (c) 2003-2016,
     Jouni Malinen <email>j@w1.fi</email> and
     contributors.
     All Rights Reserved.</para>
diff --git a/wpa_supplicant/doc/docbook/wpa_passphrase.sgml b/wpa_supplicant/doc/docbook/wpa_passphrase.sgml
index b381e40..faf1f27 100644
--- a/wpa_supplicant/doc/docbook/wpa_passphrase.sgml
+++ b/wpa_supplicant/doc/docbook/wpa_passphrase.sgml
@@ -62,7 +62,7 @@
   </refsect1>
   <refsect1>
     <title>Legal</title>
-    <para>wpa_supplicant is copyright (c) 2003-2015,
+    <para>wpa_supplicant is copyright (c) 2003-2016,
     Jouni Malinen <email>j@w1.fi</email> and
     contributors.
     All Rights Reserved.</para>
diff --git a/wpa_supplicant/doc/docbook/wpa_priv.sgml b/wpa_supplicant/doc/docbook/wpa_priv.sgml
index d13a5db..403c9b2 100644
--- a/wpa_supplicant/doc/docbook/wpa_priv.sgml
+++ b/wpa_supplicant/doc/docbook/wpa_priv.sgml
@@ -137,7 +137,7 @@
   </refsect1>
   <refsect1>
     <title>Legal</title>
-    <para>wpa_supplicant is copyright (c) 2003-2015,
+    <para>wpa_supplicant is copyright (c) 2003-2016,
     Jouni Malinen <email>j@w1.fi</email> and
     contributors.
     All Rights Reserved.</para>
diff --git a/wpa_supplicant/doc/docbook/wpa_supplicant.sgml b/wpa_supplicant/doc/docbook/wpa_supplicant.sgml
index 46c21b5..c8c1ac4 100644
--- a/wpa_supplicant/doc/docbook/wpa_supplicant.sgml
+++ b/wpa_supplicant/doc/docbook/wpa_supplicant.sgml
@@ -736,7 +736,7 @@
   </refsect1>
   <refsect1>
     <title>Legal</title>
-    <para>wpa_supplicant is copyright (c) 2003-2015,
+    <para>wpa_supplicant is copyright (c) 2003-2016,
     Jouni Malinen <email>j@w1.fi</email> and
     contributors.
     All Rights Reserved.</para>
diff --git a/wpa_supplicant/driver_i.h b/wpa_supplicant/driver_i.h
index d1f9f8b..699fd4f 100644
--- a/wpa_supplicant/driver_i.h
+++ b/wpa_supplicant/driver_i.h
@@ -100,12 +100,10 @@
 }
 
 static inline int wpa_drv_sched_scan(struct wpa_supplicant *wpa_s,
-				     struct wpa_driver_scan_params *params,
-				     u32 interval)
+				     struct wpa_driver_scan_params *params)
 {
 	if (wpa_s->driver->sched_scan)
-		return wpa_s->driver->sched_scan(wpa_s->drv_priv,
-						 params, interval);
+		return wpa_s->driver->sched_scan(wpa_s->drv_priv, params);
 	return -1;
 }
 
@@ -292,7 +290,7 @@
 	if (wpa_s->driver->send_mlme)
 		return wpa_s->driver->send_mlme(wpa_s->drv_priv,
 						data, data_len, noack,
-						freq);
+						freq, NULL, 0);
 	return -1;
 }
 
@@ -401,7 +399,7 @@
 	if (wpa_s->driver->if_add)
 		return wpa_s->driver->if_add(wpa_s->drv_priv, type, ifname,
 					     addr, bss_ctx, NULL, force_ifname,
-					     if_addr, bridge, 0);
+					     if_addr, bridge, 0, 0);
 	return -1;
 }
 
@@ -893,4 +891,30 @@
 	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);
+}
+
+static inline int wpa_drv_abort_scan(struct wpa_supplicant *wpa_s)
+{
+	if (!wpa_s->driver->abort_scan)
+		return -1;
+	return wpa_s->driver->abort_scan(wpa_s->drv_priv);
+}
+
 #endif /* DRIVER_I_H */
diff --git a/wpa_supplicant/eapol_test.c b/wpa_supplicant/eapol_test.c
index bde7508..6548bd1 100644
--- a/wpa_supplicant/eapol_test.c
+++ b/wpa_supplicant/eapol_test.c
@@ -14,6 +14,7 @@
 
 #include "common.h"
 #include "utils/ext_password.h"
+#include "common/version.h"
 #include "config.h"
 #include "eapol_supp/eapol_supp_sm.h"
 #include "eap_peer/eap.h"
@@ -76,6 +77,9 @@
 
 	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;
@@ -189,7 +193,7 @@
 		return;
 	}
 
-	radius_msg_make_authenticator(msg, (u8 *) e, sizeof(*e));
+	radius_msg_make_authenticator(msg);
 
 	hdr = (const struct eap_hdr *) eap;
 	pos = (const u8 *) (hdr + 1);
@@ -254,6 +258,13 @@
 		goto fail;
 	}
 
+	if (!find_extra_attr(e->extra_attrs, RADIUS_ATTR_SERVICE_TYPE) &&
+	    !radius_msg_add_attr_int32(msg, RADIUS_ATTR_SERVICE_TYPE,
+				       RADIUS_SERVICE_TYPE_FRAMED)) {
+		printf("Could not add Service-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,
@@ -329,7 +340,11 @@
 
 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);
 }
 
 
@@ -407,6 +422,9 @@
 {
 	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();
@@ -552,11 +570,21 @@
 }
 
 
+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) {
@@ -590,6 +618,25 @@
 		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;
@@ -614,6 +661,8 @@
 {
 	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);
@@ -757,6 +806,8 @@
 		break;
 	case EAP_CODE_FAILURE:
 		os_strlcpy(buf, "EAP Failure", sizeof(buf));
+		if (e->ctrl_iface)
+			break;
 		eloop_terminate();
 		break;
 	default:
@@ -901,25 +952,66 @@
 	if ((hdr->code == RADIUS_CODE_ACCESS_ACCEPT &&
 	     e->eapol_test_num_reauths < 0) ||
 	    hdr->code == RADIUS_CODE_ACCESS_REJECT) {
-		eloop_terminate();
+		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 *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, "test", sizeof(wpa_s->ifname));
+	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);
@@ -1155,13 +1247,13 @@
 static void usage(void)
 {
 	printf("usage:\n"
-	       "eapol_test [-enWS] -c<conf> [-a<AS IP>] [-p<AS port>] "
+	       "eapol_test [-enWSv] -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>]\n"
+	       "           [-A<client IP>] [-i<ifname>] [-T<ctrl_iface>]\n"
 	       "eapol_test scard\n"
 	       "eapol_test sim <PIN> <num triplets> [debug]\n"
 	       "\n");
@@ -1180,6 +1272,7 @@
 	       "  -W = wait for a control interface monitor before starting\n"
 	       "  -S = save configuration after authentication\n"
 	       "  -n = no MPPE keys expected\n"
+	       "  -v = show version\n"
 	       "  -t<timeout> = sets timeout in seconds (default: 30 s)\n"
 	       "  -C<Connect-Info> = RADIUS Connect-Info (default: "
 	       "CONNECT 11Mbps 802.11b)\n"
@@ -1216,6 +1309,8 @@
 	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;
@@ -1231,7 +1326,7 @@
 	wpa_debug_show_keys = 1;
 
 	for (;;) {
-		c = getopt(argc, argv, "a:A:c:C:eM:nN:o:p:P:r:R:s:St:W");
+		c = getopt(argc, argv, "a:A:c:C:ei:M:nN:o:p:P:r:R:s:St:T:vW");
 		if (c < 0)
 			break;
 		switch (c) {
@@ -1250,6 +1345,9 @@
 		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();
@@ -1290,6 +1388,13 @@
 		case 't':
 			timeout = atoi(optarg);
 			break;
+		case 'T':
+			ctrl_iface = optarg;
+			eapol_test.ctrl_iface = 1;
+			break;
+		case 'v':
+			printf("eapol_test v" VERSION_STR "\n");
+			return 0;
 		case 'W':
 			wait_for_monitor++;
 			break;
@@ -1336,7 +1441,7 @@
 					  &argv[optind + 1]);
 	}
 
-	if (conf == NULL) {
+	if (conf == NULL && !ctrl_iface) {
 		usage();
 		printf("Configuration file is required.\n");
 		return -1;
@@ -1358,12 +1463,15 @@
 	eapol_test.wpa_s = &wpa_s;
 	dl_list_init(&wpa_s.bss);
 	dl_list_init(&wpa_s.bss_id);
-	wpa_s.conf = wpa_config_read(conf, NULL);
+	if (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 (wpa_s.conf->ssid == NULL) {
+	if (!ctrl_iface && wpa_s.conf->ssid == NULL) {
 		printf("No networks defined.\n");
 		return -1;
 	}
@@ -1374,7 +1482,7 @@
 	}
 
 	wpa_init_conf(&eapol_test, &wpa_s, as_addr, as_port, as_secret,
-		      cli_addr);
+		      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"
@@ -1387,7 +1495,8 @@
 		       wpa_s.conf->ctrl_interface);
 		return -1;
 	}
-	if (wpa_supplicant_scard_init(&wpa_s, wpa_s.conf->ssid))
+	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))
@@ -1399,9 +1508,12 @@
 	if (wait_for_monitor)
 		wpa_supplicant_ctrl_iface_wait(wpa_s.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);
+	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();
diff --git a/wpa_supplicant/eapol_test.py b/wpa_supplicant/eapol_test.py
new file mode 100644
index 0000000..80e7dfc
--- /dev/null
+++ b/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/wpa_supplicant/events.c b/wpa_supplicant/events.c
index 6b0c685..1b3d8a9 100644
--- a/wpa_supplicant/events.c
+++ b/wpa_supplicant/events.c
@@ -23,6 +23,7 @@
 #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"
@@ -71,6 +72,7 @@
 }
 
 
+#ifndef CONFIG_NO_SCAN_PROCESSING
 /**
  * wpas_reenabled_network_time - Time until first network is re-enabled
  * @wpa_s: Pointer to wpa_supplicant data
@@ -106,6 +108,7 @@
 
 	return res;
 }
+#endif /* CONFIG_NO_SCAN_PROCESSING */
 
 
 void wpas_network_reenabled(void *eloop_ctx, void *timeout_ctx)
@@ -286,9 +289,6 @@
 	os_memset(wpa_s->bssid, 0, ETH_ALEN);
 	os_memset(wpa_s->pending_bssid, 0, ETH_ALEN);
 	sme_clear_on_disassoc(wpa_s);
-#ifdef CONFIG_P2P
-	os_memset(wpa_s->go_dev_addr, 0, ETH_ALEN);
-#endif /* CONFIG_P2P */
 	wpa_s->current_bss = NULL;
 	wpa_s->assoc_freq = 0;
 
@@ -305,6 +305,7 @@
 	wpa_s->key_mgmt = 0;
 
 	wpas_rrm_reset(wpa_s);
+	wpa_s->wnmsleep_used = 0;
 }
 
 
@@ -566,6 +567,23 @@
 			break;
 		}
 #endif /* CONFIG_IEEE80211W */
+		if ((ie.capabilities & WPA_CAPABILITY_MFPR) &&
+		    wpas_get_ssid_pmf(wpa_s, ssid) ==
+		    NO_MGMT_FRAME_PROTECTION) {
+			wpa_dbg(wpa_s, MSG_DEBUG,
+				"   skip RSN IE - no mgmt frame protection enabled but AP requires it");
+			break;
+		}
+#ifdef CONFIG_MBO
+		if (!(ie.capabilities & WPA_CAPABILITY_MFPC) &&
+		    wpas_mbo_get_bss_attr(bss, MBO_ATTR_ID_AP_CAPA_IND) &&
+		    wpas_get_ssid_pmf(wpa_s, ssid) !=
+		    NO_MGMT_FRAME_PROTECTION) {
+			wpa_dbg(wpa_s, MSG_DEBUG,
+				"   skip RSN IE - no mgmt frame protection enabled on MBO AP");
+			break;
+		}
+#endif /* CONFIG_MBO */
 
 		wpa_dbg(wpa_s, MSG_DEBUG, "   selected based on RSN IE");
 		return 1;
@@ -808,10 +826,10 @@
 }
 
 
-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)
+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;
@@ -819,6 +837,9 @@
 	const u8 *ie;
 	struct wpa_ssid *ssid;
 	int osen;
+#ifdef CONFIG_MBO
+	const u8 *assoc_disallow;
+#endif /* CONFIG_MBO */
 
 	ie = wpa_bss_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE);
 	wpa_ie_len = ie ? ie[1] : 0;
@@ -830,9 +851,9 @@
 	osen = ie != NULL;
 
 	wpa_dbg(wpa_s, MSG_DEBUG, "%d: " MACSTR " ssid='%s' "
-		"wpa_ie_len=%u rsn_ie_len=%u caps=0x%x level=%d%s%s%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,
+		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)) ?
@@ -980,8 +1001,14 @@
 			continue;
 		}
 
-		if (!bss_is_ess(bss)) {
-			wpa_dbg(wpa_s, MSG_DEBUG, "   skip - not ESS network");
+		if (!bss_is_ess(bss) && !bss_is_pbss(bss)) {
+			wpa_dbg(wpa_s, MSG_DEBUG, "   skip - neither ESS nor PBSS network");
+			continue;
+		}
+
+		if (ssid->pbss != bss_is_pbss(bss)) {
+			wpa_dbg(wpa_s, MSG_DEBUG, "   skip - PBSS mismatch (ssid %d bss %d)",
+				ssid->pbss, bss_is_pbss(bss));
 			continue;
 		}
 
@@ -1038,6 +1065,35 @@
 		 */
 #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;
+		}
+#ifdef CONFIG_MBO
+		assoc_disallow = wpas_mbo_get_bss_attr(
+			bss, MBO_ATTR_ID_ASSOC_DISALLOW);
+		if (assoc_disallow && assoc_disallow[1] >= 1) {
+			wpa_dbg(wpa_s, MSG_DEBUG,
+				"   skip - MBO association disallowed (reason %u)",
+				assoc_disallow[2]);
+			continue;
+		}
+
+		if (wpa_is_bss_tmp_disallowed(wpa_s, bss->bssid)) {
+			wpa_dbg(wpa_s, MSG_DEBUG,
+				"   skip - MBO retry delay has not passed yet");
+			continue;
+		}
+#endif /* CONFIG_MBO */
+
 		/* Matching configuration found */
 		return ssid;
 	}
@@ -1190,6 +1246,7 @@
 #endif /* CONFIG_P2P */
 
 #ifdef CONFIG_WPS
+		wpas_wps_pbc_overlap(wpa_s);
 		wpas_wps_cancel(wpa_s);
 #endif /* CONFIG_WPS */
 		return -1;
@@ -1289,6 +1346,7 @@
 	struct wpa_bss *current_bss = NULL;
 #ifndef CONFIG_NO_ROAMING
 	int min_diff;
+	int to_5ghz;
 #endif /* CONFIG_NO_ROAMING */
 
 	if (wpa_s->reassociate)
@@ -1344,7 +1402,10 @@
 		return 1;
 	}
 
-	if (current_bss->level < 0 && current_bss->level > selected->level) {
+	to_5ghz = selected->freq > 4000 && current_bss->freq < 4000;
+
+	if (current_bss->level < 0 &&
+	    current_bss->level > selected->level + to_5ghz * 2) {
 		wpa_dbg(wpa_s, MSG_DEBUG, "Skip roam - Current BSS has better "
 			"signal level");
 		return 0;
@@ -1363,6 +1424,13 @@
 		else
 			min_diff = 5;
 	}
+	if (to_5ghz) {
+		/* Make it easier to move to 5 GHz band */
+		if (min_diff > 2)
+			min_diff -= 2;
+		else
+			min_diff = 0;
+	}
 	if (abs(current_bss->level - selected->level) < min_diff) {
 		wpa_dbg(wpa_s, MSG_DEBUG, "Skip roam - too small difference "
 			"in signal level");
@@ -1405,6 +1473,8 @@
 			return -1;
 		if (!own_request)
 			return -1;
+		if (data && data->scan_info.external_scan)
+			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);
@@ -1429,7 +1499,7 @@
 #endif /* CONFIG_NO_RANDOM_POOL */
 
 	if (own_request && wpa_s->scan_res_handler &&
-	    (wpa_s->own_scan_running || !wpa_s->radio->external_scan_running)) {
+	    !(data && data->scan_info.external_scan)) {
 		void (*scan_res_handler)(struct wpa_supplicant *wpa_s,
 					 struct wpa_scan_results *scan_res);
 
@@ -1450,9 +1520,11 @@
 	}
 
 	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);
+		wpa_s->own_scan_running,
+		data ? data->scan_info.external_scan : 0);
 	if (wpa_s->last_scan_req == MANUAL_SCAN_REQ &&
-	    wpa_s->manual_scan_use_id && wpa_s->own_scan_running) {
+	    wpa_s->manual_scan_use_id && wpa_s->own_scan_running &&
+	    own_request && !(data && data->scan_info.external_scan)) {
 		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;
@@ -1463,7 +1535,7 @@
 
 	wpas_notify_scan_done(wpa_s, 1);
 
-	if (!wpa_s->own_scan_running && wpa_s->radio->external_scan_running) {
+	if (data && data->scan_info.external_scan) {
 		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;
@@ -1492,9 +1564,13 @@
 
 	wpas_wps_update_ap_info(wpa_s, scan_res);
 
+	if (wpa_s->wpa_state >= WPA_AUTHENTICATING &&
+	    wpa_s->wpa_state < WPA_COMPLETED)
+		goto scan_work_done;
+
 	wpa_scan_results_free(scan_res);
 
-	if (wpa_s->scan_work) {
+	if (own_request && wpa_s->scan_work) {
 		struct wpa_radio_work *work = wpa_s->scan_work;
 		wpa_s->scan_work = NULL;
 		radio_work_done(work);
@@ -1504,7 +1580,7 @@
 
 scan_work_done:
 	wpa_scan_results_free(scan_res);
-	if (wpa_s->scan_work) {
+	if (own_request && wpa_s->scan_work) {
 		struct wpa_radio_work *work = wpa_s->scan_work;
 		wpa_s->scan_work = NULL;
 		radio_work_done(work);
@@ -1544,6 +1620,13 @@
 			return 0;
 		}
 
+		if (ssid != wpa_s->current_ssid &&
+		    wpa_s->wpa_state >= WPA_AUTHENTICATING) {
+			wpa_s->own_disconnect_req = 1;
+			wpa_supplicant_deauthenticate(
+				wpa_s, WLAN_REASON_DEAUTH_LEAVING);
+		}
+
 		if (wpa_supplicant_connect(wpa_s, selected, ssid) < 0) {
 			wpa_dbg(wpa_s, MSG_DEBUG, "Connect failed");
 			return -1;
@@ -1818,6 +1901,50 @@
 #endif /* CONFIG_INTERWORKING */
 
 
+#ifdef CONFIG_FST
+static int wpas_fst_update_mbie(struct wpa_supplicant *wpa_s,
+				const u8 *ie, size_t ie_len)
+{
+	struct mb_ies_info mb_ies;
+
+	if (!ie || !ie_len || !wpa_s->fst)
+	    return -ENOENT;
+
+	os_memset(&mb_ies, 0, sizeof(mb_ies));
+
+	while (ie_len >= 2 && mb_ies.nof_ies < MAX_NOF_MB_IES_SUPPORTED) {
+		size_t len;
+
+		len = 2 + ie[1];
+		if (len > ie_len) {
+			wpa_hexdump(MSG_DEBUG, "FST: Truncated IE found",
+				    ie, ie_len);
+			break;
+		}
+
+		if (ie[0] == WLAN_EID_MULTI_BAND) {
+			wpa_printf(MSG_DEBUG, "MB IE of %u bytes found",
+				   (unsigned int) len);
+			mb_ies.ies[mb_ies.nof_ies].ie = ie + 2;
+			mb_ies.ies[mb_ies.nof_ies].ie_len = len - 2;
+			mb_ies.nof_ies++;
+		}
+
+		ie_len -= len;
+		ie += len;
+	}
+
+	if (mb_ies.nof_ies > 0) {
+		wpabuf_free(wpa_s->received_mb_ies);
+		wpa_s->received_mb_ies = mb_ies_by_info(&mb_ies);
+		return 0;
+	}
+
+	return -ENOENT;
+}
+#endif /* CONFIG_FST */
+
+
 static int wpa_supplicant_event_associnfo(struct wpa_supplicant *wpa_s,
 					  union wpa_event_data *data)
 {
@@ -1868,6 +1995,8 @@
 		}
 		if ((p[0] == WLAN_EID_VENDOR_SPECIFIC && p[1] >= 6 &&
 		     (os_memcmp(&p[2], "\x00\x50\xF2\x01\x01\x00", 6) == 0)) ||
+		    (p[0] == WLAN_EID_VENDOR_SPECIFIC && p[1] >= 4 &&
+		     (os_memcmp(&p[2], "\x50\x6F\x9A\x12", 4) == 0)) ||
 		    (p[0] == WLAN_EID_RSN && p[1] >= 2)) {
 			if (wpa_sm_set_assoc_wpa_ie(wpa_s->wpa, p, len))
 				break;
@@ -2038,6 +2167,45 @@
 }
 
 
+static void wpas_fst_update_mb_assoc(struct wpa_supplicant *wpa_s,
+				     union wpa_event_data *data)
+{
+#ifdef CONFIG_FST
+	struct assoc_info *ai = data ? &data->assoc_info : NULL;
+	struct wpa_bss *bss = wpa_s->current_bss;
+	const u8 *ieprb, *iebcn;
+
+	wpabuf_free(wpa_s->received_mb_ies);
+	wpa_s->received_mb_ies = NULL;
+
+	if (ai &&
+	    !wpas_fst_update_mbie(wpa_s, ai->resp_ies, ai->resp_ies_len)) {
+		wpa_printf(MSG_DEBUG,
+			   "FST: MB IEs updated from Association Response frame");
+		return;
+	}
+
+	if (ai &&
+	    !wpas_fst_update_mbie(wpa_s, ai->beacon_ies, ai->beacon_ies_len)) {
+		wpa_printf(MSG_DEBUG,
+			   "FST: MB IEs updated from association event Beacon IEs");
+		return;
+	}
+
+	if (!bss)
+		return;
+
+	ieprb = (const u8 *) (bss + 1);
+	iebcn = ieprb + bss->ie_len;
+
+	if (!wpas_fst_update_mbie(wpa_s, ieprb, bss->ie_len))
+		wpa_printf(MSG_DEBUG, "FST: MB IEs updated from bss IE");
+	else if (!wpas_fst_update_mbie(wpa_s, iebcn, bss->beacon_ie_len))
+		wpa_printf(MSG_DEBUG, "FST: MB IEs updated from bss beacon IE");
+#endif /* CONFIG_FST */
+}
+
+
 static void wpa_supplicant_event_assoc(struct wpa_supplicant *wpa_s,
 				       union wpa_event_data *data)
 {
@@ -2102,6 +2270,8 @@
 				"WPA/RSN IEs not updated");
 	}
 
+	wpas_fst_update_mb_assoc(wpa_s, data);
+
 #ifdef CONFIG_SME
 	os_memcpy(wpa_s->sme.prev_bssid, bssid, ETH_ALEN);
 	wpa_s->sme.prev_bssid_set = 1;
@@ -2343,7 +2513,8 @@
 	if (!wpa_s->disconnected &&
 	    (!wpa_s->auto_reconnect_disabled ||
 	     wpa_s->key_mgmt == WPA_KEY_MGMT_WPS ||
-	     wpas_wps_searching(wpa_s))) {
+	     wpas_wps_searching(wpa_s) ||
+	     wpas_wps_reenable_networks_pending(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,
@@ -2372,7 +2543,8 @@
 			"try to re-connect");
 		wpa_s->reassociate = 0;
 		wpa_s->disconnected = 1;
-		wpa_supplicant_cancel_sched_scan(wpa_s);
+		if (!wpa_s->pno)
+			wpa_supplicant_cancel_sched_scan(wpa_s);
 	}
 	bssid = wpa_s->bssid;
 	if (is_zero_ether_addr(bssid))
@@ -2402,7 +2574,8 @@
 	    !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)) {
+	    !wpas_temp_disabled(wpa_s, fast_reconnect_ssid) &&
+	    !wpa_is_bss_tmp_disallowed(wpa_s, fast_reconnect->bssid)) {
 #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,
@@ -2600,6 +2773,13 @@
 		}
 #endif /* CONFIG_P2P */
 
+#ifdef CONFIG_MATCH_IFACE
+		if (wpa_s->matched) {
+			wpa_supplicant_remove_iface(wpa_s->global, wpa_s, 0);
+			break;
+		}
+#endif /* CONFIG_MATCH_IFACE */
+
 #ifdef CONFIG_TERMINATE_ONLASTIF
 		/* check if last interface */
 		if (!any_interfaces(wpa_s->global->ifaces))
@@ -2993,25 +3173,13 @@
 	if (wpa_s->drv_priv == NULL)
 		return; /* Ignore event during drv initialization */
 
-	free_hw_features(wpa_s);
-	wpa_s->hw.modes = wpa_drv_get_hw_feature_data(
-		wpa_s, &wpa_s->hw.num_modes, &wpa_s->hw.flags);
-
-	wpas_p2p_update_channel_list(wpa_s);
-
-	/*
-	 * Check other interfaces to see if they share the same radio. If
-	 * so, they get updated with this same hw mode info.
-	 */
 	dl_list_for_each(ifs, &wpa_s->radio->ifaces, struct wpa_supplicant,
 			 radio_list) {
-		if (ifs != wpa_s) {
-			wpa_printf(MSG_DEBUG, "%s: Updating hw mode",
-				   ifs->ifname);
-			free_hw_features(ifs);
-			ifs->hw.modes = wpa_drv_get_hw_feature_data(
-				ifs, &ifs->hw.num_modes, &ifs->hw.flags);
-		}
+		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 */
@@ -3021,6 +3189,8 @@
 		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);
 }
 
 
@@ -3121,6 +3291,13 @@
 		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)
@@ -3131,9 +3308,6 @@
 static void wpa_supplicant_notify_avoid_freq(struct wpa_supplicant *wpa_s,
 					     union wpa_event_data *event)
 {
-#ifdef CONFIG_P2P
-	struct wpa_supplicant *ifs;
-#endif /* CONFIG_P2P */
 	struct wpa_freq_range_list *list;
 	char *str = NULL;
 
@@ -3150,29 +3324,13 @@
 			__func__);
 	} else {
 		wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Update channel list based on frequency avoid event");
-		wpas_p2p_update_channel_list(wpa_s);
-	}
 
-	for (ifs = wpa_s->global->ifaces; ifs; ifs = ifs->next) {
-		int freq;
-		if (!ifs->current_ssid ||
-		    !ifs->current_ssid->p2p_group ||
-		    (ifs->current_ssid->mode != WPAS_MODE_P2P_GO &&
-		     ifs->current_ssid->mode != WPAS_MODE_P2P_GROUP_FORMATION))
-			continue;
-
-		freq = ifs->current_ssid->frequency;
-		if (!freq_range_list_includes(list, freq)) {
-			wpa_dbg(ifs, MSG_DEBUG, "P2P GO operating frequency %d MHz in safe range",
-				freq);
-			continue;
-		}
-
-		wpa_dbg(ifs, MSG_DEBUG, "P2P GO operating in unsafe frequency %d MHz",
-			freq);
-		/* TODO: Consider using CSA or removing the group within
-		 * wpa_supplicant */
-		wpa_msg(ifs, MSG_INFO, P2P_EVENT_REMOVE_AND_REFORM_GROUP);
+		/*
+		 * 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 */
 
@@ -3209,6 +3367,7 @@
 	if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED &&
 	    event != EVENT_INTERFACE_ENABLED &&
 	    event != EVENT_INTERFACE_STATUS &&
+	    event != EVENT_SCAN_RESULTS &&
 	    event != EVENT_SCHED_SCAN_STOPPED) {
 		wpa_dbg(wpa_s, MSG_DEBUG,
 			"Ignore event %s (%d) while interface is disabled",
@@ -3237,12 +3396,23 @@
 
 	switch (event) {
 	case EVENT_AUTH:
+#ifdef CONFIG_FST
+		if (!wpas_fst_update_mbie(wpa_s, data->auth.ies,
+					  data->auth.ies_len))
+			wpa_printf(MSG_DEBUG,
+				   "FST: MB IEs updated from auth IE");
+#endif /* CONFIG_FST */
 		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);
+		if (data) {
+			wpa_msg(wpa_s, MSG_INFO,
+				WPA_EVENT_SUBNET_STATUS_UPDATE "status=%u",
+				data->assoc_info.subnet_status);
+		}
 		break;
 	case EVENT_DISASSOC:
 		wpas_event_disassoc(wpa_s,
@@ -3257,10 +3427,11 @@
 		break;
 #ifndef CONFIG_NO_SCAN_PROCESSING
 	case EVENT_SCAN_STARTED:
-		os_get_reltime(&wpa_s->scan_start_time);
-		if (wpa_s->own_scan_requested) {
+		if (wpa_s->own_scan_requested ||
+		    (data && !data->scan_info.external_scan)) {
 			struct os_reltime diff;
 
+			os_get_reltime(&wpa_s->scan_start_time);
 			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",
@@ -3283,7 +3454,16 @@
 		}
 		break;
 	case EVENT_SCAN_RESULTS:
-		if (os_reltime_initialized(&wpa_s->scan_start_time)) {
+		if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED) {
+			wpa_s->scan_res_handler = NULL;
+			wpa_s->own_scan_running = 0;
+			wpa_s->radio->external_scan_running = 0;
+			wpa_s->last_scan_req = NORMAL_SCAN_REQ;
+			break;
+		}
+
+		if (!(data && data->scan_info.external_scan) &&
+		    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);
@@ -3294,8 +3474,10 @@
 		}
 		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;
+		if (!(data && data->scan_info.external_scan))
+			wpa_s->own_scan_running = 0;
+		if (data && data->scan_info.nl_scan_event)
+			wpa_s->radio->external_scan_running = 0;
 		radio_work_check_next(wpa_s);
 		break;
 #endif /* CONFIG_NO_SCAN_PROCESSING */
@@ -3343,6 +3525,8 @@
 			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 {
@@ -3451,20 +3635,25 @@
 				       data->rx_from_unknown.wds);
 		break;
 	case EVENT_CH_SWITCH:
-		if (!data)
+		if (!data || !wpa_s->current_ssid)
 			break;
-		if (!wpa_s->ap_iface) {
-			wpa_dbg(wpa_s, MSG_DEBUG, "AP: Ignore channel switch "
-				"event in non-AP mode");
-			break;
+
+		wpa_s->assoc_freq = data->ch_switch.freq;
+		wpa_s->current_ssid->frequency = data->ch_switch.freq;
+
+		if (wpa_s->current_ssid->mode == WPAS_MODE_AP ||
+		    wpa_s->current_ssid->mode == WPAS_MODE_P2P_GO ||
+		    wpa_s->current_ssid->mode ==
+		    WPAS_MODE_P2P_GROUP_FORMATION) {
+			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);
 		}
 
-		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);
+		wpas_p2p_update_channel_list(wpa_s, WPAS_P2P_CHANNEL_UPDATE_CS);
 		break;
 #ifdef NEED_AP_MLME
 	case EVENT_DFS_RADAR_DETECTED:
@@ -3521,12 +3710,14 @@
 #endif /* CONFIG_AP */
 #ifdef CONFIG_P2P
 			if (stype == WLAN_FC_STYPE_PROBE_REQ &&
-			    data->rx_mgmt.frame_len > 24) {
+			    data->rx_mgmt.frame_len > IEEE80211_HDRLEN) {
 				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);
+				const u8 *ie;
+				size_t ie_len;
+
+				ie = data->rx_mgmt.frame + IEEE80211_HDRLEN;
+				ie_len = data->rx_mgmt.frame_len -
+					IEEE80211_HDRLEN;
 				wpas_p2p_probe_req_rx(
 					wpa_s, src, mgmt->da,
 					mgmt->bssid, ie, ie_len,
@@ -3566,11 +3757,12 @@
 		}
 
 		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);
+		    data->rx_mgmt.frame_len > IEEE80211_HDRLEN) {
+			const u8 *ie;
+			size_t ie_len;
+
+			ie = data->rx_mgmt.frame + IEEE80211_HDRLEN;
+			ie_len = data->rx_mgmt.frame_len - IEEE80211_HDRLEN;
 
 			wpas_notify_preq(wpa_s, mgmt->sa, mgmt->da,
 					 mgmt->bssid, ie, ie_len,
@@ -3771,9 +3963,8 @@
 					 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;
+		resched = wpa_s->scanning && wpas_scan_scheduled(wpa_s);
 		wpa_supplicant_notify_scanning(wpa_s, 0);
 
 		if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED)
@@ -3820,8 +4011,64 @@
 				     data->mesh_peer.ie_len);
 #endif /* CONFIG_MESH */
 		break;
+	case EVENT_SURVEY:
+#ifdef CONFIG_AP
+		if (!wpa_s->ap_iface)
+			break;
+		hostapd_event_get_survey(wpa_s->ap_iface,
+					 &data->survey_results);
+#endif /* CONFIG_AP */
+		break;
+	case EVENT_ACS_CHANNEL_SELECTED:
+#ifdef CONFIG_ACS
+		if (!wpa_s->ap_iface)
+			break;
+		hostapd_acs_channel_selected(wpa_s->ap_iface->bss[0],
+					     &data->acs_selected_channels);
+#endif /* CONFIG_ACS */
+		break;
 	default:
 		wpa_msg(wpa_s, MSG_INFO, "Unknown event %d", event);
 		break;
 	}
 }
+
+
+void wpa_supplicant_event_global(void *ctx, enum wpa_event_type event,
+				 union wpa_event_data *data)
+{
+	struct wpa_supplicant *wpa_s;
+
+	if (event != EVENT_INTERFACE_STATUS)
+		return;
+
+	wpa_s = wpa_supplicant_get_iface(ctx, data->interface_status.ifname);
+	if (wpa_s && wpa_s->driver->get_ifindex) {
+		unsigned int ifindex;
+
+		ifindex = wpa_s->driver->get_ifindex(wpa_s->drv_priv);
+		if (ifindex != data->interface_status.ifindex) {
+			wpa_dbg(wpa_s, MSG_DEBUG,
+				"interface status ifindex %d mismatch (%d)",
+				ifindex, data->interface_status.ifindex);
+			return;
+		}
+	}
+#ifdef CONFIG_MATCH_IFACE
+	else if (data->interface_status.ievent == EVENT_INTERFACE_ADDED) {
+		struct wpa_interface *wpa_i;
+
+		wpa_i = wpa_supplicant_match_iface(
+			ctx, data->interface_status.ifname);
+		if (!wpa_i)
+			return;
+		wpa_s = wpa_supplicant_add_iface(ctx, wpa_i, NULL);
+		os_free(wpa_i);
+		if (wpa_s)
+			wpa_s->matched = 1;
+	}
+#endif /* CONFIG_MATCH_IFACE */
+
+	if (wpa_s)
+		wpa_supplicant_event(wpa_s, event, data);
+}
diff --git a/wpa_supplicant/gas_query.c b/wpa_supplicant/gas_query.c
index 10ecce7..4f0d0e6 100644
--- a/wpa_supplicant/gas_query.c
+++ b/wpa_supplicant/gas_query.c
@@ -25,6 +25,9 @@
 /** GAS query timeout in seconds */
 #define GAS_QUERY_TIMEOUT_PERIOD 2
 
+/* GAS query wait-time / duration in ms */
+#define GAS_QUERY_WAIT_TIME_INITIAL 1000
+#define GAS_QUERY_WAIT_TIME_COMEBACK 150
 
 /**
  * struct gas_query_pending - Pending GAS query
@@ -37,6 +40,7 @@
 	u8 next_frag_id;
 	unsigned int wait_comeback:1;
 	unsigned int offchannel_tx_started:1;
+	unsigned int retry:1;
 	int freq;
 	u16 status_code;
 	struct wpabuf *req;
@@ -63,6 +67,10 @@
 
 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 void gas_query_rx_comeback_timeout(void *eloop_data, void *user_ctx);
+static void gas_query_tx_initial_req(struct gas_query *gas,
+				     struct gas_query_pending *query);
+static int gas_query_new_dialog_token(struct gas_query *gas, const u8 *dst);
 
 
 static int ms_from_time(struct os_reltime *last)
@@ -151,6 +159,7 @@
 		offchannel_send_action_done(gas->wpa_s);
 	eloop_cancel_timeout(gas_query_tx_comeback_timeout, gas, query);
 	eloop_cancel_timeout(gas_query_timeout, gas, query);
+	eloop_cancel_timeout(gas_query_rx_comeback_timeout, gas, query);
 	dl_list_del(&query->list);
 	query->cb(query->ctx, query->addr, query->dialog_token, result,
 		  query->adv_proto, query->resp, query->status_code);
@@ -235,6 +244,13 @@
 		eloop_cancel_timeout(gas_query_timeout, gas, query);
 		eloop_register_timeout(GAS_QUERY_TIMEOUT_PERIOD, 0,
 				       gas_query_timeout, gas, query);
+		if (query->wait_comeback && !query->retry) {
+			eloop_cancel_timeout(gas_query_rx_comeback_timeout,
+					     gas, query);
+			eloop_register_timeout(
+				0, (GAS_QUERY_WAIT_TIME_COMEBACK + 10) * 1000,
+				gas_query_rx_comeback_timeout, gas, query);
+		}
 	}
 	if (result == OFFCHANNEL_SEND_ACTION_FAILED) {
 		eloop_cancel_timeout(gas_query_timeout, gas, query);
@@ -254,9 +270,8 @@
 
 
 static int gas_query_tx(struct gas_query *gas, struct gas_query_pending *query,
-			struct wpabuf *req)
+			struct wpabuf *req, unsigned int wait_time)
 {
-	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 "
@@ -267,7 +282,6 @@
 		*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;
@@ -285,6 +299,7 @@
 				      struct gas_query_pending *query)
 {
 	struct wpabuf *req;
+	unsigned int wait_time;
 
 	req = gas_build_comeback_req(query->dialog_token);
 	if (req == NULL) {
@@ -292,7 +307,10 @@
 		return;
 	}
 
-	if (gas_query_tx(gas, query, req) < 0) {
+	wait_time = (query->retry || !query->offchannel_tx_started) ?
+		GAS_QUERY_WAIT_TIME_INITIAL : GAS_QUERY_WAIT_TIME_COMEBACK;
+
+	if (gas_query_tx(gas, query, req, wait_time) < 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);
@@ -302,6 +320,35 @@
 }
 
 
+static void gas_query_rx_comeback_timeout(void *eloop_data, void *user_ctx)
+{
+	struct gas_query *gas = eloop_data;
+	struct gas_query_pending *query = user_ctx;
+	int dialog_token;
+
+	wpa_printf(MSG_DEBUG,
+		   "GAS: No response to comeback request received (retry=%u)",
+		   query->retry);
+	if (gas->current != query || query->retry)
+		return;
+	dialog_token = gas_query_new_dialog_token(gas, query->addr);
+	if (dialog_token < 0)
+		return;
+	wpa_printf(MSG_DEBUG,
+		   "GAS: Retry GAS query due to comeback response timeout");
+	query->retry = 1;
+	query->dialog_token = dialog_token;
+	*(wpabuf_mhead_u8(query->req) + 2) = dialog_token;
+	query->wait_comeback = 0;
+	query->next_frag_id = 0;
+	wpabuf_free(query->adv_proto);
+	query->adv_proto = NULL;
+	eloop_cancel_timeout(gas_query_tx_comeback_timeout, gas, query);
+	eloop_cancel_timeout(gas_query_timeout, gas, query);
+	gas_query_tx_initial_req(gas, query);
+}
+
+
 static void gas_query_tx_comeback_timeout(void *eloop_data, void *user_ctx)
 {
 	struct gas_query *gas = eloop_data;
@@ -319,6 +366,11 @@
 {
 	unsigned int secs, usecs;
 
+	if (comeback_delay > 1 && query->offchannel_tx_started) {
+		offchannel_send_action_done(gas->wpa_s);
+		query->offchannel_tx_started = 0;
+	}
+
 	secs = (comeback_delay * 1024) / 1000000;
 	usecs = comeback_delay * 1024 - secs * 1000000;
 	wpa_printf(MSG_DEBUG, "GAS: Send comeback request to " MACSTR
@@ -371,6 +423,7 @@
 		   "comeback_delay=%u)",
 		   MAC2STR(query->addr), query->dialog_token, frag_id,
 		   more_frags, comeback_delay);
+	eloop_cancel_timeout(gas_query_rx_comeback_timeout, gas, query);
 
 	if ((size_t) 2 + adv_proto[1] != wpabuf_len(query->adv_proto) ||
 	    os_memcmp(adv_proto, wpabuf_head(query->adv_proto),
@@ -448,7 +501,7 @@
 		return -1;
 
 	prot = categ == WLAN_ACTION_PROTECTED_DUAL;
-	pmf = pmf_in_use(gas->wpa_s, bssid);
+	pmf = pmf_in_use(gas->wpa_s, sa);
 	if (prot && !pmf) {
 		wpa_printf(MSG_DEBUG, "GAS: Drop unexpected protected GAS frame when PMF is disabled");
 		return 0;
@@ -620,8 +673,15 @@
 	}
 
 	gas->work = work;
+	gas_query_tx_initial_req(gas, query);
+}
 
-	if (gas_query_tx(gas, query, query->req) < 0) {
+
+static void gas_query_tx_initial_req(struct gas_query *gas,
+				     struct gas_query_pending *query)
+{
+	if (gas_query_tx(gas, query, query->req,
+			 GAS_QUERY_WAIT_TIME_INITIAL) < 0) {
 		wpa_printf(MSG_DEBUG, "GAS: Failed to send Action frame to "
 			   MACSTR, MAC2STR(query->addr));
 		gas_query_free(query, 1);
@@ -633,7 +693,24 @@
 		   query->dialog_token);
 	eloop_register_timeout(GAS_QUERY_TIMEOUT_PERIOD, 0,
 			       gas_query_timeout, gas, query);
+}
 
+
+static int gas_query_new_dialog_token(struct gas_query *gas, const u8 *dst)
+{
+	static int next_start = 0;
+	int dialog_token;
+
+	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;
+	return dialog_token;
 }
 
 
@@ -658,20 +735,13 @@
 {
 	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;
+	dialog_token = gas_query_new_dialog_token(gas, dst);
+	if (dialog_token < 0)
+		return -1;
 
 	query = os_zalloc(sizeof(*query));
 	if (query == NULL)
diff --git a/wpa_supplicant/hs20_supplicant.c b/wpa_supplicant/hs20_supplicant.c
index a1afc85..3128fcb 100644
--- a/wpa_supplicant/hs20_supplicant.c
+++ b/wpa_supplicant/hs20_supplicant.c
@@ -25,6 +25,7 @@
 #include "gas_query.h"
 #include "interworking.h"
 #include "hs20_supplicant.h"
+#include "base64.h"
 
 
 #define OSU_MAX_ITEMS 10
@@ -180,13 +181,14 @@
 
 
 int hs20_anqp_send_req(struct wpa_supplicant *wpa_s, const u8 *dst, u32 stypes,
-		       const u8 *payload, size_t payload_len)
+		       const u8 *payload, size_t payload_len, int inmem)
 {
 	struct wpabuf *buf;
 	int ret = 0;
 	int freq;
 	struct wpa_bss *bss;
 	int res;
+	struct icon_entry *icon_entry;
 
 	bss = wpa_bss_get_bssid(wpa_s, dst);
 	if (!bss) {
@@ -210,15 +212,127 @@
 	if (res < 0) {
 		wpa_printf(MSG_DEBUG, "ANQP: Failed to send Query Request");
 		wpabuf_free(buf);
-		ret = -1;
+		return -1;
 	} else
 		wpa_printf(MSG_DEBUG, "ANQP: Query started with dialog token "
 			   "%u", res);
 
+	if (inmem) {
+		icon_entry = os_zalloc(sizeof(struct icon_entry));
+		if (!icon_entry)
+			return -1;
+		os_memcpy(icon_entry->bssid, dst, ETH_ALEN);
+		icon_entry->file_name = os_malloc(payload_len + 1);
+		if (!icon_entry->file_name) {
+			os_free(icon_entry);
+			return -1;
+		}
+		os_memcpy(icon_entry->file_name, payload, payload_len);
+		icon_entry->file_name[payload_len] = '\0';
+		icon_entry->dialog_token = res;
+
+		dl_list_add(&wpa_s->icon_head, &icon_entry->list);
+	}
+
 	return ret;
 }
 
 
+static struct icon_entry * hs20_find_icon(struct wpa_supplicant *wpa_s,
+					  const u8 *bssid,
+					  const char *file_name)
+{
+	struct icon_entry *icon;
+
+	dl_list_for_each(icon, &wpa_s->icon_head, struct icon_entry, list) {
+		if (os_memcmp(icon->bssid, bssid, ETH_ALEN) == 0 &&
+		    os_strcmp(icon->file_name, file_name) == 0 && icon->image)
+			return icon;
+	}
+
+	return NULL;
+}
+
+
+int hs20_get_icon(struct wpa_supplicant *wpa_s, const u8 *bssid,
+		  const char *file_name, size_t offset, size_t size,
+		  char *reply, size_t buf_len)
+{
+	struct icon_entry *icon;
+	size_t out_size;
+	unsigned char *b64;
+	size_t b64_size;
+	int reply_size;
+
+	wpa_printf(MSG_DEBUG, "HS20: Get icon " MACSTR " %s @ %u +%u (%u)",
+		   MAC2STR(bssid), file_name, (unsigned int) offset,
+		   (unsigned int) size, (unsigned int) buf_len);
+
+	icon = hs20_find_icon(wpa_s, bssid, file_name);
+	if (!icon || !icon->image || offset >= icon->image_len)
+		return -1;
+	if (size > icon->image_len - offset)
+		size = icon->image_len - offset;
+	out_size = buf_len - 3 /* max base64 padding */;
+	if (size * 4 > out_size * 3)
+		size = out_size * 3 / 4;
+	if (size == 0)
+		return -1;
+
+	b64 = base64_encode(&icon->image[offset], size, &b64_size);
+	if (buf_len >= b64_size) {
+		os_memcpy(reply, b64, b64_size);
+		reply_size = b64_size;
+	} else {
+		reply_size = -1;
+	}
+	os_free(b64);
+	return reply_size;
+}
+
+
+static void hs20_free_icon_entry(struct icon_entry *icon)
+{
+	wpa_printf(MSG_DEBUG, "HS20: Free stored icon from " MACSTR
+		   " dialog_token=%u file_name=%s image_len=%u",
+		   MAC2STR(icon->bssid), icon->dialog_token,
+		   icon->file_name ? icon->file_name : "N/A",
+		   (unsigned int) icon->image_len);
+	os_free(icon->file_name);
+	os_free(icon->image);
+	os_free(icon);
+}
+
+
+int hs20_del_icon(struct wpa_supplicant *wpa_s, const u8 *bssid,
+		  const char *file_name)
+{
+	struct icon_entry *icon, *tmp;
+	int count = 0;
+
+	if (!bssid)
+		wpa_printf(MSG_DEBUG, "HS20: Delete all stored icons");
+	else if (!file_name)
+		wpa_printf(MSG_DEBUG, "HS20: Delete all stored icons for "
+			   MACSTR, MAC2STR(bssid));
+	else
+		wpa_printf(MSG_DEBUG, "HS20: Delete stored icons for "
+			   MACSTR " file name %s", MAC2STR(bssid), file_name);
+
+	dl_list_for_each_safe(icon, tmp, &wpa_s->icon_head, struct icon_entry,
+			      list) {
+		if ((!bssid || os_memcmp(icon->bssid, bssid, ETH_ALEN) == 0) &&
+		    (!file_name ||
+		     os_strcmp(icon->file_name, file_name) == 0)) {
+			dl_list_del(&icon->list);
+			hs20_free_icon_entry(icon);
+			count++;
+		}
+	}
+	return count == 0 ? -1 : 0;
+}
+
+
 static void hs20_set_osu_access_permission(const char *osu_dir,
 					   const char *fname)
 {
@@ -243,14 +357,51 @@
 	}
 }
 
+
+static void hs20_remove_duplicate_icons(struct wpa_supplicant *wpa_s,
+					struct icon_entry *new_icon)
+{
+	struct icon_entry *icon, *tmp;
+
+	dl_list_for_each_safe(icon, tmp, &wpa_s->icon_head, struct icon_entry,
+			      list) {
+		if (icon == new_icon)
+			continue;
+		if (os_memcmp(icon->bssid, new_icon->bssid, ETH_ALEN) == 0 &&
+		    os_strcmp(icon->file_name, new_icon->file_name) == 0) {
+			dl_list_del(&icon->list);
+			hs20_free_icon_entry(icon);
+		}
+	}
+}
+
+
 static int hs20_process_icon_binary_file(struct wpa_supplicant *wpa_s,
 					 const u8 *sa, const u8 *pos,
-					 size_t slen)
+					 size_t slen, u8 dialog_token)
 {
 	char fname[256];
 	int png;
 	FILE *f;
 	u16 data_len;
+	struct icon_entry *icon;
+
+	dl_list_for_each(icon, &wpa_s->icon_head, struct icon_entry, list) {
+		if (icon->dialog_token == dialog_token && !icon->image &&
+		    os_memcmp(icon->bssid, sa, ETH_ALEN) == 0) {
+			icon->image = os_malloc(slen);
+			if (!icon->image)
+				return -1;
+			os_memcpy(icon->image, pos, slen);
+			icon->image_len = slen;
+			hs20_remove_duplicate_icons(wpa_s, icon);
+			wpa_msg(wpa_s, MSG_INFO,
+				"RX-HS20-ICON " MACSTR " %s %u",
+				MAC2STR(sa), icon->file_name,
+				(unsigned int) icon->image_len);
+			return 0;
+		}
+	}
 
 	wpa_msg(wpa_s, MSG_INFO, "RX-HS20-ANQP " MACSTR " Icon Binary File",
 		MAC2STR(sa));
@@ -358,7 +509,7 @@
 
 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 *data, size_t slen, u8 dialog_token)
 {
 	const u8 *pos = data;
 	u8 subtype;
@@ -445,7 +596,8 @@
 		}
 		break;
 	case HS20_STYPE_ICON_BINARY_FILE:
-		ret = hs20_process_icon_binary_file(wpa_s, sa, pos, slen);
+		ret = hs20_process_icon_binary_file(wpa_s, sa, pos, slen,
+						    dialog_token);
 		if (wpa_s->fetch_osu_icon_in_progress) {
 			hs20_osu_icon_fetch_result(wpa_s, ret);
 			eloop_cancel_timeout(hs20_continue_icon_fetch,
@@ -512,6 +664,7 @@
 	f = fopen(fname, "w");
 	if (f == NULL) {
 		hs20_free_osu_prov(wpa_s);
+		wpa_s->fetch_anqp_in_progress = 0;
 		return;
 	}
 
@@ -579,7 +732,8 @@
 			if (hs20_anqp_send_req(wpa_s, osu->bssid,
 					       BIT(HS20_STYPE_ICON_REQUEST),
 					       (u8 *) icon->filename,
-					       os_strlen(icon->filename)) < 0) {
+					       os_strlen(icon->filename),
+					       0) < 0) {
 				icon->failed = 1;
 				continue;
 			}
@@ -617,7 +771,7 @@
 	prov->osu_ssid_len = osu_ssid_len;
 
 	/* OSU Friendly Name Length */
-	if (pos + 2 > end) {
+	if (end - pos < 2) {
 		wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for OSU "
 			   "Friendly Name Length");
 		return;
@@ -633,9 +787,9 @@
 	pos += len2;
 
 	/* OSU Friendly Name Duples */
-	while (pos2 + 4 <= pos && prov->friendly_name_count < OSU_MAX_ITEMS) {
+	while (pos - pos2 >= 4 && prov->friendly_name_count < OSU_MAX_ITEMS) {
 		struct osu_lang_string *f;
-		if (pos2 + 1 + pos2[0] > pos || pos2[0] < 3) {
+		if (1 + pos2[0] > pos - pos2 || pos2[0] < 3) {
 			wpa_printf(MSG_DEBUG, "Invalid OSU Friendly Name");
 			break;
 		}
@@ -646,7 +800,7 @@
 	}
 
 	/* OSU Server URI */
-	if (pos + 1 > end) {
+	if (end - pos < 1) {
 		wpa_printf(MSG_DEBUG,
 			   "HS 2.0: Not enough room for OSU Server URI length");
 		return;
@@ -661,7 +815,7 @@
 	pos += uri_len;
 
 	/* OSU Method list */
-	if (pos + 1 > end) {
+	if (end - pos < 1) {
 		wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for OSU Method "
 			   "list length");
 		return;
@@ -681,7 +835,7 @@
 	}
 
 	/* Icons Available Length */
-	if (pos + 2 > end) {
+	if (end - pos < 2) {
 		wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for Icons "
 			   "Available Length");
 		return;
@@ -701,7 +855,7 @@
 		struct osu_icon *icon = &prov->icon[prov->icon_count];
 		u8 flen;
 
-		if (pos2 + 2 + 2 + 3 + 1 + 1 > pos) {
+		if (2 + 2 + 3 + 1 + 1 > pos - pos2) {
 			wpa_printf(MSG_DEBUG, "HS 2.0: Invalid Icon Metadata");
 			break;
 		}
@@ -713,46 +867,46 @@
 		os_memcpy(icon->lang, pos2, 3);
 		pos2 += 3;
 
-		flen = pos2[0];
-		if (flen > pos - pos2 - 1) {
+		flen = *pos2++;
+		if (flen > pos - pos2) {
 			wpa_printf(MSG_DEBUG, "HS 2.0: Not room for Icon Type");
 			break;
 		}
-		os_memcpy(icon->icon_type, pos2 + 1, flen);
-		pos2 += 1 + flen;
+		os_memcpy(icon->icon_type, pos2, flen);
+		pos2 += flen;
 
-		if (pos2 + 1 > pos) {
+		if (pos - pos2 < 1) {
 			wpa_printf(MSG_DEBUG, "HS 2.0: Not room for Icon "
 				   "Filename length");
 			break;
 		}
-		flen = pos2[0];
-		if (flen > pos - pos2 - 1) {
+		flen = *pos2++;
+		if (flen > pos - pos2) {
 			wpa_printf(MSG_DEBUG, "HS 2.0: Not room for Icon "
 				   "Filename");
 			break;
 		}
-		os_memcpy(icon->filename, pos2 + 1, flen);
-		pos2 += 1 + flen;
+		os_memcpy(icon->filename, pos2, flen);
+		pos2 += flen;
 
 		prov->icon_count++;
 	}
 
 	/* OSU_NAI */
-	if (pos + 1 > end) {
+	if (end - pos < 1) {
 		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) {
+	osu_nai_len = *pos++;
+	if (osu_nai_len > end - pos) {
 		wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for OSU_NAI");
 		return;
 	}
-	os_memcpy(prov->osu_nai, pos + 1, osu_nai_len);
-	pos += 1 + osu_nai_len;
+	os_memcpy(prov->osu_nai, pos, osu_nai_len);
+	pos += osu_nai_len;
 
 	/* OSU Service Description Length */
-	if (pos + 2 > end) {
+	if (end - pos < 2) {
 		wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for OSU "
 			   "Service Description Length");
 		return;
@@ -768,20 +922,20 @@
 	pos += len2;
 
 	/* OSU Service Description Duples */
-	while (pos2 + 4 <= pos && prov->serv_desc_count < OSU_MAX_ITEMS) {
+	while (pos - pos2 >= 4 && 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) {
+		descr_len = *pos2++;
+		if (descr_len > pos - pos2 || descr_len < 3) {
 			wpa_printf(MSG_DEBUG, "Invalid OSU Service "
 				   "Description");
 			break;
 		}
 		f = &prov->serv_desc[prov->serv_desc_count++];
-		os_memcpy(f->lang, pos2 + 1, 3);
-		os_memcpy(f->text, pos2 + 1 + 3, descr_len - 3);
-		pos2 += 1 + descr_len;
+		os_memcpy(f->lang, pos2, 3);
+		os_memcpy(f->text, pos2 + 3, descr_len - 3);
+		pos2 += descr_len;
 	}
 
 	wpa_printf(MSG_DEBUG, "HS 2.0: Added OSU Provider through " MACSTR,
@@ -816,9 +970,9 @@
 		end = pos + wpabuf_len(prov_anqp);
 
 		/* OSU SSID */
-		if (pos + 1 > end)
+		if (end - pos < 1)
 			continue;
-		if (pos + 1 + pos[0] > end) {
+		if (1 + pos[0] > end - pos) {
 			wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for "
 				   "OSU SSID");
 			continue;
@@ -832,7 +986,7 @@
 		osu_ssid = pos;
 		pos += osu_ssid_len;
 
-		if (pos + 1 > end) {
+		if (end - pos < 1) {
 			wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for "
 				   "Number of OSU Providers");
 			continue;
@@ -842,7 +996,7 @@
 			   num_providers);
 
 		/* OSU Providers */
-		while (pos + 2 < end && num_providers > 0) {
+		while (end - pos > 2 && num_providers > 0) {
 			num_providers--;
 			len = WPA_GET_LE16(pos);
 			pos += 2;
@@ -1002,8 +1156,16 @@
 }
 
 
+void hs20_init(struct wpa_supplicant *wpa_s)
+{
+	dl_list_init(&wpa_s->icon_head);
+}
+
+
 void hs20_deinit(struct wpa_supplicant *wpa_s)
 {
 	eloop_cancel_timeout(hs20_continue_icon_fetch, wpa_s, NULL);
 	hs20_free_osu_prov(wpa_s);
+	if (wpa_s->icon_head.next)
+		hs20_del_icon(wpa_s, NULL, NULL);
 }
diff --git a/wpa_supplicant/hs20_supplicant.h b/wpa_supplicant/hs20_supplicant.h
index 85b5120..9fc654c 100644
--- a/wpa_supplicant/hs20_supplicant.h
+++ b/wpa_supplicant/hs20_supplicant.h
@@ -11,14 +11,14 @@
 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);
+		       const u8 *payload, size_t payload_len, int inmem);
 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);
+				  const u8 *data, size_t slen, u8 dialog_token);
 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);
@@ -36,6 +36,12 @@
 void hs20_cancel_fetch_osu(struct wpa_supplicant *wpa_s);
 void hs20_icon_fetch_failed(struct wpa_supplicant *wpa_s);
 void hs20_start_osu_scan(struct wpa_supplicant *wpa_s);
+void hs20_init(struct wpa_supplicant *wpa_s);
 void hs20_deinit(struct wpa_supplicant *wpa_s);
+int hs20_get_icon(struct wpa_supplicant *wpa_s, const u8 *bssid,
+		  const char *file_name, size_t offset, size_t size,
+		  char *reply, size_t buf_len);
+int hs20_del_icon(struct wpa_supplicant *wpa_s, const u8 *bssid,
+		  const char *file_name);
 
 #endif /* HS20_SUPPLICANT_H */
diff --git a/wpa_supplicant/ibss_rsn.c b/wpa_supplicant/ibss_rsn.c
index 6299191..c00db31 100644
--- a/wpa_supplicant/ibss_rsn.c
+++ b/wpa_supplicant/ibss_rsn.c
@@ -230,7 +230,7 @@
 	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);
+	wpa_sm_set_pmk(peer->supp, psk, PMK_LEN, NULL, NULL);
 
 	peer->supp_ie_len = sizeof(peer->supp_ie);
 	if (wpa_sm_set_assoc_wpa_ie_default(peer->supp, peer->supp_ie,
@@ -697,7 +697,8 @@
 		ibss_rsn_free(prev);
 	}
 
-	wpa_deinit(ibss_rsn->auth_group);
+	if (ibss_rsn->auth_group)
+		wpa_deinit(ibss_rsn->auth_group);
 	os_free(ibss_rsn);
 
 }
diff --git a/wpa_supplicant/interworking.c b/wpa_supplicant/interworking.c
index fd47c17..589ee57 100644
--- a/wpa_supplicant/interworking.c
+++ b/wpa_supplicant/interworking.c
@@ -362,13 +362,13 @@
 	u8 elen, auth_count, a;
 	const u8 *e_end;
 
-	if (pos + 3 > end) {
+	if (end - pos < 3) {
 		wpa_printf(MSG_DEBUG, "No room for EAP Method fixed fields");
 		return NULL;
 	}
 
 	elen = *pos++;
-	if (pos + elen > end || elen < 2) {
+	if (elen > end - pos || elen < 2) {
 		wpa_printf(MSG_DEBUG, "No room for EAP Method subfield");
 		return NULL;
 	}
@@ -381,14 +381,19 @@
 	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");
+		if (end - pos < 2) {
+			wpa_printf(MSG_DEBUG,
+				   "No room for Authentication Parameter subfield header");
 			return NULL;
 		}
 
 		id = *pos++;
 		len = *pos++;
+		if (len > end - pos) {
+			wpa_printf(MSG_DEBUG,
+				   "No room for Authentication Parameter subfield");
+			return NULL;
+		}
 
 		switch (id) {
 		case NAI_REALM_EAP_AUTH_NON_EAP_INNER_AUTH:
@@ -463,7 +468,7 @@
 
 	len = WPA_GET_LE16(pos); /* NAI Realm Data field Length */
 	pos += 2;
-	if (pos + len > end || len < 3) {
+	if (len > end - pos || len < 3) {
 		wpa_printf(MSG_DEBUG, "No room for NAI Realm Data "
 			   "(len=%u; left=%u)",
 			   len, (unsigned int) (end - pos));
@@ -473,7 +478,7 @@
 
 	r->encoding = *pos++;
 	realm_len = *pos++;
-	if (pos + realm_len > f_end) {
+	if (realm_len > f_end - pos) {
 		wpa_printf(MSG_DEBUG, "No room for NAI Realm "
 			   "(len=%u; left=%u)",
 			   realm_len, (unsigned int) (f_end - pos));
@@ -485,13 +490,13 @@
 		return NULL;
 	pos += realm_len;
 
-	if (pos + 1 > f_end) {
+	if (f_end - pos < 1) {
 		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) {
+	if (r->eap_count * 3 > f_end - pos) {
 		wpa_printf(MSG_DEBUG, "No room for EAP Methods");
 		return NULL;
 	}
@@ -746,7 +751,7 @@
 		return 0;
 	pos = wpabuf_head_u8(anqp);
 	end = pos + wpabuf_len(anqp);
-	if (pos + 2 > end)
+	if (end - pos < 2)
 		return 0;
 	if (*pos != 0) {
 		wpa_printf(MSG_DEBUG, "Unsupported GUD version 0x%x", *pos);
@@ -754,7 +759,7 @@
 	}
 	pos++;
 	udhl = *pos++;
-	if (pos + udhl > end) {
+	if (udhl > end - pos) {
 		wpa_printf(MSG_DEBUG, "Invalid UDHL");
 		return 0;
 	}
@@ -764,12 +769,12 @@
 		   plmn[0], plmn[1], plmn[2], plmn2[0], plmn2[1], plmn2[2],
 		   imsi, mnc_len);
 
-	while (pos + 2 <= end) {
+	while (end - pos >= 2) {
 		u8 iei, len;
 		const u8 *l_end;
 		iei = *pos++;
 		len = *pos++ & 0x7f;
-		if (pos + len > end)
+		if (len > end - pos)
 			break;
 		l_end = pos + len;
 
@@ -780,7 +785,7 @@
 				    pos, len);
 			num = *pos++;
 			for (i = 0; i < num; i++) {
-				if (pos + 3 > l_end)
+				if (l_end - pos < 3)
 					break;
 				if (os_memcmp(pos, plmn, 3) == 0 ||
 				    os_memcmp(pos, plmn2, 3) == 0)
@@ -1082,12 +1087,12 @@
 	 * OI #1, [OI #2], [OI #3]
 	 */
 
-	if (pos + 2 > end)
+	if (end - pos < 2)
 		return 0;
 
 	pos++; /* skip Number of ANQP OIs */
 	lens = *pos++;
-	if (pos + (lens & 0x0f) + (lens >> 4) > end)
+	if ((lens & 0x0f) + (lens >> 4) > end - pos)
 		return 0;
 
 	if ((lens & 0x0f) == rc_len && os_memcmp(pos, rc_id, rc_len) == 0)
@@ -1121,7 +1126,7 @@
 	/* Set of <OI Length, OI> duples */
 	while (pos < end) {
 		len = *pos++;
-		if (pos + len > end)
+		if (len > end - pos)
 			break;
 		if (len == rc_len && os_memcmp(pos, rc_id, rc_len) == 0)
 			return 1;
@@ -1182,6 +1187,7 @@
 static int cred_below_min_backhaul(struct wpa_supplicant *wpa_s,
 				   struct wpa_cred *cred, struct wpa_bss *bss)
 {
+#ifdef CONFIG_HS20
 	int res;
 	unsigned int dl_bandwidth, ul_bandwidth;
 	const u8 *wan;
@@ -1233,6 +1239,7 @@
 		if (cred->min_ul_bandwidth_roaming > ul_bandwidth)
 			return 1;
 	}
+#endif /* CONFIG_HS20 */
 
 	return 0;
 }
@@ -1260,9 +1267,11 @@
 }
 
 
+#ifdef CONFIG_HS20
+
 static int has_proto_match(const u8 *pos, const u8 *end, u8 proto)
 {
-	while (pos + 4 <= end) {
+	while (end - pos >= 4) {
 		if (pos[0] == proto && pos[3] == 1 /* Open */)
 			return 1;
 		pos += 4;
@@ -1275,7 +1284,7 @@
 static int has_proto_port_match(const u8 *pos, const u8 *end, u8 proto,
 				u16 port)
 {
-	while (pos + 4 <= end) {
+	while (end - pos >= 4) {
 		if (pos[0] == proto && WPA_GET_LE16(&pos[1]) == port &&
 		    pos[3] == 1 /* Open */)
 			return 1;
@@ -1285,10 +1294,13 @@
 	return 0;
 }
 
+#endif /* CONFIG_HS20 */
+
 
 static int cred_conn_capab_missing(struct wpa_supplicant *wpa_s,
 				   struct wpa_cred *cred, struct wpa_bss *bss)
 {
+#ifdef CONFIG_HS20
 	int res;
 	const u8 *capab, *end;
 	unsigned int i, j;
@@ -1325,6 +1337,7 @@
 			}
 		}
 	}
+#endif /* CONFIG_HS20 */
 
 	return 0;
 }
@@ -1438,7 +1451,24 @@
 		os_free(anon);
 	}
 
-	if (cred->username && cred->username[0] &&
+	if (!ttls && cred->username && cred->username[0] && cred->realm &&
+	    !os_strchr(cred->username, '@')) {
+		char *id;
+		size_t buflen;
+		int res;
+
+		buflen = os_strlen(cred->username) + 1 +
+			os_strlen(cred->realm) + 1;
+
+		id = os_malloc(buflen);
+		if (!id)
+			return -1;
+		os_snprintf(id, buflen, "%s@%s", cred->username, cred->realm);
+		res = wpa_config_set_quoted(ssid, "identity", id);
+		os_free(id);
+		if (res < 0)
+			return -1;
+	} else if (cred->username && cred->username[0] &&
 	    wpa_config_set_quoted(ssid, "identity", cred->username) < 0)
 		return -1;
 
@@ -2125,23 +2155,27 @@
 	pos = wpabuf_head(domain_names);
 	end = pos + wpabuf_len(domain_names);
 
-	while (pos + 1 < end) {
-		if (pos + 1 + pos[0] > end)
+	while (end - pos > 1) {
+		u8 elen;
+
+		elen = *pos++;
+		if (elen > end - pos)
 			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)
+				  pos, elen);
+		if (elen == len &&
+		    os_strncasecmp(domain, (const char *) pos, 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 (!exact_match && elen > len && pos[elen - len - 1] == '.') {
+			const char *ap = (const char *) pos;
+			int offset = elen - len;
+
 			if (os_strncasecmp(domain, ap + offset, len) == 0)
 				return 1;
 		}
 
-		pos += 1 + pos[0];
+		pos += elen;
 	}
 
 	return 0;
@@ -2564,11 +2598,13 @@
 		return;
 	}
 
+#ifdef CONFIG_HS20
 	if (wpa_s->fetch_osu_icon_in_progress) {
 		wpa_printf(MSG_DEBUG, "Interworking: Next icon (in progress)");
 		hs20_next_osu_icon(wpa_s);
 		return;
 	}
+#endif /* CONFIG_HS20 */
 
 	dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
 		if (!(bss->caps & IEEE80211_CAP_ESS))
@@ -2602,6 +2638,7 @@
 	}
 
 	if (found == 0) {
+#ifdef CONFIG_HS20
 		if (wpa_s->fetch_osu_info) {
 			if (wpa_s->num_prov_found == 0 &&
 			    wpa_s->fetch_osu_waiting_scan &&
@@ -2614,6 +2651,7 @@
 			hs20_osu_icon_fetch(wpa_s);
 			return;
 		}
+#endif /* CONFIG_HS20 */
 		wpa_msg(wpa_s, MSG_INFO, "ANQP fetch completed");
 		wpa_s->fetch_anqp_in_progress = 0;
 		if (wpa_s->network_select)
@@ -2716,10 +2754,46 @@
 }
 
 
+static void anqp_add_extra(struct wpa_supplicant *wpa_s,
+			   struct wpa_bss_anqp *anqp, u16 info_id,
+			   const u8 *data, size_t slen)
+{
+	struct wpa_bss_anqp_elem *tmp, *elem = NULL;
+
+	if (!anqp)
+		return;
+
+	dl_list_for_each(tmp, &anqp->anqp_elems, struct wpa_bss_anqp_elem,
+			 list) {
+		if (tmp->infoid == info_id) {
+			elem = tmp;
+			break;
+		}
+	}
+
+	if (!elem) {
+		elem = os_zalloc(sizeof(*elem));
+		if (!elem)
+			return;
+		elem->infoid = info_id;
+		dl_list_add(&anqp->anqp_elems, &elem->list);
+	} else {
+		wpabuf_free(elem->payload);
+	}
+
+	elem->payload = wpabuf_alloc_copy(data, slen);
+	if (!elem->payload) {
+		dl_list_del(&elem->list);
+		os_free(elem);
+	}
+}
+
+
 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 *data, size_t slen,
+					    u8 dialog_token)
 {
 	const u8 *pos = data;
 	struct wpa_bss_anqp *anqp = NULL;
@@ -2829,7 +2903,8 @@
 			switch (type) {
 			case HS20_ANQP_OUI_TYPE:
 				hs20_parse_rx_hs20_anqp_resp(wpa_s, bss, sa,
-							     pos, slen);
+							     pos, slen,
+							     dialog_token);
 				break;
 			default:
 				wpa_msg(wpa_s, MSG_DEBUG,
@@ -2849,6 +2924,7 @@
 	default:
 		wpa_msg(wpa_s, MSG_DEBUG,
 			"Interworking: Unsupported ANQP Info ID %u", info_id);
+		anqp_add_extra(wpa_s, anqp, info_id, data, slen);
 		break;
 	}
 }
@@ -2871,8 +2947,10 @@
 		   " dialog_token=%u result=%d status_code=%u",
 		   MAC2STR(dst), dialog_token, result, status_code);
 	if (result != GAS_QUERY_SUCCESS) {
+#ifdef CONFIG_HS20
 		if (wpa_s->fetch_osu_icon_in_progress)
 			hs20_icon_fetch_failed(wpa_s);
+#endif /* CONFIG_HS20 */
 		anqp_result = "FAILURE";
 		goto out;
 	}
@@ -2882,8 +2960,10 @@
 	    pos[1] < 2 || pos[3] != ACCESS_NETWORK_QUERY_PROTOCOL) {
 		wpa_msg(wpa_s, MSG_DEBUG,
 			"ANQP: Unexpected Advertisement Protocol in response");
+#ifdef CONFIG_HS20
 		if (wpa_s->fetch_osu_icon_in_progress)
 			hs20_icon_fetch_failed(wpa_s);
+#endif /* CONFIG_HS20 */
 		anqp_result = "INVALID_FRAME";
 		goto out;
 	}
@@ -2927,12 +3007,14 @@
 			goto out_parse_done;
 		}
 		interworking_parse_rx_anqp_resp(wpa_s, bss, dst, info_id, pos,
-						slen);
+						slen, dialog_token);
 		pos += slen;
 	}
 
 out_parse_done:
+#ifdef CONFIG_HS20
 	hs20_notify_parse_done(wpa_s);
+#endif /* CONFIG_HS20 */
 out:
 	wpa_msg(wpa_s, MSG_INFO, ANQP_QUERY_DONE "addr=" MACSTR " result=%s",
 		MAC2STR(dst), anqp_result);
diff --git a/wpa_supplicant/libwpa_test.c b/wpa_supplicant/libwpa_test.c
new file mode 100644
index 0000000..e51ab72
--- /dev/null
+++ b/wpa_supplicant/libwpa_test.c
@@ -0,0 +1,32 @@
+/*
+ * libwpa_test - Test program for libwpa_client.* library linking
+ * 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 "includes.h"
+
+#include "common/wpa_ctrl.h"
+
+int main(int argc, char *argv[])
+{
+	struct wpa_ctrl *ctrl;
+
+	ctrl = wpa_ctrl_open("foo");
+	if (!ctrl)
+		return -1;
+	if (wpa_ctrl_attach(ctrl) == 0)
+		wpa_ctrl_detach(ctrl);
+	if (wpa_ctrl_pending(ctrl)) {
+		char buf[10];
+		size_t len;
+
+		len = sizeof(buf);
+		wpa_ctrl_recv(ctrl, buf, &len);
+	}
+	wpa_ctrl_close(ctrl);
+
+	return 0;
+}
diff --git a/wpa_supplicant/main.c b/wpa_supplicant/main.c
index 1c93306..e08c2fd 100644
--- a/wpa_supplicant/main.c
+++ b/wpa_supplicant/main.c
@@ -12,6 +12,7 @@
 #endif /* __linux__ */
 
 #include "common.h"
+#include "fst/fst.h"
 #include "wpa_supplicant_i.h"
 #include "driver_i.h"
 #include "p2p_supplicant.h"
@@ -64,41 +65,44 @@
 	       "  -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");
+	       "  -e = entropy file\n"
 #ifdef CONFIG_DEBUG_FILE
-	printf("  -f = log output to debug file instead of stdout\n");
+	       "  -f = log output to debug file instead of stdout\n"
 #endif /* CONFIG_DEBUG_FILE */
-	printf("  -g = global ctrl_interface\n"
+	       "  -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"
+	       "  -i = interface name\n"
+	       "  -I = additional configuration file\n"
+	       "  -K = include keys (passwords, etc.) in debug output\n"
 	       "  -L = show license (BSD)\n"
+#ifdef CONFIG_P2P
+	       "  -m = Configuration file for the P2P Device interface\n"
+#endif /* CONFIG_P2P */
+#ifdef CONFIG_MATCH_IFACE
+	       "  -M = start describing new matching interface\n"
+#endif /* CONFIG_MATCH_IFACE */
+	       "  -N = start describing new interface\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");
+	       "  -q = decrease debugging verbosity (-qq even less)\n"
+#ifdef CONFIG_DEBUG_SYSLOG
+	       "  -s = log output to syslog instead of stdout\n"
+#endif /* CONFIG_DEBUG_SYSLOG */
+	       "  -t = include timestamp in debug messages\n"
+#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 */
 #ifdef CONFIG_DBUS
-	printf("  -u = enable DBus control interface\n");
+	       "  -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");
+	       "  -v = show version\n"
+	       "  -W = wait for a control interface monitor before starting\n");
 
 	printf("example:\n"
 	       "  wpa_supplicant -D%s -iwlan0 -c/etc/wpa_supplicant.conf\n",
@@ -152,6 +156,28 @@
 }
 
 
+#ifdef CONFIG_MATCH_IFACE
+static int wpa_supplicant_init_match(struct wpa_global *global)
+{
+	/*
+	 * The assumption is that the first driver is the primary driver and
+	 * will handle the arrival / departure of interfaces.
+	 */
+	if (wpa_drivers[0]->global_init && !global->drv_priv[0]) {
+		global->drv_priv[0] = wpa_drivers[0]->global_init(global);
+		if (!global->drv_priv[0]) {
+			wpa_printf(MSG_ERROR,
+				   "Failed to initialize driver '%s'",
+				   wpa_drivers[0]->name);
+			return -1;
+		}
+	}
+
+	return 0;
+}
+#endif /* CONFIG_MATCH_IFACE */
+
+
 int main(int argc, char *argv[])
 {
 	int c, i;
@@ -175,7 +201,7 @@
 
 	for (;;) {
 		c = getopt(argc, argv,
-			   "b:Bc:C:D:de:f:g:G:hi:I:KLm:No:O:p:P:qsTtuvW");
+			   "b:Bc:C:D:de:f:g:G:hi:I:KLMm:No:O:p:P:qsTtuvW");
 		if (c < 0)
 			break;
 		switch (c) {
@@ -281,6 +307,20 @@
 		case 'W':
 			params.wait_for_monitor++;
 			break;
+#ifdef CONFIG_MATCH_IFACE
+		case 'M':
+			params.match_iface_count++;
+			iface = os_realloc_array(params.match_ifaces,
+						 params.match_iface_count,
+						 sizeof(struct wpa_interface));
+			if (!iface)
+				goto out;
+			params.match_ifaces = iface;
+			iface = &params.match_ifaces[params.match_iface_count -
+						     1];
+			os_memset(iface, 0, sizeof(*iface));
+			break;
+#endif /* CONFIG_MATCH_IFACE */
 		case 'N':
 			iface_count++;
 			iface = os_realloc_array(ifaces, iface_count,
@@ -309,6 +349,17 @@
 			   "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;
 
@@ -316,6 +367,9 @@
 		     ifaces[i].ctrl_interface == NULL) ||
 		    ifaces[i].ifname == NULL) {
 			if (iface_count == 1 && (params.ctrl_interface ||
+#ifdef CONFIG_MATCH_IFACE
+						 params.match_iface_count ||
+#endif /* CONFIG_MATCH_IFACE */
 						 params.dbus_ctrl_interface))
 				break;
 			usage();
@@ -329,14 +383,24 @@
 		}
 	}
 
+#ifdef CONFIG_MATCH_IFACE
+	if (exitcode == 0)
+		exitcode = wpa_supplicant_init_match(global);
+#endif /* CONFIG_MATCH_IFACE */
+
 	if (exitcode == 0)
 		exitcode = wpa_supplicant_run(global);
 
 	wpa_supplicant_deinit(global);
 
+	fst_global_deinit();
+
 out:
 	wpa_supplicant_fd_workaround(0);
 	os_free(ifaces);
+#ifdef CONFIG_MATCH_IFACE
+	os_free(params.match_ifaces);
+#endif /* CONFIG_MATCH_IFACE */
 	os_free(params.pid_file);
 
 	os_program_deinit();
diff --git a/wpa_supplicant/mbo.c b/wpa_supplicant/mbo.c
new file mode 100644
index 0000000..3292e67
--- /dev/null
+++ b/wpa_supplicant/mbo.c
@@ -0,0 +1,771 @@
+/*
+ * wpa_supplicant - MBO
+ *
+ * Copyright(c) 2015 Intel Deutschland GmbH
+ * Contact Information:
+ * Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * 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 "config.h"
+#include "wpa_supplicant_i.h"
+#include "driver_i.h"
+#include "bss.h"
+
+/* type + length + oui + oui type */
+#define MBO_IE_HEADER 6
+
+
+static int wpas_mbo_validate_non_pref_chan(u8 oper_class, u8 chan, u8 reason)
+{
+	if (reason > MBO_NON_PREF_CHAN_REASON_INT_INTERFERENCE)
+		return -1;
+
+	/* Only checking the validity of the channel and oper_class */
+	if (ieee80211_chan_to_freq(NULL, oper_class, chan) == -1)
+		return -1;
+
+	return 0;
+}
+
+
+const u8 * wpas_mbo_get_bss_attr(struct wpa_bss *bss, enum mbo_attr_id attr)
+{
+	const u8 *mbo, *end;
+
+	if (!bss)
+		return NULL;
+
+	mbo = wpa_bss_get_vendor_ie(bss, MBO_IE_VENDOR_TYPE);
+	if (!mbo)
+		return NULL;
+
+	end = mbo + 2 + mbo[1];
+	mbo += MBO_IE_HEADER;
+
+	return get_ie(mbo, end - mbo, attr);
+}
+
+
+static void wpas_mbo_non_pref_chan_attr_body(struct wpa_supplicant *wpa_s,
+					     struct wpabuf *mbo,
+					     u8 start, u8 end)
+{
+	u8 i;
+
+	wpabuf_put_u8(mbo, wpa_s->non_pref_chan[start].oper_class);
+
+	for (i = start; i < end; i++)
+		wpabuf_put_u8(mbo, wpa_s->non_pref_chan[i].chan);
+
+	wpabuf_put_u8(mbo, wpa_s->non_pref_chan[start].preference);
+	wpabuf_put_u8(mbo, wpa_s->non_pref_chan[start].reason);
+	wpabuf_put_u8(mbo, wpa_s->non_pref_chan[start].reason_detail);
+}
+
+
+static void wpas_mbo_non_pref_chan_attr(struct wpa_supplicant *wpa_s,
+					struct wpabuf *mbo, u8 start, u8 end)
+{
+	size_t size = end - start + 4;
+
+	if (size + 2 > wpabuf_tailroom(mbo))
+		return;
+
+	wpabuf_put_u8(mbo, MBO_ATTR_ID_NON_PREF_CHAN_REPORT);
+	wpabuf_put_u8(mbo, size); /* Length */
+
+	wpas_mbo_non_pref_chan_attr_body(wpa_s, mbo, start, end);
+}
+
+
+static void wpas_mbo_non_pref_chan_subelem_hdr(struct wpabuf *mbo, u8 len)
+{
+	wpabuf_put_u8(mbo, WLAN_EID_VENDOR_SPECIFIC);
+	wpabuf_put_u8(mbo, len); /* Length */
+	wpabuf_put_be24(mbo, OUI_WFA);
+	wpabuf_put_u8(mbo, MBO_ATTR_ID_NON_PREF_CHAN_REPORT);
+}
+
+
+static void wpas_mbo_non_pref_chan_subelement(struct wpa_supplicant *wpa_s,
+					      struct wpabuf *mbo, u8 start,
+					      u8 end)
+{
+	size_t size = end - start + 8;
+
+	if (size + 2 > wpabuf_tailroom(mbo))
+		return;
+
+	wpas_mbo_non_pref_chan_subelem_hdr(mbo, size);
+	wpas_mbo_non_pref_chan_attr_body(wpa_s, mbo, start, end);
+}
+
+
+static void wpas_mbo_non_pref_chan_attrs(struct wpa_supplicant *wpa_s,
+					 struct wpabuf *mbo, int subelement)
+{
+	u8 i, start = 0;
+	struct wpa_mbo_non_pref_channel *start_pref;
+
+	if (!wpa_s->non_pref_chan || !wpa_s->non_pref_chan_num) {
+		if (subelement)
+			wpas_mbo_non_pref_chan_subelem_hdr(mbo, 4);
+		return;
+	}
+	start_pref = &wpa_s->non_pref_chan[0];
+
+	for (i = 1; i <= wpa_s->non_pref_chan_num; i++) {
+		struct wpa_mbo_non_pref_channel *non_pref = NULL;
+
+		if (i < wpa_s->non_pref_chan_num)
+			non_pref = &wpa_s->non_pref_chan[i];
+		if (!non_pref ||
+		    non_pref->oper_class != start_pref->oper_class ||
+		    non_pref->reason != start_pref->reason ||
+		    non_pref->reason_detail != start_pref->reason_detail ||
+		    non_pref->preference != start_pref->preference) {
+			if (subelement)
+				wpas_mbo_non_pref_chan_subelement(wpa_s, mbo,
+								  start, i);
+			else
+				wpas_mbo_non_pref_chan_attr(wpa_s, mbo, start,
+							    i);
+
+			if (!non_pref)
+				return;
+
+			start = i;
+			start_pref = non_pref;
+		}
+	}
+}
+
+
+int wpas_mbo_ie(struct wpa_supplicant *wpa_s, u8 *buf, size_t len)
+{
+	struct wpabuf *mbo;
+	int res;
+
+	if (len < MBO_IE_HEADER + 3 + 7)
+		return 0;
+
+	/* Leave room for the MBO IE header */
+	mbo = wpabuf_alloc(len - MBO_IE_HEADER);
+	if (!mbo)
+		return 0;
+
+	/* Add non-preferred channels attribute */
+	wpas_mbo_non_pref_chan_attrs(wpa_s, mbo, 0);
+
+	/*
+	 * Send cellular capabilities attribute even if AP does not advertise
+	 * cellular capabilities.
+	 */
+	wpabuf_put_u8(mbo, MBO_ATTR_ID_CELL_DATA_CAPA);
+	wpabuf_put_u8(mbo, 1);
+	wpabuf_put_u8(mbo, wpa_s->conf->mbo_cell_capa);
+
+	res = mbo_add_ie(buf, len, wpabuf_head_u8(mbo), wpabuf_len(mbo));
+	if (!res)
+		wpa_printf(MSG_ERROR, "Failed to add MBO IE");
+
+	wpabuf_free(mbo);
+	return res;
+}
+
+
+static void wpas_mbo_send_wnm_notification(struct wpa_supplicant *wpa_s,
+					   const u8 *data, size_t len)
+{
+	struct wpabuf *buf;
+	int res;
+
+	/*
+	 * Send WNM-Notification Request frame only in case of a change in
+	 * non-preferred channels list during association, if the AP supports
+	 * MBO.
+	 */
+	if (wpa_s->wpa_state != WPA_COMPLETED || !wpa_s->current_bss ||
+	    !wpa_bss_get_vendor_ie(wpa_s->current_bss, MBO_IE_VENDOR_TYPE))
+		return;
+
+	buf = wpabuf_alloc(4 + len);
+	if (!buf)
+		return;
+
+	wpabuf_put_u8(buf, WLAN_ACTION_WNM);
+	wpabuf_put_u8(buf, WNM_NOTIFICATION_REQ);
+	wpa_s->mbo_wnm_token++;
+	if (wpa_s->mbo_wnm_token == 0)
+		wpa_s->mbo_wnm_token++;
+	wpabuf_put_u8(buf, wpa_s->mbo_wnm_token);
+	wpabuf_put_u8(buf, WLAN_EID_VENDOR_SPECIFIC); /* Type */
+
+	wpabuf_put_data(buf, data, len);
+
+	res = 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);
+	if (res < 0)
+		wpa_printf(MSG_DEBUG,
+			   "Failed to send WNM-Notification Request frame with non-preferred channel list");
+
+	wpabuf_free(buf);
+}
+
+
+static void wpas_mbo_non_pref_chan_changed(struct wpa_supplicant *wpa_s)
+{
+	struct wpabuf *buf;
+
+	buf = wpabuf_alloc(512);
+	if (!buf)
+		return;
+
+	wpas_mbo_non_pref_chan_attrs(wpa_s, buf, 1);
+	wpas_mbo_send_wnm_notification(wpa_s, wpabuf_head_u8(buf),
+				       wpabuf_len(buf));
+	wpabuf_free(buf);
+}
+
+
+static int wpa_non_pref_chan_is_eq(struct wpa_mbo_non_pref_channel *a,
+				   struct wpa_mbo_non_pref_channel *b)
+{
+	return a->oper_class == b->oper_class && a->chan == b->chan;
+}
+
+
+/*
+ * wpa_non_pref_chan_cmp - Compare two channels for sorting
+ *
+ * In MBO IE non-preferred channel subelement we can put many channels in an
+ * attribute if they are in the same operating class and have the same
+ * preference, reason, and reason detail. To make it easy for the functions that
+ * build the IE attributes and WNM Request subelements, save the channels sorted
+ * by their oper_class, reason, and reason_detail.
+ */
+static int wpa_non_pref_chan_cmp(const void *_a, const void *_b)
+{
+	const struct wpa_mbo_non_pref_channel *a = _a, *b = _b;
+
+	if (a->oper_class != b->oper_class)
+		return a->oper_class - b->oper_class;
+	if (a->reason != b->reason)
+		return a->reason - b->reason;
+	if (a->reason_detail != b->reason_detail)
+		return a->reason_detail - b->reason_detail;
+	return a->preference - b->preference;
+}
+
+
+int wpas_mbo_update_non_pref_chan(struct wpa_supplicant *wpa_s,
+				  const char *non_pref_chan)
+{
+	char *cmd, *token, *context = NULL;
+	struct wpa_mbo_non_pref_channel *chans = NULL, *tmp_chans;
+	size_t num = 0, size = 0;
+	unsigned i;
+
+	wpa_printf(MSG_DEBUG, "MBO: Update non-preferred channels, non_pref_chan=%s",
+		   non_pref_chan ? non_pref_chan : "N/A");
+
+	/*
+	 * The shortest channel configuration is 10 characters - commas, 3
+	 * colons, and 4 values that one of them (oper_class) is 2 digits or
+	 * more.
+	 */
+	if (!non_pref_chan || os_strlen(non_pref_chan) < 10)
+		goto update;
+
+	cmd = os_strdup(non_pref_chan);
+	if (!cmd)
+		return -1;
+
+	while ((token = str_token(cmd, " ", &context))) {
+		struct wpa_mbo_non_pref_channel *chan;
+		int ret;
+		unsigned int _oper_class;
+		unsigned int _chan;
+		unsigned int _preference;
+		unsigned int _reason;
+		unsigned int _reason_detail;
+
+		if (num == size) {
+			size = size ? size * 2 : 1;
+			tmp_chans = os_realloc_array(chans, size,
+						     sizeof(*chans));
+			if (!tmp_chans) {
+				wpa_printf(MSG_ERROR,
+					   "Couldn't reallocate non_pref_chan");
+				goto fail;
+			}
+			chans = tmp_chans;
+		}
+
+		chan = &chans[num];
+
+		ret = sscanf(token, "%u:%u:%u:%u:%u", &_oper_class,
+			     &_chan, &_preference, &_reason,
+			     &_reason_detail);
+		if ((ret != 4 && ret != 5) ||
+		    _oper_class > 255 || _chan > 255 ||
+		    _preference > 255 || _reason > 65535 ||
+		    (ret == 5 && _reason_detail > 255)) {
+			wpa_printf(MSG_ERROR, "Invalid non-pref chan input %s",
+				   token);
+			goto fail;
+		}
+		chan->oper_class = _oper_class;
+		chan->chan = _chan;
+		chan->preference = _preference;
+		chan->reason = _reason;
+		chan->reason_detail = ret == 4 ? 0 : _reason_detail;
+
+		if (wpas_mbo_validate_non_pref_chan(chan->oper_class,
+						    chan->chan, chan->reason)) {
+			wpa_printf(MSG_ERROR,
+				   "Invalid non_pref_chan: oper class %d chan %d reason %d",
+				   chan->oper_class, chan->chan, chan->reason);
+			goto fail;
+		}
+
+		for (i = 0; i < num; i++)
+			if (wpa_non_pref_chan_is_eq(chan, &chans[i]))
+				break;
+		if (i != num) {
+			wpa_printf(MSG_ERROR,
+				   "oper class %d chan %d is duplicated",
+				   chan->oper_class, chan->chan);
+			goto fail;
+		}
+
+		num++;
+	}
+
+	os_free(cmd);
+
+	if (chans) {
+		qsort(chans, num, sizeof(struct wpa_mbo_non_pref_channel),
+		      wpa_non_pref_chan_cmp);
+	}
+
+update:
+	os_free(wpa_s->non_pref_chan);
+	wpa_s->non_pref_chan = chans;
+	wpa_s->non_pref_chan_num = num;
+	wpas_mbo_non_pref_chan_changed(wpa_s);
+
+	return 0;
+
+fail:
+	os_free(chans);
+	os_free(cmd);
+	return -1;
+}
+
+
+void wpas_mbo_scan_ie(struct wpa_supplicant *wpa_s, struct wpabuf *ie)
+{
+	wpabuf_put_u8(ie, WLAN_EID_VENDOR_SPECIFIC);
+	wpabuf_put_u8(ie, 7);
+	wpabuf_put_be24(ie, OUI_WFA);
+	wpabuf_put_u8(ie, MBO_OUI_TYPE);
+
+	wpabuf_put_u8(ie, MBO_ATTR_ID_CELL_DATA_CAPA);
+	wpabuf_put_u8(ie, 1);
+	wpabuf_put_u8(ie, wpa_s->conf->mbo_cell_capa);
+}
+
+
+enum chan_allowed {
+	NOT_ALLOWED, ALLOWED
+};
+
+static enum chan_allowed allow_channel(struct hostapd_hw_modes *mode, u8 chan,
+				       unsigned int *flags)
+{
+	int i;
+
+	for (i = 0; i < mode->num_channels; i++) {
+		if (mode->channels[i].chan == chan)
+			break;
+	}
+
+	if (i == mode->num_channels ||
+	    (mode->channels[i].flag & HOSTAPD_CHAN_DISABLED))
+		return NOT_ALLOWED;
+
+	if (flags)
+		*flags = mode->channels[i].flag;
+
+	return ALLOWED;
+}
+
+
+static int get_center_80mhz(struct hostapd_hw_modes *mode, u8 channel)
+{
+	u8 center_channels[] = {42, 58, 106, 122, 138, 155};
+	size_t 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 verify_80mhz(struct hostapd_hw_modes *mode, u8 channel)
+{
+	u8 center_chan;
+	unsigned int i;
+
+	center_chan = get_center_80mhz(mode, channel);
+	if (!center_chan)
+		return NOT_ALLOWED;
+
+	/* check all the channels are available */
+	for (i = 0; i < 4; i++) {
+		unsigned int flags;
+		u8 adj_chan = center_chan - 6 + i * 4;
+
+		if (allow_channel(mode, adj_chan, &flags) == NOT_ALLOWED)
+			return NOT_ALLOWED;
+
+		if ((i == 0 && !(flags & HOSTAPD_CHAN_VHT_10_70)) ||
+		    (i == 1 && !(flags & HOSTAPD_CHAN_VHT_30_50)) ||
+		    (i == 2 && !(flags & HOSTAPD_CHAN_VHT_50_30)) ||
+		    (i == 3 && !(flags & HOSTAPD_CHAN_VHT_70_10)))
+			return NOT_ALLOWED;
+	}
+
+	return ALLOWED;
+}
+
+
+static int get_center_160mhz(struct hostapd_hw_modes *mode, u8 channel)
+{
+	u8 center_channels[] = { 50, 114 };
+	unsigned int i;
+
+	if (mode->mode != HOSTAPD_MODE_IEEE80211A)
+		return 0;
+
+	for (i = 0; i < ARRAY_SIZE(center_channels); i++) {
+		/*
+		 * In 160 MHz, the bandwidth "spans" 28 channels (e.g., 36-64),
+		 * so the center channel is 14 channels away from the start/end.
+		 */
+		if (channel >= center_channels[i] - 14 &&
+		    channel <= center_channels[i] + 14)
+			return center_channels[i];
+	}
+
+	return 0;
+}
+
+
+static enum chan_allowed verify_160mhz(struct hostapd_hw_modes *mode,
+				       u8 channel)
+{
+	u8 center_chan;
+	unsigned int i;
+
+	center_chan = get_center_160mhz(mode, channel);
+	if (!center_chan)
+		return NOT_ALLOWED;
+
+	/* Check all the channels are available */
+	for (i = 0; i < 8; i++) {
+		unsigned int flags;
+		u8 adj_chan = center_chan - 14 + i * 4;
+
+		if (allow_channel(mode, adj_chan, &flags) == NOT_ALLOWED)
+			return NOT_ALLOWED;
+
+		if ((i == 0 && !(flags & HOSTAPD_CHAN_VHT_10_150)) ||
+		    (i == 1 && !(flags & HOSTAPD_CHAN_VHT_30_130)) ||
+		    (i == 2 && !(flags & HOSTAPD_CHAN_VHT_50_110)) ||
+		    (i == 3 && !(flags & HOSTAPD_CHAN_VHT_70_90)) ||
+		    (i == 4 && !(flags & HOSTAPD_CHAN_VHT_90_70)) ||
+		    (i == 5 && !(flags & HOSTAPD_CHAN_VHT_110_50)) ||
+		    (i == 6 && !(flags & HOSTAPD_CHAN_VHT_130_30)) ||
+		    (i == 7 && !(flags & HOSTAPD_CHAN_VHT_150_10)))
+			return NOT_ALLOWED;
+	}
+
+	return ALLOWED;
+}
+
+
+enum chan_allowed verify_channel(struct hostapd_hw_modes *mode, u8 channel,
+				 u8 bw)
+{
+	unsigned int flag = 0;
+	enum chan_allowed res, res2;
+
+	res2 = res = allow_channel(mode, channel, &flag);
+	if (bw == BW40MINUS) {
+		if (!(flag & HOSTAPD_CHAN_HT40MINUS))
+			return NOT_ALLOWED;
+		res2 = allow_channel(mode, channel - 4, NULL);
+	} else if (bw == BW40PLUS) {
+		if (!(flag & HOSTAPD_CHAN_HT40PLUS))
+			return NOT_ALLOWED;
+		res2 = allow_channel(mode, channel + 4, NULL);
+	} else if (bw == BW80) {
+		res2 = verify_80mhz(mode, channel);
+	} else if (bw == BW160) {
+		res2 = verify_160mhz(mode, channel);
+	}
+
+	if (res == NOT_ALLOWED || res2 == NOT_ALLOWED)
+		return NOT_ALLOWED;
+
+	return ALLOWED;
+}
+
+
+static int wpas_op_class_supported(struct wpa_supplicant *wpa_s,
+				   const struct oper_class_map *op_class)
+{
+	int chan;
+	size_t i;
+	struct hostapd_hw_modes *mode;
+
+	mode = get_mode(wpa_s->hw.modes, wpa_s->hw.num_modes, op_class->mode);
+	if (!mode)
+		return 0;
+
+	if (op_class->op_class == 128 || op_class->op_class == 130) {
+		u8 channels[] = { 42, 58, 106, 122, 138, 155 };
+
+		for (i = 0; i < ARRAY_SIZE(channels); i++) {
+			if (verify_channel(mode, channels[i], op_class->bw) ==
+			    NOT_ALLOWED)
+				return 0;
+		}
+
+		return 1;
+	}
+
+	if (op_class->op_class == 129) {
+		if (verify_channel(mode, 50, op_class->bw) == NOT_ALLOWED ||
+		    verify_channel(mode, 114, op_class->bw) == NOT_ALLOWED)
+			return 0;
+
+		return 1;
+	}
+
+	for (chan = op_class->min_chan; chan <= op_class->max_chan;
+	     chan += op_class->inc) {
+		if (verify_channel(mode, chan, op_class->bw) == NOT_ALLOWED)
+			return 0;
+	}
+
+	return 1;
+}
+
+
+int wpas_mbo_supp_op_class_ie(struct wpa_supplicant *wpa_s, int freq, u8 *pos,
+			      size_t len)
+{
+	struct wpabuf *buf;
+	u8 op, current, chan;
+	u8 *ie_len;
+	int res;
+
+	/*
+	 * Assume 20 MHz channel for now.
+	 * TODO: Use the secondary channel and VHT channel width that will be
+	 * used after association.
+	 */
+	if (ieee80211_freq_to_channel_ext(freq, 0, VHT_CHANWIDTH_USE_HT,
+					  &current, &chan) == NUM_HOSTAPD_MODES)
+		return 0;
+
+	/*
+	 * Need 3 bytes for EID, length, and current operating class, plus
+	 * 1 byte for every other supported operating class.
+	 */
+	buf = wpabuf_alloc(global_op_class_size + 3);
+	if (!buf)
+		return 0;
+
+	wpabuf_put_u8(buf, WLAN_EID_SUPPORTED_OPERATING_CLASSES);
+	/* Will set the length later, putting a placeholder */
+	ie_len = wpabuf_put(buf, 1);
+	wpabuf_put_u8(buf, current);
+
+	for (op = 0; global_op_class[op].op_class; op++) {
+		if (wpas_op_class_supported(wpa_s, &global_op_class[op]))
+			wpabuf_put_u8(buf, global_op_class[op].op_class);
+	}
+
+	*ie_len = wpabuf_len(buf) - 2;
+	if (*ie_len < 2 || wpabuf_len(buf) > len) {
+		wpa_printf(MSG_ERROR,
+			   "Failed to add supported operating classes IE");
+		res = 0;
+	} else {
+		os_memcpy(pos, wpabuf_head(buf), wpabuf_len(buf));
+		res = wpabuf_len(buf);
+		wpa_hexdump_buf(MSG_DEBUG,
+				"MBO: Added supported operating classes IE",
+				buf);
+	}
+
+	wpabuf_free(buf);
+	return res;
+}
+
+
+void wpas_mbo_ie_trans_req(struct wpa_supplicant *wpa_s, const u8 *mbo_ie,
+			   size_t len)
+{
+	const u8 *pos, *cell_pref = NULL, *reason = NULL;
+	u8 id, elen;
+	u16 disallowed_sec = 0;
+
+	if (len <= 4 || WPA_GET_BE24(mbo_ie) != OUI_WFA ||
+	    mbo_ie[3] != MBO_OUI_TYPE)
+		return;
+
+	pos = mbo_ie + 4;
+	len -= 4;
+
+	while (len >= 2) {
+		id = *pos++;
+		elen = *pos++;
+		len -= 2;
+
+		if (elen > len)
+			goto fail;
+
+		switch (id) {
+		case MBO_ATTR_ID_CELL_DATA_PREF:
+			if (elen != 1)
+				goto fail;
+
+			if (wpa_s->conf->mbo_cell_capa ==
+			    MBO_CELL_CAPA_AVAILABLE)
+				cell_pref = pos;
+			else
+				wpa_printf(MSG_DEBUG,
+					   "MBO: Station does not support Cellular data connection");
+			break;
+		case MBO_ATTR_ID_TRANSITION_REASON:
+			if (elen != 1)
+				goto fail;
+
+			reason = pos;
+			break;
+		case MBO_ATTR_ID_ASSOC_RETRY_DELAY:
+			if (elen != 2)
+				goto fail;
+
+			if (wpa_s->wnm_mode &
+			    WNM_BSS_TM_REQ_BSS_TERMINATION_INCLUDED) {
+				wpa_printf(MSG_DEBUG,
+					   "MBO: Unexpected association retry delay, BSS is terminating");
+				goto fail;
+			} else if (wpa_s->wnm_mode &
+				   WNM_BSS_TM_REQ_DISASSOC_IMMINENT) {
+				disallowed_sec = WPA_GET_LE16(pos);
+			} else {
+				wpa_printf(MSG_DEBUG,
+					   "MBO: Association retry delay attribute not in disassoc imminent mode");
+			}
+
+			break;
+		case MBO_ATTR_ID_AP_CAPA_IND:
+		case MBO_ATTR_ID_NON_PREF_CHAN_REPORT:
+		case MBO_ATTR_ID_CELL_DATA_CAPA:
+		case MBO_ATTR_ID_ASSOC_DISALLOW:
+		case MBO_ATTR_ID_TRANSITION_REJECT_REASON:
+			wpa_printf(MSG_DEBUG,
+				   "MBO: Attribute %d should not be included in BTM Request frame",
+				   id);
+			break;
+		default:
+			wpa_printf(MSG_DEBUG, "MBO: Unknown attribute id %u",
+				   id);
+			return;
+		}
+
+		pos += elen;
+		len -= elen;
+	}
+
+	if (cell_pref)
+		wpa_msg(wpa_s, MSG_INFO, MBO_CELL_PREFERENCE "preference=%u",
+			*cell_pref);
+
+	if (reason)
+		wpa_msg(wpa_s, MSG_INFO, MBO_TRANSITION_REASON "reason=%u",
+			*reason);
+
+	if (disallowed_sec && wpa_s->current_bss)
+		wpa_bss_tmp_disallow(wpa_s, wpa_s->current_bss->bssid,
+				     disallowed_sec);
+
+	return;
+fail:
+	wpa_printf(MSG_DEBUG, "MBO IE parsing failed (id=%u len=%u left=%zu)",
+		   id, elen, len);
+}
+
+
+size_t wpas_mbo_ie_bss_trans_reject(struct wpa_supplicant *wpa_s, u8 *pos,
+				    size_t len,
+				    enum mbo_transition_reject_reason reason)
+{
+	u8 reject_attr[3];
+
+	reject_attr[0] = MBO_ATTR_ID_TRANSITION_REJECT_REASON;
+	reject_attr[1] = 1;
+	reject_attr[2] = reason;
+
+	return mbo_add_ie(pos, len, reject_attr, sizeof(reject_attr));
+}
+
+
+void wpas_mbo_update_cell_capa(struct wpa_supplicant *wpa_s, u8 mbo_cell_capa)
+{
+	u8 cell_capa[7];
+
+	if (wpa_s->conf->mbo_cell_capa == mbo_cell_capa) {
+		wpa_printf(MSG_DEBUG,
+			   "MBO: Cellular capability already set to %u",
+			   mbo_cell_capa);
+		return;
+	}
+
+	wpa_s->conf->mbo_cell_capa = mbo_cell_capa;
+
+	cell_capa[0] = WLAN_EID_VENDOR_SPECIFIC;
+	cell_capa[1] = 5; /* Length */
+	WPA_PUT_BE24(cell_capa + 2, OUI_WFA);
+	cell_capa[5] = MBO_ATTR_ID_CELL_DATA_CAPA;
+	cell_capa[6] = mbo_cell_capa;
+
+	wpas_mbo_send_wnm_notification(wpa_s, cell_capa, 7);
+}
diff --git a/wpa_supplicant/mesh.c b/wpa_supplicant/mesh.c
index ca012e2..89b033b 100644
--- a/wpa_supplicant/mesh.c
+++ b/wpa_supplicant/mesh.c
@@ -47,8 +47,8 @@
 
 	if (ifmsh->mconf) {
 		mesh_mpm_deinit(wpa_s, ifmsh);
-		if (ifmsh->mconf->ies) {
-			ifmsh->mconf->ies = NULL;
+		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
@@ -171,6 +171,8 @@
 	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);
@@ -191,6 +193,29 @@
 			   ssid->frequency);
 		goto out_free;
 	}
+	if (ssid->ht40)
+		conf->secondary_channel = ssid->ht40;
+	if (conf->hw_mode == HOSTAPD_MODE_IEEE80211A && ssid->vht) {
+		conf->vht_oper_chwidth = ssid->max_oper_chwidth;
+		switch (conf->vht_oper_chwidth) {
+		case VHT_CHANWIDTH_80MHZ:
+		case VHT_CHANWIDTH_80P80MHZ:
+			ieee80211_freq_to_chan(
+				ssid->frequency,
+				&conf->vht_oper_centr_freq_seg0_idx);
+			conf->vht_oper_centr_freq_seg0_idx += ssid->ht40 * 2;
+			break;
+		case VHT_CHANWIDTH_160MHZ:
+			ieee80211_freq_to_chan(
+				ssid->frequency,
+				&conf->vht_oper_centr_freq_seg0_idx);
+			conf->vht_oper_centr_freq_seg0_idx += ssid->ht40 * 2;
+			conf->vht_oper_centr_freq_seg0_idx += 40 / 5;
+			break;
+		}
+		ieee80211_freq_to_chan(ssid->vht_center_freq2,
+				       &conf->vht_oper_centr_freq_seg1_idx);
+	}
 
 	if (ssid->mesh_basic_rates == NULL) {
 		/*
@@ -316,11 +341,44 @@
 
 	wpa_supplicant_mesh_deinit(wpa_s);
 
+	if (ssid->key_mgmt & WPA_KEY_MGMT_SAE) {
+		wpa_s->pairwise_cipher = WPA_CIPHER_CCMP;
+		wpa_s->group_cipher = WPA_CIPHER_CCMP;
+		wpa_s->mgmt_group_cipher = 0;
+	} else {
+		wpa_s->pairwise_cipher = WPA_CIPHER_NONE;
+		wpa_s->group_cipher = WPA_CIPHER_NONE;
+		wpa_s->mgmt_group_cipher = 0;
+	}
+
 	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;
+	wpa_s->mesh_vht_enabled = !!params.freq.vht_enabled;
+	if (params.freq.ht_enabled && params.freq.sec_channel_offset)
+		ssid->ht40 = params.freq.sec_channel_offset;
+	if (wpa_s->mesh_vht_enabled) {
+		ssid->vht = 1;
+		switch (params.freq.bandwidth) {
+		case 80:
+			if (params.freq.center_freq2) {
+				ssid->max_oper_chwidth = VHT_CHANWIDTH_80P80MHZ;
+				ssid->vht_center_freq2 =
+					params.freq.center_freq2;
+			} else {
+				ssid->max_oper_chwidth = VHT_CHANWIDTH_80MHZ;
+			}
+			break;
+		case 160:
+			ssid->max_oper_chwidth = VHT_CHANWIDTH_160MHZ;
+			break;
+		default:
+			ssid->max_oper_chwidth = VHT_CHANWIDTH_USE_HT;
+			break;
+		}
+	}
 	if (ssid->beacon_int > 0)
 		params.beacon_int = ssid->beacon_int;
 	else if (wpa_s->conf->beacon_int > 0)
@@ -350,8 +408,8 @@
 	}
 
 	if (wpa_s->ifmsh) {
-		params.ies = wpa_s->ifmsh->mconf->ies;
-		params.ie_len = wpa_s->ifmsh->mconf->ie_len;
+		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;
 	}
 
@@ -539,3 +597,16 @@
 	mesh_wpa_s->mesh_if_created = 1;
 	return 0;
 }
+
+
+int wpas_mesh_peer_remove(struct wpa_supplicant *wpa_s, const u8 *addr)
+{
+	return mesh_mpm_close_peer(wpa_s, addr);
+}
+
+
+int wpas_mesh_peer_add(struct wpa_supplicant *wpa_s, const u8 *addr,
+		       int duration)
+{
+	return mesh_mpm_connect_peer(wpa_s, addr, duration);
+}
diff --git a/wpa_supplicant/mesh.h b/wpa_supplicant/mesh.h
index 3cb7f1b..7317083 100644
--- a/wpa_supplicant/mesh.h
+++ b/wpa_supplicant/mesh.h
@@ -18,6 +18,9 @@
 			       char *end);
 int wpas_mesh_add_interface(struct wpa_supplicant *wpa_s, char *ifname,
 			    size_t len);
+int wpas_mesh_peer_remove(struct wpa_supplicant *wpa_s, const u8 *addr);
+int wpas_mesh_peer_add(struct wpa_supplicant *wpa_s, const u8 *addr,
+		       int duration);
 
 #ifdef CONFIG_MESH
 
diff --git a/wpa_supplicant/mesh_mpm.c b/wpa_supplicant/mesh_mpm.c
index b29b5ff..c014eaf 100644
--- a/wpa_supplicant/mesh_mpm.c
+++ b/wpa_supplicant/mesh_mpm.c
@@ -14,17 +14,18 @@
 #include "ap/hostapd.h"
 #include "ap/sta_info.h"
 #include "ap/ieee802_11.h"
+#include "ap/wpa_auth.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;
+	const u8 *proto_id; /* Mesh Peering Protocol Identifier (2 octets) */
+	const u8 *llid; /* Local Link ID (2 octets) */
+	const u8 *plid; /* Peer Link ID (conditional, 2 octets) */
+	const u8 *reason; /* Reason Code (conditional, 2 octets) */
+	const u8 *chosen_pmk; /* Chosen PMK (optional, 16 octets) */
 };
 
 static void plink_timer(void *eloop_ctx, void *user_data);
@@ -43,6 +44,7 @@
 };
 
 static const char * const mplstate[] = {
+	[0] = "UNINITIALIZED",
 	[PLINK_LISTEN] = "LISTEN",
 	[PLINK_OPEN_SENT] = "OPEN_SENT",
 	[PLINK_OPEN_RCVD] = "OPEN_RCVD",
@@ -72,10 +74,10 @@
 {
 	os_memset(mpm_ie, 0, sizeof(*mpm_ie));
 
-	/* remove optional PMK at end */
-	if (len >= 16) {
-		len -= 16;
-		mpm_ie->pmk = ie + len - 16;
+	/* Remove optional Chosen PMK field at end */
+	if (len >= SAE_PMKID_LEN) {
+		mpm_ie->chosen_pmk = ie + len - SAE_PMKID_LEN;
+		len -= SAE_PMKID_LEN;
 	}
 
 	if ((action_field == PLINK_OPEN && len != 4) ||
@@ -101,8 +103,8 @@
 		len -= 2;
 	}
 
-	/* plid, present for confirm, and possibly close */
-	if (len)
+	/* Peer Link ID, present for confirm, and possibly close */
+	if (len >= 2)
 		mpm_ie->plid = ie;
 
 	return 0;
@@ -212,9 +214,6 @@
 	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;
@@ -239,6 +238,15 @@
 			   2 + 22;  /* HT operation */
 	}
 #endif /* CONFIG_IEEE80211N */
+#ifdef CONFIG_IEEE80211AC
+	if (type != PLINK_CLOSE && wpa_s->mesh_vht_enabled) {
+		buf_len += 2 + 12 + /* VHT Capabilities */
+			   2 + 5;  /* VHT Operation */
+	}
+#endif /* CONFIG_IEEE80211AC */
+	if (type != PLINK_CLOSE)
+		buf_len += conf->rsn_ie_len; /* RSN IE */
+
 	buf = wpabuf_alloc(buf_len);
 	if (!buf)
 		return;
@@ -255,13 +263,16 @@
 
 		/* aid */
 		if (type == PLINK_CONFIRM)
-			wpabuf_put_le16(buf, sta->peer_lid);
+			wpabuf_put_le16(buf, sta->aid);
 
 		/* 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);
@@ -328,11 +339,22 @@
 
 #ifdef CONFIG_IEEE80211N
 	if (type != PLINK_CLOSE && wpa_s->mesh_ht_enabled) {
+		u8 ht_capa_oper[2 + 26 + 2 + 22];
+
 		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 */
+#ifdef CONFIG_IEEE80211AC
+	if (type != PLINK_CLOSE && wpa_s->mesh_vht_enabled) {
+		u8 vht_capa_oper[2 + 12 + 2 + 5];
+
+		pos = hostapd_eid_vht_capabilities(bss, vht_capa_oper);
+		pos = hostapd_eid_vht_operation(bss, pos);
+		wpabuf_put_data(buf, vht_capa_oper, pos - vht_capa_oper);
+	}
+#endif /* CONFIG_IEEE80211AC */
 
 	if (ampe && mesh_rsn_protect_frame(wpa_s->mesh_rsn, sta, cat, buf)) {
 		wpa_msg(wpa_s, MSG_INFO,
@@ -340,6 +362,9 @@
 		goto fail;
 	}
 
+	wpa_msg(wpa_s, MSG_DEBUG, "Mesh MPM: Sending peering frame type %d to "
+		MACSTR " (my_lid=0x%x peer_lid=0x%x)",
+		type, MAC2STR(sta->addr), sta->my_lid, sta->peer_lid);
 	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);
@@ -360,6 +385,9 @@
 	struct hostapd_sta_add_params params;
 	int ret;
 
+	wpa_msg(wpa_s, MSG_DEBUG, "MPM set " MACSTR " from %s into %s",
+		MAC2STR(sta->addr), mplstate[sta->plink_state],
+		mplstate[state]);
 	sta->plink_state = state;
 
 	os_memset(&params, 0, sizeof(params));
@@ -367,8 +395,6 @@
 	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
@@ -394,6 +420,7 @@
 	struct sta_info *sta = user_data;
 	u16 reason = 0;
 	struct mesh_conf *conf = wpa_s->ifmsh->mconf;
+	struct hostapd_data *hapd = wpa_s->ifmsh->bss[0];
 
 	switch (sta->plink_state) {
 	case PLINK_OPEN_RCVD:
@@ -423,6 +450,13 @@
 		break;
 	case PLINK_HOLDING:
 		/* holding timer */
+
+		if (sta->mesh_sae_pmksa_caching) {
+			wpa_printf(MSG_DEBUG, "MPM: Peer " MACSTR
+				   " looks like it does not support mesh SAE PMKSA caching, so remove the cached entry for it",
+				   MAC2STR(sta->addr));
+			wpa_auth_pmksa_remove(hapd->wpa_auth, sta->addr);
+		}
 		mesh_mpm_fsm_restart(wpa_s, sta);
 		break;
 	default:
@@ -447,8 +481,8 @@
 }
 
 
-int mesh_mpm_plink_close(struct hostapd_data *hapd,
-			 struct sta_info *sta, void *ctx)
+static 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;
@@ -466,6 +500,85 @@
 }
 
 
+int mesh_mpm_close_peer(struct wpa_supplicant *wpa_s, const u8 *addr)
+{
+	struct hostapd_data *hapd;
+	struct sta_info *sta;
+
+	if (!wpa_s->ifmsh) {
+		wpa_msg(wpa_s, MSG_INFO, "Mesh is not prepared yet");
+		return -1;
+	}
+
+	hapd = wpa_s->ifmsh->bss[0];
+	sta = ap_get_sta(hapd, addr);
+	if (!sta) {
+		wpa_msg(wpa_s, MSG_INFO, "No such mesh peer");
+		return -1;
+	}
+
+	return mesh_mpm_plink_close(hapd, sta, wpa_s) == 0 ? 0 : -1;
+}
+
+
+static void peer_add_timer(void *eloop_ctx, void *user_data)
+{
+	struct wpa_supplicant *wpa_s = eloop_ctx;
+	struct hostapd_data *hapd = wpa_s->ifmsh->bss[0];
+
+	os_memset(hapd->mesh_required_peer, 0, ETH_ALEN);
+}
+
+
+int mesh_mpm_connect_peer(struct wpa_supplicant *wpa_s, const u8 *addr,
+			  int duration)
+{
+	struct wpa_ssid *ssid = wpa_s->current_ssid;
+	struct hostapd_data *hapd;
+	struct sta_info *sta;
+	struct mesh_conf *conf;
+
+	if (!wpa_s->ifmsh) {
+		wpa_msg(wpa_s, MSG_INFO, "Mesh is not prepared yet");
+		return -1;
+	}
+
+	if (!ssid || !ssid->no_auto_peer) {
+		wpa_msg(wpa_s, MSG_INFO,
+			"This command is available only with no_auto_peer mesh network");
+		return -1;
+	}
+
+	hapd = wpa_s->ifmsh->bss[0];
+	conf = wpa_s->ifmsh->mconf;
+
+	sta = ap_get_sta(hapd, addr);
+	if (!sta) {
+		wpa_msg(wpa_s, MSG_INFO, "No such mesh peer");
+		return -1;
+	}
+
+	if ((PLINK_OPEN_SENT <= sta->plink_state &&
+	    sta->plink_state <= PLINK_ESTAB) ||
+	    (sta->sae && sta->sae->state > SAE_NOTHING)) {
+		wpa_msg(wpa_s, MSG_INFO,
+			"Specified peer is connecting/connected");
+		return -1;
+	}
+
+	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);
+		os_memcpy(hapd->mesh_required_peer, addr, ETH_ALEN);
+		eloop_register_timeout(duration == -1 ? 10 : duration, 0,
+				       peer_add_timer, wpa_s, NULL);
+	}
+
+	return 0;
+}
+
+
 void mesh_mpm_deinit(struct wpa_supplicant *wpa_s, struct hostapd_iface *ifmsh)
 {
 	struct hostapd_data *hapd = ifmsh->bss[0];
@@ -475,6 +588,7 @@
 
 	hapd->num_plinks = 0;
 	hostapd_free_stas(hapd);
+	eloop_cancel_timeout(peer_add_timer, wpa_s, NULL);
 }
 
 
@@ -542,28 +656,44 @@
 			return NULL;
 	}
 
+	/* Set WMM by default since Mesh STAs are QoS STAs */
+	sta->flags |= WLAN_STA_WMM;
+
 	/* initialize sta */
 	if (copy_supp_rates(wpa_s, sta, elems)) {
 		ap_free_sta(data, sta);
 		return NULL;
 	}
 
-	mesh_mpm_init_link(wpa_s, sta);
+	if (!sta->my_lid)
+		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 */
 
+#ifdef CONFIG_IEEE80211AC
+	copy_sta_vht_capab(data, sta, elems->vht_capabilities);
+	set_sta_vht_opmode(data, sta, elems->vht_opmode_notif);
+#endif /* CONFIG_IEEE80211AC */
+
+	if (hostapd_get_aid(data, sta) < 0) {
+		wpa_msg(wpa_s, MSG_ERROR, "No AIDs available");
+		ap_free_sta(data, sta);
+		return NULL;
+	}
+
 	/* 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.aid = sta->aid;
 	params.listen_interval = 100;
 	params.ht_capabilities = sta->ht_capabilities;
+	params.vht_capabilities = sta->vht_capabilities;
 	params.flags |= WPA_STA_WMM;
 	params.flags_mask |= WPA_STA_AUTHENTICATED;
 	if (conf->security == MESH_CONF_SEC_NONE) {
@@ -599,7 +729,9 @@
 	if (!sta)
 		return;
 
-	if (ssid && ssid->no_auto_peer) {
+	if (ssid && ssid->no_auto_peer &&
+	    (is_zero_ether_addr(data->mesh_required_peer) ||
+	     os_memcmp(data->mesh_required_peer, addr, ETH_ALEN) != 0)) {
 		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) {
@@ -628,10 +760,13 @@
 		return;
 	}
 
-	if (conf->security == MESH_CONF_SEC_NONE)
-		mesh_mpm_plink_open(wpa_s, sta, PLINK_OPEN_SENT);
-	else
+	if (conf->security == MESH_CONF_SEC_NONE) {
+		if (sta->plink_state < PLINK_OPEN_SENT ||
+		    sta->plink_state > PLINK_ESTAB)
+			mesh_mpm_plink_open(wpa_s, sta, PLINK_OPEN_SENT);
+	} else {
 		mesh_rsn_auth_sae_sta(wpa_s, sta);
+	}
 }
 
 
@@ -676,12 +811,15 @@
 	hapd->num_plinks++;
 
 	sta->flags |= WLAN_STA_ASSOC;
+	sta->mesh_sae_pmksa_caching = 0;
 
+	eloop_cancel_timeout(peer_add_timer, wpa_s, NULL);
+	peer_add_timer(wpa_s, NULL);
 	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));
+	wpa_msg(wpa_s, MSG_INFO, MESH_PEER_CONNECTED MACSTR,
+		MAC2STR(sta->addr));
 }
 
 
@@ -819,9 +957,8 @@
 				" closed with reason %d",
 				MAC2STR(sta->addr), reason);
 
-			wpa_msg_ctrl(wpa_s, MSG_INFO,
-				     MESH_PEER_DISCONNECTED MACSTR,
-				     MAC2STR(sta->addr));
+			wpa_msg(wpa_s, MSG_INFO, MESH_PEER_DISCONNECTED MACSTR,
+				MAC2STR(sta->addr));
 
 			hapd->num_plinks--;
 
@@ -957,7 +1094,8 @@
 	 * open mesh, then go ahead and add the peer before proceeding.
 	 */
 	if (!sta && action_field == PLINK_OPEN &&
-	    !(mconf->security & MESH_CONF_SEC_AMPE))
+	    (!(mconf->security & MESH_CONF_SEC_AMPE) ||
+	     wpa_auth_pmksa_get(hapd->wpa_auth, mgmt->sa)))
 		sta = mesh_mpm_add_peer(wpa_s, mgmt->sa, &elems);
 
 	if (!sta) {
@@ -979,6 +1117,7 @@
 	if ((mconf->security & MESH_CONF_SEC_AMPE) &&
 	    mesh_rsn_process_ampe(wpa_s, sta, &elems,
 				  &mgmt->u.action.category,
+				  peer_mgmt_ie.chosen_pmk,
 				  ies, ie_len)) {
 		wpa_printf(MSG_DEBUG, "MPM: RSN process rejected frame");
 		return;
@@ -1051,8 +1190,10 @@
 
 
 /* called by ap_free_sta */
-void mesh_mpm_free_sta(struct sta_info *sta)
+void mesh_mpm_free_sta(struct hostapd_data *hapd, struct sta_info *sta)
 {
+	if (sta->plink_state == PLINK_ESTAB)
+		hapd->num_plinks--;
 	eloop_cancel_timeout(plink_timer, ELOOP_ALL_CTX, sta);
 	eloop_cancel_timeout(mesh_auth_timer, ELOOP_ALL_CTX, sta);
 }
diff --git a/wpa_supplicant/mesh_mpm.h b/wpa_supplicant/mesh_mpm.h
index 7ebaef0..5fc1e61 100644
--- a/wpa_supplicant/mesh_mpm.h
+++ b/wpa_supplicant/mesh_mpm.h
@@ -14,10 +14,13 @@
 			    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 mesh_mpm_free_sta(struct hostapd_data *hapd, struct sta_info *sta);
 void wpa_mesh_set_plink_state(struct wpa_supplicant *wpa_s,
 			      struct sta_info *sta,
 			      enum mesh_plink_state state);
+int mesh_mpm_close_peer(struct wpa_supplicant *wpa_s, const u8 *addr);
+int mesh_mpm_connect_peer(struct wpa_supplicant *wpa_s, const u8 *addr,
+			  int duration);
 
 #ifdef CONFIG_MESH
 
diff --git a/wpa_supplicant/mesh_rsn.c b/wpa_supplicant/mesh_rsn.c
index 936002d..1994f3f 100644
--- a/wpa_supplicant/mesh_rsn.c
+++ b/wpa_supplicant/mesh_rsn.c
@@ -27,12 +27,12 @@
 
 #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;
+	struct hostapd_data *hapd;
 
 	if (sta->sae->state != SAE_ACCEPTED) {
 		wpa_printf(MSG_DEBUG, "AUTH: Re-authenticate with " MACSTR
@@ -43,23 +43,20 @@
 		if (sta->sae_auth_retry < MESH_AUTH_RETRY) {
 			mesh_rsn_auth_sae_sta(wpa_s, sta);
 		} else {
+			hapd = wpa_s->ifmsh->bss[0];
+
 			if (sta->sae_auth_retry > MESH_AUTH_RETRY) {
-				ap_free_sta(wpa_s->ifmsh->bss[0], sta);
+				ap_free_sta(hapd, 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);
+				hapd->conf->ap_max_inactivity);
 		}
 		sta->sae_auth_retry++;
 	}
@@ -190,7 +187,8 @@
 static void mesh_rsn_deinit(struct mesh_rsn *rsn)
 {
 	os_memset(rsn->mgtk, 0, sizeof(rsn->mgtk));
-	wpa_deinit(rsn->auth);
+	if (rsn->auth)
+		wpa_deinit(rsn->auth);
 }
 
 
@@ -209,14 +207,15 @@
 
 	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->ies = (u8 *) ie;
-	conf->ie_len = 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);
 
@@ -289,6 +288,7 @@
 {
 	struct hostapd_data *hapd = wpa_s->ifmsh->bss[0];
 	struct wpa_ssid *ssid = wpa_s->current_ssid;
+	struct rsn_pmksa_cache_entry *pmksa;
 	unsigned int rnd;
 	int ret;
 
@@ -304,6 +304,29 @@
 			return -1;
 	}
 
+	pmksa = wpa_auth_pmksa_get(hapd->wpa_auth, sta->addr);
+	if (pmksa) {
+		if (!sta->wpa_sm)
+			sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth,
+							sta->addr, NULL);
+		if (!sta->wpa_sm) {
+			wpa_printf(MSG_ERROR,
+				   "mesh: Failed to initialize RSN state machine");
+			return -1;
+		}
+
+		wpa_printf(MSG_DEBUG,
+			   "AUTH: Mesh PMKSA cache entry found for " MACSTR
+			   " - try to use PMKSA caching instead of new SAE authentication",
+			   MAC2STR(sta->addr));
+		wpa_auth_pmksa_set_to_sm(pmksa, sta->wpa_sm, hapd->wpa_auth,
+					 sta->sae->pmkid, sta->sae->pmk);
+		sae_accept_sta(hapd, sta);
+		sta->mesh_sae_pmksa_caching = 1;
+		return 0;
+	}
+	sta->mesh_sae_pmksa_caching = 0;
+
 	if (mesh_rsn_build_sae_commit(wpa_s, ssid, sta))
 		return -1;
 
@@ -326,10 +349,7 @@
 
 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)));
+	os_memcpy(pmkid, sta->sae->pmkid, SAE_PMKID_LEN);
 }
 
 
@@ -501,6 +521,7 @@
 
 int mesh_rsn_process_ampe(struct wpa_supplicant *wpa_s, struct sta_info *sta,
 			  struct ieee802_11_elems *elems, const u8 *cat,
+			  const u8 *chosen_pmk,
 			  const u8 *start, size_t elems_len)
 {
 	int ret = 0;
@@ -514,6 +535,23 @@
 	const size_t aad_len[] = { ETH_ALEN, ETH_ALEN,
 				   (elems->mic - 2) - cat };
 
+	if (!sta->sae) {
+		struct hostapd_data *hapd = wpa_s->ifmsh->bss[0];
+
+		if (!wpa_auth_pmksa_get(hapd->wpa_auth, sta->addr)) {
+			wpa_printf(MSG_INFO,
+				   "Mesh RSN: SAE is not prepared yet");
+			return -1;
+		}
+		mesh_rsn_auth_sae_sta(wpa_s, sta);
+	}
+
+	if (chosen_pmk && os_memcmp(chosen_pmk, sta->sae->pmkid, PMKID_LEN)) {
+		wpa_msg(wpa_s, MSG_DEBUG,
+			"Mesh RSN: Invalid PMKID (Chosen PMK did not match calculated PMKID)");
+		return -1;
+	}
+
 	if (!elems->mic || elems->mic_len < AES_BLOCK_SIZE) {
 		wpa_msg(wpa_s, MSG_DEBUG, "Mesh RSN: missing mic ie");
 		return -1;
diff --git a/wpa_supplicant/mesh_rsn.h b/wpa_supplicant/mesh_rsn.h
index b1471b2..89601d4 100644
--- a/wpa_supplicant/mesh_rsn.h
+++ b/wpa_supplicant/mesh_rsn.h
@@ -30,6 +30,7 @@
 			   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 *chosen_pmk,
 			  const u8 *start, size_t elems_len);
 void mesh_auth_timer(void *eloop_ctx, void *user_data);
 
diff --git a/wpa_supplicant/notify.c b/wpa_supplicant/notify.c
index 822db74..e739363 100644
--- a/wpa_supplicant/notify.c
+++ b/wpa_supplicant/notify.c
@@ -13,10 +13,12 @@
 #include "config.h"
 #include "wpa_supplicant_i.h"
 #include "wps_supplicant.h"
+#include "binder/binder.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"
@@ -33,6 +35,12 @@
 	}
 #endif /* CONFIG_DBUS */
 
+#ifdef CONFIG_BINDER
+	global->binder = wpas_binder_init(global);
+	if (!global->binder)
+		return -1;
+#endif /* CONFIG_BINDER */
+
 	return 0;
 }
 
@@ -43,6 +51,11 @@
 	if (global->dbus)
 		wpas_dbus_deinit(global->dbus);
 #endif /* CONFIG_DBUS */
+
+#ifdef CONFIG_BINDER
+	if (global->binder)
+		wpas_binder_deinit(global->binder);
+#endif /* CONFIG_BINDER */
 }
 
 
@@ -88,6 +101,16 @@
 	/* notify the new DBus API */
 	wpas_dbus_signal_prop_changed(wpa_s, WPAS_DBUS_PROP_STATE);
 
+#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)
@@ -117,6 +140,15 @@
 }
 
 
+void wpas_notify_assoc_status_code(struct wpa_supplicant *wpa_s)
+{
+	if (wpa_s->p2p_mgmt)
+		return;
+
+	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)
@@ -646,12 +678,30 @@
 }
 
 
+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 */
 
 
diff --git a/wpa_supplicant/notify.h b/wpa_supplicant/notify.h
index 1aeec47..1b7f04d 100644
--- a/wpa_supplicant/notify.h
+++ b/wpa_supplicant/notify.h
@@ -23,6 +23,7 @@
 			       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);
@@ -114,6 +115,8 @@
 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,
@@ -135,5 +138,8 @@
 					   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/wpa_supplicant/offchannel.c b/wpa_supplicant/offchannel.c
index 63af83a..6b3f83c 100644
--- a/wpa_supplicant/offchannel.c
+++ b/wpa_supplicant/offchannel.c
@@ -118,8 +118,9 @@
 	}
 
 	wpa_printf(MSG_DEBUG, "Off-channel: Sending pending Action frame to "
-		   MACSTR " using interface %s",
-		   MAC2STR(wpa_s->pending_action_dst), iface->ifname);
+		   MACSTR " using interface %s (pending_action_tx=%p)",
+		   MAC2STR(wpa_s->pending_action_dst), iface->ifname,
+		   wpa_s->pending_action_tx);
 	res = wpa_drv_send_action(iface, wpa_s->pending_action_freq, 0,
 				  wpa_s->pending_action_dst,
 				  wpa_s->pending_action_src,
@@ -183,8 +184,12 @@
 		return;
 	}
 
-	wpa_printf(MSG_DEBUG, "Off-channel: Delete matching pending action frame");
-
+	wpa_printf(MSG_DEBUG,
+		   "Off-channel: Delete matching pending action frame (dst="
+		   MACSTR " pending_action_tx=%p)", MAC2STR(dst),
+		   wpa_s->pending_action_tx);
+	wpa_hexdump_buf(MSG_MSGDUMP, "Pending TX frame",
+			wpa_s->pending_action_tx);
 	wpabuf_free(wpa_s->pending_action_tx);
 	wpa_s->pending_action_tx = NULL;
 
@@ -250,8 +255,11 @@
 
 	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));
+			   "frame TX to " MACSTR " (pending_action_tx=%p)",
+			   MAC2STR(wpa_s->pending_action_dst),
+			   wpa_s->pending_action_tx);
+		wpa_hexdump_buf(MSG_MSGDUMP, "Pending TX frame",
+				wpa_s->pending_action_tx);
 		wpabuf_free(wpa_s->pending_action_tx);
 	}
 	wpa_s->pending_action_tx_done = 0;
@@ -268,6 +276,12 @@
 	os_memcpy(wpa_s->pending_action_bssid, bssid, ETH_ALEN);
 	wpa_s->pending_action_freq = freq;
 	wpa_s->pending_action_no_cck = no_cck;
+	wpa_printf(MSG_DEBUG,
+		   "Off-channel: Stored pending action frame (dst=" MACSTR
+		   " pending_action_tx=%p)",
+		   MAC2STR(dst), wpa_s->pending_action_tx);
+	wpa_hexdump_buf(MSG_MSGDUMP, "Pending TX frame",
+			wpa_s->pending_action_tx);
 
 	if (freq != 0 && wpa_s->drv_flags & WPA_DRIVER_FLAGS_OFFCHANNEL_TX) {
 		struct wpa_supplicant *iface;
@@ -428,6 +442,9 @@
  */
 void offchannel_clear_pending_action_tx(struct wpa_supplicant *wpa_s)
 {
+	wpa_printf(MSG_DEBUG,
+		   "Off-channel: Clear pending Action frame TX (pending_action_tx=%p",
+		   wpa_s->pending_action_tx);
 	wpabuf_free(wpa_s->pending_action_tx);
 	wpa_s->pending_action_tx = NULL;
 }
diff --git a/wpa_supplicant/p2p_supplicant.c b/wpa_supplicant/p2p_supplicant.c
index b9ebd38..b310885 100644
--- a/wpa_supplicant/p2p_supplicant.c
+++ b/wpa_supplicant/p2p_supplicant.c
@@ -47,6 +47,19 @@
 
 #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
+
+/**
+ * Defines CSA parameters which are used when GO evacuates the no longer valid
+ * channel (and if the driver supports channel switch).
+ */
+#define P2P_GO_CSA_COUNT 7
+#define P2P_GO_CSA_BLOCK_TX 0
+
 #ifndef P2P_MAX_CLIENT_IDLE
 /*
  * How many seconds to try to reconnect to the GO when connection in P2P client
@@ -85,6 +98,12 @@
 
 #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,
@@ -94,7 +113,8 @@
 	P2P_GROUP_REMOVAL_UNAVAILABLE,
 	P2P_GROUP_REMOVAL_GO_ENDING_SESSION,
 	P2P_GROUP_REMOVAL_PSK_FAILURE,
-	P2P_GROUP_REMOVAL_FREQ_CONFLICT
+	P2P_GROUP_REMOVAL_FREQ_CONFLICT,
+	P2P_GROUP_REMOVAL_GO_LEAVE_CHANNEL
 };
 
 
@@ -104,6 +124,10 @@
 			 int go);
 static int wpas_p2p_join_start(struct wpa_supplicant *wpa_s, int freq,
 			       const u8 *ssid, size_t ssid_len);
+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);
 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);
@@ -128,6 +152,16 @@
 					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);
 
 
 /*
@@ -317,6 +351,7 @@
 	int social_channels_freq[] = { 2412, 2437, 2462, 60480 };
 	size_t ielen;
 	u8 *n, i;
+	unsigned int bands;
 
 	if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
 		return -1;
@@ -346,28 +381,6 @@
 	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,
@@ -408,6 +421,29 @@
 		break;
 	}
 
+	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);
+
+	bands = wpas_get_bands(wpa_s, params->freqs);
+	p2p_scan_ie(wpa_s->global->p2p, ies, dev_id, bands);
+
+	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);
+
 	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)
@@ -515,27 +551,39 @@
 }
 
 
+static unsigned int p2p_is_active_persistent_group(struct wpa_supplicant *wpa_s)
+{
+	return !wpa_s->p2p_mgmt && wpa_s->current_ssid &&
+		!wpa_s->current_ssid->disabled &&
+		wpa_s->current_ssid->p2p_group &&
+		wpa_s->current_ssid->p2p_persistent_group;
+}
+
+
+static unsigned int p2p_is_active_persistent_go(struct wpa_supplicant *wpa_s)
+{
+	return p2p_is_active_persistent_group(wpa_s) &&
+		wpa_s->current_ssid->mode == WPAS_MODE_P2P_GO;
+}
+
+
 /* 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;
+		if (!p2p_is_active_persistent_go(wpa_s))
+			continue;
 
-			/* Prefer a group with connected clients */
-			if (p2p_get_group_num_members(wpa_s->p2p_group))
-				return wpa_s;
-			save = wpa_s;
-		}
+		/* 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) */
@@ -543,29 +591,23 @@
 }
 
 
-/* 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)
+static unsigned int p2p_is_active_persistent_cli(struct wpa_supplicant *wpa_s)
 {
-	struct wpa_ssid *s, *empty = NULL;
+	return p2p_is_active_persistent_group(wpa_s) &&
+		wpa_s->current_ssid->mode == WPAS_MODE_INFRA;
+}
 
-	if (!wpa_s)
-		return 0;
 
+/* Find an interface for a P2P group where we are the P2P Client */
+static struct wpa_supplicant *
+wpas_p2p_get_cli_group(struct wpa_supplicant *wpa_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 || !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;
-		}
+		if (p2p_is_active_persistent_cli(wpa_s))
+			return wpa_s;
 	}
 
-	return empty;
+	return NULL;
 }
 
 
@@ -584,20 +626,34 @@
 }
 
 
-static u8 p2ps_group_capability(void *ctx, u8 incoming, u8 role)
+static u8 p2ps_group_capability(void *ctx, u8 incoming, u8 role,
+				unsigned int *force_freq,
+				unsigned int *pref_freq)
 {
-	struct wpa_supplicant *wpa_s = ctx, *tmp_wpa_s;
+	struct wpa_supplicant *wpa_s = ctx;
 	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_supplicant *go_wpa_s, *cli_wpa_s;
 	struct wpa_ssid *persistent_go;
 	int p2p_no_group_iface;
+	unsigned int pref_freq_list[P2P_MAX_PREF_CHANNELS], size;
 
 	wpa_printf(MSG_DEBUG, "P2P: Conncap - in:%d role:%d", incoming, role);
 
+	if (force_freq)
+		*force_freq = 0;
+	if (pref_freq)
+		*pref_freq = 0;
+
+	size = P2P_MAX_PREF_CHANNELS;
+	if (force_freq && pref_freq &&
+	    !wpas_p2p_setup_freqs(wpa_s, 0, (int *) force_freq,
+				  (int *) pref_freq, 0, pref_freq_list, &size))
+		wpas_p2p_set_own_freq_preference(wpa_s,
+						 *force_freq ? *force_freq :
+						 *pref_freq);
+
 	/*
 	 * For non-concurrent capable devices:
 	 * If persistent_go, then no new.
@@ -605,36 +661,21 @@
 	 * If client, then no GO.
 	 */
 	go_wpa_s = wpas_p2p_get_go_group(wpa_s);
+	if (go_wpa_s)
+		owned_members = p2p_get_group_num_members(go_wpa_s->p2p_group);
 	persistent_go = wpas_p2p_get_persistent_go(wpa_s);
-	p2p_no_group_iface = wpa_s->conf->p2p_no_group_iface;
+	p2p_no_group_iface = !wpas_p2p_create_iface(wpa_s);
+	cli_wpa_s = wpas_p2p_get_cli_group(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++;
-			}
-		}
-	}
+	wpa_printf(MSG_DEBUG,
+		   "P2P: GO(iface)=%p members=%u CLI(iface)=%p persistent(ssid)=%p",
+		   go_wpa_s, owned_members, cli_wpa_s, persistent_go);
 
 	/* If not concurrent, restrict our choices */
 	if (p2p_no_group_iface) {
 		wpa_printf(MSG_DEBUG, "P2P: p2p_no_group_iface");
 
-		if (client)
+		if (cli_wpa_s)
 			return P2PS_SETUP_NONE;
 
 		if (go_wpa_s) {
@@ -666,10 +707,20 @@
 	/* If a required role has been specified, handle it here */
 	if (role && role != P2PS_SETUP_NEW) {
 		switch (incoming) {
+		case P2PS_SETUP_GROUP_OWNER | P2PS_SETUP_NEW:
+		case P2PS_SETUP_GROUP_OWNER | P2PS_SETUP_CLIENT:
+			/*
+			 * Peer has an active GO, so if the role allows it and
+			 * we do not have any active roles, become client.
+			 */
+			if ((role & P2PS_SETUP_CLIENT) && !go_wpa_s &&
+			    !cli_wpa_s)
+				return P2PS_SETUP_CLIENT;
+
+			/* fall through */
+
 		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;
 
@@ -678,7 +729,7 @@
 			 * Must be a complimentary role - cannot be a client to
 			 * more than one peer.
 			 */
-			if (incoming == role || client)
+			if (incoming == role || cli_wpa_s)
 				return P2PS_SETUP_NONE;
 
 			return P2PS_SETUP_CLIENT;
@@ -704,7 +755,7 @@
 	switch (incoming) {
 	case P2PS_SETUP_NONE:
 	case P2PS_SETUP_NEW:
-		if (client)
+		if (cli_wpa_s)
 			conncap = P2PS_SETUP_GROUP_OWNER;
 		else if (!owned_members)
 			conncap = P2PS_SETUP_NEW;
@@ -719,13 +770,13 @@
 		break;
 
 	case P2PS_SETUP_GROUP_OWNER:
-		if (!client)
+		if (!cli_wpa_s)
 			conncap = P2PS_SETUP_CLIENT;
 		break;
 
 	case P2PS_SETUP_GROUP_OWNER | P2PS_SETUP_NEW:
 	case P2PS_SETUP_GROUP_OWNER | P2PS_SETUP_CLIENT:
-		if (client)
+		if (cli_wpa_s)
 			conncap = P2PS_SETUP_GROUP_OWNER;
 		else {
 			u8 r;
@@ -747,15 +798,14 @@
 	    (!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) {
+		if (!s && !go_wpa_s && p2p_no_group_iface) {
 			p2p_set_intended_addr(wpa_s->global->p2p,
+					      wpa_s->p2p_mgmt ?
+					      wpa_s->parent->own_addr :
 					      wpa_s->own_addr);
-		} else if (!s && !owner) {
+		} else if (!s && !go_wpa_s) {
 			if (wpas_p2p_add_group_interface(wpa_s,
 							 WPA_IF_P2P_GO) < 0) {
 				wpa_printf(MSG_ERROR,
@@ -827,7 +877,7 @@
 
 	if (wpa_s->cross_connect_in_use) {
 		wpa_s->cross_connect_in_use = 0;
-		wpa_msg_global(wpa_s->parent, MSG_INFO,
+		wpa_msg_global(wpa_s->p2pdev, MSG_INFO,
 			       P2P_EVENT_CROSS_CONNECT_DISABLE "%s %s",
 			       wpa_s->ifname, wpa_s->cross_connect_uplink);
 	}
@@ -858,7 +908,7 @@
 		break;
 	}
 	if (removal_reason != P2P_GROUP_REMOVAL_SILENT) {
-		wpa_msg_global(wpa_s->parent, MSG_INFO,
+		wpa_msg_global(wpa_s->p2pdev, MSG_INFO,
 			       P2P_EVENT_GROUP_REMOVED "%s %s%s",
 			       wpa_s->ifname, gtype, reason);
 	}
@@ -868,13 +918,16 @@
 	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_s->p2pdev, 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
@@ -900,6 +953,12 @@
 		return 1;
 	}
 
+	/*
+	 * The primary interface was used for P2P group operations, so
+	 * need to reset its p2pdev.
+	 */
+	wpa_s->p2pdev = wpa_s->parent;
+
 	if (!wpa_s->p2p_go_group_formation_completed) {
 		wpa_s->global->p2p_group_formation = NULL;
 		wpa_s->p2p_in_provisioning = 0;
@@ -946,6 +1005,8 @@
 	else
 		wpa_drv_deinit_p2p_cli(wpa_s);
 
+	os_memset(wpa_s->go_dev_addr, 0, ETH_ALEN);
+
 	return 0;
 }
 
@@ -1073,7 +1134,8 @@
 	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->pbss = ssid->pbss;
+	s->pairwise_cipher = ssid->pbss ? WPA_CIPHER_GCMP : WPA_CIPHER_CCMP;
 	s->export_keys = 1;
 	if (ssid->passphrase) {
 		os_free(s->passphrase);
@@ -1213,7 +1275,7 @@
 	 * 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,
+	wpa_msg_global_ctrl(wpa_s->p2pdev, MSG_INFO,
 			    P2P_EVENT_GROUP_STARTED
 			    "%s %s ssid=\"%s\" freq=%d%s%s%s%s%s go_dev_addr="
 			    MACSTR "%s%s",
@@ -1256,8 +1318,9 @@
 	wpa_s->group_formation_reported = 1;
 
 	if (!success) {
-		wpa_msg_global(wpa_s->parent, MSG_INFO,
+		wpa_msg_global(wpa_s->p2pdev, 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,
@@ -1265,7 +1328,7 @@
 		return;
 	}
 
-	wpa_msg_global(wpa_s->parent, MSG_INFO,
+	wpa_msg_global(wpa_s->p2pdev, MSG_INFO,
 		       P2P_EVENT_GROUP_FORMATION_SUCCESS);
 
 	ssid = wpa_s->current_ssid;
@@ -1313,7 +1376,7 @@
 	}
 
 	if (persistent)
-		network_id = wpas_p2p_store_persistent_group(wpa_s->parent,
+		network_id = wpas_p2p_store_persistent_group(wpa_s->p2pdev,
 							     ssid, go_dev_addr);
 	else {
 		os_free(wpa_s->global->add_psk);
@@ -1339,6 +1402,25 @@
 };
 
 
+static void wpas_p2p_free_send_action_work(struct wpa_supplicant *wpa_s)
+{
+	struct send_action_work *awork = wpa_s->p2p_send_action_work->ctx;
+
+	wpa_printf(MSG_DEBUG,
+		   "P2P: Free Action frame radio work @%p (freq=%u dst="
+		   MACSTR " src=" MACSTR " bssid=" MACSTR " wait_time=%u)",
+		   wpa_s->p2p_send_action_work, awork->freq,
+		   MAC2STR(awork->dst), MAC2STR(awork->src),
+		   MAC2STR(awork->bssid), awork->wait_time);
+	wpa_hexdump(MSG_DEBUG, "P2P: Freeing pending Action frame",
+		    awork->buf, awork->len);
+	os_free(awork);
+	wpa_s->p2p_send_action_work->ctx = NULL;
+	radio_work_done(wpa_s->p2p_send_action_work);
+	wpa_s->p2p_send_action_work = NULL;
+}
+
+
 static void wpas_p2p_send_action_work_timeout(void *eloop_ctx,
 					      void *timeout_ctx)
 {
@@ -1348,9 +1430,7 @@
 		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;
+	wpas_p2p_free_send_action_work(wpa_s);
 }
 
 
@@ -1358,11 +1438,13 @@
 {
 	if (wpa_s->p2p_send_action_work) {
 		struct send_action_work *awork;
+
 		awork = wpa_s->p2p_send_action_work->ctx;
+		wpa_printf(MSG_DEBUG,
+			   "P2P: Clear Action TX work @%p (wait_time=%u)",
+			   wpa_s->p2p_send_action_work, awork->wait_time);
 		if (awork->wait_time == 0) {
-			os_free(awork);
-			radio_work_done(wpa_s->p2p_send_action_work);
-			wpa_s->p2p_send_action_work = NULL;
+			wpas_p2p_free_send_action_work(wpa_s);
 		} else {
 			/*
 			 * In theory, this should not be needed, but number of
@@ -1418,7 +1500,7 @@
 		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,
+		wpa_msg_global(wpa_s->p2pdev, MSG_INFO,
 			       P2P_EVENT_FALLBACK_TO_GO_NEG
 			       "reason=no-ACK-to-PD-Req");
 		wpas_p2p_fallback_to_go_neg(wpa_s, 0);
@@ -1561,11 +1643,11 @@
 	} 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 ==
+				   wpa_s->p2pdev->p2p_oob_dev_pw,
+				   wpa_s->p2pdev->p2p_oob_dev_pw_id, 1,
+				   wpa_s->p2pdev->p2p_oob_dev_pw_id ==
 				   DEV_PW_NFC_CONNECTION_HANDOVER ?
-				   wpa_s->parent->p2p_peer_oob_pubkey_hash :
+				   wpa_s->p2pdev->p2p_peer_oob_pubkey_hash :
 				   NULL,
 				   NULL, 0, 0);
 #endif /* CONFIG_WPS_NFC */
@@ -1591,7 +1673,7 @@
 	if (!wpa_s->ap_iface)
 		return;
 
-	persistent = wpas_p2p_get_persistent(wpa_s->parent, NULL, ssid->ssid,
+	persistent = wpas_p2p_get_persistent(wpa_s->p2pdev, NULL, ssid->ssid,
 					     ssid->ssid_len);
 	if (persistent == NULL)
 		return;
@@ -1656,8 +1738,8 @@
 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))
+	if (wpa_s->p2pdev->conf->update_config &&
+	    wpa_config_write(wpa_s->p2pdev->confname, wpa_s->p2pdev->conf))
 		wpa_printf(MSG_DEBUG, "P2P: Failed to update configuration");
 #endif /* CONFIG_NO_CONFIG_WRITE */
 }
@@ -1670,6 +1752,15 @@
 	struct wpa_ssid *ssid;
 	int network_id = -1;
 
+	wpa_s->ap_configured_cb = NULL;
+	wpa_s->ap_configured_cb_ctx = NULL;
+	wpa_s->ap_configured_cb_data = NULL;
+	if (!wpa_s->go_params) {
+		wpa_printf(MSG_ERROR,
+			   "P2P: p2p_go_configured() called with wpa_s->go_params == NULL");
+		return;
+	}
+
 	p2p_go_save_group_common_freqs(wpa_s, params);
 	p2p_go_dump_common_freqs(wpa_s);
 
@@ -1686,20 +1777,28 @@
 				       params->persistent_group, "");
 		wpa_s->group_formation_reported = 1;
 
-		if (wpa_s->parent->p2ps_join_addr_valid) {
-			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_join_addr_valid = 0;
+		if (wpa_s->p2pdev->p2ps_method_config_any) {
+			if (is_zero_ether_addr(wpa_s->p2pdev->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->p2pdev->p2ps_join_addr));
+				wpa_supplicant_ap_wps_pin(
+					wpa_s, wpa_s->p2pdev->p2ps_join_addr,
+					"12345670", NULL, 0, 0);
+			}
+			wpa_s->p2pdev->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->p2pdev, ssid,
 				wpa_s->global->p2p_dev_addr);
 			wpas_p2p_add_psk_list(wpa_s, ssid);
 		}
@@ -1716,11 +1815,11 @@
 			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);
+					     wpa_s->p2pdev, NULL);
 			eloop_register_timeout(
 				wpa_s->p2p_first_connection_timeout, 0,
 				wpas_p2p_group_formation_timeout,
-				wpa_s->parent, NULL);
+				wpa_s->p2pdev, NULL);
 		}
 
 		return;
@@ -1738,17 +1837,17 @@
 					  params->peer_device_addr);
 #ifdef CONFIG_WPS_NFC
 	} else if (params->wps_method == WPS_NFC) {
-		if (wpa_s->parent->p2p_oob_dev_pw_id !=
+		if (wpa_s->p2pdev->p2p_oob_dev_pw_id !=
 		    DEV_PW_NFC_CONNECTION_HANDOVER &&
-		    !wpa_s->parent->p2p_oob_dev_pw) {
+		    !wpa_s->p2pdev->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);
+			wpa_s, wpa_s->p2pdev->p2p_oob_dev_pw_id,
+			wpa_s->p2pdev->p2p_oob_dev_pw,
+			wpa_s->p2pdev->p2p_peer_oob_pk_hash_known ?
+			wpa_s->p2pdev->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,
@@ -1780,6 +1879,7 @@
 	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;
@@ -1790,6 +1890,8 @@
 	ssid->frequency = params->freq;
 	ssid->ht40 = params->ht40;
 	ssid->vht = params->vht;
+	ssid->max_oper_chwidth = params->max_oper_chwidth;
+	ssid->vht_center_freq2 = params->vht_center_freq2;
 	ssid->ssid = os_zalloc(params->ssid_len + 1);
 	if (ssid->ssid) {
 		os_memcpy(ssid->ssid, params->ssid, params->ssid_len);
@@ -1807,6 +1909,8 @@
 		 */
 		ssid->pairwise_cipher = WPA_CIPHER_GCMP;
 		ssid->group_cipher = WPA_CIPHER_GCMP;
+		/* P2P GO in 60 GHz is always a PCP (PBSS) */
+		ssid->pbss = 1;
 	}
 	if (os_strlen(params->passphrase) > 0) {
 		ssid->passphrase = os_strdup(params->passphrase);
@@ -1823,7 +1927,7 @@
 		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;
+	ssid->ap_max_inactivity = wpa_s->p2pdev->conf->p2p_go_max_inactivity;
 
 	wpa_s->ap_configured_cb = p2p_go_configured;
 	wpa_s->ap_configured_cb_ctx = wpa_s;
@@ -1862,6 +1966,7 @@
 	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;
@@ -1882,6 +1987,23 @@
 }
 
 
+static void wpas_p2p_clone_config_dh(struct wpa_supplicant *dst,
+				     const struct wpa_supplicant *src)
+{
+	struct wpa_config *d;
+	const struct wpa_config *s;
+
+	d = dst->conf;
+	s = src->conf;
+
+	if (s->wps_nfc_dh_privkey && s->wps_nfc_dh_pubkey &&
+	    !d->wps_nfc_dh_privkey && !d->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);
+	}
+}
+
+
 static void wpas_p2p_get_group_ifname(struct wpa_supplicant *wpa_s,
 				      char *ifname, size_t len)
 {
@@ -2032,7 +2154,7 @@
 					    int already_deleted)
 {
 	eloop_cancel_timeout(wpas_p2p_group_formation_timeout,
-			     wpa_s->parent, NULL);
+			     wpa_s->p2pdev, NULL);
 	if (wpa_s->global->p2p)
 		p2p_group_formation_failed(wpa_s->global->p2p);
 	wpas_group_formation_completed(wpa_s, 0, already_deleted);
@@ -2043,9 +2165,9 @@
 {
 	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);
+			     wpa_s->p2pdev, NULL);
 	eloop_register_timeout(0, 0, wpas_p2p_group_formation_timeout,
-			       wpa_s->parent, NULL);
+			       wpa_s->p2pdev, NULL);
 	wpa_s->global->p2p_fail_on_wps_complete = 0;
 }
 
@@ -2056,15 +2178,16 @@
 		return;
 	/* Speed up group formation timeout since this cannot succeed */
 	eloop_cancel_timeout(wpas_p2p_group_formation_timeout,
-			     wpa_s->parent, NULL);
+			     wpa_s->p2pdev, NULL);
 	eloop_register_timeout(0, 0, wpas_p2p_group_formation_timeout,
-			       wpa_s->parent, NULL);
+			       wpa_s->p2pdev, NULL);
 }
 
 
 static void wpas_go_neg_completed(void *ctx, struct p2p_go_neg_results *res)
 {
 	struct wpa_supplicant *wpa_s = ctx;
+	struct wpa_supplicant *group_wpa_s;
 
 	if (wpa_s->off_channel_freq || wpa_s->roc_waiting_drv_freq) {
 		wpa_drv_cancel_remain_on_channel(wpa_s);
@@ -2081,10 +2204,17 @@
 		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;
+	res->max_oper_chwidth = wpa_s->p2p_go_max_oper_chwidth;
+	res->vht_center_freq2 = wpa_s->p2p_go_vht_center_freq2;
 
 	wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_GO_NEG_SUCCESS "role=%s "
 		       "freq=%d ht40=%d peer_dev=" MACSTR " peer_iface=" MACSTR
@@ -2110,7 +2240,7 @@
 	}
 
 	if (wpa_s->create_p2p_iface) {
-		struct wpa_supplicant *group_wpa_s =
+		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);
@@ -2119,27 +2249,27 @@
 			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
-			wpas_start_wps_enrollee(group_wpa_s, res);
 	} else {
-		wpa_s->p2p_in_provisioning = 1;
-		wpa_s->global->p2p_group_formation = wpa_s;
+		group_wpa_s = wpa_s->parent;
+		wpa_s->global->p2p_group_formation = group_wpa_s;
+		if (group_wpa_s != wpa_s)
+			wpas_p2p_clone_config_dh(group_wpa_s, wpa_s);
+	}
 
-		if (res->role_go)
-			wpas_start_wps_go(wpa_s, res, 1);
-		else
-			wpas_start_wps_enrollee(ctx, res);
+	group_wpa_s->p2p_in_provisioning = 1;
+	group_wpa_s->p2pdev = wpa_s;
+	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;
+	}
+	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);
 	}
 
 	wpa_s->p2p_long_listen = 0;
@@ -2260,6 +2390,10 @@
 static void wpas_find_stopped(void *ctx)
 {
 	struct wpa_supplicant *wpa_s = ctx;
+
+	if (wpa_s->p2p_scan_work && wpas_abort_ongoing_scan(wpa_s) < 0)
+		wpa_printf(MSG_DEBUG, "P2P: Abort ongoing scan failed");
+
 	wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_FIND_STOPPED);
 	wpas_notify_p2p_find_stopped(wpa_s);
 }
@@ -2473,7 +2607,13 @@
 	params[sizeof(params) - 1] = '\0';
 
 	if (config_methods & WPS_CONFIG_DISPLAY) {
-		generated_pin = wps_generate_pin();
+		if (wps_generate_pin(&generated_pin) < 0) {
+			wpa_printf(MSG_DEBUG, "P2P: Could not generate PIN");
+			wpas_notify_p2p_provision_discovery(
+				wpa_s, peer, 0 /* response */,
+				P2P_PROV_DISC_INFO_UNAVAILABLE, 0, 0);
+			return;
+		}
 		wpas_prov_disc_local_display(wpa_s, peer, params,
 					     generated_pin);
 	} else if (config_methods & WPS_CONFIG_KEYPAD)
@@ -2518,7 +2658,13 @@
 	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();
+		if (wps_generate_pin(&generated_pin) < 0) {
+			wpa_printf(MSG_DEBUG, "P2P: Could not generate PIN");
+			wpas_notify_p2p_provision_discovery(
+				wpa_s, peer, 0 /* response */,
+				P2P_PROV_DISC_INFO_UNAVAILABLE, 0, 0);
+			return;
+		}
 		wpas_prov_disc_local_display(wpa_s, peer, params,
 					     generated_pin);
 	} else if (config_methods & WPS_CONFIG_PUSHBUTTON)
@@ -2541,7 +2687,7 @@
 	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,
+		wpa_msg_global(wpa_s->p2pdev, MSG_INFO,
 			       P2P_EVENT_FALLBACK_TO_GO_NEG
 			       "reason=PD-failed");
 		wpas_p2p_fallback_to_go_neg(wpa_s, 0);
@@ -2578,12 +2724,85 @@
 }
 
 
-static int freq_included(const struct p2p_channels *channels, unsigned int freq)
+static int freq_included(struct wpa_supplicant *wpa_s,
+			 const struct p2p_channels *channels,
+			 unsigned int freq)
 {
-	if (channels == NULL)
-		return 1; /* Assume no restrictions */
-	return p2p_channels_includes_freq(channels, 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;
+}
+
+
+static int wpas_sta_check_ecsa(struct hostapd_data *hapd,
+			       struct sta_info *sta, void *ctx)
+{
+	int *ecsa_support = ctx;
+
+	*ecsa_support &= sta->ecsa_supported;
+
+	return 0;
+}
+
+
+/* Check if all the peers support eCSA */
+static int wpas_p2p_go_clients_support_ecsa(struct wpa_supplicant *wpa_s)
+{
+	int ecsa_support = 1;
+
+	ap_for_each_sta(wpa_s->ap_iface->bss[0], wpas_sta_check_ecsa,
+			&ecsa_support);
+
+	return ecsa_support;
 }
 
 
@@ -2713,7 +2932,11 @@
 				   "invitation");
 			return P2P_SC_FAIL_UNABLE_TO_ACCOMMODATE;
 		}
-		os_memcpy(group_bssid, wpa_s->own_addr, ETH_ALEN);
+		if (wpa_s->p2p_mgmt)
+			os_memcpy(group_bssid, wpa_s->parent->own_addr,
+				  ETH_ALEN);
+		else
+			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)
@@ -2759,7 +2982,7 @@
 				   "running a GO but we are capable of MCC, "
 				   "figure out the best channel to use");
 			*force_freq = 0;
-		} else if (!freq_included(channels, *force_freq)) {
+		} 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 "
@@ -2796,8 +3019,9 @@
 		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);
+				wpa_s, s, go, 0, op_freq, 0, 0, 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,
@@ -2828,6 +3052,8 @@
 				       " unknown-network",
 				       MAC2STR(sa), MAC2STR(go_dev_addr));
 		}
+		wpas_notify_p2p_invitation_received(wpa_s, sa, go_dev_addr,
+						    bssid, 0, op_freq);
 		return;
 	}
 
@@ -2840,6 +3066,8 @@
 			       "sa=" MACSTR " persistent=%d",
 			       MAC2STR(sa), s->id);
 	}
+	wpas_notify_p2p_invitation_received(wpa_s, sa, go_dev_addr, bssid,
+					    s->id, op_freq);
 }
 
 
@@ -2896,7 +3124,7 @@
 	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 = wpas_p2p_get_persistent(wpa_s->p2pdev, peer,
 				       ssid->ssid, ssid->ssid_len);
 	wpas_remove_persistent_peer(wpa_s, ssid, peer, 1);
 }
@@ -2924,9 +3152,37 @@
 	wpa_printf(MSG_DEBUG, "P2P: Invitation result - status=%d peer=" MACSTR,
 		   status, MAC2STR(peer));
 	if (wpa_s->pending_invite_ssid_id == -1) {
+		struct wpa_supplicant *group_if =
+			wpa_s->global->p2p_invite_group;
+
 		if (status == P2P_SC_FAIL_UNKNOWN_GROUP)
 			wpas_remove_persistent_client(wpa_s, peer);
-		return; /* Invitation to active group */
+
+		/*
+		 * Invitation to an active group. If this is successful and we
+		 * are the GO, set the client wait to postpone some concurrent
+		 * operations and to allow provisioning and connection to happen
+		 * more quickly.
+		 */
+		if (status == P2P_SC_SUCCESS &&
+		    group_if && group_if->current_ssid &&
+		    group_if->current_ssid->mode == WPAS_MODE_P2P_GO) {
+			os_get_reltime(&wpa_s->global->p2p_go_wait_client);
+#ifdef CONFIG_TESTING_OPTIONS
+			if (group_if->p2p_go_csa_on_inv) {
+				wpa_printf(MSG_DEBUG,
+					   "Testing: force P2P GO CSA after invitation");
+				eloop_cancel_timeout(
+					wpas_p2p_reconsider_moving_go,
+					wpa_s, NULL);
+				eloop_register_timeout(
+					0, 50000,
+					wpas_p2p_reconsider_moving_go,
+					wpa_s, NULL);
+			}
+#endif /* CONFIG_TESTING_OPTIONS */
+		}
+		return;
 	}
 
 	if (status == P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE) {
@@ -2966,10 +3222,10 @@
 	os_sleep(0, 50000);
 
 	if (neg_freq > 0 && ssid->mode == WPAS_MODE_P2P_GO &&
-	    freq_included(channels, neg_freq))
+	    freq_included(wpa_s, channels, neg_freq))
 		freq = neg_freq;
 	else if (peer_oper_freq > 0 && ssid->mode != WPAS_MODE_P2P_GO &&
-		 freq_included(channels, peer_oper_freq))
+		 freq_included(wpa_s, channels, peer_oper_freq))
 		freq = peer_oper_freq;
 	else
 		freq = 0;
@@ -2980,11 +3236,13 @@
 				      ssid->mode == WPAS_MODE_P2P_GO,
 				      wpa_s->p2p_persistent_go_freq,
 				      freq,
+				      wpa_s->p2p_go_vht_center_freq2,
 				      wpa_s->p2p_go_ht40, wpa_s->p2p_go_vht,
+				      wpa_s->p2p_go_max_oper_chwidth,
 				      channels,
 				      ssid->mode == WPAS_MODE_P2P_GO ?
 				      P2P_MAX_INITIAL_CONN_WAIT_GO_REINVOKE :
-				      0);
+				      0, 1);
 }
 
 
@@ -3066,21 +3324,6 @@
 }
 
 
-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
 };
@@ -3114,43 +3357,6 @@
 }
 
 
-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)
@@ -3212,6 +3418,75 @@
 }
 
 
+static int wpas_p2p_get_center_160mhz(struct wpa_supplicant *wpa_s,
+				     struct hostapd_hw_modes *mode,
+				     u8 channel)
+{
+	u8 center_channels[] = { 50, 114 };
+	unsigned int i;
+
+	if (mode->mode != HOSTAPD_MODE_IEEE80211A)
+		return 0;
+
+	for (i = 0; i < ARRAY_SIZE(center_channels); i++)
+		/*
+		 * In 160 MHz, the bandwidth "spans" 28 channels (e.g., 36-64),
+		 * so the center channel is 14 channels away from the start/end.
+		 */
+		if (channel >= center_channels[i] - 14 &&
+		    channel <= center_channels[i] + 14)
+			return center_channels[i];
+
+	return 0;
+}
+
+
+static enum chan_allowed wpas_p2p_verify_160mhz(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_160mhz(wpa_s, mode, channel);
+	if (!center_chan)
+		return NOT_ALLOWED;
+	/* VHT 160 MHz uses DFS channels in most countries. */
+
+	/* Check all the channels are available */
+	for (i = 0; i < 8; i++) {
+		int adj_chan = center_chan - 14 + 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_150))
+			return NOT_ALLOWED;
+		if (i == 1 && !(flags & HOSTAPD_CHAN_VHT_30_130))
+			return NOT_ALLOWED;
+		if (i == 2 && !(flags & HOSTAPD_CHAN_VHT_50_110))
+			return NOT_ALLOWED;
+		if (i == 3 && !(flags & HOSTAPD_CHAN_VHT_70_90))
+			return NOT_ALLOWED;
+		if (i == 4 && !(flags & HOSTAPD_CHAN_VHT_90_70))
+			return NOT_ALLOWED;
+		if (i == 5 && !(flags & HOSTAPD_CHAN_VHT_110_50))
+			return NOT_ALLOWED;
+		if (i == 6 && !(flags & HOSTAPD_CHAN_VHT_130_30))
+			return NOT_ALLOWED;
+		if (i == 7 && !(flags & HOSTAPD_CHAN_VHT_150_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)
@@ -3230,6 +3505,8 @@
 		res2 = has_channel(wpa_s->global, mode, channel + 4, NULL);
 	} else if (bw == BW80) {
 		res2 = wpas_p2p_verify_80mhz(wpa_s, mode, channel, bw);
+	} else if (bw == BW160) {
+		res2 = wpas_p2p_verify_160mhz(wpa_s, mode, channel, bw);
 	}
 
 	if (res == NOT_ALLOWED || res2 == NOT_ALLOWED)
@@ -3256,11 +3533,14 @@
 
 	cla = cli_cla = 0;
 
-	for (op = 0; op_class[op].op_class; op++) {
-		const struct p2p_oper_class_map *o = &op_class[op];
+	for (op = 0; global_op_class[op].op_class; op++) {
+		const struct oper_class_map *o = &global_op_class[op];
 		u8 ch;
 		struct p2p_reg_class *reg = NULL, *cli_reg = NULL;
 
+		if (o->p2p == NO_P2P_SUPP)
+			continue;
+
 		mode = get_mode(wpa_s->hw.modes, wpa_s->hw.num_modes, o->mode);
 		if (mode == NULL)
 			continue;
@@ -3315,10 +3595,13 @@
 	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];
+	for (op = 0; global_op_class[op].op_class; op++) {
+		const struct oper_class_map *o = &global_op_class[op];
 		u8 ch;
 
+		if (o->p2p == NO_P2P_SUPP)
+			continue;
+
 		for (ch = o->min_chan; ch <= o->max_chan; ch += o->inc) {
 			if (o->mode != HOSTAPD_MODE_IEEE80211A ||
 			    (o->bw != BW40PLUS && o->bw != BW40MINUS) ||
@@ -3343,6 +3626,15 @@
 }
 
 
+int wpas_p2p_get_vht160_center(struct wpa_supplicant *wpa_s,
+			       struct hostapd_hw_modes *mode, u8 channel)
+{
+	if (!wpas_p2p_verify_channel(wpa_s, mode, channel, BW160))
+		return 0;
+	return wpas_p2p_get_center_160mhz(wpa_s, mode, channel);
+}
+
+
 static int wpas_get_noa(void *ctx, const u8 *interface_addr, u8 *buf,
 			size_t buf_len)
 {
@@ -3386,12 +3678,7 @@
 {
 	for (wpa_s = wpa_s->global->ifaces; wpa_s; wpa_s = wpa_s->next) {
 		struct wpa_ssid *ssid = wpa_s->current_ssid;
-		if (ssid == NULL)
-			continue;
-		if (ssid->mode != WPAS_MODE_INFRA)
-			continue;
-		if (wpa_s->wpa_state != WPA_COMPLETED &&
-		    wpa_s->wpa_state != WPA_GROUP_HANDSHAKE)
+		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;
@@ -3479,6 +3766,7 @@
 		return -1;
 	}
 
+	p2pdev_wpa_s->p2pdev = p2pdev_wpa_s;
 	wpa_s->pending_interface_name[0] = '\0';
 	return 0;
 }
@@ -3508,7 +3796,8 @@
 
 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 *ret_ssid, size_t *ret_ssid_len,
+				     u8 *intended_iface_addr)
 {
 	struct wpa_supplicant *wpa_s = ctx;
 	struct wpa_ssid *s;
@@ -3518,6 +3807,19 @@
 		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;
 	}
 
@@ -3526,24 +3828,40 @@
 
 
 static int wpas_get_go_info(void *ctx, u8 *intended_addr,
-			    u8 *ssid, size_t *ssid_len, int *group_iface)
+			    u8 *ssid, size_t *ssid_len, int *group_iface,
+			    unsigned int *freq)
 {
 	struct wpa_supplicant *wpa_s = ctx;
+	struct wpa_supplicant *go;
 	struct wpa_ssid *s;
-	u8 bssid[ETH_ALEN];
 
-	s = wpas_p2p_group_go_ssid(wpa_s, bssid);
-	if (!s) {
+	/*
+	 * 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;
+
+	if (freq)
+		*freq = 0;
+
+	go = wpas_p2p_get_go_group(wpa_s);
+	if (!go) {
 		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);
+			os_memcpy(intended_addr, s->bssid, ETH_ALEN);
+		else
+			return 0;
+	} else {
+		s = go->current_ssid;
+		os_memcpy(intended_addr, go->own_addr, ETH_ALEN);
+		if (freq)
+			*freq = go->assoc_freq;
 	}
 
-	*group_iface = wpas_p2p_create_iface(wpa_s);
-	if (!s)
-		return 0;
-
-	os_memcpy(intended_addr, bssid, ETH_ALEN);
 	os_memcpy(ssid, s->ssid, s->ssid_len);
 	*ssid_len = s->ssid_len;
 
@@ -3596,19 +3914,51 @@
 }
 
 
+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)
+				    int prov_start, const char *session_info,
+				    const u8 *feat_cap, size_t feat_cap_len,
+				    unsigned int freq,
+				    const u8 *group_ssid, size_t group_ssid_len)
 {
 	struct wpa_supplicant *wpa_s = ctx;
 	u8 mac[ETH_ALEN];
-	struct wpa_ssid *persistent_go, *stale, *s;
+	struct wpa_ssid *persistent_go, *stale, *s = NULL;
 	int save_config = 0;
 	struct wpa_supplicant *go_wpa_s;
+	char feat_cap_str[256];
 
 	if (!dev)
 		return;
@@ -3621,6 +3971,9 @@
 	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,
@@ -3628,22 +3981,22 @@
 				       " adv_id=%x conncap=%x"
 				       " adv_mac=" MACSTR
 				       " session=%x mac=" MACSTR
-				       " dev_passwd_id=%d",
+				       " dev_passwd_id=%d%s",
 				       MAC2STR(dev), adv_id, conncap,
 				       MAC2STR(adv_mac),
 				       ses_id, MAC2STR(ses_mac),
-				       passwd_id);
+				       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'",
+				       " dev_passwd_id=%d info='%s'%s",
 				       MAC2STR(dev), adv_id, conncap,
 				       MAC2STR(adv_mac),
 				       ses_id, MAC2STR(ses_mac),
-				       passwd_id, session_info);
+				       passwd_id, session_info, feat_cap_str);
 		}
 		return;
 	}
@@ -3665,16 +4018,25 @@
 			       P2P_EVENT_P2PS_PROVISION_DONE MACSTR
 			       " status=%d"
 			       " adv_id=%x adv_mac=" MACSTR
-			       " session=%x mac=" MACSTR,
+			       " session=%x mac=" MACSTR "%s",
 			       MAC2STR(dev), status,
 			       adv_id, MAC2STR(adv_mac),
-			       ses_id, MAC2STR(ses_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 && persist_ssid_size)
+		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)
@@ -3730,29 +4092,41 @@
 			       " status=%d"
 			       " adv_id=%x adv_mac=" MACSTR
 			       " session=%x mac=" MACSTR
-			       " persist=%d",
+			       " persist=%d%s",
 			       MAC2STR(dev), status,
 			       adv_id, MAC2STR(adv_mac),
-			       ses_id, MAC2STR(ses_mac), s->id);
+			       ses_id, MAC2STR(ses_mac), s->id, feat_cap_str);
 		return;
 	}
 
 	if (conncap == P2PS_SETUP_GROUP_OWNER) {
-		const char *go_ifname = NULL;
+		/*
+		 * 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;
+			wpa_s->global->pending_p2ps_group_freq = freq;
 
-			if (wpa_s->conf->p2p_no_group_iface)
-				go_ifname = wpa_s->ifname;
+			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])
-				go_ifname = wpa_s->pending_interface_name;
+				os_memcpy(go_ifname,
+					  wpa_s->pending_interface_name,
+					  sizeof(go_ifname));
 
-			if (!go_ifname) {
+			if (!go_ifname[0]) {
 				wpas_p2ps_prov_complete(
 					wpa_s, P2P_SC_FAIL_UNKNOWN_GROUP,
 					dev, adv_mac, ses_mac,
-					NULL, adv_id, ses_id, 0, 0,
-					NULL, 0, 0, 0, NULL);
+					grp_mac, adv_id, ses_id, 0, 0,
+					NULL, 0, 0, 0, NULL, NULL, 0, 0,
+					NULL, 0);
 				return;
 			}
 
@@ -3760,34 +4134,41 @@
 			if (response_done && persistent_go) {
 				wpas_p2p_group_add_persistent(
 					wpa_s, persistent_go,
-					0, 0, 0, 0, 0, NULL,
+					0, 0, freq, 0, 0, 0, 0, NULL,
 					persistent_go->mode ==
 					WPAS_MODE_P2P_GO ?
 					P2P_MAX_INITIAL_CONN_WAIT_GO_REINVOKE :
-					0);
+					0, 0);
 			} else if (response_done) {
-				wpas_p2p_group_add(wpa_s, 1, 0, 0, 0);
+				wpas_p2p_group_add(wpa_s, 1, freq, 0, 0, 0, 0);
 			}
 
 			if (passwd_id == DEV_PW_P2PS_DEFAULT) {
-				os_memcpy(wpa_s->p2ps_join_addr, dev, ETH_ALEN);
-				wpa_s->p2ps_join_addr_valid = 1;
-				wpa_dbg(wpa_s, MSG_DEBUG,
-					"P2PS: Saving PIN for " MACSTR,
-					MAC2STR(dev));
+				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) {
-			go_ifname = go_wpa_s->ifname;
+			os_memcpy(go_ifname, go_wpa_s->ifname,
+				  sizeof(go_ifname));
 
-			wpa_dbg(go_wpa_s, MSG_DEBUG,
-				"P2P: Setting PIN-1 For " MACSTR, MAC2STR(dev));
-			wpa_supplicant_ap_wps_pin(go_wpa_s, dev, "12345670",
-						  NULL, 0, 0);
+			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, dev, ETH_ALEN);
-			wpa_s->p2ps_join_addr_valid = 1;
-			wpa_dbg(wpa_s, MSG_DEBUG,
-				"P2PS: Saving PIN for " MACSTR, MAC2STR(dev));
+			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,
@@ -3795,11 +4176,11 @@
 			       " status=%d conncap=%x"
 			       " adv_id=%x adv_mac=" MACSTR
 			       " session=%x mac=" MACSTR
-			       " dev_passwd_id=%d go=%s",
+			       " 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);
+			       passwd_id, go_ifname, feat_cap_str);
 		return;
 	}
 
@@ -3812,27 +4193,35 @@
 	}
 
 	if (conncap == P2PS_SETUP_CLIENT) {
+		char ssid_hex[32 * 2 + 1];
+
+		if (group_ssid)
+			wpa_snprintf_hex(ssid_hex, sizeof(ssid_hex),
+					 group_ssid, group_ssid_len);
+		else
+			ssid_hex[0] = '\0';
 		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,
+			       " dev_passwd_id=%d join=" MACSTR "%s%s%s",
 			       MAC2STR(dev), status, conncap,
 			       adv_id, MAC2STR(adv_mac),
 			       ses_id, MAC2STR(ses_mac),
-			       passwd_id, MAC2STR(grp_mac));
+			       passwd_id, MAC2STR(grp_mac), feat_cap_str,
+			       group_ssid ? " group_ssid=" : "", ssid_hex);
 	} 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",
+			       " dev_passwd_id=%d%s",
 			       MAC2STR(dev), status, conncap,
 			       adv_id, MAC2STR(adv_mac),
 			       ses_id, MAC2STR(ses_mac),
-			       passwd_id);
+			       passwd_id, feat_cap_str);
 	}
 }
 
@@ -3848,10 +4237,13 @@
 {
 	struct wpa_supplicant *wpa_s = ctx;
 	struct wpa_ssid *persistent_go;
+	unsigned int freq;
 
 	if (!wpa_s->global->pending_p2ps_group)
 		return 0;
 
+	freq = wpa_s->global->pending_p2ps_group_freq;
+	wpa_s->global->pending_p2ps_group_freq = 0;
 	wpa_s->global->pending_p2ps_group = 0;
 
 	if (wpas_p2p_get_go_group(wpa_s))
@@ -3860,17 +4252,28 @@
 
 	if (persistent_go) {
 		wpas_p2p_group_add_persistent(
-			wpa_s, persistent_go, 0, 0, 0, 0, 0, NULL,
+			wpa_s, persistent_go, 0, 0, 0, 0, 0, 0, 0, NULL,
 			persistent_go->mode == WPAS_MODE_P2P_GO ?
-			P2P_MAX_INITIAL_CONN_WAIT_GO_REINVOKE : 0);
+			P2P_MAX_INITIAL_CONN_WAIT_GO_REINVOKE : 0, 0);
 	} else {
-		wpas_p2p_group_add(wpa_s, 1, 0, 0, 0);
+		wpas_p2p_group_add(wpa_s, 1, freq, 0, 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()
@@ -3924,6 +4327,7 @@
 	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);
@@ -4144,8 +4548,7 @@
 
 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)
+	if (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 |
@@ -4226,8 +4629,9 @@
 				       MAC2STR(wpa_s->pending_join_dev_addr));
 			return;
 		}
-		wpa_msg_global(wpa_s->parent, MSG_INFO,
+		wpa_msg_global(wpa_s->p2pdev, MSG_INFO,
 			       P2P_EVENT_GROUP_FORMATION_FAILURE);
+		wpas_notify_p2p_group_formation_failure(wpa_s, "");
 	}
 }
 
@@ -4361,7 +4765,7 @@
 		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,
+			wpa_msg_global(wpa_s->p2pdev, MSG_INFO,
 				       P2P_EVENT_FALLBACK_TO_GO_NEG
 				       "reason=peer-not-running-GO");
 			wpas_p2p_connect(wpa_s, wpa_s->pending_join_dev_addr,
@@ -4369,10 +4773,13 @@
 					 wpa_s->p2p_persistent_group, 0, 0, 0,
 					 wpa_s->p2p_go_intent,
 					 wpa_s->p2p_connect_freq,
+					 wpa_s->p2p_go_vht_center_freq2,
 					 wpa_s->p2p_persistent_id,
 					 wpa_s->p2p_pd_before_go_neg,
 					 wpa_s->p2p_go_ht40,
-					 wpa_s->p2p_go_vht);
+					 wpa_s->p2p_go_vht,
+					 wpa_s->p2p_go_max_oper_chwidth,
+					 NULL, 0);
 			return;
 		}
 
@@ -4380,7 +4787,7 @@
 			   "try to join the group", join ? "" :
 			   " in older scan");
 		if (!join) {
-			wpa_msg_global(wpa_s->parent, MSG_INFO,
+			wpa_msg_global(wpa_s->p2pdev, MSG_INFO,
 				       P2P_EVENT_FALLBACK_TO_GO_NEG_ENABLED);
 			wpa_s->p2p_fallback_to_go_neg = 1;
 		}
@@ -4418,8 +4825,7 @@
 		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) {
+	} else 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,
@@ -4450,9 +4856,11 @@
 		u16 method;
 
 		if (wpas_check_freq_conflict(wpa_s, freq) > 0) {
-			wpa_msg_global(wpa_s->parent, MSG_INFO,
+			wpa_msg_global(wpa_s->p2pdev, MSG_INFO,
 				       P2P_EVENT_GROUP_FORMATION_FAILURE
 				       "reason=FREQ_CONFLICT");
+			wpas_notify_p2p_group_formation_failure(
+				wpa_s, "FREQ_CONFLICT");
 			return;
 		}
 
@@ -4472,6 +4880,9 @@
 		case WPS_PBC:
 			method = WPS_CONFIG_PUSHBUTTON;
 			break;
+		case WPS_P2PS:
+			method = WPS_CONFIG_P2PS;
+			break;
 		default:
 			method = 0;
 			break;
@@ -4513,7 +4924,8 @@
 
 start:
 	/* Start join operation immediately */
-	wpas_p2p_join_start(wpa_s, 0, NULL, 0);
+	wpas_p2p_join_start(wpa_s, 0, wpa_s->p2p_join_ssid,
+			    wpa_s->p2p_join_ssid_len);
 }
 
 
@@ -4525,6 +4937,7 @@
 	struct wpabuf *wps_ie, *ies;
 	size_t ielen;
 	int freqs[2] = { 0, 0 };
+	unsigned int bands;
 
 	os_memset(&params, 0, sizeof(params));
 
@@ -4550,22 +4963,6 @@
 		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;
 		/*
@@ -4582,6 +4979,23 @@
 		params.freqs = freqs;
 	}
 
+	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);
+
+	bands = wpas_get_bands(wpa_s, freqs);
+	p2p_scan_ie(wpa_s->global->p2p, ies, NULL, bands);
+
+	params.p2p_probe = 1;
+	params.extra_ies = wpabuf_head(ies);
+	params.extra_ies_len = wpabuf_len(ies);
+
 	/*
 	 * Run a scan to update BSS table and start Provision Discovery once
 	 * the new scan results become available.
@@ -4679,8 +5093,13 @@
 		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 (ssid && ssid_len) {
+			bss = wpa_bss_get(wpa_s, wpa_s->pending_join_iface_addr,
+					  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;
@@ -4688,6 +5107,11 @@
 			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));
+		} else if (ssid && ssid_len) {
+			res.ssid_len = ssid_len;
+			os_memcpy(res.ssid, ssid, ssid_len);
+			wpa_printf(MSG_DEBUG, "P2P: Join target GO (SSID %s)",
+				   wpa_ssid_txt(ssid, ssid_len));
 		}
 	}
 
@@ -4714,11 +5138,16 @@
 
 
 static int wpas_p2p_setup_freqs(struct wpa_supplicant *wpa_s, int freq,
-				int *force_freq, int *pref_freq, int go)
+				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;
+	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));
@@ -4783,6 +5212,47 @@
 
 	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) {
@@ -4826,12 +5296,17 @@
  *	initiating Group Owner negotiation
  * @go_intent: GO Intent or -1 to use default
  * @freq: Frequency for the group or 0 for auto-selection
+ * @freq2: Center frequency of segment 1 for the GO operating in VHT 80P80 mode
  * @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
+ * @vht_chwidth: Channel width supported by GO operating with VHT support
+ *	(VHT_CHANWIDTH_*).
+ * @group_ssid: Specific Group SSID for join or %NULL if not set
+ * @group_ssid_len: Length of @group_ssid in octets
  * Returns: 0 or new PIN (if pin was %NULL) on success, -1 on unspecified
  *	failure, -2 on failure due to channel not currently available,
  *	-3 if forced channel is not supported
@@ -4839,14 +5314,17 @@
 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 go_intent, int freq, unsigned int vht_center_freq2,
+		     int persistent_id, int pd, int ht40, int vht,
+		     unsigned int vht_chwidth, const u8 *group_ssid,
+		     size_t group_ssid_len)
 {
 	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;
@@ -4863,6 +5341,8 @@
 
 	wpa_s->global->p2p_fail_on_wps_complete = 0;
 	wpa_s->global->pending_p2ps_group = 0;
+	wpa_s->global->pending_p2ps_group_freq = 0;
+	wpa_s->p2ps_method_config_any = 0;
 
 	if (go_intent < 0)
 		go_intent = wpa_s->conf->p2p_go_intent;
@@ -4879,11 +5359,14 @@
 	wpa_s->p2p_pd_before_go_neg = !!pd;
 	wpa_s->p2p_go_ht40 = !!ht40;
 	wpa_s->p2p_go_vht = !!vht;
+	wpa_s->p2p_go_vht_center_freq2 = vht_center_freq2;
+	wpa_s->p2p_go_max_oper_chwidth = vht_chwidth;
 
 	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();
+		if (wps_generate_pin((unsigned int *) &ret) < 0)
+			return -1;
 		res = os_snprintf(wpa_s->p2p_pin, sizeof(wpa_s->p2p_pin),
 				  "%08d", ret);
 		if (os_snprintf_error(sizeof(wpa_s->p2p_pin), res))
@@ -4918,18 +5401,22 @@
 		}
 		wpa_s->user_initiated_pd = 1;
 		if (wpas_p2p_join(wpa_s, iface_addr, dev_addr, wps_method,
-				  auto_join, freq, NULL, 0) < 0)
+				  auto_join, freq,
+				  group_ssid, group_ssid_len) < 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);
+				   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) {
@@ -4944,8 +5431,13 @@
 		}
 
 		if_addr = wpa_s->pending_interface_addr;
-	} else
-		if_addr = wpa_s->own_addr;
+	} else {
+		if (wpa_s->p2p_mgmt)
+			if_addr = wpa_s->parent->own_addr;
+		else
+			if_addr = wpa_s->own_addr;
+		os_memset(wpa_s->go_dev_addr, 0, ETH_ALEN);
+	}
 
 	if (auth) {
 		if (wpas_p2p_auth_go_neg(wpa_s, peer_addr, wps_method,
@@ -5090,6 +5582,38 @@
 {
 	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");
@@ -5153,30 +5677,45 @@
 }
 
 
-static int wpas_p2p_select_freq_no_pref(struct wpa_supplicant *wpa_s,
-					struct p2p_go_neg_results *params,
-					const struct p2p_channels *channels)
+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 -1;
+		return;
 
 	for (i = 0; i < 3; i++) {
 		params->freq = 2412 + ((r + i) % 3) * 25;
-		if (!wpas_p2p_disallowed_freq(wpa_s->global, params->freq) &&
-		    freq_included(channels, params->freq) &&
-		    p2p_supported_freq(wpa_s->global->p2p, params->freq))
+		if (wpas_p2p_supported_freq_go(wpa_s, channels, params->freq))
 			goto out;
 	}
 
-	/* try all channels in reg. class 81 */
+	/* try all other channels in operating class 81 */
 	for (i = 0; i < 11; i++) {
 		params->freq = 2412 + i * 5;
-		if (!wpas_p2p_disallowed_freq(wpa_s->global, params->freq) &&
-		    freq_included(channels, params->freq) &&
-		    p2p_supported_freq(wpa_s->global->p2p, params->freq))
+
+		/* 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;
 	}
 
@@ -5184,7 +5723,7 @@
 	for (i = 0; i < 4; i++) {
 		params->freq = 5180 + i * 20;
 		if (!wpas_p2p_disallowed_freq(wpa_s->global, params->freq) &&
-		    freq_included(channels, params->freq) &&
+		    freq_included(wpa_s, channels, params->freq) &&
 		    p2p_supported_freq(wpa_s->global->p2p, params->freq))
 			goto out;
 	}
@@ -5193,7 +5732,7 @@
 	for (i = 0; i < 4; i++) {
 		params->freq = 5745 + i * 20;
 		if (!wpas_p2p_disallowed_freq(wpa_s->global, params->freq) &&
-		    freq_included(channels, params->freq) &&
+		    freq_included(wpa_s, channels, params->freq) &&
 		    p2p_supported_freq(wpa_s->global->p2p, params->freq))
 			goto out;
 	}
@@ -5201,7 +5740,7 @@
 	/* try social channel class 180 channel 2 */
 	params->freq = 58320 + 1 * 2160;
 	if (!wpas_p2p_disallowed_freq(wpa_s->global, params->freq) &&
-	    freq_included(channels, params->freq) &&
+	    freq_included(wpa_s, channels, params->freq) &&
 	    p2p_supported_freq(wpa_s->global->p2p, params->freq))
 		goto out;
 
@@ -5209,150 +5748,229 @@
 	for (i = 0; i < 4; i++) {
 		params->freq = 58320 + i * 2160;
 		if (!wpas_p2p_disallowed_freq(wpa_s->global, params->freq) &&
-		    freq_included(channels, 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 -1;
+	return;
 out:
 	wpa_printf(MSG_DEBUG, "P2P: Set GO freq %d MHz (no preference known)",
 		   params->freq);
-	return 0;
 }
 
 
 static int wpas_p2p_init_go_params(struct wpa_supplicant *wpa_s,
 				   struct p2p_go_neg_results *params,
-				   int freq, int ht40, int vht,
+				   int freq, int vht_center_freq2, int ht40,
+				   int vht, int max_oper_chwidth,
 				   const struct p2p_channels *channels)
 {
 	struct wpa_used_freq_data *freqs;
-	unsigned int pref_freq, cand_freq;
+	unsigned int cand;
 	unsigned int num, i;
+	int ignore_no_freqs = 0;
+	int unused_channels = wpas_p2p_num_unused_channels(wpa_s) > 0;
 
 	os_memset(params, 0, sizeof(*params));
 	params->role_go = 1;
 	params->ht40 = ht40;
 	params->vht = vht;
-	if (freq) {
-		if (!freq_included(channels, freq)) {
-			wpa_printf(MSG_DEBUG, "P2P: Forced GO freq %d MHz not "
-				   "accepted", freq);
-			return -1;
-		}
-		wpa_printf(MSG_DEBUG, "P2P: Set GO freq based on forced "
-			   "frequency %d MHz", freq);
-		params->freq = freq;
-	} else if (wpa_s->conf->p2p_oper_reg_class == 81 &&
-		   wpa_s->conf->p2p_oper_channel >= 1 &&
-		   wpa_s->conf->p2p_oper_channel <= 11 &&
-		   freq_included(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);
-	} else 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) &&
-		   freq_included(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);
-	} else if (wpa_s->conf->p2p_oper_channel == 0 &&
-		   wpa_s->best_overall_freq > 0 &&
-		   p2p_supported_freq_go(wpa_s->global->p2p,
-					 wpa_s->best_overall_freq) &&
-		   freq_included(channels, wpa_s->best_overall_freq)) {
-		params->freq = wpa_s->best_overall_freq;
-		wpa_printf(MSG_DEBUG, "P2P: Set GO freq based on best overall "
-			   "channel %d MHz", params->freq);
-	} else if (wpa_s->conf->p2p_oper_channel == 0 &&
-		   wpa_s->best_24_freq > 0 &&
-		   p2p_supported_freq_go(wpa_s->global->p2p,
-					 wpa_s->best_24_freq) &&
-		   freq_included(channels, wpa_s->best_24_freq)) {
-		params->freq = wpa_s->best_24_freq;
-		wpa_printf(MSG_DEBUG, "P2P: Set GO freq based on best 2.4 GHz "
-			   "channel %d MHz", params->freq);
-	} else if (wpa_s->conf->p2p_oper_channel == 0 &&
-		   wpa_s->best_5_freq > 0 &&
-		   p2p_supported_freq_go(wpa_s->global->p2p,
-					 wpa_s->best_5_freq) &&
-		   freq_included(channels, wpa_s->best_5_freq)) {
-		params->freq = wpa_s->best_5_freq;
-		wpa_printf(MSG_DEBUG, "P2P: Set GO freq based on best 5 GHz "
-			   "channel %d MHz", params->freq);
-	} else if ((pref_freq = p2p_get_pref_freq(wpa_s->global->p2p,
-						  channels))) {
-		params->freq = pref_freq;
-		wpa_printf(MSG_DEBUG, "P2P: Set GO freq %d MHz from preferred "
-			   "channels", params->freq);
-	} else {
-		/* no preference, select some channel */
-		if (wpas_p2p_select_freq_no_pref(wpa_s, params, channels) < 0)
-			return -1;
-	}
+	params->max_oper_chwidth = max_oper_chwidth;
+	params->vht_center_freq2 = vht_center_freq2;
 
 	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);
+	num = get_shared_radio_freqs_data(wpa_s, freqs,
+					  wpa_s->num_multichan_concurrent);
 
-	cand_freq = wpas_p2p_pick_best_used_freq(wpa_s, freqs, num);
+	if (wpa_s->current_ssid &&
+	    wpa_s->current_ssid->mode == WPAS_MODE_P2P_GO &&
+	    wpa_s->wpa_state == WPA_COMPLETED) {
+		wpa_printf(MSG_DEBUG, "P2P: %s called for an active GO",
+			   __func__);
 
-	/* First try the best used frequency if possible */
-	if (!freq && cand_freq > 0 && freq_included(channels, cand_freq)) {
-		params->freq = cand_freq;
-	} else if (!freq) {
-		/* Try any of the used frequencies */
+		/*
+		 * If the frequency selection is done for an active P2P GO that
+		 * is not sharing a frequency, allow to select a new frequency
+		 * even if there are no unused frequencies as we are about to
+		 * move the P2P GO so its frequency can be re-used.
+		 */
 		for (i = 0; i < num; i++) {
-			if (freq_included(channels, freqs[i].freq)) {
-				wpa_printf(MSG_DEBUG, "P2P: Force GO on a channel we are already using (%u MHz)",
-					   freqs[i].freq);
-				params->freq = freqs[i].freq;
+			if (freqs[i].freq == wpa_s->current_ssid->frequency &&
+			    freqs[i].flags == 0) {
+				ignore_no_freqs = 1;
 				break;
 			}
 		}
-
-		if (i == num) {
-			if (wpas_p2p_num_unused_channels(wpa_s) <= 0) {
-				wpa_printf(MSG_DEBUG, "P2P: Cannot force GO on any of the channels we are already using");
-				os_free(freqs);
-				return -1;
-			} else {
-				wpa_printf(MSG_DEBUG, "P2P: Cannot force GO on any of the channels we are already using. Use one of the free channels");
-			}
-		}
-	} else {
-		for (i = 0; i < num; i++) {
-			if (freqs[i].freq == freq)
-				break;
-		}
-
-		if (i == num) {
-			if (wpas_p2p_num_unused_channels(wpa_s) <= 0) {
-				if (freq)
-					wpa_printf(MSG_DEBUG, "P2P: Cannot force GO on freq (%u MHz) as all the channels are in use", freq);
-				os_free(freqs);
-				return -1;
-			} else {
-				wpa_printf(MSG_DEBUG, "P2P: Use one of the free channels");
-			}
-		}
 	}
 
+	/* 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 (!ignore_no_freqs && !unused_channels) {
+			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 &&
+	    (!wpa_s->conf->p2p_ignore_shared_freq || !unused_channels)) {
+		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",
+				   cand);
+			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",
+					   freqs[i].freq);
+				params->freq = freqs[i].freq;
+				goto success;
+			}
+		}
+	}
+
+	if (!ignore_no_freqs && !unused_channels) {
+		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;
 }
 
 
@@ -5363,9 +5981,20 @@
 	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");
+		if (wpa_s->p2p_mgmt) {
+			/*
+			 * We may be called on the p2p_dev interface which
+			 * cannot be used for group operations, so always use
+			 * the primary interface.
+			 */
+			wpa_s->parent->p2pdev = wpa_s;
+			wpa_s = wpa_s->parent;
+		}
+		wpa_dbg(wpa_s, MSG_DEBUG,
+			"P2P: Use primary interface for group operations");
 		wpa_s->p2p_first_connection_timeout = 0;
+		if (wpa_s != wpa_s->p2pdev)
+			wpas_p2p_clone_config_dh(wpa_s, wpa_s->p2pdev);
 		return wpa_s;
 	}
 
@@ -5395,15 +6024,18 @@
  * @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
+ * @vht_center_freq2: segment_1 center frequency for GO operating in VHT 80P80
  * @ht40: Start GO with 40 MHz channel width
  * @vht:  Start GO with VHT support
+ * @vht_chwidth: channel bandwidth for GO operating 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)
+		       int freq, int vht_center_freq2, int ht40, int vht,
+		       int max_oper_chwidth)
 {
 	struct p2p_go_neg_results params;
 
@@ -5421,7 +6053,8 @@
 	if (freq < 0)
 		return -1;
 
-	if (wpas_p2p_init_go_params(wpa_s, &params, freq, ht40, vht, NULL))
+	if (wpas_p2p_init_go_params(wpa_s, &params, freq, vht_center_freq2,
+				    ht40, vht, max_oper_chwidth, NULL))
 		return -1;
 	if (params.freq &&
 	    !p2p_supported_freq_go(wpa_s->global->p2p, params.freq)) {
@@ -5455,13 +6088,15 @@
 
 static int wpas_start_p2p_client(struct wpa_supplicant *wpa_s,
 				 struct wpa_ssid *params, int addr_allocated,
-				 int freq)
+				 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);
@@ -5469,11 +6104,14 @@
 	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->pbss = params->pbss;
+	ssid->pairwise_cipher = params->pbss ? WPA_CIPHER_GCMP :
+		WPA_CIPHER_CCMP;
+	ssid->group_cipher = params->pbss ? WPA_CIPHER_GCMP : WPA_CIPHER_CCMP;
 	ssid->key_mgmt = WPA_KEY_MGMT_PSK;
 	ssid->ssid = os_malloc(params->ssid_len);
 	if (ssid->ssid == NULL) {
@@ -5494,12 +6132,14 @@
 	wpa_s->show_group_started = 1;
 	wpa_s->p2p_in_invitation = 1;
 	wpa_s->p2p_invite_go_freq = freq;
+	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,
+	eloop_cancel_timeout(wpas_p2p_group_formation_timeout, wpa_s->p2pdev,
 			     NULL);
 	eloop_register_timeout(P2P_MAX_INITIAL_CONN_WAIT, 0,
 			       wpas_p2p_group_formation_timeout,
-			       wpa_s->parent, NULL);
+			       wpa_s->p2pdev, NULL);
 	wpa_supplicant_select_network(wpa_s, ssid);
 
 	return 0;
@@ -5508,9 +6148,11 @@
 
 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_freq, int neg_freq,
+				  int vht_center_freq2, int ht40,
+				  int vht, int max_oper_chwidth,
+				  const struct p2p_channels *channels,
+				  int connection_timeout, int force_scan)
 {
 	struct p2p_go_neg_results params;
 	int go = 0, freq;
@@ -5524,7 +6166,7 @@
 			   "already running");
 		if (go == 0 &&
 		    eloop_cancel_timeout(wpas_p2p_group_formation_timeout,
-					 wpa_s->parent, NULL)) {
+					 wpa_s->p2pdev, NULL)) {
 			/*
 			 * This can happen if Invitation Response frame was lost
 			 * and the peer (GO of a persistent group) tries to
@@ -5537,7 +6179,7 @@
 				   "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);
+					       wpa_s->p2pdev, NULL);
 		}
 		return 0;
 	}
@@ -5558,12 +6200,12 @@
 		} else {
 			freq = wpas_p2p_select_go_freq(wpa_s, neg_freq);
 			if (freq < 0 ||
-			    (freq > 0 && !freq_included(channels, freq)))
+			    (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(channels, 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);
@@ -5571,18 +6213,20 @@
 			os_get_reltime(&now);
 			if (bss &&
 			    !os_reltime_expired(&now, &bss->last_update, 5) &&
-			    freq_included(channels, bss->freq))
+			    freq_included(wpa_s, channels, bss->freq))
 				freq = bss->freq;
 			else
 				freq = 0;
 		}
 
-		return wpas_start_p2p_client(wpa_s, ssid, addr_allocated, freq);
+		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))
+	if (wpas_p2p_init_go_params(wpa_s, &params, freq, vht_center_freq2,
+				    ht40, vht, max_oper_chwidth, channels))
 		return -1;
 
 	params.role_go = 1;
@@ -5687,6 +6331,8 @@
 	cfg->cb_ctx = wpa_s;
 	cfg->ie_update = wpas_p2p_ie_update;
 	cfg->idle_update = wpas_p2p_idle_update;
+	cfg->ip_addr_alloc = WPA_GET_BE32(wpa_s->p2pdev->conf->ip_addr_start)
+		!= 0;
 
 	group = p2p_group_init(wpa_s->global->p2p, cfg);
 	if (group == NULL)
@@ -5718,7 +6364,7 @@
 		p2p_clear_provisioning_info(wpa_s->global->p2p, go_dev_addr);
 	}
 
-	eloop_cancel_timeout(wpas_p2p_group_formation_timeout, wpa_s->parent,
+	eloop_cancel_timeout(wpas_p2p_group_formation_timeout, wpa_s->p2pdev,
 			     NULL);
 	wpa_s->p2p_go_group_formation_completed = 1;
 	if (ssid && ssid->mode == WPAS_MODE_INFRA) {
@@ -5733,7 +6379,9 @@
 			P2P_MAX_INITIAL_CONN_WAIT);
 		eloop_register_timeout(P2P_MAX_INITIAL_CONN_WAIT, 0,
 				       wpas_p2p_group_formation_timeout,
-				       wpa_s->parent, NULL);
+				       wpa_s->p2pdev, NULL);
+		/* Complete group formation on successful data connection. */
+		wpa_s->p2p_go_group_formation_completed = 0;
 	} else if (ssid) {
 		/*
 		 * Use a separate timeout for initial data connection to
@@ -5745,7 +6393,7 @@
 			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);
+				       wpa_s->p2pdev, NULL);
 		/*
 		 * Complete group formation on first successful data connection
 		 */
@@ -5784,7 +6432,7 @@
 		wpa_s->global->p2p_fail_on_wps_complete = 1;
 		eloop_deplete_timeout(0, 50000,
 				      wpas_p2p_group_formation_timeout,
-				      wpa_s->parent, NULL);
+				      wpa_s->p2pdev, NULL);
 	}
 }
 
@@ -5809,11 +6457,14 @@
 	u16 config_methods;
 
 	wpa_s->global->pending_p2ps_group = 0;
+	wpa_s->global->pending_p2ps_group_freq = 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_s, P2PS_SETUP_NONE, p2ps_prov->role,
+			&p2ps_prov->force_freq, &p2ps_prov->pref_freq);
+
 		wpa_printf(MSG_DEBUG,
 			   "P2P: %s conncap: %d - ASP parsed: %x %x %d %s",
 			   __func__, p2ps_prov->conncap,
@@ -5874,7 +6525,12 @@
 	if (!offchannel_pending_action_tx(wpa_s))
 		return;
 
-	wpas_p2p_action_tx_clear(wpa_s);
+	if (wpa_s->p2p_send_action_work) {
+		wpas_p2p_free_send_action_work(wpa_s);
+		eloop_cancel_timeout(wpas_p2p_send_action_work_timeout,
+				     wpa_s, NULL);
+		offchannel_send_action_done(wpa_s);
+	}
 
 	wpa_printf(MSG_DEBUG, "P2P: Drop pending Action TX due to new "
 		   "operation request");
@@ -6070,12 +6726,15 @@
 
 void wpas_p2p_scan_ie(struct wpa_supplicant *wpa_s, struct wpabuf *ies)
 {
+	unsigned int bands;
+
 	if (wpa_s->global->p2p_disabled)
 		return;
 	if (wpa_s->global->p2p == NULL)
 		return;
 
-	p2p_scan_ie(wpa_s->global->p2p, ies, NULL);
+	bands = wpas_get_bands(wpa_s, NULL);
+	p2p_scan_ie(wpa_s->global->p2p, ies, NULL, bands);
 }
 
 
@@ -6105,13 +6764,15 @@
 /* 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)
+		    int vht_center_freq2, int ht40, int vht, int max_chwidth,
+		    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)
@@ -6121,6 +6782,9 @@
 
 	wpa_s->p2p_persistent_go_freq = freq;
 	wpa_s->p2p_go_ht40 = !!ht40;
+	wpa_s->p2p_go_vht = !!vht;
+	wpa_s->p2p_go_max_oper_chwidth = max_chwidth;
+	wpa_s->p2p_go_vht_center_freq2 = vht_center_freq2;
 	if (ssid->mode == WPAS_MODE_P2P_GO) {
 		role = P2P_INVITE_ROLE_GO;
 		if (peer_addr == NULL) {
@@ -6137,7 +6801,9 @@
 				return -1;
 			}
 			bssid = wpa_s->pending_interface_addr;
-		} else
+		} else if (wpa_s->p2p_mgmt)
+			bssid = wpa_s->parent->own_addr;
+		else
 			bssid = wpa_s->own_addr;
 	} else {
 		role = P2P_INVITE_ROLE_CLIENT;
@@ -6145,14 +6811,18 @@
 	}
 	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);
+				   role == P2P_INVITE_ROLE_GO,
+				   pref_freq_list, &size);
 	if (res)
 		return res;
 
 	if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
 		return -1;
 
+	p2p_set_own_pref_freq_list(wpa_s->global->p2p, pref_freq_list, size);
+
 	if (wpa_s->parent->conf->p2p_ignore_shared_freq &&
 	    no_pref_freq_given && pref_freq > 0 &&
 	    wpa_s->num_multichan_concurrent > 1 &&
@@ -6185,10 +6855,13 @@
 	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;
+	wpa_s->p2p_go_vht_center_freq2 = 0;
+	wpa_s->p2p_go_max_oper_chwidth = 0;
 
 	for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) {
 		if (os_strcmp(wpa_s->ifname, ifname) == 0)
@@ -6208,7 +6881,7 @@
 
 	wpa_s->global->p2p_invite_group = wpa_s;
 	persistent = ssid->p2p_persistent_group &&
-		wpas_p2p_get_persistent(wpa_s->parent, peer_addr,
+		wpas_p2p_get_persistent(wpa_s->p2pdev, peer_addr,
 					ssid->ssid, ssid->ssid_len);
 
 	if (ssid->mode == WPAS_MODE_P2P_GO) {
@@ -6231,13 +6904,15 @@
 		freq = wpa_s->current_bss ? wpa_s->current_bss->freq :
 			(int) wpa_s->assoc_freq;
 	}
-	wpa_s->parent->pending_invite_ssid_id = -1;
+	wpa_s->p2pdev->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);
+				   role == P2P_INVITE_ROLE_ACTIVE_GO,
+				   pref_freq_list, &size);
 	if (res)
 		return res;
 	wpas_p2p_set_own_freq_preference(wpa_s, force_freq);
@@ -6260,13 +6935,22 @@
 
 	if (ssid == NULL || ssid->mode != WPAS_MODE_P2P_GROUP_FORMATION) {
 		eloop_cancel_timeout(wpas_p2p_group_formation_timeout,
-				     wpa_s->parent, NULL);
+				     wpa_s->p2pdev, NULL);
 	}
 
 	if (!wpa_s->show_group_started || !ssid)
 		return;
 
 	wpa_s->show_group_started = 0;
+	if (!wpa_s->p2p_go_group_formation_completed &&
+	    wpa_s->global->p2p_group_formation == wpa_s) {
+		wpa_dbg(wpa_s, MSG_DEBUG,
+			"P2P: Marking group formation completed on client on 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;
+	}
 
 	os_memset(go_dev_addr, 0, ETH_ALEN);
 	if (ssid->bssid_set)
@@ -6302,7 +6986,7 @@
 			       ip_addr);
 
 	if (persistent)
-		network_id = wpas_p2p_store_persistent_group(wpa_s->parent,
+		network_id = wpas_p2p_store_persistent_group(wpa_s->p2pdev,
 							     ssid, go_dev_addr);
 	if (network_id < 0)
 		network_id = ssid->id;
@@ -6639,7 +7323,7 @@
 
 			iface->cross_connect_enabled = 0;
 			iface->cross_connect_in_use = 0;
-			wpa_msg_global(iface->parent, MSG_INFO,
+			wpa_msg_global(iface->p2pdev, MSG_INFO,
 				       P2P_EVENT_CROSS_CONNECT_DISABLE "%s %s",
 				       iface->ifname,
 				       iface->cross_connect_uplink);
@@ -6669,7 +7353,7 @@
 			continue;
 
 		iface->cross_connect_in_use = 1;
-		wpa_msg_global(iface->parent, MSG_INFO,
+		wpa_msg_global(iface->p2pdev, MSG_INFO,
 			       P2P_EVENT_CROSS_CONNECT_ENABLE "%s %s",
 			       iface->ifname, iface->cross_connect_uplink);
 	}
@@ -6689,7 +7373,7 @@
 		if (!iface->cross_connect_in_use)
 			continue;
 
-		wpa_msg_global(iface->parent, MSG_INFO,
+		wpa_msg_global(iface->p2pdev, MSG_INFO,
 			       P2P_EVENT_CROSS_CONNECT_DISABLE "%s %s",
 			       iface->ifname, iface->cross_connect_uplink);
 		iface->cross_connect_in_use = 0;
@@ -6752,7 +7436,7 @@
 			break;
 
 		wpa_s->cross_connect_in_use = 1;
-		wpa_msg_global(wpa_s->parent, MSG_INFO,
+		wpa_msg_global(wpa_s->p2pdev, MSG_INFO,
 			       P2P_EVENT_CROSS_CONNECT_ENABLE "%s %s",
 			       wpa_s->ifname, wpa_s->cross_connect_uplink);
 		break;
@@ -6768,8 +7452,8 @@
 
 	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);
+	if (wpa_s != wpa_s->p2pdev)
+		wpa_msg_ctrl(wpa_s->p2pdev, MSG_INFO, WPS_EVENT_OVERLAP);
 	wpas_p2p_group_formation_failed(wpa_s, 0);
 	return 1;
 }
@@ -6782,14 +7466,22 @@
 }
 
 
-void wpas_p2p_update_channel_list(struct wpa_supplicant *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_supplicant *ifs;
+	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)) {
@@ -6800,27 +7492,17 @@
 
 	p2p_update_channel_list(wpa_s->global->p2p, &chan, &cli_chan);
 
-	for (ifs = wpa_s->global->ifaces; ifs; ifs = ifs->next) {
-		int freq;
-		if (!ifs->current_ssid ||
-		    !ifs->current_ssid->p2p_group ||
-		    (ifs->current_ssid->mode != WPAS_MODE_P2P_GO &&
-		     ifs->current_ssid->mode != WPAS_MODE_P2P_GROUP_FORMATION))
-				continue;
-		freq = ifs->current_ssid->frequency;
-		if (freq_included(&chan, freq)) {
-			wpa_dbg(ifs, MSG_DEBUG,
-				"P2P GO operating frequency %d MHz in valid range",
-				freq);
-			continue;
-		}
+	wpas_p2p_optimize_listen_channel(wpa_s, freqs, num);
 
-		wpa_dbg(ifs, MSG_DEBUG,
-			"P2P GO operating in invalid frequency %d MHz",	freq);
-		/* TODO: Consider using CSA or removing the group within
-		 * wpa_supplicant */
-		wpa_msg(ifs, MSG_INFO, P2P_EVENT_REMOVE_AND_REFORM_GROUP);
-	}
+	/*
+	 * 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);
 }
 
 
@@ -6878,7 +7560,7 @@
 				   wpa_s->ifname);
 			found = 1;
 			eloop_cancel_timeout(wpas_p2p_group_formation_timeout,
-					     wpa_s->parent, NULL);
+					     wpa_s->p2pdev, NULL);
 			if (wpa_s->p2p_in_provisioning) {
 				wpas_group_formation_completed(wpa_s, 0, 0);
 				break;
@@ -6891,6 +7573,7 @@
 				   wpa_s->ifname);
 			found = 1;
 			wpas_p2p_group_formation_failed(wpa_s, 0);
+			break;
 		}
 	}
 
@@ -7007,7 +7690,7 @@
 {
 	if (wpa_s->p2p_in_provisioning && ssid->p2p_group &&
 	    eloop_cancel_timeout(wpas_p2p_group_formation_timeout,
-				 wpa_s->parent, NULL) > 0) {
+				 wpa_s->p2pdev, NULL) > 0) {
 		/**
 		 * Remove the network by scheduling the group formation
 		 * timeout to happen immediately. The teardown code
@@ -7019,7 +7702,7 @@
 		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);
+				       wpa_s->p2pdev, NULL);
 	}
 }
 
@@ -7063,7 +7746,7 @@
 				       const u8 *addr)
 {
 	if (eloop_cancel_timeout(wpas_p2p_group_formation_timeout,
-				 wpa_s->parent, NULL) > 0) {
+				 wpa_s->p2pdev, NULL) > 0) {
 		/*
 		 * This can happen if WPS provisioning step is not terminated
 		 * cleanly (e.g., P2P Client does not send WSC_Done). Since the
@@ -7119,10 +7802,12 @@
 	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_go_vht_center_freq2,
 			 wpa_s->p2p_persistent_id,
 			 wpa_s->p2p_pd_before_go_neg,
 			 wpa_s->p2p_go_ht40,
-			 wpa_s->p2p_go_vht);
+			 wpa_s->p2p_go_vht,
+			 wpa_s->p2p_go_max_oper_chwidth, NULL, 0);
 	return ret;
 }
 
@@ -7140,7 +7825,7 @@
 
 	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
+	wpa_msg_global(wpa_s->p2pdev, MSG_INFO, P2P_EVENT_FALLBACK_TO_GO_NEG
 		       "reason=GO-not-found");
 	res = wpas_p2p_fallback_to_go_neg(wpa_s, 1);
 
@@ -7249,7 +7934,7 @@
 		return;
 	}
 
-	persistent = wpas_p2p_get_persistent(wpa_s->parent, NULL, ssid->ssid,
+	persistent = wpas_p2p_get_persistent(wpa_s->p2pdev, 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");
@@ -7278,7 +7963,7 @@
 		os_free(last);
 	}
 
-	wpas_p2p_remove_psk_entry(wpa_s->parent, persistent,
+	wpas_p2p_remove_psk_entry(wpa_s->p2pdev, persistent,
 				  p2p_dev_addr ? p2p_dev_addr : mac_addr,
 				  p2p_dev_addr == NULL);
 	if (p2p_dev_addr) {
@@ -7290,8 +7975,8 @@
 	}
 	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))
+	if (wpa_s->p2pdev->conf->update_config &&
+	    wpa_config_write(wpa_s->p2pdev->confname, wpa_s->p2pdev->conf))
 		wpa_printf(MSG_DEBUG, "P2P: Failed to update configuration");
 }
 
@@ -7470,14 +8155,14 @@
 
 		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,
+		persistent = wpas_p2p_get_persistent(wpa_s->p2pdev, 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,
+		wpa_msg_global(wpa_s->p2pdev, MSG_INFO,
 			       P2P_EVENT_PERSISTENT_PSK_FAIL "%d",
 			       persistent->id);
 	disconnect:
@@ -7656,7 +8341,10 @@
 
 	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);
+				params->go_freq, wpa_s->p2p_go_vht_center_freq2,
+				-1, 0, 1, 1, wpa_s->p2p_go_max_oper_chwidth,
+				params->go_ssid_len ? params->go_ssid : NULL,
+				params->go_ssid_len);
 }
 
 
@@ -7732,7 +8420,9 @@
 		   "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);
+				forced_freq, wpa_s->p2p_go_vht_center_freq2,
+				-1, 0, 1, 1, wpa_s->p2p_go_max_oper_chwidth,
+				NULL, 0);
 }
 
 
@@ -7746,7 +8436,9 @@
 		   "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);
+			       forced_freq, wpa_s->p2p_go_vht_center_freq2,
+			       -1, 0, 1, 1, wpa_s->p2p_go_max_oper_chwidth,
+			       NULL, 0);
 	if (res)
 		return res;
 
@@ -8037,7 +8729,9 @@
 		}
 
 		if_addr = wpa_s->pending_interface_addr;
-	} else
+	} else if (wpa_s->p2p_mgmt)
+		if_addr = wpa_s->parent->own_addr;
+	else
 		if_addr = wpa_s->own_addr;
 
 	wpa_s->p2p_nfc_tag_enabled = enabled;
@@ -8080,6 +8774,16 @@
 	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);
@@ -8101,23 +8805,194 @@
 }
 
 
-void wpas_p2p_indicate_state_change(struct wpa_supplicant *wpa_s)
+static int wpas_p2p_move_go_csa(struct wpa_supplicant *wpa_s)
 {
-	struct wpa_used_freq_data *freqs;
-	unsigned int num = wpa_s->num_multichan_concurrent;
+	struct hostapd_config *conf;
+	struct p2p_go_neg_results params;
+	struct csa_settings csa_settings;
+	struct wpa_ssid *current_ssid = wpa_s->current_ssid;
+	int old_freq = current_ssid->frequency;
+	int ret;
 
-	if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
-		return;
+	if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_AP_CSA)) {
+		wpa_dbg(wpa_s, MSG_DEBUG, "CSA is not enabled");
+		return -1;
+	}
 
 	/*
-	 * If possible, optimize the Listen channel to be a channel that is
-	 * already used by one of the other interfaces.
+	 * TODO: This function may not always work correctly. For example,
+	 * when we have a running GO and a BSS on a DFS channel.
 	 */
-	if (!wpa_s->conf->p2p_optimize_listen_chan)
+	if (wpas_p2p_init_go_params(wpa_s, &params, 0, 0, 0, 0, 0, NULL)) {
+		wpa_dbg(wpa_s, MSG_DEBUG,
+			"P2P CSA: Failed to select new frequency for GO");
+		return -1;
+	}
+
+	if (current_ssid->frequency == params.freq) {
+		wpa_dbg(wpa_s, MSG_DEBUG,
+			"P2P CSA: Selected same frequency - not moving GO");
+		return 0;
+	}
+
+	conf = hostapd_config_defaults();
+	if (!conf) {
+		wpa_dbg(wpa_s, MSG_DEBUG,
+			"P2P CSA: Failed to allocate default config");
+		return -1;
+	}
+
+	current_ssid->frequency = params.freq;
+	if (wpa_supplicant_conf_ap_ht(wpa_s, current_ssid, conf)) {
+		wpa_dbg(wpa_s, MSG_DEBUG,
+			"P2P CSA: Failed to create new GO config");
+		ret = -1;
+		goto out;
+	}
+
+	if (conf->hw_mode != wpa_s->ap_iface->current_mode->mode) {
+		wpa_dbg(wpa_s, MSG_DEBUG,
+			"P2P CSA: CSA to a different band is not supported");
+		ret = -1;
+		goto out;
+	}
+
+	os_memset(&csa_settings, 0, sizeof(csa_settings));
+	csa_settings.cs_count = P2P_GO_CSA_COUNT;
+	csa_settings.block_tx = P2P_GO_CSA_BLOCK_TX;
+	csa_settings.freq_params.freq = params.freq;
+	csa_settings.freq_params.sec_channel_offset = conf->secondary_channel;
+	csa_settings.freq_params.ht_enabled = conf->ieee80211n;
+	csa_settings.freq_params.bandwidth = conf->secondary_channel ? 40 : 20;
+
+	if (conf->ieee80211ac) {
+		int freq1 = 0, freq2 = 0;
+		u8 chan, opclass;
+
+		if (ieee80211_freq_to_channel_ext(params.freq,
+						  conf->secondary_channel,
+						  conf->vht_oper_chwidth,
+						  &opclass, &chan) ==
+		    NUM_HOSTAPD_MODES) {
+			wpa_printf(MSG_ERROR, "P2P CSA: Bad freq");
+			ret = -1;
+			goto out;
+		}
+
+		if (conf->vht_oper_centr_freq_seg0_idx)
+			freq1 = ieee80211_chan_to_freq(
+				NULL, opclass,
+				conf->vht_oper_centr_freq_seg0_idx);
+
+		if (conf->vht_oper_centr_freq_seg1_idx)
+			freq2 = ieee80211_chan_to_freq(
+				NULL, opclass,
+				conf->vht_oper_centr_freq_seg1_idx);
+
+		if (freq1 < 0 || freq2 < 0) {
+			wpa_dbg(wpa_s, MSG_DEBUG,
+				"P2P CSA: Selected invalid VHT center freqs");
+			ret = -1;
+			goto out;
+		}
+
+		csa_settings.freq_params.vht_enabled = conf->ieee80211ac;
+		csa_settings.freq_params.center_freq1 = freq1;
+		csa_settings.freq_params.center_freq2 = freq2;
+
+		switch (conf->vht_oper_chwidth) {
+		case VHT_CHANWIDTH_80MHZ:
+		case VHT_CHANWIDTH_80P80MHZ:
+			csa_settings.freq_params.bandwidth = 80;
+			break;
+		case VHT_CHANWIDTH_160MHZ:
+			csa_settings.freq_params.bandwidth = 160;
+			break;
+		}
+	}
+
+	ret = ap_switch_channel(wpa_s, &csa_settings);
+out:
+	current_ssid->frequency = old_freq;
+	hostapd_config_free(conf);
+	return ret;
+}
+
+
+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, 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;
 
-	if (!wpa_s->current_ssid || wpa_s->wpa_state != WPA_COMPLETED)
+	wpas_p2p_go_update_common_freqs(wpa_s);
+
+	/* Do not move GO in the middle of a CSA */
+	if (hostapd_csa_in_progress(wpa_s->ap_iface)) {
+		wpa_printf(MSG_DEBUG,
+			   "P2P: CSA is in progress - not moving GO");
 		return;
+	}
+
+	/*
+	 * 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)
@@ -8125,11 +9000,187 @@
 
 	num = get_shared_radio_freqs_data(wpa_s, freqs, num);
 
-	wpas_p2p_optimize_listen_channel(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;
+		} else if ((wpa_s->conf->p2p_go_freq_change_policy ==
+			    P2P_GO_FREQ_MOVE_SCM_ECSA) &&
+			   wpas_p2p_go_is_peer_freq(wpa_s, freqs[i].freq)) {
+			if (!p2p_get_group_num_members(wpa_s->p2p_group)) {
+				policy_move = 1;
+			} else if ((wpa_s->drv_flags &
+				    WPA_DRIVER_FLAGS_AP_CSA) &&
+				   wpas_p2p_go_clients_support_ecsa(wpa_s)) {
+				u8 chan;
+
+				/*
+				 * We do not support CSA between bands, so move
+				 * GO only within the same band.
+				 */
+				if (wpa_s->ap_iface->current_mode->mode ==
+				    ieee80211_freq_to_chan(freqs[i].freq,
+							   &chan))
+					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;
+	}
+
+	/*
+	 * Do not consider moving GO if it is in the middle of a CSA. When the
+	 * CSA is finished this flow should be retriggered.
+	 */
+	if (hostapd_csa_in_progress(wpa_s->ap_iface)) {
+		wpa_dbg(wpa_s, MSG_DEBUG,
+			"P2P: Not initiating a GO frequency change - CSA is in progress");
+		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) {
diff --git a/wpa_supplicant/p2p_supplicant.h b/wpa_supplicant/p2p_supplicant.h
index 1df34d0..6a770d2 100644
--- a/wpa_supplicant/p2p_supplicant.h
+++ b/wpa_supplicant/p2p_supplicant.h
@@ -17,6 +17,15 @@
 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,
@@ -25,18 +34,23 @@
 						  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 persistent_group, int auto_join, int join, int auth,
+		     int go_intent, int freq, unsigned int vht_center_freq2,
+		     int persistent_id, int pd, int ht40, int vht,
+		     unsigned int vht_chwidth, const u8 *group_ssid,
+		     size_t group_ssid_len);
 int wpas_p2p_handle_frequency_conflicts(struct wpa_supplicant *wpa_s,
                                           int freq, struct wpa_ssid *ssid);
 int wpas_p2p_group_add(struct wpa_supplicant *wpa_s, int persistent_group,
-		       int freq, int ht40, int vht);
+		       int freq, int vht_center_freq2, int ht40, int vht,
+		       int max_oper_chwidth);
 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_freq, int neg_freq,
+				  int vht_center_freq2, int ht40,
+				  int vht, int max_oper_chwidth,
+				  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 {
@@ -90,7 +104,8 @@
 			      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);
+			     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);
@@ -101,7 +116,8 @@
 int wpas_p2p_reject(struct wpa_supplicant *wpa_s, const u8 *addr);
 int wpas_p2p_invite(struct wpa_supplicant *wpa_s, const u8 *peer_addr,
 		    struct wpa_ssid *ssid, const u8 *go_dev_addr, int freq,
-		    int ht40, int vht, int pref_freq);
+		    int vht_center_freq2, int ht40, int vht,
+		    int max_oper_chwidth, 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,
@@ -130,6 +146,8 @@
 			   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);
+int wpas_p2p_get_vht160_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,
@@ -160,7 +178,10 @@
 			  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);
+
+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,
@@ -221,7 +242,9 @@
 {
 }
 
-static inline void wpas_p2p_update_channel_list(struct wpa_supplicant *wpa_s)
+static inline void
+wpas_p2p_update_channel_list(struct wpa_supplicant *wpa_s,
+			     enum wpas_p2p_channel_update_trig trig)
 {
 }
 
diff --git a/wpa_supplicant/p2p_supplicant_sd.c b/wpa_supplicant/p2p_supplicant_sd.c
index f4aa3e0..f8675e6 100644
--- a/wpa_supplicant/p2p_supplicant_sd.c
+++ b/wpa_supplicant/p2p_supplicant_sd.c
@@ -48,7 +48,7 @@
 			u8 *spos_tmp;
 
 			/* Offset */
-			if (*spos + 2 > end) {
+			if (end - *spos < 2) {
 				wpa_printf(MSG_DEBUG, "P2P: No room for full "
 					   "DNS offset field");
 				return -1;
@@ -74,14 +74,14 @@
 			return 0;
 
 		(*spos)++;
-		if (*spos + len > end) {
+		if (len > end - *spos) {
 			wpa_printf(MSG_DEBUG, "P2P: Invalid domain name "
 				   "sequence - no room for label with length "
 				   "%u", len);
 			return -1;
 		}
 
-		if (*upos + len + 2 > uend)
+		if (len + 2 > uend - *upos)
 			return -2;
 
 		os_memcpy(*upos, *spos, len);
@@ -722,11 +722,11 @@
 	if (resp == NULL)
 		return;
 
-	while (pos + 1 < end) {
+	while (end - pos > 1) {
 		wpa_printf(MSG_DEBUG, "P2P: Service Request TLV");
 		slen = WPA_GET_LE16(pos);
 		pos += 2;
-		if (pos + slen > end || slen < 2) {
+		if (slen > end - pos || slen < 2) {
 			wpa_printf(MSG_DEBUG, "P2P: Unexpected Query Data "
 				   "length");
 			wpabuf_free(resp);
@@ -827,10 +827,10 @@
 		u8 svc_len;
 
 		/* Sanity check fixed length+svc_str */
-		if (pos + 6 >= tlv_end)
+		if (6 >= tlv_end - pos)
 			break;
 		svc_len = pos[6];
-		if (pos + svc_len + 10 > tlv_end)
+		if (svc_len + 10 > tlv_end - pos)
 			break;
 
 		/* Advertisement ID */
@@ -917,13 +917,13 @@
 		}
 	}
 
-	while (pos < end) {
+	while (end - pos >= 2) {
 		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) {
+		if (slen > end - pos || slen < 3) {
 			wpa_printf(MSG_DEBUG, "P2P: Unexpected Response Data "
 				   "length");
 			return;
@@ -1185,13 +1185,14 @@
 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)
+			     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);
+				  svc_info, cpt_priority);
 	if (ret == 0)
 		wpas_p2p_sd_service_update(wpa_s);
 	return ret;
diff --git a/wpa_supplicant/scan.c b/wpa_supplicant/scan.c
index e81465c..7a52826 100644
--- a/wpa_supplicant/scan.c
+++ b/wpa_supplicant/scan.c
@@ -267,13 +267,12 @@
 
 
 int wpa_supplicant_start_sched_scan(struct wpa_supplicant *wpa_s,
-				    struct wpa_driver_scan_params *params,
-				    int interval)
+				    struct wpa_driver_scan_params *params)
 {
 	int ret;
 
 	wpa_supplicant_notify_scanning(wpa_s, 1);
-	ret = wpa_drv_sched_scan(wpa_s, params, interval * 1000);
+	ret = wpa_drv_sched_scan(wpa_s, params);
 	if (ret)
 		wpa_supplicant_notify_scanning(wpa_s, 0);
 	else
@@ -485,6 +484,18 @@
 		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 */
+
+#ifdef CONFIG_MBO
+	/* Send cellular capabilities for potential MBO STAs */
+	if (wpabuf_resize(&extra_ie, 9) == 0)
+		wpas_mbo_scan_ie(wpa_s, extra_ie);
+#endif /* CONFIG_MBO */
+
 	return extra_ie;
 }
 
@@ -516,21 +527,6 @@
 #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)
@@ -802,6 +798,9 @@
 	}
 
 	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;
@@ -1007,6 +1006,27 @@
 		}
 	}
 
+	if (!is_zero_ether_addr(wpa_s->next_scan_bssid)) {
+		struct wpa_bss *bss;
+
+		params.bssid = wpa_s->next_scan_bssid;
+		bss = wpa_bss_get_bssid_latest(wpa_s, params.bssid);
+		if (bss && bss->ssid_len && params.num_ssids == 1 &&
+		    params.ssids[0].ssid_len == 0) {
+			params.ssids[0].ssid = bss->ssid;
+			params.ssids[0].ssid_len = bss->ssid_len;
+			wpa_dbg(wpa_s, MSG_DEBUG,
+				"Scan a previously specified BSSID " MACSTR
+				" and SSID %s",
+				MAC2STR(params.bssid),
+				wpa_ssid_txt(bss->ssid, bss->ssid_len));
+		} else {
+			wpa_dbg(wpa_s, MSG_DEBUG,
+				"Scan a previously specified BSSID " MACSTR,
+				MAC2STR(params.bssid));
+		}
+	}
+
 	scan_params = &params;
 
 scan:
@@ -1067,6 +1087,8 @@
 #ifdef CONFIG_INTERWORKING
 		wpa_s->interworking_fast_assoc_tried = 0;
 #endif /* CONFIG_INTERWORKING */
+		if (params.bssid)
+			os_memset(wpa_s->next_scan_bssid, 0, ETH_ALEN);
 	}
 }
 
@@ -1173,6 +1195,7 @@
 	unsigned int max_sched_scan_ssids;
 	int wildcard = 0;
 	int need_ssids;
+	struct sched_scan_plan scan_plan;
 
 	if (!wpa_s->sched_scan_supported)
 		return -1;
@@ -1262,11 +1285,6 @@
 
 	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;
@@ -1351,14 +1369,51 @@
 	scan_params = &params;
 
 scan:
+	wpa_s->sched_scan_timed_out = 0;
+
+	/*
+	 * We cannot support multiple scan plans if the scan request includes
+	 * too many SSID's, so in this case use only the last scan plan and make
+	 * it run infinitely. It will be stopped by the timeout.
+	 */
+	if (wpa_s->sched_scan_plans_num == 1 ||
+	    (wpa_s->sched_scan_plans_num && !ssid && wpa_s->first_sched_scan)) {
+		params.sched_scan_plans = wpa_s->sched_scan_plans;
+		params.sched_scan_plans_num = wpa_s->sched_scan_plans_num;
+	} else if (wpa_s->sched_scan_plans_num > 1) {
+		wpa_dbg(wpa_s, MSG_DEBUG,
+			"Too many SSIDs. Default to using single scheduled_scan plan");
+		params.sched_scan_plans =
+			&wpa_s->sched_scan_plans[wpa_s->sched_scan_plans_num -
+						 1];
+		params.sched_scan_plans_num = 1;
+	} else {
+		if (wpa_s->conf->sched_scan_interval)
+			scan_plan.interval = wpa_s->conf->sched_scan_interval;
+		else
+			scan_plan.interval = 10;
+
+		if (scan_plan.interval > wpa_s->max_sched_scan_plan_interval) {
+			wpa_printf(MSG_WARNING,
+				   "Scan interval too long(%u), use the maximum allowed(%u)",
+				   scan_plan.interval,
+				   wpa_s->max_sched_scan_plan_interval);
+			scan_plan.interval =
+				wpa_s->max_sched_scan_plan_interval;
+		}
+
+		scan_plan.iterations = 0;
+		params.sched_scan_plans = &scan_plan;
+		params.sched_scan_plans_num = 1;
+	}
+
 	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);
+			"Starting sched scan: interval %u timeout %d",
+			params.sched_scan_plans[0].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_dbg(wpa_s, MSG_DEBUG, "Starting sched scan (no timeout)");
 	}
 
 	wpa_setband_scan_freqs(wpa_s, scan_params);
@@ -1372,8 +1427,7 @@
 		}
 	}
 
-	ret = wpa_supplicant_start_sched_scan(wpa_s, scan_params,
-					      wpa_s->sched_scan_interval);
+	ret = wpa_supplicant_start_sched_scan(wpa_s, scan_params);
 	wpabuf_free(extra_ie);
 	os_free(params.filter_ssids);
 	if (ret) {
@@ -1391,9 +1445,12 @@
 				       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;
+		params.sched_scan_plans[0].interval *= 2;
+		if ((unsigned int) wpa_s->sched_scan_timeout <
+		    params.sched_scan_plans[0].interval ||
+		    params.sched_scan_plans[0].interval >
+		    wpa_s->max_sched_scan_plan_interval) {
+			params.sched_scan_plans[0].interval = 10;
 			wpa_s->sched_scan_timeout = max_sched_scan_ssids * 2;
 		}
 	}
@@ -1507,20 +1564,7 @@
  */
 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;
+	return get_ie((const u8 *) (res + 1), res->ie_len, ie);
 }
 
 
@@ -1541,8 +1585,8 @@
 	pos = (const u8 *) (res + 1);
 	end = pos + res->ie_len;
 
-	while (pos + 1 < end) {
-		if (pos + 2 + pos[1] > end)
+	while (end - pos > 1) {
+		if (2 + pos[1] > end - pos)
 			break;
 		if (pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 &&
 		    vendor_type == WPA_GET_BE32(&pos[2]))
@@ -1578,8 +1622,8 @@
 	pos += res->ie_len;
 	end = pos + res->beacon_ie_len;
 
-	while (pos + 1 < end) {
-		if (pos + 2 + pos[1] > end)
+	while (end - pos > 1) {
+		if (2 + pos[1] > end - pos)
 			break;
 		if (pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 &&
 		    vendor_type == WPA_GET_BE32(&pos[2]))
@@ -1614,8 +1658,8 @@
 	pos = (const u8 *) (res + 1);
 	end = pos + res->ie_len;
 
-	while (pos + 1 < end) {
-		if (pos + 2 + pos[1] > end)
+	while (end - pos > 1) {
+		if (2 + pos[1] > end - pos)
 			break;
 		if (pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 &&
 		    vendor_type == WPA_GET_BE32(&pos[2]))
@@ -1679,7 +1723,7 @@
 		snr_a_full = wa->snr;
 		snr_a = MIN(wa->snr, GREAT_SNR);
 		snr_b_full = wb->snr;
-		snr_b = MIN(wa->snr, GREAT_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). */
@@ -1851,7 +1895,7 @@
 
 /*
  * Noise floor values to use when we have signal strength
- * measurements, but no noise floor measurments. These values were
+ * measurements, but no noise floor measurements. These values were
  * measured in an office environment with many APs.
  */
 #define DEFAULT_NOISE_FLOOR_2GHZ (-89)
@@ -2139,6 +2183,9 @@
 		wpa_s->scan_work = NULL;
 		radio_work_done(work);
 	}
+
+	if (wpa_s->wpa_state == WPA_SCANNING)
+		wpa_supplicant_set_state(wpa_s, wpa_s->scan_prev_wpa_state);
 }
 
 
@@ -2205,6 +2252,19 @@
 	params->only_new_results = src->only_new_results;
 	params->low_priority = src->low_priority;
 
+	if (src->sched_scan_plans_num > 0) {
+		params->sched_scan_plans =
+			os_malloc(sizeof(*src->sched_scan_plans) *
+				  src->sched_scan_plans_num);
+		if (!params->sched_scan_plans)
+			goto failed;
+
+		os_memcpy(params->sched_scan_plans, src->sched_scan_plans,
+			  sizeof(*src->sched_scan_plans) *
+			  src->sched_scan_plans_num);
+		params->sched_scan_plans_num = src->sched_scan_plans_num;
+	}
+
 	if (src->mac_addr_rand) {
 		params->mac_addr_rand = src->mac_addr_rand;
 
@@ -2222,6 +2282,17 @@
 			params->mac_addr_mask = mac_addr + ETH_ALEN;
 		}
 	}
+
+	if (src->bssid) {
+		u8 *bssid;
+
+		bssid = os_malloc(ETH_ALEN);
+		if (!bssid)
+			goto failed;
+		os_memcpy(bssid, src->bssid, ETH_ALEN);
+		params->bssid = bssid;
+	}
+
 	return params;
 
 failed:
@@ -2242,6 +2313,7 @@
 	os_free((u8 *) params->extra_ies);
 	os_free(params->freqs);
 	os_free(params->filter_ssids);
+	os_free(params->sched_scan_plans);
 
 	/*
 	 * Note: params->mac_addr_mask points to same memory allocation and
@@ -2249,20 +2321,31 @@
 	 */
 	os_free((u8 *) params->mac_addr);
 
+	os_free((u8 *) params->bssid);
+
 	os_free(params);
 }
 
 
 int wpas_start_pno(struct wpa_supplicant *wpa_s)
 {
-	int ret, interval, prio;
+	int ret, prio;
 	size_t i, num_ssid, num_match_ssid;
 	struct wpa_ssid *ssid;
 	struct wpa_driver_scan_params params;
+	struct sched_scan_plan scan_plan;
+	unsigned int max_sched_scan_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)
+		return -1;
+
 	if (wpa_s->pno || wpa_s->pno_sched_pending)
 		return 0;
 
@@ -2306,10 +2389,10 @@
 		num_ssid++;
 	}
 
-	if (num_ssid > WPAS_MAX_SCAN_SSIDS) {
+	if (num_ssid > max_sched_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;
+			   "%u", max_sched_scan_ssids, (unsigned int) num_ssid);
+		num_ssid = max_sched_scan_ssids;
 	}
 
 	if (num_match_ssid > wpa_s->max_match_sets) {
@@ -2352,8 +2435,20 @@
 	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 (wpa_s->sched_scan_plans_num) {
+		params.sched_scan_plans = wpa_s->sched_scan_plans;
+		params.sched_scan_plans_num = wpa_s->sched_scan_plans_num;
+	} else {
+		/* Set one scan plan that will run infinitely */
+		if (wpa_s->conf->sched_scan_interval)
+			scan_plan.interval = wpa_s->conf->sched_scan_interval;
+		else
+			scan_plan.interval = 10;
+
+		scan_plan.iterations = 0;
+		params.sched_scan_plans = &scan_plan;
+		params.sched_scan_plans_num = 1;
+	}
 
 	if (params.freqs == NULL && wpa_s->manual_sched_scan_freqs) {
 		wpa_dbg(wpa_s, MSG_DEBUG, "Limit sched scan to specified channels");
@@ -2368,7 +2463,7 @@
 		}
 	}
 
-	ret = wpa_supplicant_start_sched_scan(wpa_s, &params, interval);
+	ret = wpa_supplicant_start_sched_scan(wpa_s, &params);
 	os_free(params.filter_ssids);
 	if (ret == 0)
 		wpa_s->pno = 1;
@@ -2453,3 +2548,139 @@
 	wpa_s->mac_addr_rand_enable |= type;
 	return 0;
 }
+
+
+int wpas_abort_ongoing_scan(struct wpa_supplicant *wpa_s)
+{
+	int scan_work = !!wpa_s->scan_work;
+
+#ifdef CONFIG_P2P
+	scan_work |= !!wpa_s->p2p_scan_work;
+#endif /* CONFIG_P2P */
+
+	if (scan_work && wpa_s->own_scan_running) {
+		wpa_dbg(wpa_s, MSG_DEBUG, "Abort an ongoing scan");
+		return wpa_drv_abort_scan(wpa_s);
+	}
+
+	return 0;
+}
+
+
+int wpas_sched_scan_plans_set(struct wpa_supplicant *wpa_s, const char *cmd)
+{
+	struct sched_scan_plan *scan_plans = NULL;
+	const char *token, *context = NULL;
+	unsigned int num = 0;
+
+	if (!cmd)
+		return -1;
+
+	if (!cmd[0]) {
+		wpa_printf(MSG_DEBUG, "Clear sched scan plans");
+		os_free(wpa_s->sched_scan_plans);
+		wpa_s->sched_scan_plans = NULL;
+		wpa_s->sched_scan_plans_num = 0;
+		return 0;
+	}
+
+	while ((token = cstr_token(cmd, " ", &context))) {
+		int ret;
+		struct sched_scan_plan *scan_plan, *n;
+
+		n = os_realloc_array(scan_plans, num + 1, sizeof(*scan_plans));
+		if (!n)
+			goto fail;
+
+		scan_plans = n;
+		scan_plan = &scan_plans[num];
+		num++;
+
+		ret = sscanf(token, "%u:%u", &scan_plan->interval,
+			     &scan_plan->iterations);
+		if (ret <= 0 || ret > 2 || !scan_plan->interval) {
+			wpa_printf(MSG_ERROR,
+				   "Invalid sched scan plan input: %s", token);
+			goto fail;
+		}
+
+		if (!scan_plan->interval) {
+			wpa_printf(MSG_ERROR,
+				   "scan plan %u: Interval cannot be zero",
+				   num);
+			goto fail;
+		}
+
+		if (scan_plan->interval > wpa_s->max_sched_scan_plan_interval) {
+			wpa_printf(MSG_WARNING,
+				   "scan plan %u: Scan interval too long(%u), use the maximum allowed(%u)",
+				   num, scan_plan->interval,
+				   wpa_s->max_sched_scan_plan_interval);
+			scan_plan->interval =
+				wpa_s->max_sched_scan_plan_interval;
+		}
+
+		if (ret == 1) {
+			scan_plan->iterations = 0;
+			break;
+		}
+
+		if (!scan_plan->iterations) {
+			wpa_printf(MSG_ERROR,
+				   "scan plan %u: Number of iterations cannot be zero",
+				   num);
+			goto fail;
+		}
+
+		if (scan_plan->iterations >
+		    wpa_s->max_sched_scan_plan_iterations) {
+			wpa_printf(MSG_WARNING,
+				   "scan plan %u: Too many iterations(%u), use the maximum allowed(%u)",
+				   num, scan_plan->iterations,
+				   wpa_s->max_sched_scan_plan_iterations);
+			scan_plan->iterations =
+				wpa_s->max_sched_scan_plan_iterations;
+		}
+
+		wpa_printf(MSG_DEBUG,
+			   "scan plan %u: interval=%u iterations=%u",
+			   num, scan_plan->interval, scan_plan->iterations);
+	}
+
+	if (!scan_plans) {
+		wpa_printf(MSG_ERROR, "Invalid scan plans entry");
+		goto fail;
+	}
+
+	if (cstr_token(cmd, " ", &context) || scan_plans[num - 1].iterations) {
+		wpa_printf(MSG_ERROR,
+			   "All scan plans but the last must specify a number of iterations");
+		goto fail;
+	}
+
+	wpa_printf(MSG_DEBUG, "scan plan %u (last plan): interval=%u",
+		   num, scan_plans[num - 1].interval);
+
+	if (num > wpa_s->max_sched_scan_plans) {
+		wpa_printf(MSG_WARNING,
+			   "Too many scheduled scan plans (only %u supported)",
+			   wpa_s->max_sched_scan_plans);
+		wpa_printf(MSG_WARNING,
+			   "Use only the first %u scan plans, and the last one (in infinite loop)",
+			   wpa_s->max_sched_scan_plans - 1);
+		os_memcpy(&scan_plans[wpa_s->max_sched_scan_plans - 1],
+			  &scan_plans[num - 1], sizeof(*scan_plans));
+		num = wpa_s->max_sched_scan_plans;
+	}
+
+	os_free(wpa_s->sched_scan_plans);
+	wpa_s->sched_scan_plans = scan_plans;
+	wpa_s->sched_scan_plans_num = num;
+
+	return 0;
+
+fail:
+	os_free(scan_plans);
+	wpa_printf(MSG_ERROR, "invalid scan plans list");
+	return -1;
+}
diff --git a/wpa_supplicant/scan.h b/wpa_supplicant/scan.h
index 7650f5a..93ec9b3 100644
--- a/wpa_supplicant/scan.h
+++ b/wpa_supplicant/scan.h
@@ -40,8 +40,7 @@
 		       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);
+				    struct wpa_driver_scan_params *params);
 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);
@@ -54,5 +53,6 @@
 int wpas_mac_addr_rand_scan_set(struct wpa_supplicant *wpa_s,
 				unsigned int type, const u8 *addr,
 				const u8 *mask);
+int wpas_abort_ongoing_scan(struct wpa_supplicant *wpa_s);
 
 #endif /* SCAN_H */
diff --git a/wpa_supplicant/sme.c b/wpa_supplicant/sme.c
index a472feb..a6ace1a 100644
--- a/wpa_supplicant/sme.c
+++ b/wpa_supplicant/sme.c
@@ -208,6 +208,9 @@
 	u8 ext_capab[18];
 	int ext_capab_len;
 	int skip_auth;
+#ifdef CONFIG_MBO
+	const u8 *mbo;
+#endif /* CONFIG_MBO */
 
 	if (bss == NULL) {
 		wpa_msg(wpa_s, MSG_ERROR, "SME: No scan result available for "
@@ -416,9 +419,55 @@
 	}
 #endif /* CONFIG_P2P */
 
+#ifdef CONFIG_FST
+	if (wpa_s->fst_ies) {
+		int fst_ies_len = wpabuf_len(wpa_s->fst_ies);
+
+		if (wpa_s->sme.assoc_req_ie_len + fst_ies_len <=
+		    sizeof(wpa_s->sme.assoc_req_ie)) {
+			os_memcpy(wpa_s->sme.assoc_req_ie +
+				  wpa_s->sme.assoc_req_ie_len,
+				  wpabuf_head(wpa_s->fst_ies),
+				  fst_ies_len);
+			wpa_s->sme.assoc_req_ie_len += fst_ies_len;
+		}
+	}
+#endif /* CONFIG_FST */
+
+	sme_auth_handle_rrm(wpa_s, bss);
+
+#ifdef CONFIG_MBO
+	mbo = wpa_bss_get_vendor_ie(bss, MBO_IE_VENDOR_TYPE);
+	if (mbo) {
+		int len;
+
+		len = wpas_mbo_supp_op_class_ie(
+			wpa_s, bss->freq,
+			wpa_s->sme.assoc_req_ie + wpa_s->sme.assoc_req_ie_len,
+			sizeof(wpa_s->sme.assoc_req_ie) -
+			wpa_s->sme.assoc_req_ie_len);
+		if (len > 0)
+			wpa_s->sme.assoc_req_ie_len += len;
+	}
+#endif /* CONFIG_MBO */
+
+	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);
+	}
+
 #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);
@@ -438,19 +487,6 @@
 	}
 #endif /* CONFIG_HS20 */
 
-	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;
@@ -465,7 +501,18 @@
 		}
 	}
 
-	sme_auth_handle_rrm(wpa_s, bss);
+#ifdef CONFIG_MBO
+	if (mbo) {
+		int len;
+
+		len = wpas_mbo_ie(wpa_s, wpa_s->sme.assoc_req_ie +
+				  wpa_s->sme.assoc_req_ie_len,
+				  sizeof(wpa_s->sme.assoc_req_ie) -
+				  wpa_s->sme.assoc_req_ie_len);
+		if (len >= 0)
+			wpa_s->sme.assoc_req_ie_len += len;
+	}
+#endif /* CONFIG_MBO */
 
 #ifdef CONFIG_SAE
 	if (!skip_auth && params.auth_alg == WPA_AUTH_ALG_SAE &&
@@ -583,7 +630,8 @@
 	wpa_s->connect_work = work;
 
 	if (cwork->bss_removed ||
-	    !wpas_valid_bss_ssid(wpa_s, cwork->bss, cwork->ssid)) {
+	    !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;
@@ -616,6 +664,8 @@
 		radio_remove_works(wpa_s, "sme-connect", 0);
 	}
 
+	wpas_abort_ongoing_scan(wpa_s);
+
 	cwork = os_zalloc(sizeof(*cwork));
 	if (cwork == NULL)
 		return;
@@ -796,13 +846,27 @@
 		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);
+			       wpa_s->sme.sae.pmkid, wpa_s->pending_bssid);
 	}
 #endif /* CONFIG_SAE */
 
 	if (data->auth.status_code != WLAN_STATUS_SUCCESS) {
-		wpa_dbg(wpa_s, MSG_DEBUG, "SME: Authentication failed (status "
-			"code %d)", data->auth.status_code);
+		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 ||
@@ -945,8 +1009,8 @@
 	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;
+	if (wpa_s->p2pdev->set_sta_uapsd)
+		params.uapsd = wpa_s->p2pdev->sta_uapsd;
 	else
 		params.uapsd = -1;
 
@@ -1290,21 +1354,6 @@
 }
 
 
-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)
 {
diff --git a/wpa_supplicant/systemd/wpa_supplicant-nl80211.service.arg.in b/wpa_supplicant/systemd/wpa_supplicant-nl80211.service.arg.in
index bfdee25..03ac507 100644
--- a/wpa_supplicant/systemd/wpa_supplicant-nl80211.service.arg.in
+++ b/wpa_supplicant/systemd/wpa_supplicant-nl80211.service.arg.in
@@ -2,6 +2,8 @@
 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.
 
diff --git a/wpa_supplicant/systemd/wpa_supplicant-wired.service.arg.in b/wpa_supplicant/systemd/wpa_supplicant-wired.service.arg.in
index 20ba0ad..c8a744d 100644
--- a/wpa_supplicant/systemd/wpa_supplicant-wired.service.arg.in
+++ b/wpa_supplicant/systemd/wpa_supplicant-wired.service.arg.in
@@ -2,6 +2,8 @@
 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.
 
diff --git a/wpa_supplicant/systemd/wpa_supplicant.service.arg.in b/wpa_supplicant/systemd/wpa_supplicant.service.arg.in
index 10e62bc..7788b38 100644
--- a/wpa_supplicant/systemd/wpa_supplicant.service.arg.in
+++ b/wpa_supplicant/systemd/wpa_supplicant.service.arg.in
@@ -2,6 +2,8 @@
 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.
 
diff --git a/wpa_supplicant/systemd/wpa_supplicant.service.in b/wpa_supplicant/systemd/wpa_supplicant.service.in
index 4351ad8..ea964ce 100644
--- a/wpa_supplicant/systemd/wpa_supplicant.service.in
+++ b/wpa_supplicant/systemd/wpa_supplicant.service.in
@@ -1,5 +1,7 @@
 [Unit]
 Description=WPA supplicant
+Before=network.target
+Wants=network.target
 
 [Service]
 Type=dbus
diff --git a/wpa_supplicant/tests/link_test.c b/wpa_supplicant/tests/link_test.c
deleted file mode 100644
index 3bfbed5..0000000
--- a/wpa_supplicant/tests/link_test.c
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * 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/wpa_supplicant/tests/test_eap_sim_common.c b/wpa_supplicant/tests/test_eap_sim_common.c
deleted file mode 100644
index f60b182..0000000
--- a/wpa_supplicant/tests/test_eap_sim_common.c
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * 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/wpa_supplicant/tests/test_wpa.c b/wpa_supplicant/tests/test_wpa.c
deleted file mode 100644
index 39971f2..0000000
--- a/wpa_supplicant/tests/test_wpa.c
+++ /dev/null
@@ -1,369 +0,0 @@
-/*
- * 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/wpa_supplicant/wnm_sta.c b/wpa_supplicant/wnm_sta.c
index 7d79499..f77d51a 100644
--- a/wpa_supplicant/wnm_sta.c
+++ b/wpa_supplicant/wnm_sta.c
@@ -37,12 +37,14 @@
 
 /* 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,
+				   const u8 *addr, const u8 *buf, u16 buf_len,
 				   enum wnm_oper oper)
 {
+	u16 len = buf_len;
+
 	wpa_printf(MSG_DEBUG, "%s: TFS set operation %d", __func__, oper);
 
-	return wpa_drv_wnm_oper(wpa_s, oper, addr, buf, buf_len);
+	return wpa_drv_wnm_oper(wpa_s, oper, addr, (u8 *) buf, &len);
 }
 
 
@@ -137,6 +139,8 @@
 	if (res < 0)
 		wpa_printf(MSG_DEBUG, "Failed to send WNM-Sleep Request "
 			   "(action=%d, intval=%d)", action, intval);
+	else
+		wpa_s->wnmsleep_used = 1;
 
 	os_free(wnmsleep_ie);
 	os_free(wnmtfs_ie);
@@ -147,8 +151,8 @@
 
 
 static void wnm_sleep_mode_enter_success(struct wpa_supplicant *wpa_s,
-					 u8 *tfsresp_ie_start,
-					 u8 *tfsresp_ie_end)
+					 const u8 *tfsresp_ie_start,
+					 const u8 *tfsresp_ie_end)
 {
 	wpa_drv_wnm_oper(wpa_s, WNM_SLEEP_ENTER_CONFIRM,
 			 wpa_s->bssid, NULL, NULL);
@@ -164,7 +168,7 @@
 		/* 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,
+					    tfsresp_ie_len,
 					    WNM_SLEEP_TFS_RESP_IE_SET))
 			wpa_printf(MSG_DEBUG, "WNM: Fail to set TFS Resp IE");
 	}
@@ -193,8 +197,8 @@
 		return;
 	}
 
-	while (ptr + 1 < end) {
-		if (ptr + 2 + ptr[1] > end) {
+	while (end - ptr > 1) {
+		if (2 + ptr[1] > end - ptr) {
 			wpa_printf(MSG_DEBUG, "WNM: Invalid Key Data element "
 				   "length");
 			if (end > ptr) {
@@ -245,14 +249,20 @@
 	 * 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 */
+	const u8 *pos = 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;
+	const u8 *tfsresp_ie_start = NULL;
+	const u8 *tfsresp_ie_end = NULL;
 	size_t left;
 
+	if (!wpa_s->wnmsleep_used) {
+		wpa_printf(MSG_DEBUG,
+			   "WNM: Ignore WNM-Sleep Mode Response frame since WNM-Sleep Mode has not been used in this association");
+		return;
+	}
+
 	if (len < 3)
 		return;
 	key_len_total = WPA_GET_LE16(frm + 1);
@@ -265,14 +275,14 @@
 		return;
 	}
 	pos += 3 + key_len_total;
-	while (pos - frm < len) {
+	while (pos - frm + 1 < len) {
 		u8 ie_len = *(pos + 1);
-		if (pos + 2 + ie_len > frm + len) {
+		if (2 + ie_len > frm + len - pos) {
 			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)
+		if (*pos == WLAN_EID_WNMSLEEP && ie_len >= 4)
 			wnmsleep_ie = (struct wnm_sleep_element *) pos;
 		else if (*pos == WLAN_EID_TFS_RESP) {
 			if (!tfsresp_ie_start)
@@ -419,6 +429,7 @@
 {
 	struct wpa_bss *bss = wpa_s->current_bss;
 	const char *country = NULL;
+	int freq;
 
 	if (bss) {
 		const u8 *elem = wpa_bss_get_ie(bss, WLAN_EID_COUNTRY);
@@ -427,7 +438,21 @@
 			country = (const char *) (elem + 2);
 	}
 
-	return ieee80211_chan_to_freq(country, op_class, chan);
+	freq = ieee80211_chan_to_freq(country, op_class, chan);
+	if (freq <= 0 && op_class == 0) {
+		/*
+		 * Some APs do not advertise correct operating class
+		 * information. Try to determine the most likely operating
+		 * frequency based on the channel number.
+		 */
+		if (chan >= 1 && chan <= 13)
+			freq = 2407 + chan * 5;
+		else if (chan == 14)
+			freq = 2484;
+		else if (chan >= 36 && chan <= 169)
+			freq = 5000 + chan * 5;
+	}
+	return freq;
 }
 
 
@@ -521,6 +546,25 @@
 			continue;
 		}
 
+		if (wpa_s->current_ssid &&
+		    !wpa_scan_res_match(wpa_s, 0, target, wpa_s->current_ssid,
+					1)) {
+			wpa_printf(MSG_DEBUG, "Candidate BSS " MACSTR
+				   " (pref %d) does not match the current network profile",
+				   MAC2STR(nei->bssid),
+				   nei->preference_present ? nei->preference :
+				   -1);
+			continue;
+		}
+
+		if (wpa_is_bss_tmp_disallowed(wpa_s, target->bssid)) {
+			wpa_printf(MSG_DEBUG,
+				   "MBO: Candidate BSS " MACSTR
+				   " retry delay is not over yet",
+				   MAC2STR(nei->bssid));
+			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)",
@@ -542,12 +586,190 @@
 }
 
 
+static int wpa_bss_ies_eq(struct wpa_bss *a, struct wpa_bss *b, u8 eid)
+{
+	const u8 *ie_a, *ie_b;
+
+	if (!a || !b)
+		return 0;
+
+	ie_a = wpa_bss_get_ie(a, eid);
+	ie_b = wpa_bss_get_ie(b, eid);
+
+	if (!ie_a || !ie_b || ie_a[1] != ie_b[1])
+		return 0;
+
+	return os_memcmp(ie_a, ie_b, ie_a[1]) == 0;
+}
+
+
+static u32 wnm_get_bss_info(struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
+{
+	u32 info = 0;
+
+	info |= NEI_REP_BSSID_INFO_AP_UNKNOWN_REACH;
+
+	/*
+	 * Leave the security and key scope bits unset to indicate that the
+	 * security information is not available.
+	 */
+
+	if (bss->caps & WLAN_CAPABILITY_SPECTRUM_MGMT)
+		info |= NEI_REP_BSSID_INFO_SPECTRUM_MGMT;
+	if (bss->caps & WLAN_CAPABILITY_QOS)
+		info |= NEI_REP_BSSID_INFO_QOS;
+	if (bss->caps & WLAN_CAPABILITY_APSD)
+		info |= NEI_REP_BSSID_INFO_APSD;
+	if (bss->caps & WLAN_CAPABILITY_RADIO_MEASUREMENT)
+		info |= NEI_REP_BSSID_INFO_RM;
+	if (bss->caps & WLAN_CAPABILITY_DELAYED_BLOCK_ACK)
+		info |= NEI_REP_BSSID_INFO_DELAYED_BA;
+	if (bss->caps & WLAN_CAPABILITY_IMM_BLOCK_ACK)
+		info |= NEI_REP_BSSID_INFO_IMM_BA;
+	if (wpa_bss_ies_eq(bss, wpa_s->current_bss, WLAN_EID_MOBILITY_DOMAIN))
+		info |= NEI_REP_BSSID_INFO_MOBILITY_DOMAIN;
+	if (wpa_bss_ies_eq(bss, wpa_s->current_bss, WLAN_EID_HT_CAP))
+		info |= NEI_REP_BSSID_INFO_HT;
+
+	return info;
+}
+
+
+static int wnm_add_nei_rep(u8 *buf, size_t len, const u8 *bssid, u32 bss_info,
+			   u8 op_class, u8 chan, u8 phy_type, u8 pref)
+{
+	u8 *pos = buf;
+
+	if (len < 18) {
+		wpa_printf(MSG_DEBUG,
+			   "WNM: Not enough room for Neighbor Report element");
+		return -1;
+	}
+
+	*pos++ = WLAN_EID_NEIGHBOR_REPORT;
+	/* length: 13 for basic neighbor report + 3 for preference subelement */
+	*pos++ = 16;
+	os_memcpy(pos, bssid, ETH_ALEN);
+	pos += ETH_ALEN;
+	WPA_PUT_LE32(pos, bss_info);
+	pos += 4;
+	*pos++ = op_class;
+	*pos++ = chan;
+	*pos++ = phy_type;
+	*pos++ = WNM_NEIGHBOR_BSS_TRANSITION_CANDIDATE;
+	*pos++ = 1;
+	*pos++ = pref;
+	return pos - buf;
+}
+
+
+static int wnm_nei_rep_add_bss(struct wpa_supplicant *wpa_s,
+			       struct wpa_bss *bss, u8 *buf, size_t len,
+			       u8 pref)
+{
+	const u8 *ie;
+	u8 op_class, chan;
+	int sec_chan = 0, vht = 0;
+	enum phy_type phy_type;
+	u32 info;
+	struct ieee80211_ht_operation *ht_oper = NULL;
+	struct ieee80211_vht_operation *vht_oper = NULL;
+
+	ie = wpa_bss_get_ie(bss, WLAN_EID_HT_OPERATION);
+	if (ie && ie[1] >= 2) {
+		ht_oper = (struct ieee80211_ht_operation *) (ie + 2);
+
+		if (ht_oper->ht_param & HT_INFO_HT_PARAM_SECONDARY_CHNL_ABOVE)
+			sec_chan = 1;
+		else if (ht_oper->ht_param &
+			 HT_INFO_HT_PARAM_SECONDARY_CHNL_BELOW)
+			sec_chan = -1;
+	}
+
+	ie = wpa_bss_get_ie(bss, WLAN_EID_VHT_OPERATION);
+	if (ie && ie[1] >= 1) {
+		vht_oper = (struct ieee80211_vht_operation *) (ie + 2);
+
+		if (vht_oper->vht_op_info_chwidth == VHT_CHANWIDTH_80MHZ ||
+		    vht_oper->vht_op_info_chwidth == VHT_CHANWIDTH_160MHZ ||
+		    vht_oper->vht_op_info_chwidth == VHT_CHANWIDTH_80P80MHZ)
+			vht = vht_oper->vht_op_info_chwidth;
+	}
+
+	if (ieee80211_freq_to_channel_ext(bss->freq, sec_chan, vht, &op_class,
+					  &chan) == NUM_HOSTAPD_MODES) {
+		wpa_printf(MSG_DEBUG,
+			   "WNM: Cannot determine operating class and channel");
+		return -2;
+	}
+
+	phy_type = ieee80211_get_phy_type(bss->freq, (ht_oper != NULL),
+					  (vht_oper != NULL));
+	if (phy_type == PHY_TYPE_UNSPECIFIED) {
+		wpa_printf(MSG_DEBUG,
+			   "WNM: Cannot determine BSS phy type for Neighbor Report");
+		return -2;
+	}
+
+	info = wnm_get_bss_info(wpa_s, bss);
+
+	return wnm_add_nei_rep(buf, len, bss->bssid, info, op_class, chan,
+			       phy_type, pref);
+}
+
+
+static int wnm_add_cand_list(struct wpa_supplicant *wpa_s, u8 *buf, size_t len)
+{
+	u8 *pos = buf;
+	unsigned int i, pref = 255;
+	struct os_reltime now;
+	struct wpa_ssid *ssid = wpa_s->current_ssid;
+
+	if (!ssid)
+		return 0;
+
+	/*
+	 * TODO: Define when scan results are no longer valid for the candidate
+	 * list.
+	 */
+	os_get_reltime(&now);
+	if (os_reltime_expired(&now, &wpa_s->last_scan, 10))
+		return 0;
+
+	wpa_printf(MSG_DEBUG,
+		   "WNM: Add candidate list to BSS Transition Management Response frame");
+	for (i = 0; i < wpa_s->last_scan_res_used && pref; i++) {
+		struct wpa_bss *bss = wpa_s->last_scan_res[i];
+		int res;
+
+		if (wpa_scan_res_match(wpa_s, i, bss, ssid, 1)) {
+			res = wnm_nei_rep_add_bss(wpa_s, bss, pos, len, pref--);
+			if (res == -2)
+				continue; /* could not build entry for BSS */
+			if (res < 0)
+				break; /* no more room for candidates */
+			if (pref == 1)
+				break;
+
+			pos += res;
+			len -= res;
+		}
+	}
+
+	wpa_hexdump(MSG_DEBUG,
+		    "WNM: BSS Transition Management Response candidate list",
+		    buf, pos - buf);
+
+	return pos - buf;
+}
+
+
 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;
+	u8 buf[2000], *pos;
 	struct ieee80211_mgmt *mgmt;
 	size_t len;
 	int res;
@@ -587,6 +809,17 @@
 		pos += ETH_ALEN;
 	}
 
+	if (status == WNM_BSS_TM_ACCEPT)
+		pos += wnm_add_cand_list(wpa_s, pos, buf + sizeof(buf) - pos);
+
+#ifdef CONFIG_MBO
+	if (status != WNM_BSS_TM_ACCEPT) {
+		pos += wpas_mbo_ie_bss_trans_reject(
+			wpa_s, pos, buf + sizeof(buf) - pos,
+			MBO_TRANSITION_REJECT_REASON_UNSPECIFIED);
+	}
+#endif /* CONFIG_MBO */
+
 	len = pos - (u8 *) &mgmt->u.action.category;
 
 	res = wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid,
@@ -643,6 +876,7 @@
 	if (bss == wpa_s->current_bss) {
 		wpa_printf(MSG_DEBUG,
 			   "WNM: Already associated with the preferred candidate");
+		wnm_deallocate_memory(wpa_s);
 		return 1;
 	}
 
@@ -795,8 +1029,11 @@
 {
 	unsigned int beacon_int;
 	u8 valid_int;
+#ifdef CONFIG_MBO
+	const u8 *vendor;
+#endif /* CONFIG_MBO */
 
-	if (pos + 5 > end)
+	if (end - pos < 5)
 		return;
 
 	if (wpa_s->current_bss)
@@ -819,7 +1056,7 @@
 	pos += 5;
 
 	if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_BSS_TERMINATION_INCLUDED) {
-		if (pos + 12 > end) {
+		if (end - pos < 12) {
 			wpa_printf(MSG_DEBUG, "WNM: Too short BSS TM Request");
 			return;
 		}
@@ -830,7 +1067,7 @@
 	if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_ESS_DISASSOC_IMMINENT) {
 		char url[256];
 
-		if (pos + 1 > end || pos + 1 + pos[0] > end) {
+		if (end - pos < 1 || 1 + pos[0] > end - pos) {
 			wpa_printf(MSG_DEBUG, "WNM: Invalid BSS Transition "
 				   "Management Request (URL)");
 			return;
@@ -855,6 +1092,12 @@
 		}
 	}
 
+#ifdef CONFIG_MBO
+	vendor = get_ie(pos, end - pos, WLAN_EID_VENDOR_SPECIFIC);
+	if (vendor)
+		wpas_mbo_ie_trans_req(wpa_s, vendor + 2, vendor[1]);
+#endif /* CONFIG_MBO */
+
 	if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_PREF_CAND_LIST_INCLUDED) {
 		unsigned int valid_ms;
 
@@ -866,7 +1109,7 @@
 		if (wpa_s->wnm_neighbor_report_elements == NULL)
 			return;
 
-		while (pos + 2 <= end &&
+		while (end - pos >= 2 &&
 		       wpa_s->wnm_num_neighbor_report < WNM_MAX_NEIGHBOR_REPORT)
 		{
 			u8 tag = *pos++;
@@ -874,7 +1117,7 @@
 
 			wpa_printf(MSG_DEBUG, "WNM: Neighbor report tag %u",
 				   tag);
-			if (pos + len > end) {
+			if (len > end - pos) {
 				wpa_printf(MSG_DEBUG, "WNM: Truncated request");
 				return;
 			}
@@ -883,11 +1126,22 @@
 				rep = &wpa_s->wnm_neighbor_report_elements[
 					wpa_s->wnm_num_neighbor_report];
 				wnm_parse_neighbor_report(wpa_s, pos, len, rep);
+				wpa_s->wnm_num_neighbor_report++;
 			}
 
 			pos += len;
-			wpa_s->wnm_num_neighbor_report++;
 		}
+
+		if (!wpa_s->wnm_num_neighbor_report) {
+			wpa_printf(MSG_DEBUG,
+				   "WNM: Candidate list included bit is set, but no candidates found");
+			wnm_send_bss_transition_mgmt_resp(
+				wpa_s, wpa_s->wnm_dialog_token,
+				WNM_BSS_TM_REJECT_NO_SUITABLE_CANDIDATES,
+				0, NULL);
+			return;
+		}
+
 		wnm_sort_cand_list(wpa_s);
 		wnm_dump_cand_list(wpa_s);
 		valid_ms = valid_int * beacon_int * 128 / 125;
@@ -916,6 +1170,14 @@
 		}
 
 		wnm_set_scan_freqs(wpa_s);
+		if (wpa_s->wnm_num_neighbor_report == 1) {
+			os_memcpy(wpa_s->next_scan_bssid,
+				  wpa_s->wnm_neighbor_report_elements[0].bssid,
+				  ETH_ALEN);
+			wpa_printf(MSG_DEBUG,
+				   "WNM: Scan only for a specific BSSID since there is only a single candidate "
+				   MACSTR, MAC2STR(wpa_s->next_scan_bssid));
+		}
 		wpa_supplicant_req_scan(wpa_s, 0, 0);
 	} else if (reply) {
 		enum bss_trans_mgmt_status_code status;
@@ -933,16 +1195,17 @@
 
 
 int wnm_send_bss_transition_mgmt_query(struct wpa_supplicant *wpa_s,
-				       u8 query_reason)
+				       u8 query_reason, int cand_list)
 {
-	u8 buf[1000], *pos;
+	u8 buf[2000], *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);
+		   MACSTR " query_reason=%u%s",
+		   MAC2STR(wpa_s->bssid), query_reason,
+		   cand_list ? " candidate list" : "");
 
 	mgmt = (struct ieee80211_mgmt *) buf;
 	os_memset(&buf, 0, sizeof(buf));
@@ -957,6 +1220,9 @@
 	mgmt->u.action.u.bss_tm_query.query_reason = query_reason;
 	pos = mgmt->u.action.u.bss_tm_query.variable;
 
+	if (cand_list)
+		pos += wnm_add_cand_list(wpa_s, pos, buf + sizeof(buf) - pos);
+
 	len = pos - (u8 *) &mgmt->u.action.category;
 
 	ret = wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid,
@@ -977,7 +1243,7 @@
 	pos = data;
 	end = data + len;
 
-	while (pos + 1 < end) {
+	while (end - pos > 1) {
 		ie = *pos++;
 		ie_len = *pos++;
 		wpa_printf(MSG_DEBUG, "WNM: WFA subelement %u len %u",
@@ -1015,7 +1281,7 @@
 				url = NULL;
 				osu_method = 1;
 			} else {
-				if (pos + url_len + 1 > ie_end) {
+				if (url_len + 1 > ie_end - pos) {
 					wpa_printf(MSG_DEBUG, "WNM: Not enough room for Server URL (len=%u) and Server Method (left %d)",
 						   url_len,
 						   (int) (ie_end - pos));
@@ -1054,7 +1320,7 @@
 				   "Imminent - Reason Code %u   "
 				   "Re-Auth Delay %u  URL Length %u",
 				   code, reauth_delay, url_len);
-			if (pos + url_len > ie_end)
+			if (url_len > ie_end - pos)
 				break;
 			url = os_malloc(url_len + 1);
 			if (url == NULL)
diff --git a/wpa_supplicant/wnm_sta.h b/wpa_supplicant/wnm_sta.h
index 8de4348..81d8153 100644
--- a/wpa_supplicant/wnm_sta.h
+++ b/wpa_supplicant/wnm_sta.h
@@ -56,7 +56,7 @@
 			      const struct ieee80211_mgmt *mgmt, size_t len);
 
 int wnm_send_bss_transition_mgmt_query(struct wpa_supplicant *wpa_s,
-				       u8 query_reason);
+				       u8 query_reason, int cand_list);
 void wnm_deallocate_memory(struct wpa_supplicant *wpa_s);
 
 
diff --git a/wpa_supplicant/wpa_cli.c b/wpa_supplicant/wpa_cli.c
index c5d8333..36a7a4e 100644
--- a/wpa_supplicant/wpa_cli.c
+++ b/wpa_supplicant/wpa_cli.c
@@ -1,6 +1,6 @@
 /*
  * WPA Supplicant - command line interface for wpa_supplicant daemon
- * Copyright (c) 2004-2015, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2016, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -28,7 +28,7 @@
 
 static const char *const wpa_cli_version =
 "wpa_cli v" VERSION_STR "\n"
-"Copyright (c) 2004-2015, Jouni Malinen <j@w1.fi> and contributors";
+"Copyright (c) 2004-2016, Jouni Malinen <j@w1.fi> and contributors";
 
 
 static const char *const wpa_cli_license =
@@ -66,6 +66,12 @@
 "OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n"
 "\n";
 
+#define VENDOR_ELEM_FRAME_ID \
+	"  0: Probe Req (P2P), 1: Probe Resp (P2P) , 2: Probe Resp (GO), " \
+	"3: Beacon (GO), 4: PD Req, 5: PD Resp, 6: GO Neg Req, " \
+	"7: GO Neg Resp, 8: GO Neg Conf, 9: Inv Req, 10: Inv Resp, " \
+	"11: Assoc Req (P2P), 12: Assoc Resp (P2P)"
+
 static struct wpa_ctrl *ctrl_conn;
 static struct wpa_ctrl *mon_conn;
 static int wpa_cli_quit = 0;
@@ -76,6 +82,7 @@
 #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;
@@ -107,7 +114,9 @@
 {
 	printf("wpa_cli [-p<path to ctrl sockets>] [-i<ifname>] [-hvB] "
 	       "[-a<action file>] \\\n"
-	       "        [-P<pid file>] [-g<global ctrl>] [-G<ping interval>]  "
+	       "        [-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"
@@ -330,6 +339,13 @@
 	}
 #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);
@@ -343,14 +359,14 @@
 		}
 	}
 
-	ctrl_conn = wpa_ctrl_open(cfile);
+	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_open(cfile);
+		mon_conn = wpa_ctrl_open2(cfile, client_socket_dir);
 	else
 		mon_conn = NULL;
 	os_free(cfile);
@@ -502,6 +518,10 @@
 		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");
 }
 
@@ -618,6 +638,7 @@
 		"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",
@@ -1533,7 +1554,7 @@
 	"ssid", "scan_ssid", "bssid", "bssid_blacklist",
 	"bssid_whitelist", "psk", "proto", "key_mgmt",
 	"bg_scan_period", "pairwise", "group", "auth_alg", "scan_freq",
-	"freq_list",
+	"freq_list", "max_oper_chwidth",
 #ifdef IEEE8021X_EAPOL
 	"eap", "identity", "anonymous_identity", "password", "ca_cert",
 	"ca_path", "client_cert", "private_key", "private_key_passwd",
@@ -1591,7 +1612,7 @@
 #ifdef CONFIG_HS20
 	"update_identifier",
 #endif /* CONFIG_HS20 */
-	"mac_addr"
+	"mac_addr", "pbss"
 };
 
 
@@ -1749,6 +1770,13 @@
 }
 
 
+static int wpa_cli_cmd_abort_scan(struct wpa_ctrl *ctrl, int argc,
+				  char *argv[])
+{
+	return wpa_ctrl_command(ctrl, "ABORT_SCAN");
+}
+
+
 static int wpa_cli_cmd_bss(struct wpa_ctrl *ctrl, int argc, char *argv[])
 {
 	return wpa_cli_cmd(ctrl, "BSS", 1, argc, argv);
@@ -1851,14 +1879,15 @@
 
 	/*
 	 * INTERFACE_ADD <ifname>TAB<confname>TAB<driver>TAB<ctrl_interface>TAB
-	 * <driver_param>TAB<bridge_name>[TAB<create>]
+	 * <driver_param>TAB<bridge_name>[TAB<create>[TAB<type>]]
 	 */
 	res = os_snprintf(cmd, sizeof(cmd),
-			  "INTERFACE_ADD %s\t%s\t%s\t%s\t%s\t%s\t%s",
+			  "INTERFACE_ADD %s\t%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] : "");
+			  argc > 5 ? argv[5] : "", argc > 6 ? argv[6] : "",
+			  argc > 7 ? argv[7] : "");
 	if (os_snprintf_error(sizeof(cmd), res))
 		return -1;
 	cmd[sizeof(cmd) - 1] = '\0';
@@ -1898,6 +1927,12 @@
 		printf("Not connected to hostapd - 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);
@@ -2007,6 +2042,20 @@
 	return wpa_cli_cmd(ctrl, "MESH_GROUP_REMOVE", 1, argc, argv);
 }
 
+
+static int wpa_cli_cmd_mesh_peer_remove(struct wpa_ctrl *ctrl, int argc,
+					char *argv[])
+{
+	return wpa_cli_cmd(ctrl, "MESH_PEER_REMOVE", 1, argc, argv);
+}
+
+
+static int wpa_cli_cmd_mesh_peer_add(struct wpa_ctrl *ctrl, int argc,
+				     char *argv[])
+{
+	return wpa_cli_cmd(ctrl, "MESH_PEER_ADD", 1, argc, argv);
+}
+
 #endif /* CONFIG_MESH */
 
 
@@ -2462,6 +2511,27 @@
 	return wpa_cli_cmd(ctrl, "P2P_REMOVE_CLIENT", 1, argc, argv);
 }
 
+
+static int wpa_cli_cmd_vendor_elem_add(struct wpa_ctrl *ctrl, int argc,
+				       char *argv[])
+{
+	return wpa_cli_cmd(ctrl, "VENDOR_ELEM_ADD", 2, argc, argv);
+}
+
+
+static int wpa_cli_cmd_vendor_elem_get(struct wpa_ctrl *ctrl, int argc,
+				       char *argv[])
+{
+	return wpa_cli_cmd(ctrl, "VENDOR_ELEM_GET", 1, argc, argv);
+}
+
+
+static int wpa_cli_cmd_vendor_elem_remove(struct wpa_ctrl *ctrl, int argc,
+					  char *argv[])
+{
+	return wpa_cli_cmd(ctrl, "VENDOR_ELEM_REMOVE", 2, argc, argv);
+}
+
 #endif /* CONFIG_P2P */
 
 #ifdef CONFIG_WIFI_DISPLAY
@@ -2704,6 +2774,13 @@
 }
 
 
+static int wpa_cli_cmd_signal_monitor(struct wpa_ctrl *ctrl, int argc,
+				   char *argv[])
+{
+	return wpa_cli_cmd(ctrl, "SIGNAL_MONITOR", 0, argc, argv);
+}
+
+
 static int wpa_cli_cmd_pktcnt_poll(struct wpa_ctrl *ctrl, int argc,
 				   char *argv[])
 {
@@ -2801,6 +2878,13 @@
 }
 
 
+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
@@ -2979,6 +3063,9 @@
 	{ "scan_results", wpa_cli_cmd_scan_results, NULL,
 	  cli_cmd_flag_none,
 	  "= get latest scan results" },
+	{ "abort_scan", wpa_cli_cmd_abort_scan, NULL,
+	  cli_cmd_flag_none,
+	  "= request ongoing scan to be aborted" },
 	{ "bss", wpa_cli_cmd_bss, wpa_cli_complete_bss,
 	  cli_cmd_flag_none,
 	  "<<idx> | <bssid>> = get detailed scan result info" },
@@ -2995,8 +3082,10 @@
 	{ "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" },
+	  "  <bridge_name> <create> <type> = adds new interface, all "
+	  "parameters but\n"
+	  "  <ifname> are optional. Supported types are station ('sta') and "
+	  "AP ('ap')" },
 	{ "interface_remove", wpa_cli_cmd_interface_remove, NULL,
 	  cli_cmd_flag_none,
 	  "<ifname> = removes the interface" },
@@ -3135,6 +3224,12 @@
 	{ "mesh_group_remove", wpa_cli_cmd_mesh_group_remove, NULL,
 	  cli_cmd_flag_none,
 	  "<ifname> = Remove mesh group interface" },
+	{ "mesh_peer_remove", wpa_cli_cmd_mesh_peer_remove, NULL,
+	  cli_cmd_flag_none,
+	  "<addr> = Remove a mesh peer" },
+	{ "mesh_peer_add", wpa_cli_cmd_mesh_peer_add, NULL,
+	  cli_cmd_flag_none,
+	  "<addr> [duration=<seconds>] = Add a mesh peer" },
 #endif /* CONFIG_MESH */
 #ifdef CONFIG_P2P
 	{ "p2p_find", wpa_cli_cmd_p2p_find, wpa_cli_complete_p2p_find,
@@ -3226,6 +3321,18 @@
 	{ "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" },
+	{ "vendor_elem_add", wpa_cli_cmd_vendor_elem_add, NULL,
+	  cli_cmd_flag_none,
+	  "<frame id> <hexdump of elem(s)> = add vendor specific IEs to frame(s)\n"
+	  VENDOR_ELEM_FRAME_ID },
+	{ "vendor_elem_get", wpa_cli_cmd_vendor_elem_get, NULL,
+	  cli_cmd_flag_none,
+	  "<frame id> = get vendor specific IE(s) to frame(s)\n"
+	  VENDOR_ELEM_FRAME_ID },
+	{ "vendor_elem_remove", wpa_cli_cmd_vendor_elem_remove, NULL,
+	  cli_cmd_flag_none,
+	  "<frame id> <hexdump of elem(s)> = remove vendor specific IE(s) in frame(s)\n"
+	  VENDOR_ELEM_FRAME_ID },
 #endif /* CONFIG_P2P */
 #ifdef CONFIG_WIFI_DISPLAY
 	{ "wfd_subelem_set", wpa_cli_cmd_wfd_subelem_set, NULL,
@@ -3314,6 +3421,9 @@
 	{ "signal_poll", wpa_cli_cmd_signal_poll, NULL,
 	  cli_cmd_flag_none,
 	  "= get signal parameters" },
+	{ "signal_monitor", wpa_cli_cmd_signal_monitor, NULL,
+	  cli_cmd_flag_none,
+	  "= set signal monitor parameters" },
 	{ "pktcnt_poll", wpa_cli_cmd_pktcnt_poll, NULL,
 	  cli_cmd_flag_none,
 	  "= get TX/RX packet counters" },
@@ -3328,7 +3438,7 @@
 	{ "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" },
+	  "<query reason> [list] = Send BSS Transition Management Query" },
 #endif /* CONFIG_WNM */
 	{ "raw", wpa_cli_cmd_raw, NULL, cli_cmd_flag_sensitive,
 	  "<params..> = Sent unprocessed command" },
@@ -3355,6 +3465,9 @@
 	  "<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 }
 };
 
@@ -3566,6 +3679,10 @@
 	size_t len;
 	int res;
 
+	/* If no interface is specified, set the global */
+	if (!arg1)
+		arg1 = "global";
+
 	len = os_strlen(arg1) + os_strlen(arg2) + 2;
 	arg = os_malloc(len);
 	if (arg == NULL)
@@ -3585,6 +3702,9 @@
 	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;
@@ -4056,7 +4176,7 @@
 	if (ctrl_ifname == NULL)
 		ctrl_ifname = wpa_cli_get_default_ifname();
 
-	if (!wpa_cli_open_connection(ctrl_ifname, 1) == 0) {
+	if (wpa_cli_open_connection(ctrl_ifname, 1)) {
 		if (!warning_displayed) {
 			printf("Could not connect to wpa_supplicant: "
 			       "%s - re-trying\n",
@@ -4166,18 +4286,17 @@
 {
 	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) {
-#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);
-			return ifname;
-		}
-#endif /* ANDROID */
 		return NULL;
 	}
 	while ((dent = readdir(dir))) {
@@ -4221,6 +4340,7 @@
 	}
 	wpa_ctrl_close(ctrl);
 #endif /* CONFIG_CTRL_IFACE_NAMED_PIPE */
+#endif /* ANDROID */
 
 	return ifname;
 }
@@ -4237,7 +4357,7 @@
 		return -1;
 
 	for (;;) {
-		c = getopt(argc, argv, "a:Bg:G:hi:p:P:v");
+		c = getopt(argc, argv, "a:Bg:G:hi:p:P:s:v");
 		if (c < 0)
 			break;
 		switch (c) {
@@ -4269,6 +4389,9 @@
 		case 'P':
 			pid_file = optarg;
 			break;
+		case 's':
+			client_socket_dir = optarg;
+			break;
 		default:
 			usage();
 			return -1;
@@ -4342,7 +4465,7 @@
 			}
 		}
 
-		if (daemonize && os_daemonize(pid_file))
+		if (daemonize && os_daemonize(pid_file) && eloop_sock_requeue())
 			return -1;
 
 		if (action_file)
diff --git a/wpa_supplicant/wpa_gui-qt4/wpagui.cpp b/wpa_supplicant/wpa_gui-qt4/wpagui.cpp
index 3bd3a9c..a0aa05e 100644
--- a/wpa_supplicant/wpa_gui-qt4/wpagui.cpp
+++ b/wpa_supplicant/wpa_gui-qt4/wpagui.cpp
@@ -639,7 +639,7 @@
 
 void WpaGui::updateNetworks()
 {
-	char buf[2048], *start, *end, *id, *ssid, *bssid, *flags;
+	char buf[4096], *start, *end, *id, *ssid, *bssid, *flags;
 	size_t len;
 	int first_active = -1;
 	int was_selected = -1;
diff --git a/wpa_supplicant/wpa_priv.c b/wpa_supplicant/wpa_priv.c
index 4611a1d..511df4f 100644
--- a/wpa_supplicant/wpa_priv.c
+++ b/wpa_supplicant/wpa_priv.c
@@ -29,8 +29,11 @@
 	char *sock_name;
 	int fd;
 
+	void *ctx;
+
 	const struct wpa_driver_ops *driver;
 	void *drv_priv;
+	void *drv_global_priv;
 	struct sockaddr_un drv_addr;
 	int wpas_registered;
 
@@ -39,6 +42,10 @@
 	struct sockaddr_un l2_addr;
 };
 
+struct wpa_priv_global {
+	struct wpa_priv_interface *interfaces;
+};
+
 
 static void wpa_priv_cmd_register(struct wpa_priv_interface *iface,
 				  struct sockaddr_un *from)
@@ -48,6 +55,10 @@
 		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;
 	}
 
@@ -58,10 +69,25 @@
 		iface->l2 = NULL;
 	}
 
-	if (iface->driver->init == NULL)
+	if (iface->driver->init2) {
+		if (iface->driver->global_init) {
+			iface->drv_global_priv =
+				iface->driver->global_init(iface->ctx);
+			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;
-
-	iface->drv_priv = iface->driver->init(iface, iface->ifname);
+	}
 	if (iface->drv_priv == NULL) {
 		wpa_printf(MSG_DEBUG, "Failed to initialize driver wrapper");
 		return;
@@ -87,6 +113,10 @@
 		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;
 	}
 }
@@ -172,6 +202,58 @@
 }
 
 
+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)
 {
@@ -307,6 +389,10 @@
 	    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;
@@ -356,7 +442,8 @@
 	}
 
 	proto = reg_cmd[0];
-	if (proto != ETH_P_EAPOL && proto != ETH_P_RSN_PREAUTH) {
+	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;
@@ -529,6 +616,9 @@
 		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;
 	}
 }
 
@@ -555,7 +645,7 @@
 
 
 static struct wpa_priv_interface *
-wpa_priv_interface_init(const char *dir, const char *params)
+wpa_priv_interface_init(void *ctx, const char *dir, const char *params)
 {
 	struct wpa_priv_interface *iface;
 	char *pos;
@@ -571,6 +661,7 @@
 	if (iface == NULL)
 		return NULL;
 	iface->fd = -1;
+	iface->ctx = ctx;
 
 	len = pos - params;
 	iface->driver_name = dup_binstr(params, len);
@@ -698,6 +789,36 @@
 }
 
 
+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)
 {
@@ -851,6 +972,10 @@
 				    &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);
@@ -874,14 +999,48 @@
 	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 - TODO",
-			   event);
+		wpa_printf(MSG_DEBUG, "Unsupported driver event %d (%s) - TODO",
+			   event, event_to_string(event));
 		break;
 	}
 }
 
 
+void wpa_supplicant_event_global(void *ctx, enum wpa_event_type event,
+				 union wpa_event_data *data)
+{
+	struct wpa_priv_global *global = ctx;
+	struct wpa_priv_interface *iface;
+
+	if (event != EVENT_INTERFACE_STATUS)
+		return;
+
+	for (iface = global->interfaces; iface; iface = iface->next) {
+		if (os_strcmp(iface->ifname, data->interface_status.ifname) ==
+		    0)
+			break;
+	}
+	if (iface && iface->driver->get_ifindex) {
+		unsigned int ifindex;
+
+		ifindex = iface->driver->get_ifindex(iface->drv_priv);
+		if (ifindex != data->interface_status.ifindex) {
+			wpa_printf(MSG_DEBUG,
+				   "%s: interface status ifindex %d mismatch (%d)",
+				   iface->ifname, ifindex,
+				   data->interface_status.ifindex);
+			return;
+		}
+	}
+	if (iface)
+		wpa_supplicant_event(iface, event, data);
+}
+
+
 void wpa_supplicant_rx_eapol(void *ctx, const u8 *src_addr,
 			     const u8 *buf, size_t len)
 {
@@ -940,12 +1099,13 @@
 static void usage(void)
 {
 	printf("wpa_priv v" VERSION_STR "\n"
-	       "Copyright (c) 2007-2009, Jouni Malinen <j@w1.fi> and "
+	       "Copyright (c) 2007-2016, Jouni Malinen <j@w1.fi> and "
 	       "contributors\n"
 	       "\n"
 	       "usage:\n"
-	       "  wpa_priv [-Bdd] [-P<pid file>] <driver:ifname> "
-	       "[driver:ifname ...]\n");
+	       "  wpa_priv [-Bdd] [-c<ctrl dir>] [-P<pid file>] "
+	       "<driver:ifname> \\\n"
+	       "           [driver:ifname ...]\n");
 }
 
 
@@ -956,13 +1116,17 @@
 	char *pid_file = NULL;
 	int daemonize = 0;
 	char *ctrl_dir = "/var/run/wpa_priv";
-	struct wpa_priv_interface *interfaces = NULL, *iface;
+	struct wpa_priv_global global;
+	struct wpa_priv_interface *iface;
 
 	if (os_program_init())
 		return -1;
 
 	wpa_priv_fd_workaround();
 
+	os_memset(&global, 0, sizeof(global));
+	global.interfaces = NULL;
+
 	for (;;) {
 		c = getopt(argc, argv, "Bc:dP:");
 		if (c < 0)
@@ -982,32 +1146,32 @@
 			break;
 		default:
 			usage();
-			goto out;
+			goto out2;
 		}
 	}
 
 	if (optind >= argc) {
 		usage();
-		goto out;
+		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 out;
+		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]);
+		iface = wpa_priv_interface_init(&global, ctrl_dir, argv[i]);
 		if (iface == NULL)
 			goto out;
-		iface->next = interfaces;
-		interfaces = iface;
+		iface->next = global.interfaces;
+		global.interfaces = iface;
 	}
 
-	if (daemonize && os_daemonize(pid_file))
+	if (daemonize && os_daemonize(pid_file) && eloop_sock_requeue())
 		goto out;
 
 	eloop_register_signal_terminate(wpa_priv_terminate, NULL);
@@ -1016,7 +1180,7 @@
 	ret = 0;
 
 out:
-	iface = interfaces;
+	iface = global.interfaces;
 	while (iface) {
 		struct wpa_priv_interface *prev = iface;
 		iface = iface->next;
@@ -1025,7 +1189,9 @@
 
 	eloop_destroy();
 
-	os_daemonize_terminate(pid_file);
+out2:
+	if (daemonize)
+		os_daemonize_terminate(pid_file);
 	os_free(pid_file);
 	os_program_deinit();
 
diff --git a/wpa_supplicant/wpa_supplicant.c b/wpa_supplicant/wpa_supplicant.c
index 722294d..51bb245 100644
--- a/wpa_supplicant/wpa_supplicant.c
+++ b/wpa_supplicant/wpa_supplicant.c
@@ -1,6 +1,6 @@
 /*
  * WPA Supplicant
- * Copyright (c) 2003-2015, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2003-2016, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -11,6 +11,10 @@
  */
 
 #include "includes.h"
+#ifdef CONFIG_MATCH_IFACE
+#include <net/if.h>
+#include <fnmatch.h>
+#endif /* CONFIG_MATCH_IFACE */
 
 #include "common.h"
 #include "crypto/random.h"
@@ -35,6 +39,7 @@
 #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"
@@ -57,7 +62,7 @@
 
 const char *const wpa_supplicant_version =
 "wpa_supplicant v" VERSION_STR "\n"
-"Copyright (c) 2003-2015, Jouni Malinen <j@w1.fi> and contributors";
+"Copyright (c) 2003-2016, 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"
@@ -396,6 +401,18 @@
 }
 
 
+static void free_bss_tmp_disallowed(struct wpa_supplicant *wpa_s)
+{
+	struct wpa_bss_tmp_disallowed *bss, *prev;
+
+	dl_list_for_each_safe(bss, prev, &wpa_s->bss_tmp_disallowed,
+			      struct wpa_bss_tmp_disallowed, list) {
+		dl_list_del(&bss->list);
+		os_free(bss);
+	}
+}
+
+
 static void wpa_supplicant_cleanup(struct wpa_supplicant *wpa_s)
 {
 	int i;
@@ -544,6 +561,18 @@
 	}
 
 	wmm_ac_notify_disassoc(wpa_s);
+
+	wpa_s->sched_scan_plans_num = 0;
+	os_free(wpa_s->sched_scan_plans);
+	wpa_s->sched_scan_plans = NULL;
+
+#ifdef CONFIG_MBO
+	wpa_s->non_pref_chan_num = 0;
+	os_free(wpa_s->non_pref_chan);
+	wpa_s->non_pref_chan = NULL;
+#endif /* CONFIG_MBO */
+
+	free_bss_tmp_disallowed(wpa_s);
 }
 
 
@@ -962,6 +991,11 @@
 			wpa_supplicant_terminate_proc(global);
 		}
 	}
+
+	if (wpa_debug_reopen_file() < 0) {
+		/* Ignore errors since we cannot really do much to fix this */
+		wpa_printf(MSG_DEBUG, "Could not reopen debug log file");
+	}
 }
 
 
@@ -1149,6 +1183,10 @@
 			return -1;
 	}
 
+#ifdef CONFIG_NO_WPA
+	wpa_s->group_cipher = WPA_CIPHER_NONE;
+	wpa_s->pairwise_cipher = WPA_CIPHER_NONE;
+#else /* CONFIG_NO_WPA */
 	sel = ie.group_cipher & ssid->group_cipher;
 	wpa_s->group_cipher = wpa_pick_group_cipher(sel);
 	if (wpa_s->group_cipher < 0) {
@@ -1168,6 +1206,7 @@
 	}
 	wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using PTK %s",
 		wpa_cipher_txt(wpa_s->pairwise_cipher));
+#endif /* CONFIG_NO_WPA */
 
 	sel = ie.key_mgmt & ssid->key_mgmt;
 #ifdef CONFIG_SAE
@@ -1278,7 +1317,8 @@
 		int psk_set = 0;
 
 		if (ssid->psk_set) {
-			wpa_sm_set_pmk(wpa_s->wpa, ssid->psk, PMK_LEN, NULL);
+			wpa_sm_set_pmk(wpa_s->wpa, ssid->psk, PMK_LEN, NULL,
+				       NULL);
 			psk_set = 1;
 		}
 #ifndef CONFIG_NO_PBKDF2
@@ -1289,7 +1329,7 @@
 				    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);
+			wpa_sm_set_pmk(wpa_s->wpa, psk, PMK_LEN, NULL, NULL);
 			psk_set = 1;
 			os_memset(psk, 0, sizeof(psk));
 		}
@@ -1327,7 +1367,8 @@
 				wpa_hexdump_key(MSG_MSGDUMP, "PSK (from "
 						"external passphrase)",
 						psk, PMK_LEN);
-				wpa_sm_set_pmk(wpa_s->wpa, psk, PMK_LEN, NULL);
+				wpa_sm_set_pmk(wpa_s->wpa, psk, PMK_LEN, NULL,
+					       NULL);
 				psk_set = 1;
 				os_memset(psk, 0, sizeof(psk));
 			} else
@@ -1340,7 +1381,8 @@
 					ext_password_free(pw);
 					return -1;
 				}
-				wpa_sm_set_pmk(wpa_s->wpa, psk, PMK_LEN, NULL);
+				wpa_sm_set_pmk(wpa_s->wpa, psk, PMK_LEN, NULL,
+					       NULL);
 				psk_set = 1;
 				os_memset(psk, 0, sizeof(psk));
 			} else {
@@ -1403,6 +1445,9 @@
 		if (wpa_s->conf->hs20)
 			*pos |= 0x40; /* Bit 46 - WNM-Notification */
 #endif /* CONFIG_HS20 */
+#ifdef CONFIG_MBO
+		*pos |= 0x40; /* Bit 46 - WNM-Notification */
+#endif /* CONFIG_MBO */
 		break;
 	case 6: /* Bits 48-55 */
 		break;
@@ -1585,6 +1630,15 @@
 	struct wpa_connect_work *cwork;
 	int rand_style;
 
+	wpa_s->own_disconnect_req = 0;
+
+	/*
+	 * If we are starting a new connection, any previously pending EAPOL
+	 * RX cannot be valid anymore.
+	 */
+	wpabuf_free(wpa_s->pending_eapol_rx);
+	wpa_s->pending_eapol_rx = NULL;
+
 	if (ssid->mac_addr == -1)
 		rand_style = wpa_s->conf->mac_addr;
 	else
@@ -1592,9 +1646,11 @@
 
 	wmm_ac_clear_saved_tspecs(wpa_s);
 	wpa_s->reassoc_same_bss = 0;
+	wpa_s->reassoc_same_ess = 0;
 
 	if (wpa_s->last_ssid == ssid) {
 		wpa_dbg(wpa_s, MSG_DEBUG, "Re-association to the same ESS");
+		wpa_s->reassoc_same_ess = 1;
 		if (wpa_s->current_bss && wpa_s->current_bss == bss) {
 			wmm_ac_save_tspecs(wpa_s);
 			wpa_s->reassoc_same_bss = 1;
@@ -1660,10 +1716,9 @@
 			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);
+		wpa_msg(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");
@@ -1693,6 +1748,8 @@
 		return;
 	}
 
+	wpas_abort_ongoing_scan(wpa_s);
+
 	cwork = os_zalloc(sizeof(*cwork));
 	if (cwork == NULL)
 		return;
@@ -1714,6 +1771,36 @@
 }
 
 
+static int drv_supports_vht(struct wpa_supplicant *wpa_s,
+			    const struct wpa_ssid *ssid)
+{
+	enum hostapd_hw_mode hw_mode;
+	struct hostapd_hw_modes *mode = NULL;
+	u8 channel;
+	int i;
+
+#ifdef CONFIG_HT_OVERRIDES
+	if (ssid->disable_ht)
+		return 0;
+#endif /* CONFIG_HT_OVERRIDES */
+
+	hw_mode = ieee80211_freq_to_chan(ssid->frequency, &channel);
+	if (hw_mode == NUM_HOSTAPD_MODES)
+		return 0;
+	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 0;
+
+	return mode->vht_capab != 0;
+}
+
+
 void ibss_mesh_setup_freq(struct wpa_supplicant *wpa_s,
 			  const struct wpa_ssid *ssid,
 			  struct hostapd_freq_params *freq)
@@ -1726,8 +1813,10 @@
 	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;
+	unsigned int j, k;
 	struct hostapd_freq_params vht_freq;
+	int chwidth, seg0, seg1;
+	u32 vht_caps = 0;
 
 	freq->freq = ssid->frequency;
 
@@ -1877,12 +1966,12 @@
 		   "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)
+	if (!drv_supports_vht(wpa_s, ssid))
 		return;
 
 	/* For IBSS check VHT_IBSS flag */
-	if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_VHT_IBSS))
+	if (ssid->mode == WPAS_MODE_IBSS &&
+	    !(wpa_s->drv_flags & WPA_DRIVER_FLAGS_VHT_IBSS))
 		return;
 
 	vht_freq = *freq;
@@ -1913,12 +2002,45 @@
 			return;
 	}
 
+	chwidth = VHT_CHANWIDTH_80MHZ;
+	seg0 = vht80[j] + 6;
+	seg1 = 0;
+
+	if (ssid->max_oper_chwidth == VHT_CHANWIDTH_80P80MHZ) {
+		/* setup center_freq2, bandwidth */
+		for (k = 0; k < ARRAY_SIZE(vht80); k++) {
+			/* Only accept 80 MHz segments separated by a gap */
+			if (j == k || abs(vht80[j] - vht80[k]) == 16)
+				continue;
+			for (i = vht80[k]; i < vht80[k] + 16; i += 4) {
+				struct hostapd_channel_data *chan;
+
+				chan = hw_get_channel_chan(mode, i, NULL);
+				if (!chan)
+					continue;
+
+				if (chan->flag & (HOSTAPD_CHAN_DISABLED |
+						  HOSTAPD_CHAN_NO_IR |
+						  HOSTAPD_CHAN_RADAR))
+					continue;
+
+				/* Found a suitable second segment for 80+80 */
+				chwidth = VHT_CHANWIDTH_80P80MHZ;
+				vht_caps |=
+					VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ;
+				seg1 = vht80[k] + 6;
+			}
+
+			if (chwidth == VHT_CHANWIDTH_80P80MHZ)
+				break;
+		}
+	}
+
 	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)
+				    chwidth, seg0, seg1, vht_caps) != 0)
 		return;
 
 	*freq = vht_freq;
@@ -1943,6 +2065,7 @@
 	int wep_keys_set = 0;
 	int assoc_failed = 0;
 	struct wpa_ssid *old_ssid;
+	u8 prev_bssid[ETH_ALEN];
 #ifdef CONFIG_HT_OVERRIDES
 	struct ieee80211_ht_capabilities htcaps;
 	struct ieee80211_ht_capabilities htcaps_mask;
@@ -1951,6 +2074,9 @@
        struct ieee80211_vht_capabilities vhtcaps;
        struct ieee80211_vht_capabilities vhtcaps_mask;
 #endif /* CONFIG_VHT_OVERRIDES */
+#ifdef CONFIG_MBO
+	const u8 *mbo = NULL;
+#endif /* CONFIG_MBO */
 
 	if (deinit) {
 		if (work->started) {
@@ -1966,12 +2092,14 @@
 
 	wpa_s->connect_work = work;
 
-	if (cwork->bss_removed || !wpas_valid_bss_ssid(wpa_s, bss, ssid)) {
+	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_memcpy(prev_bssid, wpa_s->bssid, ETH_ALEN);
 	os_memset(&params, 0, sizeof(params));
 	wpa_s->reassociate = 0;
 	wpa_s->eap_expected_failure = 0;
@@ -2015,7 +2143,9 @@
 			wpa_ssid_txt(ssid->ssid, ssid->ssid_len));
 		os_memset(wpa_s->pending_bssid, 0, ETH_ALEN);
 	}
-	wpa_supplicant_cancel_sched_scan(wpa_s);
+	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
@@ -2132,25 +2262,21 @@
 	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;
+#ifdef CONFIG_MBO
+	if (bss) {
+		mbo = wpa_bss_get_vendor_ie(bss, MBO_IE_VENDOR_TYPE);
+		if (mbo) {
+			int 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);
+			len = wpas_mbo_supp_op_class_ie(wpa_s, bss->freq,
+							wpa_ie + wpa_ie_len,
+							sizeof(wpa_ie) -
+							wpa_ie_len);
+			if (len > 0)
+				wpa_ie_len += len;
 		}
 	}
-#endif /* CONFIG_HS20 */
+#endif /* CONFIG_MBO */
 
 	/*
 	 * Workaround: Add Extended Capabilities element only if the AP
@@ -2176,6 +2302,27 @@
 		}
 	}
 
+#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 */
+
 	if (wpa_s->vendor_elem[VENDOR_ELEM_ASSOC_REQ]) {
 		struct wpabuf *buf = wpa_s->vendor_elem[VENDOR_ELEM_ASSOC_REQ];
 		size_t len;
@@ -2188,6 +2335,29 @@
 		}
 	}
 
+#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 */
+
+#ifdef CONFIG_MBO
+	if (mbo) {
+		int len;
+
+		len = wpas_mbo_ie(wpa_s, wpa_ie + wpa_ie_len,
+				  sizeof(wpa_ie) - wpa_ie_len);
+		if (len >= 0)
+			wpa_ie_len += len;
+	}
+#endif /* CONFIG_MBO */
+
 	wpa_clear_keys(wpa_s, bss ? bss->bssid : NULL);
 	use_crypt = 1;
 	cipher_pairwise = wpa_s->pairwise_cipher;
@@ -2240,9 +2410,11 @@
 		}
 		params.bssid_hint = bss->bssid;
 		params.freq_hint = bss->freq;
+		params.pbss = bss_is_pbss(bss);
 	} else {
 		params.ssid = ssid->ssid;
 		params.ssid_len = ssid->ssid_len;
+		params.pbss = ssid->pbss;
 	}
 
 	if (ssid->mode == WPAS_MODE_IBSS && ssid->bssid_set &&
@@ -2326,8 +2498,8 @@
 
 	params.p2p = ssid->p2p_group;
 
-	if (wpa_s->parent->set_sta_uapsd)
-		params.uapsd = wpa_s->parent->sta_uapsd;
+	if (wpa_s->p2pdev->set_sta_uapsd)
+		params.uapsd = wpa_s->p2pdev->sta_uapsd;
 	else
 		params.uapsd = -1;
 
@@ -2368,6 +2540,10 @@
 	}
 #endif /* CONFIG_P2P */
 
+	if (wpa_s->reassoc_same_ess && !is_zero_ether_addr(prev_bssid) &&
+	    wpa_s->current_ssid)
+		params.prev_bssid = prev_bssid;
+
 	ret = wpa_drv_associate(wpa_s, &params);
 	if (ret < 0) {
 		wpa_msg(wpa_s, MSG_INFO, "Association request to the driver "
@@ -2504,8 +2680,8 @@
 
 #ifdef CONFIG_MESH
 	if (wpa_s->ifmsh) {
-		wpa_msg_ctrl(wpa_s, MSG_INFO, MESH_GROUP_REMOVED "%s",
-			     wpa_s->ifname);
+		wpa_msg(wpa_s, MSG_INFO, MESH_GROUP_REMOVED "%s",
+			wpa_s->ifname);
 		wpa_supplicant_leave_mesh(wpa_s);
 	}
 #endif /* CONFIG_MESH */
@@ -2558,15 +2734,20 @@
 	} else
 		wpa_supplicant_enable_one_network(wpa_s, ssid);
 
-	if (wpa_s->reassociate && !wpa_s->disconnected) {
+	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)
+		if (wpa_supplicant_fast_associate(wpa_s) != 1) {
+			wpa_s->scan_req = NORMAL_SCAN_REQ;
 			wpa_supplicant_req_scan(wpa_s, 0, 0);
+		}
 	}
 }
 
@@ -2667,7 +2848,8 @@
 			wpas_notify_network_enabled_changed(wpa_s, other_ssid);
 	}
 
-	if (ssid && ssid == wpa_s->current_ssid && wpa_s->current_ssid) {
+	if (ssid && ssid == wpa_s->current_ssid && wpa_s->current_ssid &&
+	    wpa_s->wpa_state >= WPA_AUTHENTICATING) {
 		/* We are already associated with the selected network */
 		wpa_printf(MSG_DEBUG, "Already associated with the "
 			   "selected network - do nothing");
@@ -2694,8 +2876,10 @@
 	wpa_s->reassociate = 1;
 
 	if (wpa_s->connect_without_scan ||
-	    wpa_supplicant_fast_associate(wpa_s) != 1)
+	    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);
@@ -2770,6 +2954,11 @@
 	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 &&
@@ -2966,7 +3155,7 @@
 	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();
+		global->drv_priv[i] = wpa_drivers[i]->global_init(global);
 		if (global->drv_priv[i] == NULL) {
 			wpa_printf(MSG_ERROR, "Failed to initialize driver "
 				   "'%s'", wpa_drivers[i]->name);
@@ -3275,6 +3464,12 @@
 		}
 	}
 
+	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
@@ -3327,8 +3522,11 @@
 	wpa_s->scan_interval = 5;
 	wpa_s->new_connection = 1;
 	wpa_s->parent = parent ? parent : wpa_s;
+	wpa_s->p2pdev = wpa_s->parent;
 	wpa_s->sched_scanning = 0;
 
+	dl_list_init(&wpa_s->bss_tmp_disallowed);
+
 	return wpa_s;
 }
 
@@ -3700,6 +3898,124 @@
 }
 
 
+#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)
 {
@@ -3718,6 +4034,55 @@
 }
 
 
+enum wpa_radio_work_band wpas_freq_to_band(int freq)
+{
+	if (freq < 3000)
+		return BAND_2_4_GHZ;
+	if (freq > 50000)
+		return BAND_60_GHZ;
+	return BAND_5_GHZ;
+}
+
+
+unsigned int wpas_get_bands(struct wpa_supplicant *wpa_s, const int *freqs)
+{
+	int i;
+	unsigned int band = 0;
+
+	if (freqs) {
+		/* freqs are specified for the radio work */
+		for (i = 0; freqs[i]; i++)
+			band |= wpas_freq_to_band(freqs[i]);
+	} else {
+		/*
+		 * freqs are not specified, implies all
+		 * the supported freqs by HW
+		 */
+		for (i = 0; i < wpa_s->hw.num_modes; i++) {
+			if (wpa_s->hw.modes[i].num_channels != 0) {
+				if (wpa_s->hw.modes[i].mode ==
+				    HOSTAPD_MODE_IEEE80211B ||
+				    wpa_s->hw.modes[i].mode ==
+				    HOSTAPD_MODE_IEEE80211G)
+					band |= BAND_2_4_GHZ;
+				else if (wpa_s->hw.modes[i].mode ==
+					 HOSTAPD_MODE_IEEE80211A)
+					band |= BAND_5_GHZ;
+				else if (wpa_s->hw.modes[i].mode ==
+					 HOSTAPD_MODE_IEEE80211AD)
+					band |= BAND_60_GHZ;
+				else if (wpa_s->hw.modes[i].mode ==
+					 HOSTAPD_MODE_IEEE80211ANY)
+					band = BAND_2_4_GHZ | BAND_5_GHZ |
+						BAND_60_GHZ;
+			}
+		}
+	}
+
+	return band;
+}
+
+
 static struct wpa_radio * radio_add_interface(struct wpa_supplicant *wpa_s,
 					      const char *rn)
 {
@@ -3770,11 +4135,103 @@
 	}
 #endif /* CONFIG_P2P */
 
+	if (work->started) {
+		work->wpa_s->radio->num_active_works--;
+		wpa_dbg(work->wpa_s, MSG_DEBUG,
+			"radio_work_free('%s'@%p: num_active_works --> %u",
+			work->type, work,
+			work->wpa_s->radio->num_active_works);
+	}
+
 	dl_list_del(&work->list);
 	os_free(work);
 }
 
 
+static struct wpa_radio_work * radio_work_get_next_work(struct wpa_radio *radio)
+{
+	struct wpa_radio_work *active_work = NULL;
+	struct wpa_radio_work *tmp;
+
+	/* Get the active work to know the type and band. */
+	dl_list_for_each(tmp, &radio->work, struct wpa_radio_work, list) {
+		if (tmp->started) {
+			active_work = tmp;
+			break;
+		}
+	}
+
+	if (!active_work) {
+		/* No active work, start one */
+		radio->num_active_works = 0;
+		dl_list_for_each(tmp, &radio->work, struct wpa_radio_work,
+				 list) {
+			if (os_strcmp(tmp->type, "scan") == 0 &&
+			    radio->external_scan_running &&
+			    (((struct wpa_driver_scan_params *)
+			      tmp->ctx)->only_new_results ||
+			     tmp->wpa_s->clear_driver_scan_cache))
+				continue;
+			return tmp;
+		}
+		return NULL;
+	}
+
+	if (os_strcmp(active_work->type, "sme-connect") == 0 ||
+	    os_strcmp(active_work->type, "connect") == 0) {
+		/*
+		 * If the active work is either connect or sme-connect,
+		 * do not parallelize them with other radio works.
+		 */
+		wpa_dbg(active_work->wpa_s, MSG_DEBUG,
+			"Do not parallelize radio work with %s",
+			active_work->type);
+		return NULL;
+	}
+
+	dl_list_for_each(tmp, &radio->work, struct wpa_radio_work, list) {
+		if (tmp->started)
+			continue;
+
+		/*
+		 * If connect or sme-connect are enqueued, parallelize only
+		 * those operations ahead of them in the queue.
+		 */
+		if (os_strcmp(tmp->type, "connect") == 0 ||
+		    os_strcmp(tmp->type, "sme-connect") == 0)
+			break;
+
+		/*
+		 * Check that the radio works are distinct and
+		 * on different bands.
+		 */
+		if (os_strcmp(active_work->type, tmp->type) != 0 &&
+		    (active_work->bands != tmp->bands)) {
+			/*
+			 * If a scan has to be scheduled through nl80211 scan
+			 * interface and if an external scan is already running,
+			 * do not schedule the scan since it is likely to get
+			 * rejected by kernel.
+			 */
+			if (os_strcmp(tmp->type, "scan") == 0 &&
+			    radio->external_scan_running &&
+			    (((struct wpa_driver_scan_params *)
+			      tmp->ctx)->only_new_results ||
+			     tmp->wpa_s->clear_driver_scan_cache))
+				continue;
+
+			wpa_dbg(active_work->wpa_s, MSG_DEBUG,
+				"active_work:%s new_work:%s",
+				active_work->type, tmp->type);
+			return tmp;
+		}
+	}
+
+	/* Did not find a radio work to schedule in parallel. */
+	return NULL;
+}
+
+
 static void radio_start_next_work(void *eloop_ctx, void *timeout_ctx)
 {
 	struct wpa_radio *radio = eloop_ctx;
@@ -3783,26 +4240,48 @@
 	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");
+	if (work == NULL) {
+		radio->num_active_works = 0;
 		return;
 	}
 
+	wpa_s = dl_list_first(&radio->ifaces, struct wpa_supplicant,
+			      radio_list);
+
+	if (!(wpa_s &&
+	      wpa_s->drv_flags & WPA_DRIVER_FLAGS_OFFCHANNEL_SIMULTANEOUS)) {
+		if (work->started)
+			return; /* already started and still in progress */
+
+		if (wpa_s && wpa_s->radio->external_scan_running) {
+			wpa_printf(MSG_DEBUG, "Delay radio work start until externally triggered scan completes");
+			return;
+		}
+	} else {
+		work = NULL;
+		if (radio->num_active_works < MAX_ACTIVE_WORKS) {
+			/* get the work to schedule next */
+			work = radio_work_get_next_work(radio);
+		}
+		if (!work)
+			return;
+	}
+
+	wpa_s = work->wpa_s;
 	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",
+	wpa_dbg(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;
+	radio->num_active_works++;
+
 	work->cb(work, 0);
+
+	if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_OFFCHANNEL_SIMULTANEOUS) &&
+	    radio->num_active_works < MAX_ACTIVE_WORKS)
+		radio_work_check_next(wpa_s);
 }
 
 
@@ -3910,6 +4389,7 @@
 		   void (*cb)(struct wpa_radio_work *work, int deinit),
 		   void *ctx)
 {
+	struct wpa_radio *radio = wpa_s->radio;
 	struct wpa_radio_work *work;
 	int was_empty;
 
@@ -3924,6 +4404,16 @@
 	work->cb = cb;
 	work->ctx = ctx;
 
+	if (freq)
+		work->bands = wpas_freq_to_band(freq);
+	else if (os_strcmp(type, "scan") == 0 ||
+		 os_strcmp(type, "p2p-scan") == 0)
+		work->bands = wpas_get_bands(wpa_s,
+					     ((struct wpa_driver_scan_params *)
+					      ctx)->freqs);
+	else
+		work->bands = wpas_get_bands(wpa_s, NULL);
+
 	was_empty = dl_list_empty(&wpa_s->radio->work);
 	if (next)
 		dl_list_add(&wpa_s->radio->work, &work->list);
@@ -3932,6 +4422,12 @@
 	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);
+	} else if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_OFFCHANNEL_SIMULTANEOUS)
+		   && radio->num_active_works < MAX_ACTIVE_WORKS) {
+		wpa_dbg(wpa_s, MSG_DEBUG,
+			"Try to schedule a radio work (num_active_works=%u)",
+			radio->num_active_works);
+		radio_work_check_next(wpa_s);
 	}
 
 	return 0;
@@ -4187,6 +4683,11 @@
 		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->max_sched_scan_plans = capa.max_sched_scan_plans;
+		wpa_s->max_sched_scan_plan_interval =
+			capa.max_sched_scan_plan_interval;
+		wpa_s->max_sched_scan_plan_iterations =
+			capa.max_sched_scan_plan_iterations;
 		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;
@@ -4238,6 +4739,28 @@
 		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;
 
@@ -4304,6 +4827,15 @@
 
 	wpas_rrm_reset(wpa_s);
 
+	wpas_sched_scan_plans_set(wpa_s, wpa_s->conf->sched_scan_plans);
+
+#ifdef CONFIG_HS20
+	hs20_init(wpa_s);
+#endif /* CONFIG_HS20 */
+#ifdef CONFIG_MBO
+	wpas_mbo_update_non_pref_chan(wpa_s, wpa_s->conf->non_pref_chan);
+#endif /* CONFIG_MBO */
+
 	return 0;
 }
 
@@ -4319,6 +4851,8 @@
 
 	iface = global->ifaces;
 	while (iface) {
+		if (iface->p2pdev == wpa_s)
+			iface->p2pdev = iface->parent;
 		if (iface == wpa_s || iface->parent != wpa_s) {
 			iface = iface->next;
 			continue;
@@ -4346,6 +4880,17 @@
 	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);
 
@@ -4378,6 +4923,74 @@
 }
 
 
+#ifdef CONFIG_MATCH_IFACE
+
+/**
+ * wpa_supplicant_match_iface - Match an interface description to a name
+ * @global: Pointer to global data from wpa_supplicant_init()
+ * @ifname: Name of the interface to match
+ * Returns: Pointer to the created interface description or %NULL on failure
+ */
+struct wpa_interface * wpa_supplicant_match_iface(struct wpa_global *global,
+						  const char *ifname)
+{
+	int i;
+	struct wpa_interface *iface, *miface;
+
+	for (i = 0; i < global->params.match_iface_count; i++) {
+		miface = &global->params.match_ifaces[i];
+		if (!miface->ifname ||
+		    fnmatch(miface->ifname, ifname, 0) == 0) {
+			iface = os_zalloc(sizeof(*iface));
+			if (!iface)
+				return NULL;
+			*iface = *miface;
+			iface->ifname = ifname;
+			return iface;
+		}
+	}
+
+	return NULL;
+}
+
+
+/**
+ * wpa_supplicant_match_existing - Match existing interfaces
+ * @global: Pointer to global data from wpa_supplicant_init()
+ * Returns: 0 on success, -1 on failure
+ */
+static int wpa_supplicant_match_existing(struct wpa_global *global)
+{
+	struct if_nameindex *ifi, *ifp;
+	struct wpa_supplicant *wpa_s;
+	struct wpa_interface *iface;
+
+	ifp = if_nameindex();
+	if (!ifp) {
+		wpa_printf(MSG_ERROR, "if_nameindex: %s", strerror(errno));
+		return -1;
+	}
+
+	for (ifi = ifp; ifi->if_name; ifi++) {
+		wpa_s = wpa_supplicant_get_iface(global, ifi->if_name);
+		if (wpa_s)
+			continue;
+		iface = wpa_supplicant_match_iface(global, ifi->if_name);
+		if (iface) {
+			wpa_s = wpa_supplicant_add_iface(global, iface, NULL);
+			os_free(iface);
+			if (wpa_s)
+				wpa_s->matched = 1;
+		}
+	}
+
+	if_freenameindex(ifp);
+	return 0;
+}
+
+#endif /* CONFIG_MATCH_IFACE */
+
+
 /**
  * wpa_supplicant_add_iface - Add a new network interface
  * @global: Pointer to global data from wpa_supplicant_init()
@@ -4578,6 +5191,33 @@
 #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
@@ -4652,9 +5292,23 @@
 	if (params->override_ctrl_interface)
 		global->params.override_ctrl_interface =
 			os_strdup(params->override_ctrl_interface);
+#ifdef CONFIG_MATCH_IFACE
+	global->params.match_iface_count = params->match_iface_count;
+	if (params->match_iface_count) {
+		global->params.match_ifaces =
+			os_calloc(params->match_iface_count,
+				  sizeof(struct wpa_interface));
+		os_memcpy(global->params.match_ifaces,
+			  params->match_ifaces,
+			  params->match_iface_count *
+			  sizeof(struct wpa_interface));
+	}
+#endif /* CONFIG_MATCH_IFACE */
+#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 =
@@ -4704,6 +5358,9 @@
 	}
 #endif /* CONFIG_WIFI_DISPLAY */
 
+	eloop_register_timeout(WPA_SUPPLICANT_CLEANUP_INTERVAL, 0,
+			       wpas_periodic, global, NULL);
+
 	return global;
 }
 
@@ -4722,12 +5379,18 @@
 	struct wpa_supplicant *wpa_s;
 
 	if (global->params.daemonize &&
-	    wpa_supplicant_daemon(global->params.pid_file))
+	    (wpa_supplicant_daemon(global->params.pid_file) ||
+	     eloop_sock_requeue()))
 		return -1;
 
+#ifdef CONFIG_MATCH_IFACE
+	if (wpa_supplicant_match_existing(global))
+		return -1;
+#endif
+
 	if (global->params.wait_for_monitor) {
 		for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next)
-			if (wpa_s->ctrl_iface)
+			if (wpa_s->ctrl_iface && !wpa_s->p2p_mgmt)
 				wpa_supplicant_ctrl_iface_wait(
 					wpa_s->ctrl_iface);
 	}
@@ -4755,6 +5418,8 @@
 	if (global == NULL)
 		return;
 
+	eloop_cancel_timeout(wpas_periodic, global, NULL);
+
 #ifdef CONFIG_WIFI_DISPLAY
 	wifi_display_deinit(global);
 #endif /* CONFIG_WIFI_DISPLAY */
@@ -4791,7 +5456,12 @@
 	os_free(global->params.ctrl_interface_group);
 	os_free(global->params.override_driver);
 	os_free(global->params.override_ctrl_interface);
+#ifdef CONFIG_MATCH_IFACE
+	os_free(global->params.match_ifaces);
+#endif /* CONFIG_MATCH_IFACE */
+#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);
@@ -4821,6 +5491,9 @@
 	if (wpa_s->conf->changed_parameters & CFG_CHANGED_EXT_PW_BACKEND)
 		wpas_init_ext_pw(wpa_s);
 
+	if (wpa_s->conf->changed_parameters & CFG_CHANGED_SCHED_SCAN_PLANS)
+		wpas_sched_scan_plans_set(wpa_s, wpa_s->conf->sched_scan_plans);
+
 #ifdef CONFIG_WPS
 	wpas_wps_update_config(wpa_s);
 #endif /* CONFIG_WPS */
@@ -5060,6 +5733,16 @@
 		if (wpa_s->wpa_state == WPA_SCANNING && !wpa_s->scanning)
 			wpa_supplicant_req_scan(wpa_s, 0, 0);
 		break;
+	case WPA_CTRL_REQ_EXT_CERT_CHECK:
+		if (eap->pending_ext_cert_check != PENDING_CHECK)
+			return -1;
+		if (os_strcmp(value, "good") == 0)
+			eap->pending_ext_cert_check = EXT_CERT_CHECK_GOOD;
+		else if (os_strcmp(value, "bad") == 0)
+			eap->pending_ext_cert_check = EXT_CERT_CHECK_BAD;
+		else
+			return -1;
+		break;
 	default:
 		wpa_printf(MSG_DEBUG, "CTRL_IFACE: Unknown field '%s'", field);
 		return -1;
@@ -5330,7 +6013,8 @@
 			continue;
 
 		if (ifs->current_ssid->mode == WPAS_MODE_AP ||
-		    ifs->current_ssid->mode == WPAS_MODE_P2P_GO)
+		    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;
@@ -5346,7 +6030,7 @@
 			freqs_data[idx++].freq = freq;
 
 		if (ifs->current_ssid->mode == WPAS_MODE_INFRA) {
-			freqs_data[i].flags = ifs->current_ssid->p2p_group ?
+			freqs_data[i].flags |= ifs->current_ssid->p2p_group ?
 				WPA_FREQ_USED_BY_P2P_CLIENT :
 				WPA_FREQ_USED_BY_INFRA_STATION;
 		}
@@ -5646,3 +6330,175 @@
 	}
 	wpabuf_free(buf);
 }
+
+
+struct wpa_supplicant *
+wpas_vendor_elem(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:
+	case VENDOR_ELEM_P2P_ASSOC_RESP:
+		return wpa_s->p2pdev;
+#endif /* CONFIG_P2P */
+	default:
+		return wpa_s;
+	}
+}
+
+
+void wpas_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 */
+}
+
+
+int wpas_vendor_elem_remove(struct wpa_supplicant *wpa_s, int frame,
+			    const u8 *elem, size_t len)
+{
+	u8 *ie, *end;
+
+	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, elem, 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;
+		}
+		wpas_vendor_elem_update(wpa_s);
+		return 0;
+	}
+
+	return -1;
+}
+
+
+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 struct
+wpa_bss_tmp_disallowed * wpas_get_disallowed_bss(struct wpa_supplicant *wpa_s,
+						 const u8 *bssid)
+{
+	struct wpa_bss_tmp_disallowed *bss;
+
+	dl_list_for_each(bss, &wpa_s->bss_tmp_disallowed,
+			 struct wpa_bss_tmp_disallowed, list) {
+		if (os_memcmp(bssid, bss->bssid, ETH_ALEN) == 0)
+			return bss;
+	}
+
+	return NULL;
+}
+
+
+void wpa_bss_tmp_disallow(struct wpa_supplicant *wpa_s, const u8 *bssid,
+			  unsigned int sec)
+{
+	struct wpa_bss_tmp_disallowed *bss;
+	struct os_reltime until;
+
+	os_get_reltime(&until);
+	until.sec += sec;
+
+	bss = wpas_get_disallowed_bss(wpa_s, bssid);
+	if (bss) {
+		bss->disallowed_until = until;
+		return;
+	}
+
+	bss = os_malloc(sizeof(*bss));
+	if (!bss) {
+		wpa_printf(MSG_DEBUG,
+			   "Failed to allocate memory for temp disallow BSS");
+		return;
+	}
+
+	bss->disallowed_until = until;
+	os_memcpy(bss->bssid, bssid, ETH_ALEN);
+	dl_list_add(&wpa_s->bss_tmp_disallowed, &bss->list);
+}
+
+
+int wpa_is_bss_tmp_disallowed(struct wpa_supplicant *wpa_s, const u8 *bssid)
+{
+	struct wpa_bss_tmp_disallowed *bss = NULL, *tmp, *prev;
+	struct os_reltime now, age;
+
+	os_get_reltime(&now);
+
+	dl_list_for_each_safe(tmp, prev, &wpa_s->bss_tmp_disallowed,
+			 struct wpa_bss_tmp_disallowed, list) {
+		if (!os_reltime_before(&now, &tmp->disallowed_until)) {
+			/* This BSS is not disallowed anymore */
+			dl_list_del(&tmp->list);
+			os_free(tmp);
+			continue;
+		}
+		if (os_memcmp(bssid, tmp->bssid, ETH_ALEN) == 0) {
+			bss = tmp;
+			break;
+		}
+	}
+	if (!bss)
+		return 0;
+
+	os_reltime_sub(&bss->disallowed_until, &now, &age);
+	wpa_printf(MSG_DEBUG,
+		   "BSS " MACSTR " disabled for %ld.%0ld seconds",
+		   MAC2STR(bss->bssid), age.sec, age.usec);
+	return 1;
+}
diff --git a/wpa_supplicant/wpa_supplicant.conf b/wpa_supplicant/wpa_supplicant.conf
index d380965..e55b380 100644
--- a/wpa_supplicant/wpa_supplicant.conf
+++ b/wpa_supplicant/wpa_supplicant.conf
@@ -108,12 +108,35 @@
 #    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
 
+# Whether to force passive scan for network connection
+#
+# By default, scans will send out Probe Request frames on channels that allow
+# active scanning. This advertise the local station to the world. Normally this
+# is fine, but users may wish to do passive scanning where the radio should only
+# listen quietly for Beacon frames and not send any Probe Request frames. Actual
+# functionality may be driver dependent.
+#
+# 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.
+#
+# 0:  Do normal scans (allow active scans) (default)
+# 1:  Do passive scans.
+#passive_scan=0
+
 # 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
@@ -291,7 +314,9 @@
 # up to the limit of 300 seconds (3, 9, 27 ... 300)
 # For periodic module, parameters would be <fixed interval>
 #autoscan=periodic:30
-# So a delay of 30 seconds will be applied between each scan
+# So a delay of 30 seconds will be applied between each scan.
+# Note: If sched_scan_plans are configured and supported by the driver,
+# autoscan is ignored.
 
 # filter_ssids - SSID-based scan result filtering
 # 0 = do not filter scan results (default)
@@ -561,6 +586,8 @@
 #	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
+#	3 = require valid OCSP stapling response for all not-trusted
+#	    certificates in the server certificate chain
 #
 # sim_num: Identifier for which SIM to use in multi-SIM devices
 #
@@ -593,6 +620,41 @@
 # Hotspot 2.0
 # hs20=1
 
+# Scheduled scan plans
+#
+# A space delimited list of scan plans. Each scan plan specifies the scan
+# interval and number of iterations, delimited by a colon. The last scan plan
+# will run infinitely and thus must specify only the interval and not the number
+# of iterations.
+#
+# The driver advertises the maximum number of scan plans supported. If more scan
+# plans than supported are configured, only the first ones are set (up to the
+# maximum supported). The last scan plan that specifies only the interval is
+# always set as the last plan.
+#
+# If the scan interval or the number of iterations for a scan plan exceeds the
+# maximum supported, it will be set to the maximum supported value.
+#
+# Format:
+# sched_scan_plans=<interval:iterations> <interval:iterations> ... <interval>
+#
+# Example:
+# sched_scan_plans=10:100 20:200 30
+
+# Multi Band Operation (MBO) non-preferred channels
+# A space delimited list of non-preferred channels where each channel is a colon
+# delimited list of values. Reason detail is optional.
+# Format:
+# non_pref_chan=<oper_class>:<chan>:<preference>:<reason>[:reason_detail]
+# Example:
+# non_pref_chan="81:5:10:2:0 81:1:0:2:0 81:9:0:2"
+
+# MBO Cellular Data Capabilities
+# 1 = Cellular data connection available
+# 2 = Cellular data connection not available
+# 3 = Not cellular capable (default)
+#mbo_cell_capa=3
+
 # network block
 #
 # Each network (usually AP's sharing the same SSID) is configured as a separate
@@ -654,6 +716,13 @@
 # an IBSS network with the configured SSID is already present, the frequency of
 # the network will be used instead of this configured value.
 #
+# pbss: Whether to use PBSS. Relevant to IEEE 802.11ad networks only.
+# Used together with mode configuration. When mode is AP, it means to start a
+# PCP instead of a regular AP. When mode is infrastructure it means connect
+# to a PCP instead of AP. P2P_GO and P2P_GROUP_FORMATION modes must use PBSS
+# in IEEE 802.11ad network.
+# For more details, see IEEE Std 802.11ad-2012.
+#
 # 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
@@ -702,8 +771,19 @@
 # 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-NONE = WPA-None for IBSS (deprecated; use proto=RSN key_mgmt=WPA-PSK
+#	instead)
+# FT-PSK = Fast BSS Transition (IEEE 802.11r) with pre-shared key
+# FT-EAP = Fast BSS Transition (IEEE 802.11r) with EAP authentication
 # WPA-PSK-SHA256 = Like WPA-PSK but using stronger SHA256-based algorithms
 # WPA-EAP-SHA256 = Like WPA-EAP but using stronger SHA256-based algorithms
+# SAE = Simultaneous authentication of equals; pre-shared key/password -based
+#	authentication with stronger security than WPA-PSK especially when using
+#	not that strong password
+# FT-SAE = SAE with FT
+# WPA-EAP-SUITE-B = Suite B 128-bit level
+# WPA-EAP-SUITE-B-192 = Suite B 192-bit level
+# OSEN = Hotspot 2.0 Rel 2 online signup connection
 # If not set, this defaults to: WPA-PSK WPA-EAP
 #
 # ieee80211w: whether management frame protection is enabled
@@ -986,10 +1066,17 @@
 #	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)
+# tls_ext_cert_check=0 - No external server certificate validation (default)
+# tls_ext_cert_check=1 - External server certificate validation enabled; this
+#	requires an external program doing validation of server certificate
+#	chain when receiving CTRL-RSP-EXT_CERT_CHECK event from the control
+#	interface and report the result of the validation with
+#	CTRL-RSP_EXT_CERT_CHECK.
 #
 # Following certificate/private key fields are used in inner Phase2
 # authentication when using EAP-TTLS or EAP-PEAP.
@@ -1021,6 +1108,8 @@
 #	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
+#	3 = require valid OCSP stapling response for all not-trusted
+#	    certificates in the server certificate chain
 #
 # openssl_ciphers: OpenSSL specific cipher configuration
 #	This can be used to override the global openssl_ciphers configuration
@@ -1054,6 +1143,9 @@
 # number of authentication servers. Strict EAP conformance mode can be
 # configured by disabling workarounds with eap_workaround=0.
 
+# update_identifier: PPS MO ID
+#	(Hotspot 2.0 PerProviderSubscription/UpdateIdentifier)
+
 # Station inactivity limit
 #
 # If a station does not send anything in ap_max_inactivity seconds, an
@@ -1136,6 +1228,32 @@
 #  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
diff --git a/wpa_supplicant/wpa_supplicant_i.h b/wpa_supplicant/wpa_supplicant_i.h
index 4f63456..c485891 100644
--- a/wpa_supplicant/wpa_supplicant_i.h
+++ b/wpa_supplicant/wpa_supplicant_i.h
@@ -44,6 +44,7 @@
 struct ctrl_iface_priv;
 struct ctrl_iface_global_priv;
 struct wpas_dbus_priv;
+struct wpas_binder_priv;
 
 /**
  * struct wpa_interface - Parameters for wpa_supplicant_add_iface()
@@ -225,9 +226,20 @@
 	 * This can also be %NULL. In such a case, if a P2P Device dedicated
 	 * interfaces is created, the main configuration file will be used.
 	 */
-	const char *conf_p2p_dev;
+	char *conf_p2p_dev;
 #endif /* CONFIG_P2P */
 
+#ifdef CONFIG_MATCH_IFACE
+	/**
+	 * match_ifaces - Interface descriptions to match
+	 */
+	struct wpa_interface *match_ifaces;
+
+	/**
+	 * match_iface_count - Number of defined matching interfaces
+	 */
+	int match_iface_count;
+#endif /* CONFIG_MATCH_IFACE */
 };
 
 struct p2p_srv_bonjour {
@@ -253,6 +265,7 @@
 	struct wpa_params params;
 	struct ctrl_iface_global_priv *ctrl_iface;
 	struct wpas_dbus_priv *dbus;
+	struct wpas_binder_priv *binder;
 	void **drv_priv;
 	size_t drv_count;
 	struct os_time suspend_time;
@@ -278,6 +291,7 @@
 	unsigned int p2p_24ghz_social_channels:1;
 	unsigned int pending_p2ps_group:1;
 	unsigned int pending_group_iface_for_p2ps:1;
+	unsigned int pending_p2ps_group_freq;
 
 #ifdef CONFIG_WIFI_DISPLAY
 	int wifi_display;
@@ -300,10 +314,14 @@
 	char name[16]; /* from driver_ops get_radio_name() or empty if not
 			* available */
 	unsigned int external_scan_running:1;
+	unsigned int num_active_works;
 	struct dl_list ifaces; /* struct wpa_supplicant::radio_list entries */
 	struct dl_list work; /* struct wpa_radio_work::list entries */
 };
 
+#define MAX_ACTIVE_WORKS 2
+
+
 /**
  * struct wpa_radio_work - Radio work item
  */
@@ -316,6 +334,7 @@
 	void *ctx;
 	unsigned int started:1;
 	struct os_reltime time;
+	unsigned int bands;
 };
 
 int radio_add_work(struct wpa_supplicant *wpa_s, unsigned int freq,
@@ -347,6 +366,9 @@
 	unsigned int timeout;
 };
 
+enum wpa_radio_work_band wpas_freq_to_band(int freq);
+unsigned int wpas_get_bands(struct wpa_supplicant *wpa_s, const int *freqs);
+
 /**
  * offchannel_send_action_result - Result of offchannel send Action frame
  */
@@ -414,6 +436,21 @@
 	WPAS_TEST_FAILURE_SCAN_TRIGGER,
 };
 
+struct icon_entry {
+	struct dl_list list;
+	u8 bssid[ETH_ALEN];
+	u8 dialog_token;
+	char *file_name;
+	u8 *image;
+	size_t image_len;
+};
+
+struct wpa_bss_tmp_disallowed {
+	struct dl_list list;
+	u8 bssid[ETH_ALEN];
+	struct os_reltime disallowed_until;
+};
+
 /**
  * struct wpa_supplicant - Internal data for wpa_supplicant interface
  *
@@ -427,12 +464,16 @@
 	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 *p2pdev;
 	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_MATCH_IFACE
+	int matched;
+#endif /* CONFIG_MATCH_IFACE */
 #ifdef CONFIG_CTRL_IFACE_DBUS
 	char *dbus_path;
 #endif /* CONFIG_CTRL_IFACE_DBUS */
@@ -443,6 +484,9 @@
 	char *preq_notify_peer;
 #endif /* CONFIG_AP */
 #endif /* CONFIG_CTRL_IFACE_DBUS_NEW */
+#ifdef CONFIG_CTRL_IFACE_BINDER
+	const void *binder_object_key;
+#endif /* CONFIG_CTRL_IFACE_BINDER */
 	char bridge_ifname[16];
 
 	char *confname;
@@ -455,7 +499,8 @@
 	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 */
+	unsigned int reassoc_same_bss:1; /* reassociating to the same BSS */
+	unsigned int reassoc_same_ess:1; /* reassociating to the same ESS */
 	int disconnected; /* all connections disabled; i.e., do no reassociate
 			   * before this has been cleared */
 	struct wpa_ssid *current_ssid;
@@ -500,9 +545,10 @@
 
 	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;
+	struct sched_scan_plan *sched_scan_plans;
+	size_t sched_scan_plans_num;
 
 	void (*scan_res_handler)(struct wpa_supplicant *wpa_s,
 				 struct wpa_scan_results *scan_res);
@@ -593,6 +639,8 @@
 	} 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;
@@ -611,6 +659,7 @@
 #define MAX_SCAN_ID 16
 	int scan_id[MAX_SCAN_ID];
 	unsigned int scan_id_count;
+	u8 next_scan_bssid[ETH_ALEN];
 
 	struct wpa_ssid_value *ssids_from_scan_req;
 	unsigned int num_ssids_from_scan_req;
@@ -632,6 +681,9 @@
 
 	int max_scan_ssids;
 	int max_sched_scan_ssids;
+	unsigned int max_sched_scan_plans;
+	unsigned int max_sched_scan_plan_interval;
+	unsigned int max_sched_scan_plan_iterations;
 	int sched_scan_supported;
 	unsigned int max_match_sets;
 	unsigned int max_remain_on_chan;
@@ -656,6 +708,7 @@
 	unsigned int reattach:1; /* reassociation to the same BSS requested */
 	unsigned int mac_addr_changed:1;
 	unsigned int added_vif:1;
+	unsigned int wnmsleep_used:1;
 
 	struct os_reltime last_mac_addr_change;
 	int last_mac_addr_style;
@@ -720,7 +773,7 @@
 	int mesh_if_idx;
 	unsigned int mesh_if_created:1;
 	unsigned int mesh_ht_enabled:1;
-	int mesh_auth_block_duration; /* sec */
+	unsigned int mesh_vht_enabled:1;
 #endif /* CONFIG_MESH */
 
 	unsigned int off_channel_freq;
@@ -820,7 +873,7 @@
 	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_join_addr_valid:1;
+	unsigned int p2ps_method_config_any:1;
 	unsigned int p2p_cli_probe:1;
 	int p2p_persistent_go_freq;
 	int p2p_persistent_id;
@@ -842,6 +895,9 @@
 	int *p2p_group_common_freqs;
 	unsigned int p2p_group_common_freqs_num;
 	u8 p2ps_join_addr[ETH_ALEN];
+
+	unsigned int p2p_go_max_oper_chwidth;
+	unsigned int p2p_go_vht_center_freq2;
 #endif /* CONFIG_P2P */
 
 	struct wpa_ssid *bgscan_ssid;
@@ -883,6 +939,7 @@
 	unsigned int fetch_osu_icon_in_progress:1;
 	struct wpa_bss *interworking_gas_bss;
 	unsigned int osu_icon_id;
+	struct dl_list icon_head; /* struct icon_entry */
 	struct osu_provider *osu_prov;
 	size_t osu_prov_count;
 	struct os_reltime osu_icon_fetch_start;
@@ -912,6 +969,9 @@
 	/* WLAN_REASON_* reason codes. Negative if locally generated. */
 	int disconnect_reason;
 
+	/* WLAN_STATUS_* status codes from (Re)Association Response frame. */
+	u16 assoc_status_code;
+
 	struct ext_password_data *ext_pw;
 
 	struct wpabuf *last_gas_resp, *prev_gas_resp;
@@ -967,6 +1027,7 @@
 	struct l2_packet_data *l2_test;
 	unsigned int extra_roc_dur;
 	enum wpa_supplicant_test_failure test_failure;
+	unsigned int p2p_go_csa_on_inv:1;
 #endif /* CONFIG_TESTING_OPTIONS */
 
 	struct wmm_ac_assoc_data *wmm_ac_assoc_info;
@@ -977,6 +1038,31 @@
 	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 */
+
+#ifdef CONFIG_MBO
+	/* Multiband operation non-preferred channel */
+	struct wpa_mbo_non_pref_channel {
+		enum mbo_non_pref_chan_reason reason;
+		u8 oper_class;
+		u8 chan;
+		u8 reason_detail;
+		u8 preference;
+	} *non_pref_chan;
+	size_t non_pref_chan_num;
+	u8 mbo_wnm_token;
+#endif /* CONFIG_MBO */
+
+	/*
+	 * This should be under CONFIG_MBO, but it is left out to allow using
+	 * the bss_temp_disallowed list for other purposes as well.
+	 */
+	struct dl_list bss_tmp_disallowed;
 };
 
 
@@ -1042,6 +1128,8 @@
 
 void wpa_show_license(void);
 
+struct wpa_interface * wpa_supplicant_match_iface(struct wpa_global *global,
+						  const char *ifname);
 struct wpa_supplicant * wpa_supplicant_add_iface(struct wpa_global *global,
 						 struct wpa_interface *iface,
 						 struct wpa_supplicant *parent);
@@ -1089,6 +1177,22 @@
 					      const u8 *frame, size_t len,
 					      int rssi);
 
+
+/* MBO functions */
+int wpas_mbo_ie(struct wpa_supplicant *wpa_s, u8 *buf, size_t len);
+const u8 * wpas_mbo_get_bss_attr(struct wpa_bss *bss, enum mbo_attr_id attr);
+int wpas_mbo_update_non_pref_chan(struct wpa_supplicant *wpa_s,
+				  const char *non_pref_chan);
+void wpas_mbo_scan_ie(struct wpa_supplicant *wpa_s, struct wpabuf *ie);
+int wpas_mbo_supp_op_class_ie(struct wpa_supplicant *wpa_s, int freq, u8 *pos,
+			      size_t len);
+void wpas_mbo_ie_trans_req(struct wpa_supplicant *wpa_s, const u8 *ie,
+			   size_t len);
+size_t wpas_mbo_ie_bss_trans_reject(struct wpa_supplicant *wpa_s, u8 *pos,
+				    size_t len,
+				    enum mbo_transition_reject_reason reason);
+void wpas_mbo_update_cell_capa(struct wpa_supplicant *wpa_s, u8 mbo_cell_capa);
+
 /**
  * wpa_supplicant_ctrl_iface_ctrl_rsp_handle - Handle a control response
  * @wpa_s: Pointer to wpa_supplicant data
@@ -1149,4 +1253,34 @@
 			   int *freq_array, unsigned int len);
 
 void wpas_network_reenabled(void *eloop_ctx, void *timeout_ctx);
+
+void wpas_vendor_elem_update(struct wpa_supplicant *wpa_s);
+struct wpa_supplicant * wpas_vendor_elem(struct wpa_supplicant *wpa_s,
+					 enum wpa_vendor_elem_frame frame);
+int wpas_vendor_elem_remove(struct wpa_supplicant *wpa_s, int frame,
+			    const u8 *elem, size_t len);
+
+#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 */
+
+int wpas_sched_scan_plans_set(struct wpa_supplicant *wpa_s, const char *cmd);
+
+struct hostapd_hw_modes * get_mode(struct hostapd_hw_modes *modes,
+				   u16 num_modes, enum hostapd_hw_mode mode);
+
+void wpa_bss_tmp_disallow(struct wpa_supplicant *wpa_s, const u8 *bssid,
+			  unsigned int sec);
+int wpa_is_bss_tmp_disallowed(struct wpa_supplicant *wpa_s, const u8 *bssid);
+
+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);
+
 #endif /* WPA_SUPPLICANT_I_H */
diff --git a/wpa_supplicant/wpas_glue.c b/wpa_supplicant/wpas_glue.c
index 29c22ba..f84c8b9 100644
--- a/wpa_supplicant/wpas_glue.c
+++ b/wpa_supplicant/wpas_glue.c
@@ -739,6 +739,8 @@
 		return WPA_CTRL_REQ_SIM;
 	else if (os_strcmp(field, "PSK_PASSPHRASE") == 0)
 		return WPA_CTRL_REQ_PSK_PASSPHRASE;
+	else if (os_strcmp(field, "EXT_CERT_CHECK") == 0)
+		return WPA_CTRL_REQ_EXT_CERT_CHECK;
 	return WPA_CTRL_REQ_UNKNOWN;
 }
 
@@ -782,6 +784,10 @@
 		*txt = "PSK or passphrase";
 		ret = "PSK_PASSPHRASE";
 		break;
+	case WPA_CTRL_REQ_EXT_CERT_CHECK:
+		*txt = "External server certificate validation";
+		ret = "EXT_CERT_CHECK";
+		break;
 	default:
 		break;
 	}
@@ -837,6 +843,8 @@
 	if (ssid == NULL)
 		return;
 
+	if (field == WPA_CTRL_REQ_EXT_CERT_CHECK)
+		ssid->eap.pending_ext_cert_check = PENDING_CHECK;
 	wpas_notify_network_request(wpa_s, ssid, field, default_txt);
 
 	field_name = wpa_supplicant_ctrl_req_to_string(field, default_txt,
@@ -1013,7 +1021,6 @@
 
 	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,
@@ -1028,6 +1035,7 @@
 	else
 		return 0;
 }
+#endif /* CONFIG_NO_WPA */
 
 
 int wpa_supplicant_init_wpa(struct wpa_supplicant *wpa_s)
@@ -1124,6 +1132,7 @@
 			}
 		}
 #endif /* CONFIG_P2P */
+		conf.wpa_rsc_relaxation = wpa_s->conf->wpa_rsc_relaxation;
 	}
 	wpa_sm_set_config(wpa_s->wpa, ssid ? &conf : NULL);
 }
diff --git a/wpa_supplicant/wps_supplicant.c b/wpa_supplicant/wps_supplicant.c
index 2db7914..0860eb4 100644
--- a/wpa_supplicant/wps_supplicant.c
+++ b/wpa_supplicant/wps_supplicant.c
@@ -583,8 +583,8 @@
 		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
+	if (wpa_s->p2pdev && wpa_s->p2pdev != wpa_s) {
+		wpa_msg(wpa_s->p2pdev, MSG_INFO, WPS_EVENT_M2D
 			"dev_password_id=%d config_error=%d",
 			m2d->dev_password_id, m2d->config_error);
 	}
@@ -617,8 +617,8 @@
 			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
+		if (wpa_s->p2pdev && wpa_s->p2pdev != wpa_s)
+			wpa_msg(wpa_s->p2pdev, MSG_INFO, WPS_EVENT_FAIL
 				"msg=%d config_error=%d reason=%d (%s)",
 				fail->msg, fail->config_error,
 				fail->error_indication,
@@ -627,8 +627,8 @@
 		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
+		if (wpa_s->p2pdev && wpa_s->p2pdev != wpa_s)
+			wpa_msg(wpa_s->p2pdev, MSG_INFO, WPS_EVENT_FAIL
 				"msg=%d config_error=%d",
 				fail->msg, fail->config_error);
 	}
@@ -683,6 +683,13 @@
 }
 
 
+int wpas_wps_reenable_networks_pending(struct wpa_supplicant *wpa_s)
+{
+	return eloop_is_timeout_registered(wpas_wps_reenable_networks_cb,
+					   wpa_s, NULL);
+}
+
+
 static void wpa_supplicant_wps_event_success(struct wpa_supplicant *wpa_s)
 {
 	wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_SUCCESS);
@@ -955,8 +962,20 @@
 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);
 }
 
@@ -1130,6 +1149,10 @@
 			ssid->ssid_len = wpa_s->go_params->ssid_len;
 			os_memcpy(ssid->ssid, wpa_s->go_params->ssid,
 				  ssid->ssid_len);
+			if (wpa_s->go_params->freq > 56160) {
+				/* P2P in 60 GHz uses PBSS */
+				ssid->pbss = 1;
+			}
 			wpa_hexdump_ascii(MSG_DEBUG, "WPS: Use specific AP "
 					  "SSID", ssid->ssid, ssid->ssid_len);
 		}
@@ -1197,6 +1220,10 @@
 			ssid->ssid_len = wpa_s->go_params->ssid_len;
 			os_memcpy(ssid->ssid, wpa_s->go_params->ssid,
 				  ssid->ssid_len);
+			if (wpa_s->go_params->freq > 56160) {
+				/* P2P in 60 GHz uses PBSS */
+				ssid->pbss = 1;
+			}
 			wpa_hexdump_ascii(MSG_DEBUG, "WPS: Use specific AP "
 					  "SSID", ssid->ssid, ssid->ssid_len);
 		}
@@ -1209,7 +1236,10 @@
 		os_snprintf(val, sizeof(val), "\"dev_pw_id=%u%s\"",
 			    dev_pw_id, hash);
 	} else {
-		rpin = wps_generate_pin();
+		if (wps_generate_pin(&rpin) < 0) {
+			wpa_printf(MSG_DEBUG, "WPS: Could not generate PIN");
+			return -1;
+		}
 		os_snprintf(val, sizeof(val), "\"pin=%08d dev_pw_id=%u%s\"",
 			    rpin, dev_pw_id, hash);
 	}
@@ -1236,6 +1266,22 @@
 }
 
 
+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)
 {
diff --git a/wpa_supplicant/wps_supplicant.h b/wpa_supplicant/wps_supplicant.h
index 683bd50..c8fe47e 100644
--- a/wpa_supplicant/wps_supplicant.h
+++ b/wpa_supplicant/wps_supplicant.h
@@ -33,6 +33,7 @@
 		       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);
@@ -84,6 +85,7 @@
 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);
+int wpas_wps_reenable_networks_pending(struct wpa_supplicant *wpa_s);
 
 #else /* CONFIG_WPS */
 
@@ -146,6 +148,12 @@
 {
 }
 
+static inline int
+wpas_wps_reenable_networks_pending(struct wpa_supplicant *wpa_s)
+{
+	return 0;
+}
+
 #endif /* CONFIG_WPS */
 
 #endif /* WPS_SUPPLICANT_H */