Guard against return value already being null am: 9c42c0b am: e380dbe am: a013472 am: 9744b79
am: 62c35f6

* commit '62c35f65f3effb640f5f800a5ce36d1069b356cb':
  Guard against return value already being null
diff --git a/CONTRIBUTIONS b/CONTRIBUTIONS
index d20a556..ca09bae 100644
--- a/CONTRIBUTIONS
+++ b/CONTRIBUTIONS
@@ -112,7 +112,7 @@
 
 Modified BSD license (no advertisement clause):
 
-Copyright (c) 2002-2014, Jouni Malinen <j@w1.fi> and contributors
+Copyright (c) 2002-2015, Jouni Malinen <j@w1.fi> and contributors
 All Rights Reserved.
 
 Redistribution and use in source and binary forms, with or without
diff --git a/COPYING b/COPYING
index 8a98582..5962e2f 100644
--- a/COPYING
+++ b/COPYING
@@ -1,7 +1,7 @@
 wpa_supplicant and hostapd
 --------------------------
 
-Copyright (c) 2002-2012, Jouni Malinen <j@w1.fi> and contributors
+Copyright (c) 2002-2015, Jouni Malinen <j@w1.fi> and contributors
 All Rights Reserved.
 
 
diff --git a/README b/README
index 8de14a6..07d1d25 100644
--- a/README
+++ b/README
@@ -1,7 +1,7 @@
 wpa_supplicant and hostapd
 --------------------------
 
-Copyright (c) 2002-2014, Jouni Malinen <j@w1.fi> and contributors
+Copyright (c) 2002-2015, Jouni Malinen <j@w1.fi> and contributors
 All Rights Reserved.
 
 These programs are licensed under the BSD license (the one with
diff --git a/hostapd/Android.mk b/hostapd/Android.mk
index a0e0e5d..52d4cfe 100644
--- a/hostapd/Android.mk
+++ b/hostapd/Android.mk
@@ -29,8 +29,9 @@
 
 # Set Android extended P2P functionality
 L_CFLAGS += -DANDROID_P2P
+
 ifeq ($(BOARD_HOSTAPD_PRIVATE_LIB),)
-L_CFLAGS += -DANDROID_P2P_STUB
+L_CFLAGS += -DANDROID_LIB_STUB
 endif
 
 # Use Android specific directory for control interface sockets
@@ -94,6 +95,7 @@
 OBJS += src/ap/pmksa_cache_auth.c
 OBJS += src/ap/ieee802_11_shared.c
 OBJS += src/ap/beacon.c
+OBJS += src/ap/bss_load.c
 OBJS_d =
 OBJS_p =
 LIBS =
@@ -132,6 +134,7 @@
 
 OBJS += src/common/ieee802_11_common.c
 OBJS += src/common/wpa_common.c
+OBJS += src/common/hw_features_common.c
 
 OBJS += src/eapol_auth/eapol_auth_sm.c
 
@@ -178,8 +181,6 @@
 OBJS += src/ap/ctrl_iface_ap.c
 endif
 
-OBJS += src/crypto/md5.c
-
 L_CFLAGS += -DCONFIG_CTRL_IFACE -DCONFIG_CTRL_IFACE_UNIX
 
 ifdef CONFIG_IAPP
@@ -199,6 +200,22 @@
 
 ifdef CONFIG_HS20
 NEED_AES_OMAC1=y
+CONFIG_PROXYARP=y
+endif
+
+ifdef CONFIG_PROXYARP
+CONFIG_L2_PACKET=y
+endif
+
+ifdef CONFIG_SUITEB
+L_CFLAGS += -DCONFIG_SUITEB
+NEED_SHA256=y
+NEED_AES_OMAC1=y
+endif
+
+ifdef CONFIG_SUITEB192
+L_CFLAGS += -DCONFIG_SUITEB192
+NEED_SHA384=y
 endif
 
 ifdef CONFIG_IEEE80211W
@@ -541,33 +558,6 @@
 CONFIG_INTERNAL_DH_GROUP5=y
 endif
 
-ifeq ($(CONFIG_TLS), schannel)
-ifdef TLS_FUNCS
-OBJS += src/crypto/tls_schannel.c
-endif
-OBJS += src/crypto/crypto_cryptoapi.c
-OBJS_p += src/crypto/crypto_cryptoapi.c
-CONFIG_INTERNAL_SHA256=y
-CONFIG_INTERNAL_RC4=y
-CONFIG_INTERNAL_DH_GROUP5=y
-endif
-
-ifeq ($(CONFIG_TLS), nss)
-ifdef TLS_FUNCS
-OBJS += src/crypto/tls_nss.c
-LIBS += -lssl3
-endif
-OBJS += src/crypto/crypto_nss.c
-ifdef NEED_FIPS186_2_PRF
-OBJS += src/crypto/fips_prf_internal.c
-OBJS += src/crypto/sha1-internal.c
-endif
-LIBS += -lnss3
-LIBS_h += -lnss3
-CONFIG_INTERNAL_MD4=y
-CONFIG_INTERNAL_DH_GROUP5=y
-endif
-
 ifeq ($(CONFIG_TLS), internal)
 ifndef CONFIG_CRYPTO
 CONFIG_CRYPTO=internal
@@ -674,7 +664,9 @@
 AESOBJS += src/crypto/aes-internal.c src/crypto/aes-internal-enc.c
 endif
 
+ifneq ($(CONFIG_TLS), openssl)
 AESOBJS += src/crypto/aes-wrap.c
+endif
 ifdef NEED_AES_EAX
 AESOBJS += src/crypto/aes-eax.c
 NEED_AES_CTR=y
@@ -689,13 +681,17 @@
 AESOBJS += src/crypto/aes-omac1.c
 endif
 ifdef NEED_AES_UNWRAP
+ifneq ($(CONFIG_TLS), openssl)
 NEED_AES_DEC=y
 AESOBJS += src/crypto/aes-unwrap.c
 endif
+endif
 ifdef NEED_AES_CBC
 NEED_AES_DEC=y
+ifneq ($(CONFIG_TLS), openssl)
 AESOBJS += src/crypto/aes-cbc.c
 endif
+endif
 ifdef NEED_AES_DEC
 ifdef CONFIG_INTERNAL_AES
 AESOBJS += src/crypto/aes-internal-dec.c
@@ -732,6 +728,10 @@
 OBJS += $(SHA1OBJS)
 endif
 
+ifneq ($(CONFIG_TLS), openssl)
+OBJS += src/crypto/md5.c
+endif
+
 ifdef NEED_MD5
 ifdef CONFIG_INTERNAL_MD5
 OBJS += src/crypto/md5-internal.c
@@ -770,6 +770,9 @@
 OBJS += src/crypto/sha256-tlsprf.c
 endif
 endif
+ifdef NEED_SHA384
+L_CFLAGS += -DCONFIG_SHA384
+endif
 
 ifdef NEED_DH_GROUPS
 OBJS += src/crypto/dh_groups.c
@@ -794,8 +797,10 @@
 HOBJS += src/crypto/random.c
 HOBJS += src/utils/eloop.c
 HOBJS += $(SHA1OBJS)
+ifneq ($(CONFIG_TLS), openssl)
 HOBJS += src/crypto/md5.c
 endif
+endif
 
 ifdef CONFIG_RADIUS_SERVER
 L_CFLAGS += -DRADIUS_SERVER
@@ -853,6 +858,15 @@
 OBJS += src/ap/gas_serv.c
 endif
 
+ifdef CONFIG_PROXYARP
+L_CFLAGS += -DCONFIG_PROXYARP
+OBJS += src/ap/x_snoop.c
+OBJS += src/ap/dhcp_snoop.c
+ifdef CONFIG_IPV6
+OBJS += src/ap/ndisc_snoop.c
+endif
+endif
+
 OBJS += src/drivers/driver_common.c
 
 ifdef CONFIG_ACS
@@ -879,6 +893,7 @@
 
 OBJS_c = hostapd_cli.c src/common/wpa_ctrl.c src/utils/os_$(CONFIG_OS).c
 OBJS_c += src/utils/eloop.c
+OBJS_c += src/utils/common.c
 ifdef CONFIG_WPA_TRACE
 OBJS_c += src/utils/trace.c
 endif
diff --git a/hostapd/ChangeLog b/hostapd/ChangeLog
index f0e4604..e6f8c6a 100644
--- a/hostapd/ChangeLog
+++ b/hostapd/ChangeLog
@@ -1,5 +1,49 @@
 ChangeLog for hostapd
 
+2015-03-15 - v2.4
+	* allow OpenSSL cipher configuration to be set for internal EAP server
+	  (openssl_ciphers parameter)
+	* fixed number of small issues based on hwsim test case failures and
+	  static analyzer reports
+	* fixed Accounting-Request to not include duplicated Acct-Session-Id
+	* add support for Acct-Multi-Session-Id in RADIUS Accounting messages
+	* add support for PMKSA caching with SAE
+	* add support for generating BSS Load element (bss_load_update_period)
+	* fixed channel switch from VHT to HT
+	* add INTERFACE-ENABLED and INTERFACE-DISABLED ctrl_iface events
+	* add support for learning STA IPv4/IPv6 addresses and configuring
+	  ProxyARP support
+	* dropped support for the madwifi driver interface
+	* add support for Suite B (128-bit and 192-bit level) key management and
+	  cipher suites
+	* fixed a regression with driver=wired
+	* extend EAPOL-Key msg 1/4 retry workaround for changing SNonce
+	* add BSS_TM_REQ ctrl_iface command to send BSS Transition Management
+	  Request frames and BSS-TM-RESP event to indicate response to such
+	  frame
+	* add support for EAP Re-Authentication Protocol (ERP)
+	* fixed AP IE in EAPOL-Key 3/4 when both WPA and FT was enabled
+	* fixed a regression in HT 20/40 coex Action frame parsing
+	* set stdout to be line-buffered
+	* add support for vendor specific VHT extension to enable 256 QAM rates
+	  (VHT-MCS 8 and 9) on 2.4 GHz band
+	* RADIUS DAS:
+	  - extend Disconnect-Request processing to allow matching of multiple
+	    sessions
+	  - support Acct-Multi-Session-Id as an identifier
+	  - allow PMKSA cache entry to be removed without association
+	* expire hostapd STA entry if kernel does not have a matching entry
+	* allow chanlist to be used to specify a subset of channels for ACS
+	* improve ACS behavior on 2.4 GHz band and allow channel bias to be
+	  configured with acs_chan_bias parameter
+	* do not reply to a Probe Request frame that includes DSS Parameter Set
+	  element in which the channel does not match the current operating
+	  channel
+	* add UPDATE_BEACON ctrl_iface command; this can be used to force Beacon
+	  frame contents to be updated and to start beaconing on an interface
+	  that used start_disabled=1
+	* fixed some RADIUS server failover cases
+
 2014-10-09 - v2.3
 	* fixed number of minor issues identified in static analyzer warnings
 	* fixed DFS and channel switch operation for multi-BSS cases
diff --git a/hostapd/Makefile b/hostapd/Makefile
index ac6373e..d4fd36e 100644
--- a/hostapd/Makefile
+++ b/hostapd/Makefile
@@ -6,9 +6,12 @@
 CFLAGS = -MMD -O2 -Wall -g
 endif
 
+CFLAGS += $(EXTRA_CFLAGS)
 CFLAGS += -I$(abspath ../src)
 CFLAGS += -I$(abspath ../src/utils)
 
+export BINDIR ?= /usr/local/bin/
+
 # Uncomment following line and set the path to your kernel tree include
 # directory if your C library does not include all header files.
 # CFLAGS += -DUSE_KERNEL_HEADERS -I/usr/src/linux/include
@@ -59,6 +62,7 @@
 OBJS += ../src/ap/pmksa_cache_auth.o
 OBJS += ../src/ap/ieee802_11_shared.o
 OBJS += ../src/ap/beacon.o
+OBJS += ../src/ap/bss_load.o
 
 OBJS_c = hostapd_cli.o ../src/common/wpa_ctrl.o ../src/utils/os_$(CONFIG_OS).o
 
@@ -104,6 +108,7 @@
 endif
 
 OBJS += ../src/utils/common.o
+OBJS_c += ../src/utils/common.o
 OBJS += ../src/utils/wpa_debug.o
 OBJS_c += ../src/utils/wpa_debug.o
 OBJS += ../src/utils/wpabuf.o
@@ -112,6 +117,7 @@
 
 OBJS += ../src/common/ieee802_11_common.o
 OBJS += ../src/common/wpa_common.o
+OBJS += ../src/common/hw_features_common.o
 
 OBJS += ../src/eapol_auth/eapol_auth_sm.o
 
@@ -166,8 +172,6 @@
 OBJS += ../src/ap/ctrl_iface_ap.o
 endif
 
-OBJS += ../src/crypto/md5.o
-
 CFLAGS += -DCONFIG_CTRL_IFACE -DCONFIG_CTRL_IFACE_UNIX
 
 ifdef CONFIG_IAPP
@@ -187,6 +191,22 @@
 
 ifdef CONFIG_HS20
 NEED_AES_OMAC1=y
+CONFIG_PROXYARP=y
+endif
+
+ifdef CONFIG_PROXYARP
+CONFIG_L2_PACKET=y
+endif
+
+ifdef CONFIG_SUITEB
+CFLAGS += -DCONFIG_SUITEB
+NEED_SHA256=y
+NEED_AES_OMAC1=y
+endif
+
+ifdef CONFIG_SUITEB192
+CFLAGS += -DCONFIG_SUITEB192
+NEED_SHA384=y
 endif
 
 ifdef CONFIG_IEEE80211W
@@ -208,6 +228,7 @@
 OBJS += ../src/common/sae.o
 NEED_ECC=y
 NEED_DH_GROUPS=y
+NEED_AP_MLME=y
 endif
 
 ifdef CONFIG_WNM
@@ -246,6 +267,12 @@
 endif
 
 
+ifdef CONFIG_ERP
+CFLAGS += -DCONFIG_ERP
+NEED_SHA256=y
+NEED_HMAC_SHA256_KDF=y
+endif
+
 ifdef CONFIG_EAP_MD5
 CFLAGS += -DEAP_SERVER_MD5
 OBJS += ../src/eap_server/eap_server_md5.o
@@ -528,33 +555,6 @@
 CONFIG_INTERNAL_DH_GROUP5=y
 endif
 
-ifeq ($(CONFIG_TLS), schannel)
-ifdef TLS_FUNCS
-OBJS += ../src/crypto/tls_schannel.o
-endif
-OBJS += ../src/crypto/crypto_cryptoapi.o
-OBJS_p += ../src/crypto/crypto_cryptoapi.o
-CONFIG_INTERNAL_SHA256=y
-CONFIG_INTERNAL_RC4=y
-CONFIG_INTERNAL_DH_GROUP5=y
-endif
-
-ifeq ($(CONFIG_TLS), nss)
-ifdef TLS_FUNCS
-OBJS += ../src/crypto/tls_nss.o
-LIBS += -lssl3
-endif
-OBJS += ../src/crypto/crypto_nss.o
-ifdef NEED_FIPS186_2_PRF
-OBJS += ../src/crypto/fips_prf_internal.o
-SHA1OBJS += ../src/crypto/sha1-internal.o
-endif
-LIBS += -lnss3
-LIBS_h += -lnss3
-CONFIG_INTERNAL_MD4=y
-CONFIG_INTERNAL_DH_GROUP5=y
-endif
-
 ifeq ($(CONFIG_TLS), internal)
 ifndef CONFIG_CRYPTO
 CONFIG_CRYPTO=internal
@@ -661,7 +661,9 @@
 AESOBJS += ../src/crypto/aes-internal.o ../src/crypto/aes-internal-enc.o
 endif
 
+ifneq ($(CONFIG_TLS), openssl)
 AESOBJS += ../src/crypto/aes-wrap.o
+endif
 ifdef NEED_AES_EAX
 AESOBJS += ../src/crypto/aes-eax.o
 NEED_AES_CTR=y
@@ -676,13 +678,17 @@
 AESOBJS += ../src/crypto/aes-omac1.o
 endif
 ifdef NEED_AES_UNWRAP
+ifneq ($(CONFIG_TLS), openssl)
 NEED_AES_DEC=y
 AESOBJS += ../src/crypto/aes-unwrap.o
 endif
+endif
 ifdef NEED_AES_CBC
 NEED_AES_DEC=y
+ifneq ($(CONFIG_TLS), openssl)
 AESOBJS += ../src/crypto/aes-cbc.o
 endif
+endif
 ifdef NEED_AES_DEC
 ifdef CONFIG_INTERNAL_AES
 AESOBJS += ../src/crypto/aes-internal-dec.o
@@ -718,6 +724,10 @@
 OBJS += $(SHA1OBJS)
 endif
 
+ifneq ($(CONFIG_TLS), openssl)
+OBJS += ../src/crypto/md5.o
+endif
+
 ifdef NEED_MD5
 ifdef CONFIG_INTERNAL_MD5
 OBJS += ../src/crypto/md5-internal.o
@@ -755,6 +765,12 @@
 ifdef NEED_TLS_PRF_SHA256
 OBJS += ../src/crypto/sha256-tlsprf.o
 endif
+ifdef NEED_HMAC_SHA256_KDF
+OBJS += ../src/crypto/sha256-kdf.o
+endif
+endif
+ifdef NEED_SHA384
+CFLAGS += -DCONFIG_SHA384
 endif
 
 ifdef NEED_DH_GROUPS
@@ -780,8 +796,10 @@
 HOBJS += ../src/crypto/random.o
 HOBJS += ../src/utils/eloop.o
 HOBJS += $(SHA1OBJS)
+ifneq ($(CONFIG_TLS), openssl)
 HOBJS += ../src/crypto/md5.o
 endif
+endif
 
 ifdef CONFIG_RADIUS_SERVER
 CFLAGS += -DRADIUS_SERVER
@@ -839,6 +857,15 @@
 OBJS += ../src/ap/gas_serv.o
 endif
 
+ifdef CONFIG_PROXYARP
+CFLAGS += -DCONFIG_PROXYARP
+OBJS += ../src/ap/x_snoop.o
+OBJS += ../src/ap/dhcp_snoop.o
+ifdef CONFIG_IPV6
+OBJS += ../src/ap/ndisc_snoop.o
+endif
+endif
+
 OBJS += ../src/drivers/driver_common.o
 
 ifdef CONFIG_WPA_CLI_EDIT
@@ -881,6 +908,10 @@
 Q=
 E=true
 endif
+ifeq ($(QUIET), 1)
+Q=@
+E=true
+endif
 
 ifdef CONFIG_CODE_COVERAGE
 %.o: %.c
@@ -901,9 +932,10 @@
 		exit 1; \
 	fi
 
-install: all
-	mkdir -p $(DESTDIR)/usr/local/bin
-	for i in $(ALL); do cp -f $$i $(DESTDIR)/usr/local/bin/$$i; done
+$(DESTDIR)$(BINDIR)/%: %
+	install -D $(<) $(@)
+
+install: $(addprefix $(DESTDIR)$(BINDIR)/,$(ALL))
 
 ../src/drivers/build.hostapd:
 	@if [ -f ../src/drivers/build.wpa_supplicant ]; then \
@@ -924,7 +956,8 @@
 	$(Q)$(CC) $(LDFLAGS) -o hostapd_cli $(OBJS_c) $(LIBS_c)
 	@$(E) "  LD " $@
 
-NOBJS = nt_password_hash.o ../src/crypto/ms_funcs.o $(SHA1OBJS) ../src/crypto/md5.o
+NOBJS = nt_password_hash.o ../src/crypto/ms_funcs.o $(SHA1OBJS)
+NOBJS += ../src/utils/common.o
 ifdef NEED_RC4
 ifdef CONFIG_INTERNAL_RC4
 NOBJS += ../src/crypto/rc4.o
diff --git a/hostapd/README b/hostapd/README
index 50868ee..366b199 100644
--- a/hostapd/README
+++ b/hostapd/README
@@ -2,7 +2,7 @@
 	  Authenticator and RADIUS authentication server
 ================================================================
 
-Copyright (c) 2002-2014, Jouni Malinen <j@w1.fi> and contributors
+Copyright (c) 2002-2015, Jouni Malinen <j@w1.fi> and contributors
 All Rights Reserved.
 
 This program is licensed under the BSD license (the one with
@@ -74,12 +74,6 @@
 	Please note that station firmware version needs to be 1.7.0 or newer
 	to work in WPA mode.
 
-	madwifi driver for cards based on Atheros chip set (ar521x)
-	(http://sourceforge.net/projects/madwifi/)
-	Please note that you will need to add the correct path for
-	madwifi driver root directory in .config (see defconfig file for
-	an example: CFLAGS += -I<path>)
-
 	mac80211-based drivers that support AP mode (with driver=nl80211).
 	This includes drivers for Atheros (ath9k) and Broadcom (b43)
 	chipsets.
diff --git a/hostapd/README-WPS b/hostapd/README-WPS
index bb7d35f..d5f713a 100644
--- a/hostapd/README-WPS
+++ b/hostapd/README-WPS
@@ -58,10 +58,9 @@
 
 WPS is an optional component that needs to be enabled in hostapd build
 configuration (.config). Here is an example configuration that
-includes WPS support and uses madwifi driver interface:
+includes WPS support and uses nl80211 driver interface:
 
-CONFIG_DRIVER_MADWIFI=y
-CFLAGS += -I/usr/src/madwifi-0.9.3
+CONFIG_DRIVER_NL80211=y
 CONFIG_WPS=y
 CONFIG_WPS_UPNP=y
 
@@ -74,8 +73,8 @@
 (hostapd.conf) that enables WPS:
 
 # Configure the driver and network interface
-driver=madwifi
-interface=ath0
+driver=nl80211
+interface=wlan0
 
 # WPA2-Personal configuration for the AP
 ssid=wps-test
diff --git a/hostapd/android.config b/hostapd/android.config
index ad83308..938aa54 100644
--- a/hostapd/android.config
+++ b/hostapd/android.config
@@ -15,10 +15,6 @@
 # Driver interface for wired authenticator
 #CONFIG_DRIVER_WIRED=y
 
-# Driver interface for madwifi driver
-#CONFIG_DRIVER_MADWIFI=y
-#CFLAGS += -I../../madwifi # change to the madwifi source directory
-
 # Driver interface for drivers using the nl80211 kernel interface
 #CONFIG_DRIVER_NL80211=y
 # driver_nl80211.c requires a rather new libnl (version 1.1) which may not be
@@ -132,7 +128,7 @@
 #CONFIG_IEEE80211R=y
 
 # Use the hostapd's IEEE 802.11 authentication (ACL), but without
-# the IEEE 802.11 Management capability (e.g., madwifi or FreeBSD/net80211)
+# the IEEE 802.11 Management capability (e.g., FreeBSD/net80211)
 #CONFIG_DRIVER_RADIUS_ACL=y
 
 # IEEE 802.11n (High Throughput) support
diff --git a/hostapd/config_file.c b/hostapd/config_file.c
index 44de826..49f8320 100644
--- a/hostapd/config_file.c
+++ b/hostapd/config_file.c
@@ -1,6 +1,6 @@
 /*
  * hostapd / Configuration file parser
- * Copyright (c) 2003-2014, 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.
@@ -216,15 +216,21 @@
 	FILE *f;
 	char buf[512], *pos, *start, *pos2;
 	int line = 0, ret = 0, num_methods;
-	struct hostapd_eap_user *user = NULL, *tail = NULL;
+	struct hostapd_eap_user *user = NULL, *tail = NULL, *new_user = NULL;
 
 	if (!fname)
 		return 0;
 
 	if (os_strncmp(fname, "sqlite:", 7) == 0) {
+#ifdef CONFIG_SQLITE
 		os_free(conf->eap_user_sqlite);
 		conf->eap_user_sqlite = os_strdup(fname + 7);
 		return 0;
+#else /* CONFIG_SQLITE */
+		wpa_printf(MSG_ERROR,
+			   "EAP user file in SQLite DB, but CONFIG_SQLITE was not enabled in the build.");
+		return -1;
+#endif /* CONFIG_SQLITE */
 	}
 
 	f = fopen(fname, "r");
@@ -494,7 +500,7 @@
 
 	done:
 		if (tail == NULL) {
-			tail = conf->eap_user = user;
+			tail = new_user = user;
 		} else {
 			tail->next = user;
 			tail = user;
@@ -510,6 +516,18 @@
 
 	fclose(f);
 
+	if (ret == 0) {
+		user = conf->eap_user;
+		while (user) {
+			struct hostapd_eap_user *prev;
+
+			prev = user;
+			user = user->next;
+			hostapd_config_free_eap_user(prev);
+		}
+		conf->eap_user = new_user;
+	}
+
 	return ret;
 }
 #endif /* EAP_SERVER */
@@ -680,6 +698,14 @@
 		else if (os_strcmp(start, "FT-SAE") == 0)
 			val |= WPA_KEY_MGMT_FT_SAE;
 #endif /* CONFIG_SAE */
+#ifdef CONFIG_SUITEB
+		else if (os_strcmp(start, "WPA-EAP-SUITE-B") == 0)
+			val |= WPA_KEY_MGMT_IEEE8021X_SUITE_B;
+#endif /* CONFIG_SUITEB */
+#ifdef CONFIG_SUITEB192
+		else if (os_strcmp(start, "WPA-EAP-SUITE-B-192") == 0)
+			val |= WPA_KEY_MGMT_IEEE8021X_SUITE_B_192;
+#endif /* CONFIG_SUITEB192 */
 		else {
 			wpa_printf(MSG_ERROR, "Line %d: invalid key_mgmt '%s'",
 				   line, start);
@@ -755,6 +781,24 @@
 }
 
 
+static int hostapd_parse_chanlist(struct hostapd_config *conf, char *val)
+{
+	char *pos;
+
+	/* for backwards compatibility, translate ' ' in conf str to ',' */
+	pos = val;
+	while (pos) {
+		pos = os_strchr(pos, ' ');
+		if (pos)
+			*pos++ = ',';
+	}
+	if (freq_range_list_parse(&conf->acs_ch_list, val))
+		return -1;
+
+	return 0;
+}
+
+
 static int hostapd_parse_intlist(int **int_list, char *val)
 {
 	int *list;
@@ -855,7 +899,9 @@
 static int valid_cw(int cw)
 {
 	return (cw == 1 || cw == 3 || cw == 7 || cw == 15 || cw == 31 ||
-		cw == 63 || cw == 127 || cw == 255 || cw == 511 || cw == 1023);
+		cw == 63 || cw == 127 || cw == 255 || cw == 511 || cw == 1023 ||
+		cw == 2047 || cw == 4095 || cw == 8191 || cw == 16383 ||
+		cw == 32767);
 }
 
 
@@ -1119,8 +1165,6 @@
 		conf->vht_capab |= (1 << VHT_CAP_SOUNDING_DIMENSION_OFFSET);
 	if (os_strstr(capab, "[MU-BEAMFORMER]"))
 		conf->vht_capab |= VHT_CAP_MU_BEAMFORMER_CAPABLE;
-	if (os_strstr(capab, "[MU-BEAMFORMEE]"))
-		conf->vht_capab |= VHT_CAP_MU_BEAMFORMEE_CAPABLE;
 	if (os_strstr(capab, "[VHT-TXOP-PS]"))
 		conf->vht_capab |= VHT_CAP_VHT_TXOP_PS;
 	if (os_strstr(capab, "[HTC-VHT]"))
@@ -1679,7 +1723,7 @@
 	char *str;
 
 	str = wpa_config_parse_string(pos, &slen);
-	if (str == NULL || slen < 1 || slen > HOSTAPD_MAX_SSID_LEN) {
+	if (str == NULL || slen < 1 || slen > SSID_MAX_LEN) {
 		wpa_printf(MSG_ERROR, "Line %d: Invalid SSID '%s'", line, pos);
 		os_free(str);
 		return -1;
@@ -1836,6 +1880,48 @@
 #endif /* CONFIG_WPS_NFC */
 
 
+#ifdef CONFIG_ACS
+static int hostapd_config_parse_acs_chan_bias(struct hostapd_config *conf,
+					      char *pos)
+{
+	struct acs_bias *bias = NULL, *tmp;
+	unsigned int num = 0;
+	char *end;
+
+	while (*pos) {
+		tmp = os_realloc_array(bias, num + 1, sizeof(*bias));
+		if (!tmp)
+			goto fail;
+		bias = tmp;
+
+		bias[num].channel = atoi(pos);
+		if (bias[num].channel <= 0)
+			goto fail;
+		pos = os_strchr(pos, ':');
+		if (!pos)
+			goto fail;
+		pos++;
+		bias[num].bias = strtod(pos, &end);
+		if (end == pos || bias[num].bias < 0.0)
+			goto fail;
+		pos = end;
+		if (*pos != ' ' && *pos != '\0')
+			goto fail;
+		num++;
+	}
+
+	os_free(conf->acs_chan_bias);
+	conf->acs_chan_bias = bias;
+	conf->num_acs_chan_bias = num;
+
+	return 0;
+fail:
+	os_free(bias);
+	return -1;
+}
+#endif /* CONFIG_ACS */
+
+
 static int hostapd_config_fill(struct hostapd_config *conf,
 			       struct hostapd_bss_config *bss,
 			       char *buf, char *pos, int line)
@@ -1865,6 +1951,9 @@
 				   line, pos);
 			return 1;
 		}
+	} else if (os_strcmp(buf, "driver_params") == 0) {
+		os_free(conf->driver_params);
+		conf->driver_params = os_strdup(pos);
 	} else if (os_strcmp(buf, "debug") == 0) {
 		wpa_printf(MSG_DEBUG, "Line %d: DEPRECATED: 'debug' configuration variable is not used anymore",
 			   line);
@@ -1881,7 +1970,7 @@
 			   line);
 	} else if (os_strcmp(buf, "ssid") == 0) {
 		bss->ssid.ssid_len = os_strlen(pos);
-		if (bss->ssid.ssid_len > HOSTAPD_MAX_SSID_LEN ||
+		if (bss->ssid.ssid_len > SSID_MAX_LEN ||
 		    bss->ssid.ssid_len < 1) {
 			wpa_printf(MSG_ERROR, "Line %d: invalid SSID '%s'",
 				   line, pos);
@@ -1892,7 +1981,7 @@
 	} else if (os_strcmp(buf, "ssid2") == 0) {
 		size_t slen;
 		char *str = wpa_config_parse_string(pos, &slen);
-		if (str == NULL || slen < 1 || slen > HOSTAPD_MAX_SSID_LEN) {
+		if (str == NULL || slen < 1 || slen > SSID_MAX_LEN) {
 			wpa_printf(MSG_ERROR, "Line %d: invalid SSID '%s'",
 				   line, pos);
 			os_free(str);
@@ -1984,6 +2073,9 @@
 	} else if (os_strcmp(buf, "dh_file") == 0) {
 		os_free(bss->dh_file);
 		bss->dh_file = os_strdup(pos);
+	} else if (os_strcmp(buf, "openssl_ciphers") == 0) {
+		os_free(bss->openssl_ciphers);
+		bss->openssl_ciphers = os_strdup(pos);
 	} else if (os_strcmp(buf, "fragment_size") == 0) {
 		bss->fragment_size = atoi(pos);
 #ifdef EAP_SERVER_FAST
@@ -2044,6 +2136,8 @@
 	} else if (os_strcmp(buf, "pwd_group") == 0) {
 		bss->pwd_group = atoi(pos);
 #endif /* EAP_SERVER_PWD */
+	} else if (os_strcmp(buf, "eap_server_erp") == 0) {
+		bss->eap_server_erp = atoi(pos);
 #endif /* EAP_SERVER */
 	} else if (os_strcmp(buf, "eap_message") == 0) {
 		char *term;
@@ -2063,6 +2157,11 @@
 				   (term - bss->eap_req_id_text) - 1);
 			bss->eap_req_id_text_len--;
 		}
+	} else if (os_strcmp(buf, "erp_send_reauth_start") == 0) {
+		bss->erp_send_reauth_start = atoi(pos);
+	} else if (os_strcmp(buf, "erp_domain") == 0) {
+		os_free(bss->erp_domain);
+		bss->erp_domain = os_strdup(pos);
 	} else if (os_strcmp(buf, "wep_key_len_broadcast") == 0) {
 		bss->default_wep_key_len = atoi(pos);
 		if (bss->default_wep_key_len > 13) {
@@ -2114,6 +2213,14 @@
 		os_free(bss->nas_identifier);
 		bss->nas_identifier = os_strdup(pos);
 #ifndef CONFIG_NO_RADIUS
+	} else if (os_strcmp(buf, "radius_client_addr") == 0) {
+		if (hostapd_parse_ip_addr(pos, &bss->radius->client_addr)) {
+			wpa_printf(MSG_ERROR,
+				   "Line %d: invalid IP address '%s'",
+				   line, pos);
+			return 1;
+		}
+		bss->radius->force_client_addr = 1;
 	} else if (os_strcmp(buf, "auth_server_addr") == 0) {
 		if (hostapd_config_read_radius_addr(
 			    &bss->radius->auth_servers,
@@ -2125,6 +2232,15 @@
 			return 1;
 		}
 	} else if (bss->radius->auth_server &&
+		   os_strcmp(buf, "auth_server_addr_replace") == 0) {
+		if (hostapd_parse_ip_addr(pos,
+					  &bss->radius->auth_server->addr)) {
+			wpa_printf(MSG_ERROR,
+				   "Line %d: invalid IP address '%s'",
+				   line, pos);
+			return 1;
+		}
+	} else if (bss->radius->auth_server &&
 		   os_strcmp(buf, "auth_server_port") == 0) {
 		bss->radius->auth_server->port = atoi(pos);
 	} else if (bss->radius->auth_server &&
@@ -2150,6 +2266,15 @@
 			return 1;
 		}
 	} else if (bss->radius->acct_server &&
+		   os_strcmp(buf, "acct_server_addr_replace") == 0) {
+		if (hostapd_parse_ip_addr(pos,
+					  &bss->radius->acct_server->addr)) {
+			wpa_printf(MSG_ERROR,
+				   "Line %d: invalid IP address '%s'",
+				   line, pos);
+			return 1;
+		}
+	} else if (bss->radius->acct_server &&
 		   os_strcmp(buf, "acct_server_port") == 0) {
 		bss->radius->acct_server->port = atoi(pos);
 	} else if (bss->radius->acct_server &&
@@ -2250,12 +2375,11 @@
 		os_free(bss->ssid.wpa_passphrase);
 		bss->ssid.wpa_passphrase = os_strdup(pos);
 		if (bss->ssid.wpa_passphrase) {
-			os_free(bss->ssid.wpa_psk);
-			bss->ssid.wpa_psk = NULL;
+			hostapd_config_clear_wpa_psk(&bss->ssid.wpa_psk);
 			bss->ssid.wpa_passphrase_set = 1;
 		}
 	} else if (os_strcmp(buf, "wpa_psk") == 0) {
-		os_free(bss->ssid.wpa_psk);
+		hostapd_config_clear_wpa_psk(&bss->ssid.wpa_psk);
 		bss->ssid.wpa_psk = os_zalloc(sizeof(struct hostapd_wpa_psk));
 		if (bss->ssid.wpa_psk == NULL)
 			return 1;
@@ -2263,8 +2387,7 @@
 		    pos[PMK_LEN * 2] != '\0') {
 			wpa_printf(MSG_ERROR, "Line %d: Invalid PSK '%s'.",
 				   line, pos);
-			os_free(bss->ssid.wpa_psk);
-			bss->ssid.wpa_psk = NULL;
+			hostapd_config_clear_wpa_psk(&bss->ssid.wpa_psk);
 			return 1;
 		}
 		bss->ssid.wpa_psk->group = 1;
@@ -2405,9 +2528,6 @@
 	} else if (os_strcmp(buf, "radius_server_ipv6") == 0) {
 		bss->radius_server_ipv6 = atoi(pos);
 #endif /* RADIUS_SERVER */
-	} else if (os_strcmp(buf, "test_socket") == 0) {
-		os_free(bss->test_socket);
-		bss->test_socket = os_strdup(pos);
 	} else if (os_strcmp(buf, "use_pae_group_addr") == 0) {
 		bss->use_pae_group_addr = atoi(pos);
 	} else if (os_strcmp(buf, "hw_mode") == 0) {
@@ -2419,13 +2539,17 @@
 			conf->hw_mode = HOSTAPD_MODE_IEEE80211G;
 		else if (os_strcmp(pos, "ad") == 0)
 			conf->hw_mode = HOSTAPD_MODE_IEEE80211AD;
+		else if (os_strcmp(pos, "any") == 0)
+			conf->hw_mode = HOSTAPD_MODE_IEEE80211ANY;
 		else {
 			wpa_printf(MSG_ERROR, "Line %d: unknown hw_mode '%s'",
 				   line, pos);
 			return 1;
 		}
 	} else if (os_strcmp(buf, "wps_rf_bands") == 0) {
-		if (os_strcmp(pos, "a") == 0)
+		if (os_strcmp(pos, "ad") == 0)
+			bss->wps_rf_bands = WPS_RF_60GHZ;
+		else if (os_strcmp(pos, "a") == 0)
 			bss->wps_rf_bands = WPS_RF_50GHZ;
 		else if (os_strcmp(pos, "g") == 0 ||
 			 os_strcmp(pos, "b") == 0)
@@ -2446,12 +2570,15 @@
 				   line);
 			return 1;
 #else /* CONFIG_ACS */
+			conf->acs = 1;
 			conf->channel = 0;
 #endif /* CONFIG_ACS */
-		} else
+		} else {
 			conf->channel = atoi(pos);
+			conf->acs = conf->channel == 0;
+		}
 	} else if (os_strcmp(buf, "chanlist") == 0) {
-		if (hostapd_parse_intlist(&conf->chanlist, pos)) {
+		if (hostapd_parse_chanlist(conf, pos)) {
 			wpa_printf(MSG_ERROR, "Line %d: invalid channel list",
 				   line);
 			return 1;
@@ -2478,6 +2605,12 @@
 			return 1;
 		}
 		conf->acs_num_scans = val;
+	} else if (os_strcmp(buf, "acs_chan_bias") == 0) {
+		if (hostapd_config_parse_acs_chan_bias(conf, pos)) {
+			wpa_printf(MSG_ERROR, "Line %d: invalid acs_chan_bias",
+				   line);
+			return -1;
+		}
 #endif /* CONFIG_ACS */
 	} else if (os_strcmp(buf, "dtim_period") == 0) {
 		bss->dtim_period = atoi(pos);
@@ -2486,6 +2619,15 @@
 				   line, bss->dtim_period);
 			return 1;
 		}
+	} else if (os_strcmp(buf, "bss_load_update_period") == 0) {
+		bss->bss_load_update_period = atoi(pos);
+		if (bss->bss_load_update_period < 0 ||
+		    bss->bss_load_update_period > 100) {
+			wpa_printf(MSG_ERROR,
+				   "Line %d: invalid bss_load_update_period %d",
+				   line, bss->bss_load_update_period);
+			return 1;
+		}
 	} else if (os_strcmp(buf, "rts_threshold") == 0) {
 		conf->rts_threshold = atoi(pos);
 		if (conf->rts_threshold < 0 || conf->rts_threshold > 2347) {
@@ -2669,6 +2811,8 @@
 		conf->vht_oper_centr_freq_seg0_idx = atoi(pos);
 	} else if (os_strcmp(buf, "vht_oper_centr_freq_seg1_idx") == 0) {
 		conf->vht_oper_centr_freq_seg1_idx = atoi(pos);
+	} else if (os_strcmp(buf, "vendor_vht") == 0) {
+		bss->vendor_vht = atoi(pos);
 #endif /* CONFIG_IEEE80211AC */
 	} else if (os_strcmp(buf, "max_listen_interval") == 0) {
 		bss->max_listen_interval = atoi(pos);
@@ -2697,7 +2841,7 @@
 		os_free(bss->wps_pin_requests);
 		bss->wps_pin_requests = os_strdup(pos);
 	} else if (os_strcmp(buf, "device_name") == 0) {
-		if (os_strlen(pos) > 32) {
+		if (os_strlen(pos) > WPS_DEV_NAME_MAX_LEN) {
 			wpa_printf(MSG_ERROR, "Line %d: Too long "
 				   "device_name", line);
 			return 1;
@@ -2996,6 +3140,10 @@
 		bss->hs20 = atoi(pos);
 	} else if (os_strcmp(buf, "disable_dgaf") == 0) {
 		bss->disable_dgaf = atoi(pos);
+	} else if (os_strcmp(buf, "proxy_arp") == 0) {
+		bss->proxy_arp = atoi(pos);
+	} else if (os_strcmp(buf, "na_mcast_to_ucast") == 0) {
+		bss->na_mcast_to_ucast = atoi(pos);
 	} else if (os_strcmp(buf, "osen") == 0) {
 		bss->osen = atoi(pos);
 	} else if (os_strcmp(buf, "anqp_domain_id") == 0) {
@@ -3106,6 +3254,8 @@
 		pos++;
 		WPA_PUT_LE16(&bss->bss_load_test[3], atoi(pos));
 		bss->bss_load_test_set = 1;
+	} else if (os_strcmp(buf, "radio_measurements") == 0) {
+		bss->radio_measurements = atoi(pos);
 #endif /* CONFIG_TESTING_OPTIONS */
 	} else if (os_strcmp(buf, "vendor_elements") == 0) {
 		struct wpabuf *elems;
diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
index 591c395..8ab2941 100644
--- a/hostapd/ctrl_iface.c
+++ b/hostapd/ctrl_iface.c
@@ -1,6 +1,6 @@
 /*
  * hostapd / UNIX domain socket -based control interface
- * Copyright (c) 2004-2014, 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.
@@ -10,6 +10,11 @@
 
 #ifndef CONFIG_NATIVE_WINDOWS
 
+#ifdef CONFIG_TESTING_OPTIONS
+#include <net/ethernet.h>
+#include <netinet/ip.h>
+#endif /* CONFIG_TESTING_OPTIONS */
+
 #include <sys/un.h>
 #include <sys/stat.h>
 #include <stddef.h>
@@ -18,9 +23,11 @@
 #include "utils/eloop.h"
 #include "common/version.h"
 #include "common/ieee802_11_defs.h"
+#include "crypto/tls.h"
 #include "drivers/driver.h"
 #include "radius/radius_client.h"
 #include "radius/radius_server.h"
+#include "l2_packet/l2_packet.h"
 #include "ap/hostapd.h"
 #include "ap/ap_config.h"
 #include "ap/ieee802_1x.h"
@@ -33,6 +40,7 @@
 #include "ap/hs20.h"
 #include "ap/wnm_ap.h"
 #include "ap/wpa_auth.h"
+#include "ap/beacon.h"
 #include "wps/wps_defs.h"
 #include "wps/wps.h"
 #include "config_file.h"
@@ -240,14 +248,14 @@
 		if (!wps_pin_valid(pin_val)) {
 			wpa_printf(MSG_DEBUG, "WPS: Invalid checksum digit");
 			ret = os_snprintf(buf, buflen, "FAIL-CHECKSUM\n");
-			if (ret < 0 || (size_t) ret >= buflen)
+			if (os_snprintf_error(buflen, ret))
 				return -1;
 			return ret;
 		}
 	}
 
 	ret = os_snprintf(buf, buflen, "%s", pin);
-	if (ret < 0 || (size_t) ret >= buflen)
+	if (os_snprintf_error(buflen, ret))
 		return -1;
 
 	return ret;
@@ -578,7 +586,7 @@
 	ret = os_snprintf(pos, end - pos, "PBC Status: %s\n",
 			  pbc_status_str(hapd->wps_stats.pbc_status));
 
-	if (ret < 0 || ret >= end - pos)
+	if (os_snprintf_error(end - pos, ret))
 		return pos - buf;
 	pos += ret;
 
@@ -588,7 +596,7 @@
 			   (hapd->wps_stats.status == WPS_STATUS_FAILURE ?
 			    "Failed" : "None")));
 
-	if (ret < 0 || ret >= end - pos)
+	if (os_snprintf_error(end - pos, ret))
 		return pos - buf;
 	pos += ret;
 
@@ -599,7 +607,7 @@
 				  "Failure Reason: %s\n",
 				  wps_ei_str(hapd->wps_stats.failure_reason));
 
-		if (ret < 0 || ret >= end - pos)
+		if (os_snprintf_error(end - pos, ret))
 			return pos - buf;
 		pos += ret;
 	}
@@ -608,7 +616,7 @@
 		ret = os_snprintf(pos, end - pos, "Peer Address: " MACSTR "\n",
 				  MAC2STR(hapd->wps_stats.peer_addr));
 
-		if (ret < 0 || ret >= end - pos)
+		if (os_snprintf_error(end - pos, ret))
 			return pos - buf;
 		pos += ret;
 	}
@@ -857,6 +865,193 @@
 	return wnm_send_ess_disassoc_imminent(hapd, sta, url, disassoc_timer);
 }
 
+
+static int hostapd_ctrl_iface_bss_tm_req(struct hostapd_data *hapd,
+					 const char *cmd)
+{
+	u8 addr[ETH_ALEN];
+	const char *pos, *end;
+	int disassoc_timer = 0;
+	struct sta_info *sta;
+	u8 req_mode = 0, valid_int = 0x01;
+	u8 bss_term_dur[12];
+	char *url = NULL;
+	int ret;
+	u8 nei_rep[1000];
+	u8 *nei_pos = nei_rep;
+
+	if (hwaddr_aton(cmd, addr)) {
+		wpa_printf(MSG_DEBUG, "Invalid STA MAC address");
+		return -1;
+	}
+
+	sta = ap_get_sta(hapd, addr);
+	if (sta == NULL) {
+		wpa_printf(MSG_DEBUG, "Station " MACSTR
+			   " not found for BSS TM Request message",
+			   MAC2STR(addr));
+		return -1;
+	}
+
+	pos = os_strstr(cmd, " disassoc_timer=");
+	if (pos) {
+		pos += 16;
+		disassoc_timer = atoi(pos);
+		if (disassoc_timer < 0 || disassoc_timer > 65535) {
+			wpa_printf(MSG_DEBUG, "Invalid disassoc_timer");
+			return -1;
+		}
+	}
+
+	pos = os_strstr(cmd, " valid_int=");
+	if (pos) {
+		pos += 11;
+		valid_int = atoi(pos);
+	}
+
+	pos = os_strstr(cmd, " bss_term=");
+	if (pos) {
+		pos += 10;
+		req_mode |= WNM_BSS_TM_REQ_BSS_TERMINATION_INCLUDED;
+		/* TODO: TSF configurable/learnable */
+		bss_term_dur[0] = 4; /* Subelement ID */
+		bss_term_dur[1] = 10; /* Length */
+		os_memset(bss_term_dur, 2, 8);
+		end = os_strchr(pos, ',');
+		if (end == NULL) {
+			wpa_printf(MSG_DEBUG, "Invalid bss_term data");
+			return -1;
+		}
+		end++;
+		WPA_PUT_LE16(&bss_term_dur[10], atoi(end));
+	}
+
+
+	/*
+	 * BSS Transition Candidate List Entries - Neighbor Report elements
+	 * neighbor=<BSSID>,<BSSID Information>,<Operating Class>,
+	 * <Channel Number>,<PHY Type>[,<hexdump of Optional Subelements>]
+	 */
+	pos = cmd;
+	while (pos) {
+		u8 *nei_start;
+		long int val;
+		char *endptr, *tmp;
+
+		pos = os_strstr(pos, " neighbor=");
+		if (!pos)
+			break;
+		if (nei_pos + 15 > nei_rep + sizeof(nei_rep)) {
+			wpa_printf(MSG_DEBUG,
+				   "Not enough room for additional neighbor");
+			return -1;
+		}
+		pos += 10;
+
+		nei_start = nei_pos;
+		*nei_pos++ = WLAN_EID_NEIGHBOR_REPORT;
+		nei_pos++; /* length to be filled in */
+
+		if (hwaddr_aton(pos, nei_pos)) {
+			wpa_printf(MSG_DEBUG, "Invalid BSSID");
+			return -1;
+		}
+		nei_pos += ETH_ALEN;
+		pos += 17;
+		if (*pos != ',') {
+			wpa_printf(MSG_DEBUG, "Missing BSSID Information");
+			return -1;
+		}
+		pos++;
+
+		val = strtol(pos, &endptr, 0);
+		WPA_PUT_LE32(nei_pos, val);
+		nei_pos += 4;
+		if (*endptr != ',') {
+			wpa_printf(MSG_DEBUG, "Missing Operating Class");
+			return -1;
+		}
+		pos = endptr + 1;
+
+		*nei_pos++ = atoi(pos); /* Operating Class */
+		pos = os_strchr(pos, ',');
+		if (pos == NULL) {
+			wpa_printf(MSG_DEBUG, "Missing Channel Number");
+			return -1;
+		}
+		pos++;
+
+		*nei_pos++ = atoi(pos); /* Channel Number */
+		pos = os_strchr(pos, ',');
+		if (pos == NULL) {
+			wpa_printf(MSG_DEBUG, "Missing PHY Type");
+			return -1;
+		}
+		pos++;
+
+		*nei_pos++ = atoi(pos); /* PHY Type */
+		end = os_strchr(pos, ' ');
+		tmp = os_strchr(pos, ',');
+		if (tmp && (!end || tmp < end)) {
+			/* Optional Subelements (hexdump) */
+			size_t len;
+
+			pos = tmp + 1;
+			end = os_strchr(pos, ' ');
+			if (end)
+				len = end - pos;
+			else
+				len = os_strlen(pos);
+			if (nei_pos + len / 2 > nei_rep + sizeof(nei_rep)) {
+				wpa_printf(MSG_DEBUG,
+					   "Not enough room for neighbor subelements");
+				return -1;
+			}
+			if (len & 0x01 ||
+			    hexstr2bin(pos, nei_pos, len / 2) < 0) {
+				wpa_printf(MSG_DEBUG,
+					   "Invalid neighbor subelement info");
+				return -1;
+			}
+			nei_pos += len / 2;
+			pos = end;
+		}
+
+		nei_start[1] = nei_pos - nei_start - 2;
+	}
+
+	pos = os_strstr(cmd, " url=");
+	if (pos) {
+		size_t len;
+		pos += 5;
+		end = os_strchr(pos, ' ');
+		if (end)
+			len = end - pos;
+		else
+			len = os_strlen(pos);
+		url = os_malloc(len + 1);
+		if (url == NULL)
+			return -1;
+		os_memcpy(url, pos, len);
+		url[len] = '\0';
+		req_mode |= WNM_BSS_TM_REQ_ESS_DISASSOC_IMMINENT;
+	}
+
+	if (os_strstr(cmd, " pref=1"))
+		req_mode |= WNM_BSS_TM_REQ_PREF_CAND_LIST_INCLUDED;
+	if (os_strstr(cmd, " abridged=1"))
+		req_mode |= WNM_BSS_TM_REQ_ABRIDGED;
+	if (os_strstr(cmd, " disassoc_imminent=1"))
+		req_mode |= WNM_BSS_TM_REQ_DISASSOC_IMMINENT;
+
+	ret = wnm_send_bss_tm_req(hapd, sta, req_mode, disassoc_timer,
+				  valid_int, bss_term_dur, url,
+				  nei_pos > nei_rep ? nei_rep : NULL,
+				  nei_pos - nei_rep);
+	os_free(url);
+	return ret;
+}
+
 #endif /* CONFIG_WNM */
 
 
@@ -874,7 +1069,7 @@
 			  MAC2STR(hapd->own_addr),
 			  wpa_ssid_txt(hapd->conf->ssid.ssid,
 				       hapd->conf->ssid.ssid_len));
-	if (ret < 0 || ret >= end - pos)
+	if (os_snprintf_error(end - pos, ret))
 		return pos - buf;
 	pos += ret;
 
@@ -883,7 +1078,7 @@
 			  hapd->conf->wps_state == 0 ? "disabled" :
 			  (hapd->conf->wps_state == 1 ? "not configured" :
 			   "configured"));
-	if (ret < 0 || ret >= end - pos)
+	if (os_snprintf_error(end - pos, ret))
 		return pos - buf;
 	pos += ret;
 
@@ -891,7 +1086,7 @@
 	    hapd->conf->ssid.wpa_passphrase) {
 		ret = os_snprintf(pos, end - pos, "passphrase=%s\n",
 				  hapd->conf->ssid.wpa_passphrase);
-		if (ret < 0 || ret >= end - pos)
+		if (os_snprintf_error(end - pos, ret))
 			return pos - buf;
 		pos += ret;
 	}
@@ -903,7 +1098,7 @@
 		wpa_snprintf_hex(hex, sizeof(hex),
 				 hapd->conf->ssid.wpa_psk->psk, PMK_LEN);
 		ret = os_snprintf(pos, end - pos, "psk=%s\n", hex);
-		if (ret < 0 || ret >= end - pos)
+		if (os_snprintf_error(end - pos, ret))
 			return pos - buf;
 		pos += ret;
 	}
@@ -911,39 +1106,39 @@
 
 	if (hapd->conf->wpa && hapd->conf->wpa_key_mgmt) {
 		ret = os_snprintf(pos, end - pos, "key_mgmt=");
-		if (ret < 0 || ret >= end - pos)
+		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 (ret < 0 || ret >= end - pos)
+			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 (ret < 0 || ret >= end - pos)
+			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 (ret < 0 || ret >= end - pos)
+			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 (ret < 0 || ret >= end - pos)
+			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 (ret < 0 || ret >= end - pos)
+			if (os_snprintf_error(end - pos, ret))
 				return pos - buf;
 			pos += ret;
 		}
@@ -952,13 +1147,13 @@
 #ifdef CONFIG_IEEE80211W
 		if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_PSK_SHA256) {
 			ret = os_snprintf(pos, end - pos, "WPA-PSK-SHA256 ");
-			if (ret < 0 || ret >= end - pos)
+			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 (ret < 0 || ret >= end - pos)
+			if (os_snprintf_error(end - pos, ret))
 				return pos - buf;
 			pos += ret;
 		}
@@ -966,14 +1161,28 @@
 #ifdef CONFIG_SAE
 		if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_SAE) {
 			ret = os_snprintf(pos, end - pos, "SAE ");
-			if (ret < 0 || ret >= end - pos)
+			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;
+		}
 
 		ret = os_snprintf(pos, end - pos, "\n");
-		if (ret < 0 || ret >= end - pos)
+		if (os_snprintf_error(end - pos, ret))
 			return pos - buf;
 		pos += ret;
 	}
@@ -981,14 +1190,14 @@
 	if (hapd->conf->wpa) {
 		ret = os_snprintf(pos, end - pos, "group_cipher=%s\n",
 				  wpa_cipher_txt(hapd->conf->wpa_group));
-		if (ret < 0 || ret >= end - pos)
+		if (os_snprintf_error(end - pos, ret))
 			return pos - buf;
 		pos += ret;
 	}
 
 	if ((hapd->conf->wpa & WPA_PROTO_RSN) && hapd->conf->rsn_pairwise) {
 		ret = os_snprintf(pos, end - pos, "rsn_pairwise_cipher=");
-		if (ret < 0 || ret >= end - pos)
+		if (os_snprintf_error(end - pos, ret))
 			return pos - buf;
 		pos += ret;
 
@@ -999,14 +1208,14 @@
 		pos += ret;
 
 		ret = os_snprintf(pos, end - pos, "\n");
-		if (ret < 0 || ret >= end - pos)
+		if (os_snprintf_error(end - pos, ret))
 			return pos - buf;
 		pos += ret;
 	}
 
 	if ((hapd->conf->wpa & WPA_PROTO_WPA) && hapd->conf->wpa_pairwise) {
 		ret = os_snprintf(pos, end - pos, "wpa_pairwise_cipher=");
-		if (ret < 0 || ret >= end - pos)
+		if (os_snprintf_error(end - pos, ret))
 			return pos - buf;
 		pos += ret;
 
@@ -1017,7 +1226,7 @@
 		pos += ret;
 
 		ret = os_snprintf(pos, end - pos, "\n");
-		if (ret < 0 || ret >= end - pos)
+		if (os_snprintf_error(end - pos, ret))
 			return pos - buf;
 		pos += ret;
 	}
@@ -1074,6 +1283,8 @@
 #ifdef CONFIG_TESTING_OPTIONS
 	} else if (os_strcasecmp(cmd, "ext_mgmt_frame_handling") == 0) {
 		hapd->ext_mgmt_frame_handling = atoi(value);
+	} else if (os_strcasecmp(cmd, "ext_eapol_frame_io") == 0) {
+		hapd->ext_eapol_frame_io = atoi(value);
 #endif /* CONFIG_TESTING_OPTIONS */
 	} else {
 		struct sta_info *sta;
@@ -1122,7 +1333,12 @@
 
 	if (os_strcmp(cmd, "version") == 0) {
 		res = os_snprintf(buf, buflen, "%s", VERSION_STR);
-		if (res < 0 || (unsigned int) res >= buflen)
+		if (os_snprintf_error(buflen, res))
+			return -1;
+		return res;
+	} else if (os_strcmp(cmd, "tls_library") == 0) {
+		res = tls_get_library_version(buf, buflen);
+		if (os_snprintf_error(buflen, res))
 			return -1;
 		return res;
 	}
@@ -1249,6 +1465,287 @@
 	return res;
 }
 
+
+static int hostapd_ctrl_iface_eapol_rx(struct hostapd_data *hapd, char *cmd)
+{
+	char *pos;
+	u8 src[ETH_ALEN], *buf;
+	int used;
+	size_t len;
+
+	wpa_printf(MSG_DEBUG, "External EAPOL RX: %s", cmd);
+
+	pos = cmd;
+	used = hwaddr_aton2(pos, src);
+	if (used < 0)
+		return -1;
+	pos += used;
+	while (*pos == ' ')
+		pos++;
+
+	len = os_strlen(pos);
+	if (len & 1)
+		return -1;
+	len /= 2;
+
+	buf = os_malloc(len);
+	if (buf == NULL)
+		return -1;
+
+	if (hexstr2bin(pos, buf, len) < 0) {
+		os_free(buf);
+		return -1;
+	}
+
+	ieee802_1x_receive(hapd, src, buf, len);
+	os_free(buf);
+
+	return 0;
+}
+
+
+static u16 ipv4_hdr_checksum(const void *buf, size_t len)
+{
+	size_t i;
+	u32 sum = 0;
+	const u16 *pos = buf;
+
+	for (i = 0; i < len / 2; i++)
+		sum += *pos++;
+
+	while (sum >> 16)
+		sum = (sum & 0xffff) + (sum >> 16);
+
+	return sum ^ 0xffff;
+}
+
+
+#define HWSIM_PACKETLEN 1500
+#define HWSIM_IP_LEN (HWSIM_PACKETLEN - sizeof(struct ether_header))
+
+void hostapd_data_test_rx(void *ctx, const u8 *src_addr, const u8 *buf,
+			  size_t len)
+{
+	struct hostapd_data *hapd = ctx;
+	const struct ether_header *eth;
+	const struct iphdr *ip;
+	const u8 *pos;
+	unsigned int i;
+
+	if (len != HWSIM_PACKETLEN)
+		return;
+
+	eth = (const struct ether_header *) buf;
+	ip = (const struct iphdr *) (eth + 1);
+	pos = (const u8 *) (ip + 1);
+
+	if (ip->ihl != 5 || ip->version != 4 ||
+	    ntohs(ip->tot_len) != HWSIM_IP_LEN)
+		return;
+
+	for (i = 0; i < HWSIM_IP_LEN - sizeof(*ip); i++) {
+		if (*pos != (u8) i)
+			return;
+		pos++;
+	}
+
+	wpa_msg(hapd->msg_ctx, MSG_INFO, "DATA-TEST-RX " MACSTR " " MACSTR,
+		MAC2STR(eth->ether_dhost), MAC2STR(eth->ether_shost));
+}
+
+
+static int hostapd_ctrl_iface_data_test_config(struct hostapd_data *hapd,
+					       char *cmd)
+{
+	int enabled = atoi(cmd);
+	char *pos;
+	const char *ifname;
+
+	if (!enabled) {
+		if (hapd->l2_test) {
+			l2_packet_deinit(hapd->l2_test);
+			hapd->l2_test = NULL;
+			wpa_dbg(hapd->msg_ctx, MSG_DEBUG,
+				"test data: Disabled");
+		}
+		return 0;
+	}
+
+	if (hapd->l2_test)
+		return 0;
+
+	pos = os_strstr(cmd, " ifname=");
+	if (pos)
+		ifname = pos + 8;
+	else
+		ifname = hapd->conf->iface;
+
+	hapd->l2_test = l2_packet_init(ifname, hapd->own_addr,
+					ETHERTYPE_IP, hostapd_data_test_rx,
+					hapd, 1);
+	if (hapd->l2_test == NULL)
+		return -1;
+
+	wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "test data: Enabled");
+
+	return 0;
+}
+
+
+static int hostapd_ctrl_iface_data_test_tx(struct hostapd_data *hapd, char *cmd)
+{
+	u8 dst[ETH_ALEN], src[ETH_ALEN];
+	char *pos;
+	int used;
+	long int val;
+	u8 tos;
+	u8 buf[HWSIM_PACKETLEN];
+	struct ether_header *eth;
+	struct iphdr *ip;
+	u8 *dpos;
+	unsigned int i;
+
+	if (hapd->l2_test == NULL)
+		return -1;
+
+	/* format: <dst> <src> <tos> */
+
+	pos = cmd;
+	used = hwaddr_aton2(pos, dst);
+	if (used < 0)
+		return -1;
+	pos += used;
+	while (*pos == ' ')
+		pos++;
+	used = hwaddr_aton2(pos, src);
+	if (used < 0)
+		return -1;
+	pos += used;
+
+	val = strtol(pos, NULL, 0);
+	if (val < 0 || val > 0xff)
+		return -1;
+	tos = val;
+
+	eth = (struct ether_header *) buf;
+	os_memcpy(eth->ether_dhost, dst, ETH_ALEN);
+	os_memcpy(eth->ether_shost, src, ETH_ALEN);
+	eth->ether_type = htons(ETHERTYPE_IP);
+	ip = (struct iphdr *) (eth + 1);
+	os_memset(ip, 0, sizeof(*ip));
+	ip->ihl = 5;
+	ip->version = 4;
+	ip->ttl = 64;
+	ip->tos = tos;
+	ip->tot_len = htons(HWSIM_IP_LEN);
+	ip->protocol = 1;
+	ip->saddr = htonl(192 << 24 | 168 << 16 | 1 << 8 | 1);
+	ip->daddr = htonl(192 << 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,
+			   HWSIM_PACKETLEN) < 0)
+		return -1;
+
+	wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "test data: TX dst=" MACSTR
+		" src=" MACSTR " tos=0x%x", MAC2STR(dst), MAC2STR(src), tos);
+
+	return 0;
+}
+
+
+static int hostapd_ctrl_iface_data_test_frame(struct hostapd_data *hapd,
+					      char *cmd)
+{
+	u8 *buf;
+	struct ether_header *eth;
+	struct l2_packet_data *l2 = NULL;
+	size_t len;
+	u16 ethertype;
+	int res = -1;
+	const char *ifname = hapd->conf->iface;
+
+	if (os_strncmp(cmd, "ifname=", 7) == 0) {
+		cmd += 7;
+		ifname = cmd;
+		cmd = os_strchr(cmd, ' ');
+		if (cmd == NULL)
+			return -1;
+		*cmd++ = '\0';
+	}
+
+	len = os_strlen(cmd);
+	if (len & 1 || len < ETH_HLEN * 2)
+		return -1;
+	len /= 2;
+
+	buf = os_malloc(len);
+	if (buf == NULL)
+		return -1;
+
+	if (hexstr2bin(cmd, buf, len) < 0)
+		goto done;
+
+	eth = (struct ether_header *) buf;
+	ethertype = ntohs(eth->ether_type);
+
+	l2 = l2_packet_init(ifname, hapd->own_addr, ethertype,
+			    hostapd_data_test_rx, hapd, 1);
+	if (l2 == NULL)
+		goto done;
+
+	res = l2_packet_send(l2, eth->ether_dhost, ethertype, buf, len);
+	wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "test data: TX frame res=%d", res);
+done:
+	if (l2)
+		l2_packet_deinit(l2);
+	os_free(buf);
+
+	return res < 0 ? -1 : 0;
+}
+
+
+static int hostapd_ctrl_test_alloc_fail(struct hostapd_data *hapd, char *cmd)
+{
+#ifdef WPA_TRACE_BFD
+	extern char wpa_trace_fail_func[256];
+	extern unsigned int wpa_trace_fail_after;
+	char *pos;
+
+	wpa_trace_fail_after = atoi(cmd);
+	pos = os_strchr(cmd, ':');
+	if (pos) {
+		pos++;
+		os_strlcpy(wpa_trace_fail_func, pos,
+			   sizeof(wpa_trace_fail_func));
+	} else {
+		wpa_trace_fail_after = 0;
+	}
+
+	return 0;
+#else /* WPA_TRACE_BFD */
+	return -1;
+#endif /* WPA_TRACE_BFD */
+}
+
+
+static int hostapd_ctrl_get_alloc_fail(struct hostapd_data *hapd,
+				       char *buf, size_t buflen)
+{
+#ifdef WPA_TRACE_BFD
+	extern char wpa_trace_fail_func[256];
+	extern unsigned int wpa_trace_fail_after;
+
+	return os_snprintf(buf, buflen, "%u:%s", wpa_trace_fail_after,
+			   wpa_trace_fail_func);
+#else /* WPA_TRACE_BFD */
+	return -1;
+#endif /* WPA_TRACE_BFD */
+}
+
 #endif /* CONFIG_TESTING_OPTIONS */
 
 
@@ -1366,7 +1863,8 @@
 	res = recvfrom(sock, buf, sizeof(buf) - 1, 0,
 		       (struct sockaddr *) &from, &fromlen);
 	if (res < 0) {
-		perror("recvfrom(ctrl_iface)");
+		wpa_printf(MSG_ERROR, "recvfrom(ctrl_iface): %s",
+			   strerror(errno));
 		return;
 	}
 	buf[res] = '\0';
@@ -1376,8 +1874,11 @@
 
 	reply = os_malloc(reply_size);
 	if (reply == NULL) {
-		sendto(sock, "FAIL\n", 5, 0, (struct sockaddr *) &from,
-		       fromlen);
+		if (sendto(sock, "FAIL\n", 5, 0, (struct sockaddr *) &from,
+			   fromlen) < 0) {
+			wpa_printf(MSG_DEBUG, "CTRL: sendto failed: %s",
+				   strerror(errno));
+		}
 		return;
 	}
 
@@ -1455,6 +1956,9 @@
 	} else if (os_strncmp(buf, "DISASSOCIATE ", 13) == 0) {
 		if (hostapd_ctrl_iface_disassociate(hapd, buf + 13))
 			reply_len = -1;
+	} else if (os_strcmp(buf, "STOP_AP") == 0) {
+		if (hostapd_ctrl_iface_stop_ap(hapd))
+			reply_len = -1;
 #ifdef CONFIG_IEEE80211W
 #ifdef NEED_AP_MLME
 	} else if (os_strncmp(buf, "SA_QUERY ", 9) == 0) {
@@ -1525,6 +2029,9 @@
 	} else if (os_strncmp(buf, "ESS_DISASSOC ", 13) == 0) {
 		if (hostapd_ctrl_iface_ess_disassoc(hapd, buf + 13))
 			reply_len = -1;
+	} else if (os_strncmp(buf, "BSS_TM_REQ ", 11) == 0) {
+		if (hostapd_ctrl_iface_bss_tm_req(hapd, buf + 11))
+			reply_len = -1;
 #endif /* CONFIG_WNM */
 	} else if (os_strcmp(buf, "GET_CONFIG") == 0) {
 		reply_len = hostapd_ctrl_iface_get_config(hapd, reply,
@@ -1544,6 +2051,9 @@
 	} else if (os_strncmp(buf, "DISABLE", 7) == 0) {
 		if (hostapd_ctrl_iface_disable(hapd->iface))
 			reply_len = -1;
+	} else if (os_strcmp(buf, "UPDATE_BEACON") == 0) {
+		if (ieee802_11_set_beacon(hapd))
+			reply_len = -1;
 #ifdef CONFIG_TESTING_OPTIONS
 	} else if (os_strncmp(buf, "RADAR ", 6) == 0) {
 		if (hostapd_ctrl_iface_radar(hapd, buf + 6))
@@ -1551,6 +2061,24 @@
 	} else if (os_strncmp(buf, "MGMT_TX ", 8) == 0) {
 		if (hostapd_ctrl_iface_mgmt_tx(hapd, buf + 8))
 			reply_len = -1;
+	} else if (os_strncmp(buf, "EAPOL_RX ", 9) == 0) {
+		if (hostapd_ctrl_iface_eapol_rx(hapd, buf + 9) < 0)
+			reply_len = -1;
+	} else if (os_strncmp(buf, "DATA_TEST_CONFIG ", 17) == 0) {
+		if (hostapd_ctrl_iface_data_test_config(hapd, buf + 17) < 0)
+			reply_len = -1;
+	} else if (os_strncmp(buf, "DATA_TEST_TX ", 13) == 0) {
+		if (hostapd_ctrl_iface_data_test_tx(hapd, buf + 13) < 0)
+			reply_len = -1;
+	} else if (os_strncmp(buf, "DATA_TEST_FRAME ", 16) == 0) {
+		if (hostapd_ctrl_iface_data_test_frame(hapd, buf + 16) < 0)
+			reply_len = -1;
+	} else if (os_strncmp(buf, "TEST_ALLOC_FAIL ", 16) == 0) {
+		if (hostapd_ctrl_test_alloc_fail(hapd, buf + 16) < 0)
+			reply_len = -1;
+	} else if (os_strcmp(buf, "GET_ALLOC_FAIL") == 0) {
+		reply_len = hostapd_ctrl_get_alloc_fail(hapd, reply,
+							reply_size);
 #endif /* CONFIG_TESTING_OPTIONS */
 	} else if (os_strncmp(buf, "CHAN_SWITCH ", 12) == 0) {
 		if (hostapd_ctrl_iface_chan_switch(hapd->iface, buf + 12))
@@ -1558,7 +2086,11 @@
 	} else if (os_strncmp(buf, "VENDOR ", 7) == 0) {
 		reply_len = hostapd_ctrl_iface_vendor(hapd, buf + 7, reply,
 						      reply_size);
-
+	} else if (os_strcmp(buf, "ERP_FLUSH") == 0) {
+		ieee802_1x_erp_flush(hapd);
+#ifdef RADIUS_SERVER
+		radius_server_erp_flush(hapd->radius_srv);
+#endif /* RADIUS_SERVER */
 	} else {
 		os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
 		reply_len = 16;
@@ -1568,7 +2100,11 @@
 		os_memcpy(reply, "FAIL\n", 5);
 		reply_len = 5;
 	}
-	sendto(sock, reply, reply_len, 0, (struct sockaddr *) &from, fromlen);
+	if (sendto(sock, reply, reply_len, 0, (struct sockaddr *) &from,
+		   fromlen) < 0) {
+		wpa_printf(MSG_DEBUG, "CTRL: sendto failed: %s",
+			   strerror(errno));
+	}
 	os_free(reply);
 }
 
@@ -1594,7 +2130,8 @@
 }
 
 
-static void hostapd_ctrl_iface_msg_cb(void *ctx, int level, int global,
+static void hostapd_ctrl_iface_msg_cb(void *ctx, int level,
+				      enum wpa_msg_type type,
 				      const char *txt, size_t len)
 {
 	struct hostapd_data *hapd = ctx;
@@ -1623,7 +2160,8 @@
 			wpa_printf(MSG_DEBUG, "Using existing control "
 				   "interface directory.");
 		} else {
-			perror("mkdir[ctrl_interface]");
+			wpa_printf(MSG_ERROR, "mkdir[ctrl_interface]: %s",
+				   strerror(errno));
 			goto fail;
 		}
 	}
@@ -1631,7 +2169,8 @@
 	if (hapd->conf->ctrl_interface_gid_set &&
 	    chown(hapd->conf->ctrl_interface, -1,
 		  hapd->conf->ctrl_interface_gid) < 0) {
-		perror("chown[ctrl_interface]");
+		wpa_printf(MSG_ERROR, "chown[ctrl_interface]: %s",
+			   strerror(errno));
 		return -1;
 	}
 
@@ -1639,7 +2178,8 @@
 	    hapd->iface->interfaces->ctrl_iface_group &&
 	    chown(hapd->conf->ctrl_interface, -1,
 		  hapd->iface->interfaces->ctrl_iface_group) < 0) {
-		perror("chown[ctrl_interface]");
+		wpa_printf(MSG_ERROR, "chown[ctrl_interface]: %s",
+			   strerror(errno));
 		return -1;
 	}
 
@@ -1664,7 +2204,7 @@
 
 	s = socket(PF_UNIX, SOCK_DGRAM, 0);
 	if (s < 0) {
-		perror("socket(PF_UNIX)");
+		wpa_printf(MSG_ERROR, "socket(PF_UNIX): %s", strerror(errno));
 		goto fail;
 	}
 
@@ -1685,15 +2225,16 @@
 				   " allow connections - assuming it was left"
 				   "over from forced program termination");
 			if (unlink(fname) < 0) {
-				perror("unlink[ctrl_iface]");
-				wpa_printf(MSG_ERROR, "Could not unlink "
-					   "existing ctrl_iface socket '%s'",
-					   fname);
+				wpa_printf(MSG_ERROR,
+					   "Could not unlink existing ctrl_iface socket '%s': %s",
+					   fname, strerror(errno));
 				goto fail;
 			}
 			if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) <
 			    0) {
-				perror("hostapd-ctrl-iface: bind(PF_UNIX)");
+				wpa_printf(MSG_ERROR,
+					   "hostapd-ctrl-iface: bind(PF_UNIX): %s",
+					   strerror(errno));
 				goto fail;
 			}
 			wpa_printf(MSG_DEBUG, "Successfully replaced leftover "
@@ -1711,26 +2252,32 @@
 
 	if (hapd->conf->ctrl_interface_gid_set &&
 	    chown(fname, -1, hapd->conf->ctrl_interface_gid) < 0) {
-		perror("chown[ctrl_interface/ifname]");
+		wpa_printf(MSG_ERROR, "chown[ctrl_interface/ifname]: %s",
+			   strerror(errno));
 		goto fail;
 	}
 
 	if (!hapd->conf->ctrl_interface_gid_set &&
 	    hapd->iface->interfaces->ctrl_iface_group &&
 	    chown(fname, -1, hapd->iface->interfaces->ctrl_iface_group) < 0) {
-		perror("chown[ctrl_interface/ifname]");
+		wpa_printf(MSG_ERROR, "chown[ctrl_interface/ifname]: %s",
+			   strerror(errno));
 		goto fail;
 	}
 
 	if (chmod(fname, S_IRWXU | S_IRWXG) < 0) {
-		perror("chmod[ctrl_interface/ifname]");
+		wpa_printf(MSG_ERROR, "chmod[ctrl_interface/ifname]: %s",
+			   strerror(errno));
 		goto fail;
 	}
 	os_free(fname);
 
 	hapd->ctrl_sock = s;
-	eloop_register_read_sock(s, hostapd_ctrl_iface_receive, hapd,
-				 NULL);
+	if (eloop_register_read_sock(s, hostapd_ctrl_iface_receive, hapd,
+				     NULL) < 0) {
+		hostapd_ctrl_iface_deinit(hapd);
+		return -1;
+	}
 	hapd->msg_ctx = hapd;
 	wpa_msg_register_cb(hostapd_ctrl_iface_msg_cb);
 
@@ -1777,11 +2324,17 @@
 	}
 
 	dst = hapd->ctrl_dst;
+	hapd->ctrl_dst = NULL;
 	while (dst) {
 		prev = dst;
 		dst = dst->next;
 		os_free(prev);
 	}
+
+#ifdef CONFIG_TESTING_OPTIONS
+	l2_packet_deinit(hapd->l2_test);
+	hapd->l2_test = NULL;
+#endif /* CONFIG_TESTING_OPTIONS */
 }
 
 
@@ -1831,7 +2384,8 @@
 	res = recvfrom(sock, buf, sizeof(buf) - 1, 0,
 		       (struct sockaddr *) &from, &fromlen);
 	if (res < 0) {
-		perror("recvfrom(ctrl_iface)");
+		wpa_printf(MSG_ERROR, "recvfrom(ctrl_iface): %s",
+			   strerror(errno));
 		return;
 	}
 	buf[res] = '\0';
@@ -1871,7 +2425,11 @@
 		reply_len = 5;
 	}
 
-	sendto(sock, reply, reply_len, 0, (struct sockaddr *) &from, fromlen);
+	if (sendto(sock, reply, reply_len, 0, (struct sockaddr *) &from,
+		   fromlen) < 0) {
+		wpa_printf(MSG_DEBUG, "CTRL: sendto failed: %s",
+			   strerror(errno));
+	}
 }
 
 
@@ -1912,13 +2470,15 @@
 			wpa_printf(MSG_DEBUG, "Using existing control "
 				   "interface directory.");
 		} else {
-			perror("mkdir[ctrl_interface]");
+			wpa_printf(MSG_ERROR, "mkdir[ctrl_interface]: %s",
+				   strerror(errno));
 			goto fail;
 		}
 	} else if (interface->ctrl_iface_group &&
 		   chown(interface->global_iface_path, -1,
 			 interface->ctrl_iface_group) < 0) {
-		perror("chown[ctrl_interface]");
+		wpa_printf(MSG_ERROR, "chown[ctrl_interface]: %s",
+			   strerror(errno));
 		goto fail;
 	}
 
@@ -1928,7 +2488,7 @@
 
 	s = socket(PF_UNIX, SOCK_DGRAM, 0);
 	if (s < 0) {
-		perror("socket(PF_UNIX)");
+		wpa_printf(MSG_ERROR, "socket(PF_UNIX): %s", strerror(errno));
 		goto fail;
 	}
 
@@ -1949,15 +2509,15 @@
 				   " allow connections - assuming it was left"
 				   "over from forced program termination");
 			if (unlink(fname) < 0) {
-				perror("unlink[ctrl_iface]");
-				wpa_printf(MSG_ERROR, "Could not unlink "
-					   "existing ctrl_iface socket '%s'",
-					   fname);
+				wpa_printf(MSG_ERROR,
+					   "Could not unlink existing ctrl_iface socket '%s': %s",
+					   fname, strerror(errno));
 				goto fail;
 			}
 			if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) <
 			    0) {
-				perror("bind(PF_UNIX)");
+				wpa_printf(MSG_ERROR, "bind(PF_UNIX): %s",
+					   strerror(errno));
 				goto fail;
 			}
 			wpa_printf(MSG_DEBUG, "Successfully replaced leftover "
@@ -1975,12 +2535,14 @@
 
 	if (interface->ctrl_iface_group &&
 	    chown(fname, -1, interface->ctrl_iface_group) < 0) {
-		perror("chown[ctrl_interface]");
+		wpa_printf(MSG_ERROR, "chown[ctrl_interface]: %s",
+			   strerror(errno));
 		goto fail;
 	}
 
 	if (chmod(fname, S_IRWXU | S_IRWXG) < 0) {
-		perror("chmod[ctrl_interface/ifname]");
+		wpa_printf(MSG_ERROR, "chmod[ctrl_interface/ifname]: %s",
+			   strerror(errno));
 		goto fail;
 	}
 	os_free(fname);
diff --git a/hostapd/defconfig b/hostapd/defconfig
index 5b74b64..4cde2b5 100644
--- a/hostapd/defconfig
+++ b/hostapd/defconfig
@@ -15,10 +15,6 @@
 # Driver interface for wired authenticator
 #CONFIG_DRIVER_WIRED=y
 
-# Driver interface for madwifi driver
-#CONFIG_DRIVER_MADWIFI=y
-#CFLAGS += -I../../madwifi # change to the madwifi source directory
-
 # Driver interface for drivers using the nl80211 kernel interface
 CONFIG_DRIVER_NL80211=y
 
@@ -60,6 +56,9 @@
 # Integrated EAP server
 CONFIG_EAP=y
 
+# EAP Re-authentication Protocol (ERP) in integrated EAP server
+CONFIG_ERP=y
+
 # EAP-MD5 for the integrated EAP server
 CONFIG_EAP_MD5=y
 
@@ -142,7 +141,7 @@
 #CONFIG_IEEE80211R=y
 
 # Use the hostapd's IEEE 802.11 authentication (ACL), but without
-# the IEEE 802.11 Management capability (e.g., madwifi or FreeBSD/net80211)
+# the IEEE 802.11 Management capability (e.g., FreeBSD/net80211)
 #CONFIG_DRIVER_RADIUS_ACL=y
 
 # IEEE 802.11n (High Throughput) support
diff --git a/hostapd/hlr_auc_gw.c b/hostapd/hlr_auc_gw.c
index c041887..8afe457 100644
--- a/hostapd/hlr_auc_gw.c
+++ b/hostapd/hlr_auc_gw.c
@@ -87,6 +87,7 @@
 	u8 amf[2];
 	u8 sqn[6];
 	int set;
+	size_t res_len;
 };
 
 static struct milenage_parameters *milenage_db = NULL;
@@ -96,6 +97,7 @@
 #define EAP_AKA_RAND_LEN 16
 #define EAP_AKA_AUTN_LEN 16
 #define EAP_AKA_AUTS_LEN 14
+#define EAP_AKA_RES_MIN_LEN 4
 #define EAP_AKA_RES_MAX_LEN 16
 #define EAP_AKA_IK_LEN 16
 #define EAP_AKA_CK_LEN 16
@@ -124,7 +126,8 @@
 		"  ki CHAR(32) NOT NULL,"
 		"  opc CHAR(32) NOT NULL,"
 		"  amf CHAR(4) NOT NULL,"
-		"  sqn CHAR(12) NOT NULL"
+		"  sqn CHAR(12) NOT NULL,"
+		"  res_len INTEGER"
 		");";
 
 	printf("Adding database table for milenage information\n");
@@ -190,6 +193,10 @@
 			printf("Invalid sqn value in database\n");
 			return -1;
 		}
+
+		if (os_strcmp(col[i], "res_len") == 0 && argv[i]) {
+			m->res_len = atoi(argv[i]);
+		}
 	}
 
 	return 0;
@@ -206,8 +213,7 @@
 	os_snprintf(db_tmp_milenage.imsi, sizeof(db_tmp_milenage.imsi),
 		    "%llu", imsi);
 	os_snprintf(cmd, sizeof(cmd),
-		    "SELECT ki,opc,amf,sqn FROM milenage WHERE imsi=%llu;",
-		    imsi);
+		    "SELECT * FROM milenage WHERE imsi=%llu;", imsi);
 	if (sqlite3_exec(sqlite_db, cmd, get_milenage_cb, &db_tmp_milenage,
 			 NULL) != SQLITE_OK)
 		return NULL;
@@ -424,7 +430,7 @@
 	while (fgets(buf, sizeof(buf), f)) {
 		line++;
 
-		/* Parse IMSI Ki OPc AMF SQN */
+		/* Parse IMSI Ki OPc AMF SQN [RES_len] */
 		buf[sizeof(buf) - 1] = '\0';
 		if (buf[0] == '#')
 			continue;
@@ -515,7 +521,19 @@
 			ret = -1;
 			break;
 		}
-		pos = pos2 + 1;
+
+		if (pos2) {
+			pos = pos2 + 1;
+			m->res_len = atoi(pos);
+			if (m->res_len &&
+			    (m->res_len < EAP_AKA_RES_MIN_LEN ||
+			     m->res_len > EAP_AKA_RES_MAX_LEN)) {
+				printf("%s:%d - Invalid RES_len (%s)\n",
+				       fname, line, pos);
+				ret = -1;
+				break;
+			}
+		}
 
 		m->next = milenage_db;
 		milenage_db = m;
@@ -711,7 +729,7 @@
 	rend = resp + resp_len;
 	rpos = resp;
 	ret = os_snprintf(rpos, rend - rpos, "GSM-AUTH-RESP %s", imsi);
-	if (ret < 0 || ret >= rend - rpos)
+	if (os_snprintf_error(rend - rpos, ret))
 		return -1;
 	rpos += ret;
 
@@ -737,7 +755,7 @@
 
 	printf("No GSM triplets found for %s\n", imsi);
 	ret = os_snprintf(rpos, rend - rpos, " FAILURE");
-	if (ret < 0 || ret >= rend - rpos)
+	if (os_snprintf_error(rend - rpos, ret))
 		return -1;
 	rpos += ret;
 
@@ -798,6 +816,10 @@
 		}
 		milenage_generate(m->opc, m->amf, m->ki, m->sqn, _rand,
 				  autn, ik, ck, res, &res_len);
+		if (m->res_len >= EAP_AKA_RES_MIN_LEN &&
+		    m->res_len <= EAP_AKA_RES_MAX_LEN &&
+		    m->res_len < res_len)
+			res_len = m->res_len;
 	} else {
 		printf("Unknown IMSI: %s\n", imsi);
 #ifdef AKA_USE_FIXED_TEST_VALUES
diff --git a/hostapd/hlr_auc_gw.milenage_db b/hostapd/hlr_auc_gw.milenage_db
index ecd06d7..c156a29 100644
--- a/hostapd/hlr_auc_gw.milenage_db
+++ b/hostapd/hlr_auc_gw.milenage_db
@@ -5,8 +5,10 @@
 # authentication. In case of GSM/EAP-SIM, AMF and SQN values are not used, but
 # dummy values will need to be included in this file.
 
-# IMSI Ki OPc AMF SQN
+# IMSI Ki OPc AMF SQN [RES_len]
 232010000000000 90dca4eda45b53cf0f12d7c9c3bc6a89 cb9cccc4b9258e6dca4760379fb82581 61df 000000000000
+# Example using truncated 32-bit RES instead of 64-bit default
+232010000000001 90dca4eda45b53cf0f12d7c9c3bc6a89 cb9cccc4b9258e6dca4760379fb82581 61df 000000000000 4
 
 # These values are from Test Set 19 which has the AMF separation bit set to 1
 # and as such, is suitable for EAP-AKA' test.
diff --git a/hostapd/hostapd.8 b/hostapd/hostapd.8
index b4456bb..d19d862 100644
--- a/hostapd/hostapd.8
+++ b/hostapd/hostapd.8
@@ -12,7 +12,7 @@
 .B hostapd
 is a user space daemon for access point and authentication servers.
 It implements IEEE 802.11 access point management, IEEE 802.1X/WPA/WPA2/EAP Authenticators and RADIUS authentication server.
-The current version supports Linux (Host AP, madwifi, mac80211-based drivers) and FreeBSD (net80211).
+The current version supports Linux (Host AP, mac80211-based drivers) and FreeBSD (net80211).
 
 .B hostapd
 is designed to be a "daemon" program that runs in the background and acts as the backend component controlling authentication.
diff --git a/hostapd/hostapd.conf b/hostapd/hostapd.conf
index a7ab0f6..00fc142 100644
--- a/hostapd/hostapd.conf
+++ b/hostapd/hostapd.conf
@@ -2,10 +2,10 @@
 # Empty lines and lines starting with # are ignored
 
 # AP netdevice name (without 'ap' postfix, i.e., wlan0 uses wlan0ap for
-# management frames); ath0 for madwifi
+# management frames with the Host AP driver); wlan0 with many nl80211 drivers
 interface=wlan0
 
-# In case of madwifi, atheros, and nl80211 driver interfaces, an additional
+# In case of atheros and nl80211 driver interfaces, an additional
 # configuration parameter, bridge, may be used to notify hostapd if the
 # interface is included in a bridge. This parameter is not used with Host AP
 # driver. If the bridge parameter is not set, the drivers will automatically
@@ -18,12 +18,15 @@
 # interface is also created.
 #bridge=br0
 
-# Driver interface type (hostap/wired/madwifi/test/none/nl80211/bsd);
+# Driver interface type (hostap/wired/none/nl80211/bsd);
 # default: hostap). nl80211 is used with all Linux mac80211 drivers.
 # Use driver=none if building hostapd as a standalone RADIUS server that does
 # not control any wireless/wired driver.
 # driver=hostap
 
+# Driver interface parameters (mainly for development testing use)
+# driver_params=<params>
+
 # hostapd event logger configuration
 #
 # Two output method: syslog and stdout (only usable if not forking to
@@ -124,7 +127,9 @@
 
 # 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)
+# specify band). When using ACS (see channel parameter), a special value "any"
+# can be used to indicate that any support band can be used. This special case
+# is currently supported only with drivers with which offloaded ACS is used.
 # Default: IEEE 802.11b
 hw_mode=g
 
@@ -151,16 +156,27 @@
 # interference that may help choosing a better channel. This can also help fine
 # tune the ACS scan time in case a driver has different scan dwell times.
 #
+# acs_chan_bias is a space-separated list of <channel>:<bias> pairs. It can be
+# used to increase (or decrease) the likelihood of a specific channel to be
+# selected by the ACS algorithm. The total interference factor for each channel
+# gets multiplied by the specified bias value before finding the channel with
+# the lowest value. In other words, values between 0.0 and 1.0 can be used to
+# make a channel more likely to be picked while values larger than 1.0 make the
+# specified channel less likely to be picked. This can be used, e.g., to prefer
+# the commonly used 2.4 GHz band channels 1, 6, and 11 (which is the default
+# behavior on 2.4 GHz band if no acs_chan_bias parameter is specified).
+#
 # Defaults:
 #acs_num_scans=5
+#acs_chan_bias=1:0.8 6:0.8 11:0.8
 
 # Channel list restriction. This option allows hostapd to select one of the
-# provided channels when a channel should be automatically selected. This
-# is currently only used for DFS when the current channels becomes unavailable
-# due to radar interference, and is currently only useful when ieee80211h=1 is
-# set.
-# Default: not set (allow any enabled channel to be selected)
+# provided channels when a channel should be automatically selected.
+# Channel list can be provided as range using hyphen ('-') or individual
+# channels can be specified by space (' ') seperated values
+# Default: all channels allowed in selected hw_mode
 #chanlist=100 104 108 112 116
+#chanlist=1 6 11-13
 
 # Beacon interval in kus (1.024 ms) (default: 100; range 15..65535)
 beacon_int=100
@@ -221,7 +237,7 @@
 # Station MAC address -based authentication
 # Please note that this kind of access control requires a driver that uses
 # hostapd to take care of management frame processing and as such, this can be
-# used with driver=hostap or driver=nl80211, but not with driver=madwifi.
+# used with driver=hostap or driver=nl80211, but not with driver=atheros.
 # 0 = accept unless in deny list
 # 1 = deny unless in accept list
 # 2 = use external RADIUS server (accept/deny lists are searched first)
@@ -264,8 +280,9 @@
 #		(data0 is the highest priority queue)
 # parameters:
 #   aifs: AIFS (default 2)
-#   cwmin: cwMin (1, 3, 7, 15, 31, 63, 127, 255, 511, 1023)
-#   cwmax: cwMax (1, 3, 7, 15, 31, 63, 127, 255, 511, 1023); cwMax >= cwMin
+#   cwmin: cwMin (1, 3, 7, 15, 31, 63, 127, 255, 511, 1023, 2047, 4095, 8191,
+#	   16383, 32767)
+#   cwmax: cwMax (same values as cwMin, cwMax >= cwMin)
 #   burst: maximum length (in milliseconds with precision of up to 0.1 ms) for
 #          bursting
 #
@@ -326,8 +343,9 @@
 # note - txop_limit is in units of 32microseconds
 # note - acm is admission control mandatory flag. 0 = admission control not
 # required, 1 = mandatory
-# note - here cwMin and cmMax are in exponent form. the actual cw value used
-# will be (2^n)-1 where n is the value given here
+# note - Here cwMin and cmMax are in exponent form. The actual cw value used
+# will be (2^n)-1 where n is the value given here. The allowed range for these
+# wmm_ac_??_{cwmin,cwmax} is 0..15 with cwmax >= cwmin.
 #
 wmm_enabled=1
 #
@@ -435,6 +453,11 @@
 # associated stations in the BSS. By default, this bridging is allowed.
 #ap_isolate=1
 
+# BSS Load update period (in BUs)
+# This field is used to enable and configure adding a BSS Load element into
+# Beacon and Probe Response frames.
+#bss_load_update_period=50
+
 # Fixed BSS Load value for testing purposes
 # This field can be used to configure hostapd to add a fixed BSS Load element
 # into Beacon and Probe Response frames for testing purposes. The format is
@@ -489,9 +512,9 @@
 
 # If set non-zero, require stations to perform scans of overlapping
 # channels to test for stations which would be affected by 40 MHz traffic.
-# This parameter sets the interval in seconds between these scans. This
-# is useful only for testing that stations properly set the OBSS interval,
-# since the other parameters in the OBSS scan parameters IE are set to 0.
+# This parameter sets the interval in seconds between these scans. Setting this
+# to non-zero allows 2.4 GHz band AP to move dynamically to a 40 MHz channel if
+# no co-existence issues with neighboring devices are found.
 #obss_interval=0
 
 ##### IEEE 802.11ac related configuration #####################################
@@ -577,11 +600,6 @@
 # 0 = Not supported or sent by Non-AP STA (default)
 # 1 = Supported
 #
-# MU Beamformee Capable: [MU-BEAMFORMEE]
-# Indicates support for operation as an MU beamformee
-# 0 = Not supported or sent by AP (default)
-# 1 = Supported
-#
 # VHT TXOP PS: [VHT-TXOP-PS]
 # Indicates whether or not the AP supports VHT TXOP Power Save Mode
 #  or whether or not the STA is in VHT TXOP Power Save mode
@@ -688,6 +706,17 @@
 # is only used by one station.
 #use_pae_group_addr=1
 
+# EAP Re-authentication Protocol (ERP) authenticator (RFC 6696)
+#
+# Whether to initiate EAP authentication with EAP-Initiate/Re-auth-Start before
+# EAP-Identity/Request
+#erp_send_reauth_start=1
+#
+# Domain name for EAP-Initiate/Re-auth-Start. Omitted from the message if not
+# set (no local ER server). This is also used by the integrated EAP server if
+# ERP is enabled (eap_server_erp=1).
+#erp_domain=example.com
+
 ##### Integrated EAP server ###################################################
 
 # Optionally, hostapd can be configured to use an integrated EAP server
@@ -760,9 +789,18 @@
 # is in DSA parameters format, it will be automatically converted into DH
 # params. This parameter is required if anonymous EAP-FAST is used.
 # You can generate DH parameters file with OpenSSL, e.g.,
-# "openssl dhparam -out /etc/hostapd.dh.pem 1024"
+# "openssl dhparam -out /etc/hostapd.dh.pem 2048"
 #dh_file=/etc/hostapd.dh.pem
 
+# OpenSSL cipher string
+#
+# This is an OpenSSL specific configuration option for configuring the default
+# ciphers. If not set, "DEFAULT:!EXP:!LOW" is used as the default.
+# See https://www.openssl.org/docs/apps/ciphers.html for OpenSSL documentation
+# on cipher suite configuration. This is applicable only if hostapd is built to
+# use OpenSSL.
+#openssl_ciphers=DEFAULT:!EXP:!LOW
+
 # Fragment size for EAP methods
 #fragment_size=1400
 
@@ -824,6 +862,10 @@
 # EAP method is enabled, the peer will be allowed to connect without TNC.
 #tnc=1
 
+# EAP Re-authentication Protocol (ERP) - RFC 6696
+#
+# Whether to enable ERP on the EAP server.
+#eap_server_erp=1
 
 ##### IEEE 802.11f - Inter-Access Point Protocol (IAPP) #######################
 
@@ -845,6 +887,12 @@
 # 48 octets long.
 #nas_identifier=ap.example.com
 
+# RADIUS client forced local IP address for the access point
+# Normally the local IP address is determined automatically based on configured
+# IP addresses, but this field can be used to force a specific address to be
+# used, e.g., when the device has multiple IP addresses.
+#radius_client_addr=127.0.0.1
+
 # RADIUS authentication server
 #auth_server_addr=127.0.0.1
 #auth_server_port=1812
@@ -1384,7 +1432,7 @@
 # 12-digit, all-numeric code that identifies the consumer package.
 #upc=123456789012
 
-# WPS RF Bands (a = 5G, b = 2.4G, g = 2.4G, ag = dual band)
+# WPS RF Bands (a = 5G, b = 2.4G, g = 2.4G, ag = dual band, ad = 60 GHz)
 # This value should be set according to RF band(s) supported by the AP if
 # hw_mode is not set. For dual band dual concurrent devices, this needs to be
 # set to ag to allow both RF bands to be advertized.
@@ -1439,6 +1487,18 @@
 # 1 = enabled
 #bss_transition=1
 
+# Proxy ARP
+# 0 = disabled (default)
+# 1 = enabled
+#proxy_arp=1
+
+# IPv6 Neighbor Advertisement multicast-to-unicast conversion
+# This can be used with Proxy ARP to allow multicast NAs to be forwarded to
+# associated STAs using link layer unicast delivery.
+# 0 = disabled (default)
+# 1 = enabled
+#na_mcast_to_ucast=0
+
 ##### IEEE 802.11u-2011 #######################################################
 
 # Enable Interworking service
diff --git a/hostapd/hostapd_cli.c b/hostapd/hostapd_cli.c
index 9e62bef..e299183 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-2014, 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.
@@ -10,22 +10,23 @@
 #include <dirent.h>
 
 #include "common/wpa_ctrl.h"
+#include "common/ieee802_11_defs.h"
 #include "utils/common.h"
 #include "utils/eloop.h"
 #include "utils/edit.h"
 #include "common/version.h"
 
 
-static const char *hostapd_cli_version =
+static const char *const hostapd_cli_version =
 "hostapd_cli v" VERSION_STR "\n"
-"Copyright (c) 2004-2014, Jouni Malinen <j@w1.fi> and contributors";
+"Copyright (c) 2004-2015, Jouni Malinen <j@w1.fi> and contributors";
 
 
-static const char *hostapd_cli_license =
+static const char *const hostapd_cli_license =
 "This software may be distributed under the terms of the BSD license.\n"
 "See README for more details.\n";
 
-static const char *hostapd_cli_full_license =
+static const char *const hostapd_cli_full_license =
 "This software may be distributed under the terms of the BSD license.\n"
 "\n"
 "Redistribution and use in source and binary forms, with or without\n"
@@ -56,7 +57,7 @@
 "OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n"
 "\n";
 
-static const char *commands_help =
+static const char *const commands_help =
 "Commands:\n"
 "   mib                  get MIB variables (dot1x, dot11, radius)\n"
 "   sta <addr>           get MIB variables for one station\n"
@@ -393,7 +394,7 @@
 	else
 		res = os_snprintf(cmd, sizeof(cmd), "WPS_CHECK_PIN %s",
 				  argv[0]);
-	if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
+	if (os_snprintf_error(sizeof(cmd), res)) {
 		printf("Too long WPS_CHECK_PIN command.\n");
 		return -1;
 	}
@@ -456,7 +457,7 @@
 
 	res = os_snprintf(cmd, sizeof(cmd), "WPS_NFC_CONFIG_TOKEN %s",
 			  argv[0]);
-	if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
+	if (os_snprintf_error(sizeof(cmd), res)) {
 		printf("Too long WPS_NFC_CONFIG_TOKEN command.\n");
 		return -1;
 	}
@@ -477,7 +478,7 @@
 	}
 
 	res = os_snprintf(cmd, sizeof(cmd), "WPS_NFC_TOKEN %s", argv[0]);
-	if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
+	if (os_snprintf_error(sizeof(cmd), res)) {
 		printf("Too long WPS_NFC_TOKEN command.\n");
 		return -1;
 	}
@@ -499,7 +500,7 @@
 
 	res = os_snprintf(cmd, sizeof(cmd), "NFC_GET_HANDOVER_SEL %s %s",
 			  argv[0], argv[1]);
-	if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
+	if (os_snprintf_error(sizeof(cmd), res)) {
 		printf("Too long NFC_GET_HANDOVER_SEL command.\n");
 		return -1;
 	}
@@ -541,7 +542,7 @@
 				      char *argv[])
 {
 	char buf[256];
-	char ssid_hex[2 * 32 + 1];
+	char ssid_hex[2 * SSID_MAX_LEN + 1];
 	char key_hex[2 * 64 + 1];
 	int i;
 
@@ -552,7 +553,7 @@
 	}
 
 	ssid_hex[0] = '\0';
-	for (i = 0; i < 32; i++) {
+	for (i = 0; i < SSID_MAX_LEN; i++) {
 		if (argv[0][i] == '\0')
 			break;
 		os_snprintf(&ssid_hex[i * 2], 3, "%02x", argv[0][i]);
@@ -596,7 +597,7 @@
 
 	res = os_snprintf(buf, sizeof(buf), "DISASSOC_IMMINENT %s %s",
 			  argv[0], argv[1]);
-	if (res < 0 || res >= (int) sizeof(buf))
+	if (os_snprintf_error(sizeof(buf), res))
 		return -1;
 	return wpa_ctrl_command(ctrl, buf);
 }
@@ -616,12 +617,39 @@
 
 	res = os_snprintf(buf, sizeof(buf), "ESS_DISASSOC %s %s %s",
 			  argv[0], argv[1], argv[2]);
-	if (res < 0 || res >= (int) sizeof(buf))
+	if (os_snprintf_error(sizeof(buf), res))
 		return -1;
 	return wpa_ctrl_command(ctrl, buf);
 }
 
 
+static int hostapd_cli_cmd_bss_tm_req(struct wpa_ctrl *ctrl, int argc,
+				      char *argv[])
+{
+	char buf[2000], *tmp;
+	int res, i, total;
+
+	if (argc < 1) {
+		printf("Invalid 'bss_tm_req' command - at least one argument (STA addr) is needed\n");
+		return -1;
+	}
+
+	res = os_snprintf(buf, sizeof(buf), "BSS_TM_REQ %s", argv[0]);
+	if (os_snprintf_error(sizeof(buf), res))
+		return -1;
+
+	total = res;
+	for (i = 1; i < argc; i++) {
+		tmp = &buf[total];
+		res = os_snprintf(tmp, sizeof(buf) - total, " %s", argv[i]);
+		if (os_snprintf_error(sizeof(buf) - total, res))
+			return -1;
+		total += res;
+	}
+	return wpa_ctrl_command(ctrl, buf);
+}
+
+
 static int hostapd_cli_cmd_get_config(struct wpa_ctrl *ctrl, int argc,
 				      char *argv[])
 {
@@ -709,7 +737,7 @@
 	}
 
 	res = os_snprintf(buf, sizeof(buf), "SET_QOS_MAP_SET %s", argv[0]);
-	if (res < 0 || res >= (int) sizeof(buf))
+	if (os_snprintf_error(sizeof(buf), res))
 		return -1;
 	return wpa_ctrl_command(ctrl, buf);
 }
@@ -728,7 +756,7 @@
 	}
 
 	res = os_snprintf(buf, sizeof(buf), "SEND_QOS_MAP_CONF %s", argv[0]);
-	if (res < 0 || res >= (int) sizeof(buf))
+	if (os_snprintf_error(sizeof(buf), res))
 		return -1;
 	return wpa_ctrl_command(ctrl, buf);
 }
@@ -748,7 +776,7 @@
 
 	res = os_snprintf(buf, sizeof(buf), "HS20_WNM_NOTIF %s %s",
 			  argv[0], argv[1]);
-	if (res < 0 || res >= (int) sizeof(buf))
+	if (os_snprintf_error(sizeof(buf), res))
 		return -1;
 	return wpa_ctrl_command(ctrl, buf);
 }
@@ -773,7 +801,7 @@
 		res = os_snprintf(buf, sizeof(buf),
 				  "HS20_DEAUTH_REQ %s %s %s",
 				  argv[0], argv[1], argv[2]);
-	if (res < 0 || res >= (int) sizeof(buf))
+	if (os_snprintf_error(sizeof(buf), res))
 		return -1;
 	return wpa_ctrl_command(ctrl, buf);
 }
@@ -866,7 +894,7 @@
 	}
 
 	res = os_snprintf(cmd, sizeof(cmd), "SET %s %s", argv[0], argv[1]);
-	if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
+	if (os_snprintf_error(sizeof(cmd), res)) {
 		printf("Too long SET command.\n");
 		return -1;
 	}
@@ -886,7 +914,7 @@
 	}
 
 	res = os_snprintf(cmd, sizeof(cmd), "GET %s", argv[0]);
-	if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
+	if (os_snprintf_error(sizeof(cmd), res)) {
 		printf("Too long GET command.\n");
 		return -1;
 	}
@@ -914,7 +942,7 @@
 
 	res = os_snprintf(cmd, sizeof(cmd), "CHAN_SWITCH %s %s",
 			  argv[0], argv[1]);
-	if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
+	if (os_snprintf_error(sizeof(cmd), res)) {
 		printf("Too long CHAN_SWITCH command.\n");
 		return -1;
 	}
@@ -923,7 +951,7 @@
 	for (i = 2; i < argc; i++) {
 		tmp = cmd + total;
 		res = os_snprintf(tmp, sizeof(cmd) - total, " %s", argv[i]);
-		if (res < 0 || (size_t) res >= sizeof(cmd) - total - 1) {
+		if (os_snprintf_error(sizeof(cmd) - total, res)) {
 			printf("Too long CHAN_SWITCH command.\n");
 			return -1;
 		}
@@ -933,6 +961,27 @@
 }
 
 
+static int hostapd_cli_cmd_enable(struct wpa_ctrl *ctrl, int argc,
+				      char *argv[])
+{
+	return wpa_ctrl_command(ctrl, "ENABLE");
+}
+
+
+static int hostapd_cli_cmd_reload(struct wpa_ctrl *ctrl, int argc,
+				      char *argv[])
+{
+	return wpa_ctrl_command(ctrl, "RELOAD");
+}
+
+
+static int hostapd_cli_cmd_disable(struct wpa_ctrl *ctrl, int argc,
+				      char *argv[])
+{
+	return wpa_ctrl_command(ctrl, "DISABLE");
+}
+
+
 static int hostapd_cli_cmd_vendor(struct wpa_ctrl *ctrl, int argc, char *argv[])
 {
 	char cmd[256];
@@ -946,7 +995,7 @@
 
 	res = os_snprintf(cmd, sizeof(cmd), "VENDOR %s %s %s", argv[0], argv[1],
 			  argc == 3 ? argv[2] : "");
-	if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
+	if (os_snprintf_error(sizeof(cmd), res)) {
 		printf("Too long VENDOR command.\n");
 		return -1;
 	}
@@ -954,12 +1003,19 @@
 }
 
 
+static int hostapd_cli_cmd_erp_flush(struct wpa_ctrl *ctrl, int argc,
+				     char *argv[])
+{
+	return wpa_ctrl_command(ctrl, "ERP_FLUSH");
+}
+
+
 struct hostapd_cli_cmd {
 	const char *cmd;
 	int (*handler)(struct wpa_ctrl *ctrl, int argc, char *argv[]);
 };
 
-static struct hostapd_cli_cmd hostapd_cli_commands[] = {
+static const struct hostapd_cli_cmd hostapd_cli_commands[] = {
 	{ "ping", hostapd_cli_cmd_ping },
 	{ "mib", hostapd_cli_cmd_mib },
 	{ "relog", hostapd_cli_cmd_relog },
@@ -989,6 +1045,7 @@
 #endif /* CONFIG_WPS */
 	{ "disassoc_imminent", hostapd_cli_cmd_disassoc_imminent },
 	{ "ess_disassoc", hostapd_cli_cmd_ess_disassoc },
+	{ "bss_tm_req", hostapd_cli_cmd_bss_tm_req },
 	{ "get_config", hostapd_cli_cmd_get_config },
 	{ "help", hostapd_cli_cmd_help },
 	{ "interface", hostapd_cli_cmd_interface },
@@ -1003,13 +1060,17 @@
 	{ "hs20_wnm_notif", hostapd_cli_cmd_hs20_wnm_notif },
 	{ "hs20_deauth_req", hostapd_cli_cmd_hs20_deauth_req },
 	{ "vendor", hostapd_cli_cmd_vendor },
+	{ "enable", hostapd_cli_cmd_enable },
+	{ "reload", hostapd_cli_cmd_reload },
+	{ "disable", hostapd_cli_cmd_disable },
+	{ "erp_flush", hostapd_cli_cmd_erp_flush },
 	{ NULL, NULL }
 };
 
 
 static void wpa_request(struct wpa_ctrl *ctrl, int argc, char *argv[])
 {
-	struct hostapd_cli_cmd *cmd, *match = NULL;
+	const struct hostapd_cli_cmd *cmd, *match = NULL;
 	int count;
 
 	count = 0;
diff --git a/hostapd/main.c b/hostapd/main.c
index c3af704..e36c948 100644
--- a/hostapd/main.c
+++ b/hostapd/main.c
@@ -1,6 +1,6 @@
 /*
  * hostapd / main()
- * Copyright (c) 2002-2011, 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.
@@ -28,8 +28,6 @@
 #include "eap_register.h"
 #include "ctrl_iface.h"
 
-struct wowlan_triggers *wpa_get_wowlan_triggers(const char *wowlan_triggers,
-						struct wpa_driver_capa *capa);
 
 struct hapd_global {
 	void **drv_priv;
@@ -186,9 +184,7 @@
 	}
 	params.bssid = b;
 	params.ifname = hapd->conf->iface;
-	params.ssid = hapd->conf->ssid.ssid;
-	params.ssid_len = hapd->conf->ssid.ssid_len;
-	params.test_socket = hapd->conf->test_socket;
+	params.driver_params = hapd->iconf->driver_params;
 	params.use_pae_group_addr = hapd->conf->use_pae_group_addr;
 
 	params.num_bridge = hapd->iface->num_bss;
@@ -217,6 +213,7 @@
 		struct wowlan_triggers *triggs;
 
 		iface->drv_flags = capa.flags;
+		iface->smps_modes = capa.smps_modes;
 		iface->probe_resp_offloads = capa.probe_resp_offloads;
 		iface->extended_capa = capa.extended_capa;
 		iface->extended_capa_mask = capa.extended_capa_mask;
@@ -411,7 +408,7 @@
 #endif /* EAP_SERVER_TNC */
 
 	if (daemonize && os_daemonize(pid_file)) {
-		perror("daemon");
+		wpa_printf(MSG_ERROR, "daemon: %s", strerror(errno));
 		return -1;
 	}
 
@@ -427,7 +424,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-2014, Jouni Malinen <j@w1.fi> "
+		"Copyright (c) 2002-2015, Jouni Malinen <j@w1.fi> "
 		"and contributors\n");
 }
 
@@ -641,6 +638,8 @@
 
 	if (log_file)
 		wpa_debug_open_file(log_file);
+	else
+		wpa_debug_setup_stdout();
 #ifdef CONFIG_DEBUG_LINUX_TRACING
 	if (enable_trace_dbg) {
 		int tret = wpa_debug_open_linux_tracing();
@@ -662,7 +661,7 @@
 	}
 
 	if (hostapd_global_init(&interfaces, entropy_file)) {
-		wpa_printf(MSG_ERROR, "Failed to initilize global context");
+		wpa_printf(MSG_ERROR, "Failed to initialize global context");
 		return -1;
 	}
 
diff --git a/hs20/client/Android.mk b/hs20/client/Android.mk
index ced6d8d..a71e86d 100644
--- a/hs20/client/Android.mk
+++ b/hs20/client/Android.mk
@@ -14,19 +14,21 @@
 # headers to the include path by hand.
 ifeq ($(wildcard external/icu),)
 INCLUDES += external/icu4c/common
+else
+# The LOCAL_EXPORT_C_INCLUDE_DIRS from ICU did not seem to fully resolve the
+# build (e.g., "mm -B" failed to build, but following that with "mm" allowed
+# the build to complete). For now, add the include directory manually here for
+# Android 5.0.
+ver = $(filter 5.0%,$(PLATFORM_VERSION))
+ifneq (,$(strip $(ver)))
+INCLUDES += external/icu/icu4c/source/common
+endif
 endif
 
 
-#GTKCFLAGS := $(shell pkg-config --cflags gtk+-2.0 webkit-1.0)
-#GTKLIBS := $(shell pkg-config --libs gtk+-2.0 webkit-1.0)
-#CFLAGS += $(GTKCFLAGS)
-#LIBS += $(GTKLIBS)
-
 L_CFLAGS += -DCONFIG_CTRL_IFACE
 L_CFLAGS += -DCONFIG_CTRL_IFACE_UNIX
 L_CFLAGS += -DCONFIG_CTRL_IFACE_CLIENT_DIR=\"/data/misc/wifi/sockets\"
-L_CFLAGS += -DLIBXML_SCHEMAS_ENABLED
-L_CFLAGS += -DLIBXML_REGEXP_ENABLED
 
 OBJS = spp_client.c
 OBJS += oma_dm_client.c
@@ -55,8 +57,7 @@
 
 L_CFLAGS += -DEAP_TLS_OPENSSL
 
-#CFLAGS += $(shell xml2-config --cflags)
-#LIBS += $(shell xml2-config --libs)
+L_CFLAGS += -Wno-unused-parameter
 
 
 ########################
diff --git a/hs20/client/Makefile b/hs20/client/Makefile
index ca67b54..94cd5f1 100644
--- a/hs20/client/Makefile
+++ b/hs20/client/Makefile
@@ -67,7 +67,13 @@
 
 CFLAGS += $(shell xml2-config --cflags)
 LIBS += $(shell xml2-config --libs)
+
+# Allow static/custom linking of libcurl.
+ifdef CUST_CURL_LINKAGE
+LIBS += ${CUST_CURL_LINKAGE}
+else
 LIBS += -lcurl
+endif
 
 CFLAGS += -DEAP_TLS_OPENSSL
 LIBS += -lssl -lcrypto
diff --git a/hs20/client/oma_dm_client.c b/hs20/client/oma_dm_client.c
index 82e9106..5854b72 100644
--- a/hs20/client/oma_dm_client.c
+++ b/hs20/client/oma_dm_client.c
@@ -394,6 +394,10 @@
 	}
 
 	data = xml_node_get_text(ctx->xml, node);
+	if (data == NULL) {
+		wpa_printf(MSG_INFO, "Invalid data");
+		return DM_RESP_BAD_REQUEST;
+	}
 	wpa_printf(MSG_INFO, "Data: %s", data);
 	wpa_printf(MSG_INFO, "Launch browser to URI '%s'", data);
 	write_summary(ctx, "Launch browser to URI '%s'", data);
@@ -428,6 +432,10 @@
 	}
 
 	data = xml_node_get_text(ctx->xml, node);
+	if (data == NULL) {
+		wpa_printf(MSG_INFO, "Invalid data");
+		return DM_RESP_BAD_REQUEST;
+	}
 	wpa_printf(MSG_INFO, "Data: %s", data);
 	getcert = xml_node_from_buf(ctx->xml, data);
 	xml_node_get_text_free(ctx->xml, data);
@@ -576,6 +584,11 @@
 	if (node) {
 		char *type;
 		type = xml_node_get_text(ctx->xml, node);
+		if (type == NULL) {
+			wpa_printf(MSG_ERROR, "Could not find type text");
+			os_free(uri);
+			return DM_RESP_BAD_REQUEST;
+		}
 		use_tnds = node &&
 			os_strstr(type, "application/vnd.syncml.dmtnds+xml");
 	}
@@ -648,6 +661,10 @@
 		return DM_RESP_BAD_REQUEST;
 	}
 	locuri = xml_node_get_text(ctx->xml, node);
+	if (locuri == NULL) {
+		wpa_printf(MSG_ERROR, "No LocURI node text found");
+		return DM_RESP_BAD_REQUEST;
+	}
 	wpa_printf(MSG_INFO, "Target LocURI: %s", locuri);
 	if (os_strncasecmp(locuri, "./Wi-Fi/", 8) != 0) {
 		wpa_printf(MSG_INFO, "Unsupported Add Target LocURI");
@@ -755,6 +772,11 @@
 	if (node) {
 		char *type;
 		type = xml_node_get_text(ctx->xml, node);
+		if (type == NULL) {
+			wpa_printf(MSG_INFO, "Could not find type text");
+			os_free(locuri);
+			return DM_RESP_BAD_REQUEST;
+		}
 		use_tnds = node &&
 			os_strstr(type, "application/vnd.syncml.dmtnds+xml");
 	}
diff --git a/hs20/client/osu_client.c b/hs20/client/osu_client.c
index a439bde..0315f7b 100644
--- a/hs20/client/osu_client.c
+++ b/hs20/client/osu_client.c
@@ -9,6 +9,9 @@
 #include "includes.h"
 #include <time.h>
 #include <sys/stat.h>
+#ifdef ANDROID
+#include "private/android_filesystem_config.h"
+#endif /* ANDROID */
 
 #include "common.h"
 #include "utils/browser.h"
@@ -22,6 +25,8 @@
 #include "crypto/sha256.h"
 #include "osu_client.h"
 
+const char *spp_xsd_fname = "spp.xsd";
+
 
 void write_result(struct hs20_osu_client *ctx, const char *fmt, ...)
 {
@@ -397,9 +402,9 @@
 	}
 
 	node = get_child_node(ctx->xml, pps,
-			      "PolicyUpdate/TrustRoot");
+			      "Policy/PolicyUpdate/TrustRoot");
 	if (node == NULL) {
-		wpa_printf(MSG_INFO, "No PolicyUpdate/TrustRoot/CertURL found from PPS");
+		wpa_printf(MSG_INFO, "No Policy/PolicyUpdate/TrustRoot/CertURL found from PPS");
 		xml_node_free(ctx->xml, pps);
 		return -1;
 	}
@@ -537,6 +542,7 @@
 				   uri);
 			write_result(ctx, "Unsupported location for addMO to "
 				     "add PPS MO (extra directory): '%s'", uri);
+			free(fqdn);
 			return -1;
 		}
 		*pos = '\0'; /* remove trailing slash and PPS node name */
@@ -544,8 +550,9 @@
 	wpa_printf(MSG_INFO, "SP FQDN: %s", fqdn);
 
 	if (!server_dnsname_suffix_match(ctx, fqdn)) {
-		wpa_printf(MSG_INFO, "FQDN '%s' for new PPS MO did not have suffix match with server's dNSName values",
-			   fqdn);
+		wpa_printf(MSG_INFO,
+			   "FQDN '%s' for new PPS MO did not have suffix match with server's dNSName values, count: %d",
+			   fqdn, (int) ctx->server_dnsname_count);
 		write_result(ctx, "FQDN '%s' for new PPS MO did not have suffix match with server's dNSName values",
 			     fqdn);
 		free(fqdn);
@@ -571,6 +578,21 @@
 		}
 	}
 
+#ifdef ANDROID
+	/* Allow processes running with Group ID as AID_WIFI,
+	 * to read files from SP/<fqdn> directory */
+	if (chown(fname, -1, AID_WIFI)) {
+		wpa_printf(MSG_INFO, "CTRL: Could not chown directory: %s",
+			   strerror(errno));
+		/* Try to continue anyway */
+	}
+	if (chmod(fname, S_IRWXU | S_IRGRP | S_IXGRP) < 0) {
+		wpa_printf(MSG_INFO, "CTRL: Could not chmod directory: %s",
+			   strerror(errno));
+		/* Try to continue anyway */
+	}
+#endif /* ANDROID */
+
 	snprintf(fname, fname_len, "SP/%s/pps.xml", fqdn);
 
 	if (os_file_exists(fname)) {
@@ -669,6 +691,10 @@
 	wpa_printf(MSG_INFO, "Updating PPS MO %s", pps_fname);
 
 	str = xml_node_to_str(ctx->xml, pps);
+	if (str == NULL) {
+		wpa_printf(MSG_ERROR, "No node found");
+		return -1;
+	}
 	wpa_printf(MSG_MSGDUMP, "[hs20] Updated PPS: '%s'", str);
 
 	snprintf(backup, sizeof(backup), "%s.bak", pps_fname);
@@ -2072,10 +2098,14 @@
 	}
 
 	ctx->no_reconnect = 1;
-	if (methods & 0x02)
+	if (methods & 0x02) {
+		wpa_printf(MSG_DEBUG, "Calling cmd_prov from osu_connect");
 		res = cmd_prov(ctx, url);
-	else if (methods & 0x01)
+	} else if (methods & 0x01) {
+		wpa_printf(MSG_DEBUG,
+			   "Calling cmd_oma_dm_prov from osu_connect");
 		res = cmd_oma_dm_prov(ctx, url);
+	}
 
 	wpa_printf(MSG_INFO, "Remove OSU network connection");
 	write_summary(ctx, "Remove OSU network connection");
@@ -2117,7 +2147,7 @@
 	snprintf(fname, sizeof(fname), "%s/osu-providers.txt", dir);
 	osu = parse_osu_providers(fname, &osu_count);
 	if (osu == NULL) {
-		wpa_printf(MSG_INFO, "Could not any OSU providers from %s",
+		wpa_printf(MSG_INFO, "Could not find any OSU providers from %s",
 			   fname);
 		write_result(ctx, "No OSU providers available");
 		return -1;
@@ -2268,12 +2298,19 @@
 		}
 
 		if (connect == 2) {
-			if (last->methods & 0x02)
+			if (last->methods & 0x02) {
+				wpa_printf(MSG_DEBUG,
+					   "Calling cmd_prov from cmd_osu_select");
 				ret = cmd_prov(ctx, last->url);
-			else if (last->methods & 0x01)
+			} else if (last->methods & 0x01) {
+				wpa_printf(MSG_DEBUG,
+					   "Calling cmd_oma_dm_prov from cmd_osu_select");
 				ret = cmd_oma_dm_prov(ctx, last->url);
-			else
+			} else {
+				wpa_printf(MSG_DEBUG,
+					   "No supported OSU provisioning method");
 				ret = -1;
+			}
 		} else if (connect)
 			ret = osu_connect(ctx, last->bssid, last->osu_ssid,
 					  last->url, last->methods,
@@ -2343,8 +2380,8 @@
 }
 
 
-static void cmd_sub_rem(struct hs20_osu_client *ctx, const char *address,
-			const char *pps_fname, const char *ca_fname)
+static int cmd_sub_rem(struct hs20_osu_client *ctx, const char *address,
+		       const char *pps_fname, const char *ca_fname)
 {
 	xml_node_t *pps, *node;
 	char pps_fname_buf[300];
@@ -2371,12 +2408,12 @@
 		} else if (get_wpa_status(ctx->ifname, "provisioning_sp", buf,
 					  sizeof(buf)) < 0) {
 			wpa_printf(MSG_INFO, "Could not get provisioning Home SP FQDN from wpa_supplicant");
-			return;
+			return -1;
 		}
 		os_free(ctx->fqdn);
 		ctx->fqdn = os_strdup(buf);
 		if (ctx->fqdn == NULL)
-			return;
+			return -1;
 		wpa_printf(MSG_INFO, "Home SP FQDN for current credential: %s",
 			   buf);
 		os_snprintf(pps_fname_buf, sizeof(pps_fname_buf),
@@ -2391,14 +2428,14 @@
 	if (!os_file_exists(pps_fname)) {
 		wpa_printf(MSG_INFO, "PPS file '%s' does not exist or is not accessible",
 			   pps_fname);
-		return;
+		return -1;
 	}
 	wpa_printf(MSG_INFO, "Using PPS file: %s", pps_fname);
 
 	if (ca_fname && !os_file_exists(ca_fname)) {
 		wpa_printf(MSG_INFO, "CA file '%s' does not exist or is not accessible",
 			   ca_fname);
-		return;
+		return -1;
 	}
 	wpa_printf(MSG_INFO, "Using server trust root: %s", ca_fname);
 	ctx->ca_fname = ca_fname;
@@ -2406,7 +2443,7 @@
 	pps = node_from_file(ctx->xml, pps_fname);
 	if (pps == NULL) {
 		wpa_printf(MSG_INFO, "Could not read PPS MO");
-		return;
+		return -1;
 	}
 
 	if (!ctx->fqdn) {
@@ -2414,18 +2451,18 @@
 		node = get_child_node(ctx->xml, pps, "HomeSP/FQDN");
 		if (node == NULL) {
 			wpa_printf(MSG_INFO, "No HomeSP/FQDN found from PPS");
-			return;
+			return -1;
 		}
 		tmp = xml_node_get_text(ctx->xml, node);
 		if (tmp == NULL) {
 			wpa_printf(MSG_INFO, "No HomeSP/FQDN text found from PPS");
-			return;
+			return -1;
 		}
 		ctx->fqdn = os_strdup(tmp);
 		xml_node_get_text_free(ctx->xml, tmp);
 		if (!ctx->fqdn) {
 			wpa_printf(MSG_INFO, "No FQDN known");
-			return;
+			return -1;
 		}
 	}
 
@@ -2474,7 +2511,7 @@
 	}
 	if (!address) {
 		wpa_printf(MSG_INFO, "Server URL not known");
-		return;
+		return -1;
 	}
 
 	write_summary(ctx, "Wait for IP address for subscriptiom remediation");
@@ -2497,6 +2534,7 @@
 	xml_node_get_text_free(ctx->xml, cred_username);
 	str_clear_free(cred_password);
 	xml_node_free(ctx->xml, pps);
+	return 0;
 }
 
 
@@ -2667,7 +2705,7 @@
 
 	end = os_strchr(pos, '/');
 	end2 = os_strchr(pos, ':');
-	if (end && end2 && end2 < end)
+	if ((end && end2 && end2 < end) || (!end && end2))
 		end = end2;
 	if (end)
 		end--;
@@ -2697,8 +2735,8 @@
 	int found;
 	char *host = NULL;
 
-	wpa_printf(MSG_INFO, "osu_cert_cb(osu_cert_validation=%d)",
-		   !ctx->no_osu_cert_validation);
+	wpa_printf(MSG_INFO, "osu_cert_cb(osu_cert_validation=%d, url=%s)",
+		   !ctx->no_osu_cert_validation, ctx->server_url);
 
 	host = get_hostname(ctx->server_url);
 
@@ -2787,17 +2825,21 @@
 		char *name = ctx->icon_filename[j];
 		size_t name_len = os_strlen(name);
 
-		wpa_printf(MSG_INFO, "Looking for icon file name '%s' match",
-			   name);
+		wpa_printf(MSG_INFO,
+			   "[%i] Looking for icon file name '%s' match",
+			   j, name);
 		for (i = 0; i < cert->num_logo; i++) {
 			struct http_logo *logo = &cert->logo[i];
 			size_t uri_len = os_strlen(logo->uri);
 			char *pos;
 
-			wpa_printf(MSG_INFO, "Comparing to '%s' uri_len=%d name_len=%d",
-				   logo->uri, (int) uri_len, (int) name_len);
-			if (uri_len < 1 + name_len)
+			wpa_printf(MSG_INFO,
+				   "[%i] Comparing to '%s' uri_len=%d name_len=%d",
+				   i, logo->uri, (int) uri_len, (int) name_len);
+			if (uri_len < 1 + name_len) {
+				wpa_printf(MSG_INFO, "URI Length is too short");
 				continue;
+			}
 			pos = &logo->uri[uri_len - name_len - 1];
 			if (*pos != '/')
 				continue;
@@ -2824,17 +2866,30 @@
 		for (i = 0; i < cert->num_logo; i++) {
 			struct http_logo *logo = &cert->logo[i];
 
-			if (logo->hash_len != 32)
+			if (logo->hash_len != 32) {
+				wpa_printf(MSG_INFO,
+					   "[%i][%i] Icon hash length invalid (should be 32): %d",
+					   j, i, (int) logo->hash_len);
 				continue;
+			}
 			if (os_memcmp(logo->hash, ctx->icon_hash[j], 32) == 0) {
 				found = 1;
 				break;
 			}
+
+			wpa_printf(MSG_DEBUG,
+				   "[%u][%u] Icon hash did not match", j, i);
+			wpa_hexdump_ascii(MSG_DEBUG, "logo->hash",
+					  logo->hash, 32);
+			wpa_hexdump_ascii(MSG_DEBUG, "ctx->icon_hash[j]",
+					  ctx->icon_hash[j], 32);
 		}
 
 		if (!found) {
-			wpa_printf(MSG_INFO, "No icon hash match found");
-			write_result(ctx, "No icon hash match found");
+			wpa_printf(MSG_INFO,
+				   "No icon hash match (by hash) found");
+			write_result(ctx,
+				     "No icon hash match (by hash) found");
 			return -1;
 		}
 	}
@@ -2932,6 +2987,7 @@
 	       "    [-w<wpa_supplicant ctrl_iface dir>] "
 	       "[-r<result file>] [-f<debug file>] \\\n"
 	       "    [-s<summary file>] \\\n"
+	       "    [-x<spp.xsd file name>] \\\n"
 	       "    <command> [arguments..]\n"
 	       "commands:\n"
 	       "- to_tnds <XML MO> <XML MO in TNDS format> [URN]\n"
@@ -2973,7 +3029,7 @@
 		return -1;
 
 	for (;;) {
-		c = getopt(argc, argv, "df:hi:KNO:qr:s:S:tw:");
+		c = getopt(argc, argv, "df:hKNO:qr:s:S:tw:x:");
 		if (c < 0)
 			break;
 		switch (c) {
@@ -3011,6 +3067,9 @@
 		case 'w':
 			wpas_ctrl_path = optarg;
 			break;
+		case 'x':
+			spp_xsd_fname = optarg;
+			break;
 		case 'h':
 		default:
 			usage();
@@ -3066,10 +3125,11 @@
 		if (argc - optind < 2)
 			wpa_printf(MSG_ERROR, "Server URL missing from command line");
 		else
-			cmd_sub_rem(&ctx, argv[optind + 1],
-				    argc > optind + 2 ? argv[optind + 2] : NULL,
-				    argc > optind + 3 ? argv[optind + 3] :
-				    NULL);
+			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();
@@ -3084,6 +3144,7 @@
 			exit(0);
 		}
 		ctx.ca_fname = argv[optind + 2];
+		wpa_printf(MSG_DEBUG, "Calling cmd_prov from main");
 		cmd_prov(&ctx, argv[optind + 1]);
 	} else if (strcmp(argv[optind], "sim_prov") == 0) {
 		if (argc - optind < 2) {
diff --git a/hs20/client/spp_client.c b/hs20/client/spp_client.c
index 302a050..c619541 100644
--- a/hs20/client/spp_client.c
+++ b/hs20/client/spp_client.c
@@ -21,6 +21,8 @@
 #include "osu_client.h"
 
 
+extern const char *spp_xsd_fname;
+
 static int hs20_spp_update_response(struct hs20_osu_client *ctx,
 				    const char *session_id,
 				    const char *spp_status,
@@ -59,7 +61,7 @@
 		return -1;
 	}
 
-	ret = xml_validate(xctx, node, "spp.xsd", &err);
+	ret = xml_validate(xctx, node, spp_xsd_fname, &err);
 	if (ret < 0) {
 		wpa_printf(MSG_INFO, "XML schema validation error(s)\n%s", err);
 		write_summary(ctx, "SPP XML schema validation failed");
@@ -77,9 +79,14 @@
 	xml_node_t *fnode, *tnds;
 	char *str;
 
+	errno = 0;
 	fnode = node_from_file(ctx, fname);
-	if (!fnode)
+	if (!fnode) {
+		wpa_printf(MSG_ERROR,
+			   "Failed to create XML node from file: %s, possible error: %s",
+			   fname, strerror(errno));
 		return;
+	}
 	tnds = mo_to_tnds(ctx, fnode, 0, urn, "syncml:dmddf1.2");
 	xml_node_free(ctx, fnode);
 	if (!tnds)
@@ -952,7 +959,9 @@
 		return -1;
 	}
 
-	wpa_printf(MSG_INFO, "Credential provisioning requested");
+	wpa_printf(MSG_INFO,
+		   "Credential provisioning requested - URL: %s ca_fname: %s",
+		   url, ctx->ca_fname ? ctx->ca_fname : "N/A");
 
 	os_free(ctx->server_url);
 	ctx->server_url = os_strdup(url);
diff --git a/hs20/server/Makefile b/hs20/server/Makefile
index 587633b..248ed5c 100644
--- a/hs20/server/Makefile
+++ b/hs20/server/Makefile
@@ -12,6 +12,7 @@
 CFLAGS = -MMD -O2 -Wall -g
 endif
 
+CFLAGS += -I../../src
 CFLAGS += -I../../src/utils
 CFLAGS += -I../../src/crypto
 
diff --git a/hs20/server/ca/clean.sh b/hs20/server/ca/clean.sh
old mode 100644
new mode 100755
index c69a1f5..c72dcbd
--- a/hs20/server/ca/clean.sh
+++ b/hs20/server/ca/clean.sh
@@ -5,6 +5,9 @@
 done
 
 rm -f openssl.cnf.tmp
-rm -r demoCA
+if [ -d demoCA ]; then
+    rm -r demoCA
+fi
 rm -f ca.pem logo.asn1 logo.der server.der ocsp-server-cache.der
+rm -f my-openssl.cnf my-openssl-root.cnf
 #rm -r rootCA
diff --git a/hs20/server/ca/openssl-root.cnf b/hs20/server/ca/openssl-root.cnf
index 5b220fe..5bc50be 100644
--- a/hs20/server/ca/openssl-root.cnf
+++ b/hs20/server/ca/openssl-root.cnf
@@ -69,8 +69,8 @@
 attributes		= req_attributes
 x509_extensions	= v3_ca	# The extentions to add to the self signed cert
 
-input_password = whatever
-output_password = whatever
+input_password = @PASSWORD@
+output_password = @PASSWORD@
 
 string_mask = utf8only
 
diff --git a/hs20/server/ca/openssl.cnf b/hs20/server/ca/openssl.cnf
index a939f08..6141013 100644
--- a/hs20/server/ca/openssl.cnf
+++ b/hs20/server/ca/openssl.cnf
@@ -80,8 +80,8 @@
 attributes		= req_attributes
 x509_extensions	= v3_ca	# The extentions to add to the self signed cert
 
-input_password = whatever
-output_password = whatever
+input_password = @PASSWORD@
+output_password = @PASSWORD@
 
 string_mask = utf8only
 
@@ -95,7 +95,7 @@
 localityName_default		= Tuusula
 
 0.organizationName		= Organization Name (eg, company)
-0.organizationName_default	= w1.fi
+0.organizationName_default	= @DOMAIN@
 
 ##organizationalUnitName		= Organizational Unit Name (eg, section)
 #organizationalUnitName_default	=
@@ -117,10 +117,10 @@
 authorityKeyIdentifier=keyid:always,issuer
 basicConstraints = critical, CA:true, pathlen:0
 keyUsage = critical, cRLSign, keyCertSign
-authorityInfoAccess = OCSP;URI:http://osu.w1.fi:8888/
+authorityInfoAccess = OCSP;URI:@OCSP_URI@
 # For SP intermediate CA
 #subjectAltName=critical,otherName:1.3.6.1.4.1.40808.1.1.1;UTF8String:engExample OSU
-#nameConstraints=permitted;DNS:.w1.fi
+#nameConstraints=permitted;DNS:.@DOMAIN@
 #1.3.6.1.5.5.7.1.12=ASN1:SEQUENCE:LogotypeExtn
 
 [ v3_osu_server ]
@@ -150,16 +150,16 @@
 #value2=SEQUENCE:HashAlgAndValueSHA1
 [HashAlgAndValueSHA256]
 hashAlg=SEQUENCE:sha256_alg
-hashValue=FORMAT:HEX,OCTETSTRING:4532f7ec36424381617c03c6ce87b55a51d6e7177ffafda243cebf280a68954d
+hashValue=FORMAT:HEX,OCTETSTRING:@LOGO_HASH256@
 [HashAlgAndValueSHA1]
 hashAlg=SEQUENCE:sha1_alg
-hashValue=FORMAT:HEX,OCTETSTRING:5e1d5085676eede6b02da14d31c523ec20ffba0b
+hashValue=FORMAT:HEX,OCTETSTRING:@LOGO_HASH1@
 [sha256_alg]
 algorithm=OID:sha256
 [sha1_alg]
 algorithm=OID:sha1
 [URI]
-uri=IA5STRING:http://osu.w1.fi/w1fi_logo.png
+uri=IA5STRING:@LOGO_URI@
 [LogotypeImageInfo]
 # default value color(1), component optional
 #type=IMP:0,INTEGER:1
@@ -184,7 +184,7 @@
 basicConstraints=CA:FALSE
 subjectKeyIdentifier=hash
 authorityKeyIdentifier=keyid,issuer
-authorityInfoAccess = OCSP;URI:http://osu.w1.fi:8888/
+authorityInfoAccess = OCSP;URI:@OCSP_URI@
 #@ALTNAME@
 extendedKeyUsage = clientAuth
 
@@ -194,7 +194,7 @@
 basicConstraints=critical, CA:FALSE
 subjectKeyIdentifier=hash
 authorityKeyIdentifier=keyid,issuer
-authorityInfoAccess = OCSP;URI:http://osu.w1.fi:8888/
+authorityInfoAccess = OCSP;URI:@OCSP_URI@
 #@ALTNAME@
 extendedKeyUsage = critical, serverAuth
 keyUsage = critical, keyEncipherment
diff --git a/hs20/server/ca/setup.sh b/hs20/server/ca/setup.sh
old mode 100644
new mode 100755
index f61bf73..78abccc
--- a/hs20/server/ca/setup.sh
+++ b/hs20/server/ca/setup.sh
@@ -5,6 +5,67 @@
 fi
 export OPENSSL_CONF=$PWD/openssl.cnf
 PASS=whatever
+if [ -z "$DOMAIN" ]; then
+    DOMAIN=w1.fi
+fi
+COMPANY=w1.fi
+OPER_ENG="engw1.fi TESTING USE"
+OPER_FI="finw1.fi TESTIKÄYTTÖ"
+CNR="Hotspot 2.0 Trust Root CA - 99"
+CNO="ocsp.$DOMAIN"
+CNV="osu-revoked.$DOMAIN"
+CNOC="osu-client.$DOMAIN"
+OSU_SERVER_HOSTNAME="osu.$DOMAIN"
+DEBUG=0
+OCSP_URI="http://$CNO:8888/"
+LOGO_URI="http://osu.w1.fi/w1fi_logo.png"
+LOGO_HASH256="4532f7ec36424381617c03c6ce87b55a51d6e7177ffafda243cebf280a68954d"
+LOGO_HASH1="5e1d5085676eede6b02da14d31c523ec20ffba0b"
+
+# Command line overrides
+USAGE=$( cat <<EOF
+Usage:\n
+# -c:  Company name, used to generate Subject name CN for Intermediate CA\n
+# -C:  Subject name CN of the Root CA ($CNR)\n
+# -D:  Enable debugging (set -x, etc)\n
+# -g:  Logo sha1 hash ($LOGO_HASH1)\n
+# -G:  Logo sha256 hash ($LOGO_HASH256)\n
+# -h:  Show this help message\n
+# -l:  Logo URI ($LOGO_URI)\n
+# -m:  Domain ($DOMAIN)\n
+# -o:  Subject name CN for OSU-Client Server ($CNOC)\n
+# -O:  Subject name CN for OCSP Server ($CNO)\n
+# -p:  passphrase for private keys ($PASS)\n
+# -r:  Operator-english ($OPER_ENG)\n
+# -R:  Operator-finish ($OPER_FI)\n
+# -S:  OSU Server name ($OSU_SERVER_HOSTNAME)\n
+# -u:  OCSP-URI ($OCSP_URI)\n
+# -V:  Subject name CN for OSU-Revoked Server ($CNV)\n
+EOF
+)
+
+while getopts "c:C:Dg:G:l:m:o:O:p:r:R:S:u:V:h" flag
+  do
+  case $flag in
+      c) COMPANY=$OPTARG;;
+      C) CNR=$OPTARG;;
+      D) DEBUG=1;;
+      g) LOGO_HASH1=$OPTARG;;
+      G) LOGO_HASH256=$OPTARG;;
+      h) echo -e $USAGE; exit 0;;
+      l) LOGO_URI=$OPTARG;;
+      m) DOMAIN=$OPTARG;;
+      o) CNOC=$OPTARG;;
+      O) CNO=$OPTARG;;
+      p) PASS=$OPTARG;;
+      r) OPER_ENG=$OPTARG;;
+      R) OPER_FI=$OPTARG;;
+      S) OSU_SERVER_HOSTNAME=$OPTARG;;
+      u) OCSP_URI=$OPTARG;;
+      V) CNV=$OPTARG;;
+      *) echo "Unknown flag: $flag"; echo -e $USAGE; exit 1;;
+  esac
+done
 
 fail()
 {
@@ -16,7 +77,25 @@
 echo "---[ Root CA ]----------------------------------------------------------"
 echo
 
-cat openssl-root.cnf | sed "s/#@CN@/commonName_default = Hotspot 2.0 Trust Root CA - 99/" > openssl.cnf.tmp
+if [ $DEBUG = 1 ]
+then
+    set -x
+fi
+
+# Set the passphrase and some other common config accordingly.
+cat openssl-root.cnf | sed "s/@PASSWORD@/$PASS/" \
+ > my-openssl-root.cnf
+
+cat openssl.cnf | sed "s/@PASSWORD@/$PASS/" |
+sed "s,@OCSP_URI@,$OCSP_URI," |
+sed "s,@LOGO_URI@,$LOGO_URI," |
+sed "s,@LOGO_HASH1@,$LOGO_HASH1," |
+sed "s,@LOGO_HASH256@,$LOGO_HASH256," |
+sed "s/@DOMAIN@/$DOMAIN/" \
+ > my-openssl.cnf
+
+
+cat my-openssl-root.cnf | sed "s/#@CN@/commonName_default = $CNR/" > openssl.cnf.tmp
 mkdir -p rootCA/certs rootCA/crl rootCA/newcerts rootCA/private
 touch rootCA/index.txt
 if [ -e rootCA/private/cakey.pem ]; then
@@ -26,6 +105,8 @@
     $OPENSSL req -config openssl.cnf.tmp -batch -new -newkey rsa:4096 -keyout rootCA/private/cakey.pem -out rootCA/careq.pem || fail "Failed to generate Root CA private key"
     echo " * Sign Root CA certificate"
     $OPENSSL ca -config openssl.cnf.tmp -md sha256 -create_serial -out rootCA/cacert.pem -days 10957 -batch -keyfile rootCA/private/cakey.pem -passin pass:$PASS -selfsign -extensions v3_ca -outdir rootCA/newcerts -infiles rootCA/careq.pem || fail "Failed to sign Root CA certificate"
+    $OPENSSL x509 -in rootCA/cacert.pem -out rootCA/cacert.der -outform DER || fail "Failed to create rootCA DER"
+    sha256sum rootCA/cacert.der > rootCA/cacert.fingerprint || fail "Failed to create rootCA fingerprint"
 fi
 if [ ! -e rootCA/crlnumber ]; then
     echo 00 > rootCA/crlnumber
@@ -35,7 +116,7 @@
 echo "---[ Intermediate CA ]--------------------------------------------------"
 echo
 
-cat openssl.cnf | sed "s/#@CN@/commonName_default = w1.fi Hotspot 2.0 Intermediate CA/" > openssl.cnf.tmp
+cat my-openssl.cnf | sed "s/#@CN@/commonName_default = $COMPANY Hotspot 2.0 Intermediate CA/" > openssl.cnf.tmp
 mkdir -p demoCA/certs demoCA/crl demoCA/newcerts demoCA/private
 touch demoCA/index.txt
 if [ -e demoCA/private/cakey.pem ]; then
@@ -47,6 +128,8 @@
     $OPENSSL ca -config openssl.cnf.tmp -md sha256 -create_serial -out demoCA/cacert.pem -days 3652 -batch -keyfile rootCA/private/cakey.pem -cert rootCA/cacert.pem -passin pass:$PASS -extensions v3_ca -infiles demoCA/careq.pem || fail "Failed to sign Intermediate CA certificate"
     # horrible from security view point, but for testing purposes since OCSP responder does not seem to support -passin
     openssl rsa -in demoCA/private/cakey.pem -out demoCA/private/cakey-plain.pem -passin pass:$PASS
+    $OPENSSL x509 -in demoCA/cacert.pem -out demoCA/cacert.der -outform DER || fail "Failed to create demoCA DER."
+    sha256sum demoCA/cacert.der > demoCA/cacert.fingerprint || fail "Failed to create demoCA fingerprint"
 fi
 if [ ! -e demoCA/crlnumber ]; then
     echo 00 > demoCA/crlnumber
@@ -56,45 +139,46 @@
 echo "OCSP responder"
 echo
 
-cat openssl.cnf | sed "s/#@CN@/commonName_default = ocsp.w1.fi/" > openssl.cnf.tmp
+cat my-openssl.cnf | sed "s/#@CN@/commonName_default = $CNO/" > openssl.cnf.tmp
 $OPENSSL req -config $PWD/openssl.cnf.tmp -batch -new -newkey rsa:2048 -nodes -out ocsp.csr -keyout ocsp.key -extensions v3_OCSP
-$OPENSSL ca -config $PWD/openssl.cnf.tmp -batch -md sha256 -keyfile demoCA/private/cakey.pem -passin pass:$PASS -in ocsp.csr -out ocsp.pem -days 730 -extensions v3_OCSP
+$OPENSSL ca -config $PWD/openssl.cnf.tmp -batch -md sha256 -keyfile demoCA/private/cakey.pem -passin pass:$PASS -in ocsp.csr -out ocsp.pem -days 730 -extensions v3_OCSP || fail "Could not generate ocsp.pem"
 
 echo
 echo "---[ Server - to be revoked ] ------------------------------------------"
 echo
 
-cat openssl.cnf | sed "s/#@CN@/commonName_default = osu-revoked.w1.fi/" > openssl.cnf.tmp
+cat my-openssl.cnf | sed "s/#@CN@/commonName_default = $CNV/" > openssl.cnf.tmp
 $OPENSSL req -config $PWD/openssl.cnf.tmp -batch -new -newkey rsa:2048 -nodes -out server-revoked.csr -keyout server-revoked.key
 $OPENSSL ca -config $PWD/openssl.cnf.tmp -batch -md sha256 -in server-revoked.csr -out server-revoked.pem -key $PASS -days 730 -extensions ext_server
 $OPENSSL ca -revoke server-revoked.pem -key $PASS
 
 echo
 echo "---[ Server - with client ext key use ] ---------------------------------"
+echo "---[ Only used for negative-testing for OSU-client implementation ] -----"
 echo
 
-cat openssl.cnf | sed "s/#@CN@/commonName_default = osu-client.w1.fi/" > openssl.cnf.tmp
-$OPENSSL req -config $PWD/openssl.cnf.tmp -batch -new -newkey rsa:2048 -nodes -out server-client.csr -keyout server-client.key
-$OPENSSL ca -config $PWD/openssl.cnf.tmp -batch -md sha256 -in server-client.csr -out server-client.pem -key $PASS -days 730 -extensions ext_client
+cat my-openssl.cnf | sed "s/#@CN@/commonName_default = $CNOC/" > openssl.cnf.tmp
+$OPENSSL req -config $PWD/openssl.cnf.tmp -batch -new -newkey rsa:2048 -nodes -out server-client.csr -keyout server-client.key || fail "Could not create server-client.key"
+$OPENSSL ca -config $PWD/openssl.cnf.tmp -batch -md sha256 -in server-client.csr -out server-client.pem -key $PASS -days 730 -extensions ext_client || fail "Could not create server-client.pem"
 
 echo
 echo "---[ User ]-------------------------------------------------------------"
 echo
 
-cat openssl.cnf | sed "s/#@CN@/commonName_default = User/" > openssl.cnf.tmp
-$OPENSSL req -config $PWD/openssl.cnf.tmp -batch -new -newkey rsa:2048 -nodes -out user.csr -keyout user.key
-$OPENSSL ca -config $PWD/openssl.cnf.tmp -batch -md sha256 -in user.csr -out user.pem -key $PASS -days 730 -extensions ext_client
+cat my-openssl.cnf | sed "s/#@CN@/commonName_default = User/" > openssl.cnf.tmp
+$OPENSSL req -config $PWD/openssl.cnf.tmp -batch -new -newkey rsa:2048 -nodes -out user.csr -keyout user.key || fail "Could not create user.key"
+$OPENSSL ca -config $PWD/openssl.cnf.tmp -batch -md sha256 -in user.csr -out user.pem -key $PASS -days 730 -extensions ext_client || fail "Could not create user.pem"
 
 echo
 echo "---[ Server ]-----------------------------------------------------------"
 echo
 
-ALT="DNS:osu.w1.fi"
-ALT="$ALT,otherName:1.3.6.1.4.1.40808.1.1.1;UTF8String:engw1.fi TESTING USE"
-ALT="$ALT,otherName:1.3.6.1.4.1.40808.1.1.1;UTF8String:finw1.fi TESTIKÄYTTÖ"
+ALT="DNS:$OSU_SERVER_HOSTNAME"
+ALT="$ALT,otherName:1.3.6.1.4.1.40808.1.1.1;UTF8String:$OPER_ENG"
+ALT="$ALT,otherName:1.3.6.1.4.1.40808.1.1.1;UTF8String:$OPER_FI"
 
-cat openssl.cnf |
-	sed "s/#@CN@/commonName_default = osu.w1.fi/" |
+cat my-openssl.cnf |
+	sed "s/#@CN@/commonName_default = $OSU_SERVER_HOSTNAME/" |
 	sed "s/^##organizationalUnitName/organizationalUnitName/" |
 	sed "s/#@OU@/organizationalUnitName_default = Hotspot 2.0 Online Sign Up Server/" |
 	sed "s/#@ALTNAME@/subjectAltName=critical,$ALT/" \
@@ -113,7 +197,7 @@
 echo "---[ CRL ]---------------------------------------------------------------"
 echo
 
-$OPENSSL ca -config $PWD/openssl.cnf -gencrl -md sha256 -out demoCA/crl/crl.pem -passin pass:$PASS
+$OPENSSL ca -config $PWD/my-openssl.cnf -gencrl -md sha256 -out demoCA/crl/crl.pem -passin pass:$PASS
 
 echo
 echo "---[ Verify ]------------------------------------------------------------"
diff --git a/hs20/server/hs20-osu-server.txt b/hs20/server/hs20-osu-server.txt
index 80985f7..001d6f2 100644
--- a/hs20/server/hs20-osu-server.txt
+++ b/hs20/server/hs20-osu-server.txt
@@ -100,6 +100,21 @@
 # the examples as-is for initial testing).
 cp -r www /home/user/hs20-server
 
+# Build local keys and certs
+cd ca
+# Display help options.
+./setup.sh -h
+
+# Remove old keys, fill in appropriate values, and generate your keys.
+# For instance:
+./clean.sh
+rm -fr rootCA"
+old_hostname=myserver.local
+./setup.sh -C "Hotspot 2.0 Trust Root CA - CT" -d $old_hostname \
+   -I "Hotspot 2.0 Intermediate CA - CT" -o $old_hostname-osu-client \
+   -O $old_hostname-oscp -p lanforge -S $old_hostname \
+   -V $old_hostname-osu-revoked \
+   -m local -u http://$old_hostname:8888/
 
 # Configure subscription policies
 mkdir -p /home/user/hs20-server/spp/policy
@@ -156,6 +171,50 @@
 ./hostapd -B as-sql.conf
 
 
+OSEN RADIUS server configuration notes
+
+The OSEN RADIUS server config file should have the 'ocsp_stapling_response'
+configuration in it. For example:
+
+# hostapd-radius config for the radius used by the OSEN AP
+interface=eth0#0
+driver=none
+logger_syslog=-1
+logger_syslog_level=2
+logger_stdout=-1
+logger_stdout_level=2
+ctrl_interface=/var/run/hostapd
+ctrl_interface_group=0
+eap_server=1
+eap_user_file=/home/user/hs20-server/AS/hostapd-osen.eap_user
+server_id=ben-ota-2-osen
+radius_server_auth_port=1811
+radius_server_clients=/home/user/hs20-server/AS/hostap.radius_clients
+
+ca_cert=/home/user/hs20-server/ca/ca.pem
+server_cert=/home/user/hs20-server/ca/server.pem
+private_key=/home/user/hs20-server/ca/server.key
+private_key_passwd=whatever
+
+ocsp_stapling_response=/home/user/hs20-server/ca/ocsp-server-cache.der
+
+The /home/user/hs20-server/AS/hostapd-osen.eap_user file should look
+similar to this, and should coorelate with the osu_nai entry in
+the non-OSEN VAP config file.  For instance:
+
+# cat hostapd-osen.eap_user
+# For OSEN authentication (Hotspot 2.0 Release 2)
+"osen@w1.fi"      WFA-UNAUTH-TLS
+
+
+# Run OCSP server:
+cd /home/user/hs20-server/ca
+./ocsp-responder.sh&
+
+# Update cache (This should be run periodically)
+./ocsp-update-cache.sh
+
+
 Configure web server
 --------------------
 
@@ -172,6 +231,8 @@
         </Directory>
 
 Update SSL configuration to use the OSU server certificate/key.
+They keys and certs are called 'server.key' and 'server.pem' from
+ca/setup.sh.
 
 Enable default-ssl site and restart Apache2:
   sudo a2ensite default-ssl
diff --git a/hs20/server/spp_server.c b/hs20/server/spp_server.c
index 4d77d0e..33e3fa1 100644
--- a/hs20/server/spp_server.c
+++ b/hs20/server/spp_server.c
@@ -103,6 +103,28 @@
 }
 
 
+static void db_update_session_machine_managed(struct hs20_svc *ctx,
+					      const char *user,
+					      const char *realm,
+					      const char *sessionid,
+					      const int pw_mm)
+{
+	char *sql;
+
+	sql = sqlite3_mprintf("UPDATE sessions SET machine_managed=%Q WHERE id=%Q AND user=%Q AND realm=%Q",
+			      pw_mm ? "1" : "0", sessionid, user, realm);
+	if (sql == NULL)
+		return;
+	debug_print(ctx, 1, "DB: %s", sql);
+	if (sqlite3_exec(ctx->db, sql, NULL, NULL, NULL) != SQLITE_OK) {
+		debug_print(ctx, 1,
+			    "Failed to update session machine_managed: %s",
+			    sqlite3_errmsg(ctx->db));
+	}
+	sqlite3_free(sql);
+}
+
+
 static void db_add_session_pps(struct hs20_svc *ctx, const char *user,
 			       const char *realm, const char *sessionid,
 			       xml_node_t *node)
@@ -1378,6 +1400,11 @@
 
 	debug_print(ctx, 1, "Request DB subscription registration on success "
 		    "notification");
+	if (machine_managed) {
+		db_update_session_password(ctx, user, realm, session_id, pw);
+		db_update_session_machine_managed(ctx, user, realm, session_id,
+						  machine_managed);
+	}
 	db_add_session_pps(ctx, user, realm, session_id, pps);
 
 	hs20_eventlog_node(ctx, user, realm, session_id,
@@ -2169,7 +2196,9 @@
 		session_id = xml_node_get_attr_value_ns(ctx->xml, node,
 							SPP_NS_URI,
 							"sessionID");
-		debug_print(ctx, 1, "SPP message failed validation");
+		debug_print(ctx, 1,
+			    "SPP message failed validation, xsd file: %s  xml-error: %s",
+			    fname, xml_err);
 		hs20_eventlog_node(ctx, auth_user, auth_realm, session_id,
 				   "SPP message failed validation", node);
 		hs20_eventlog(ctx, auth_user, auth_realm, session_id,
diff --git a/hs20/server/www/signup.php b/hs20/server/www/signup.php
index a626704..aeb2f68 100644
--- a/hs20/server/www/signup.php
+++ b/hs20/server/www/signup.php
@@ -17,7 +17,7 @@
 
 $row = $db->query("SELECT realm FROM sessions WHERE id='$id'")->fetch();
 if ($row == false) {
-   die("Session not found");
+   die("Session not found for id: $id");
 }
 $realm = $row['realm'];
 
diff --git a/src/ap/Makefile b/src/ap/Makefile
index adfd3df..98788fe 100644
--- a/src/ap/Makefile
+++ b/src/ap/Makefile
@@ -1,8 +1,67 @@
-all:
-	@echo Nothing to be made.
+all: libap.a
 
 clean:
-	rm -f *~ *.o *.d *.gcno *.gcda *.gcov
+	rm -f *~ *.o *.d *.gcno *.gcda *.gcov libap.a
 
 install:
 	@echo Nothing to be made.
+
+include ../lib.rules
+
+CFLAGS += -DHOSTAPD
+CFLAGS += -DNEED_AP_MLME
+CFLAGS += -DCONFIG_HS20
+CFLAGS += -DCONFIG_INTERWORKING
+CFLAGS += -DCONFIG_IEEE80211R
+CFLAGS += -DCONFIG_IEEE80211W
+CFLAGS += -DCONFIG_WPS
+CFLAGS += -DCONFIG_PROXYARP
+CFLAGS += -DCONFIG_IAPP
+
+LIB_OBJS= \
+	accounting.o \
+	ap_config.o \
+	ap_drv_ops.o \
+	ap_list.o \
+	ap_mlme.o \
+	authsrv.o \
+	beacon.o \
+	bss_load.o \
+	ctrl_iface_ap.o \
+	dfs.o \
+	dhcp_snoop.o \
+	drv_callbacks.o \
+	eap_user_db.o \
+	gas_serv.o \
+	hostapd.o \
+	hs20.o \
+	hw_features.o \
+	iapp.o \
+	ieee802_11_auth.o \
+	ieee802_11.o \
+	ieee802_11_ht.o \
+	ieee802_11_shared.o \
+	ieee802_11_vht.o \
+	ieee802_1x.o \
+	ndisc_snoop.o \
+	p2p_hostapd.o \
+	peerkey_auth.o \
+	pmksa_cache_auth.o \
+	preauth_auth.o \
+	sta_info.o \
+	tkip_countermeasures.o \
+	utils.o \
+	vlan_init.o \
+	wmm.o \
+	wnm_ap.o \
+	wpa_auth.o \
+	wpa_auth_ft.o \
+	wpa_auth_glue.o \
+	wpa_auth_ie.o \
+	wps_hostapd.o \
+	x_snoop.o
+
+libap.a: $(LIB_OBJS)
+	$(AR) crT $@ $?
+
+-include $(OBJS:%.o=%.d)
diff --git a/src/ap/accounting.c b/src/ap/accounting.c
index 6290d3f..a096de4 100644
--- a/src/ap/accounting.c
+++ b/src/ap/accounting.c
@@ -10,6 +10,8 @@
 
 #include "utils/common.h"
 #include "utils/eloop.h"
+#include "eapol_auth/eapol_auth_sm.h"
+#include "eapol_auth/eapol_auth_sm_i.h"
 #include "radius/radius.h"
 #include "radius/radius_client.h"
 #include "hostapd.h"
@@ -50,12 +52,19 @@
 	if (sta) {
 		radius_msg_make_authenticator(msg, (u8 *) sta, sizeof(*sta));
 
-		os_snprintf(buf, sizeof(buf), "%08X-%08X",
-			    sta->acct_session_id_hi, sta->acct_session_id_lo);
-		if (!radius_msg_add_attr(msg, RADIUS_ATTR_ACCT_SESSION_ID,
-					 (u8 *) buf, os_strlen(buf))) {
-			wpa_printf(MSG_INFO, "Could not add Acct-Session-Id");
-			goto fail;
+		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));
@@ -450,10 +459,14 @@
 {
 	struct os_time now;
 
-	/* Acct-Session-Id should be unique over reboots. If reliable clock is
-	 * not available, this could be replaced with reboot counter, etc. */
+	/* 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);
-	hapd->acct_session_id_hi = now.sec;
+	if (os_get_random((u8 *) &hapd->acct_session_id_hi,
+			  sizeof(hapd->acct_session_id_hi)) < 0)
+		hapd->acct_session_id_hi = now.sec;
+	hapd->acct_session_id_hi ^= now.usec;
 
 	if (radius_client_register(hapd->radius, RADIUS_ACCT,
 				   accounting_receive, hapd))
@@ -466,7 +479,7 @@
 
 
 /**
- * accounting_deinit: Deinitilize accounting
+ * accounting_deinit: Deinitialize accounting
  * @hapd: hostapd BSS data
  */
 void accounting_deinit(struct hostapd_data *hapd)
diff --git a/src/ap/acs.c b/src/ap/acs.c
index b94b8a4..03d797f 100644
--- a/src/ap/acs.c
+++ b/src/ap/acs.c
@@ -242,6 +242,7 @@
 
 
 static int acs_request_scan(struct hostapd_iface *iface);
+static int acs_survey_is_sufficient(struct freq_survey *survey);
 
 
 static void acs_clean_chan_surveys(struct hostapd_channel_data *chan)
@@ -328,6 +329,7 @@
 	struct freq_survey *survey;
 	unsigned int i = 0;
 	long double int_factor = 0;
+	unsigned count = 0;
 
 	if (dl_list_empty(&chan->survey_list))
 		return;
@@ -339,18 +341,27 @@
 
 	dl_list_for_each(survey, &chan->survey_list, struct freq_survey, list)
 	{
+		i++;
+
+		if (!acs_survey_is_sufficient(survey)) {
+			wpa_printf(MSG_DEBUG, "ACS: %d: insufficient data", i);
+			continue;
+		}
+
+		count++;
 		int_factor = acs_survey_interference_factor(survey,
 							    iface->lowest_nf);
 		chan->interference_factor += int_factor;
 		wpa_printf(MSG_DEBUG, "ACS: %d: min_nf=%d interference_factor=%Lg nf=%d time=%lu busy=%lu rx=%lu",
-			   ++i, chan->min_nf, int_factor,
+			   i, chan->min_nf, int_factor,
 			   survey->nf, (unsigned long) survey->channel_time,
 			   (unsigned long) survey->channel_time_busy,
 			   (unsigned long) survey->channel_time_rx);
 	}
 
-	chan->interference_factor = chan->interference_factor /
-		dl_list_len(&chan->survey_list);
+	if (!count)
+		return;
+	chan->interference_factor /= count;
 }
 
 
@@ -384,18 +395,19 @@
 static int acs_survey_is_sufficient(struct freq_survey *survey)
 {
 	if (!(survey->filled & SURVEY_HAS_NF)) {
-		wpa_printf(MSG_ERROR, "ACS: Survey is missing noise floor");
+		wpa_printf(MSG_INFO, "ACS: Survey is missing noise floor");
 		return 0;
 	}
 
 	if (!(survey->filled & SURVEY_HAS_CHAN_TIME)) {
-		wpa_printf(MSG_ERROR, "ACS: Survey is missing channel time");
+		wpa_printf(MSG_INFO, "ACS: Survey is missing channel time");
 		return 0;
 	}
 
 	if (!(survey->filled & SURVEY_HAS_CHAN_TIME_BUSY) &&
 	    !(survey->filled & SURVEY_HAS_CHAN_TIME_RX)) {
-		wpa_printf(MSG_ERROR, "ACS: Survey is missing RX and busy time (at least one is required)");
+		wpa_printf(MSG_INFO,
+			   "ACS: Survey is missing RX and busy time (at least one is required)");
 		return 0;
 	}
 
@@ -406,18 +418,27 @@
 static int acs_survey_list_is_sufficient(struct hostapd_channel_data *chan)
 {
 	struct freq_survey *survey;
+	int ret = -1;
 
 	dl_list_for_each(survey, &chan->survey_list, struct freq_survey, list)
 	{
-		if (!acs_survey_is_sufficient(survey)) {
-			wpa_printf(MSG_ERROR, "ACS: Channel %d has insufficient survey data",
-				   chan->chan);
-			return 0;
+		if (acs_survey_is_sufficient(survey)) {
+			ret = 1;
+			break;
 		}
+		ret = 0;
 	}
 
-	return 1;
+	if (ret == -1)
+		ret = 1; /* no survey list entries */
 
+	if (!ret) {
+		wpa_printf(MSG_INFO,
+			   "ACS: Channel %d has insufficient survey data",
+			   chan->chan);
+	}
+
+	return ret;
 }
 
 
@@ -455,6 +476,16 @@
 }
 
 
+static int is_in_chanlist(struct hostapd_iface *iface,
+			  struct hostapd_channel_data *chan)
+{
+	if (!iface->conf->acs_ch_list.num)
+		return 1;
+
+	return freq_range_list_includes(&iface->conf->acs_ch_list, chan->chan);
+}
+
+
 static void acs_survey_all_chans_intereference_factor(
 	struct hostapd_iface *iface)
 {
@@ -467,6 +498,9 @@
 		if (!acs_usable_chan(chan))
 			continue;
 
+		if (!is_in_chanlist(iface, chan))
+			continue;
+
 		wpa_printf(MSG_DEBUG, "ACS: Survey analysis for channel %d (%d MHz)",
 			   chan->chan, chan->freq);
 
@@ -498,6 +532,36 @@
 }
 
 
+static int is_24ghz_mode(enum hostapd_hw_mode mode)
+{
+	return mode == HOSTAPD_MODE_IEEE80211B ||
+		mode == HOSTAPD_MODE_IEEE80211G;
+}
+
+
+static int is_common_24ghz_chan(int chan)
+{
+	return chan == 1 || chan == 6 || chan == 11;
+}
+
+
+#ifndef ACS_ADJ_WEIGHT
+#define ACS_ADJ_WEIGHT 0.85
+#endif /* ACS_ADJ_WEIGHT */
+
+#ifndef ACS_NEXT_ADJ_WEIGHT
+#define ACS_NEXT_ADJ_WEIGHT 0.55
+#endif /* ACS_NEXT_ADJ_WEIGHT */
+
+#ifndef ACS_24GHZ_PREFER_1_6_11
+/*
+ * Select commonly used channels 1, 6, 11 by default even if a neighboring
+ * channel has a smaller interference factor as long as it is not better by more
+ * than this multiplier.
+ */
+#define ACS_24GHZ_PREFER_1_6_11 0.8
+#endif /* ACS_24GHZ_PREFER_1_6_11 */
+
 /*
  * At this point it's assumed chan->interface_factor has been computed.
  * This function should be reusable regardless of interference computation
@@ -512,6 +576,7 @@
 	long double factor, ideal_factor = 0;
 	int i, j;
 	int n_chans = 1;
+	unsigned int k;
 
 	/* TODO: HT40- support */
 
@@ -538,11 +603,16 @@
 		   -1);
 
 	for (i = 0; i < iface->current_mode->num_channels; i++) {
+		double total_weight;
+		struct acs_bias *bias, tmp_bias;
+
 		chan = &iface->current_mode->channels[i];
 
 		if (chan->flag & HOSTAPD_CHAN_DISABLED)
 			continue;
 
+		if (!is_in_chanlist(iface, chan))
+			continue;
 
 		/* HT40 on 5 GHz has a limited set of primary channels as per
 		 * 11n Annex J */
@@ -567,14 +637,17 @@
 		factor = 0;
 		if (acs_usable_chan(chan))
 			factor = chan->interference_factor;
+		total_weight = 1;
 
 		for (j = 1; j < n_chans; j++) {
 			adj_chan = acs_find_chan(iface, chan->freq + (j * 20));
 			if (!adj_chan)
 				break;
 
-			if (acs_usable_chan(adj_chan))
+			if (acs_usable_chan(adj_chan)) {
 				factor += adj_chan->interference_factor;
+				total_weight += 1;
+			}
 		}
 
 		if (j != n_chans) {
@@ -585,36 +658,69 @@
 
 		/* 2.4 GHz has overlapping 20 MHz channels. Include adjacent
 		 * channel interference factor. */
-		if (iface->current_mode->mode == HOSTAPD_MODE_IEEE80211B ||
-		    iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G) {
+		if (is_24ghz_mode(iface->current_mode->mode)) {
 			for (j = 0; j < n_chans; j++) {
-				/* TODO: perhaps a multiplier should be used
-				 * here? */
-
 				adj_chan = acs_find_chan(iface, chan->freq +
 							 (j * 20) - 5);
-				if (adj_chan && acs_usable_chan(adj_chan))
-					factor += adj_chan->interference_factor;
+				if (adj_chan && acs_usable_chan(adj_chan)) {
+					factor += ACS_ADJ_WEIGHT *
+						adj_chan->interference_factor;
+					total_weight += ACS_ADJ_WEIGHT;
+				}
 
 				adj_chan = acs_find_chan(iface, chan->freq +
 							 (j * 20) - 10);
-				if (adj_chan && acs_usable_chan(adj_chan))
-					factor += adj_chan->interference_factor;
+				if (adj_chan && acs_usable_chan(adj_chan)) {
+					factor += ACS_NEXT_ADJ_WEIGHT *
+						adj_chan->interference_factor;
+					total_weight += ACS_NEXT_ADJ_WEIGHT;
+				}
 
 				adj_chan = acs_find_chan(iface, chan->freq +
 							 (j * 20) + 5);
-				if (adj_chan && acs_usable_chan(adj_chan))
-					factor += adj_chan->interference_factor;
+				if (adj_chan && acs_usable_chan(adj_chan)) {
+					factor += ACS_ADJ_WEIGHT *
+						adj_chan->interference_factor;
+					total_weight += ACS_ADJ_WEIGHT;
+				}
 
 				adj_chan = acs_find_chan(iface, chan->freq +
 							 (j * 20) + 10);
-				if (adj_chan && acs_usable_chan(adj_chan))
-					factor += adj_chan->interference_factor;
+				if (adj_chan && acs_usable_chan(adj_chan)) {
+					factor += ACS_NEXT_ADJ_WEIGHT *
+						adj_chan->interference_factor;
+					total_weight += ACS_NEXT_ADJ_WEIGHT;
+				}
 			}
 		}
 
-		wpa_printf(MSG_DEBUG, "ACS:  * channel %d: total interference = %Lg",
-			   chan->chan, factor);
+		factor /= total_weight;
+
+		bias = NULL;
+		if (iface->conf->acs_chan_bias) {
+			for (k = 0; k < iface->conf->num_acs_chan_bias; k++) {
+				bias = &iface->conf->acs_chan_bias[k];
+				if (bias->channel == chan->chan)
+					break;
+				bias = NULL;
+			}
+		} else if (is_24ghz_mode(iface->current_mode->mode) &&
+			   is_common_24ghz_chan(chan->chan)) {
+			tmp_bias.channel = chan->chan;
+			tmp_bias.bias = ACS_24GHZ_PREFER_1_6_11;
+			bias = &tmp_bias;
+		}
+
+		if (bias) {
+			factor *= bias->bias;
+			wpa_printf(MSG_DEBUG,
+				   "ACS:  * channel %d: total interference = %Lg (%f bias)",
+				   chan->chan, factor, bias->bias);
+		} else {
+			wpa_printf(MSG_DEBUG,
+				   "ACS:  * channel %d: total interference = %Lg",
+				   chan->chan, factor);
+		}
 
 		if (acs_usable_chan(chan) &&
 		    (!ideal_chan || factor < ideal_factor)) {
@@ -788,6 +894,9 @@
 		if (chan->flag & HOSTAPD_CHAN_DISABLED)
 			continue;
 
+		if (!is_in_chanlist(iface, chan))
+			continue;
+
 		*freq++ = chan->freq;
 	}
 	*freq = 0;
@@ -816,6 +925,14 @@
 
 	wpa_printf(MSG_INFO, "ACS: Automatic channel selection started, this may take a bit");
 
+	if (iface->drv_flags & WPA_DRIVER_FLAGS_ACS_OFFLOAD) {
+		wpa_printf(MSG_INFO, "ACS: Offloading to driver");
+		err = hostapd_drv_do_acs(iface->bss[0]);
+		if (err)
+			return HOSTAPD_CHAN_INVALID;
+		return HOSTAPD_CHAN_ACS;
+	}
+
 	acs_cleanup(iface);
 
 	err = acs_request_scan(iface);
diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c
index c7da69e..455013a 100644
--- a/src/ap/ap_config.c
+++ b/src/ap/ap_config.c
@@ -181,6 +181,8 @@
 	conf->corrupt_gtk_rekey_mic_probability = 0.0;
 #endif /* CONFIG_TESTING_OPTIONS */
 
+	conf->acs = 0;
+	conf->acs_ch_list.num = 0;
 #ifdef CONFIG_ACS
 	conf->acs_num_scans = 5;
 #endif /* CONFIG_ACS */
@@ -394,20 +396,27 @@
 }
 
 
+void hostapd_config_clear_wpa_psk(struct hostapd_wpa_psk **l)
+{
+	struct hostapd_wpa_psk *psk, *tmp;
+
+	for (psk = *l; psk;) {
+		tmp = psk;
+		psk = psk->next;
+		bin_clear_free(tmp, sizeof(*tmp));
+	}
+	*l = NULL;
+}
+
+
 void hostapd_config_free_bss(struct hostapd_bss_config *conf)
 {
-	struct hostapd_wpa_psk *psk, *prev;
 	struct hostapd_eap_user *user, *prev_user;
 
 	if (conf == NULL)
 		return;
 
-	psk = conf->ssid.wpa_psk;
-	while (psk) {
-		prev = psk;
-		psk = psk->next;
-		bin_clear_free(prev, sizeof(*prev));
-	}
+	hostapd_config_clear_wpa_psk(&conf->ssid.wpa_psk);
 
 	str_clear_free(conf->ssid.wpa_passphrase);
 	os_free(conf->ssid.wpa_psk_file);
@@ -425,6 +434,7 @@
 	os_free(conf->eap_user_sqlite);
 
 	os_free(conf->eap_req_id_text);
+	os_free(conf->erp_domain);
 	os_free(conf->accept_mac);
 	os_free(conf->deny_mac);
 	os_free(conf->nas_identifier);
@@ -444,12 +454,12 @@
 	os_free(conf->private_key_passwd);
 	os_free(conf->ocsp_stapling_response);
 	os_free(conf->dh_file);
+	os_free(conf->openssl_ciphers);
 	os_free(conf->pac_opaque_encr_key);
 	os_free(conf->eap_fast_a_id);
 	os_free(conf->eap_fast_a_id_info);
 	os_free(conf->eap_sim_db);
 	os_free(conf->radius_server_clients);
-	os_free(conf->test_socket);
 	os_free(conf->radius);
 	os_free(conf->radius_das_shared_secret);
 	hostapd_config_free_vlan(conf);
@@ -495,6 +505,12 @@
 	os_free(conf->model_description);
 	os_free(conf->model_url);
 	os_free(conf->upc);
+	{
+		unsigned int i;
+
+		for (i = 0; i < MAX_WPS_VENDOR_EXTENSIONS; i++)
+			wpabuf_free(conf->wps_vendor_ext[i]);
+	}
 	wpabuf_free(conf->wps_nfc_dh_pubkey);
 	wpabuf_free(conf->wps_nfc_dh_privkey);
 	wpabuf_free(conf->wps_nfc_dev_pw);
@@ -565,7 +581,11 @@
 	os_free(conf->bss);
 	os_free(conf->supported_rates);
 	os_free(conf->basic_rates);
-	os_free(conf->chanlist);
+	os_free(conf->acs_ch_list.range);
+	os_free(conf->driver_params);
+#ifdef CONFIG_ACS
+	os_free(conf->acs_chan_bias);
+#endif /* CONFIG_ACS */
 
 	os_free(conf);
 }
@@ -799,9 +819,9 @@
 
 	if (full_config && bss->wps_state && bss->wpa &&
 	    (!(bss->wpa & 2) ||
-	     !(bss->rsn_pairwise & WPA_CIPHER_CCMP))) {
+	     !(bss->rsn_pairwise & (WPA_CIPHER_CCMP | WPA_CIPHER_GCMP)))) {
 		wpa_printf(MSG_INFO, "WPS: WPA/TKIP configuration without "
-			   "WPA2/CCMP forced WPS to be disabled");
+			   "WPA2/CCMP/GCMP forced WPS to be disabled");
 		bss->wps_state = 0;
 	}
 #endif /* CONFIG_WPS */
@@ -823,6 +843,29 @@
 }
 
 
+static int hostapd_config_check_cw(struct hostapd_config *conf, int queue)
+{
+	int tx_cwmin = conf->tx_queue[queue].cwmin;
+	int tx_cwmax = conf->tx_queue[queue].cwmax;
+	int ac_cwmin = conf->wmm_ac_params[queue].cwmin;
+	int ac_cwmax = conf->wmm_ac_params[queue].cwmax;
+
+	if (tx_cwmin > tx_cwmax) {
+		wpa_printf(MSG_ERROR,
+			   "Invalid TX queue cwMin/cwMax values. cwMin(%d) greater than cwMax(%d)",
+			   tx_cwmin, tx_cwmax);
+		return -1;
+	}
+	if (ac_cwmin > ac_cwmax) {
+		wpa_printf(MSG_ERROR,
+			   "Invalid WMM AC cwMin/cwMax values. cwMin(%d) greater than cwMax(%d)",
+			   ac_cwmin, ac_cwmax);
+		return -1;
+	}
+	return 0;
+}
+
+
 int hostapd_config_check(struct hostapd_config *conf, int full_config)
 {
 	size_t i;
@@ -852,6 +895,11 @@
 		return -1;
 	}
 
+	for (i = 0; i < NUM_TX_QUEUES; i++) {
+		if (hostapd_config_check_cw(conf, i))
+			return -1;
+	}
+
 	for (i = 0; i < conf->num_bss; i++) {
 		if (hostapd_config_check_bss(conf->bss[i], conf, full_config))
 			return -1;
@@ -888,12 +936,20 @@
 		int cipher = WPA_CIPHER_NONE;
 		bss->ssid.security_policy = SECURITY_IEEE_802_1X;
 		bss->ssid.wep.default_len = bss->default_wep_key_len;
-		if (bss->default_wep_key_len)
+		if (full_config && bss->default_wep_key_len) {
 			cipher = bss->default_wep_key_len >= 13 ?
 				WPA_CIPHER_WEP104 : WPA_CIPHER_WEP40;
+		} else if (full_config && bss->ssid.wep.keys_set) {
+			if (bss->ssid.wep.len[0] >= 13)
+				cipher = WPA_CIPHER_WEP104;
+			else
+				cipher = WPA_CIPHER_WEP40;
+		}
 		bss->wpa_group = cipher;
 		bss->wpa_pairwise = cipher;
 		bss->rsn_pairwise = cipher;
+		if (full_config)
+			bss->wpa_key_mgmt = WPA_KEY_MGMT_IEEE8021X_NO_WPA;
 	} else if (bss->ssid.wep.keys_set) {
 		int cipher = WPA_CIPHER_WEP40;
 		if (bss->ssid.wep.len[0] >= 13)
@@ -902,6 +958,8 @@
 		bss->wpa_group = cipher;
 		bss->wpa_pairwise = cipher;
 		bss->rsn_pairwise = cipher;
+		if (full_config)
+			bss->wpa_key_mgmt = WPA_KEY_MGMT_NONE;
 	} else if (bss->osen) {
 		bss->ssid.security_policy = SECURITY_OSEN;
 		bss->wpa_group = WPA_CIPHER_CCMP;
@@ -912,5 +970,7 @@
 		bss->wpa_group = WPA_CIPHER_NONE;
 		bss->wpa_pairwise = WPA_CIPHER_NONE;
 		bss->rsn_pairwise = WPA_CIPHER_NONE;
+		if (full_config)
+			bss->wpa_key_mgmt = WPA_KEY_MGMT_NONE;
 	}
 }
diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
index 2858c6e..c14eeda 100644
--- a/src/ap/ap_config.h
+++ b/src/ap/ap_config.h
@@ -12,9 +12,38 @@
 #include "common/defs.h"
 #include "ip_addr.h"
 #include "common/wpa_common.h"
+#include "common/ieee802_11_defs.h"
 #include "common/ieee802_11_common.h"
 #include "wps/wps.h"
 
+/**
+ * mesh_conf - local MBSS state and settings
+ */
+struct mesh_conf {
+	u8 meshid[32];
+	u8 meshid_len;
+	/* Active Path Selection Protocol Identifier */
+	u8 mesh_pp_id;
+	/* Active Path Selection Metric Identifier */
+	u8 mesh_pm_id;
+	/* Congestion Control Mode Identifier */
+	u8 mesh_cc_id;
+	/* Synchronization Protocol Identifier */
+	u8 mesh_sp_id;
+	/* Authentication Protocol Identifier */
+	u8 mesh_auth_id;
+	u8 *ies;
+	int ie_len;
+#define MESH_CONF_SEC_NONE BIT(0)
+#define MESH_CONF_SEC_AUTH BIT(1)
+#define MESH_CONF_SEC_AMPE BIT(2)
+	unsigned int security;
+	int dot11MeshMaxRetries;
+	int dot11MeshRetryTimeout; /* msec */
+	int dot11MeshConfirmTimeout; /* msec */
+	int dot11MeshHoldingTimeout; /* msec */
+};
+
 #define MAX_STA_COUNT 2007
 #define MAX_VLAN_ID 4094
 
@@ -29,8 +58,6 @@
 struct ft_remote_r0kh;
 struct ft_remote_r1kh;
 
-#define HOSTAPD_MAX_SSID_LEN 32
-
 #define NUM_WEP_KEYS 4
 struct hostapd_wep_keys {
 	u8 idx;
@@ -50,7 +77,7 @@
 } secpolicy;
 
 struct hostapd_ssid {
-	u8 ssid[HOSTAPD_MAX_SSID_LEN];
+	u8 ssid[SSID_MAX_LEN];
 	size_t ssid_len;
 	unsigned int ssid_set:1;
 	unsigned int utf8_ssid:1;
@@ -86,12 +113,10 @@
 	struct hostapd_vlan *next;
 	int vlan_id; /* VLAN ID or -1 (VLAN_ID_WILDCARD) for wildcard entry */
 	char ifname[IFNAMSIZ + 1];
+	int configured;
 	int dynamic_vlan;
 #ifdef CONFIG_FULL_DYNAMIC_VLAN
 
-#define DVLAN_CLEAN_BR 	0x1
-#define DVLAN_CLEAN_VLAN	0x2
-#define DVLAN_CLEAN_VLAN_PORT	0x4
 #define DVLAN_CLEAN_WLAN_PORT	0x8
 	int clean;
 #endif /* CONFIG_FULL_DYNAMIC_VLAN */
@@ -196,6 +221,7 @@
 	int max_num_sta; /* maximum number of STAs in station table */
 
 	int dtim_period;
+	int bss_load_update_period;
 
 	int ieee802_1x; /* use IEEE 802.1X */
 	int eapol_version;
@@ -204,6 +230,7 @@
 	struct hostapd_eap_user *eap_user;
 	char *eap_user_sqlite;
 	char *eap_sim_db;
+	int eap_server_erp; /* Whether ERP is enabled on internal EAP server */
 	struct hostapd_ip_addr own_ip_addr;
 	char *nas_identifier;
 	struct hostapd_radius_servers *radius;
@@ -230,6 +257,8 @@
 	int wep_rekeying_period;
 	int broadcast_key_idx_min, broadcast_key_idx_max;
 	int eap_reauth_period;
+	int erp_send_reauth_start;
+	char *erp_domain;
 
 	int ieee802_11f; /* use IEEE 802.11f (IAPP) */
 	char iapp_iface[IFNAMSIZ + 1]; /* interface used with IAPP broadcast
@@ -302,6 +331,7 @@
 	int check_crl;
 	char *ocsp_stapling_response;
 	char *dh_file;
+	char *openssl_ciphers;
 	u8 *pac_opaque_encr_key;
 	u8 *eap_fast_a_id;
 	size_t eap_fast_a_id_len;
@@ -319,8 +349,6 @@
 	int radius_server_acct_port;
 	int radius_server_ipv6;
 
-	char *test_socket; /* UNIX domain socket path for driver_test */
-
 	int use_pae_group_addr; /* Whether to send EAPOL frames to PAE group
 				 * address instead of individual address
 				 * (for driver_wired.c).
@@ -458,6 +486,8 @@
 	unsigned int qos_map_set_len;
 
 	int osen;
+	int proxy_arp;
+	int na_mcast_to_ucast;
 #ifdef CONFIG_HS20
 	int hs20;
 	int disable_dgaf;
@@ -478,7 +508,7 @@
 		char file[256];
 	} *hs20_icons;
 	size_t hs20_icons_count;
-	u8 osu_ssid[HOSTAPD_MAX_SSID_LEN];
+	u8 osu_ssid[SSID_MAX_LEN];
 	size_t osu_ssid_len;
 	struct hs20_osu_provider {
 		unsigned int friendly_name_count;
@@ -514,6 +544,13 @@
 	u8 bss_load_test[5];
 	u8 bss_load_test_set;
 #endif /* CONFIG_TESTING_OPTIONS */
+
+#define MESH_ENABLED BIT(0)
+	int mesh;
+
+	int radio_measurements;
+
+	int vendor_vht;
 };
 
 
@@ -529,7 +566,8 @@
 	int fragm_threshold;
 	u8 send_probe_response;
 	u8 channel;
-	int *chanlist;
+	u8 acs;
+	struct wpa_freq_range_list acs_ch_list;
 	enum hostapd_hw_mode hw_mode; /* HOSTAPD_MODE_IEEE80211A, .. */
 	enum {
 		LONG_PREAMBLE = 0,
@@ -540,6 +578,7 @@
 	int *basic_rates;
 
 	const struct wpa_driver_ops *driver;
+	char *driver_params;
 
 	int ap_table_max_size;
 	int ap_table_expiration_time;
@@ -589,6 +628,10 @@
 	u8 vht_oper_centr_freq_seg0_idx;
 	u8 vht_oper_centr_freq_seg1_idx;
 
+#ifdef CONFIG_P2P
+	u8 p2p_go_ctwindow;
+#endif /* CONFIG_P2P */
+
 #ifdef CONFIG_TESTING_OPTIONS
 	double ignore_probe_probability;
 	double ignore_auth_probability;
@@ -599,6 +642,11 @@
 
 #ifdef CONFIG_ACS
 	unsigned int acs_num_scans;
+	struct acs_bias {
+		int channel;
+		double bias;
+	} *acs_chan_bias;
+	unsigned int num_acs_chan_bias;
 #endif /* CONFIG_ACS */
 };
 
@@ -608,6 +656,7 @@
 struct hostapd_config * hostapd_config_defaults(void);
 void hostapd_config_defaults_bss(struct hostapd_bss_config *bss);
 void hostapd_config_free_eap_user(struct hostapd_eap_user *user);
+void hostapd_config_clear_wpa_psk(struct hostapd_wpa_psk **p);
 void hostapd_config_free_bss(struct hostapd_bss_config *conf);
 void hostapd_config_free(struct hostapd_config *conf);
 int hostapd_maclist_found(struct mac_acl_entry *list, int num_entries,
diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
index cc4ac10..f3f7edd 100644
--- a/src/ap/ap_drv_ops.c
+++ b/src/ap/ap_drv_ops.c
@@ -10,6 +10,7 @@
 
 #include "utils/common.h"
 #include "common/ieee802_11_defs.h"
+#include "common/hw_features_common.h"
 #include "wps/wps.h"
 #include "p2p/p2p.h"
 #include "hostapd.h"
@@ -216,6 +217,15 @@
 }
 
 
+int hostapd_reset_ap_wps_ie(struct hostapd_data *hapd)
+{
+	if (hapd->driver == NULL || hapd->driver->set_ap_wps_ie == NULL)
+		return 0;
+
+	return hapd->driver->set_ap_wps_ie(hapd->drv_priv, NULL, NULL, NULL);
+}
+
+
 int hostapd_set_ap_wps_ie(struct hostapd_data *hapd)
 {
 	struct wpabuf *beacon, *proberesp, *assocresp;
@@ -280,8 +290,14 @@
 		params.wpa = hapd->conf->wpa;
 		params.ieee802_1x = hapd->conf->ieee802_1x;
 		params.wpa_group = hapd->conf->wpa_group;
-		params.wpa_pairwise = hapd->conf->wpa_pairwise |
-			hapd->conf->rsn_pairwise;
+		if ((hapd->conf->wpa & (WPA_PROTO_WPA | WPA_PROTO_RSN)) ==
+		    (WPA_PROTO_WPA | WPA_PROTO_RSN))
+			params.wpa_pairwise = hapd->conf->wpa_pairwise |
+				hapd->conf->rsn_pairwise;
+		else if (hapd->conf->wpa & WPA_PROTO_RSN)
+			params.wpa_pairwise = hapd->conf->rsn_pairwise;
+		else if (hapd->conf->wpa & WPA_PROTO_WPA)
+			params.wpa_pairwise = hapd->conf->wpa_pairwise;
 		params.wpa_key_mgmt = hapd->conf->wpa_key_mgmt;
 		params.rsn_preauth = hapd->conf->rsn_preauth;
 #ifdef CONFIG_IEEE80211W
@@ -477,93 +493,8 @@
 }
 
 
-int hostapd_set_freq_params(struct hostapd_freq_params *data, int mode,
-			    int freq, int channel, int ht_enabled,
-			    int vht_enabled, int sec_channel_offset,
-			    int vht_oper_chwidth, int center_segment0,
-			    int center_segment1, u32 vht_caps)
-{
-	int tmp;
-
-	os_memset(data, 0, sizeof(*data));
-	data->mode = mode;
-	data->freq = freq;
-	data->channel = channel;
-	data->ht_enabled = ht_enabled;
-	data->vht_enabled = vht_enabled;
-	data->sec_channel_offset = sec_channel_offset;
-	data->center_freq1 = freq + sec_channel_offset * 10;
-	data->center_freq2 = 0;
-	data->bandwidth = sec_channel_offset ? 40 : 20;
-
-	/*
-	 * This validation code is probably misplaced, maybe it should be
-	 * in src/ap/hw_features.c and check the hardware support as well.
-	 */
-	if (data->vht_enabled) switch (vht_oper_chwidth) {
-	case VHT_CHANWIDTH_USE_HT:
-		if (center_segment1)
-			return -1;
-		if (center_segment0 != 0 &&
-		    5000 + center_segment0 * 5 != data->center_freq1 &&
-		    2407 + center_segment0 * 5 != data->center_freq1)
-			return -1;
-		break;
-	case VHT_CHANWIDTH_80P80MHZ:
-		if (!(vht_caps & VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ)) {
-			wpa_printf(MSG_ERROR,
-				   "80+80 channel width is not supported!");
-			return -1;
-		}
-		if (center_segment1 == center_segment0 + 4 ||
-		    center_segment1 == center_segment0 - 4)
-			return -1;
-		data->center_freq2 = 5000 + center_segment1 * 5;
-		/* fall through */
-	case VHT_CHANWIDTH_80MHZ:
-		data->bandwidth = 80;
-		if (vht_oper_chwidth == 1 && center_segment1)
-			return -1;
-		if (vht_oper_chwidth == 3 && !center_segment1)
-			return -1;
-		if (!sec_channel_offset)
-			return -1;
-		/* primary 40 part must match the HT configuration */
-		tmp = (30 + freq - 5000 - center_segment0 * 5)/20;
-		tmp /= 2;
-		if (data->center_freq1 != 5000 +
-					 center_segment0 * 5 - 20 + 40 * tmp)
-			return -1;
-		data->center_freq1 = 5000 + center_segment0 * 5;
-		break;
-	case VHT_CHANWIDTH_160MHZ:
-		data->bandwidth = 160;
-		if (!(vht_caps & (VHT_CAP_SUPP_CHAN_WIDTH_160MHZ |
-				  VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ))) {
-			wpa_printf(MSG_ERROR,
-				   "160MHZ channel width is not supported!");
-			return -1;
-		}
-		if (center_segment1)
-			return -1;
-		if (!sec_channel_offset)
-			return -1;
-		/* primary 40 part must match the HT configuration */
-		tmp = (70 + freq - 5000 - center_segment0 * 5)/20;
-		tmp /= 2;
-		if (data->center_freq1 != 5000 +
-					 center_segment0 * 5 - 60 + 40 * tmp)
-			return -1;
-		data->center_freq1 = 5000 + center_segment0 * 5;
-		break;
-	}
-
-	return 0;
-}
-
-
-int hostapd_set_freq(struct hostapd_data *hapd, int mode, int freq,
-		     int channel, int ht_enabled, int vht_enabled,
+int hostapd_set_freq(struct hostapd_data *hapd, enum hostapd_hw_mode mode,
+		     int freq, int channel, int ht_enabled, int vht_enabled,
 		     int sec_channel_offset, int vht_oper_chwidth,
 		     int center_segment0, int center_segment1)
 {
@@ -573,7 +504,8 @@
 				    vht_enabled, sec_channel_offset,
 				    vht_oper_chwidth,
 				    center_segment0, center_segment1,
-				    hapd->iface->current_mode->vht_capab))
+				    hapd->iface->current_mode ?
+				    hapd->iface->current_mode->vht_capab : 0))
 		return -1;
 
 	if (hapd->driver == NULL)
@@ -701,7 +633,7 @@
 {
 	if (hapd->driver == NULL || hapd->driver->send_mlme == NULL)
 		return 0;
-	return hapd->driver->send_mlme(hapd->drv_priv, msg, len, noack);
+	return hapd->driver->send_mlme(hapd->drv_priv, msg, len, noack, 0);
 }
 
 
@@ -747,7 +679,8 @@
 }
 
 
-int hostapd_start_dfs_cac(struct hostapd_iface *iface, int mode, int freq,
+int hostapd_start_dfs_cac(struct hostapd_iface *iface,
+			  enum hostapd_hw_mode mode, int freq,
 			  int channel, int ht_enabled, int vht_enabled,
 			  int sec_channel_offset, int vht_oper_chwidth,
 			  int center_segment0, int center_segment1)
@@ -792,3 +725,71 @@
 	return hapd->driver->set_qos_map(hapd->drv_priv, qos_map_set,
 					 qos_map_set_len);
 }
+
+
+int hostapd_drv_do_acs(struct hostapd_data *hapd)
+{
+	struct drv_acs_params params;
+	int ret, i, acs_ch_list_all = 0;
+	u8 *channels = NULL;
+	unsigned int num_channels = 0;
+	struct hostapd_hw_modes *mode;
+
+	if (hapd->driver == NULL || hapd->driver->do_acs == NULL)
+		return 0;
+
+	os_memset(&params, 0, sizeof(params));
+	params.hw_mode = hapd->iface->conf->hw_mode;
+
+	/*
+	 * If no chanlist config parameter is provided, include all enabled
+	 * channels of the selected hw_mode.
+	 */
+	if (!hapd->iface->conf->acs_ch_list.num)
+		acs_ch_list_all = 1;
+
+	mode = hapd->iface->current_mode;
+	if (mode == NULL)
+		return -1;
+	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;
+	}
+
+	params.ch_list = channels;
+	params.ch_list_len = num_channels;
+
+	params.ht_enabled = !!(hapd->iface->conf->ieee80211n);
+	params.ht40_enabled = !!(hapd->iface->conf->ht_capab &
+				 HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET);
+	params.vht_enabled = !!(hapd->iface->conf->ieee80211ac);
+	params.ch_width = 20;
+	if (hapd->iface->conf->ieee80211n && params.ht40_enabled)
+		params.ch_width = 40;
+
+	/* Note: VHT20 is defined by combination of ht_capab & vht_oper_chwidth
+	 */
+	if (hapd->iface->conf->ieee80211ac && params.ht40_enabled) {
+		if (hapd->iface->conf->vht_oper_chwidth == VHT_CHANWIDTH_80MHZ)
+			params.ch_width = 80;
+		else if (hapd->iface->conf->vht_oper_chwidth ==
+			 VHT_CHANWIDTH_160MHZ ||
+			 hapd->iface->conf->vht_oper_chwidth ==
+			 VHT_CHANWIDTH_80P80MHZ)
+			params.ch_width = 160;
+	}
+
+	ret = hapd->driver->do_acs(hapd->drv_priv, &params);
+	os_free(channels);
+
+	return ret;
+}
diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
index 7cc9d7d..82eaf3f 100644
--- a/src/ap/ap_drv_ops.h
+++ b/src/ap/ap_drv_ops.h
@@ -24,6 +24,7 @@
 void hostapd_free_ap_extra_ies(struct hostapd_data *hapd, struct wpabuf *beacon,
 			       struct wpabuf *proberesp,
 			       struct wpabuf *assocresp);
+int hostapd_reset_ap_wps_ie(struct hostapd_data *hapd);
 int hostapd_set_ap_wps_ie(struct hostapd_data *hapd);
 int hostapd_set_authorized(struct hostapd_data *hapd,
 			   struct sta_info *sta, int authorized);
@@ -57,8 +58,8 @@
 int hostapd_get_seqnum(const char *ifname, struct hostapd_data *hapd,
 		       const u8 *addr, int idx, u8 *seq);
 int hostapd_flush(struct hostapd_data *hapd);
-int hostapd_set_freq(struct hostapd_data *hapd, int mode, int freq,
-		     int channel, int ht_enabled, int vht_enabled,
+int hostapd_set_freq(struct hostapd_data *hapd, enum hostapd_hw_mode mode,
+		     int freq, int channel, int ht_enabled, int vht_enabled,
 		     int sec_channel_offset, int vht_oper_chwidth,
 		     int center_segment0, int center_segment1);
 int hostapd_set_rts(struct hostapd_data *hapd, int rts);
@@ -102,15 +103,12 @@
 		      int reassoc, u16 status, const u8 *ie, size_t len);
 int hostapd_add_tspec(struct hostapd_data *hapd, const u8 *addr,
 		      u8 *tspec_ie, size_t tspec_ielen);
-int hostapd_start_dfs_cac(struct hostapd_iface *iface, int mode, int freq,
+int hostapd_start_dfs_cac(struct hostapd_iface *iface,
+			  enum hostapd_hw_mode mode, int freq,
 			  int channel, int ht_enabled, int vht_enabled,
 			  int sec_channel_offset, int vht_oper_chwidth,
 			  int center_segment0, int center_segment1);
-int hostapd_set_freq_params(struct hostapd_freq_params *data, int mode,
-			    int freq, int channel, int ht_enabled,
-			    int vht_enabled, int sec_channel_offset,
-			    int vht_oper_chwidth, int center_segment0,
-			    int center_segment1, u32 vht_caps);
+int hostapd_drv_do_acs(struct hostapd_data *hapd);
 
 
 #include "drivers/driver.h"
@@ -280,6 +278,47 @@
 	return hapd->driver->status(hapd->drv_priv, buf, buflen);
 }
 
+static inline int hostapd_drv_br_add_ip_neigh(struct hostapd_data *hapd,
+					      int version, const u8 *ipaddr,
+					      int prefixlen, const u8 *addr)
+{
+	if (hapd->driver == NULL || hapd->drv_priv == NULL ||
+	    hapd->driver->br_add_ip_neigh == NULL)
+		return -1;
+	return hapd->driver->br_add_ip_neigh(hapd->drv_priv, version, ipaddr,
+					     prefixlen, addr);
+}
+
+static inline int hostapd_drv_br_delete_ip_neigh(struct hostapd_data *hapd,
+						 u8 version, const u8 *ipaddr)
+{
+	if (hapd->driver == NULL || hapd->drv_priv == NULL ||
+	    hapd->driver->br_delete_ip_neigh == NULL)
+		return -1;
+	return hapd->driver->br_delete_ip_neigh(hapd->drv_priv, version,
+						ipaddr);
+}
+
+static inline int hostapd_drv_br_port_set_attr(struct hostapd_data *hapd,
+					       enum drv_br_port_attr attr,
+					       unsigned int val)
+{
+	if (hapd->driver == NULL || hapd->drv_priv == NULL ||
+	    hapd->driver->br_port_set_attr == NULL)
+		return -1;
+	return hapd->driver->br_port_set_attr(hapd->drv_priv, attr, val);
+}
+
+static inline int hostapd_drv_br_set_net_param(struct hostapd_data *hapd,
+					       enum drv_br_net_param param,
+					       unsigned int val)
+{
+	if (hapd->driver == NULL || hapd->drv_priv == NULL ||
+	    hapd->driver->br_set_net_param == NULL)
+		return -1;
+	return hapd->driver->br_set_net_param(hapd->drv_priv, param, val);
+}
+
 static inline int hostapd_drv_vendor_cmd(struct hostapd_data *hapd,
 					 int vendor_id, int subcmd,
 					 const u8 *data, size_t data_len,
@@ -291,4 +330,11 @@
 					data_len, buf);
 }
 
+static inline int hostapd_drv_stop_ap(struct hostapd_data *hapd)
+{
+	if (hapd->driver == NULL || hapd->driver->stop_ap == NULL)
+		return 0;
+	return hapd->driver->stop_ap(hapd->drv_priv);
+}
+
 #endif /* AP_DRV_OPS */
diff --git a/src/ap/ap_list.c b/src/ap/ap_list.c
index 287d520..78a1f7c 100644
--- a/src/ap/ap_list.c
+++ b/src/ap/ap_list.c
@@ -111,8 +111,8 @@
 	if (s->hnext != NULL)
 		s->hnext = s->hnext->hnext;
 	else
-		printf("AP: could not remove AP " MACSTR " from hash table\n",
-		       MAC2STR(ap->addr));
+		wpa_printf(MSG_INFO, "AP: could not remove AP " MACSTR
+			   " from hash table",  MAC2STR(ap->addr));
 }
 
 
@@ -182,7 +182,8 @@
 	if (!ap) {
 		ap = ap_ap_add(iface, mgmt->bssid);
 		if (!ap) {
-			printf("Failed to allocate AP information entry\n");
+			wpa_printf(MSG_INFO,
+				   "Failed to allocate AP information entry");
 			return;
 		}
 		new_ap = 1;
@@ -192,14 +193,14 @@
 			  elems->supp_rates, elems->supp_rates_len,
 			  elems->ext_supp_rates, elems->ext_supp_rates_len);
 
-	if (elems->erp_info && elems->erp_info_len == 1)
+	if (elems->erp_info)
 		ap->erp = elems->erp_info[0];
 	else
 		ap->erp = -1;
 
-	if (elems->ds_params && elems->ds_params_len == 1)
+	if (elems->ds_params)
 		ap->channel = elems->ds_params[0];
-	else if (elems->ht_operation && elems->ht_operation_len >= 1)
+	else if (elems->ht_operation)
 		ap->channel = elems->ht_operation[0];
 	else if (fi)
 		ap->channel = fi->channel;
diff --git a/src/ap/authsrv.c b/src/ap/authsrv.c
index 86f1cbe..f10e1b7 100644
--- a/src/ap/authsrv.c
+++ b/src/ap/authsrv.c
@@ -55,10 +55,11 @@
 {
 	const struct hostapd_eap_user *eap_user;
 	int i;
+	int rv = -1;
 
 	eap_user = hostapd_get_eap_user(ctx, identity, identity_len, phase2);
 	if (eap_user == NULL)
-		return -1;
+		goto out;
 
 	if (user == NULL)
 		return 0;
@@ -72,7 +73,7 @@
 	if (eap_user->password) {
 		user->password = os_malloc(eap_user->password_len);
 		if (user->password == NULL)
-			return -1;
+			goto out;
 		os_memcpy(user->password, eap_user->password,
 			  eap_user->password_len);
 		user->password_len = eap_user->password_len;
@@ -83,8 +84,13 @@
 	user->ttls_auth = eap_user->ttls_auth;
 	user->remediation = eap_user->remediation;
 	user->accept_attr = eap_user->accept_attr;
+	rv = 0;
 
-	return 0;
+out:
+	if (rv)
+		wpa_printf(MSG_DEBUG, "%s: Failed to find user", __func__);
+
+	return rv;
 }
 
 
@@ -124,6 +130,8 @@
 	srv.subscr_remediation_url = conf->subscr_remediation_url;
 	srv.subscr_remediation_method = conf->subscr_remediation_method;
 #endif /* CONFIG_HS20 */
+	srv.erp = conf->eap_server_erp;
+	srv.erp_domain = conf->erp_domain;
 
 	hapd->radius_srv = radius_server_init(&srv);
 	if (hapd->radius_srv == NULL) {
@@ -158,6 +166,7 @@
 		params.private_key = hapd->conf->private_key;
 		params.private_key_passwd = hapd->conf->private_key_passwd;
 		params.dh_file = hapd->conf->dh_file;
+		params.openssl_ciphers = hapd->conf->openssl_ciphers;
 		params.ocsp_stapling_response =
 			hapd->conf->ocsp_stapling_response;
 
diff --git a/src/ap/beacon.c b/src/ap/beacon.c
index 4cae0d9..51d0c15 100644
--- a/src/ap/beacon.c
+++ b/src/ap/beacon.c
@@ -15,6 +15,7 @@
 #include "utils/common.h"
 #include "common/ieee802_11_defs.h"
 #include "common/ieee802_11_common.h"
+#include "common/hw_features_common.h"
 #include "wps/wps_defs.h"
 #include "p2p/p2p.h"
 #include "hostapd.h"
@@ -32,18 +33,47 @@
 
 #ifdef NEED_AP_MLME
 
+static u8 * hostapd_eid_rm_enabled_capab(struct hostapd_data *hapd, u8 *eid,
+					 size_t len)
+{
+	if (!hapd->conf->radio_measurements || len < 2 + 4)
+		return eid;
+
+	*eid++ = WLAN_EID_RRM_ENABLED_CAPABILITIES;
+	*eid++ = 5;
+	*eid++ = (hapd->conf->radio_measurements & BIT(0)) ?
+		WLAN_RRM_CAPS_NEIGHBOR_REPORT : 0x00;
+	*eid++ = 0x00;
+	*eid++ = 0x00;
+	*eid++ = 0x00;
+	*eid++ = 0x00;
+	return eid;
+}
+
+
 static u8 * hostapd_eid_bss_load(struct hostapd_data *hapd, u8 *eid, size_t len)
 {
+	if (len < 2 + 5)
+		return eid;
+
 #ifdef CONFIG_TESTING_OPTIONS
 	if (hapd->conf->bss_load_test_set) {
-		if (2 + 5 > len)
-			return eid;
 		*eid++ = WLAN_EID_BSS_LOAD;
 		*eid++ = 5;
 		os_memcpy(eid, hapd->conf->bss_load_test, 5);
 		eid += 5;
+		return eid;
 	}
 #endif /* CONFIG_TESTING_OPTIONS */
+	if (hapd->conf->bss_load_update_period) {
+		*eid++ = WLAN_EID_BSS_LOAD;
+		*eid++ = 5;
+		WPA_PUT_LE16(eid, hapd->num_sta);
+		eid += 2;
+		*eid++ = hapd->iface->channel_utilization;
+		WPA_PUT_LE16(eid, 0); /* no available admission capabity */
+		eid += 2;
+	}
 	return eid;
 }
 
@@ -330,7 +360,6 @@
 
 
 static u8 * hostapd_gen_probe_resp(struct hostapd_data *hapd,
-				   struct sta_info *sta,
 				   const struct ieee80211_mgmt *req,
 				   int is_p2p, size_t *resp_len)
 {
@@ -350,6 +379,10 @@
 #endif /* CONFIG_P2P */
 	if (hapd->conf->vendor_elements)
 		buflen += wpabuf_len(hapd->conf->vendor_elements);
+	if (hapd->conf->vendor_vht) {
+		buflen += 5 + 2 + sizeof(struct ieee80211_vht_capabilities) +
+			2 + sizeof(struct ieee80211_vht_operation);
+	}
 	resp = os_zalloc(buflen);
 	if (resp == NULL)
 		return NULL;
@@ -368,7 +401,7 @@
 
 	/* hardware or low-level driver will setup seq_ctrl and timestamp */
 	resp->u.probe_resp.capab_info =
-		host_to_le16(hostapd_own_capab_info(hapd, sta, 1));
+		host_to_le16(hostapd_own_capab_info(hapd));
 
 	pos = resp->u.probe_resp.variable;
 	*pos++ = WLAN_EID_SSID;
@@ -398,6 +431,8 @@
 
 	pos = hostapd_eid_bss_load(hapd, pos, epos - pos);
 
+	pos = hostapd_eid_rm_enabled_capab(hapd, pos, epos - pos);
+
 #ifdef CONFIG_IEEE80211N
 	pos = hostapd_eid_ht_capabilities(hapd, pos);
 	pos = hostapd_eid_ht_operation(hapd, pos);
@@ -415,8 +450,12 @@
 	pos = hostapd_add_csa_elems(hapd, pos, (u8 *)resp,
 				    &hapd->cs_c_off_proberesp);
 #ifdef CONFIG_IEEE80211AC
-	pos = hostapd_eid_vht_capabilities(hapd, pos);
-	pos = hostapd_eid_vht_operation(hapd, pos);
+	if (hapd->iconf->ieee80211ac && !hapd->conf->disable_11ac) {
+		pos = hostapd_eid_vht_capabilities(hapd, pos);
+		pos = hostapd_eid_vht_operation(hapd, pos);
+	}
+	if (hapd->conf->vendor_vht)
+		pos = hostapd_eid_vendor_vht(hapd, pos);
 #endif /* CONFIG_IEEE80211AC */
 
 	/* Wi-Fi Alliance WMM */
@@ -508,7 +547,6 @@
 	struct ieee802_11_elems elems;
 	const u8 *ie;
 	size_t ie_len;
-	struct sta_info *sta = NULL;
 	size_t i, resp_len;
 	int noack;
 	enum ssid_match_result res;
@@ -540,6 +578,27 @@
 		return;
 	}
 
+	/*
+	 * No need to reply if the Probe Request frame was sent on an adjacent
+	 * channel. IEEE Std 802.11-2012 describes this as a requirement for an
+	 * AP with dot11RadioMeasurementActivated set to true, but strictly
+	 * speaking does not allow such ignoring of Probe Request frames if
+	 * dot11RadioMeasurementActivated is false. Anyway, this can help reduce
+	 * number of unnecessary Probe Response frames for cases where the STA
+	 * is less likely to see them (Probe Request frame sent on a
+	 * neighboring, but partially overlapping, channel).
+	 */
+	if (elems.ds_params &&
+	    hapd->iface->current_mode &&
+	    (hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G ||
+	     hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211B) &&
+	    hapd->iconf->channel != elems.ds_params[0]) {
+		wpa_printf(MSG_DEBUG,
+			   "Ignore Probe Request due to DS Params mismatch: chan=%u != ds.chan=%u",
+			   hapd->iconf->channel, elems.ds_params[0]);
+		return;
+	}
+
 #ifdef CONFIG_P2P
 	if (hapd->p2p && elems.wps_ie) {
 		struct wpabuf *wps;
@@ -574,8 +633,6 @@
 		return;
 	}
 
-	sta = ap_get_sta(hapd, mgmt->sa);
-
 #ifdef CONFIG_P2P
 	if ((hapd->conf->p2p & P2P_GROUP_OWNER) &&
 	    elems.ssid_len == P2P_WILDCARD_SSID_LEN &&
@@ -588,10 +645,7 @@
 
 	res = ssid_match(hapd, elems.ssid, elems.ssid_len,
 			 elems.ssid_list, elems.ssid_list_len);
-	if (res != NO_SSID_MATCH) {
-		if (sta)
-			sta->ssid_probe = &hapd->conf->ssid;
-	} else {
+	if (res == NO_SSID_MATCH) {
 		if (!(mgmt->da[0] & 0x01)) {
 			wpa_printf(MSG_MSGDUMP, "Probe Request from " MACSTR
 				   " for foreign SSID '%s' (DA " MACSTR ")%s",
@@ -658,7 +712,7 @@
 	}
 #endif /* CONFIG_TESTING_OPTIONS */
 
-	resp = hostapd_gen_probe_resp(hapd, sta, mgmt, elems.p2p != NULL,
+	resp = hostapd_gen_probe_resp(hapd, mgmt, elems.p2p != NULL,
 				      &resp_len);
 	if (resp == NULL)
 		return;
@@ -713,7 +767,7 @@
 			   "this");
 
 	/* Generate a Probe Response template for the non-P2P case */
-	return hostapd_gen_probe_resp(hapd, NULL, NULL, 0, resp_len);
+	return hostapd_gen_probe_resp(hapd, NULL, 0, resp_len);
 }
 
 #endif /* NEED_AP_MLME */
@@ -745,6 +799,14 @@
 #endif /* CONFIG_P2P */
 	if (hapd->conf->vendor_elements)
 		tail_len += wpabuf_len(hapd->conf->vendor_elements);
+
+#ifdef CONFIG_IEEE80211AC
+	if (hapd->conf->vendor_vht) {
+		tail_len += 5 + 2 + sizeof(struct ieee80211_vht_capabilities) +
+			2 + sizeof(struct ieee80211_vht_operation);
+	}
+#endif /* CONFIG_IEEE80211AC */
+
 	tailpos = tail = os_malloc(tail_len);
 	if (head == NULL || tail == NULL) {
 		wpa_printf(MSG_ERROR, "Failed to set beacon data");
@@ -764,7 +826,7 @@
 		host_to_le16(hapd->iconf->beacon_int);
 
 	/* hardware or low-level driver will setup seq_ctrl and timestamp */
-	capab_info = hostapd_own_capab_info(hapd, NULL, 0);
+	capab_info = hostapd_own_capab_info(hapd);
 	head->u.beacon.capab_info = host_to_le16(capab_info);
 	pos = &head->u.beacon.variable[0];
 
@@ -808,6 +870,10 @@
 	tailpos = hostapd_eid_wpa(hapd, tailpos, tail + BEACON_TAIL_BUF_SIZE -
 				  tailpos);
 
+	tailpos = hostapd_eid_rm_enabled_capab(hapd, tailpos,
+					       tail + BEACON_TAIL_BUF_SIZE -
+					       tailpos);
+
 	tailpos = hostapd_eid_bss_load(hapd, tailpos,
 				       tail + BEACON_TAIL_BUF_SIZE - tailpos);
 
@@ -830,8 +896,12 @@
 	tailpos = hostapd_add_csa_elems(hapd, tailpos, tail,
 					&hapd->cs_c_off_beacon);
 #ifdef CONFIG_IEEE80211AC
-	tailpos = hostapd_eid_vht_capabilities(hapd, tailpos);
-	tailpos = hostapd_eid_vht_operation(hapd, tailpos);
+	if (hapd->iconf->ieee80211ac && !hapd->conf->disable_11ac) {
+		tailpos = hostapd_eid_vht_capabilities(hapd, tailpos);
+		tailpos = hostapd_eid_vht_operation(hapd, tailpos);
+	}
+	if (hapd->conf->vendor_vht)
+		tailpos = hostapd_eid_vendor_vht(hapd, tailpos);
 #endif /* CONFIG_IEEE80211AC */
 
 	/* Wi-Fi Alliance WMM */
@@ -886,8 +956,14 @@
 	params->basic_rates = hapd->iface->basic_rates;
 	params->ssid = hapd->conf->ssid.ssid;
 	params->ssid_len = hapd->conf->ssid.ssid_len;
-	params->pairwise_ciphers = hapd->conf->wpa_pairwise |
-		hapd->conf->rsn_pairwise;
+	if ((hapd->conf->wpa & (WPA_PROTO_WPA | WPA_PROTO_RSN)) ==
+	    (WPA_PROTO_WPA | WPA_PROTO_RSN))
+		params->pairwise_ciphers = hapd->conf->wpa_pairwise |
+			hapd->conf->rsn_pairwise;
+	else if (hapd->conf->wpa & WPA_PROTO_RSN)
+		params->pairwise_ciphers = hapd->conf->rsn_pairwise;
+	else if (hapd->conf->wpa & WPA_PROTO_WPA)
+		params->pairwise_ciphers = hapd->conf->wpa_pairwise;
 	params->group_cipher = hapd->conf->wpa_group;
 	params->key_mgmt_suites = hapd->conf->wpa_key_mgmt;
 	params->auth_algs = hapd->conf->auth_algs;
@@ -908,6 +984,7 @@
 		break;
 	}
 	params->isolate = hapd->conf->isolate;
+	params->smps_mode = hapd->iconf->ht_capab & HT_CAP_INFO_SMPS_MASK;
 #ifdef NEED_AP_MLME
 	params->cts_protect = !!(ieee802_11_erp_info(hapd) &
 				ERP_INFO_USE_PROTECTION);
@@ -930,6 +1007,9 @@
 		params->hessid = hapd->conf->hessid;
 	params->access_network_type = hapd->conf->access_network_type;
 	params->ap_max_inactivity = hapd->conf->ap_max_inactivity;
+#ifdef CONFIG_P2P
+	params->p2p_go_ctwindow = hapd->iconf->p2p_go_ctwindow;
+#endif /* CONFIG_P2P */
 #ifdef CONFIG_HS20
 	params->disable_dgaf = hapd->conf->disable_dgaf;
 	if (hapd->conf->osen) {
@@ -978,6 +1058,8 @@
 	params.beacon_ies = beacon;
 	params.proberesp_ies = proberesp;
 	params.assocresp_ies = assocresp;
+	params.reenable = hapd->reenable_beacon;
+	hapd->reenable_beacon = 0;
 
 	if (iface->current_mode &&
 	    hostapd_set_freq_params(&freq, iconf->hw_mode, iface->freq,
diff --git a/src/ap/bss_load.c b/src/ap/bss_load.c
new file mode 100644
index 0000000..fb63942
--- /dev/null
+++ b/src/ap/bss_load.c
@@ -0,0 +1,65 @@
+/*
+ * BSS Load Element / Channel Utilization
+ * Copyright (c) 2014, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "hostapd.h"
+#include "bss_load.h"
+#include "ap_drv_ops.h"
+#include "beacon.h"
+
+
+static void update_channel_utilization(void *eloop_data, void *user_data)
+{
+	struct hostapd_data *hapd = eloop_data;
+	unsigned int sec, usec;
+	int err;
+
+	if (!(hapd->beacon_set_done && hapd->started))
+		return;
+
+	err = hostapd_drv_get_survey(hapd, hapd->iface->freq);
+	if (err) {
+		wpa_printf(MSG_ERROR, "BSS Load: Failed to get survey data");
+		return;
+	}
+
+	ieee802_11_set_beacon(hapd);
+
+	sec = ((hapd->bss_load_update_timeout / 1000) * 1024) / 1000;
+	usec = (hapd->bss_load_update_timeout % 1000) * 1024;
+	eloop_register_timeout(sec, usec, update_channel_utilization, hapd,
+			       NULL);
+}
+
+
+int bss_load_update_init(struct hostapd_data *hapd)
+{
+	struct hostapd_bss_config *conf = hapd->conf;
+	struct hostapd_config *iconf = hapd->iconf;
+	unsigned int sec, usec;
+
+	if (!conf->bss_load_update_period || !iconf->beacon_int)
+		return -1;
+
+	hapd->bss_load_update_timeout = conf->bss_load_update_period *
+					iconf->beacon_int;
+	sec = ((hapd->bss_load_update_timeout / 1000) * 1024) / 1000;
+	usec = (hapd->bss_load_update_timeout % 1000) * 1024;
+	eloop_register_timeout(sec, usec, update_channel_utilization, hapd,
+			       NULL);
+	return 0;
+}
+
+
+void bss_load_update_deinit(struct hostapd_data *hapd)
+{
+	eloop_cancel_timeout(update_channel_utilization, hapd, NULL);
+}
diff --git a/src/ap/bss_load.h b/src/ap/bss_load.h
new file mode 100644
index 0000000..ac3c793
--- /dev/null
+++ b/src/ap/bss_load.h
@@ -0,0 +1,17 @@
+/*
+ * BSS load update
+ * Copyright (c) 2014, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef BSS_LOAD_UPDATE_H
+#define BSS_LOAD_UPDATE_H
+
+
+int bss_load_update_init(struct hostapd_data *hapd);
+void bss_load_update_deinit(struct hostapd_data *hapd);
+
+
+#endif /* BSS_LOAD_UPDATE_H */
diff --git a/src/ap/ctrl_iface_ap.c b/src/ap/ctrl_iface_ap.c
index 39edbd7..60afcb0 100644
--- a/src/ap/ctrl_iface_ap.c
+++ b/src/ap/ctrl_iface_ap.c
@@ -1,6 +1,6 @@
 /*
  * Control interface for shared AP commands
- * Copyright (c) 2004-2013, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2014, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -10,6 +10,7 @@
 
 #include "utils/common.h"
 #include "common/ieee802_11_defs.h"
+#include "common/sae.h"
 #include "eapol_auth/eapol_auth_sm.h"
 #include "hostapd.h"
 #include "ieee802_1x.h"
@@ -36,7 +37,7 @@
 			  "rx_bytes=%lu\ntx_bytes=%lu\n",
 			  data.rx_packets, data.tx_packets,
 			  data.rx_bytes, data.tx_bytes);
-	if (ret < 0 || (size_t) ret >= buflen)
+	if (os_snprintf_error(buflen, ret))
 		return 0;
 	return ret;
 }
@@ -55,7 +56,7 @@
 
 	ret = os_snprintf(buf, buflen, "connected_time=%u\n",
 			  (unsigned int) age.sec);
-	if (ret < 0 || (size_t) ret >= buflen)
+	if (os_snprintf_error(buflen, ret))
 		return 0;
 	return ret;
 }
@@ -92,7 +93,7 @@
 	len = 0;
 	ret = os_snprintf(buf + len, buflen - len, MACSTR "\nflags=",
 			  MAC2STR(sta->addr));
-	if (ret < 0 || (size_t) ret >= buflen - len)
+	if (os_snprintf_error(buflen - len, ret))
 		return len;
 	len += ret;
 
@@ -104,7 +105,7 @@
 	ret = os_snprintf(buf + len, buflen - len, "\naid=%d\ncapability=0x%x\n"
 			  "listen_interval=%d\nsupported_rates=",
 			  sta->aid, sta->capability, sta->listen_interval);
-	if (ret < 0 || (size_t) ret >= buflen - len)
+	if (os_snprintf_error(buflen - len, ret))
 		return len;
 	len += ret;
 
@@ -112,14 +113,14 @@
 		ret = os_snprintf(buf + len, buflen - len, "%02x%s",
 				  sta->supported_rates[i],
 				  i + 1 < sta->supported_rates_len ? " " : "");
-		if (ret < 0 || (size_t) ret >= buflen - len)
+		if (os_snprintf_error(buflen - len, ret))
 			return len;
 		len += ret;
 	}
 
 	ret = os_snprintf(buf + len, buflen - len, "\ntimeout_next=%s\n",
 			  timeout_next_str(sta->timeout_next));
-	if (ret < 0 || (size_t) ret >= buflen - len)
+	if (os_snprintf_error(buflen - len, ret))
 		return len;
 	len += ret;
 
@@ -143,6 +144,22 @@
 	len += hostapd_get_sta_tx_rx(hapd, sta, buf + len, buflen - len);
 	len += hostapd_get_sta_conn_time(sta, buf + len, buflen - len);
 
+#ifdef CONFIG_SAE
+	if (sta->sae && sta->sae->state == SAE_ACCEPTED) {
+		res = os_snprintf(buf + len, buflen - len, "sae_group=%d\n",
+				  sta->sae->group);
+		if (!os_snprintf_error(buflen - len, res))
+			len += res;
+	}
+#endif /* CONFIG_SAE */
+
+	if (sta->vlan_id > 0) {
+		res = os_snprintf(buf + len, buflen - len, "vlan_id=%d\n",
+				  sta->vlan_id);
+		if (!os_snprintf_error(buflen - len, res))
+			len += res;
+	}
+
 	return len;
 }
 
@@ -164,7 +181,7 @@
 
 	if (hwaddr_aton(txtaddr, addr)) {
 		ret = os_snprintf(buf, buflen, "FAIL\n");
-		if (ret < 0 || (size_t) ret >= buflen)
+		if (os_snprintf_error(buflen, ret))
 			return 0;
 		return ret;
 	}
@@ -203,7 +220,7 @@
 	if (hwaddr_aton(txtaddr, addr) ||
 	    (sta = ap_get_sta(hapd, addr)) == NULL) {
 		ret = os_snprintf(buf, buflen, "FAIL\n");
-		if (ret < 0 || (size_t) ret >= buflen)
+		if (os_snprintf_error(buflen, ret))
 			return 0;
 		return ret;
 	}
@@ -422,7 +439,7 @@
 			  iface->num_sta_ht40_intolerant,
 			  iface->olbc_ht,
 			  iface->ht_op_mode);
-	if (ret < 0 || (size_t) ret >= buflen - len)
+	if (os_snprintf_error(buflen - len, ret))
 		return len;
 	len += ret;
 
@@ -444,7 +461,7 @@
 				  iface->dfs_cac_ms / 1000,
 				  left_time);
 	}
-	if (ret < 0 || (size_t) ret >= buflen - len)
+	if (os_snprintf_error(buflen - len, ret))
 		return len;
 	len += ret;
 
@@ -463,7 +480,7 @@
 			  iface->conf->vht_oper_chwidth,
 			  iface->conf->vht_oper_centr_freq_seg0_idx,
 			  iface->conf->vht_oper_centr_freq_seg1_idx);
-	if (ret < 0 || (size_t) ret >= buflen - len)
+	if (os_snprintf_error(buflen - len, ret))
 		return len;
 	len += ret;
 
@@ -480,7 +497,7 @@
 				  wpa_ssid_txt(bss->conf->ssid.ssid,
 					       bss->conf->ssid.ssid_len),
 				  (int) i, bss->num_sta);
-		if (ret < 0 || (size_t) ret >= buflen - len)
+		if (os_snprintf_error(buflen - len, ret))
 			return len;
 		len += ret;
 	}
@@ -527,3 +544,9 @@
 
 	return 0;
 }
+
+
+int hostapd_ctrl_iface_stop_ap(struct hostapd_data *hapd)
+{
+	return hostapd_drv_stop_ap(hapd);
+}
diff --git a/src/ap/ctrl_iface_ap.h b/src/ap/ctrl_iface_ap.h
index ee58b4c..e5297d0 100644
--- a/src/ap/ctrl_iface_ap.h
+++ b/src/ap/ctrl_iface_ap.h
@@ -23,6 +23,6 @@
 			      size_t buflen);
 int hostapd_parse_csa_settings(const char *pos,
 			       struct csa_settings *settings);
-
+int hostapd_ctrl_iface_stop_ap(struct hostapd_data *hapd);
 
 #endif /* CTRL_IFACE_AP_H */
diff --git a/src/ap/dfs.c b/src/ap/dfs.c
index a6ec20b..715f19b 100644
--- a/src/ap/dfs.c
+++ b/src/ap/dfs.c
@@ -1,7 +1,7 @@
 /*
  * DFS - Dynamic Frequency Selection
  * Copyright (c) 2002-2013, Jouni Malinen <j@w1.fi>
- * Copyright (c) 2013, Qualcomm Atheros, Inc.
+ * Copyright (c) 2013-2015, Qualcomm Atheros, Inc.
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -11,6 +11,7 @@
 
 #include "utils/common.h"
 #include "common/ieee802_11_defs.h"
+#include "common/hw_features_common.h"
 #include "common/wpa_ctrl.h"
 #include "hostapd.h"
 #include "ap_drv_ops.h"
@@ -121,6 +122,20 @@
 }
 
 
+static struct hostapd_channel_data *
+dfs_get_chan_data(struct hostapd_hw_modes *mode, int freq, int first_chan_idx)
+{
+	int i;
+
+	for (i = first_chan_idx; i < mode->num_channels; i++) {
+		if (mode->channels[i].freq == freq)
+			return &mode->channels[i];
+	}
+
+	return NULL;
+}
+
+
 static int dfs_chan_range_available(struct hostapd_hw_modes *mode,
 				    int first_chan_idx, int num_chans,
 				    int skip_radar)
@@ -128,15 +143,15 @@
 	struct hostapd_channel_data *first_chan, *chan;
 	int i;
 
-	if (first_chan_idx + num_chans >= mode->num_channels)
+	if (first_chan_idx + num_chans > mode->num_channels)
 		return 0;
 
 	first_chan = &mode->channels[first_chan_idx];
 
 	for (i = 0; i < num_chans; i++) {
-		chan = &mode->channels[first_chan_idx + i];
-
-		if (first_chan->freq + i * 20 != chan->freq)
+		chan = dfs_get_chan_data(mode, first_chan->freq + i * 20,
+					 first_chan_idx);
+		if (!chan)
 			return 0;
 
 		if (!dfs_channel_available(chan, skip_radar))
@@ -150,16 +165,10 @@
 static int is_in_chanlist(struct hostapd_iface *iface,
 			  struct hostapd_channel_data *chan)
 {
-	int *entry;
-
-	if (!iface->conf->chanlist)
+	if (!iface->conf->acs_ch_list.num)
 		return 1;
 
-	for (entry = iface->conf->chanlist; *entry != -1; entry++) {
-		if (*entry == chan->chan)
-			return 1;
-	}
-	return 0;
+	return freq_range_list_includes(&iface->conf->acs_ch_list, chan->chan);
 }
 
 
@@ -440,7 +449,8 @@
 	if (num_available_chandefs == 0)
 		return NULL;
 
-	os_get_random((u8 *) &_rand, sizeof(_rand));
+	if (os_get_random((u8 *) &_rand, sizeof(_rand)) < 0)
+		_rand = os_random();
 	chan_idx = _rand % num_available_chandefs;
 	dfs_find_channel(iface, &chan, chan_idx, skip_radar);
 
@@ -639,6 +649,16 @@
 	int res, n_chans, n_chans1, start_chan_idx, start_chan_idx1;
 	int skip_radar = 0;
 
+	if (!iface->current_mode) {
+		/*
+		 * This can happen with drivers that do not provide mode
+		 * information and as such, cannot really use hostapd for DFS.
+		 */
+		wpa_printf(MSG_DEBUG,
+			   "DFS: No current_mode information - assume no need to perform DFS operations by hostapd");
+		return 1;
+	}
+
 	iface->cac_started = 0;
 
 	do {
@@ -736,11 +756,19 @@
 
 	if (success) {
 		/* Complete iface/ap configuration */
-		set_dfs_state(iface, freq, ht_enabled, chan_offset,
-			      chan_width, cf1, cf2,
-			      HOSTAPD_CHAN_DFS_AVAILABLE);
-		iface->cac_started = 0;
-		hostapd_setup_interface_complete(iface, 0);
+		if (iface->drv_flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD) {
+			/* Complete AP configuration for the first bring up. */
+			if (iface->state != HAPD_IFACE_ENABLED)
+				hostapd_setup_interface_complete(iface, 0);
+			else
+				iface->cac_started = 0;
+		} else {
+			set_dfs_state(iface, freq, ht_enabled, chan_offset,
+				      chan_width, cf1, cf2,
+				      HOSTAPD_CHAN_DFS_AVAILABLE);
+			iface->cac_started = 0;
+			hostapd_setup_interface_complete(iface, 0);
+		}
 	}
 
 	return 0;
@@ -922,13 +950,17 @@
 {
 	int res;
 
-	if (!iface->conf->ieee80211h)
-		return 0;
-
 	wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_RADAR_DETECTED
 		"freq=%d ht_enabled=%d chan_offset=%d chan_width=%d cf1=%d cf2=%d",
 		freq, ht_enabled, chan_offset, chan_width, cf1, cf2);
 
+	/* Proceed only if DFS is not offloaded to the driver */
+	if (iface->drv_flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD)
+		return 0;
+
+	if (!iface->conf->ieee80211h)
+		return 0;
+
 	/* mark radar frequency as invalid */
 	set_dfs_state(iface, freq, ht_enabled, chan_offset, chan_width,
 		      cf1, cf2, HOSTAPD_CHAN_DFS_UNAVAILABLE);
@@ -952,6 +984,11 @@
 	wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_NOP_FINISHED
 		"freq=%d ht_enabled=%d chan_offset=%d chan_width=%d cf1=%d cf2=%d",
 		freq, ht_enabled, chan_offset, chan_width, cf1, cf2);
+
+	/* Proceed only if DFS is not offloaded to the driver */
+	if (iface->drv_flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD)
+		return 0;
+
 	/* TODO add correct implementation here */
 	set_dfs_state(iface, freq, ht_enabled, chan_offset, chan_width,
 		      cf1, cf2, HOSTAPD_CHAN_DFS_USABLE);
@@ -983,3 +1020,53 @@
 		res = dfs_check_chans_radar(iface, start_chan_idx1, n_chans1);
 	return res;
 }
+
+
+int hostapd_dfs_start_cac(struct hostapd_iface *iface, int freq,
+			  int ht_enabled, int chan_offset, int chan_width,
+			  int cf1, int cf2)
+{
+	wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_CAC_START
+		"freq=%d chan=%d chan_offset=%d width=%d seg0=%d "
+		"seg1=%d cac_time=%ds",
+		freq, (freq - 5000) / 5, chan_offset, chan_width, cf1, cf2, 60);
+	iface->cac_started = 1;
+	return 0;
+}
+
+
+/*
+ * Main DFS handler for offloaded case.
+ * 2 - continue channel/AP setup for non-DFS channel
+ * 1 - continue channel/AP setup for DFS channel
+ * 0 - channel/AP setup will be continued after CAC
+ * -1 - hit critical error
+ */
+int hostapd_handle_dfs_offload(struct hostapd_iface *iface)
+{
+	wpa_printf(MSG_DEBUG, "%s: iface->cac_started: %d",
+		   __func__, iface->cac_started);
+
+	/*
+	 * If DFS has already been started, then we are being called from a
+	 * callback to continue AP/channel setup. Reset the CAC start flag and
+	 * return.
+	 */
+	if (iface->cac_started) {
+		wpa_printf(MSG_DEBUG, "%s: iface->cac_started: %d",
+			   __func__, iface->cac_started);
+		iface->cac_started = 0;
+		return 1;
+	}
+
+	if (ieee80211_is_dfs(iface->freq)) {
+		wpa_printf(MSG_DEBUG, "%s: freq %d MHz requires DFS",
+			   __func__, iface->freq);
+		return 0;
+	}
+
+	wpa_printf(MSG_DEBUG,
+		   "%s: freq %d MHz does not require DFS. Continue channel/AP setup",
+		   __func__, iface->freq);
+	return 2;
+}
diff --git a/src/ap/dfs.h b/src/ap/dfs.h
index a619c55..be8c0e6 100644
--- a/src/ap/dfs.h
+++ b/src/ap/dfs.h
@@ -22,5 +22,9 @@
 			     int ht_enabled,
 			     int chan_offset, int chan_width, int cf1, int cf2);
 int hostapd_is_dfs_required(struct hostapd_iface *iface);
+int hostapd_dfs_start_cac(struct hostapd_iface *iface, int freq,
+			  int ht_enabled, int chan_offset, int chan_width,
+			  int cf1, int cf2);
+int hostapd_handle_dfs_offload(struct hostapd_iface *iface);
 
 #endif /* DFS_H */
diff --git a/src/ap/dhcp_snoop.c b/src/ap/dhcp_snoop.c
new file mode 100644
index 0000000..3a77225
--- /dev/null
+++ b/src/ap/dhcp_snoop.c
@@ -0,0 +1,178 @@
+/*
+ * DHCP snooping for Proxy ARP
+ * Copyright (c) 2014, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+#include <netinet/ip.h>
+#include <netinet/udp.h>
+
+#include "utils/common.h"
+#include "l2_packet/l2_packet.h"
+#include "hostapd.h"
+#include "sta_info.h"
+#include "ap_drv_ops.h"
+#include "x_snoop.h"
+#include "dhcp_snoop.h"
+
+struct bootp_pkt {
+	struct iphdr iph;
+	struct udphdr udph;
+	u8 op;
+	u8 htype;
+	u8 hlen;
+	u8 hops;
+	be32 xid;
+	be16 secs;
+	be16 flags;
+	be32 client_ip;
+	be32 your_ip;
+	be32 server_ip;
+	be32 relay_ip;
+	u8 hw_addr[16];
+	u8 serv_name[64];
+	u8 boot_file[128];
+	u8 exten[312];
+} STRUCT_PACKED;
+
+#define DHCPACK	5
+static const u8 ic_bootp_cookie[] = { 99, 130, 83, 99 };
+
+
+static const char * ipaddr_str(u32 addr)
+{
+	static char buf[17];
+
+	os_snprintf(buf, sizeof(buf), "%u.%u.%u.%u",
+		    (addr >> 24) & 0xff, (addr >> 16) & 0xff,
+		    (addr >> 8) & 0xff, addr & 0xff);
+	return buf;
+}
+
+
+static void handle_dhcp(void *ctx, const u8 *src_addr, const u8 *buf,
+			size_t len)
+{
+	struct hostapd_data *hapd = ctx;
+	const struct bootp_pkt *b;
+	struct sta_info *sta;
+	int exten_len;
+	const u8 *end, *pos;
+	int res, msgtype = 0, prefixlen = 32;
+	u32 subnet_mask = 0;
+	u16 tot_len;
+
+	exten_len = len - ETH_HLEN - (sizeof(*b) - sizeof(b->exten));
+	if (exten_len < 4)
+		return;
+
+	b = (const struct bootp_pkt *) &buf[ETH_HLEN];
+	tot_len = ntohs(b->iph.tot_len);
+	if (tot_len > (unsigned int) (len - ETH_HLEN))
+		return;
+
+	if (os_memcmp(b->exten, ic_bootp_cookie, ARRAY_SIZE(ic_bootp_cookie)))
+		return;
+
+	/* Parse DHCP options */
+	end = (const u8 *) b + tot_len;
+	pos = &b->exten[4];
+	while (pos < end && *pos != 0xff) {
+		const u8 *opt = pos++;
+
+		if (*opt == 0) /* padding */
+			continue;
+
+		pos += *pos + 1;
+		if (pos >= end)
+			break;
+
+		switch (*opt) {
+		case 1:  /* subnet mask */
+			if (opt[1] == 4)
+				subnet_mask = WPA_GET_BE32(&opt[2]);
+			if (subnet_mask == 0)
+				return;
+			while (!(subnet_mask & 0x1)) {
+				subnet_mask >>= 1;
+				prefixlen--;
+			}
+			break;
+		case 53: /* message type */
+			if (opt[1])
+				msgtype = opt[2];
+			break;
+		default:
+			break;
+		}
+	}
+
+	if (msgtype == DHCPACK) {
+		if (b->your_ip == 0)
+			return;
+
+		/* DHCPACK for DHCPREQUEST */
+		sta = ap_get_sta(hapd, b->hw_addr);
+		if (!sta)
+			return;
+
+		wpa_printf(MSG_DEBUG, "dhcp_snoop: Found DHCPACK for " MACSTR
+			   " @ IPv4 address %s/%d",
+			   MAC2STR(sta->addr), ipaddr_str(ntohl(b->your_ip)),
+			   prefixlen);
+
+		if (sta->ipaddr == b->your_ip)
+			return;
+
+		if (sta->ipaddr != 0) {
+			wpa_printf(MSG_DEBUG,
+				   "dhcp_snoop: Removing IPv4 address %s from the ip neigh table",
+				   ipaddr_str(be_to_host32(sta->ipaddr)));
+			hostapd_drv_br_delete_ip_neigh(hapd, 4,
+						       (u8 *) &sta->ipaddr);
+		}
+
+		res = hostapd_drv_br_add_ip_neigh(hapd, 4, (u8 *) &b->your_ip,
+						  prefixlen, sta->addr);
+		if (res) {
+			wpa_printf(MSG_DEBUG,
+				   "dhcp_snoop: Adding ip neigh table failed: %d",
+				   res);
+			return;
+		}
+		sta->ipaddr = b->your_ip;
+	}
+
+	if (hapd->conf->disable_dgaf && is_broadcast_ether_addr(buf)) {
+		for (sta = hapd->sta_list; sta; sta = sta->next) {
+			if (!(sta->flags & WLAN_STA_AUTHORIZED))
+				continue;
+			x_snoop_mcast_to_ucast_convert_send(hapd, sta,
+							    (u8 *) buf, len);
+		}
+	}
+}
+
+
+int dhcp_snoop_init(struct hostapd_data *hapd)
+{
+	hapd->sock_dhcp = x_snoop_get_l2_packet(hapd, handle_dhcp,
+						L2_PACKET_FILTER_DHCP);
+	if (hapd->sock_dhcp == NULL) {
+		wpa_printf(MSG_DEBUG,
+			   "dhcp_snoop: Failed to initialize L2 packet processing for DHCP packet: %s",
+			   strerror(errno));
+		return -1;
+	}
+
+	return 0;
+}
+
+
+void dhcp_snoop_deinit(struct hostapd_data *hapd)
+{
+	l2_packet_deinit(hapd->sock_dhcp);
+}
diff --git a/src/ap/dhcp_snoop.h b/src/ap/dhcp_snoop.h
new file mode 100644
index 0000000..93d0050
--- /dev/null
+++ b/src/ap/dhcp_snoop.h
@@ -0,0 +1,30 @@
+/*
+ * DHCP snooping for Proxy ARP
+ * Copyright (c) 2014, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef DHCP_SNOOP_H
+#define DHCP_SNOOP_H
+
+#ifdef CONFIG_PROXYARP
+
+int dhcp_snoop_init(struct hostapd_data *hapd);
+void dhcp_snoop_deinit(struct hostapd_data *hapd);
+
+#else /* CONFIG_PROXYARP */
+
+static inline int dhcp_snoop_init(struct hostapd_data *hapd)
+{
+	return 0;
+}
+
+static inline void dhcp_snoop_deinit(struct hostapd_data *hapd)
+{
+}
+
+#endif /* CONFIG_PROXYARP */
+
+#endif /* DHCP_SNOOP_H */
diff --git a/src/ap/drv_callbacks.c b/src/ap/drv_callbacks.c
index 3bde720..6ecd094 100644
--- a/src/ap/drv_callbacks.c
+++ b/src/ap/drv_callbacks.c
@@ -126,8 +126,6 @@
 #ifdef CONFIG_IEEE80211N
 #ifdef NEED_AP_MLME
 	if (elems.ht_capabilities &&
-	    elems.ht_capabilities_len >=
-	    sizeof(struct ieee80211_ht_capabilities) &&
 	    (hapd->iface->conf->ht_capab &
 	     HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET)) {
 		struct ieee80211_ht_capabilities *ht_cap =
@@ -439,12 +437,13 @@
 			     int offset, int width, int cf1, int cf2)
 {
 #ifdef NEED_AP_MLME
-	int channel, chwidth, seg0_idx = 0, seg1_idx = 0;
+	int channel, chwidth, seg0_idx = 0, seg1_idx = 0, is_dfs;
 
 	hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211,
-		       HOSTAPD_LEVEL_INFO, "driver had channel switch: "
-		       "freq=%d, ht=%d, offset=%d, width=%d, cf1=%d, cf2=%d",
-		       freq, ht, offset, width, cf1, cf2);
+		       HOSTAPD_LEVEL_INFO,
+		       "driver had channel switch: freq=%d, ht=%d, offset=%d, width=%d (%s), cf1=%d, cf2=%d",
+		       freq, ht, offset, width, channel_width_to_string(width),
+		       cf1, cf2);
 
 	hapd->iface->freq = freq;
 
@@ -489,18 +488,25 @@
 
 	hapd->iconf->channel = channel;
 	hapd->iconf->ieee80211n = ht;
+	if (!ht)
+		hapd->iconf->ieee80211ac = 0;
 	hapd->iconf->secondary_channel = offset;
 	hapd->iconf->vht_oper_chwidth = chwidth;
 	hapd->iconf->vht_oper_centr_freq_seg0_idx = seg0_idx;
 	hapd->iconf->vht_oper_centr_freq_seg1_idx = seg1_idx;
 
+	is_dfs = ieee80211_is_dfs(freq);
+
 	if (hapd->csa_in_progress &&
 	    freq == hapd->cs_freq_params.freq) {
 		hostapd_cleanup_cs_params(hapd);
 		ieee802_11_set_beacon(hapd);
 
-		wpa_msg(hapd->msg_ctx, MSG_INFO, AP_CSA_FINISHED "freq=%d",
-			freq);
+		wpa_msg(hapd->msg_ctx, MSG_INFO, AP_CSA_FINISHED
+			"freq=%d dfs=%d", freq, is_dfs);
+	} else if (hapd->iface->drv_flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD) {
+		wpa_msg(hapd->msg_ctx, MSG_INFO, AP_CSA_FINISHED
+			"freq=%d dfs=%d", freq, is_dfs);
 	}
 #endif /* NEED_AP_MLME */
 }
@@ -522,6 +528,94 @@
 }
 
 
+#ifdef CONFIG_ACS
+static void hostapd_acs_channel_selected(struct hostapd_data *hapd,
+					 struct acs_selected_channels *acs_res)
+{
+	int ret, i;
+
+	if (hapd->iconf->channel) {
+		wpa_printf(MSG_INFO, "ACS: Channel was already set to %d",
+			   hapd->iconf->channel);
+		return;
+	}
+
+	if (!hapd->iface->current_mode) {
+		for (i = 0; i < hapd->iface->num_hw_features; i++) {
+			struct hostapd_hw_modes *mode =
+				&hapd->iface->hw_features[i];
+
+			if (mode->mode == acs_res->hw_mode) {
+				hapd->iface->current_mode = mode;
+				break;
+			}
+		}
+		if (!hapd->iface->current_mode) {
+			hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211,
+				       HOSTAPD_LEVEL_WARNING,
+				       "driver selected to bad hw_mode");
+			return;
+		}
+	}
+
+	hapd->iface->freq = hostapd_hw_get_freq(hapd, acs_res->pri_channel);
+
+	if (!acs_res->pri_channel) {
+		hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211,
+			       HOSTAPD_LEVEL_WARNING,
+			       "driver switched to bad channel");
+		return;
+	}
+
+	hapd->iconf->channel = acs_res->pri_channel;
+	hapd->iconf->acs = 1;
+
+	if (acs_res->sec_channel == 0)
+		hapd->iconf->secondary_channel = 0;
+	else if (acs_res->sec_channel < acs_res->pri_channel)
+		hapd->iconf->secondary_channel = -1;
+	else if (acs_res->sec_channel > acs_res->pri_channel)
+		hapd->iconf->secondary_channel = 1;
+	else {
+		wpa_printf(MSG_ERROR, "Invalid secondary channel!");
+		return;
+	}
+
+	if (hapd->iface->conf->ieee80211ac) {
+		/* set defaults for backwards compatibility */
+		hapd->iconf->vht_oper_centr_freq_seg1_idx = 0;
+		hapd->iconf->vht_oper_centr_freq_seg0_idx = 0;
+		hapd->iconf->vht_oper_chwidth = VHT_CHANWIDTH_USE_HT;
+		if (acs_res->ch_width == 80) {
+			hapd->iconf->vht_oper_centr_freq_seg0_idx =
+				acs_res->vht_seg0_center_ch;
+			hapd->iconf->vht_oper_chwidth = VHT_CHANWIDTH_80MHZ;
+		} else if (acs_res->ch_width == 160) {
+			if (acs_res->vht_seg1_center_ch == 0) {
+				hapd->iconf->vht_oper_centr_freq_seg0_idx =
+					acs_res->vht_seg0_center_ch;
+				hapd->iconf->vht_oper_chwidth =
+					VHT_CHANWIDTH_160MHZ;
+			} else {
+				hapd->iconf->vht_oper_centr_freq_seg0_idx =
+					acs_res->vht_seg0_center_ch;
+				hapd->iconf->vht_oper_centr_freq_seg1_idx =
+					acs_res->vht_seg1_center_ch;
+				hapd->iconf->vht_oper_chwidth =
+					VHT_CHANWIDTH_80P80MHZ;
+			}
+		}
+	}
+
+	ret = hostapd_acs_completed(hapd->iface, 0);
+	if (ret) {
+		wpa_printf(MSG_ERROR,
+			   "ACS: Possibly channel configuration is invalid");
+	}
+}
+#endif /* CONFIG_ACS */
+
+
 int hostapd_probe_req_rx(struct hostapd_data *hapd, const u8 *sa, const u8 *da,
 			 const u8 *bssid, const u8 *ie, size_t ie_len,
 			 int ssi_signal)
@@ -858,6 +952,42 @@
 }
 
 
+static void hostapd_single_channel_get_survey(struct hostapd_iface *iface,
+					      struct survey_results *survey_res)
+{
+	struct hostapd_channel_data *chan;
+	struct freq_survey *survey;
+	u64 divisor, dividend;
+
+	survey = dl_list_first(&survey_res->survey_list, struct freq_survey,
+			       list);
+	if (!survey || !survey->freq)
+		return;
+
+	chan = hostapd_get_mode_channel(iface, survey->freq);
+	if (!chan || chan->flag & HOSTAPD_CHAN_DISABLED)
+		return;
+
+	wpa_printf(MSG_DEBUG, "Single Channel Survey: (freq=%d channel_time=%ld channel_time_busy=%ld)",
+		   survey->freq,
+		   (unsigned long int) survey->channel_time,
+		   (unsigned long int) survey->channel_time_busy);
+
+	if (survey->channel_time > iface->last_channel_time &&
+	    survey->channel_time > survey->channel_time_busy) {
+		dividend = survey->channel_time_busy -
+			iface->last_channel_time_busy;
+		divisor = survey->channel_time - iface->last_channel_time;
+
+		iface->channel_utilization = dividend * 255 / divisor;
+		wpa_printf(MSG_DEBUG, "Channel Utilization: %d",
+			   iface->channel_utilization);
+	}
+	iface->last_channel_time = survey->channel_time;
+	iface->last_channel_time_busy = survey->channel_time_busy;
+}
+
+
 static void hostapd_event_get_survey(struct hostapd_data *hapd,
 				     struct survey_results *survey_results)
 {
@@ -870,6 +1000,11 @@
 		return;
 	}
 
+	if (survey_results->freq_filter) {
+		hostapd_single_channel_get_survey(iface, survey_results);
+		return;
+	}
+
 	dl_list_for_each_safe(survey, tmp, &survey_results->survey_list,
 			      struct freq_survey, list) {
 		chan = hostapd_get_mode_channel(iface, survey->freq);
@@ -943,6 +1078,16 @@
 				 radar->cf1, radar->cf2);
 }
 
+
+static void hostapd_event_dfs_cac_started(struct hostapd_data *hapd,
+					  struct dfs_event *radar)
+{
+	wpa_printf(MSG_DEBUG, "DFS offload CAC started on %d MHz", radar->freq);
+	hostapd_dfs_start_cac(hapd->iface, radar->freq, radar->ht_enabled,
+			      radar->chan_offset, radar->chan_width,
+			      radar->cf1, radar->cf2);
+}
+
 #endif /* NEED_AP_MLME */
 
 
@@ -979,12 +1124,6 @@
 		if (hapd->iface->scan_cb)
 			hapd->iface->scan_cb(hapd->iface);
 		break;
-#ifdef CONFIG_IEEE80211R
-	case EVENT_FT_RRB_RX:
-		wpa_ft_rrb_rx(hapd->wpa_auth, data->ft_rrb_rx.src,
-			      data->ft_rrb_rx.data, data->ft_rrb_rx.data_len);
-		break;
-#endif /* CONFIG_IEEE80211R */
 	case EVENT_WPS_BUTTON_PUSHED:
 		hostapd_wps_button_pushed(hapd, NULL);
 		break;
@@ -1124,7 +1263,36 @@
 		hostapd_channel_list_updated(
 			hapd->iface, data->channel_list_changed.initiator);
 		break;
+	case EVENT_DFS_CAC_STARTED:
+		if (!data)
+			break;
+		hostapd_event_dfs_cac_started(hapd, &data->dfs_event);
+		break;
 #endif /* NEED_AP_MLME */
+	case EVENT_INTERFACE_ENABLED:
+		wpa_msg(hapd->msg_ctx, MSG_INFO, INTERFACE_ENABLED);
+		if (hapd->disabled && hapd->started) {
+			hapd->disabled = 0;
+			/*
+			 * Try to re-enable interface if the driver stopped it
+			 * when the interface got disabled.
+			 */
+			wpa_auth_reconfig_group_keys(hapd->wpa_auth);
+			hapd->reenable_beacon = 1;
+			ieee802_11_set_beacon(hapd);
+		}
+		break;
+	case EVENT_INTERFACE_DISABLED:
+		hostapd_free_stas(hapd);
+		wpa_msg(hapd->msg_ctx, MSG_INFO, INTERFACE_DISABLED);
+		hapd->disabled = 1;
+		break;
+#ifdef CONFIG_ACS
+	case EVENT_ACS_CHANNEL_SELECTED:
+		hostapd_acs_channel_selected(hapd,
+					     &data->acs_selected_channels);
+		break;
+#endif /* CONFIG_ACS */
 	default:
 		wpa_printf(MSG_DEBUG, "Unknown event %d", event);
 		break;
diff --git a/src/ap/eap_user_db.c b/src/ap/eap_user_db.c
index 559d77f..082d0f5 100644
--- a/src/ap/eap_user_db.c
+++ b/src/ap/eap_user_db.c
@@ -138,8 +138,12 @@
 	char id_str[256], cmd[300];
 	size_t i;
 
-	if (identity_len >= sizeof(id_str))
+	if (identity_len >= sizeof(id_str)) {
+		wpa_printf(MSG_DEBUG, "%s: identity len too big: %d >= %d",
+			   __func__, (int) identity_len,
+			   (int) (sizeof(id_str)));
 		return NULL;
+	}
 	os_memcpy(id_str, identity, identity_len);
 	id_str[identity_len] = '\0';
 	for (i = 0; i < identity_len; i++) {
@@ -182,7 +186,9 @@
 	wpa_printf(MSG_DEBUG, "DB: %s", cmd);
 	if (sqlite3_exec(db, cmd, get_user_cb, &hapd->tmp_eap_user, NULL) !=
 	    SQLITE_OK) {
-		wpa_printf(MSG_DEBUG, "DB: Failed to complete SQL operation");
+		wpa_printf(MSG_DEBUG,
+			   "DB: Failed to complete SQL operation: %s  db: %s",
+			   sqlite3_errmsg(db), hapd->conf->eap_user_sqlite);
 	} else if (hapd->tmp_eap_user.next)
 		user = &hapd->tmp_eap_user;
 
@@ -192,8 +198,10 @@
 		wpa_printf(MSG_DEBUG, "DB: %s", cmd);
 		if (sqlite3_exec(db, cmd, get_wildcard_cb, &hapd->tmp_eap_user,
 				 NULL) != SQLITE_OK) {
-			wpa_printf(MSG_DEBUG, "DB: Failed to complete SQL "
-				   "operation");
+			wpa_printf(MSG_DEBUG,
+				   "DB: Failed to complete SQL operation: %s  db: %s",
+				   sqlite3_errmsg(db),
+				   hapd->conf->eap_user_sqlite);
 		} else if (hapd->tmp_eap_user.next) {
 			user = &hapd->tmp_eap_user;
 			os_free(user->identity);
diff --git a/src/ap/gas_serv.c b/src/ap/gas_serv.c
index ad07107..9d19f98 100644
--- a/src/ap/gas_serv.c
+++ b/src/ap/gas_serv.c
@@ -58,7 +58,7 @@
 	}
 
 	if (sta->gas_dialog == NULL) {
-		sta->gas_dialog = os_zalloc(GAS_DIALOG_MAX *
+		sta->gas_dialog = os_calloc(GAS_DIALOG_MAX,
 					    sizeof(struct gas_dialog_info));
 		if (sta->gas_dialog == NULL)
 			return NULL;
@@ -748,6 +748,7 @@
 	size_t home_realm_query_len;
 	const u8 *icon_name;
 	size_t icon_name_len;
+	int p2p_sd;
 };
 
 
@@ -919,6 +920,21 @@
 		return;
 	}
 
+#ifdef CONFIG_P2P
+	if (*pos == P2P_OUI_TYPE) {
+		/*
+		 * This is for P2P SD and will be taken care of by the P2P
+		 * implementation. This query needs to be ignored in the generic
+		 * GAS server to avoid duplicated response.
+		 */
+		wpa_printf(MSG_DEBUG,
+			   "ANQP: Ignore WFA vendor type %u (P2P SD) in generic GAS server",
+			   *pos);
+		qi->p2p_sd = 1;
+		return;
+	}
+#endif /* CONFIG_P2P */
+
 	if (*pos != HS20_ANQP_OUI_TYPE) {
 		wpa_printf(MSG_DEBUG, "ANQP: Unsupported WFA vendor type %u",
 			   *pos);
@@ -969,6 +985,14 @@
 			buf);
 	if (!buf)
 		return;
+#ifdef CONFIG_P2P
+	if (wpabuf_len(buf) == 0 && qi->p2p_sd) {
+		wpa_printf(MSG_DEBUG,
+			   "ANQP: Do not send response to P2P SD from generic GAS service (P2P SD implementation will process this)");
+		wpabuf_free(buf);
+		return;
+	}
+#endif /* CONFIG_P2P */
 
 	if (wpabuf_len(buf) > hapd->gas_frag_limit ||
 	    hapd->conf->gas_comeback_delay) {
diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
index 3142391..5abe5ed 100644
--- a/src/ap/hostapd.c
+++ b/src/ap/hostapd.c
@@ -15,6 +15,8 @@
 #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 "hostapd.h"
 #include "authsrv.h"
 #include "sta_info.h"
@@ -35,6 +37,10 @@
 #include "gas_serv.h"
 #include "dfs.h"
 #include "ieee802_11.h"
+#include "bss_load.h"
+#include "x_snoop.h"
+#include "dhcp_snoop.h"
+#include "ndisc_snoop.h"
 
 
 static int hostapd_flush_old_stations(struct hostapd_data *hapd, u16 reason);
@@ -76,8 +82,7 @@
 		 * Force PSK to be derived again since SSID or passphrase may
 		 * have changed.
 		 */
-		os_free(ssid->wpa_psk);
-		ssid->wpa_psk = NULL;
+		hostapd_config_clear_wpa_psk(&hapd->conf->ssid.wpa_psk);
 	}
 	if (hostapd_setup_wpa_psk(hapd->conf)) {
 		wpa_printf(MSG_ERROR, "Failed to re-configure WPA PSK "
@@ -174,6 +179,7 @@
 		hapd = iface->bss[j];
 		hapd->iconf = newconf;
 		hapd->iconf->channel = oldconf->channel;
+		hapd->iconf->acs = oldconf->acs;
 		hapd->iconf->secondary_channel = oldconf->secondary_channel;
 		hapd->iconf->ieee80211n = oldconf->ieee80211n;
 		hapd->iconf->ieee80211ac = oldconf->ieee80211ac;
@@ -252,6 +258,16 @@
 
 static void hostapd_free_hapd_data(struct hostapd_data *hapd)
 {
+	os_free(hapd->probereq_cb);
+	hapd->probereq_cb = NULL;
+
+#ifdef CONFIG_P2P
+	wpabuf_free(hapd->p2p_beacon_ie);
+	hapd->p2p_beacon_ie = NULL;
+	wpabuf_free(hapd->p2p_probe_resp_ie);
+	hapd->p2p_probe_resp_ie = NULL;
+#endif /* CONFIG_P2P */
+
 	if (!hapd->started) {
 		wpa_printf(MSG_ERROR, "%s: Interface %s wasn't started",
 			   __func__, hapd->conf->iface);
@@ -294,28 +310,28 @@
 		}
 	}
 
-	os_free(hapd->probereq_cb);
-	hapd->probereq_cb = NULL;
-
-#ifdef CONFIG_P2P
-	wpabuf_free(hapd->p2p_beacon_ie);
-	hapd->p2p_beacon_ie = NULL;
-	wpabuf_free(hapd->p2p_probe_resp_ie);
-	hapd->p2p_probe_resp_ie = NULL;
-#endif /* CONFIG_P2P */
-
 	wpabuf_free(hapd->time_adv);
 
 #ifdef CONFIG_INTERWORKING
 	gas_serv_deinit(hapd);
 #endif /* CONFIG_INTERWORKING */
 
+	bss_load_update_deinit(hapd);
+	ndisc_snoop_deinit(hapd);
+	dhcp_snoop_deinit(hapd);
+	x_snoop_deinit(hapd);
+
 #ifdef CONFIG_SQLITE
 	bin_clear_free(hapd->tmp_eap_user.identity,
 		       hapd->tmp_eap_user.identity_len);
 	bin_clear_free(hapd->tmp_eap_user.password,
 		       hapd->tmp_eap_user.password_len);
 #endif /* CONFIG_SQLITE */
+
+#ifdef CONFIG_MESH
+	wpabuf_free(hapd->mesh_pending_auth);
+	hapd->mesh_pending_auth = NULL;
+#endif /* CONFIG_MESH */
 }
 
 
@@ -341,6 +357,11 @@
 static void hostapd_cleanup_iface_partial(struct hostapd_iface *iface)
 {
 	wpa_printf(MSG_DEBUG, "%s(%p)", __func__, iface);
+#ifdef CONFIG_IEEE80211N
+#ifdef NEED_AP_MLME
+	hostapd_stop_setup_timers(iface);
+#endif /* NEED_AP_MLME */
+#endif /* CONFIG_IEEE80211N */
 	hostapd_free_hw_features(iface->hw_features, iface->num_hw_features);
 	iface->hw_features = NULL;
 	os_free(iface->current_rates);
@@ -600,51 +621,190 @@
 
 
 static struct sta_info * hostapd_das_find_sta(struct hostapd_data *hapd,
-					      struct radius_das_attrs *attr)
+					      struct radius_das_attrs *attr,
+					      int *multi)
 {
-	struct sta_info *sta = NULL;
+	struct sta_info *selected, *sta;
 	char buf[128];
+	int num_attr = 0;
+	int count;
 
-	if (attr->sta_addr)
+	*multi = 0;
+
+	for (sta = hapd->sta_list; sta; sta = sta->next)
+		sta->radius_das_match = 1;
+
+	if (attr->sta_addr) {
+		num_attr++;
 		sta = ap_get_sta(hapd, attr->sta_addr);
+		if (!sta) {
+			wpa_printf(MSG_DEBUG,
+				   "RADIUS DAS: No Calling-Station-Id match");
+			return NULL;
+		}
 
-	if (sta == NULL && attr->acct_session_id &&
-	    attr->acct_session_id_len == 17) {
+		selected = sta;
 		for (sta = hapd->sta_list; sta; sta = sta->next) {
+			if (sta != selected)
+				sta->radius_das_match = 0;
+		}
+		wpa_printf(MSG_DEBUG, "RADIUS DAS: Calling-Station-Id match");
+	}
+
+	if (attr->acct_session_id) {
+		num_attr++;
+		if (attr->acct_session_id_len != 17) {
+			wpa_printf(MSG_DEBUG,
+				   "RADIUS DAS: Acct-Session-Id cannot match");
+			return NULL;
+		}
+		count = 0;
+
+		for (sta = hapd->sta_list; sta; sta = sta->next) {
+			if (!sta->radius_das_match)
+				continue;
 			os_snprintf(buf, sizeof(buf), "%08X-%08X",
 				    sta->acct_session_id_hi,
 				    sta->acct_session_id_lo);
-			if (os_memcmp(attr->acct_session_id, buf, 17) == 0)
-				break;
+			if (os_memcmp(attr->acct_session_id, buf, 17) != 0)
+				sta->radius_das_match = 0;
+			else
+				count++;
 		}
+
+		if (count == 0) {
+			wpa_printf(MSG_DEBUG,
+				   "RADIUS DAS: No matches remaining after Acct-Session-Id check");
+			return NULL;
+		}
+		wpa_printf(MSG_DEBUG, "RADIUS DAS: Acct-Session-Id match");
 	}
 
-	if (sta == NULL && attr->cui) {
+	if (attr->acct_multi_session_id) {
+		num_attr++;
+		if (attr->acct_multi_session_id_len != 17) {
+			wpa_printf(MSG_DEBUG,
+				   "RADIUS DAS: Acct-Multi-Session-Id cannot match");
+			return NULL;
+		}
+		count = 0;
+
+		for (sta = hapd->sta_list; sta; sta = sta->next) {
+			if (!sta->radius_das_match)
+				continue;
+			if (!sta->eapol_sm ||
+			    !sta->eapol_sm->acct_multi_session_id_hi) {
+				sta->radius_das_match = 0;
+				continue;
+			}
+			os_snprintf(buf, sizeof(buf), "%08X+%08X",
+				    sta->eapol_sm->acct_multi_session_id_hi,
+				    sta->eapol_sm->acct_multi_session_id_lo);
+			if (os_memcmp(attr->acct_multi_session_id, buf, 17) !=
+			    0)
+				sta->radius_das_match = 0;
+			else
+				count++;
+		}
+
+		if (count == 0) {
+			wpa_printf(MSG_DEBUG,
+				   "RADIUS DAS: No matches remaining after Acct-Multi-Session-Id check");
+			return NULL;
+		}
+		wpa_printf(MSG_DEBUG,
+			   "RADIUS DAS: Acct-Multi-Session-Id match");
+	}
+
+	if (attr->cui) {
+		num_attr++;
+		count = 0;
+
 		for (sta = hapd->sta_list; sta; sta = sta->next) {
 			struct wpabuf *cui;
+
+			if (!sta->radius_das_match)
+				continue;
 			cui = ieee802_1x_get_radius_cui(sta->eapol_sm);
-			if (cui && wpabuf_len(cui) == attr->cui_len &&
+			if (!cui || wpabuf_len(cui) != attr->cui_len ||
 			    os_memcmp(wpabuf_head(cui), attr->cui,
-				      attr->cui_len) == 0)
-				break;
+				      attr->cui_len) != 0)
+				sta->radius_das_match = 0;
+			else
+				count++;
 		}
+
+		if (count == 0) {
+			wpa_printf(MSG_DEBUG,
+				   "RADIUS DAS: No matches remaining after Chargeable-User-Identity check");
+			return NULL;
+		}
+		wpa_printf(MSG_DEBUG,
+			   "RADIUS DAS: Chargeable-User-Identity match");
 	}
 
-	if (sta == NULL && attr->user_name) {
+	if (attr->user_name) {
+		num_attr++;
+		count = 0;
+
 		for (sta = hapd->sta_list; sta; sta = sta->next) {
 			u8 *identity;
 			size_t identity_len;
+
+			if (!sta->radius_das_match)
+				continue;
 			identity = ieee802_1x_get_identity(sta->eapol_sm,
 							   &identity_len);
-			if (identity &&
-			    identity_len == attr->user_name_len &&
+			if (!identity ||
+			    identity_len != attr->user_name_len ||
 			    os_memcmp(identity, attr->user_name, identity_len)
-			    == 0)
-				break;
+			    != 0)
+				sta->radius_das_match = 0;
+			else
+				count++;
+		}
+
+		if (count == 0) {
+			wpa_printf(MSG_DEBUG,
+				   "RADIUS DAS: No matches remaining after User-Name check");
+			return NULL;
+		}
+		wpa_printf(MSG_DEBUG,
+			   "RADIUS DAS: User-Name match");
+	}
+
+	if (num_attr == 0) {
+		/*
+		 * In theory, we could match all current associations, but it
+		 * seems safer to just reject requests that do not include any
+		 * session identification attributes.
+		 */
+		wpa_printf(MSG_DEBUG,
+			   "RADIUS DAS: No session identification attributes included");
+		return NULL;
+	}
+
+	selected = NULL;
+	for (sta = hapd->sta_list; sta; sta = sta->next) {
+		if (sta->radius_das_match) {
+			if (selected) {
+				*multi = 1;
+				return NULL;
+			}
+			selected = sta;
 		}
 	}
 
-	return sta;
+	return selected;
+}
+
+
+static int hostapd_das_disconnect_pmksa(struct hostapd_data *hapd,
+					struct radius_das_attrs *attr)
+{
+	if (!hapd->wpa_auth)
+		return -1;
+	return wpa_auth_radius_das_disconnect_pmksa(hapd->wpa_auth, attr);
 }
 
 
@@ -653,14 +813,29 @@
 {
 	struct hostapd_data *hapd = ctx;
 	struct sta_info *sta;
+	int multi;
 
 	if (hostapd_das_nas_mismatch(hapd, attr))
 		return RADIUS_DAS_NAS_MISMATCH;
 
-	sta = hostapd_das_find_sta(hapd, attr);
-	if (sta == NULL)
+	sta = hostapd_das_find_sta(hapd, attr, &multi);
+	if (sta == NULL) {
+		if (multi) {
+			wpa_printf(MSG_DEBUG,
+				   "RADIUS DAS: Multiple sessions match - not supported");
+			return RADIUS_DAS_MULTI_SESSION_MATCH;
+		}
+		if (hostapd_das_disconnect_pmksa(hapd, attr) == 0) {
+			wpa_printf(MSG_DEBUG,
+				   "RADIUS DAS: PMKSA cache entry matched");
+			return RADIUS_DAS_SUCCESS;
+		}
+		wpa_printf(MSG_DEBUG, "RADIUS DAS: No matching session found");
 		return RADIUS_DAS_SESSION_NOT_FOUND;
+	}
 
+	wpa_printf(MSG_DEBUG, "RADIUS DAS: Found a matching session " MACSTR
+		   " - disconnecting", MAC2STR(sta->addr));
 	wpa_auth_pmksa_remove(hapd->wpa_auth, sta->addr);
 
 	hostapd_drv_sta_deauth(hapd, sta->addr,
@@ -687,10 +862,11 @@
 static int hostapd_setup_bss(struct hostapd_data *hapd, int first)
 {
 	struct hostapd_bss_config *conf = hapd->conf;
-	u8 ssid[HOSTAPD_MAX_SSID_LEN + 1];
+	u8 ssid[SSID_MAX_LEN + 1];
 	int ssid_len, set_ssid;
 	char force_ifname[IFNAMSIZ];
 	u8 if_addr[ETH_ALEN];
+	int flush_old_stations = 1;
 
 	wpa_printf(MSG_DEBUG, "%s(hapd=%p (%s), first=%d)",
 		   __func__, hapd, conf->iface, first);
@@ -745,7 +921,14 @@
 	if (conf->wmm_enabled < 0)
 		conf->wmm_enabled = hapd->iconf->ieee80211n;
 
-	hostapd_flush_old_stations(hapd, WLAN_REASON_PREV_AUTH_NOT_VALID);
+#ifdef CONFIG_MESH
+	if (hapd->iface->mconf == NULL)
+		flush_old_stations = 0;
+#endif /* CONFIG_MESH */
+
+	if (flush_old_stations)
+		hostapd_flush_old_stations(hapd,
+					   WLAN_REASON_PREV_AUTH_NOT_VALID);
 	hostapd_set_privacy(hapd, 0);
 
 	hostapd_broadcast_wep_clear(hapd);
@@ -875,6 +1058,31 @@
 	}
 #endif /* CONFIG_INTERWORKING */
 
+	if (conf->bss_load_update_period && bss_load_update_init(hapd)) {
+		wpa_printf(MSG_ERROR, "BSS Load initialization failed");
+		return -1;
+	}
+
+	if (conf->proxy_arp) {
+		if (x_snoop_init(hapd)) {
+			wpa_printf(MSG_ERROR,
+				   "Generic snooping infrastructure initialization failed");
+			return -1;
+		}
+
+		if (dhcp_snoop_init(hapd)) {
+			wpa_printf(MSG_ERROR,
+				   "DHCP snooping initialization failed");
+			return -1;
+		}
+
+		if (ndisc_snoop_init(hapd)) {
+			wpa_printf(MSG_ERROR,
+				   "Neighbor Discovery snooping initialization failed");
+			return -1;
+		}
+	}
+
 	if (!hostapd_drv_none(hapd) && vlan_init(hapd)) {
 		wpa_printf(MSG_ERROR, "VLAN initialization failed.");
 		return -1;
@@ -899,6 +1107,11 @@
 	int i;
 	struct hostapd_tx_queue_params *p;
 
+#ifdef CONFIG_MESH
+	if (iface->mconf == NULL)
+		return;
+#endif /* CONFIG_MESH */
+
 	for (i = 0; i < NUM_TX_QUEUES; i++) {
 		p = &iface->conf->tx_queue[i];
 
@@ -1164,6 +1377,8 @@
 	struct hostapd_data *hapd = iface->bss[0];
 	size_t j;
 	u8 *prev_addr;
+	int delay_apply_cfg = 0;
+	int res_dfs_offload = 0;
 
 	if (err)
 		goto fail;
@@ -1190,10 +1405,37 @@
 					goto fail;
 				return res;
 			}
+		} else {
+			/* If DFS is offloaded to the driver */
+			res_dfs_offload = hostapd_handle_dfs_offload(iface);
+			if (res_dfs_offload <= 0) {
+				if (res_dfs_offload < 0)
+					goto fail;
+			} else {
+				wpa_printf(MSG_DEBUG,
+					   "Proceed with AP/channel setup");
+				/*
+				 * If this is a DFS channel, move to completing
+				 * AP setup.
+				 */
+				if (res_dfs_offload == 1)
+					goto dfs_offload;
+				/* Otherwise fall through. */
+			}
 		}
 #endif /* NEED_AP_MLME */
 
-		if (hostapd_set_freq(hapd, hapd->iconf->hw_mode, iface->freq,
+#ifdef CONFIG_MESH
+		if (iface->mconf != NULL) {
+			wpa_printf(MSG_DEBUG,
+				   "%s: Mesh configuration will be applied while joining the mesh network",
+				   iface->bss[0]->conf->iface);
+			delay_apply_cfg = 1;
+		}
+#endif /* CONFIG_MESH */
+
+		if (!delay_apply_cfg &&
+		    hostapd_set_freq(hapd, hapd->iconf->hw_mode, iface->freq,
 				     hapd->iconf->channel,
 				     hapd->iconf->ieee80211n,
 				     hapd->iconf->ieee80211ac,
@@ -1274,6 +1516,19 @@
 			goto fail;
 	}
 
+	if ((iface->drv_flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD) &&
+	    !res_dfs_offload) {
+		/*
+		 * If freq is DFS, and DFS is offloaded to the driver, then wait
+		 * for CAC to complete.
+		 */
+		wpa_printf(MSG_DEBUG, "%s: Wait for CAC to complete", __func__);
+		return res_dfs_offload;
+	}
+
+#ifdef NEED_AP_MLME
+dfs_offload:
+#endif /* NEED_AP_MLME */
 	hostapd_set_state(iface, HAPD_IFACE_ENABLED);
 	wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, AP_EVENT_ENABLED);
 	if (hapd->setup_complete_cb)
@@ -1365,6 +1620,7 @@
 	wpa_printf(MSG_DEBUG, "%s: deinit bss %s", __func__,
 		   hapd->conf->iface);
 	hostapd_bss_deinit_no_free(hapd);
+	wpa_msg(hapd->msg_ctx, MSG_INFO, AP_EVENT_DISABLED);
 	hostapd_cleanup(hapd);
 }
 
@@ -1377,6 +1633,8 @@
 	if (iface == NULL)
 		return;
 
+	hostapd_set_state(iface, HAPD_IFACE_DISABLED);
+
 #ifdef CONFIG_IEEE80211N
 #ifdef NEED_AP_MLME
 	hostapd_stop_setup_timers(iface);
@@ -1809,33 +2067,37 @@
 }
 
 
-static struct hostapd_iface * hostapd_data_alloc(
-	struct hapd_interfaces *interfaces, struct hostapd_config *conf)
+static int hostapd_data_alloc(struct hostapd_iface *hapd_iface,
+			      struct hostapd_config *conf)
 {
 	size_t i;
-	struct hostapd_iface *hapd_iface =
-		interfaces->iface[interfaces->count - 1];
 	struct hostapd_data *hapd;
 
-	hapd_iface->conf = conf;
-	hapd_iface->num_bss = conf->num_bss;
-
-	hapd_iface->bss = os_zalloc(conf->num_bss *
+	hapd_iface->bss = os_calloc(conf->num_bss,
 				    sizeof(struct hostapd_data *));
 	if (hapd_iface->bss == NULL)
-		return NULL;
+		return -1;
 
 	for (i = 0; i < conf->num_bss; i++) {
 		hapd = hapd_iface->bss[i] =
 			hostapd_alloc_bss_data(hapd_iface, conf, conf->bss[i]);
-		if (hapd == NULL)
-			return NULL;
+		if (hapd == NULL) {
+			while (i > 0) {
+				i--;
+				os_free(hapd_iface->bss[i]);
+				hapd_iface->bss[i] = NULL;
+			}
+			os_free(hapd_iface->bss);
+			hapd_iface->bss = NULL;
+			return -1;
+		}
 		hapd->msg_ctx = hapd;
 	}
 
-	hapd_iface->interfaces = interfaces;
+	hapd_iface->conf = conf;
+	hapd_iface->num_bss = conf->num_bss;
 
-	return hapd_iface;
+	return 0;
 }
 
 
@@ -1882,9 +2144,14 @@
 		}
 
 		if (new_iface) {
-			if (interfaces->driver_init(hapd_iface) ||
-			    hostapd_setup_interface(hapd_iface)) {
-				interfaces->count--;
+			if (interfaces->driver_init(hapd_iface))
+				goto fail;
+
+			if (hostapd_setup_interface(hapd_iface)) {
+				hostapd_deinit_driver(
+					hapd_iface->bss[0]->driver,
+					hapd_iface->bss[0]->drv_priv,
+					hapd_iface);
 				goto fail;
 			}
 		} else {
@@ -1904,6 +2171,8 @@
 				hapd_iface->num_bss--;
 				wpa_printf(MSG_DEBUG, "%s: free hapd %p %s",
 					   __func__, hapd, hapd->conf->iface);
+				hostapd_config_free_bss(hapd->conf);
+				hapd->conf = NULL;
 				os_free(hapd);
 				return -1;
 			}
@@ -1934,6 +2203,7 @@
 			   "for interface", __func__);
 		goto fail;
 	}
+	new_iface = hapd_iface;
 
 	if (conf_file && interfaces->config_read_cb) {
 		conf = interfaces->config_read_cb(conf_file);
@@ -1948,17 +2218,18 @@
 		goto fail;
 	}
 
-	hapd_iface = hostapd_data_alloc(interfaces, conf);
-	if (hapd_iface == NULL) {
+	if (hostapd_data_alloc(hapd_iface, conf) < 0) {
 		wpa_printf(MSG_ERROR, "%s: Failed to allocate memory "
 			   "for hostapd", __func__);
 		goto fail;
 	}
+	conf = NULL;
 
 	if (start_ctrl_iface(hapd_iface) < 0)
 		goto fail;
 
-	wpa_printf(MSG_INFO, "Add interface '%s'", conf->bss[0]->iface);
+	wpa_printf(MSG_INFO, "Add interface '%s'",
+		   hapd_iface->conf->bss[0]->iface);
 
 	return 0;
 
@@ -1978,14 +2249,18 @@
 				wpa_printf(MSG_DEBUG, "%s: free hapd %p (%s)",
 					   __func__, hapd_iface->bss[i],
 					   hapd->conf->iface);
+				hostapd_cleanup(hapd);
 				os_free(hapd);
 				hapd_iface->bss[i] = NULL;
 			}
 			os_free(hapd_iface->bss);
+			hapd_iface->bss = NULL;
 		}
-		wpa_printf(MSG_DEBUG, "%s: free iface %p",
-			   __func__, hapd_iface);
-		os_free(hapd_iface);
+		if (new_iface) {
+			interfaces->count--;
+			interfaces->iface[interfaces->count] = NULL;
+		}
+		hostapd_cleanup_iface(hapd_iface);
 	}
 	return -1;
 }
@@ -2005,6 +2280,7 @@
 		wpa_printf(MSG_DEBUG, "%s: free hapd %p (%s)",
 			   __func__, hapd, hapd->conf->iface);
 		hostapd_config_free_bss(hapd->conf);
+		hapd->conf = NULL;
 		os_free(hapd);
 
 		iface->num_bss--;
@@ -2367,6 +2643,12 @@
 			   struct csa_settings *settings)
 {
 	int ret;
+
+	if (!(hapd->iface->drv_flags & WPA_DRIVER_FLAGS_AP_CSA)) {
+		wpa_printf(MSG_INFO, "CSA is not supported");
+		return -1;
+	}
+
 	ret = hostapd_fill_csa_settings(hapd, settings);
 	if (ret)
 		return ret;
diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h
index 3c8727b..be5c7a8 100644
--- a/src/ap/hostapd.h
+++ b/src/ap/hostapd.h
@@ -1,6 +1,6 @@
 /*
  * hostapd / Initialization and configuration
- * Copyright (c) 2002-2013, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2002-2014, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -10,6 +10,7 @@
 #define HOSTAPD_H
 
 #include "common/defs.h"
+#include "utils/list.h"
 #include "ap_config.h"
 #include "drivers/driver.h"
 
@@ -22,6 +23,9 @@
 struct full_dynamic_vlan;
 enum wps_event;
 union wps_event_data;
+#ifdef CONFIG_MESH
+struct mesh_conf;
+#endif /* CONFIG_MESH */
 
 struct hostapd_iface;
 
@@ -45,6 +49,9 @@
 	struct hostapd_iface **iface;
 
 	size_t terminate_on_error;
+#ifndef CONFIG_NO_VLAN
+	struct dynamic_iface *vlan_priv;
+#endif /* CONFIG_NO_VLAN */
 };
 
 enum hostapd_chan_status {
@@ -101,6 +108,8 @@
 	struct hostapd_bss_config *conf;
 	int interface_added; /* virtual interface added for this BSS */
 	unsigned int started:1;
+	unsigned int disabled:1;
+	unsigned int reenable_beacon:1;
 
 	u8 own_addr[ETH_ALEN];
 
@@ -150,6 +159,7 @@
 	void *ssl_ctx;
 	void *eap_sim_db_priv;
 	struct radius_server_data *radius_srv;
+	struct dl_list erp_keys; /* struct eap_server_erp_key */
 
 	int parameter_set_count;
 
@@ -218,6 +228,9 @@
 	unsigned int cs_c_off_proberesp;
 	int csa_in_progress;
 
+	/* BSS Load */
+	unsigned int bss_load_update_timeout;
+
 #ifdef CONFIG_P2P
 	struct p2p_data *p2p;
 	struct p2p_group *p2p_group;
@@ -235,6 +248,17 @@
 #ifdef CONFIG_INTERWORKING
 	size_t gas_frag_limit;
 #endif /* CONFIG_INTERWORKING */
+#ifdef CONFIG_PROXYARP
+	struct l2_packet_data *sock_dhcp;
+	struct l2_packet_data *sock_ndisc;
+#endif /* CONFIG_PROXYARP */
+#ifdef CONFIG_MESH
+	int num_plinks;
+	int max_plinks;
+	void (*mesh_sta_free_cb)(struct sta_info *sta);
+	struct wpabuf *mesh_pending_auth;
+	struct os_reltime mesh_pending_auth_time;
+#endif /* CONFIG_MESH */
 
 #ifdef CONFIG_SQLITE
 	struct hostapd_eap_user tmp_eap_user;
@@ -247,7 +271,10 @@
 #endif /* CONFIG_SAE */
 
 #ifdef CONFIG_TESTING_OPTIONS
-	int ext_mgmt_frame_handling;
+	unsigned int ext_mgmt_frame_handling:1;
+	unsigned int ext_eapol_frame_io:1;
+
+	struct l2_packet_data *l2_test;
 #endif /* CONFIG_TESTING_OPTIONS */
 };
 
@@ -272,6 +299,10 @@
 		HAPD_IFACE_ENABLED
 	} state;
 
+#ifdef CONFIG_MESH
+	struct mesh_conf *mconf;
+#endif /* CONFIG_MESH */
+
 	size_t num_bss;
 	struct hostapd_data **bss;
 
@@ -288,7 +319,10 @@
 	struct ap_info *ap_list; /* AP info list head */
 	struct ap_info *ap_hash[STA_HASH_SIZE];
 
-	unsigned int drv_flags;
+	u64 drv_flags;
+
+	/* SMPS modes supported by the driver (WPA_DRIVER_SMPS_MODE_*) */
+	unsigned int smps_modes;
 
 	/*
 	 * A bitmap of supported protocols for probe response offload. See
@@ -351,6 +385,11 @@
 	/* lowest observed noise floor in dBm */
 	s8 lowest_nf;
 
+	/* channel utilization calculation */
+	u64 last_channel_time;
+	u64 last_channel_time_busy;
+	u8 channel_utilization;
+
 	unsigned int dfs_cac_ms;
 	struct os_reltime dfs_cac_start;
 
diff --git a/src/ap/hw_features.c b/src/ap/hw_features.c
index 4e66d1b..28324a8 100644
--- a/src/ap/hw_features.c
+++ b/src/ap/hw_features.c
@@ -15,6 +15,7 @@
 #include "common/ieee802_11_defs.h"
 #include "common/ieee802_11_common.h"
 #include "common/wpa_ctrl.h"
+#include "common/hw_features_common.h"
 #include "hostapd.h"
 #include "ap_config.h"
 #include "ap_drv_ops.h"
@@ -74,7 +75,7 @@
 int hostapd_get_hw_features(struct hostapd_iface *iface)
 {
 	struct hostapd_data *hapd = iface->bss[0];
-	int ret = 0, i, j;
+	int i, j;
 	u16 num_modes, flags;
 	struct hostapd_hw_modes *modes;
 
@@ -107,7 +108,8 @@
 
 			/*
 			 * Disable all channels that are marked not to allow
-			 * IBSS operation or active scanning.
+			 * to initiate radiation (a.k.a. passive scan and no
+			 * IBSS).
 			 * Use radar channels only if the driver supports DFS.
 			 */
 			if ((feature->channels[j].flag &
@@ -118,8 +120,7 @@
 				    !(iface->drv_flags &
 				      WPA_DRIVER_FLAGS_DFS_OFFLOAD)) ||
 				   (feature->channels[j].flag &
-				    (HOSTAPD_CHAN_NO_IBSS |
-				     HOSTAPD_CHAN_PASSIVE_SCAN))) {
+				    HOSTAPD_CHAN_NO_IR)) {
 				feature->channels[j].flag |=
 					HOSTAPD_CHAN_DISABLED;
 			}
@@ -137,7 +138,7 @@
 		}
 	}
 
-	return ret;
+	return 0;
 }
 
 
@@ -223,66 +224,16 @@
 #ifdef CONFIG_IEEE80211N
 static int ieee80211n_allowed_ht40_channel_pair(struct hostapd_iface *iface)
 {
-	int sec_chan, ok, j, first;
-	int allowed[] = { 36, 44, 52, 60, 100, 108, 116, 124, 132, 149, 157,
-			  184, 192 };
-	size_t k;
+	int pri_chan, sec_chan;
 
 	if (!iface->conf->secondary_channel)
 		return 1; /* HT40 not used */
 
-	sec_chan = iface->conf->channel + iface->conf->secondary_channel * 4;
-	wpa_printf(MSG_DEBUG, "HT40: control channel: %d  "
-		   "secondary channel: %d",
-		   iface->conf->channel, sec_chan);
+	pri_chan = iface->conf->channel;
+	sec_chan = pri_chan + iface->conf->secondary_channel * 4;
 
-	/* Verify that HT40 secondary channel is an allowed 20 MHz
-	 * channel */
-	ok = 0;
-	for (j = 0; j < iface->current_mode->num_channels; j++) {
-		struct hostapd_channel_data *chan =
-			&iface->current_mode->channels[j];
-		if (!(chan->flag & HOSTAPD_CHAN_DISABLED) &&
-		    chan->chan == sec_chan) {
-			ok = 1;
-			break;
-		}
-	}
-	if (!ok) {
-		wpa_printf(MSG_ERROR, "HT40 secondary channel %d not allowed",
-			   sec_chan);
-		return 0;
-	}
-
-	/*
-	 * Verify that HT40 primary,secondary channel pair is allowed per
-	 * IEEE 802.11n Annex J. This is only needed for 5 GHz band since
-	 * 2.4 GHz rules allow all cases where the secondary channel fits into
-	 * the list of allowed channels (already checked above).
-	 */
-	if (iface->current_mode->mode != HOSTAPD_MODE_IEEE80211A)
-		return 1;
-
-	if (iface->conf->secondary_channel > 0)
-		first = iface->conf->channel;
-	else
-		first = sec_chan;
-
-	ok = 0;
-	for (k = 0; k < ARRAY_SIZE(allowed); k++) {
-		if (first == allowed[k]) {
-			ok = 1;
-			break;
-		}
-	}
-	if (!ok) {
-		wpa_printf(MSG_ERROR, "HT40 channel pair (%d, %d) not allowed",
-			   iface->conf->channel,
-			   iface->conf->secondary_channel);
-		return 0;
-	}
-
-	return 1;
+	return allowed_ht40_channel_pair(iface->current_mode, pri_chan,
+					 sec_chan);
 }
 
 
@@ -298,214 +249,34 @@
 }
 
 
-static void ieee80211n_get_pri_sec_chan(struct wpa_scan_res *bss,
-					int *pri_chan, int *sec_chan)
-{
-	struct ieee80211_ht_operation *oper;
-	struct ieee802_11_elems elems;
-
-	*pri_chan = *sec_chan = 0;
-
-	ieee802_11_parse_elems((u8 *) (bss + 1), bss->ie_len, &elems, 0);
-	if (elems.ht_operation &&
-	    elems.ht_operation_len >= sizeof(*oper)) {
-		oper = (struct ieee80211_ht_operation *) elems.ht_operation;
-		*pri_chan = oper->primary_chan;
-		if (oper->ht_param & HT_INFO_HT_PARAM_STA_CHNL_WIDTH) {
-			int sec = oper->ht_param &
-				HT_INFO_HT_PARAM_SECONDARY_CHNL_OFF_MASK;
-			if (sec == HT_INFO_HT_PARAM_SECONDARY_CHNL_ABOVE)
-				*sec_chan = *pri_chan + 4;
-			else if (sec == HT_INFO_HT_PARAM_SECONDARY_CHNL_BELOW)
-				*sec_chan = *pri_chan - 4;
-		}
-	}
-}
-
-
 static int ieee80211n_check_40mhz_5g(struct hostapd_iface *iface,
 				     struct wpa_scan_results *scan_res)
 {
-	int pri_chan, sec_chan, pri_freq, sec_freq, pri_bss, sec_bss;
-	int bss_pri_chan, bss_sec_chan;
-	size_t i;
-	int match;
+	int pri_chan, sec_chan;
+	int res;
 
 	pri_chan = iface->conf->channel;
 	sec_chan = pri_chan + iface->conf->secondary_channel * 4;
-	pri_freq = hostapd_hw_get_freq(iface->bss[0], pri_chan);
-	if (iface->conf->secondary_channel > 0)
-		sec_freq = pri_freq + 20;
-	else
-		sec_freq = pri_freq - 20;
 
-	/*
-	 * Switch PRI/SEC channels if Beacons were detected on selected SEC
-	 * channel, but not on selected PRI channel.
-	 */
-	pri_bss = sec_bss = 0;
-	for (i = 0; i < scan_res->num; i++) {
-		struct wpa_scan_res *bss = scan_res->res[i];
-		if (bss->freq == pri_freq)
-			pri_bss++;
-		else if (bss->freq == sec_freq)
-			sec_bss++;
-	}
-	if (sec_bss && !pri_bss) {
-		wpa_printf(MSG_INFO, "Switch own primary and secondary "
-			   "channel to get secondary channel with no Beacons "
-			   "from other BSSes");
+	res = check_40mhz_5g(iface->current_mode, scan_res, pri_chan, sec_chan);
+
+	if (res == 2)
 		ieee80211n_switch_pri_sec(iface);
-		return 1;
-	}
 
-	/*
-	 * Match PRI/SEC channel with any existing HT40 BSS on the same
-	 * channels that we are about to use (if already mixed order in
-	 * existing BSSes, use own preference).
-	 */
-	match = 0;
-	for (i = 0; i < scan_res->num; i++) {
-		struct wpa_scan_res *bss = scan_res->res[i];
-		ieee80211n_get_pri_sec_chan(bss, &bss_pri_chan, &bss_sec_chan);
-		if (pri_chan == bss_pri_chan &&
-		    sec_chan == bss_sec_chan) {
-			match = 1;
-			break;
-		}
-	}
-	if (!match) {
-		for (i = 0; i < scan_res->num; i++) {
-			struct wpa_scan_res *bss = scan_res->res[i];
-			ieee80211n_get_pri_sec_chan(bss, &bss_pri_chan,
-						    &bss_sec_chan);
-			if (pri_chan == bss_sec_chan &&
-			    sec_chan == bss_pri_chan) {
-				wpa_printf(MSG_INFO, "Switch own primary and "
-					   "secondary channel due to BSS "
-					   "overlap with " MACSTR,
-					   MAC2STR(bss->bssid));
-				ieee80211n_switch_pri_sec(iface);
-				break;
-			}
-		}
-	}
-
-	return 1;
-}
-
-
-static int ieee80211n_check_20mhz_bss(struct wpa_scan_res *bss, int pri_freq,
-				      int start, int end)
-{
-	struct ieee802_11_elems elems;
-	struct ieee80211_ht_operation *oper;
-
-	if (bss->freq < start || bss->freq > end || bss->freq == pri_freq)
-		return 0;
-
-	ieee802_11_parse_elems((u8 *) (bss + 1), bss->ie_len, &elems, 0);
-	if (!elems.ht_capabilities) {
-		wpa_printf(MSG_DEBUG, "Found overlapping legacy BSS: "
-			   MACSTR " freq=%d", MAC2STR(bss->bssid), bss->freq);
-		return 1;
-	}
-
-	if (elems.ht_operation &&
-	    elems.ht_operation_len >= sizeof(*oper)) {
-		oper = (struct ieee80211_ht_operation *) elems.ht_operation;
-		if (oper->ht_param & HT_INFO_HT_PARAM_SECONDARY_CHNL_OFF_MASK)
-			return 0;
-
-		wpa_printf(MSG_DEBUG, "Found overlapping 20 MHz HT BSS: "
-			   MACSTR " freq=%d", MAC2STR(bss->bssid), bss->freq);
-		return 1;
-	}
-	return 0;
+	return !!res;
 }
 
 
 static int ieee80211n_check_40mhz_2g4(struct hostapd_iface *iface,
 				      struct wpa_scan_results *scan_res)
 {
-	int pri_freq, sec_freq;
-	int affected_start, affected_end;
-	size_t i;
+	int pri_chan, sec_chan;
 
-	pri_freq = hostapd_hw_get_freq(iface->bss[0], iface->conf->channel);
-	if (iface->conf->secondary_channel > 0)
-		sec_freq = pri_freq + 20;
-	else
-		sec_freq = pri_freq - 20;
-	affected_start = (pri_freq + sec_freq) / 2 - 25;
-	affected_end = (pri_freq + sec_freq) / 2 + 25;
-	wpa_printf(MSG_DEBUG, "40 MHz affected channel range: [%d,%d] MHz",
-		   affected_start, affected_end);
-	for (i = 0; i < scan_res->num; i++) {
-		struct wpa_scan_res *bss = scan_res->res[i];
-		int pri = bss->freq;
-		int sec = pri;
-		int sec_chan, pri_chan;
-		struct ieee802_11_elems elems;
+	pri_chan = iface->conf->channel;
+	sec_chan = pri_chan + iface->conf->secondary_channel * 4;
 
-		/* Check for overlapping 20 MHz BSS */
-		if (ieee80211n_check_20mhz_bss(bss, pri_freq, affected_start,
-					       affected_end)) {
-			wpa_printf(MSG_DEBUG,
-				   "Overlapping 20 MHz BSS is found");
-			return 0;
-		}
-
-		ieee80211n_get_pri_sec_chan(bss, &pri_chan, &sec_chan);
-
-		if (sec_chan) {
-			if (sec_chan < pri_chan)
-				sec = pri - 20;
-			else
-				sec = pri + 20;
-		}
-
-		if ((pri < affected_start || pri > affected_end) &&
-		    (sec < affected_start || sec > affected_end))
-			continue; /* not within affected channel range */
-
-		wpa_printf(MSG_DEBUG, "Neighboring BSS: " MACSTR
-			   " freq=%d pri=%d sec=%d",
-			   MAC2STR(bss->bssid), bss->freq, pri_chan, sec_chan);
-
-		if (sec_chan) {
-			if (pri_freq != pri || sec_freq != sec) {
-				wpa_printf(MSG_DEBUG, "40 MHz pri/sec "
-					   "mismatch with BSS " MACSTR
-					   " <%d,%d> (chan=%d%c) vs. <%d,%d>",
-					   MAC2STR(bss->bssid),
-					   pri, sec, pri_chan,
-					   sec > pri ? '+' : '-',
-					   pri_freq, sec_freq);
-				return 0;
-			}
-		}
-
-		ieee802_11_parse_elems((u8 *) (bss + 1), bss->ie_len, &elems,
-				       0);
-		if (elems.ht_capabilities &&
-		    elems.ht_capabilities_len >=
-		    sizeof(struct ieee80211_ht_capabilities)) {
-			struct ieee80211_ht_capabilities *ht_cap =
-				(struct ieee80211_ht_capabilities *)
-				elems.ht_capabilities;
-
-			if (le_to_host16(ht_cap->ht_capabilities_info) &
-			    HT_CAP_INFO_40MHZ_INTOLERANT) {
-				wpa_printf(MSG_DEBUG,
-					   "40 MHz Intolerant is set on channel %d in BSS "
-					   MACSTR, pri, MAC2STR(bss->bssid));
-				return 0;
-			}
-		}
-	}
-
-	return 1;
+	return check_40mhz_2g4(iface->current_mode, scan_res, pri_chan,
+			       sec_chan);
 }
 
 
@@ -576,8 +347,13 @@
 		sec_freq = pri_freq + 20;
 	else
 		sec_freq = pri_freq - 20;
-	affected_start = (pri_freq + sec_freq) / 2 - 25;
-	affected_end = (pri_freq + sec_freq) / 2 + 25;
+	/*
+	 * Note: Need to find the PRI channel also in cases where the affected
+	 * channel is the SEC channel of a 40 MHz BSS, so need to include the
+	 * scanning coverage here to be 40 MHz from the center frequency.
+	 */
+	affected_start = (pri_freq + sec_freq) / 2 - 40;
+	affected_end = (pri_freq + sec_freq) / 2 + 40;
 	wpa_printf(MSG_DEBUG, "40 MHz affected channel range: [%d,%d] MHz",
 		   affected_start, affected_end);
 
@@ -739,18 +515,35 @@
 		return 0;
 	}
 
-	if ((conf & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET) &&
+	/*
+	 * Driver ACS chosen channel may not be HT40 due to internal driver
+	 * restrictions.
+	 */
+	if (!iface->conf->acs && (conf & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET) &&
 	    !(hw & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET)) {
 		wpa_printf(MSG_ERROR, "Driver does not support configured "
 			   "HT capability [HT40*]");
 		return 0;
 	}
 
-	if ((conf & HT_CAP_INFO_SMPS_MASK) != (hw & HT_CAP_INFO_SMPS_MASK) &&
-	    (conf & HT_CAP_INFO_SMPS_MASK) != HT_CAP_INFO_SMPS_DISABLED) {
-		wpa_printf(MSG_ERROR, "Driver does not support configured "
-			   "HT capability [SMPS-*]");
-		return 0;
+	switch (conf & HT_CAP_INFO_SMPS_MASK) {
+	case HT_CAP_INFO_SMPS_STATIC:
+		if (!(iface->smps_modes & WPA_DRIVER_SMPS_MODE_STATIC)) {
+			wpa_printf(MSG_ERROR,
+				   "Driver does not support configured HT capability [SMPS-STATIC]");
+			return 0;
+		}
+		break;
+	case HT_CAP_INFO_SMPS_DYNAMIC:
+		if (!(iface->smps_modes & WPA_DRIVER_SMPS_MODE_DYNAMIC)) {
+			wpa_printf(MSG_ERROR,
+				   "Driver does not support configured HT capability [SMPS-DYNAMIC]");
+			return 0;
+		}
+		break;
+	case HT_CAP_INFO_SMPS_DISABLED:
+	default:
+		break;
 	}
 
 	if ((conf & HT_CAP_INFO_GREEN_FIELD) &&
@@ -839,16 +632,16 @@
 }
 
 
-static int ieee80211ac_cap_check_max(u32 hw, u32 conf, u32 cap,
+static int ieee80211ac_cap_check_max(u32 hw, u32 conf, u32 mask,
+				     unsigned int shift,
 				     const char *name)
 {
-	u32 hw_max = hw & cap;
-	u32 conf_val = conf & cap;
+	u32 hw_max = hw & mask;
+	u32 conf_val = conf & mask;
 
 	if (conf_val > hw_max) {
-		int offset = find_first_bit(cap);
 		wpa_printf(MSG_ERROR, "Configured VHT capability [%s] exceeds max value supported by the driver (%d > %d)",
-			   name, conf_val >> offset, hw_max >> offset);
+			   name, conf_val >> shift, hw_max >> shift);
 		return 0;
 	}
 	return 1;
@@ -857,12 +650,31 @@
 
 static int ieee80211ac_supported_vht_capab(struct hostapd_iface *iface)
 {
-	u32 hw = iface->current_mode->vht_capab;
+	struct hostapd_hw_modes *mode = iface->current_mode;
+	u32 hw = mode->vht_capab;
 	u32 conf = iface->conf->vht_capab;
 
 	wpa_printf(MSG_DEBUG, "hw vht capab: 0x%x, conf vht capab: 0x%x",
 		   hw, conf);
 
+	if (mode->mode == HOSTAPD_MODE_IEEE80211G &&
+	    iface->conf->bss[0]->vendor_vht &&
+	    mode->vht_capab == 0 && iface->hw_features) {
+		int i;
+
+		for (i = 0; i < iface->num_hw_features; i++) {
+			if (iface->hw_features[i].mode ==
+			    HOSTAPD_MODE_IEEE80211A) {
+				mode = &iface->hw_features[i];
+				hw = mode->vht_capab;
+				wpa_printf(MSG_DEBUG,
+					   "update hw vht capab based on 5 GHz band: 0x%x",
+					   hw);
+				break;
+			}
+		}
+	}
+
 #define VHT_CAP_CHECK(cap) \
 	do { \
 		if (!ieee80211ac_cap_check(hw, conf, cap, #cap)) \
@@ -871,7 +683,8 @@
 
 #define VHT_CAP_CHECK_MAX(cap) \
 	do { \
-		if (!ieee80211ac_cap_check_max(hw, conf, cap, #cap)) \
+		if (!ieee80211ac_cap_check_max(hw, conf, cap, cap ## _SHIFT, \
+					       #cap)) \
 			return 0; \
 	} while (0)
 
@@ -936,6 +749,9 @@
 	int i;
 	struct hostapd_channel_data *chan;
 
+	if (!iface->current_mode)
+		return 0;
+
 	for (i = 0; i < iface->current_mode->num_channels; i++) {
 		chan = &iface->current_mode->channels[i];
 		if (chan->chan != channel)
@@ -945,12 +761,10 @@
 			return 1;
 
 		wpa_printf(MSG_DEBUG,
-			   "%schannel [%i] (%i) is disabled for use in AP mode, flags: 0x%x%s%s%s",
+			   "%schannel [%i] (%i) is disabled for use in AP mode, flags: 0x%x%s%s",
 			   primary ? "" : "Configured HT40 secondary ",
 			   i, chan->chan, chan->flag,
-			   chan->flag & HOSTAPD_CHAN_NO_IBSS ? " NO-IBSS" : "",
-			   chan->flag & HOSTAPD_CHAN_PASSIVE_SCAN ?
-			   " PASSIVE-SCAN" : "",
+			   chan->flag & HOSTAPD_CHAN_NO_IR ? " NO-IR" : "",
 			   chan->flag & HOSTAPD_CHAN_RADAR ? " RADAR" : "");
 	}
 
@@ -999,6 +813,12 @@
 
 static void hostapd_notify_bad_chans(struct hostapd_iface *iface)
 {
+	if (!iface->current_mode) {
+		hostapd_logger(iface->bss[0], NULL, HOSTAPD_MODULE_IEEE80211,
+			       HOSTAPD_LEVEL_WARNING,
+			       "Hardware does not support configured mode");
+		return;
+	}
 	hostapd_logger(iface->bss[0], NULL,
 		       HOSTAPD_MODULE_IEEE80211,
 		       HOSTAPD_LEVEL_WARNING,
@@ -1089,14 +909,18 @@
 	}
 
 	if (iface->current_mode == NULL) {
-		wpa_printf(MSG_ERROR, "Hardware does not support configured "
-			   "mode");
-		hostapd_logger(iface->bss[0], NULL, HOSTAPD_MODULE_IEEE80211,
-			       HOSTAPD_LEVEL_WARNING,
-			       "Hardware does not support configured mode "
-			       "(%d) (hw_mode in hostapd.conf)",
-			       (int) iface->conf->hw_mode);
-		return -2;
+		if (!(iface->drv_flags & WPA_DRIVER_FLAGS_ACS_OFFLOAD) ||
+		    !(iface->drv_flags & WPA_DRIVER_FLAGS_SUPPORT_HW_MODE_ANY))
+		{
+			wpa_printf(MSG_ERROR,
+				   "Hardware does not support configured mode");
+			hostapd_logger(iface->bss[0], NULL,
+				       HOSTAPD_MODULE_IEEE80211,
+				       HOSTAPD_LEVEL_WARNING,
+				       "Hardware does not support configured mode (%d) (hw_mode in hostapd.conf)",
+				       (int) iface->conf->hw_mode);
+			return -2;
+		}
 	}
 
 	switch (hostapd_check_chans(iface)) {
@@ -1131,35 +955,11 @@
 
 int hostapd_hw_get_freq(struct hostapd_data *hapd, int chan)
 {
-	int i;
-
-	if (!hapd->iface->current_mode)
-		return 0;
-
-	for (i = 0; i < hapd->iface->current_mode->num_channels; i++) {
-		struct hostapd_channel_data *ch =
-			&hapd->iface->current_mode->channels[i];
-		if (ch->chan == chan)
-			return ch->freq;
-	}
-
-	return 0;
+	return hw_get_freq(hapd->iface->current_mode, chan);
 }
 
 
 int hostapd_hw_get_channel(struct hostapd_data *hapd, int freq)
 {
-	int i;
-
-	if (!hapd->iface->current_mode)
-		return 0;
-
-	for (i = 0; i < hapd->iface->current_mode->num_channels; i++) {
-		struct hostapd_channel_data *ch =
-			&hapd->iface->current_mode->channels[i];
-		if (ch->freq == freq)
-			return ch->chan;
-	}
-
-	return 0;
+	return hw_get_chan(hapd->iface->current_mode, freq);
 }
diff --git a/src/ap/hw_features.h b/src/ap/hw_features.h
index 0f67ab8..ca7f22b 100644
--- a/src/ap/hw_features.h
+++ b/src/ap/hw_features.h
@@ -36,6 +36,11 @@
 	return -1;
 }
 
+static inline int hostapd_acs_completed(struct hostapd_iface *iface, int err)
+{
+	return -1;
+}
+
 static inline int hostapd_select_hw_mode(struct hostapd_iface *iface)
 {
 	return -100;
diff --git a/src/ap/iapp.c b/src/ap/iapp.c
index 9b2900f..99aa04d 100644
--- a/src/ap/iapp.c
+++ b/src/ap/iapp.c
@@ -361,7 +361,7 @@
 
 	switch (hdr->command) {
 	case IAPP_CMD_ADD_notify:
-		iapp_process_add_notify(iapp, &from, hdr, hlen - sizeof(*hdr));
+		iapp_process_add_notify(iapp, &from, hdr, len - sizeof(*hdr));
 		break;
 	case IAPP_CMD_MOVE_notify:
 		/* TODO: MOVE is using TCP; so move this to TCP handler once it
diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
index de1ee5e..db20c86 100644
--- a/src/ap/ieee802_11.c
+++ b/src/ap/ieee802_11.c
@@ -1,6 +1,6 @@
 /*
  * hostapd / IEEE 802.11 Management
- * Copyright (c) 2002-2013, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2002-2014, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -29,6 +29,7 @@
 #include "sta_info.h"
 #include "ieee802_1x.h"
 #include "wpa_auth.h"
+#include "pmksa_cache_auth.h"
 #include "wmm.h"
 #include "ap_list.h"
 #include "accounting.h"
@@ -131,8 +132,7 @@
 }
 
 
-u16 hostapd_own_capab_info(struct hostapd_data *hapd, struct sta_info *sta,
-			   int probe)
+u16 hostapd_own_capab_info(struct hostapd_data *hapd)
 {
 	int capab = WLAN_CAPABILITY_ESS;
 	int privacy;
@@ -165,20 +165,6 @@
 		privacy = 1;
 #endif /* CONFIG_HS20 */
 
-	if (sta) {
-		int policy, def_klen;
-		if (probe && sta->ssid_probe) {
-			policy = sta->ssid_probe->security_policy;
-			def_klen = sta->ssid_probe->wep.default_len;
-		} else {
-			policy = sta->ssid->security_policy;
-			def_klen = sta->ssid->wep.default_len;
-		}
-		privacy = policy != SECURITY_PLAINTEXT;
-		if (policy == SECURITY_IEEE_802_1X && def_klen == 0)
-			privacy = 0;
-	}
-
 	if (privacy)
 		capab |= WLAN_CAPABILITY_PRIVACY;
 
@@ -198,6 +184,9 @@
 	    (hapd->iconf->spectrum_mgmt_required || dfs))
 		capab |= WLAN_CAPABILITY_SPECTRUM_MGMT;
 
+	if (hapd->conf->radio_measurements)
+		capab |= IEEE80211_CAP_RRM;
+
 	return capab;
 }
 
@@ -324,8 +313,12 @@
 
 #ifdef CONFIG_SAE
 
-static struct wpabuf * auth_process_sae_commit(struct hostapd_data *hapd,
-					       struct sta_info *sta)
+#define dot11RSNASAERetransPeriod 40	/* msec */
+#define dot11RSNASAESync 5		/* attempts */
+
+
+static struct wpabuf * auth_build_sae_commit(struct hostapd_data *hapd,
+					     struct sta_info *sta, int update)
 {
 	struct wpabuf *buf;
 
@@ -334,7 +327,8 @@
 		return NULL;
 	}
 
-	if (sae_prepare_commit(hapd->own_addr, sta->addr,
+	if (update &&
+	    sae_prepare_commit(hapd->own_addr, sta->addr,
 			       (u8 *) hapd->conf->ssid.wpa_passphrase,
 			       os_strlen(hapd->conf->ssid.wpa_passphrase),
 			       sta->sae) < 0) {
@@ -342,15 +336,11 @@
 		return NULL;
 	}
 
-	if (sae_process_commit(sta->sae) < 0) {
-		wpa_printf(MSG_DEBUG, "SAE: Failed to process peer commit");
-		return NULL;
-	}
-
 	buf = wpabuf_alloc(SAE_COMMIT_MAX_LEN);
 	if (buf == NULL)
 		return NULL;
-	sae_write_commit(sta->sae, buf, NULL);
+	sae_write_commit(sta->sae, buf, sta->sae->tmp ?
+			 sta->sae->tmp->anti_clogging_token : NULL);
 
 	return buf;
 }
@@ -371,6 +361,46 @@
 }
 
 
+static int auth_sae_send_commit(struct hostapd_data *hapd,
+				struct sta_info *sta,
+				const u8 *bssid, int update)
+{
+	struct wpabuf *data;
+
+	data = auth_build_sae_commit(hapd, sta, update);
+	if (data == NULL)
+		return WLAN_STATUS_UNSPECIFIED_FAILURE;
+
+	send_auth_reply(hapd, sta->addr, bssid,
+			WLAN_AUTH_SAE, 1, WLAN_STATUS_SUCCESS,
+			wpabuf_head(data), wpabuf_len(data));
+
+	wpabuf_free(data);
+
+	return WLAN_STATUS_SUCCESS;
+}
+
+
+static int auth_sae_send_confirm(struct hostapd_data *hapd,
+				 struct sta_info *sta,
+				 const u8 *bssid)
+{
+	struct wpabuf *data;
+
+	data = auth_build_sae_confirm(hapd, sta);
+	if (data == NULL)
+		return WLAN_STATUS_UNSPECIFIED_FAILURE;
+
+	send_auth_reply(hapd, sta->addr, bssid,
+			WLAN_AUTH_SAE, 2, WLAN_STATUS_SUCCESS,
+			wpabuf_head(data), wpabuf_len(data));
+
+	wpabuf_free(data);
+
+	return WLAN_STATUS_SUCCESS;
+}
+
+
 static int use_sae_anti_clogging(struct hostapd_data *hapd)
 {
 	struct sta_info *sta;
@@ -411,7 +441,7 @@
 
 
 static struct wpabuf * auth_build_token_req(struct hostapd_data *hapd,
-					    const u8 *addr)
+					    int group, const u8 *addr)
 {
 	struct wpabuf *buf;
 	u8 *token;
@@ -428,10 +458,12 @@
 		hapd->last_sae_token_key_update = now;
 	}
 
-	buf = wpabuf_alloc(SHA256_MAC_LEN);
+	buf = wpabuf_alloc(sizeof(le16) + SHA256_MAC_LEN);
 	if (buf == NULL)
 		return NULL;
 
+	wpabuf_put_le16(buf, group); /* Finite Cyclic Group */
+
 	token = wpabuf_put(buf, SHA256_MAC_LEN);
 	hmac_sha256(hapd->sae_token_key, sizeof(hapd->sae_token_key),
 		    addr, ETH_ALEN, token);
@@ -440,32 +472,309 @@
 }
 
 
+static int sae_check_big_sync(struct sta_info *sta)
+{
+	if (sta->sae->sync > dot11RSNASAESync) {
+		sta->sae->state = SAE_NOTHING;
+		sta->sae->sync = 0;
+		return -1;
+	}
+	return 0;
+}
+
+
+static void auth_sae_retransmit_timer(void *eloop_ctx, void *eloop_data)
+{
+	struct hostapd_data *hapd = eloop_ctx;
+	struct sta_info *sta = eloop_data;
+	int ret;
+
+	if (sae_check_big_sync(sta))
+		return;
+	sta->sae->sync++;
+
+	switch (sta->sae->state) {
+	case SAE_COMMITTED:
+		ret = auth_sae_send_commit(hapd, sta, hapd->own_addr, 0);
+		eloop_register_timeout(0, 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,
+				       auth_sae_retransmit_timer, hapd, sta);
+		break;
+	default:
+		ret = -1;
+		break;
+	}
+
+	if (ret != WLAN_STATUS_SUCCESS)
+		wpa_printf(MSG_INFO, "SAE: Failed to retransmit: ret=%d", ret);
+}
+
+
+void sae_clear_retransmit_timer(struct hostapd_data *hapd, struct sta_info *sta)
+{
+	eloop_cancel_timeout(auth_sae_retransmit_timer, hapd, sta);
+}
+
+
+static void sae_set_retransmit_timer(struct hostapd_data *hapd,
+				     struct sta_info *sta)
+{
+	if (!(hapd->conf->mesh & MESH_ENABLED))
+		return;
+
+	eloop_cancel_timeout(auth_sae_retransmit_timer, hapd, sta);
+	eloop_register_timeout(0, dot11RSNASAERetransPeriod * 1000,
+			       auth_sae_retransmit_timer, hapd, sta);
+}
+
+
+static int sae_sm_step(struct hostapd_data *hapd, struct sta_info *sta,
+		       const u8 *bssid, u8 auth_transaction)
+{
+	int ret;
+
+	if (auth_transaction != 1 && auth_transaction != 2)
+		return WLAN_STATUS_UNSPECIFIED_FAILURE;
+
+	switch (sta->sae->state) {
+	case SAE_NOTHING:
+		if (auth_transaction == 1) {
+			ret = auth_sae_send_commit(hapd, sta, bssid, 1);
+			if (ret)
+				return ret;
+			sta->sae->state = SAE_COMMITTED;
+
+			if (sae_process_commit(sta->sae) < 0)
+				return WLAN_STATUS_UNSPECIFIED_FAILURE;
+
+			/*
+			 * In mesh case, both Commit and Confirm can be sent
+			 * immediately. In infrastructure BSS, only a single
+			 * Authentication frame (Commit) is expected from the AP
+			 * here and the second one (Confirm) will be sent once
+			 * the STA has sent its second Authentication frame
+			 * (Confirm).
+			 */
+			if (hapd->conf->mesh & MESH_ENABLED) {
+				/*
+				 * Send both Commit and Confirm immediately
+				 * based on SAE finite state machine
+				 * Nothing -> Confirm transition.
+				 */
+				ret = auth_sae_send_confirm(hapd, sta, bssid);
+				if (ret)
+					return ret;
+				sta->sae->state = SAE_CONFIRMED;
+			} else {
+				/*
+				 * For infrastructure BSS, send only the Commit
+				 * message now to get alternating sequence of
+				 * Authentication frames between the AP and STA.
+				 * Confirm will be sent in
+				 * Commited -> Confirmed/Accepted transition
+				 * when receiving Confirm from STA.
+				 */
+			}
+			sta->sae->sync = 0;
+			sae_set_retransmit_timer(hapd, sta);
+		} else {
+			hostapd_logger(hapd, sta->addr,
+				       HOSTAPD_MODULE_IEEE80211,
+				       HOSTAPD_LEVEL_DEBUG,
+				       "SAE confirm before commit");
+		}
+		break;
+	case SAE_COMMITTED:
+		sae_clear_retransmit_timer(hapd, sta);
+		if (auth_transaction == 1) {
+			if (sae_process_commit(sta->sae) < 0)
+				return WLAN_STATUS_UNSPECIFIED_FAILURE;
+
+			ret = auth_sae_send_confirm(hapd, sta, bssid);
+			if (ret)
+				return ret;
+			sta->sae->state = SAE_CONFIRMED;
+			sta->sae->sync = 0;
+			sae_set_retransmit_timer(hapd, sta);
+		} else if (hapd->conf->mesh & MESH_ENABLED) {
+			/*
+			 * In mesh case, follow SAE finite state machine and
+			 * send Commit now, if sync count allows.
+			 */
+			if (sae_check_big_sync(sta))
+				return WLAN_STATUS_SUCCESS;
+			sta->sae->sync++;
+
+			ret = auth_sae_send_commit(hapd, sta, bssid, 0);
+			if (ret)
+				return ret;
+
+			sae_set_retransmit_timer(hapd, sta);
+		} else {
+			/*
+			 * For instructure BSS, send the postponed Confirm from
+			 * Nothing -> Confirmed transition that was reduced to
+			 * Nothing -> Committed above.
+			 */
+			ret = auth_sae_send_confirm(hapd, sta, bssid);
+			if (ret)
+				return ret;
+
+			sta->sae->state = SAE_CONFIRMED;
+
+			/*
+			 * Since this was triggered on Confirm RX, run another
+			 * step to get to Accepted without waiting for
+			 * additional events.
+			 */
+			return sae_sm_step(hapd, sta, bssid, auth_transaction);
+		}
+		break;
+	case SAE_CONFIRMED:
+		sae_clear_retransmit_timer(hapd, sta);
+		if (auth_transaction == 1) {
+			if (sae_check_big_sync(sta))
+				return WLAN_STATUS_SUCCESS;
+			sta->sae->sync++;
+
+			ret = auth_sae_send_commit(hapd, sta, bssid, 1);
+			if (ret)
+				return ret;
+
+			if (sae_process_commit(sta->sae) < 0)
+				return WLAN_STATUS_UNSPECIFIED_FAILURE;
+
+			ret = auth_sae_send_confirm(hapd, sta, bssid);
+			if (ret)
+				return ret;
+
+			sae_set_retransmit_timer(hapd, sta);
+		} else {
+			sta->flags |= WLAN_STA_AUTH;
+			sta->auth_alg = WLAN_AUTH_SAE;
+			mlme_authenticate_indication(hapd, sta);
+			wpa_auth_sm_event(sta->wpa_sm, WPA_AUTH);
+			sta->sae->state = SAE_ACCEPTED;
+			wpa_auth_pmksa_add_sae(hapd->wpa_auth, sta->addr,
+					       sta->sae->pmk);
+		}
+		break;
+	case SAE_ACCEPTED:
+		if (auth_transaction == 1) {
+			wpa_printf(MSG_DEBUG, "SAE: remove the STA (" MACSTR
+				   ") doing reauthentication",
+				   MAC2STR(sta->addr));
+			ap_free_sta(hapd, sta);
+		} else {
+			if (sae_check_big_sync(sta))
+				return WLAN_STATUS_SUCCESS;
+			sta->sae->sync++;
+
+			ret = auth_sae_send_confirm(hapd, sta, bssid);
+			sae_clear_temp_data(sta->sae);
+			if (ret)
+				return ret;
+		}
+		break;
+	default:
+		wpa_printf(MSG_ERROR, "SAE: invalid state %d",
+			   sta->sae->state);
+		return WLAN_STATUS_UNSPECIFIED_FAILURE;
+	}
+	return WLAN_STATUS_SUCCESS;
+}
+
+
 static void handle_auth_sae(struct hostapd_data *hapd, struct sta_info *sta,
 			    const struct ieee80211_mgmt *mgmt, size_t len,
-			    u8 auth_transaction)
+			    u16 auth_transaction, u16 status_code)
 {
 	u16 resp = WLAN_STATUS_SUCCESS;
 	struct wpabuf *data = NULL;
 
 	if (!sta->sae) {
-		if (auth_transaction != 1)
+		if (auth_transaction != 1 || status_code != WLAN_STATUS_SUCCESS)
 			return;
 		sta->sae = os_zalloc(sizeof(*sta->sae));
 		if (sta->sae == NULL)
 			return;
 		sta->sae->state = SAE_NOTHING;
+		sta->sae->sync = 0;
 	}
 
 	if (auth_transaction == 1) {
-		const u8 *token = NULL;
+		const u8 *token = NULL, *pos, *end;
 		size_t token_len = 0;
 		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
 			       HOSTAPD_LEVEL_DEBUG,
-			       "start SAE authentication (RX commit)");
+			       "start SAE authentication (RX commit, status=%u)",
+			       status_code);
+
+		if ((hapd->conf->mesh & MESH_ENABLED) &&
+		    status_code == WLAN_STATUS_ANTI_CLOGGING_TOKEN_REQ &&
+		    sta->sae->tmp) {
+			pos = mgmt->u.auth.variable;
+			end = ((const u8 *) mgmt) + len;
+			if (pos + sizeof(le16) > end) {
+				wpa_printf(MSG_ERROR,
+					   "SAE: Too short anti-clogging token request");
+				resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+				goto reply;
+			}
+			resp = sae_group_allowed(sta->sae,
+						 hapd->conf->sae_groups,
+						 WPA_GET_LE16(pos));
+			if (resp != WLAN_STATUS_SUCCESS) {
+				wpa_printf(MSG_ERROR,
+					   "SAE: Invalid group in anti-clogging token request");
+				goto reply;
+			}
+			pos += sizeof(le16);
+
+			wpabuf_free(sta->sae->tmp->anti_clogging_token);
+			sta->sae->tmp->anti_clogging_token =
+				wpabuf_alloc_copy(pos, end - pos);
+			if (sta->sae->tmp->anti_clogging_token == NULL) {
+				wpa_printf(MSG_ERROR,
+					   "SAE: Failed to alloc for anti-clogging token");
+				return;
+			}
+
+			/*
+			 * IEEE Std 802.11-2012, 11.3.8.6.4: If the Status code
+			 * is 76, a new Commit Message shall be constructed
+			 * with the Anti-Clogging Token from the received
+			 * Authentication frame, and the commit-scalar and
+			 * COMMIT-ELEMENT previously sent.
+			 */
+			if (auth_sae_send_commit(hapd, sta, mgmt->bssid, 0)) {
+				wpa_printf(MSG_ERROR,
+					   "SAE: Failed to send commit message");
+				return;
+			}
+			sta->sae->state = SAE_COMMITTED;
+			sta->sae->sync = 0;
+			sae_set_retransmit_timer(hapd, sta);
+			return;
+		}
+
+		if (status_code != WLAN_STATUS_SUCCESS)
+			return;
+
 		resp = sae_parse_commit(sta->sae, mgmt->u.auth.variable,
 					((const u8 *) mgmt) + len -
 					mgmt->u.auth.variable, &token,
 					&token_len, hapd->conf->sae_groups);
+		if (resp == SAE_SILENTLY_DISCARD) {
+			wpa_printf(MSG_DEBUG,
+				   "SAE: Drop commit message from " MACSTR " due to reflection attack",
+				   MAC2STR(sta->addr));
+			return;
+		}
 		if (token && check_sae_token(hapd, sta->addr, token, token_len)
 		    < 0) {
 			wpa_printf(MSG_DEBUG, "SAE: Drop commit message with "
@@ -474,69 +783,91 @@
 			return;
 		}
 
-		if (resp == WLAN_STATUS_SUCCESS) {
-			if (!token && use_sae_anti_clogging(hapd)) {
-				wpa_printf(MSG_DEBUG, "SAE: Request anti-"
-					   "clogging token from " MACSTR,
-					   MAC2STR(sta->addr));
-				data = auth_build_token_req(hapd, sta->addr);
-				resp = WLAN_STATUS_ANTI_CLOGGING_TOKEN_REQ;
-			} else {
-				data = auth_process_sae_commit(hapd, sta);
-				if (data == NULL)
-					resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
-				else
-					sta->sae->state = SAE_COMMITTED;
-			}
+		if (resp != WLAN_STATUS_SUCCESS)
+			goto reply;
+
+		if (!token && use_sae_anti_clogging(hapd)) {
+			wpa_printf(MSG_DEBUG,
+				   "SAE: Request anti-clogging token from "
+				   MACSTR, MAC2STR(sta->addr));
+			data = auth_build_token_req(hapd, sta->sae->group,
+						    sta->addr);
+			resp = WLAN_STATUS_ANTI_CLOGGING_TOKEN_REQ;
+			if (hapd->conf->mesh & MESH_ENABLED)
+				sta->sae->state = SAE_NOTHING;
+			goto reply;
 		}
+
+		resp = sae_sm_step(hapd, sta, mgmt->bssid, auth_transaction);
 	} else if (auth_transaction == 2) {
-		if (sta->sae->state != SAE_COMMITTED) {
-			hostapd_logger(hapd, sta->addr,
-				       HOSTAPD_MODULE_IEEE80211,
-				       HOSTAPD_LEVEL_DEBUG,
-				       "SAE confirm before commit");
-			resp = WLAN_STATUS_UNKNOWN_AUTH_TRANSACTION;
-			goto failed;
-		}
 		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
 			       HOSTAPD_LEVEL_DEBUG,
-			       "SAE authentication (RX confirm)");
-		if (sae_check_confirm(sta->sae, mgmt->u.auth.variable,
-				       ((u8 *) mgmt) + len -
-				       mgmt->u.auth.variable) < 0) {
-			resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
-		} else {
-			resp = WLAN_STATUS_SUCCESS;
-			sta->flags |= WLAN_STA_AUTH;
-			wpa_auth_sm_event(sta->wpa_sm, WPA_AUTH);
-			sta->auth_alg = WLAN_AUTH_SAE;
-			mlme_authenticate_indication(hapd, sta);
-
-			data = auth_build_sae_confirm(hapd, sta);
-			if (data == NULL)
+			       "SAE authentication (RX confirm, status=%u)",
+			       status_code);
+		if (status_code != WLAN_STATUS_SUCCESS)
+			return;
+		if (sta->sae->state >= SAE_CONFIRMED ||
+		    !(hapd->conf->mesh & MESH_ENABLED)) {
+			if (sae_check_confirm(sta->sae, mgmt->u.auth.variable,
+					      ((u8 *) mgmt) + len -
+					      mgmt->u.auth.variable) < 0) {
 				resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
-			else {
-				sta->sae->state = SAE_ACCEPTED;
-				sae_clear_temp_data(sta->sae);
+				goto reply;
 			}
 		}
+		resp = sae_sm_step(hapd, sta, mgmt->bssid, auth_transaction);
 	} else {
 		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
 			       HOSTAPD_LEVEL_DEBUG,
-			       "unexpected SAE authentication transaction %u",
-			       auth_transaction);
+			       "unexpected SAE authentication transaction %u (status=%u)",
+			       auth_transaction, status_code);
+		if (status_code != WLAN_STATUS_SUCCESS)
+			return;
 		resp = WLAN_STATUS_UNKNOWN_AUTH_TRANSACTION;
 	}
 
-failed:
-	sta->auth_alg = WLAN_AUTH_SAE;
-
-	send_auth_reply(hapd, mgmt->sa, mgmt->bssid, WLAN_AUTH_SAE,
-			auth_transaction, resp,
-			data ? wpabuf_head(data) : (u8 *) "",
-			data ? wpabuf_len(data) : 0);
+reply:
+	if (resp != WLAN_STATUS_SUCCESS) {
+		send_auth_reply(hapd, mgmt->sa, mgmt->bssid, WLAN_AUTH_SAE,
+				auth_transaction, resp,
+				data ? wpabuf_head(data) : (u8 *) "",
+				data ? wpabuf_len(data) : 0);
+	}
 	wpabuf_free(data);
 }
+
+
+/**
+ * auth_sae_init_committed - Send COMMIT and start SAE in committed state
+ * @hapd: BSS data for the device initiating the authentication
+ * @sta: the peer to which commit authentication frame is sent
+ *
+ * This function implements Init event handling (IEEE Std 802.11-2012,
+ * 11.3.8.6.3) in which initial COMMIT message is sent. Prior to calling, the
+ * sta->sae structure should be initialized appropriately via a call to
+ * sae_prepare_commit().
+ */
+int auth_sae_init_committed(struct hostapd_data *hapd, struct sta_info *sta)
+{
+	int ret;
+
+	if (!sta->sae || !sta->sae->tmp)
+		return -1;
+
+	if (sta->sae->state != SAE_NOTHING)
+		return -1;
+
+	ret = auth_sae_send_commit(hapd, sta, hapd->own_addr, 0);
+	if (ret)
+		return -1;
+
+	sta->sae->state = SAE_COMMITTED;
+	sta->sae->sync = 0;
+	sae_set_retransmit_timer(hapd, sta);
+
+	return 0;
+}
+
 #endif /* CONFIG_SAE */
 
 
@@ -556,6 +887,7 @@
 	size_t resp_ies_len = 0;
 	char *identity = NULL;
 	char *radius_cui = NULL;
+	u16 seq_ctrl;
 
 	if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.auth)) {
 		wpa_printf(MSG_INFO, "handle_auth - too short payload (len=%lu)",
@@ -577,6 +909,7 @@
 	auth_transaction = le_to_host16(mgmt->u.auth.auth_transaction);
 	status_code = le_to_host16(mgmt->u.auth.status_code);
 	fc = le_to_host16(mgmt->frame_control);
+	seq_ctrl = le_to_host16(mgmt->seq_ctrl);
 
 	if (len >= IEEE80211_HDRLEN + sizeof(mgmt->u.auth) +
 	    2 + WLAN_AUTH_CHALLENGE_LEN &&
@@ -585,10 +918,12 @@
 		challenge = &mgmt->u.auth.variable[2];
 
 	wpa_printf(MSG_DEBUG, "authentication: STA=" MACSTR " auth_alg=%d "
-		   "auth_transaction=%d status_code=%d wep=%d%s",
+		   "auth_transaction=%d status_code=%d wep=%d%s "
+		   "seq_ctrl=0x%x%s",
 		   MAC2STR(mgmt->sa), auth_alg, auth_transaction,
 		   status_code, !!(fc & WLAN_FC_ISWEP),
-		   challenge ? " challenge" : "");
+		   challenge ? " challenge" : "",
+		   seq_ctrl, (fc & WLAN_FC_RETRY) ? " retry" : "");
 
 	if (hapd->tkip_countermeasures) {
 		resp = WLAN_REASON_MICHAEL_MIC_FAILURE;
@@ -649,11 +984,46 @@
 		return;
 	}
 
-	sta = ap_sta_add(hapd, mgmt->sa);
-	if (!sta) {
-		resp = WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA;
-		goto fail;
+	sta = ap_get_sta(hapd, mgmt->sa);
+	if (sta) {
+		if ((fc & WLAN_FC_RETRY) &&
+		    sta->last_seq_ctrl != WLAN_INVALID_MGMT_SEQ &&
+		    sta->last_seq_ctrl == seq_ctrl &&
+		    sta->last_subtype == WLAN_FC_STYPE_AUTH) {
+			hostapd_logger(hapd, sta->addr,
+				       HOSTAPD_MODULE_IEEE80211,
+				       HOSTAPD_LEVEL_DEBUG,
+				       "Drop repeated authentication frame seq_ctrl=0x%x",
+				       seq_ctrl);
+			return;
+		}
+	} else {
+#ifdef CONFIG_MESH
+		if (hapd->conf->mesh & MESH_ENABLED) {
+			/* if the mesh peer is not available, we don't do auth.
+			 */
+			wpa_printf(MSG_DEBUG, "Mesh peer " MACSTR
+				   " not yet known - drop Authentiation frame",
+				   MAC2STR(mgmt->sa));
+			/*
+			 * Save a copy of the frame so that it can be processed
+			 * if a new peer entry is added shortly after this.
+			 */
+			wpabuf_free(hapd->mesh_pending_auth);
+			hapd->mesh_pending_auth = wpabuf_alloc_copy(mgmt, len);
+			os_get_reltime(&hapd->mesh_pending_auth_time);
+			return;
+		}
+#endif /* CONFIG_MESH */
+
+		sta = ap_sta_add(hapd, mgmt->sa);
+		if (!sta) {
+			resp = WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA;
+			goto fail;
+		}
 	}
+	sta->last_seq_ctrl = seq_ctrl;
+	sta->last_subtype = WLAN_FC_STYPE_AUTH;
 
 	if (vlan_id > 0) {
 		if (!hostapd_vlan_id_valid(hapd->conf->vlan, vlan_id)) {
@@ -737,7 +1107,23 @@
 #endif /* CONFIG_IEEE80211R */
 #ifdef CONFIG_SAE
 	case WLAN_AUTH_SAE:
-		handle_auth_sae(hapd, sta, mgmt, len, auth_transaction);
+#ifdef CONFIG_MESH
+		if (status_code == WLAN_STATUS_SUCCESS &&
+		    hapd->conf->mesh & MESH_ENABLED) {
+			if (sta->wpa_sm == NULL)
+				sta->wpa_sm =
+					wpa_auth_sta_init(hapd->wpa_auth,
+							  sta->addr, NULL);
+			if (sta->wpa_sm == NULL) {
+				wpa_printf(MSG_DEBUG,
+					   "SAE: Failed to initialize WPA state machine");
+				resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+				goto fail;
+			}
+		}
+#endif /* CONFIG_MESH */
+		handle_auth_sae(hapd, sta, mgmt, len, auth_transaction,
+				status_code);
 		return;
 #endif /* CONFIG_SAE */
 	}
@@ -902,8 +1288,7 @@
 	if (resp != WLAN_STATUS_SUCCESS)
 		return resp;
 #ifdef CONFIG_IEEE80211N
-	resp = copy_sta_ht_capab(hapd, sta, elems.ht_capabilities,
-				 elems.ht_capabilities_len);
+	resp = copy_sta_ht_capab(hapd, sta, elems.ht_capabilities);
 	if (resp != WLAN_STATUS_SUCCESS)
 		return resp;
 	if (hapd->iconf->ieee80211n && hapd->iconf->require_ht &&
@@ -916,8 +1301,7 @@
 #endif /* CONFIG_IEEE80211N */
 
 #ifdef CONFIG_IEEE80211AC
-	resp = copy_sta_vht_capab(hapd, sta, elems.vht_capabilities,
-				  elems.vht_capabilities_len);
+	resp = copy_sta_vht_capab(hapd, sta, elems.vht_capabilities);
 	if (resp != WLAN_STATUS_SUCCESS)
 		return resp;
 
@@ -932,6 +1316,13 @@
 			       "mandatory VHT PHY - reject association");
 		return WLAN_STATUS_ASSOC_DENIED_NO_VHT;
 	}
+
+	if (hapd->conf->vendor_vht && !elems.vht_capabilities) {
+		resp = copy_sta_vendor_vht(hapd, sta, elems.vendor_vht,
+					   elems.vendor_vht_len);
+		if (resp != WLAN_STATUS_SUCCESS)
+			return resp;
+	}
 #endif /* CONFIG_IEEE80211AC */
 
 #ifdef CONFIG_P2P
@@ -1072,9 +1463,21 @@
 
 #ifdef CONFIG_SAE
 		if (wpa_auth_uses_sae(sta->wpa_sm) &&
-		    sta->auth_alg != WLAN_AUTH_SAE &&
-		    !(sta->auth_alg == WLAN_AUTH_FT &&
-		      wpa_auth_uses_ft_sae(sta->wpa_sm))) {
+		    sta->auth_alg == WLAN_AUTH_OPEN) {
+			struct rsn_pmksa_cache_entry *sa;
+			sa = wpa_auth_sta_get_pmksa(sta->wpa_sm);
+			if (!sa || sa->akmp != WPA_KEY_MGMT_SAE) {
+				wpa_printf(MSG_DEBUG,
+					   "SAE: No PMKSA cache entry found for "
+					   MACSTR, MAC2STR(sta->addr));
+				return WLAN_STATUS_INVALID_PMKID;
+			}
+			wpa_printf(MSG_DEBUG, "SAE: " MACSTR
+				   " using PMKSA caching", MAC2STR(sta->addr));
+		} else if (wpa_auth_uses_sae(sta->wpa_sm) &&
+			   sta->auth_alg != WLAN_AUTH_SAE &&
+			   !(sta->auth_alg == WLAN_AUTH_FT &&
+			     wpa_auth_uses_ft_sae(sta->wpa_sm))) {
 			wpa_printf(MSG_DEBUG, "SAE: " MACSTR " tried to use "
 				   "SAE AKM after non-SAE auth_alg %u",
 				   MAC2STR(sta->addr), sta->auth_alg);
@@ -1180,7 +1583,7 @@
 	send_len = IEEE80211_HDRLEN;
 	send_len += sizeof(reply->u.assoc_resp);
 	reply->u.assoc_resp.capab_info =
-		host_to_le16(hostapd_own_capab_info(hapd, sta, 0));
+		host_to_le16(hostapd_own_capab_info(hapd));
 	reply->u.assoc_resp.status_code = host_to_le16(status_code);
 	reply->u.assoc_resp.aid = host_to_le16(sta->aid | BIT(14) | BIT(15));
 	/* Supported rates */
@@ -1209,8 +1612,10 @@
 #endif /* CONFIG_IEEE80211N */
 
 #ifdef CONFIG_IEEE80211AC
-	p = hostapd_eid_vht_capabilities(hapd, p);
-	p = hostapd_eid_vht_operation(hapd, p);
+	if (hapd->iconf->ieee80211ac && !hapd->conf->disable_11ac) {
+		p = hostapd_eid_vht_capabilities(hapd, p);
+		p = hostapd_eid_vht_operation(hapd, p);
+	}
 #endif /* CONFIG_IEEE80211AC */
 
 	p = hostapd_eid_ext_capab(hapd, p);
@@ -1218,6 +1623,11 @@
 	if (sta->qos_map_enabled)
 		p = hostapd_eid_qos_map_set(hapd, p);
 
+#ifdef CONFIG_IEEE80211AC
+	if (hapd->conf->vendor_vht && (sta->flags & WLAN_STA_VENDOR_VHT))
+		p = hostapd_eid_vendor_vht(hapd, p);
+#endif /* CONFIG_IEEE80211AC */
+
 	if (sta->flags & WLAN_STA_WMM)
 		p = hostapd_eid_wmm(hapd, p);
 
@@ -1275,7 +1685,7 @@
 			 const struct ieee80211_mgmt *mgmt, size_t len,
 			 int reassoc)
 {
-	u16 capab_info, listen_interval;
+	u16 capab_info, listen_interval, seq_ctrl, fc;
 	u16 resp = WLAN_STATUS_SUCCESS;
 	const u8 *pos;
 	int left, i;
@@ -1308,15 +1718,19 @@
 	}
 #endif /* CONFIG_TESTING_OPTIONS */
 
+	fc = le_to_host16(mgmt->frame_control);
+	seq_ctrl = le_to_host16(mgmt->seq_ctrl);
+
 	if (reassoc) {
 		capab_info = le_to_host16(mgmt->u.reassoc_req.capab_info);
 		listen_interval = le_to_host16(
 			mgmt->u.reassoc_req.listen_interval);
 		wpa_printf(MSG_DEBUG, "reassociation request: STA=" MACSTR
 			   " capab_info=0x%02x listen_interval=%d current_ap="
-			   MACSTR,
+			   MACSTR " seq_ctrl=0x%x%s",
 			   MAC2STR(mgmt->sa), capab_info, listen_interval,
-			   MAC2STR(mgmt->u.reassoc_req.current_ap));
+			   MAC2STR(mgmt->u.reassoc_req.current_ap),
+			   seq_ctrl, (fc & WLAN_FC_RETRY) ? " retry" : "");
 		left = len - (IEEE80211_HDRLEN + sizeof(mgmt->u.reassoc_req));
 		pos = mgmt->u.reassoc_req.variable;
 	} else {
@@ -1324,8 +1738,10 @@
 		listen_interval = le_to_host16(
 			mgmt->u.assoc_req.listen_interval);
 		wpa_printf(MSG_DEBUG, "association request: STA=" MACSTR
-			   " capab_info=0x%02x listen_interval=%d",
-			   MAC2STR(mgmt->sa), capab_info, listen_interval);
+			   " capab_info=0x%02x listen_interval=%d "
+			   "seq_ctrl=0x%x%s",
+			   MAC2STR(mgmt->sa), capab_info, listen_interval,
+			   seq_ctrl, (fc & WLAN_FC_RETRY) ? " retry" : "");
 		left = len - (IEEE80211_HDRLEN + sizeof(mgmt->u.assoc_req));
 		pos = mgmt->u.assoc_req.variable;
 	}
@@ -1351,6 +1767,21 @@
 		return;
 	}
 
+	if ((fc & WLAN_FC_RETRY) &&
+	    sta->last_seq_ctrl != WLAN_INVALID_MGMT_SEQ &&
+	    sta->last_seq_ctrl == seq_ctrl &&
+	    sta->last_subtype == reassoc ? WLAN_FC_STYPE_REASSOC_REQ :
+	    WLAN_FC_STYPE_ASSOC_REQ) {
+		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+			       HOSTAPD_LEVEL_DEBUG,
+			       "Drop repeated association frame seq_ctrl=0x%x",
+			       seq_ctrl);
+		return;
+	}
+	sta->last_seq_ctrl = seq_ctrl;
+	sta->last_subtype = reassoc ? WLAN_FC_STYPE_REASSOC_REQ :
+		WLAN_FC_STYPE_ASSOC_REQ;
+
 	if (hapd->tkip_countermeasures) {
 		resp = WLAN_REASON_MICHAEL_MIC_FAILURE;
 		goto fail;
@@ -1476,6 +1907,7 @@
 	}
 
 	ap_sta_set_authorized(hapd, sta, 0);
+	sta->last_seq_ctrl = WLAN_INVALID_MGMT_SEQ;
 	sta->flags &= ~(WLAN_STA_ASSOC | WLAN_STA_ASSOC_REQ_OK);
 	wpa_auth_sm_event(sta->wpa_sm, WPA_DISASSOC);
 	hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
@@ -1486,6 +1918,9 @@
 	 * authenticated. */
 	accounting_sta_stop(hapd, sta);
 	ieee802_1x_free_station(sta);
+	if (sta->ipaddr)
+		hostapd_drv_br_delete_ip_neigh(hapd, 4, (u8 *) &sta->ipaddr);
+	ap_sta_ip6addr_del(hapd, sta);
 	hostapd_drv_sta_remove(hapd, sta->addr);
 
 	if (sta->timeout_next == STA_NULLFUNC ||
@@ -1525,6 +1960,7 @@
 	}
 
 	ap_sta_set_authorized(hapd, sta, 0);
+	sta->last_seq_ctrl = WLAN_INVALID_MGMT_SEQ;
 	sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC |
 			WLAN_STA_ASSOC_REQ_OK);
 	wpa_auth_sm_event(sta->wpa_sm, WPA_DEAUTH);
@@ -1624,6 +2060,26 @@
 	}
 #endif /* CONFIG_IEEE80211W */
 
+	if (sta) {
+		u16 fc = le_to_host16(mgmt->frame_control);
+		u16 seq_ctrl = le_to_host16(mgmt->seq_ctrl);
+
+		if ((fc & WLAN_FC_RETRY) &&
+		    sta->last_seq_ctrl != WLAN_INVALID_MGMT_SEQ &&
+		    sta->last_seq_ctrl == seq_ctrl &&
+		    sta->last_subtype == WLAN_FC_STYPE_ACTION) {
+			hostapd_logger(hapd, sta->addr,
+				       HOSTAPD_MODULE_IEEE80211,
+				       HOSTAPD_LEVEL_DEBUG,
+				       "Drop repeated action frame seq_ctrl=0x%x",
+				       seq_ctrl);
+			return 1;
+		}
+
+		sta->last_seq_ctrl = seq_ctrl;
+		sta->last_subtype = WLAN_FC_STYPE_ACTION;
+	}
+
 	switch (mgmt->u.action.category) {
 #ifdef CONFIG_IEEE80211R
 	case WLAN_ACTION_FT:
@@ -1648,7 +2104,8 @@
 	case WLAN_ACTION_PUBLIC:
 	case WLAN_ACTION_PROTECTED_DUAL:
 #ifdef CONFIG_IEEE80211N
-		if (mgmt->u.action.u.public_action.action ==
+		if (len >= IEEE80211_HDRLEN + 2 &&
+		    mgmt->u.action.u.public_action.action ==
 		    WLAN_PA_20_40_BSS_COEX) {
 			wpa_printf(MSG_DEBUG,
 				   "HT20/40 coex mgmt frame received from STA "
@@ -1758,6 +2215,9 @@
 	    !((hapd->conf->p2p & P2P_GROUP_OWNER) &&
 	      stype == WLAN_FC_STYPE_ACTION) &&
 #endif /* CONFIG_P2P */
+#ifdef CONFIG_MESH
+	    !(hapd->conf->mesh & MESH_ENABLED) &&
+#endif /* CONFIG_MESH */
 	    os_memcmp(mgmt->bssid, hapd->own_addr, ETH_ALEN) != 0) {
 		wpa_printf(MSG_INFO, "MGMT: BSSID=" MACSTR " not our address",
 			   MAC2STR(mgmt->bssid));
@@ -1865,7 +2325,7 @@
 				       char *ifname_wds)
 {
 	int i;
-	struct hostapd_ssid *ssid = sta->ssid;
+	struct hostapd_ssid *ssid = &hapd->conf->ssid;
 
 	if (hapd->conf->ieee802_1x || hapd->conf->wpa)
 		return;
@@ -2003,11 +2463,11 @@
 		 * so bind it to the selected VLAN interface now, since the
 		 * interface selection is not going to change anymore.
 		 */
-		if (ap_sta_bind_vlan(hapd, sta, 0) < 0)
+		if (ap_sta_bind_vlan(hapd, sta) < 0)
 			return;
 	} else if (sta->vlan_id) {
 		/* VLAN ID already set (e.g., by PMKSA caching), so bind STA */
-		if (ap_sta_bind_vlan(hapd, sta, 0) < 0)
+		if (ap_sta_bind_vlan(hapd, sta) < 0)
 			return;
 	}
 
diff --git a/src/ap/ieee802_11.h b/src/ap/ieee802_11.h
index cf0d3f2..44c1bff 100644
--- a/src/ap/ieee802_11.h
+++ b/src/ap/ieee802_11.h
@@ -14,6 +14,7 @@
 struct sta_info;
 struct hostapd_frame_info;
 struct ieee80211_ht_capabilities;
+struct ieee80211_vht_capabilities;
 struct ieee80211_mgmt;
 
 int ieee802_11_mgmt(struct hostapd_data *hapd, const u8 *buf, size_t len,
@@ -40,8 +41,7 @@
 	return 0;
 }
 #endif /* NEED_AP_MLME */
-u16 hostapd_own_capab_info(struct hostapd_data *hapd, struct sta_info *sta,
-			   int probe);
+u16 hostapd_own_capab_info(struct hostapd_data *hapd);
 void ap_ht2040_timeout(void *eloop_data, void *user_data);
 u8 * hostapd_eid_ext_capab(struct hostapd_data *hapd, u8 *eid);
 u8 * hostapd_eid_qos_map_set(struct hostapd_data *hapd, u8 *eid);
@@ -51,6 +51,7 @@
 u8 * hostapd_eid_ht_operation(struct hostapd_data *hapd, u8 *eid);
 u8 * hostapd_eid_vht_capabilities(struct hostapd_data *hapd, u8 *eid);
 u8 * hostapd_eid_vht_operation(struct hostapd_data *hapd, u8 *eid);
+u8 * hostapd_eid_vendor_vht(struct hostapd_data *hapd, u8 *eid);
 int hostapd_ht_operation_update(struct hostapd_iface *iface);
 void ieee802_11_send_sa_query_req(struct hostapd_data *hapd,
 				  const u8 *addr, const u8 *trans_id);
@@ -61,12 +62,15 @@
 			   struct ieee80211_vht_capabilities *vht_cap,
 			   struct ieee80211_vht_capabilities *neg_vht_cap);
 u16 copy_sta_ht_capab(struct hostapd_data *hapd, struct sta_info *sta,
-		      const u8 *ht_capab, size_t ht_capab_len);
+		      const u8 *ht_capab);
+u16 copy_sta_vendor_vht(struct hostapd_data *hapd, struct sta_info *sta,
+			const u8 *ie, size_t len);
+
 void update_ht_state(struct hostapd_data *hapd, struct sta_info *sta);
 void ht40_intolerant_add(struct hostapd_iface *iface, struct sta_info *sta);
 void ht40_intolerant_remove(struct hostapd_iface *iface, struct sta_info *sta);
 u16 copy_sta_vht_capab(struct hostapd_data *hapd, struct sta_info *sta,
-		       const u8 *vht_capab, size_t vht_capab_len);
+		       const u8 *vht_capab);
 u16 set_sta_vht_opmode(struct hostapd_data *hapd, struct sta_info *sta,
 		       const u8 *vht_opmode);
 void hostapd_tx_status(struct hostapd_data *hapd, const u8 *addr,
@@ -89,4 +93,15 @@
 void hostapd_client_poll_ok(struct hostapd_data *hapd, const u8 *addr);
 u8 * hostapd_eid_bss_max_idle_period(struct hostapd_data *hapd, u8 *eid);
 
+int auth_sae_init_committed(struct hostapd_data *hapd, struct sta_info *sta);
+#ifdef CONFIG_SAE
+void sae_clear_retransmit_timer(struct hostapd_data *hapd,
+				struct sta_info *sta);
+#else /* CONFIG_SAE */
+static inline void sae_clear_retransmit_timer(struct hostapd_data *hapd,
+					      struct sta_info *sta)
+{
+}
+#endif /* CONFIG_SAE */
+
 #endif /* IEEE802_11_H */
diff --git a/src/ap/ieee802_11_auth.c b/src/ap/ieee802_11_auth.c
index 56c3ce0..0238257 100644
--- a/src/ap/ieee802_11_auth.c
+++ b/src/ap/ieee802_11_auth.c
@@ -561,6 +561,19 @@
 		if (hapd->conf->wpa_psk_radius == PSK_RADIUS_REQUIRED &&
 		    !cache->psk)
 			cache->accepted = HOSTAPD_ACL_REJECT;
+
+		if (cache->vlan_id &&
+		    !hostapd_vlan_id_valid(hapd->conf->vlan, cache->vlan_id)) {
+			hostapd_logger(hapd, query->addr,
+				       HOSTAPD_MODULE_RADIUS,
+				       HOSTAPD_LEVEL_INFO,
+				       "Invalid VLAN ID %d received from RADIUS server",
+				       cache->vlan_id);
+			cache->vlan_id = 0;
+		}
+		if (hapd->conf->ssid.dynamic_vlan == DYNAMIC_VLAN_REQUIRED &&
+		    !cache->vlan_id)
+			cache->accepted = HOSTAPD_ACL_REJECT;
 	} else
 		cache->accepted = HOSTAPD_ACL_REJECT;
 	cache->next = hapd->acl_cache;
diff --git a/src/ap/ieee802_11_ht.c b/src/ap/ieee802_11_ht.c
index fe87883..11fde2a 100644
--- a/src/ap/ieee802_11_ht.c
+++ b/src/ap/ieee802_11_ht.c
@@ -209,9 +209,10 @@
 	struct hostapd_iface *iface = hapd->iface;
 	struct ieee80211_2040_bss_coex_ie *bc_ie;
 	struct ieee80211_2040_intol_chan_report *ic_report;
-	int is_ht_allowed = 1;
+	int is_ht40_allowed = 1;
 	int i;
-	const u8 *data = ((const u8 *) mgmt) + 1;
+	const u8 *start = (const u8 *) mgmt;
+	const u8 *data = start + IEEE80211_HDRLEN + 2;
 
 	hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
 		       HOSTAPD_LEVEL_DEBUG, "hostapd_public_action - action=%d",
@@ -220,20 +221,28 @@
 	if (!(iface->conf->ht_capab & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET))
 		return;
 
-	if (len < IEEE80211_HDRLEN + 1)
+	if (len < IEEE80211_HDRLEN + 2 + sizeof(*bc_ie))
 		return;
-	data++;
 
-	bc_ie = (struct ieee80211_2040_bss_coex_ie *) &data[0];
-	ic_report = (struct ieee80211_2040_intol_chan_report *)
-		(&data[0] + sizeof(*bc_ie));
+	bc_ie = (struct ieee80211_2040_bss_coex_ie *) data;
+	if (bc_ie->element_id != WLAN_EID_20_40_BSS_COEXISTENCE ||
+	    bc_ie->length < 1) {
+		wpa_printf(MSG_DEBUG, "Unexpected IE (%u,%u) in coex report",
+			   bc_ie->element_id, bc_ie->length);
+		return;
+	}
+	if (len < IEEE80211_HDRLEN + 2 + 2 + bc_ie->length)
+		return;
+	data += 2 + bc_ie->length;
 
+	wpa_printf(MSG_DEBUG, "20/40 BSS Coexistence Information field: 0x%x",
+		   bc_ie->coex_param);
 	if (bc_ie->coex_param & WLAN_20_40_BSS_COEX_20MHZ_WIDTH_REQ) {
 		hostapd_logger(hapd, mgmt->sa,
 			       HOSTAPD_MODULE_IEEE80211,
 			       HOSTAPD_LEVEL_DEBUG,
 			       "20 MHz BSS width request bit is set in BSS coexistence information field");
-		is_ht_allowed = 0;
+		is_ht40_allowed = 0;
 	}
 
 	if (bc_ie->coex_param & WLAN_20_40_BSS_COEX_40MHZ_INTOL) {
@@ -241,27 +250,39 @@
 			       HOSTAPD_MODULE_IEEE80211,
 			       HOSTAPD_LEVEL_DEBUG,
 			       "40 MHz intolerant bit is set in BSS coexistence information field");
-		is_ht_allowed = 0;
+		is_ht40_allowed = 0;
 	}
 
-	if (ic_report &&
-	    (ic_report->element_id == WLAN_EID_20_40_BSS_INTOLERANT)) {
+	if (start + len - data >= 3 &&
+	    data[0] == WLAN_EID_20_40_BSS_INTOLERANT && data[1] >= 1) {
+		u8 ielen = data[1];
+
+		if (ielen > start + len - data - 2)
+			return;
+		ic_report = (struct ieee80211_2040_intol_chan_report *) data;
+		wpa_printf(MSG_DEBUG,
+			   "20/40 BSS Intolerant Channel Report: Operating Class %u",
+			   ic_report->op_class);
+
 		/* Go through the channel report to find any BSS there in the
 		 * affected channel range */
-		for (i = 0; i < ic_report->length - 1; i++) {
-			if (is_40_allowed(iface, ic_report->variable[i]))
+		for (i = 0; i < ielen - 1; i++) {
+			u8 chan = ic_report->variable[i];
+
+			if (is_40_allowed(iface, chan))
 				continue;
 			hostapd_logger(hapd, mgmt->sa,
 				       HOSTAPD_MODULE_IEEE80211,
 				       HOSTAPD_LEVEL_DEBUG,
 				       "20_40_INTOLERANT channel %d reported",
-				       ic_report->variable[i]);
-			is_ht_allowed = 0;
-			break;
+				       chan);
+			is_ht40_allowed = 0;
 		}
 	}
+	wpa_printf(MSG_DEBUG, "is_ht40_allowed=%d num_sta_ht40_intolerant=%d",
+		   is_ht40_allowed, iface->num_sta_ht40_intolerant);
 
-	if (!is_ht_allowed &&
+	if (!is_ht40_allowed &&
 	    (iface->drv_flags & WPA_DRIVER_FLAGS_HT_2040_COEX)) {
 		if (iface->conf->secondary_channel) {
 			hostapd_logger(hapd, mgmt->sa,
@@ -271,7 +292,8 @@
 			iface->conf->secondary_channel = 0;
 			ieee802_11_set_beacons(iface);
 		}
-		if (!iface->num_sta_ht40_intolerant) {
+		if (!iface->num_sta_ht40_intolerant &&
+		    iface->conf->obss_interval) {
 			unsigned int delay_time;
 			delay_time = OVERLAPPING_BSS_TRANS_DELAY_FACTOR *
 				iface->conf->obss_interval;
@@ -279,18 +301,24 @@
 					     NULL);
 			eloop_register_timeout(delay_time, 0, ap_ht2040_timeout,
 					       hapd->iface, NULL);
+			wpa_printf(MSG_DEBUG,
+				   "Reschedule HT 20/40 timeout to occur in %u seconds",
+				   delay_time);
 		}
 	}
 }
 
 
 u16 copy_sta_ht_capab(struct hostapd_data *hapd, struct sta_info *sta,
-		      const u8 *ht_capab, size_t ht_capab_len)
+		      const u8 *ht_capab)
 {
-	/* Disable HT caps for STAs associated to no-HT BSSes. */
+	/*
+	 * Disable HT caps for STAs associated to no-HT BSSes, or for stations
+	 * that did not specify a valid WMM IE in the (Re)Association Request
+	 * frame.
+	 */
 	if (!ht_capab ||
-	    ht_capab_len < sizeof(struct ieee80211_ht_capabilities) ||
-	    hapd->conf->disable_11n) {
+	    !(sta->flags & WLAN_STA_WMM) || hapd->conf->disable_11n) {
 		sta->flags &= ~WLAN_STA_HT;
 		os_free(sta->ht_capabilities);
 		sta->ht_capabilities = NULL;
diff --git a/src/ap/ieee802_11_shared.c b/src/ap/ieee802_11_shared.c
index 12403f9..d462ac8 100644
--- a/src/ap/ieee802_11_shared.c
+++ b/src/ap/ieee802_11_shared.c
@@ -174,6 +174,8 @@
 			*pos |= 0x01; /* Bit 0 - Coexistence management */
 		break;
 	case 1: /* Bits 8-15 */
+		if (hapd->conf->proxy_arp)
+			*pos |= 0x10; /* Bit 12 - Proxy ARP */
 		break;
 	case 2: /* Bits 16-23 */
 		if (hapd->conf->wnm_sleep_mode)
diff --git a/src/ap/ieee802_11_vht.c b/src/ap/ieee802_11_vht.c
index 437cf50..5bf1b5d 100644
--- a/src/ap/ieee802_11_vht.c
+++ b/src/ap/ieee802_11_vht.c
@@ -22,12 +22,25 @@
 u8 * hostapd_eid_vht_capabilities(struct hostapd_data *hapd, u8 *eid)
 {
 	struct ieee80211_vht_capabilities *cap;
+	struct hostapd_hw_modes *mode = hapd->iface->current_mode;
 	u8 *pos = eid;
 
-	if (!hapd->iconf->ieee80211ac || !hapd->iface->current_mode ||
-	    hapd->conf->disable_11ac)
+	if (!mode)
 		return eid;
 
+	if (mode->mode == HOSTAPD_MODE_IEEE80211G && hapd->conf->vendor_vht &&
+	    mode->vht_capab == 0 && hapd->iface->hw_features) {
+		int i;
+
+		for (i = 0; i < hapd->iface->num_hw_features; i++) {
+			if (hapd->iface->hw_features[i].mode ==
+			    HOSTAPD_MODE_IEEE80211A) {
+				mode = &hapd->iface->hw_features[i];
+				break;
+			}
+		}
+	}
+
 	*pos++ = WLAN_EID_VHT_CAP;
 	*pos++ = sizeof(*cap);
 
@@ -37,8 +50,7 @@
 		hapd->iface->conf->vht_capab);
 
 	/* Supported MCS set comes from hw */
-	os_memcpy(&cap->vht_supported_mcs_set,
-	          hapd->iface->current_mode->vht_mcs_set, 8);
+	os_memcpy(&cap->vht_supported_mcs_set, mode->vht_mcs_set, 8);
 
 	pos += sizeof(*cap);
 
@@ -51,9 +63,6 @@
 	struct ieee80211_vht_operation *oper;
 	u8 *pos = eid;
 
-	if (!hapd->iconf->ieee80211ac || hapd->conf->disable_11ac)
-		return eid;
-
 	*pos++ = WLAN_EID_VHT_OPERATION;
 	*pos++ = sizeof(*oper);
 
@@ -81,13 +90,54 @@
 }
 
 
+static int check_valid_vht_mcs(struct hostapd_hw_modes *mode,
+			       const u8 *sta_vht_capab)
+{
+	const struct ieee80211_vht_capabilities *vht_cap;
+	struct ieee80211_vht_capabilities ap_vht_cap;
+	u16 sta_rx_mcs_set, ap_tx_mcs_set;
+	int i;
+
+	if (!mode)
+		return 1;
+
+	/*
+	 * Disable VHT caps for STAs for which there is not even a single
+	 * allowed MCS in any supported number of streams, i.e., STA is
+	 * advertising 3 (not supported) as VHT MCS rates for all supported
+	 * stream cases.
+	 */
+	os_memcpy(&ap_vht_cap.vht_supported_mcs_set, mode->vht_mcs_set,
+		  sizeof(ap_vht_cap.vht_supported_mcs_set));
+	vht_cap = (const struct ieee80211_vht_capabilities *) sta_vht_capab;
+
+	/* AP Tx MCS map vs. STA Rx MCS map */
+	sta_rx_mcs_set = le_to_host16(vht_cap->vht_supported_mcs_set.rx_map);
+	ap_tx_mcs_set = le_to_host16(ap_vht_cap.vht_supported_mcs_set.tx_map);
+
+	for (i = 0; i < VHT_RX_NSS_MAX_STREAMS; i++) {
+		if ((ap_tx_mcs_set & (0x3 << (i * 2))) == 3)
+			continue;
+
+		if ((sta_rx_mcs_set & (0x3 << (i * 2))) == 3)
+			continue;
+
+		return 1;
+	}
+
+	wpa_printf(MSG_DEBUG,
+		   "No matching VHT MCS found between AP TX and STA RX");
+	return 0;
+}
+
+
 u16 copy_sta_vht_capab(struct hostapd_data *hapd, struct sta_info *sta,
-		       const u8 *vht_capab, size_t vht_capab_len)
+		       const u8 *vht_capab)
 {
 	/* Disable VHT caps for STAs associated to no-VHT BSSes. */
 	if (!vht_capab ||
-	    vht_capab_len < sizeof(struct ieee80211_vht_capabilities) ||
-	    hapd->conf->disable_11ac) {
+	    hapd->conf->disable_11ac ||
+	    !check_valid_vht_mcs(hapd->iface->current_mode, vht_capab)) {
 		sta->flags &= ~WLAN_STA_VHT;
 		os_free(sta->vht_capabilities);
 		sta->vht_capabilities = NULL;
@@ -109,6 +159,66 @@
 }
 
 
+u16 copy_sta_vendor_vht(struct hostapd_data *hapd, struct sta_info *sta,
+			const u8 *ie, size_t len)
+{
+	const u8 *vht_capab;
+	unsigned int vht_capab_len;
+
+	if (!ie || len < 5 + 2 + sizeof(struct ieee80211_vht_capabilities) ||
+	    hapd->conf->disable_11ac)
+		goto no_capab;
+
+	/* The VHT Capabilities element embedded in vendor VHT */
+	vht_capab = ie + 5;
+	if (vht_capab[0] != WLAN_EID_VHT_CAP)
+		goto no_capab;
+	vht_capab_len = vht_capab[1];
+	if (vht_capab_len < sizeof(struct ieee80211_vht_capabilities) ||
+	    (int) vht_capab_len > ie + len - vht_capab - 2)
+		goto no_capab;
+	vht_capab += 2;
+
+	if (sta->vht_capabilities == NULL) {
+		sta->vht_capabilities =
+			os_zalloc(sizeof(struct ieee80211_vht_capabilities));
+		if (sta->vht_capabilities == NULL)
+			return WLAN_STATUS_UNSPECIFIED_FAILURE;
+	}
+
+	sta->flags |= WLAN_STA_VHT | WLAN_STA_VENDOR_VHT;
+	os_memcpy(sta->vht_capabilities, vht_capab,
+		  sizeof(struct ieee80211_vht_capabilities));
+	return WLAN_STATUS_SUCCESS;
+
+no_capab:
+	sta->flags &= ~WLAN_STA_VENDOR_VHT;
+	return WLAN_STATUS_SUCCESS;
+}
+
+
+u8 * hostapd_eid_vendor_vht(struct hostapd_data *hapd, u8 *eid)
+{
+	u8 *pos = eid;
+
+	if (!hapd->iface->current_mode)
+		return eid;
+
+	*pos++ = WLAN_EID_VENDOR_SPECIFIC;
+	*pos++ = (5 +		/* The Vendor OUI, type and subtype */
+		  2 + sizeof(struct ieee80211_vht_capabilities) +
+		  2 + sizeof(struct ieee80211_vht_operation));
+
+	WPA_PUT_BE32(pos, (OUI_BROADCOM << 8) | VENDOR_VHT_TYPE);
+	pos += 4;
+	*pos++ = VENDOR_VHT_SUBTYPE;
+	pos = hostapd_eid_vht_capabilities(hapd, pos);
+	pos = hostapd_eid_vht_operation(hapd, pos);
+
+	return pos;
+}
+
+
 u16 set_sta_vht_opmode(struct hostapd_data *hapd, struct sta_info *sta,
 		       const u8 *vht_oper_notif)
 {
diff --git a/src/ap/ieee802_1x.c b/src/ap/ieee802_1x.c
index 2d09b67..d45b98f 100644
--- a/src/ap/ieee802_1x.c
+++ b/src/ap/ieee802_1x.c
@@ -66,6 +66,20 @@
 
 	if (wpa_auth_pairwise_set(sta->wpa_sm))
 		encrypt = 1;
+#ifdef CONFIG_TESTING_OPTIONS
+	if (hapd->ext_eapol_frame_io) {
+		size_t hex_len = 2 * len + 1;
+		char *hex = os_malloc(hex_len);
+
+		if (hex) {
+			wpa_snprintf_hex(hex, hex_len, buf, len);
+			wpa_msg(hapd->msg_ctx, MSG_INFO,
+				"EAPOL-TX " MACSTR " %s",
+				MAC2STR(sta->addr), hex);
+			os_free(hex);
+		}
+	} else
+#endif /* CONFIG_TESTING_OPTIONS */
 	if (sta->flags & WLAN_STA_PREAUTH) {
 		rsn_preauth_send(hapd, sta, buf, len);
 	} else {
@@ -282,9 +296,15 @@
 {
 	const u8 *identity;
 	size_t identity_len;
+	const struct eap_hdr *hdr = (const struct eap_hdr *) eap;
 
 	if (len <= sizeof(struct eap_hdr) ||
-	    eap[sizeof(struct eap_hdr)] != EAP_TYPE_IDENTITY)
+	    (hdr->code == EAP_CODE_RESPONSE &&
+	     eap[sizeof(struct eap_hdr)] != EAP_TYPE_IDENTITY) ||
+	    (hdr->code == EAP_CODE_INITIATE &&
+	     eap[sizeof(struct eap_hdr)] != EAP_ERP_TYPE_REAUTH) ||
+	    (hdr->code != EAP_CODE_RESPONSE &&
+	     hdr->code != EAP_CODE_INITIATE))
 		return;
 
 	identity = eap_get_identity(sm->eap, &identity_len);
@@ -326,7 +346,8 @@
 		return -1;
 	}
 
-	suite = wpa_cipher_to_suite((hapd->conf->wpa & 0x2) ?
+	suite = wpa_cipher_to_suite(((hapd->conf->wpa & 0x2) ||
+				     hapd->conf->osen) ?
 				    WPA_PROTO_RSN : WPA_PROTO_WPA,
 				    hapd->conf->wpa_group);
 	if (!hostapd_config_get_radius_attr(req_attr,
@@ -433,7 +454,7 @@
 	}
 #endif /* CONFIG_IEEE80211R */
 
-	if (hapd->conf->wpa && sta->wpa_sm &&
+	if ((hapd->conf->wpa || hapd->conf->osen) && sta->wpa_sm &&
 	    add_common_radius_sta_attr_rsn(hapd, req_attr, sta, msg) < 0)
 		return -1;
 
@@ -579,7 +600,7 @@
 		goto fail;
 	}
 
-	if (eap && !radius_msg_add_eap(msg, eap, len)) {
+	if (!radius_msg_add_eap(msg, eap, len)) {
 		wpa_printf(MSG_INFO, "Could not add EAP-Message");
 		goto fail;
 	}
@@ -697,6 +718,39 @@
 }
 
 
+static void handle_eap_initiate(struct hostapd_data *hapd,
+				struct sta_info *sta, struct eap_hdr *eap,
+				size_t len)
+{
+#ifdef CONFIG_ERP
+	u8 type, *data;
+	struct eapol_state_machine *sm = sta->eapol_sm;
+
+	if (sm == NULL)
+		return;
+
+	if (len < sizeof(*eap) + 1) {
+		wpa_printf(MSG_INFO,
+			   "handle_eap_initiate: too short response data");
+		return;
+	}
+
+	data = (u8 *) (eap + 1);
+	type = data[0];
+
+	hostapd_logger(hapd, sm->addr, HOSTAPD_MODULE_IEEE8021X,
+		       HOSTAPD_LEVEL_DEBUG, "received EAP packet (code=%d "
+		       "id=%d len=%d) from STA: EAP Initiate type %u",
+		       eap->code, eap->identifier, be_to_host16(eap->length),
+		       type);
+
+	wpabuf_free(sm->eap_if->eapRespData);
+	sm->eap_if->eapRespData = wpabuf_alloc_copy(eap, len);
+	sm->eapolEap = TRUE;
+#endif /* CONFIG_ERP */
+}
+
+
 /* Process incoming EAP packet from Supplicant */
 static void handle_eap(struct hostapd_data *hapd, struct sta_info *sta,
 		       u8 *buf, size_t len)
@@ -740,6 +794,13 @@
 	case EAP_CODE_FAILURE:
 		wpa_printf(MSG_DEBUG, " (failure)");
 		return;
+	case EAP_CODE_INITIATE:
+		wpa_printf(MSG_DEBUG, " (initiate)");
+		handle_eap_initiate(hapd, sta, eap, eap_len);
+		break;
+	case EAP_CODE_FINISH:
+		wpa_printf(MSG_DEBUG, " (finish)");
+		break;
 	default:
 		wpa_printf(MSG_DEBUG, " (unknown code)");
 		return;
@@ -961,8 +1022,9 @@
 	int key_mgmt;
 
 #ifdef CONFIG_WPS
-	if (hapd->conf->wps_state && hapd->conf->wpa &&
-	    (sta->flags & (WLAN_STA_WPS | WLAN_STA_MAYBE_WPS))) {
+	if (hapd->conf->wps_state &&
+	    ((hapd->conf->wpa && (sta->flags & WLAN_STA_MAYBE_WPS)) ||
+	     (sta->flags & WLAN_STA_WPS))) {
 		/*
 		 * Need to enable IEEE 802.1X/EAPOL state machines for possible
 		 * WPS handshake even if IEEE 802.1X/EAPOL is not used for
@@ -1047,8 +1109,6 @@
 
 	pmksa = wpa_auth_sta_get_pmksa(sta->wpa_sm);
 	if (pmksa) {
-		int old_vlanid;
-
 		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X,
 			       HOSTAPD_LEVEL_DEBUG,
 			       "PMK from PMKSA cache - skip IEEE 802.1X/EAP");
@@ -1062,11 +1122,8 @@
 		sta->eapol_sm->authFail = FALSE;
 		if (sta->eapol_sm->eap)
 			eap_sm_notify_cached(sta->eapol_sm->eap);
-		old_vlanid = sta->vlan_id;
 		pmksa_cache_to_eapol_data(pmksa, sta->eapol_sm);
-		if (sta->ssid->dynamic_vlan == DYNAMIC_VLAN_DISABLED)
-			sta->vlan_id = 0;
-		ap_sta_bind_vlan(hapd, sta, old_vlanid);
+		ap_sta_bind_vlan(hapd, sta);
 	} else {
 		if (reassoc) {
 			/*
@@ -1150,15 +1207,11 @@
 		if (eap_type >= 0)
 			sm->eap_type_authsrv = eap_type;
 		os_snprintf(buf, sizeof(buf), "EAP-Request-%s (%d)",
-			    eap_type >= 0 ? eap_server_get_name(0, eap_type) :
-			    "??",
-			    eap_type);
+			    eap_server_get_name(0, eap_type), eap_type);
 		break;
 	case EAP_CODE_RESPONSE:
 		os_snprintf(buf, sizeof(buf), "EAP Response-%s (%d)",
-			    eap_type >= 0 ? eap_server_get_name(0, eap_type) :
-			    "??",
-			    eap_type);
+			    eap_server_get_name(0, eap_type), eap_type);
 		break;
 	case EAP_CODE_SUCCESS:
 		os_strlcpy(buf, "EAP Success", sizeof(buf));
@@ -1214,6 +1267,11 @@
 			sm->eap_if->aaaEapKeyDataLen = len;
 			sm->eap_if->aaaEapKeyAvailable = TRUE;
 		}
+	} else {
+		wpa_printf(MSG_DEBUG,
+			   "MS-MPPE: 1x_get_keys, could not get keys: %p  send: %p  recv: %p",
+			   keys, keys ? keys->send : NULL,
+			   keys ? keys->recv : NULL);
 	}
 
 	if (keys) {
@@ -1228,7 +1286,7 @@
 					  struct sta_info *sta,
 					  struct radius_msg *msg)
 {
-	u8 *class;
+	u8 *attr_class;
 	size_t class_len;
 	struct eapol_state_machine *sm = sta->eapol_sm;
 	int count, i;
@@ -1250,12 +1308,12 @@
 
 	nclass_count = 0;
 
-	class = NULL;
+	attr_class = NULL;
 	for (i = 0; i < count; i++) {
 		do {
 			if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_CLASS,
-						    &class, &class_len,
-						    class) < 0) {
+						    &attr_class, &class_len,
+						    attr_class) < 0) {
 				i = count;
 				break;
 			}
@@ -1265,7 +1323,7 @@
 		if (nclass[nclass_count].data == NULL)
 			break;
 
-		os_memcpy(nclass[nclass_count].data, class, class_len);
+		os_memcpy(nclass[nclass_count].data, attr_class, class_len);
 		nclass[nclass_count].len = class_len;
 		nclass_count++;
 	}
@@ -1528,7 +1586,7 @@
 	struct hostapd_data *hapd = data;
 	struct sta_info *sta;
 	u32 session_timeout = 0, termination_action, acct_interim_interval;
-	int session_timeout_set, old_vlanid = 0;
+	int session_timeout_set, vlan_id = 0;
 	struct eapol_state_machine *sm;
 	int override_eapReq = 0;
 	struct radius_hdr *hdr = radius_msg_get_hdr(msg);
@@ -1595,20 +1653,27 @@
 
 	switch (hdr->code) {
 	case RADIUS_CODE_ACCESS_ACCEPT:
-		if (sta->ssid->dynamic_vlan == DYNAMIC_VLAN_DISABLED)
-			sta->vlan_id = 0;
+		if (hapd->conf->ssid.dynamic_vlan == DYNAMIC_VLAN_DISABLED)
+			vlan_id = 0;
 #ifndef CONFIG_NO_VLAN
-		else {
-			old_vlanid = sta->vlan_id;
-			sta->vlan_id = radius_msg_get_vlanid(msg);
-		}
-		if (sta->vlan_id > 0 &&
-		    hostapd_vlan_id_valid(hapd->conf->vlan, sta->vlan_id)) {
+		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", sta->vlan_id);
-		} else if (sta->ssid->dynamic_vlan == DYNAMIC_VLAN_REQUIRED) {
+				       "VLAN ID %d", vlan_id);
+		} else if (vlan_id > 0) {
+			sta->eapol_sm->authFail = TRUE;
+			hostapd_logger(hapd, sta->addr,
+				       HOSTAPD_MODULE_RADIUS,
+				       HOSTAPD_LEVEL_INFO,
+				       "Invalid VLAN ID %d received from RADIUS server",
+				       vlan_id);
+			break;
+		} else if (hapd->conf->ssid.dynamic_vlan ==
+			   DYNAMIC_VLAN_REQUIRED) {
 			sta->eapol_sm->authFail = TRUE;
 			hostapd_logger(hapd, sta->addr,
 				       HOSTAPD_MODULE_IEEE8021X,
@@ -1619,7 +1684,9 @@
 		}
 #endif /* CONFIG_NO_VLAN */
 
-		if (ap_sta_bind_vlan(hapd, sta, old_vlanid) < 0)
+		sta->vlan_id = vlan_id;
+		if ((sta->flags & WLAN_STA_ASSOC) &&
+		    ap_sta_bind_vlan(hapd, sta) < 0)
 			break;
 
 		sta->session_timeout_set = !!session_timeout_set;
@@ -1864,10 +1931,11 @@
 	struct hostapd_data *hapd = ctx;
 	const struct hostapd_eap_user *eap_user;
 	int i;
+	int rv = -1;
 
 	eap_user = hostapd_get_eap_user(hapd, identity, identity_len, phase2);
 	if (eap_user == NULL)
-		return -1;
+		goto out;
 
 	os_memset(user, 0, sizeof(*user));
 	user->phase2 = phase2;
@@ -1879,7 +1947,7 @@
 	if (eap_user->password) {
 		user->password = os_malloc(eap_user->password_len);
 		if (user->password == NULL)
-			return -1;
+			goto out;
 		os_memcpy(user->password, eap_user->password,
 			  eap_user->password_len);
 		user->password_len = eap_user->password_len;
@@ -1889,8 +1957,13 @@
 	user->macacl = eap_user->macacl;
 	user->ttls_auth = eap_user->ttls_auth;
 	user->remediation = eap_user->remediation;
+	rv = 0;
 
-	return 0;
+out:
+	if (rv)
+		wpa_printf(MSG_DEBUG, "%s: Failed to find user", __func__);
+
+	return rv;
 }
 
 
@@ -1972,12 +2045,43 @@
 }
 
 
+#ifdef CONFIG_ERP
+
+static struct eap_server_erp_key *
+ieee802_1x_erp_get_key(void *ctx, const char *keyname)
+{
+	struct hostapd_data *hapd = ctx;
+	struct eap_server_erp_key *erp;
+
+	dl_list_for_each(erp, &hapd->erp_keys, struct eap_server_erp_key,
+			 list) {
+		if (os_strcmp(erp->keyname_nai, keyname) == 0)
+			return erp;
+	}
+
+	return NULL;
+}
+
+
+static int ieee802_1x_erp_add_key(void *ctx, struct eap_server_erp_key *erp)
+{
+	struct hostapd_data *hapd = ctx;
+
+	dl_list_add(&hapd->erp_keys, &erp->list);
+	return 0;
+}
+
+#endif /* CONFIG_ERP */
+
+
 int ieee802_1x_init(struct hostapd_data *hapd)
 {
 	int i;
 	struct eapol_auth_config conf;
 	struct eapol_auth_cb cb;
 
+	dl_list_init(&hapd->erp_keys);
+
 	os_memset(&conf, 0, sizeof(conf));
 	conf.ctx = hapd;
 	conf.eap_reauth_period = hapd->conf->eap_reauth_period;
@@ -1989,6 +2093,9 @@
 	conf.eap_sim_db_priv = hapd->eap_sim_db_priv;
 	conf.eap_req_id_text = hapd->conf->eap_req_id_text;
 	conf.eap_req_id_text_len = hapd->conf->eap_req_id_text_len;
+	conf.erp_send_reauth_start = hapd->conf->erp_send_reauth_start;
+	conf.erp_domain = hapd->conf->erp_domain;
+	conf.erp = hapd->conf->eap_server_erp;
 	conf.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;
@@ -2021,6 +2128,10 @@
 	cb.abort_auth = _ieee802_1x_abort_auth;
 	cb.tx_key = _ieee802_1x_tx_key;
 	cb.eapol_event = ieee802_1x_eapol_event;
+#ifdef CONFIG_ERP
+	cb.erp_get_key = ieee802_1x_erp_get_key;
+	cb.erp_add_key = ieee802_1x_erp_add_key;
+#endif /* CONFIG_ERP */
 
 	hapd->eapol_auth = eapol_auth_init(&conf, &cb);
 	if (hapd->eapol_auth == NULL)
@@ -2052,6 +2163,18 @@
 }
 
 
+void ieee802_1x_erp_flush(struct hostapd_data *hapd)
+{
+	struct eap_server_erp_key *erp;
+
+	while ((erp = dl_list_first(&hapd->erp_keys, struct eap_server_erp_key,
+				    list)) != NULL) {
+		dl_list_del(&erp->list);
+		bin_clear_free(erp, sizeof(*erp));
+	}
+}
+
+
 void ieee802_1x_deinit(struct hostapd_data *hapd)
 {
 	eloop_cancel_timeout(ieee802_1x_rekey, hapd, NULL);
@@ -2062,6 +2185,8 @@
 
 	eapol_auth_deinit(hapd->eapol_auth);
 	hapd->eapol_auth = NULL;
+
+	ieee802_1x_erp_flush(hapd);
 }
 
 
@@ -2218,9 +2343,9 @@
 }
 
 
-static const char * bool_txt(Boolean bool)
+static const char * bool_txt(Boolean val)
 {
-	return bool ? "TRUE" : "FALSE";
+	return val ? "TRUE" : "FALSE";
 }
 
 
@@ -2252,7 +2377,7 @@
 			  sta->aid,
 			  EAPOL_VERSION,
 			  sm->initialize);
-	if (ret < 0 || (size_t) ret >= buflen - len)
+	if (os_snprintf_error(buflen - len, ret))
 		return len;
 	len += ret;
 
@@ -2280,7 +2405,7 @@
 			  sm->reAuthPeriod,
 			  bool_txt(sm->reAuthEnabled),
 			  bool_txt(sm->keyTxEnabled));
-	if (ret < 0 || (size_t) ret >= buflen - len)
+	if (os_snprintf_error(buflen - len, ret))
 		return len;
 	len += ret;
 
@@ -2310,7 +2435,7 @@
 			  sm->dot1xAuthEapLengthErrorFramesRx,
 			  sm->dot1xAuthLastEapolFrameVersion,
 			  MAC2STR(sm->addr));
-	if (ret < 0 || (size_t) ret >= buflen - len)
+	if (os_snprintf_error(buflen - len, ret))
 		return len;
 	len += ret;
 
@@ -2348,7 +2473,7 @@
 			  sm->backendOtherRequestsToSupplicant,
 			  sm->backendAuthSuccesses,
 			  sm->backendAuthFails);
-	if (ret < 0 || (size_t) ret >= buflen - len)
+	if (os_snprintf_error(buflen - len, ret))
 		return len;
 	len += ret;
 
@@ -2370,20 +2495,28 @@
 			  1 : 2,
 			  (unsigned int) diff.sec,
 			  sm->identity);
-	if (ret < 0 || (size_t) ret >= buflen - len)
+	if (os_snprintf_error(buflen - len, ret))
 		return len;
 	len += ret;
 
+	if (sm->acct_multi_session_id_hi) {
+		ret = os_snprintf(buf + len, buflen - len,
+				  "authMultiSessionId=%08X+%08X\n",
+				  sm->acct_multi_session_id_hi,
+				  sm->acct_multi_session_id_lo);
+		if (os_snprintf_error(buflen - len, ret))
+			return len;
+		len += ret;
+	}
+
 	name1 = eap_server_get_name(0, sm->eap_type_authsrv);
 	name2 = eap_server_get_name(0, sm->eap_type_supp);
 	ret = os_snprintf(buf + len, buflen - len,
 			  "last_eap_type_as=%d (%s)\n"
 			  "last_eap_type_sta=%d (%s)\n",
-			  sm->eap_type_authsrv,
-			  name1 ? name1 : "",
-			  sm->eap_type_supp,
-			  name2 ? name2 : "");
-	if (ret < 0 || (size_t) ret >= buflen - len)
+			  sm->eap_type_authsrv, name1,
+			  sm->eap_type_supp, name2);
+	if (os_snprintf_error(buflen - len, ret))
 		return len;
 	len += ret;
 
diff --git a/src/ap/ieee802_1x.h b/src/ap/ieee802_1x.h
index e1df940..de6e0e7 100644
--- a/src/ap/ieee802_1x.h
+++ b/src/ap/ieee802_1x.h
@@ -29,6 +29,7 @@
 				   struct sta_info *sta, int authorized);
 void ieee802_1x_dump_state(FILE *f, const char *prefix, struct sta_info *sta);
 int ieee802_1x_init(struct hostapd_data *hapd);
+void ieee802_1x_erp_flush(struct hostapd_data *hapd);
 void ieee802_1x_deinit(struct hostapd_data *hapd);
 int ieee802_1x_tx_status(struct hostapd_data *hapd, struct sta_info *sta,
 			 const u8 *buf, size_t len, int ack);
diff --git a/src/ap/ndisc_snoop.c b/src/ap/ndisc_snoop.c
new file mode 100644
index 0000000..0adcc97
--- /dev/null
+++ b/src/ap/ndisc_snoop.c
@@ -0,0 +1,179 @@
+/*
+ * Neighbor Discovery snooping for Proxy ARP
+ * Copyright (c) 2014, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+#include <netinet/ip6.h>
+#include <netinet/icmp6.h>
+
+#include "utils/common.h"
+#include "l2_packet/l2_packet.h"
+#include "hostapd.h"
+#include "sta_info.h"
+#include "ap_drv_ops.h"
+#include "list.h"
+#include "x_snoop.h"
+
+struct ip6addr {
+	struct in6_addr addr;
+	struct dl_list list;
+};
+
+struct icmpv6_ndmsg {
+	struct ip6_hdr ipv6h;
+	struct icmp6_hdr icmp6h;
+	struct in6_addr target_addr;
+	u8 opt_type;
+	u8 len;
+	u8 opt_lladdr[0];
+} STRUCT_PACKED;
+
+#define ROUTER_ADVERTISEMENT	134
+#define NEIGHBOR_SOLICITATION	135
+#define NEIGHBOR_ADVERTISEMENT	136
+#define SOURCE_LL_ADDR		1
+
+static int sta_ip6addr_add(struct sta_info *sta, struct in6_addr *addr)
+{
+	struct ip6addr *ip6addr;
+
+	ip6addr = os_zalloc(sizeof(*ip6addr));
+	if (!ip6addr)
+		return -1;
+
+	os_memcpy(&ip6addr->addr, addr, sizeof(*addr));
+
+	dl_list_add_tail(&sta->ip6addr, &ip6addr->list);
+
+	return 0;
+}
+
+
+void sta_ip6addr_del(struct hostapd_data *hapd, struct sta_info *sta)
+{
+	struct ip6addr *ip6addr, *prev;
+
+	dl_list_for_each_safe(ip6addr, prev, &sta->ip6addr, struct ip6addr,
+			      list) {
+		hostapd_drv_br_delete_ip_neigh(hapd, 6, (u8 *) &ip6addr->addr);
+		os_free(ip6addr);
+	}
+}
+
+
+static int sta_has_ip6addr(struct sta_info *sta, struct in6_addr *addr)
+{
+	struct ip6addr *ip6addr;
+
+	dl_list_for_each(ip6addr, &sta->ip6addr, struct ip6addr, list) {
+		if (ip6addr->addr.s6_addr32[0] == addr->s6_addr32[0] &&
+		    ip6addr->addr.s6_addr32[1] == addr->s6_addr32[1] &&
+		    ip6addr->addr.s6_addr32[2] == addr->s6_addr32[2] &&
+		    ip6addr->addr.s6_addr32[3] == addr->s6_addr32[3])
+			return 1;
+	}
+
+	return 0;
+}
+
+
+static void ucast_to_stas(struct hostapd_data *hapd, const u8 *buf, size_t len)
+{
+	struct sta_info *sta;
+
+	for (sta = hapd->sta_list; sta; sta = sta->next) {
+		if (!(sta->flags & WLAN_STA_AUTHORIZED))
+			continue;
+		x_snoop_mcast_to_ucast_convert_send(hapd, sta, (u8 *) buf, len);
+	}
+}
+
+
+static void handle_ndisc(void *ctx, const u8 *src_addr, const u8 *buf,
+			 size_t len)
+{
+	struct hostapd_data *hapd = ctx;
+	struct icmpv6_ndmsg *msg;
+	struct in6_addr *saddr;
+	struct sta_info *sta;
+	int res;
+	char addrtxt[INET6_ADDRSTRLEN + 1];
+
+	if (len < ETH_HLEN + sizeof(struct ip6_hdr) + sizeof(struct icmp6_hdr))
+		return;
+	msg = (struct icmpv6_ndmsg *) &buf[ETH_HLEN];
+	switch (msg->icmp6h.icmp6_type) {
+	case NEIGHBOR_SOLICITATION:
+		if (len < ETH_HLEN + sizeof(*msg))
+			return;
+		if (msg->opt_type != SOURCE_LL_ADDR)
+			return;
+
+		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)) {
+			if (len < ETH_HLEN + sizeof(*msg) + ETH_ALEN)
+				return;
+			sta = ap_get_sta(hapd, msg->opt_lladdr);
+			if (!sta)
+				return;
+
+			if (sta_has_ip6addr(sta, saddr))
+				return;
+
+			if (inet_ntop(AF_INET6, saddr, addrtxt, sizeof(addrtxt))
+			    == NULL)
+				addrtxt[0] = '\0';
+			wpa_printf(MSG_DEBUG, "ndisc_snoop: Learned new IPv6 address %s for "
+				   MACSTR, addrtxt, MAC2STR(sta->addr));
+			hostapd_drv_br_delete_ip_neigh(hapd, 6, (u8 *) saddr);
+			res = hostapd_drv_br_add_ip_neigh(hapd, 6, (u8 *) saddr,
+							  128, sta->addr);
+			if (res) {
+				wpa_printf(MSG_ERROR,
+					   "ndisc_snoop: Adding ip neigh failed: %d",
+					   res);
+				return;
+			}
+
+			if (sta_ip6addr_add(sta, saddr))
+				return;
+		}
+		break;
+	case ROUTER_ADVERTISEMENT:
+		if (hapd->conf->disable_dgaf)
+			ucast_to_stas(hapd, buf, len);
+		break;
+	case NEIGHBOR_ADVERTISEMENT:
+		if (hapd->conf->na_mcast_to_ucast)
+			ucast_to_stas(hapd, buf, len);
+		break;
+	default:
+		break;
+	}
+}
+
+
+int ndisc_snoop_init(struct hostapd_data *hapd)
+{
+	hapd->sock_ndisc = x_snoop_get_l2_packet(hapd, handle_ndisc,
+						 L2_PACKET_FILTER_NDISC);
+	if (hapd->sock_ndisc == NULL) {
+		wpa_printf(MSG_DEBUG,
+			   "ndisc_snoop: Failed to initialize L2 packet processing for NDISC packets: %s",
+			   strerror(errno));
+		return -1;
+	}
+
+	return 0;
+}
+
+
+void ndisc_snoop_deinit(struct hostapd_data *hapd)
+{
+	l2_packet_deinit(hapd->sock_ndisc);
+}
diff --git a/src/ap/ndisc_snoop.h b/src/ap/ndisc_snoop.h
new file mode 100644
index 0000000..3cc9a55
--- /dev/null
+++ b/src/ap/ndisc_snoop.h
@@ -0,0 +1,36 @@
+/*
+ * Neighbor Discovery snooping for Proxy ARP
+ * Copyright (c) 2014, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef NDISC_SNOOP_H
+#define NDISC_SNOOP_H
+
+#if defined(CONFIG_PROXYARP) && defined(CONFIG_IPV6)
+
+int ndisc_snoop_init(struct hostapd_data *hapd);
+void ndisc_snoop_deinit(struct hostapd_data *hapd);
+void sta_ip6addr_del(struct hostapd_data *hapd, struct sta_info *sta);
+
+#else /* CONFIG_PROXYARP && CONFIG_IPV6 */
+
+static inline int ndisc_snoop_init(struct hostapd_data *hapd)
+{
+	return 0;
+}
+
+static inline void ndisc_snoop_deinit(struct hostapd_data *hapd)
+{
+}
+
+static inline void sta_ip6addr_del(struct hostapd_data *hapd,
+				   struct sta_info *sta)
+{
+}
+
+#endif /* CONFIG_PROXYARP && CONFIG_IPV6 */
+
+#endif /* NDISC_SNOOP_H */
diff --git a/src/ap/peerkey_auth.c b/src/ap/peerkey_auth.c
index 612babc..efc1d7e 100644
--- a/src/ap/peerkey_auth.c
+++ b/src/ap/peerkey_auth.c
@@ -79,15 +79,15 @@
 
 
 void wpa_smk_m1(struct wpa_authenticator *wpa_auth,
-		struct wpa_state_machine *sm, struct wpa_eapol_key *key)
+		struct wpa_state_machine *sm, struct wpa_eapol_key *key,
+		const u8 *key_data, size_t key_data_len)
 {
 	struct wpa_eapol_ie_parse kde;
 	struct wpa_stsl_search search;
 	u8 *buf, *pos;
 	size_t buf_len;
 
-	if (wpa_parse_kde_ies((const u8 *) (key + 1),
-			      WPA_GET_BE16(key->key_data_length), &kde) < 0) {
+	if (wpa_parse_kde_ies(key_data, key_data_len, &kde) < 0) {
 		wpa_printf(MSG_INFO, "RSN: Failed to parse KDEs in SMK M1");
 		return;
 	}
@@ -253,14 +253,14 @@
 
 
 void wpa_smk_m3(struct wpa_authenticator *wpa_auth,
-		struct wpa_state_machine *sm, struct wpa_eapol_key *key)
+		struct wpa_state_machine *sm, struct wpa_eapol_key *key,
+		const u8 *key_data, size_t key_data_len)
 {
 	struct wpa_eapol_ie_parse kde;
 	struct wpa_stsl_search search;
 	u8 smk[32], buf[ETH_ALEN + 8 + 2 * WPA_NONCE_LEN], *pos;
 
-	if (wpa_parse_kde_ies((const u8 *) (key + 1),
-			      WPA_GET_BE16(key->key_data_length), &kde) < 0) {
+	if (wpa_parse_kde_ies(key_data, key_data_len, &kde) < 0) {
 		wpa_printf(MSG_INFO, "RSN: Failed to parse KDEs in SMK M3");
 		return;
 	}
@@ -324,15 +324,15 @@
 
 
 void wpa_smk_error(struct wpa_authenticator *wpa_auth,
-		   struct wpa_state_machine *sm, struct wpa_eapol_key *key)
+		   struct wpa_state_machine *sm,
+		   const u8 *key_data, size_t key_data_len)
 {
 	struct wpa_eapol_ie_parse kde;
 	struct wpa_stsl_search search;
 	struct rsn_error_kde error;
 	u16 mui, error_type;
 
-	if (wpa_parse_kde_ies((const u8 *) (key + 1),
-			      WPA_GET_BE16(key->key_data_length), &kde) < 0) {
+	if (wpa_parse_kde_ies(key_data, key_data_len, &kde) < 0) {
 		wpa_printf(MSG_INFO, "RSN: Failed to parse KDEs in SMK Error");
 		return;
 	}
diff --git a/src/ap/pmksa_cache_auth.c b/src/ap/pmksa_cache_auth.c
index 9de4cff..877affe 100644
--- a/src/ap/pmksa_cache_auth.c
+++ b/src/ap/pmksa_cache_auth.c
@@ -1,6 +1,6 @@
 /*
  * hostapd - PMKSA cache for IEEE 802.11i RSN
- * 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.
@@ -12,6 +12,7 @@
 #include "utils/eloop.h"
 #include "eapol_auth/eapol_auth_sm.h"
 #include "eapol_auth/eapol_auth_sm_i.h"
+#include "radius/radius_das.h"
 #include "sta_info.h"
 #include "ap_config.h"
 #include "pmksa_cache_auth.h"
@@ -146,6 +147,9 @@
 
 	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;
 }
 
 
@@ -183,6 +187,9 @@
 
 	eapol->eap_type_authsrv = entry->eap_type_authsrv;
 	((struct sta_info *) eapol->sta)->vlan_id = entry->vlan_id;
+
+	eapol->acct_multi_session_id_hi = entry->acct_multi_session_id_hi;
+	eapol->acct_multi_session_id_lo = entry->acct_multi_session_id_lo;
 }
 
 
@@ -227,6 +234,8 @@
  * @pmksa: Pointer to PMKSA cache data from pmksa_cache_auth_init()
  * @pmk: The new pairwise master key
  * @pmk_len: PMK length in bytes, usually PMK_LEN (32)
+ * @kck: Key confirmation key or %NULL if not yet derived
+ * @kck_len: KCK length in bytes
  * @aa: Authenticator address
  * @spa: Supplicant address
  * @session_timeout: Session timeout
@@ -242,8 +251,9 @@
 struct rsn_pmksa_cache_entry *
 pmksa_cache_auth_add(struct rsn_pmksa_cache *pmksa,
 		     const u8 *pmk, size_t pmk_len,
-		const u8 *aa, const u8 *spa, int session_timeout,
-		struct eapol_state_machine *eapol, int akmp)
+		     const u8 *kck, size_t kck_len,
+		     const u8 *aa, const u8 *spa, int session_timeout,
+		     struct eapol_state_machine *eapol, int akmp)
 {
 	struct rsn_pmksa_cache_entry *entry, *pos;
 	struct os_reltime now;
@@ -251,13 +261,21 @@
 	if (pmk_len > PMK_LEN)
 		return NULL;
 
+	if (wpa_key_mgmt_suite_b(akmp) && !kck)
+		return NULL;
+
 	entry = os_zalloc(sizeof(*entry));
 	if (entry == NULL)
 		return NULL;
 	os_memcpy(entry->pmk, pmk, pmk_len);
 	entry->pmk_len = pmk_len;
-	rsn_pmkid(pmk, pmk_len, aa, spa, entry->pmkid,
-		  wpa_key_mgmt_sha256(akmp));
+	if (akmp == WPA_KEY_MGMT_IEEE8021X_SUITE_B_192)
+		rsn_pmkid_suite_b_192(kck, kck_len, aa, spa, entry->pmkid);
+	else if (wpa_key_mgmt_suite_b(akmp))
+		rsn_pmkid_suite_b(kck, kck_len, aa, spa, entry->pmkid);
+	else
+		rsn_pmkid(pmk, pmk_len, aa, spa, entry->pmkid,
+			  wpa_key_mgmt_sha256(akmp));
 	os_get_reltime(&now);
 	entry->expiration = now.sec;
 	if (session_timeout > 0)
@@ -437,3 +455,74 @@
 
 	return pmksa;
 }
+
+
+static int das_attr_match(struct rsn_pmksa_cache_entry *entry,
+			  struct radius_das_attrs *attr)
+{
+	int match = 0;
+
+	if (attr->sta_addr) {
+		if (os_memcmp(attr->sta_addr, entry->spa, ETH_ALEN) != 0)
+			return 0;
+		match++;
+	}
+
+	if (attr->acct_multi_session_id) {
+		char buf[20];
+
+		if (attr->acct_multi_session_id_len != 17)
+			return 0;
+		os_snprintf(buf, sizeof(buf), "%08X+%08X",
+			    entry->acct_multi_session_id_hi,
+			    entry->acct_multi_session_id_lo);
+		if (os_memcmp(attr->acct_multi_session_id, buf, 17) != 0)
+			return 0;
+		match++;
+	}
+
+	if (attr->cui) {
+		if (!entry->cui ||
+		    attr->cui_len != wpabuf_len(entry->cui) ||
+		    os_memcmp(attr->cui, wpabuf_head(entry->cui),
+			      attr->cui_len) != 0)
+			return 0;
+		match++;
+	}
+
+	if (attr->user_name) {
+		if (!entry->identity ||
+		    attr->user_name_len != entry->identity_len ||
+		    os_memcmp(attr->user_name, entry->identity,
+			      attr->user_name_len) != 0)
+			return 0;
+		match++;
+	}
+
+	return match;
+}
+
+
+int pmksa_cache_auth_radius_das_disconnect(struct rsn_pmksa_cache *pmksa,
+					   struct radius_das_attrs *attr)
+{
+	int found = 0;
+	struct rsn_pmksa_cache_entry *entry, *prev;
+
+	if (attr->acct_session_id)
+		return -1;
+
+	entry = pmksa->pmksa;
+	while (entry) {
+		if (das_attr_match(entry, attr)) {
+			found++;
+			prev = entry;
+			entry = entry->next;
+			pmksa_cache_free_entry(pmksa, prev);
+			continue;
+		}
+		entry = entry->next;
+	}
+
+	return found ? 0 : -1;
+}
diff --git a/src/ap/pmksa_cache_auth.h b/src/ap/pmksa_cache_auth.h
index aa90024..8b7be12 100644
--- a/src/ap/pmksa_cache_auth.h
+++ b/src/ap/pmksa_cache_auth.h
@@ -30,6 +30,9 @@
 	u8 eap_type_authsrv;
 	int vlan_id;
 	int opportunistic;
+
+	u32 acct_multi_session_id_hi;
+	u32 acct_multi_session_id_lo;
 };
 
 struct rsn_pmksa_cache;
@@ -47,6 +50,7 @@
 struct rsn_pmksa_cache_entry *
 pmksa_cache_auth_add(struct rsn_pmksa_cache *pmksa,
 		     const u8 *pmk, size_t pmk_len,
+		     const u8 *kck, size_t kck_len,
 		     const u8 *aa, const u8 *spa, int session_timeout,
 		     struct eapol_state_machine *eapol, int akmp);
 struct rsn_pmksa_cache_entry *
@@ -57,5 +61,7 @@
 			       struct eapol_state_machine *eapol);
 void pmksa_cache_free_entry(struct rsn_pmksa_cache *pmksa,
 			    struct rsn_pmksa_cache_entry *entry);
+int pmksa_cache_auth_radius_das_disconnect(struct rsn_pmksa_cache *pmksa,
+					   struct radius_das_attrs *attr);
 
 #endif /* PMKSA_CACHE_H */
diff --git a/src/ap/sta_info.c b/src/ap/sta_info.c
index efd2a72..20847d5 100644
--- a/src/ap/sta_info.c
+++ b/src/ap/sta_info.c
@@ -31,6 +31,7 @@
 #include "ap_drv_ops.h"
 #include "gas_serv.h"
 #include "wnm_ap.h"
+#include "ndisc_snoop.h"
 #include "sta_info.h"
 
 static void ap_sta_remove_in_other_bss(struct hostapd_data *hapd,
@@ -144,6 +145,12 @@
 }
 
 
+void ap_sta_ip6addr_del(struct hostapd_data *hapd, struct sta_info *sta)
+{
+	sta_ip6addr_del(hapd, sta);
+}
+
+
 void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta)
 {
 	int set_beacon = 0;
@@ -156,10 +163,27 @@
 	if (sta->flags & WLAN_STA_WDS)
 		hostapd_set_wds_sta(hapd, NULL, sta->addr, sta->aid, 0);
 
+	if (sta->ipaddr)
+		hostapd_drv_br_delete_ip_neigh(hapd, 4, (u8 *) &sta->ipaddr);
+	ap_sta_ip6addr_del(hapd, sta);
+
 	if (!hapd->iface->driver_ap_teardown &&
 	    !(sta->flags & WLAN_STA_PREAUTH))
 		hostapd_drv_sta_remove(hapd, sta->addr);
 
+#ifndef CONFIG_NO_VLAN
+	if (sta->vlan_id_bound) {
+		/*
+		 * Need to remove the STA entry before potentially removing the
+		 * VLAN.
+		 */
+		if (hapd->iface->driver_ap_teardown &&
+		    !(sta->flags & WLAN_STA_PREAUTH))
+			hostapd_drv_sta_remove(hapd, sta->addr);
+		vlan_remove_dynamic(hapd, sta->vlan_id_bound);
+	}
+#endif /* CONFIG_NO_VLAN */
+
 	ap_sta_hash_del(hapd, sta);
 	ap_sta_list_del(hapd, sta);
 
@@ -224,6 +248,11 @@
 		set_beacon++;
 #endif /* NEED_AP_MLME && CONFIG_IEEE80211N */
 
+#ifdef CONFIG_MESH
+	if (hapd->mesh_sta_free_cb)
+		hapd->mesh_sta_free_cb(sta);
+#endif /* CONFIG_MESH */
+
 	if (set_beacon)
 		ieee802_11_set_beacons(hapd->iface);
 
@@ -234,6 +263,7 @@
 	eloop_cancel_timeout(ap_handle_session_warning_timer, hapd, sta);
 	eloop_cancel_timeout(ap_sta_deauth_cb_timeout, hapd, sta);
 	eloop_cancel_timeout(ap_sta_disassoc_cb_timeout, hapd, sta);
+	sae_clear_retransmit_timer(hapd, sta);
 
 	ieee802_1x_free_station(sta);
 	wpa_auth_sta_deinit(sta->wpa_sm);
@@ -353,8 +383,15 @@
 			 * but do not disconnect the station now.
 			 */
 			next_time = hapd->conf->ap_max_inactivity + fuzz;
-		} else if (inactive_sec < hapd->conf->ap_max_inactivity &&
-			   sta->flags & WLAN_STA_ASSOC) {
+		} else if (inactive_sec == -ENOENT) {
+			wpa_msg(hapd->msg_ctx, MSG_DEBUG,
+				"Station " MACSTR " has lost its driver entry",
+				MAC2STR(sta->addr));
+
+			/* Avoid sending client probe on removed client */
+			sta->timeout_next = STA_DISASSOC;
+			goto skip_poll;
+		} else if (inactive_sec < hapd->conf->ap_max_inactivity) {
 			/* station activity detected; reset timeout state */
 			wpa_msg(hapd->msg_ctx, MSG_DEBUG,
 				"Station " MACSTR " has been active %is ago",
@@ -386,6 +423,7 @@
 		next_time = hapd->conf->ap_max_inactivity;
 	}
 
+skip_poll:
 	if (next_time) {
 		wpa_printf(MSG_DEBUG, "%s: register ap_handle_timer timeout "
 			   "for " MACSTR " (%lu seconds)",
@@ -594,8 +632,9 @@
 	hapd->sta_list = sta;
 	hapd->num_sta++;
 	ap_sta_hash_add(hapd, sta);
-	sta->ssid = &hapd->conf->ssid;
 	ap_sta_remove_in_other_bss(hapd, sta);
+	sta->last_seq_ctrl = WLAN_INVALID_MGMT_SEQ;
+	dl_list_init(&sta->ip6addr);
 
 	return sta;
 }
@@ -605,6 +644,10 @@
 {
 	ieee802_1x_notify_port_enabled(sta->eapol_sm, 0);
 
+	if (sta->ipaddr)
+		hostapd_drv_br_delete_ip_neigh(hapd, 4, (u8 *) &sta->ipaddr);
+	ap_sta_ip6addr_del(hapd, sta);
+
 	wpa_printf(MSG_DEBUG, "Removing STA " MACSTR " from kernel driver",
 		   MAC2STR(sta->addr));
 	if (hostapd_drv_sta_remove(hapd, sta->addr) &&
@@ -657,6 +700,7 @@
 {
 	wpa_printf(MSG_DEBUG, "%s: disassociate STA " MACSTR,
 		   hapd->conf->iface, MAC2STR(sta->addr));
+	sta->last_seq_ctrl = WLAN_INVALID_MGMT_SEQ;
 	sta->flags &= ~(WLAN_STA_ASSOC | WLAN_STA_ASSOC_REQ_OK);
 	ap_sta_set_authorized(hapd, sta, 0);
 	sta->timeout_next = STA_DEAUTH;
@@ -695,7 +739,8 @@
 {
 	wpa_printf(MSG_DEBUG, "%s: deauthenticate STA " MACSTR,
 		   hapd->conf->iface, MAC2STR(sta->addr));
-	sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC);
+	sta->last_seq_ctrl = WLAN_INVALID_MGMT_SEQ;
+	sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC | WLAN_STA_ASSOC_REQ_OK);
 	ap_sta_set_authorized(hapd, sta, 0);
 	sta->timeout_next = STA_REMOVE;
 	wpa_printf(MSG_DEBUG, "%s: reschedule ap_handle_timer timeout "
@@ -735,33 +780,19 @@
 #endif /* CONFIG_WPS */
 
 
-int ap_sta_bind_vlan(struct hostapd_data *hapd, struct sta_info *sta,
-		     int old_vlanid)
+int ap_sta_bind_vlan(struct hostapd_data *hapd, struct sta_info *sta)
 {
 #ifndef CONFIG_NO_VLAN
 	const char *iface;
 	struct hostapd_vlan *vlan = NULL;
 	int ret;
-
-	/*
-	 * Do not proceed furthur if the vlan id remains same. We do not want
-	 * duplicate dynamic vlan entries.
-	 */
-	if (sta->vlan_id == old_vlanid)
-		return 0;
-
-	/*
-	 * During 1x reauth, if the vlan id changes, then remove the old id and
-	 * proceed furthur to add the new one.
-	 */
-	if (old_vlanid > 0)
-		vlan_remove_dynamic(hapd, old_vlanid);
+	int old_vlanid = sta->vlan_id_bound;
 
 	iface = hapd->conf->iface;
-	if (sta->ssid->vlan[0])
-		iface = sta->ssid->vlan;
+	if (hapd->conf->ssid.vlan[0])
+		iface = hapd->conf->ssid.vlan;
 
-	if (sta->ssid->dynamic_vlan == DYNAMIC_VLAN_DISABLED)
+	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;
@@ -779,12 +810,21 @@
 			iface = vlan->ifname;
 	}
 
+	/*
+	 * Do not increment ref counters if the VLAN ID remains same, but do
+	 * not skip hostapd_drv_set_sta_vlan() as hostapd_drv_sta_remove() might
+	 * have been called before.
+	 */
+	if (sta->vlan_id == old_vlanid)
+		goto skip_counting;
+
 	if (sta->vlan_id > 0 && vlan == NULL) {
 		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
 			       HOSTAPD_LEVEL_DEBUG, "could not find VLAN for "
 			       "binding station to (vlan_id=%d)",
 			       sta->vlan_id);
-		return -1;
+		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) {
@@ -793,11 +833,12 @@
 				       HOSTAPD_LEVEL_DEBUG, "could not add "
 				       "dynamic VLAN interface for vlan_id=%d",
 				       sta->vlan_id);
-			return -1;
+			ret = -1;
+			goto done;
 		}
 
 		iface = vlan->ifname;
-		if (vlan_setup_encryption_dyn(hapd, sta->ssid, iface) != 0) {
+		if (vlan_setup_encryption_dyn(hapd, iface) != 0) {
 			hostapd_logger(hapd, sta->addr,
 				       HOSTAPD_MODULE_IEEE80211,
 				       HOSTAPD_LEVEL_DEBUG, "could not "
@@ -810,7 +851,7 @@
 			       HOSTAPD_LEVEL_DEBUG, "added new dynamic VLAN "
 			       "interface '%s'", iface);
 	} else if (vlan && vlan->vlan_id == sta->vlan_id) {
-		if (sta->vlan_id > 0) {
+		if (vlan->dynamic_vlan > 0) {
 			vlan->dynamic_vlan++;
 			hostapd_logger(hapd, sta->addr,
 				       HOSTAPD_MODULE_IEEE80211,
@@ -824,7 +865,7 @@
 		 * 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, sta->ssid, iface) != 0) {
+		if (vlan_setup_encryption_dyn(hapd, iface) != 0) {
 			hostapd_logger(hapd, sta->addr,
 				       HOSTAPD_MODULE_IEEE80211,
 				       HOSTAPD_LEVEL_DEBUG, "could not "
@@ -834,6 +875,10 @@
 		}
 	}
 
+	/* ref counters have been increased, so mark the station */
+	sta->vlan_id_bound = sta->vlan_id;
+
+skip_counting:
 	hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
 		       HOSTAPD_LEVEL_DEBUG, "binding station to interface "
 		       "'%s'", iface);
@@ -847,6 +892,12 @@
 			       HOSTAPD_LEVEL_DEBUG, "could not bind the STA "
 			       "entry to vlan_id=%d", sta->vlan_id);
 	}
+
+	/* During 1x reauth, if the vlan id changes, then remove the old id. */
+	if (old_vlanid > 0 && old_vlanid != sta->vlan_id)
+		vlan_remove_dynamic(hapd, old_vlanid);
+done:
+
 	return ret;
 #else /* CONFIG_NO_VLAN */
 	return 0;
@@ -904,7 +955,15 @@
 	sta->sa_query_trans_id = nbuf;
 	sta->sa_query_count++;
 
-	os_get_random(trans_id, WLAN_SA_QUERY_TR_ID_LEN);
+	if (os_get_random(trans_id, WLAN_SA_QUERY_TR_ID_LEN) < 0) {
+		/*
+		 * We don't really care which ID is used here, so simply
+		 * hardcode this if the mostly theoretical os_get_random()
+		 * failure happens.
+		 */
+		trans_id[0] = 0x12;
+		trans_id[1] = 0x34;
+	}
 
 	timeout = hapd->conf->assoc_sa_query_retry_timeout;
 	sec = ((timeout / 1000) * 1024) / 1000;
@@ -949,6 +1008,11 @@
 	if (!!authorized == !!(sta->flags & WLAN_STA_AUTHORIZED))
 		return;
 
+	if (authorized)
+		sta->flags |= WLAN_STA_AUTHORIZED;
+	else
+		sta->flags &= ~WLAN_STA_AUTHORIZED;
+
 #ifdef CONFIG_P2P
 	if (hapd->p2p_group == NULL) {
 		if (sta->p2p_ie != NULL &&
@@ -964,6 +1028,10 @@
 #endif /* CONFIG_P2P */
 		os_snprintf(buf, sizeof(buf), MACSTR, MAC2STR(sta->addr));
 
+	if (hapd->sta_authorized_cb)
+		hapd->sta_authorized_cb(hapd->sta_authorized_cb_ctx,
+					sta->addr, authorized, dev_addr);
+
 	if (authorized) {
 		char ip_addr[100];
 		ip_addr[0] = '\0';
@@ -984,8 +1052,6 @@
 			wpa_msg_no_global(hapd->msg_ctx_parent, MSG_INFO,
 					  AP_STA_CONNECTED "%s%s",
 					  buf, ip_addr);
-
-		sta->flags |= WLAN_STA_AUTHORIZED;
 	} else {
 		wpa_msg(hapd->msg_ctx, MSG_INFO, AP_STA_DISCONNECTED "%s", buf);
 
@@ -993,13 +1059,7 @@
 		    hapd->msg_ctx_parent != hapd->msg_ctx)
 			wpa_msg_no_global(hapd->msg_ctx_parent, MSG_INFO,
 					  AP_STA_DISCONNECTED "%s", buf);
-
-		sta->flags &= ~WLAN_STA_AUTHORIZED;
 	}
-
-	if (hapd->sta_authorized_cb)
-		hapd->sta_authorized_cb(hapd->sta_authorized_cb_ctx,
-					sta->addr, authorized, dev_addr);
 }
 
 
@@ -1067,7 +1127,7 @@
 	int res;
 
 	buf[0] = '\0';
-	res = os_snprintf(buf, buflen, "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
+	res = os_snprintf(buf, buflen, "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
 			  (flags & WLAN_STA_AUTH ? "[AUTH]" : ""),
 			  (flags & WLAN_STA_ASSOC ? "[ASSOC]" : ""),
 			  (flags & WLAN_STA_AUTHORIZED ? "[AUTHORIZED]" : ""),
@@ -1085,8 +1145,11 @@
 			  (flags & WLAN_STA_WPS2 ? "[WPS2]" : ""),
 			  (flags & WLAN_STA_GAS ? "[GAS]" : ""),
 			  (flags & WLAN_STA_VHT ? "[VHT]" : ""),
+			  (flags & WLAN_STA_VENDOR_VHT ? "[VENDOR_VHT]" : ""),
 			  (flags & WLAN_STA_WNM_SLEEP_MODE ?
 			   "[WNM_SLEEP_MODE]" : ""));
+	if (os_snprintf_error(buflen, res))
+		res = -1;
 
 	return res;
 }
diff --git a/src/ap/sta_info.h b/src/ap/sta_info.h
index faf32d8..52a9997 100644
--- a/src/ap/sta_info.h
+++ b/src/ap/sta_info.h
@@ -9,6 +9,13 @@
 #ifndef STA_INFO_H
 #define STA_INFO_H
 
+#ifdef CONFIG_MESH
+/* needed for mesh_plink_state enum */
+#include "common/defs.h"
+#endif /* CONFIG_MESH */
+
+#include "list.h"
+
 /* STA flags */
 #define WLAN_STA_AUTH BIT(0)
 #define WLAN_STA_ASSOC BIT(1)
@@ -28,6 +35,7 @@
 #define WLAN_STA_VHT BIT(18)
 #define WLAN_STA_WNM_SLEEP_MODE BIT(19)
 #define WLAN_STA_VHT_OPMODE_ENABLED BIT(20)
+#define WLAN_STA_VENDOR_VHT BIT(21)
 #define WLAN_STA_PENDING_DISASSOC_CB BIT(29)
 #define WLAN_STA_PENDING_DEAUTH_CB BIT(30)
 #define WLAN_STA_NONERP BIT(31)
@@ -41,6 +49,8 @@
 	struct sta_info *next; /* next entry in sta list */
 	struct sta_info *hnext; /* next entry in hash table list */
 	u8 addr[6];
+	be32 ipaddr;
+	struct dl_list ip6addr; /* list head for struct ip6addr */
 	u16 aid; /* STA's unique AID (1 .. 2007) or 0 if not yet assigned */
 	u32 flags; /* Bitfield of WLAN_STA_* */
 	u16 capability;
@@ -49,6 +59,20 @@
 	int supported_rates_len;
 	u8 qosinfo; /* Valid when WLAN_STA_WMM is set */
 
+#ifdef CONFIG_MESH
+	enum mesh_plink_state plink_state;
+	u16 peer_lid;
+	u16 my_lid;
+	u16 mpm_close_reason;
+	int mpm_retries;
+	u8 my_nonce[32];
+	u8 peer_nonce[32];
+	u8 aek[32];	/* SHA256 digest length */
+	u8 mtk[16];
+	u8 mgtk[16];
+	u8 sae_auth_retry;
+#endif /* CONFIG_MESH */
+
 	unsigned int nonerp_set:1;
 	unsigned int no_short_slot_time_set:1;
 	unsigned int no_short_preamble_set:1;
@@ -61,6 +85,7 @@
 	unsigned int remediation:1;
 	unsigned int hs20_deauth_requested:1;
 	unsigned int session_timeout_set:1;
+	unsigned int radius_das_match:1;
 
 	u16 auth_alg;
 
@@ -92,10 +117,8 @@
 	struct wpa_state_machine *wpa_sm;
 	struct rsn_preauth_interface *preauth_iface;
 
-	struct hostapd_ssid *ssid; /* SSID selection based on (Re)AssocReq */
-	struct hostapd_ssid *ssid_probe; /* SSID selection based on ProbeReq */
-
-	int vlan_id;
+	int vlan_id; /* 0: none, >0: VID */
+	int vlan_id_bound; /* updated by ap_sta_bind_vlan() */
 	 /* PSKs from RADIUS authentication server */
 	struct hostapd_sta_wpa_psk_short *psk;
 
@@ -138,6 +161,12 @@
 #endif /* CONFIG_SAE */
 
 	u32 session_timeout; /* valid only if session_timeout_set == 1 */
+
+	/* Last Authentication/(Re)Association Request/Action frame sequence
+	 * control */
+	u16 last_seq_ctrl;
+	/* Last Authentication/(Re)Association Request/Action frame subtype */
+	u8 last_subtype;
 };
 
 
@@ -167,6 +196,7 @@
 struct sta_info * ap_get_sta_p2p(struct hostapd_data *hapd, const u8 *addr);
 void ap_sta_hash_add(struct hostapd_data *hapd, struct sta_info *sta);
 void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta);
+void ap_sta_ip6addr_del(struct hostapd_data *hapd, struct sta_info *sta);
 void hostapd_free_stas(struct hostapd_data *hapd);
 void ap_handle_timer(void *eloop_ctx, void *timeout_ctx);
 void ap_sta_replenish_timeout(struct hostapd_data *hapd, struct sta_info *sta,
@@ -186,8 +216,7 @@
 int ap_sta_wps_cancel(struct hostapd_data *hapd,
 		      struct sta_info *sta, void *ctx);
 #endif /* CONFIG_WPS */
-int ap_sta_bind_vlan(struct hostapd_data *hapd, struct sta_info *sta,
-		     int old_vlanid);
+int ap_sta_bind_vlan(struct hostapd_data *hapd, struct sta_info *sta);
 void ap_sta_start_sa_query(struct hostapd_data *hapd, struct sta_info *sta);
 void ap_sta_stop_sa_query(struct hostapd_data *hapd, struct sta_info *sta);
 int ap_check_sa_query_timeout(struct hostapd_data *hapd, struct sta_info *sta);
diff --git a/src/ap/utils.c b/src/ap/utils.c
index 931968c..d60555a 100644
--- a/src/ap/utils.c
+++ b/src/ap/utils.c
@@ -59,6 +59,8 @@
 		if (!osta)
 			continue;
 
+		wpa_printf(MSG_INFO, "%s: Prune association for " MACSTR,
+			   ohapd->conf->iface, MAC2STR(osta->addr));
 		ap_sta_disassociate(ohapd, osta, WLAN_REASON_UNSPECIFIED);
 	}
 
diff --git a/src/ap/vlan_init.c b/src/ap/vlan_init.c
index 4e4a352..fd1c8dd 100644
--- a/src/ap/vlan_init.c
+++ b/src/ap/vlan_init.c
@@ -9,6 +9,13 @@
  */
 
 #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"
@@ -20,12 +27,6 @@
 
 #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>
-
 #include "drivers/priv_netlink.h"
 #include "utils/eloop.h"
 
@@ -34,6 +35,90 @@
 	int s; /* socket on which to listen for new/removed interfaces. */
 };
 
+#define DVLAN_CLEAN_BR         0x1
+#define DVLAN_CLEAN_VLAN       0x2
+#define DVLAN_CLEAN_VLAN_PORT  0x4
+
+struct dynamic_iface {
+	char ifname[IFNAMSIZ + 1];
+	int usage;
+	int clean;
+	struct dynamic_iface *next;
+};
+
+
+/* Increment ref counter for ifname and add clean flag.
+ * If not in list, add it only if some flags are given.
+ */
+static void dyn_iface_get(struct hostapd_data *hapd, const char *ifname,
+			  int clean)
+{
+	struct dynamic_iface *next, **dynamic_ifaces;
+	struct hapd_interfaces *interfaces;
+
+	interfaces = hapd->iface->interfaces;
+	dynamic_ifaces = &interfaces->vlan_priv;
+
+	for (next = *dynamic_ifaces; next; next = next->next) {
+		if (os_strcmp(ifname, next->ifname) == 0)
+			break;
+	}
+
+	if (next) {
+		next->usage++;
+		next->clean |= clean;
+		return;
+	}
+
+	if (!clean)
+		return;
+
+	next = os_zalloc(sizeof(*next));
+	if (!next)
+		return;
+	os_strlcpy(next->ifname, ifname, sizeof(next->ifname));
+	next->usage = 1;
+	next->clean = clean;
+	next->next = *dynamic_ifaces;
+	*dynamic_ifaces = next;
+}
+
+
+/* Decrement reference counter for given ifname.
+ * Return clean flag iff reference counter was decreased to zero, else zero
+ */
+static int dyn_iface_put(struct hostapd_data *hapd, const char *ifname)
+{
+	struct dynamic_iface *next, *prev = NULL, **dynamic_ifaces;
+	struct hapd_interfaces *interfaces;
+	int clean;
+
+	interfaces = hapd->iface->interfaces;
+	dynamic_ifaces = &interfaces->vlan_priv;
+
+	for (next = *dynamic_ifaces; next; next = next->next) {
+		if (os_strcmp(ifname, next->ifname) == 0)
+			break;
+		prev = next;
+	}
+
+	if (!next)
+		return 0;
+
+	next->usage--;
+	if (next->usage)
+		return 0;
+
+	if (prev)
+		prev->next = next->next;
+	else
+		*dynamic_ifaces = next->next;
+	clean = next->clean;
+	os_free(next);
+
+	return clean;
+}
+
 
 static int ifconfig_helper(const char *if_name, int up)
 {
@@ -481,11 +566,13 @@
 	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) {
+		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",
@@ -500,8 +587,8 @@
 				            "brvlan%d", vlan->vlan_id);
 			}
 
-			if (!br_addbr(br_name))
-				vlan->clean |= DVLAN_CLEAN_BR;
+			dyn_iface_get(hapd, br_name,
+				      br_addbr(br_name) ? 0 : DVLAN_CLEAN_BR);
 
 			ifconfig_up(br_name);
 
@@ -517,13 +604,16 @@
 						    sizeof(vlan_ifname),
 						    "vlan%d", vlan->vlan_id);
 
+				clean = 0;
 				ifconfig_up(tagged_interface);
 				if (!vlan_add(tagged_interface, vlan->vlan_id,
 					      vlan_ifname))
-					vlan->clean |= DVLAN_CLEAN_VLAN;
+					clean |= DVLAN_CLEAN_VLAN;
 
 				if (!br_addif(br_name, vlan_ifname))
-					vlan->clean |= DVLAN_CLEAN_VLAN_PORT;
+					clean |= DVLAN_CLEAN_VLAN_PORT;
+
+				dyn_iface_get(hapd, vlan_ifname, clean);
 
 				ifconfig_up(vlan_ifname);
 			}
@@ -547,13 +637,15 @@
 	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) {
+		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,
@@ -581,20 +673,27 @@
 					os_snprintf(vlan_ifname,
 						    sizeof(vlan_ifname),
 						    "vlan%d", vlan->vlan_id);
-				if (vlan->clean & DVLAN_CLEAN_VLAN_PORT)
-					br_delif(br_name, vlan_ifname);
-				ifconfig_down(vlan_ifname);
 
-				if (vlan->clean & DVLAN_CLEAN_VLAN)
+				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);
+				}
 			}
 
-			if ((vlan->clean & DVLAN_CLEAN_BR) &&
+			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 {
@@ -617,6 +716,7 @@
 	struct ifinfomsg *ifi;
 	int attrlen, nlmsg_len, rta_len;
 	struct rtattr *attr;
+	char ifname[IFNAMSIZ + 1];
 
 	if (len < sizeof(*ifi))
 		return;
@@ -631,29 +731,44 @@
 
 	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)) {
-		char ifname[IFNAMSIZ + 1];
-
 		if (attr->rta_type == IFLA_IFNAME) {
 			int n = attr->rta_len - rta_len;
 			if (n < 0)
 				break;
 
-			os_memset(ifname, 0, sizeof(ifname));
-
-			if ((size_t) n > sizeof(ifname))
-				n = sizeof(ifname);
+			if ((size_t) n >= sizeof(ifname))
+				n = sizeof(ifname) - 1;
 			os_memcpy(ifname, ((char *) attr) + rta_len, n);
 
-			if (del)
-				vlan_dellink(ifname, hapd);
-			else
-				vlan_newlink(ifname, hapd);
 		}
 
 		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);
 }
 
 
@@ -677,7 +792,7 @@
 	}
 
 	h = (struct nlmsghdr *) buf;
-	while (left >= (int) sizeof(*h)) {
+	while (NLMSG_OK(h, left)) {
 		int len, plen;
 
 		len = h->nlmsg_len;
@@ -698,9 +813,7 @@
 			break;
 		}
 
-		len = NLMSG_ALIGN(len);
-		left -= len;
-		h = (struct nlmsghdr *) ((char *) h + len);
+		h = NLMSG_NEXT(h, left);
 	}
 
 	if (left > 0) {
@@ -769,8 +882,7 @@
 #endif /* CONFIG_FULL_DYNAMIC_VLAN */
 
 
-int vlan_setup_encryption_dyn(struct hostapd_data *hapd,
-			      struct hostapd_ssid *mssid, const char *dyn_vlan)
+int vlan_setup_encryption_dyn(struct hostapd_data *hapd, const char *dyn_vlan)
 {
         int i;
 
@@ -780,10 +892,11 @@
 	/* 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 (mssid->wep.key[i] &&
+		if (hapd->conf->ssid.wep.key[i] &&
 		    hostapd_drv_set_key(dyn_vlan, hapd, WPA_ALG_WEP, NULL, i,
-					i == mssid->wep.idx, NULL, 0,
-					mssid->wep.key[i], mssid->wep.len[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");
@@ -891,7 +1004,7 @@
 				       struct hostapd_vlan *vlan,
 				       int vlan_id)
 {
-	struct hostapd_vlan *n;
+	struct hostapd_vlan *n = NULL;
 	char *ifname, *pos;
 
 	if (vlan == NULL || vlan_id <= 0 || vlan_id > MAX_VLAN_ID ||
@@ -904,28 +1017,24 @@
 	if (ifname == NULL)
 		return NULL;
 	pos = os_strchr(ifname, '#');
-	if (pos == NULL) {
-		os_free(ifname);
-		return NULL;
-	}
+	if (pos == NULL)
+		goto free_ifname;
 	*pos++ = '\0';
 
 	n = os_zalloc(sizeof(*n));
-	if (n == NULL) {
-		os_free(ifname);
-		return NULL;
-	}
+	if (n == NULL)
+		goto free_ifname;
 
 	n->vlan_id = vlan_id;
 	n->dynamic_vlan = 1;
 
 	os_snprintf(n->ifname, sizeof(n->ifname), "%s%d%s", ifname, vlan_id,
 		    pos);
-	os_free(ifname);
 
 	if (hostapd_vlan_if_add(hapd, n->ifname)) {
 		os_free(n);
-		return NULL;
+		n = NULL;
+		goto free_ifname;
 	}
 
 	n->next = hapd->conf->vlan;
@@ -935,6 +1044,8 @@
 	ifconfig_up(n->ifname);
 #endif /* CONFIG_FULL_DYNAMIC_VLAN */
 
+free_ifname:
+	os_free(ifname);
 	return n;
 }
 
@@ -946,7 +1057,8 @@
 	if (vlan_id <= 0 || vlan_id > MAX_VLAN_ID)
 		return 1;
 
-	wpa_printf(MSG_DEBUG, "VLAN: %s(vlan_id=%d)", __func__, vlan_id);
+	wpa_printf(MSG_DEBUG, "VLAN: %s(ifname=%s vlan_id=%d)",
+		   __func__, hapd->conf->iface, vlan_id);
 
 	vlan = hapd->conf->vlan;
 	while (vlan) {
@@ -960,8 +1072,12 @@
 	if (vlan == NULL)
 		return 1;
 
-	if (vlan->dynamic_vlan == 0)
+	if (vlan->dynamic_vlan == 0) {
 		hostapd_vlan_if_remove(hapd, vlan->ifname);
+#ifdef CONFIG_FULL_DYNAMIC_VLAN
+		vlan_dellink(vlan->ifname, hapd);
+#endif /* CONFIG_FULL_DYNAMIC_VLAN */
+	}
 
 	return 0;
 }
diff --git a/src/ap/vlan_init.h b/src/ap/vlan_init.h
index 781eaac..fc39443 100644
--- a/src/ap/vlan_init.h
+++ b/src/ap/vlan_init.h
@@ -18,7 +18,6 @@
 				       int vlan_id);
 int vlan_remove_dynamic(struct hostapd_data *hapd, int vlan_id);
 int vlan_setup_encryption_dyn(struct hostapd_data *hapd,
-			      struct hostapd_ssid *mssid,
 			      const char *dyn_vlan);
 #else /* CONFIG_NO_VLAN */
 static inline int vlan_init(struct hostapd_data *hapd)
@@ -43,7 +42,6 @@
 }
 
 static inline int vlan_setup_encryption_dyn(struct hostapd_data *hapd,
-					    struct hostapd_ssid *mssid,
 					    const char *dyn_vlan)
 {
 	return -1;
diff --git a/src/ap/vlan_util.c b/src/ap/vlan_util.c
index cc54051..d4e0efb 100644
--- a/src/ap/vlan_util.c
+++ b/src/ap/vlan_util.c
@@ -31,7 +31,7 @@
 */
 int vlan_add(const char *if_name, int vid, const char *vlan_if_name)
 {
-	int ret = -1;
+	int err, ret = -1;
 	struct nl_sock *handle = NULL;
 	struct nl_cache *cache = NULL;
 	struct rtnl_link *rlink = NULL;
@@ -58,14 +58,18 @@
 		goto vlan_add_error;
 	}
 
-	if (nl_connect(handle, NETLINK_ROUTE) < 0) {
-		wpa_printf(MSG_ERROR, "VLAN: failed to connect to netlink");
+	err = nl_connect(handle, NETLINK_ROUTE);
+	if (err < 0) {
+		wpa_printf(MSG_ERROR, "VLAN: failed to connect to netlink: %s",
+			   nl_geterror(err));
 		goto vlan_add_error;
 	}
 
-	if (rtnl_link_alloc_cache(handle, AF_UNSPEC, &cache) < 0) {
+	err = rtnl_link_alloc_cache(handle, AF_UNSPEC, &cache);
+	if (err < 0) {
 		cache = NULL;
-		wpa_printf(MSG_ERROR, "VLAN: failed to alloc cache");
+		wpa_printf(MSG_ERROR, "VLAN: failed to alloc cache: %s",
+			   nl_geterror(err));
 		goto vlan_add_error;
 	}
 
@@ -92,23 +96,29 @@
 		goto vlan_add_error;
 	}
 
-	if (rtnl_link_set_type(rlink, "vlan") < 0) {
-		wpa_printf(MSG_ERROR, "VLAN: failed to set link type");
+	err = rtnl_link_set_type(rlink, "vlan");
+	if (err < 0) {
+		wpa_printf(MSG_ERROR, "VLAN: failed to set link type: %s",
+			   nl_geterror(err));
 		goto vlan_add_error;
 	}
 
 	rtnl_link_set_link(rlink, if_idx);
 	rtnl_link_set_name(rlink, vlan_if_name);
 
-	if (rtnl_link_vlan_set_id(rlink, vid) < 0) {
-		wpa_printf(MSG_ERROR, "VLAN: failed to set link vlan id");
+	err = rtnl_link_vlan_set_id(rlink, vid);
+	if (err < 0) {
+		wpa_printf(MSG_ERROR, "VLAN: failed to set link vlan id: %s",
+			   nl_geterror(err));
 		goto vlan_add_error;
 	}
 
-	if (rtnl_link_add(handle, rlink, NLM_F_CREATE) < 0) {
+	err = rtnl_link_add(handle, rlink, NLM_F_CREATE);
+	if (err < 0) {
 		wpa_printf(MSG_ERROR, "VLAN: failed to create link %s for "
-			   "vlan %d on %s (%d)",
-			   vlan_if_name, vid, if_name, if_idx);
+			   "vlan %d on %s (%d): %s",
+			   vlan_if_name, vid, if_name, if_idx,
+			   nl_geterror(err));
 		goto vlan_add_error;
 	}
 
@@ -127,7 +137,7 @@
 
 int vlan_rem(const char *if_name)
 {
-	int ret = -1;
+	int err, ret = -1;
 	struct nl_sock *handle = NULL;
 	struct nl_cache *cache = NULL;
 	struct rtnl_link *rlink = NULL;
@@ -140,14 +150,18 @@
 		goto vlan_rem_error;
 	}
 
-	if (nl_connect(handle, NETLINK_ROUTE) < 0) {
-		wpa_printf(MSG_ERROR, "VLAN: failed to connect to netlink");
+	err = nl_connect(handle, NETLINK_ROUTE);
+	if (err < 0) {
+		wpa_printf(MSG_ERROR, "VLAN: failed to connect to netlink: %s",
+			   nl_geterror(err));
 		goto vlan_rem_error;
 	}
 
-	if (rtnl_link_alloc_cache(handle, AF_UNSPEC, &cache) < 0) {
+	err = rtnl_link_alloc_cache(handle, AF_UNSPEC, &cache);
+	if (err < 0) {
 		cache = NULL;
-		wpa_printf(MSG_ERROR, "VLAN: failed to alloc cache");
+		wpa_printf(MSG_ERROR, "VLAN: failed to alloc cache: %s",
+			   nl_geterror(err));
 		goto vlan_rem_error;
 	}
 
@@ -158,9 +172,10 @@
 		goto vlan_rem_error;
 	}
 
-	if (rtnl_link_delete(handle, rlink) < 0) {
-		wpa_printf(MSG_ERROR, "VLAN: failed to remove link %s",
-			   if_name);
+	err = rtnl_link_delete(handle, rlink);
+	if (err < 0) {
+		wpa_printf(MSG_ERROR, "VLAN: failed to remove link %s: %s",
+			   if_name, nl_geterror(err));
 		goto vlan_rem_error;
 	}
 
diff --git a/src/ap/wmm.c b/src/ap/wmm.c
index 6d4177c..314e244 100644
--- a/src/ap/wmm.c
+++ b/src/ap/wmm.c
@@ -274,6 +274,9 @@
 		return;
 	}
 
+	if (left < 0)
+		return; /* not a valid WMM Action frame */
+
 	/* extract the tspec info element */
 	if (ieee802_11_parse_elems(pos, left, &elems, 1) == ParseFailed) {
 		hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
diff --git a/src/ap/wnm_ap.c b/src/ap/wnm_ap.c
index cf25dbb..4c8bc10 100644
--- a/src/ap/wnm_ap.c
+++ b/src/ap/wnm_ap.c
@@ -1,6 +1,6 @@
 /*
  * hostapd - WNM
- * Copyright (c) 2011-2013, Qualcomm Atheros, Inc.
+ * Copyright (c) 2011-2014, Qualcomm Atheros, Inc.
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -11,6 +11,7 @@
 #include "utils/common.h"
 #include "utils/eloop.h"
 #include "common/ieee802_11_defs.h"
+#include "common/wpa_ctrl.h"
 #include "ap/hostapd.h"
 #include "ap/sta_info.h"
 #include "ap/ap_config.h"
@@ -358,7 +359,16 @@
 		}
 		wpa_printf(MSG_DEBUG, "WNM: Target BSSID: " MACSTR,
 			   MAC2STR(pos));
+		wpa_msg(hapd->msg_ctx, MSG_INFO, BSS_TM_RESP MACSTR
+			" status_code=%u bss_termination_delay=%u target_bssid="
+			MACSTR,
+			MAC2STR(addr), status_code, bss_termination_delay,
+			MAC2STR(pos));
 		pos += ETH_ALEN;
+	} else {
+		wpa_msg(hapd->msg_ctx, MSG_INFO, BSS_TM_RESP MACSTR
+			" status_code=%u bss_termination_delay=%u",
+			MAC2STR(addr), status_code, bss_termination_delay);
 	}
 
 	wpa_hexdump(MSG_DEBUG, "WNM: BSS Transition Candidate List Entries",
@@ -436,6 +446,34 @@
 }
 
 
+static void set_disassoc_timer(struct hostapd_data *hapd, struct sta_info *sta,
+			       int disassoc_timer)
+{
+	int timeout, beacon_int;
+
+	/*
+	 * Prevent STA from reconnecting using cached PMKSA to force
+	 * full authentication with the authentication server (which may
+	 * decide to reject the connection),
+	 */
+	wpa_auth_pmksa_remove(hapd->wpa_auth, sta->addr);
+
+	beacon_int = hapd->iconf->beacon_int;
+	if (beacon_int < 1)
+		beacon_int = 100; /* best guess */
+	/* Calculate timeout in ms based on beacon_int in TU */
+	timeout = disassoc_timer * beacon_int * 128 / 125;
+	wpa_printf(MSG_DEBUG, "Disassociation timer for " MACSTR
+		   " set to %d ms", MAC2STR(sta->addr), timeout);
+
+	sta->timeout_next = STA_DISASSOC_FROM_CLI;
+	eloop_cancel_timeout(ap_handle_timer, hapd, sta);
+	eloop_register_timeout(timeout / 1000,
+			       timeout % 1000 * 1000,
+			       ap_handle_timer, hapd, sta);
+}
+
+
 int wnm_send_ess_disassoc_imminent(struct hostapd_data *hapd,
 				   struct sta_info *sta, const char *url,
 				   int disassoc_timer)
@@ -477,30 +515,81 @@
 		return -1;
 	}
 
-	/* send disassociation frame after time-out */
 	if (disassoc_timer) {
-		int timeout, beacon_int;
+		/* send disassociation frame after time-out */
+		set_disassoc_timer(hapd, sta, disassoc_timer);
+	}
 
-		/*
-		 * Prevent STA from reconnecting using cached PMKSA to force
-		 * full authentication with the authentication server (which may
-		 * decide to reject the connection),
-		 */
-		wpa_auth_pmksa_remove(hapd->wpa_auth, sta->addr);
+	return 0;
+}
 
-		beacon_int = hapd->iconf->beacon_int;
-		if (beacon_int < 1)
-			beacon_int = 100; /* best guess */
-		/* Calculate timeout in ms based on beacon_int in TU */
-		timeout = disassoc_timer * beacon_int * 128 / 125;
-		wpa_printf(MSG_DEBUG, "Disassociation timer for " MACSTR
-			   " set to %d ms", MAC2STR(sta->addr), timeout);
 
-		sta->timeout_next = STA_DISASSOC_FROM_CLI;
-		eloop_cancel_timeout(ap_handle_timer, hapd, sta);
-		eloop_register_timeout(timeout / 1000,
-				       timeout % 1000 * 1000,
-				       ap_handle_timer, hapd, sta);
+int wnm_send_bss_tm_req(struct hostapd_data *hapd, struct sta_info *sta,
+			u8 req_mode, int disassoc_timer, u8 valid_int,
+			const u8 *bss_term_dur, const char *url,
+			const u8 *nei_rep, size_t nei_rep_len)
+{
+	u8 *buf, *pos;
+	struct ieee80211_mgmt *mgmt;
+	size_t url_len;
+
+	wpa_printf(MSG_DEBUG, "WNM: Send BSS Transition Management Request to "
+		   MACSTR " req_mode=0x%x disassoc_timer=%d valid_int=0x%x",
+		   MAC2STR(sta->addr), req_mode, disassoc_timer, valid_int);
+	buf = os_zalloc(1000 + nei_rep_len);
+	if (buf == NULL)
+		return -1;
+	mgmt = (struct ieee80211_mgmt *) buf;
+	mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
+					   WLAN_FC_STYPE_ACTION);
+	os_memcpy(mgmt->da, sta->addr, ETH_ALEN);
+	os_memcpy(mgmt->sa, hapd->own_addr, ETH_ALEN);
+	os_memcpy(mgmt->bssid, hapd->own_addr, ETH_ALEN);
+	mgmt->u.action.category = WLAN_ACTION_WNM;
+	mgmt->u.action.u.bss_tm_req.action = WNM_BSS_TRANS_MGMT_REQ;
+	mgmt->u.action.u.bss_tm_req.dialog_token = 1;
+	mgmt->u.action.u.bss_tm_req.req_mode = req_mode;
+	mgmt->u.action.u.bss_tm_req.disassoc_timer =
+		host_to_le16(disassoc_timer);
+	mgmt->u.action.u.bss_tm_req.validity_interval = valid_int;
+
+	pos = mgmt->u.action.u.bss_tm_req.variable;
+
+	if ((req_mode & WNM_BSS_TM_REQ_BSS_TERMINATION_INCLUDED) &&
+	    bss_term_dur) {
+		os_memcpy(pos, bss_term_dur, 12);
+		pos += 12;
+	}
+
+	if (url) {
+		/* Session Information URL */
+		url_len = os_strlen(url);
+		if (url_len > 255) {
+			os_free(buf);
+			return -1;
+		}
+
+		*pos++ = url_len;
+		os_memcpy(pos, url, url_len);
+		pos += url_len;
+	}
+
+	if (nei_rep) {
+		os_memcpy(pos, nei_rep, nei_rep_len);
+		pos += nei_rep_len;
+	}
+
+	if (hostapd_drv_send_mlme(hapd, buf, pos - buf, 0) < 0) {
+		wpa_printf(MSG_DEBUG,
+			   "Failed to send BSS Transition Management Request frame");
+		os_free(buf);
+		return -1;
+	}
+	os_free(buf);
+
+	if (disassoc_timer) {
+		/* send disassociation frame after time-out */
+		set_disassoc_timer(hapd, sta, disassoc_timer);
 	}
 
 	return 0;
diff --git a/src/ap/wnm_ap.h b/src/ap/wnm_ap.h
index eeaf5ec..7789307 100644
--- a/src/ap/wnm_ap.h
+++ b/src/ap/wnm_ap.h
@@ -1,6 +1,6 @@
 /*
  * IEEE 802.11v WNM related functions and structures
- * Copyright (c) 2011-2013, Qualcomm Atheros, Inc.
+ * Copyright (c) 2011-2014, Qualcomm Atheros, Inc.
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -18,5 +18,9 @@
 int wnm_send_ess_disassoc_imminent(struct hostapd_data *hapd,
 				   struct sta_info *sta, const char *url,
 				   int disassoc_timer);
+int wnm_send_bss_tm_req(struct hostapd_data *hapd, struct sta_info *sta,
+			u8 req_mode, int disassoc_timer, u8 valid_int,
+			const u8 *bss_term_dur, const char *url,
+			const u8 *nei_rep, size_t nei_rep_len);
 
 #endif /* WNM_AP_H */
diff --git a/src/ap/wpa_auth.c b/src/ap/wpa_auth.c
index 1a16b5c..f23a57a 100644
--- a/src/ap/wpa_auth.c
+++ b/src/ap/wpa_auth.c
@@ -1,6 +1,6 @@
 /*
  * IEEE 802.11 RSN / WPA Authenticator
- * 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.
@@ -33,7 +33,8 @@
 
 static void wpa_send_eapol_timeout(void *eloop_ctx, void *timeout_ctx);
 static int wpa_sm_step(struct wpa_state_machine *sm);
-static int wpa_verify_key_mic(struct wpa_ptk *PTK, u8 *data, size_t data_len);
+static int wpa_verify_key_mic(int akmp, struct wpa_ptk *PTK, u8 *data,
+			      size_t data_len);
 static void wpa_sm_call_step(void *eloop_ctx, void *timeout_ctx);
 static void wpa_group_sm_step(struct wpa_authenticator *wpa_auth,
 			      struct wpa_group *group);
@@ -42,6 +43,14 @@
 			  struct wpa_group *group);
 static int wpa_group_config_group_keys(struct wpa_authenticator *wpa_auth,
 				       struct wpa_group *group);
+static int wpa_derive_ptk(struct wpa_state_machine *sm, const u8 *snonce,
+			  const u8 *pmk, struct wpa_ptk *ptk);
+static void wpa_group_free(struct wpa_authenticator *wpa_auth,
+			   struct wpa_group *group);
+static void wpa_group_get(struct wpa_authenticator *wpa_auth,
+			  struct wpa_group *group);
+static void wpa_group_put(struct wpa_authenticator *wpa_auth,
+			  struct wpa_group *group);
 
 static const u32 dot11RSNAConfigGroupUpdateCount = 4;
 static const u32 dot11RSNAConfigPairwiseUpdateCount = 4;
@@ -64,6 +73,14 @@
 }
 
 
+static inline void wpa_auth_psk_failure_report(
+	struct wpa_authenticator *wpa_auth, const u8 *addr)
+{
+	if (wpa_auth->cb.psk_failure_report)
+		wpa_auth->cb.psk_failure_report(wpa_auth->cb.ctx, addr);
+}
+
+
 static inline void wpa_auth_set_eapol(struct wpa_authenticator *wpa_auth,
 				      const u8 *addr, wpa_eapol_variable var,
 				      int value)
@@ -135,6 +152,17 @@
 }
 
 
+#ifdef CONFIG_MESH
+static inline int wpa_auth_start_ampe(struct wpa_authenticator *wpa_auth,
+				      const u8 *addr)
+{
+	if (wpa_auth->cb.start_ampe == NULL)
+		return -1;
+	return wpa_auth->cb.start_ampe(wpa_auth->cb.ctx, addr);
+}
+#endif /* CONFIG_MESH */
+
+
 int wpa_auth_for_each_sta(struct wpa_authenticator *wpa_auth,
 			  int (*cb)(struct wpa_state_machine *sm, void *ctx),
 			  void *cb_ctx)
@@ -240,15 +268,22 @@
 static void wpa_rekey_gtk(void *eloop_ctx, void *timeout_ctx)
 {
 	struct wpa_authenticator *wpa_auth = eloop_ctx;
-	struct wpa_group *group;
+	struct wpa_group *group, *next;
 
 	wpa_auth_logger(wpa_auth, NULL, LOGGER_DEBUG, "rekeying GTK");
-	for (group = wpa_auth->group; group; group = group->next) {
+	group = wpa_auth->group;
+	while (group) {
+		wpa_group_get(wpa_auth, group);
+
 		group->GTKReKey = TRUE;
 		do {
 			group->changed = FALSE;
 			wpa_group_sm_step(wpa_auth, group);
 		} while (group->changed);
+
+		next = group->next;
+		wpa_group_put(wpa_auth, group);
+		group = next;
 	}
 
 	if (wpa_auth->conf.wpa_group_rekey) {
@@ -401,6 +436,7 @@
 						wpa_auth);
 	if (wpa_auth->pmksa == NULL) {
 		wpa_printf(MSG_ERROR, "PMKSA cache initialization failed.");
+		os_free(wpa_auth->group);
 		os_free(wpa_auth->wpa_ie);
 		os_free(wpa_auth);
 		return NULL;
@@ -410,6 +446,7 @@
 	wpa_auth->ft_pmk_cache = wpa_ft_pmk_cache_init();
 	if (wpa_auth->ft_pmk_cache == NULL) {
 		wpa_printf(MSG_ERROR, "FT PMK cache initialization failed.");
+		os_free(wpa_auth->group);
 		os_free(wpa_auth->wpa_ie);
 		pmksa_cache_auth_deinit(wpa_auth->pmksa);
 		os_free(wpa_auth);
@@ -549,6 +586,7 @@
 
 	sm->wpa_auth = wpa_auth;
 	sm->group = wpa_auth->group;
+	wpa_group_get(sm->wpa_auth, sm->group);
 
 	return sm;
 }
@@ -627,6 +665,7 @@
 #endif /* CONFIG_IEEE80211R */
 	os_free(sm->last_rx_eapol_key);
 	os_free(sm->wpa_ie);
+	wpa_group_put(sm->wpa_auth, sm->group);
 	os_free(sm);
 }
 
@@ -782,40 +821,96 @@
 }
 
 
+static int wpa_try_alt_snonce(struct wpa_state_machine *sm, u8 *data,
+			      size_t data_len)
+{
+	struct wpa_ptk PTK;
+	int ok = 0;
+	const u8 *pmk = NULL;
+
+	for (;;) {
+		if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt)) {
+			pmk = wpa_auth_get_psk(sm->wpa_auth, sm->addr,
+					       sm->p2p_dev_addr, pmk);
+			if (pmk == NULL)
+				break;
+		} else
+			pmk = sm->PMK;
+
+		wpa_derive_ptk(sm, sm->alt_SNonce, pmk, &PTK);
+
+		if (wpa_verify_key_mic(sm->wpa_key_mgmt, &PTK, data, data_len)
+		    == 0) {
+			ok = 1;
+			break;
+		}
+
+		if (!wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt))
+			break;
+	}
+
+	if (!ok) {
+		wpa_printf(MSG_DEBUG,
+			   "WPA: Earlier SNonce did not result in matching MIC");
+		return -1;
+	}
+
+	wpa_printf(MSG_DEBUG,
+		   "WPA: Earlier SNonce resulted in matching MIC");
+	sm->alt_snonce_valid = 0;
+	os_memcpy(sm->SNonce, sm->alt_SNonce, WPA_NONCE_LEN);
+	os_memcpy(&sm->PTK, &PTK, sizeof(PTK));
+	sm->PTK_valid = TRUE;
+
+	return 0;
+}
+
+
 void wpa_receive(struct wpa_authenticator *wpa_auth,
 		 struct wpa_state_machine *sm,
 		 u8 *data, size_t data_len)
 {
 	struct ieee802_1x_hdr *hdr;
 	struct wpa_eapol_key *key;
+	struct wpa_eapol_key_192 *key192;
 	u16 key_info, key_data_length;
 	enum { PAIRWISE_2, PAIRWISE_4, GROUP_2, REQUEST,
 	       SMK_M1, SMK_M3, SMK_ERROR } msg;
 	char *msgtxt;
 	struct wpa_eapol_ie_parse kde;
 	int ft;
-	const u8 *eapol_key_ie;
-	size_t eapol_key_ie_len;
+	const u8 *eapol_key_ie, *key_data;
+	size_t eapol_key_ie_len, keyhdrlen, mic_len;
 
 	if (wpa_auth == NULL || !wpa_auth->conf.wpa || sm == NULL)
 		return;
 
-	if (data_len < sizeof(*hdr) + sizeof(*key))
+	mic_len = wpa_mic_len(sm->wpa_key_mgmt);
+	keyhdrlen = mic_len == 24 ? sizeof(*key192) : sizeof(*key);
+
+	if (data_len < sizeof(*hdr) + keyhdrlen)
 		return;
 
 	hdr = (struct ieee802_1x_hdr *) data;
 	key = (struct wpa_eapol_key *) (hdr + 1);
+	key192 = (struct wpa_eapol_key_192 *) (hdr + 1);
 	key_info = WPA_GET_BE16(key->key_info);
-	key_data_length = WPA_GET_BE16(key->key_data_length);
+	if (mic_len == 24) {
+		key_data = (const u8 *) (key192 + 1);
+		key_data_length = WPA_GET_BE16(key192->key_data_length);
+	} else {
+		key_data = (const u8 *) (key + 1);
+		key_data_length = WPA_GET_BE16(key->key_data_length);
+	}
 	wpa_printf(MSG_DEBUG, "WPA: Received EAPOL-Key from " MACSTR
 		   " key_info=0x%x type=%u key_data_length=%u",
 		   MAC2STR(sm->addr), key_info, key->type, key_data_length);
-	if (key_data_length > data_len - sizeof(*hdr) - sizeof(*key)) {
+	if (key_data_length > data_len - sizeof(*hdr) - keyhdrlen) {
 		wpa_printf(MSG_INFO, "WPA: Invalid EAPOL-Key frame - "
 			   "key_data overflow (%d > %lu)",
 			   key_data_length,
 			   (unsigned long) (data_len - sizeof(*hdr) -
-					    sizeof(*key)));
+					    keyhdrlen));
 		return;
 	}
 
@@ -884,6 +979,7 @@
 		    sm->pairwise == WPA_CIPHER_GCMP) {
 			if (wpa_use_aes_cmac(sm) &&
 			    sm->wpa_key_mgmt != WPA_KEY_MGMT_OSEN &&
+			    !wpa_key_mgmt_suite_b(sm->wpa_key_mgmt) &&
 			    ver != WPA_KEY_INFO_TYPE_AES_128_CMAC) {
 				wpa_auth_logger(wpa_auth, sm->addr,
 						LOGGER_WARNING,
@@ -902,6 +998,13 @@
 				return;
 			}
 		}
+
+		if (wpa_key_mgmt_suite_b(sm->wpa_key_mgmt) &&
+		    ver != WPA_KEY_INFO_TYPE_AKM_DEFINED) {
+			wpa_auth_logger(wpa_auth, sm->addr, LOGGER_WARNING,
+					"did not use EAPOL-Key descriptor version 0 as required for AKM-defined cases");
+			return;
+		}
 	}
 
 	if (key_info & WPA_KEY_INFO_REQUEST) {
@@ -937,8 +1040,25 @@
 					 "based on retransmitted EAPOL-Key "
 					 "1/4");
 			sm->update_snonce = 1;
-			wpa_replay_counter_mark_invalid(sm->prev_key_replay,
-							key->replay_counter);
+			os_memcpy(sm->alt_SNonce, sm->SNonce, WPA_NONCE_LEN);
+			sm->alt_snonce_valid = TRUE;
+			os_memcpy(sm->alt_replay_counter,
+				  sm->key_replay[0].counter,
+				  WPA_REPLAY_COUNTER_LEN);
+			goto continue_processing;
+		}
+
+		if (msg == PAIRWISE_4 && sm->alt_snonce_valid &&
+		    sm->wpa_ptk_state == WPA_PTK_PTKINITNEGOTIATING &&
+		    os_memcmp(key->replay_counter, sm->alt_replay_counter,
+			      WPA_REPLAY_COUNTER_LEN) == 0) {
+			/*
+			 * Supplicant may still be using the old SNonce since
+			 * there was two EAPOL-Key 2/4 messages and they had
+			 * different SNonce values.
+			 */
+			wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_DEBUG,
+					 "Try to process received EAPOL-Key 4/4 based on old Replay Counter and SNonce from an earlier EAPOL-Key 1/4");
 			goto continue_processing;
 		}
 
@@ -997,8 +1117,7 @@
 			wpa_sta_disconnect(wpa_auth, sm->addr);
 			return;
 		}
-		if (wpa_parse_kde_ies((u8 *) (key + 1), key_data_length,
-				      &kde) < 0) {
+		if (wpa_parse_kde_ies(key_data, key_data_length, &kde) < 0) {
 			wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_INFO,
 					 "received EAPOL-Key msg 2/4 with "
 					 "invalid Key Data contents");
@@ -1123,7 +1242,10 @@
 
 	sm->MICVerified = FALSE;
 	if (sm->PTK_valid && !sm->update_snonce) {
-		if (wpa_verify_key_mic(&sm->PTK, data, data_len)) {
+		if (wpa_verify_key_mic(sm->wpa_key_mgmt, &sm->PTK, data,
+				       data_len) &&
+		    (msg != PAIRWISE_4 || !sm->alt_snonce_valid ||
+		     wpa_try_alt_snonce(sm, data, data_len))) {
 			wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO,
 					"received EAPOL-Key with invalid MIC");
 			return;
@@ -1152,7 +1274,7 @@
 		 */
 		if (msg == SMK_ERROR) {
 #ifdef CONFIG_PEERKEY
-			wpa_smk_error(wpa_auth, sm, key);
+			wpa_smk_error(wpa_auth, sm, key_data, key_data_length);
 #endif /* CONFIG_PEERKEY */
 			return;
 		} else if (key_info & WPA_KEY_INFO_ERROR) {
@@ -1167,11 +1289,12 @@
 			wpa_request_new_ptk(sm);
 #ifdef CONFIG_PEERKEY
 		} else if (msg == SMK_M1) {
-			wpa_smk_m1(wpa_auth, sm, key);
+			wpa_smk_m1(wpa_auth, sm, key, key_data,
+				   key_data_length);
 #endif /* CONFIG_PEERKEY */
 		} else if (key_data_length > 0 &&
-			   wpa_parse_kde_ies((const u8 *) (key + 1),
-					     key_data_length, &kde) == 0 &&
+			   wpa_parse_kde_ies(key_data, key_data_length,
+					     &kde) == 0 &&
 			   kde.mac_addr) {
 		} else {
 			wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO,
@@ -1209,7 +1332,7 @@
 
 #ifdef CONFIG_PEERKEY
 	if (msg == SMK_M3) {
-		wpa_smk_m3(wpa_auth, sm, key);
+		wpa_smk_m3(wpa_auth, sm, key, key_data, key_data_length);
 		return;
 	}
 #endif /* CONFIG_PEERKEY */
@@ -1284,18 +1407,24 @@
 {
 	struct ieee802_1x_hdr *hdr;
 	struct wpa_eapol_key *key;
-	size_t len;
+	struct wpa_eapol_key_192 *key192;
+	size_t len, mic_len, keyhdrlen;
 	int alg;
 	int key_data_len, pad_len = 0;
 	u8 *buf, *pos;
 	int version, pairwise;
 	int i;
+	u8 *key_data;
 
-	len = sizeof(struct ieee802_1x_hdr) + sizeof(struct wpa_eapol_key);
+	mic_len = wpa_mic_len(sm->wpa_key_mgmt);
+	keyhdrlen = mic_len == 24 ? sizeof(*key192) : sizeof(*key);
+
+	len = sizeof(struct ieee802_1x_hdr) + keyhdrlen;
 
 	if (force_version)
 		version = force_version;
-	else if (sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN)
+	else if (sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN ||
+		 wpa_key_mgmt_suite_b(sm->wpa_key_mgmt))
 		version = WPA_KEY_INFO_TYPE_AKM_DEFINED;
 	else if (wpa_use_aes_cmac(sm))
 		version = WPA_KEY_INFO_TYPE_AES_128_CMAC;
@@ -1320,6 +1449,7 @@
 
 	if ((version == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES ||
 	     sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN ||
+	     wpa_key_mgmt_suite_b(sm->wpa_key_mgmt) ||
 	     version == WPA_KEY_INFO_TYPE_AES_128_CMAC) && encr) {
 		pad_len = key_data_len % 8;
 		if (pad_len)
@@ -1336,6 +1466,8 @@
 	hdr->type = IEEE802_1X_TYPE_EAPOL_KEY;
 	hdr->length = host_to_be16(len  - sizeof(*hdr));
 	key = (struct wpa_eapol_key *) (hdr + 1);
+	key192 = (struct wpa_eapol_key_192 *) (hdr + 1);
+	key_data = ((u8 *) (hdr + 1)) + keyhdrlen;
 
 	key->type = sm->wpa == WPA_VERSION_WPA2 ?
 		EAPOL_KEY_TYPE_RSN : EAPOL_KEY_TYPE_WPA;
@@ -1361,6 +1493,8 @@
 	inc_byte_array(sm->key_replay[0].counter, WPA_REPLAY_COUNTER_LEN);
 	os_memcpy(key->replay_counter, sm->key_replay[0].counter,
 		  WPA_REPLAY_COUNTER_LEN);
+	wpa_hexdump(MSG_DEBUG, "WPA: Replay Counter",
+		    key->replay_counter, WPA_REPLAY_COUNTER_LEN);
 	sm->key_replay[0].valid = TRUE;
 
 	if (nonce)
@@ -1370,8 +1504,11 @@
 		os_memcpy(key->key_rsc, key_rsc, WPA_KEY_RSC_LEN);
 
 	if (kde && !encr) {
-		os_memcpy(key + 1, kde, kde_len);
-		WPA_PUT_BE16(key->key_data_length, kde_len);
+		os_memcpy(key_data, kde, kde_len);
+		if (mic_len == 24)
+			WPA_PUT_BE16(key192->key_data_length, kde_len);
+		else
+			WPA_PUT_BE16(key->key_data_length, kde_len);
 	} else if (encr && kde) {
 		buf = os_zalloc(key_data_len);
 		if (buf == NULL) {
@@ -1389,30 +1526,46 @@
 				buf, key_data_len);
 		if (version == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES ||
 		    sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN ||
+		    wpa_key_mgmt_suite_b(sm->wpa_key_mgmt) ||
 		    version == WPA_KEY_INFO_TYPE_AES_128_CMAC) {
-			if (aes_wrap(sm->PTK.kek, 16,
-				     (key_data_len - 8) / 8, buf,
-				     (u8 *) (key + 1))) {
+			if (aes_wrap(sm->PTK.kek, sm->PTK.kek_len,
+				     (key_data_len - 8) / 8, buf, key_data)) {
 				os_free(hdr);
 				os_free(buf);
 				return;
 			}
-			WPA_PUT_BE16(key->key_data_length, key_data_len);
-		} else {
+			if (mic_len == 24)
+				WPA_PUT_BE16(key192->key_data_length,
+					     key_data_len);
+			else
+				WPA_PUT_BE16(key->key_data_length,
+					     key_data_len);
+		} else if (sm->PTK.kek_len == 16) {
 			u8 ek[32];
 			os_memcpy(key->key_iv,
 				  sm->group->Counter + WPA_NONCE_LEN - 16, 16);
 			inc_byte_array(sm->group->Counter, WPA_NONCE_LEN);
 			os_memcpy(ek, key->key_iv, 16);
-			os_memcpy(ek + 16, sm->PTK.kek, 16);
-			os_memcpy(key + 1, buf, key_data_len);
-			rc4_skip(ek, 32, 256, (u8 *) (key + 1), key_data_len);
-			WPA_PUT_BE16(key->key_data_length, key_data_len);
+			os_memcpy(ek + 16, sm->PTK.kek, sm->PTK.kek_len);
+			os_memcpy(key_data, buf, key_data_len);
+			rc4_skip(ek, 32, 256, key_data, key_data_len);
+			if (mic_len == 24)
+				WPA_PUT_BE16(key192->key_data_length,
+					     key_data_len);
+			else
+				WPA_PUT_BE16(key->key_data_length,
+					     key_data_len);
+		} else {
+			os_free(hdr);
+			os_free(buf);
+			return;
 		}
 		os_free(buf);
 	}
 
 	if (key_info & WPA_KEY_INFO_MIC) {
+		u8 *key_mic;
+
 		if (!sm->PTK_valid) {
 			wpa_auth_logger(wpa_auth, sm->addr, LOGGER_DEBUG,
 					"PTK not valid when sending EAPOL-Key "
@@ -1420,8 +1573,11 @@
 			os_free(hdr);
 			return;
 		}
-		wpa_eapol_key_mic(sm->PTK.kck, version, (u8 *) hdr, len,
-				  key->key_mic);
+
+		key_mic = key192->key_mic; /* same offset for key and key192 */
+		wpa_eapol_key_mic(sm->PTK.kck, sm->PTK.kck_len,
+				  sm->wpa_key_mgmt, version,
+				  (u8 *) hdr, len, key_mic);
 #ifdef CONFIG_TESTING_OPTIONS
 		if (!pairwise &&
 		    wpa_auth->conf.corrupt_gtk_rekey_mic_probability > 0.0 &&
@@ -1429,7 +1585,7 @@
 		    wpa_auth->conf.corrupt_gtk_rekey_mic_probability) {
 			wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO,
 					"Corrupting group EAPOL-Key Key MIC");
-			key->key_mic[0]++;
+			key_mic[0]++;
 		}
 #endif /* CONFIG_TESTING_OPTIONS */
 	}
@@ -1473,27 +1629,32 @@
 }
 
 
-static int wpa_verify_key_mic(struct wpa_ptk *PTK, u8 *data, size_t data_len)
+static int wpa_verify_key_mic(int akmp, struct wpa_ptk *PTK, u8 *data,
+			      size_t data_len)
 {
 	struct ieee802_1x_hdr *hdr;
 	struct wpa_eapol_key *key;
+	struct wpa_eapol_key_192 *key192;
 	u16 key_info;
 	int ret = 0;
-	u8 mic[16];
+	u8 mic[WPA_EAPOL_KEY_MIC_MAX_LEN];
+	size_t mic_len = wpa_mic_len(akmp);
 
 	if (data_len < sizeof(*hdr) + sizeof(*key))
 		return -1;
 
 	hdr = (struct ieee802_1x_hdr *) data;
 	key = (struct wpa_eapol_key *) (hdr + 1);
+	key192 = (struct wpa_eapol_key_192 *) (hdr + 1);
 	key_info = WPA_GET_BE16(key->key_info);
-	os_memcpy(mic, key->key_mic, 16);
-	os_memset(key->key_mic, 0, 16);
-	if (wpa_eapol_key_mic(PTK->kck, key_info & WPA_KEY_INFO_TYPE_MASK,
-			      data, data_len, key->key_mic) ||
-	    os_memcmp_const(mic, key->key_mic, 16) != 0)
+	os_memcpy(mic, key192->key_mic, mic_len);
+	os_memset(key192->key_mic, 0, mic_len);
+	if (wpa_eapol_key_mic(PTK->kck, PTK->kck_len, akmp,
+			      key_info & WPA_KEY_INFO_TYPE_MASK,
+			      data, data_len, key192->key_mic) ||
+	    os_memcmp_const(mic, key192->key_mic, mic_len) != 0)
 		ret = -1;
-	os_memcpy(key->key_mic, mic, 16);
+	os_memcpy(key192->key_mic, mic, mic_len);
 	return ret;
 }
 
@@ -1520,6 +1681,14 @@
 
 	switch (event) {
 	case WPA_AUTH:
+#ifdef CONFIG_MESH
+		/* PTKs are derived through AMPE */
+		if (wpa_auth_start_ampe(sm->wpa_auth, sm->addr)) {
+			/* not mesh */
+			break;
+		}
+		return 0;
+#endif /* CONFIG_MESH */
 	case WPA_ASSOC:
 		break;
 	case WPA_DEAUTH:
@@ -1733,8 +1902,12 @@
 		}
 #endif /* CONFIG_IEEE80211R */
 	} else {
-		wpa_printf(MSG_DEBUG, "WPA: Could not get PMK");
+		wpa_printf(MSG_DEBUG, "WPA: Could not get PMK, get_msk: %p",
+			   sm->wpa_auth->cb.get_msk);
+		sm->Disconnect = TRUE;
+		return;
 	}
+	os_memset(msk, 0, sizeof(msk));
 
 	sm->req_replay_counter_used = 0;
 	/* IEEE 802.11i does not set keyRun to FALSE, but not doing this
@@ -1773,6 +1946,7 @@
 	SM_ENTRY_MA(WPA_PTK, PTKSTART, wpa_ptk);
 	sm->PTKRequest = FALSE;
 	sm->TimeoutEvt = FALSE;
+	sm->alt_snonce_valid = FALSE;
 
 	sm->TimeoutCtr++;
 	if (sm->TimeoutCtr > (int) dot11RSNAConfigPairwiseUpdateCount) {
@@ -1795,10 +1969,13 @@
 		pmkid[0] = WLAN_EID_VENDOR_SPECIFIC;
 		pmkid[1] = RSN_SELECTOR_LEN + PMKID_LEN;
 		RSN_SELECTOR_PUT(&pmkid[2], RSN_KEY_DATA_PMKID);
-		if (sm->pmksa)
+		if (sm->pmksa) {
 			os_memcpy(&pmkid[2 + RSN_SELECTOR_LEN],
 				  sm->pmksa->pmkid, PMKID_LEN);
-		else {
+		} else if (wpa_key_mgmt_suite_b(sm->wpa_key_mgmt)) {
+			/* No KCK available to derive PMKID */
+			pmkid = NULL;
+		} else {
 			/*
 			 * Calculate PMKID since no PMKSA cache entry was
 			 * available with pre-calculated PMKID.
@@ -1814,28 +1991,24 @@
 }
 
 
-static int wpa_derive_ptk(struct wpa_state_machine *sm, const u8 *pmk,
-			  struct wpa_ptk *ptk)
+static int wpa_derive_ptk(struct wpa_state_machine *sm, const u8 *snonce,
+			  const u8 *pmk, struct wpa_ptk *ptk)
 {
-	size_t ptk_len = wpa_cipher_key_len(sm->pairwise) + 32;
 #ifdef CONFIG_IEEE80211R
 	if (wpa_key_mgmt_ft(sm->wpa_key_mgmt))
-		return wpa_auth_derive_ptk_ft(sm, pmk, ptk, ptk_len);
+		return wpa_auth_derive_ptk_ft(sm, pmk, ptk);
 #endif /* CONFIG_IEEE80211R */
 
-	wpa_pmk_to_ptk(pmk, PMK_LEN, "Pairwise key expansion",
-		       sm->wpa_auth->addr, sm->addr, sm->ANonce, sm->SNonce,
-		       (u8 *) ptk, ptk_len,
-		       wpa_key_mgmt_sha256(sm->wpa_key_mgmt));
-
-	return 0;
+	return wpa_pmk_to_ptk(pmk, PMK_LEN, "Pairwise key expansion",
+			      sm->wpa_auth->addr, sm->addr, sm->ANonce, snonce,
+			      ptk, sm->wpa_key_mgmt, sm->pairwise);
 }
 
 
 SM_STATE(WPA_PTK, PTKCALCNEGOTIATING)
 {
 	struct wpa_ptk PTK;
-	int ok = 0;
+	int ok = 0, psk_found = 0;
 	const u8 *pmk = NULL;
 
 	SM_ENTRY_MA(WPA_PTK, PTKCALCNEGOTIATING, wpa_ptk);
@@ -1851,12 +2024,14 @@
 					       sm->p2p_dev_addr, pmk);
 			if (pmk == NULL)
 				break;
+			psk_found = 1;
 		} else
 			pmk = sm->PMK;
 
-		wpa_derive_ptk(sm, pmk, &PTK);
+		wpa_derive_ptk(sm, sm->SNonce, pmk, &PTK);
 
-		if (wpa_verify_key_mic(&PTK, sm->last_rx_eapol_key,
+		if (wpa_verify_key_mic(sm->wpa_key_mgmt, &PTK,
+				       sm->last_rx_eapol_key,
 				       sm->last_rx_eapol_key_len) == 0) {
 			ok = 1;
 			break;
@@ -1869,6 +2044,8 @@
 	if (!ok) {
 		wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG,
 				"invalid MIC in msg 2/4 of 4-Way Handshake");
+		if (psk_found)
+			wpa_auth_psk_failure_report(sm->wpa_auth, sm->addr);
 		return;
 	}
 
@@ -2009,8 +2186,10 @@
 	if (sm->wpa == WPA_VERSION_WPA &&
 	    (sm->wpa_auth->conf.wpa & WPA_PROTO_RSN) &&
 	    wpa_ie_len > wpa_ie[1] + 2 && wpa_ie[0] == WLAN_EID_RSN) {
-		/* WPA-only STA, remove RSN IE */
+		/* WPA-only STA, remove RSN IE and possible MDIE */
 		wpa_ie = wpa_ie + wpa_ie[1] + 2;
+		if (wpa_ie[0] == WLAN_EID_MOBILITY_DOMAIN)
+			wpa_ie = wpa_ie + wpa_ie[1] + 2;
 		wpa_ie_len = wpa_ie[1] + 2;
 	}
 	wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG,
@@ -2157,7 +2336,7 @@
 		enum wpa_alg alg = wpa_cipher_to_alg(sm->pairwise);
 		int klen = wpa_cipher_key_len(sm->pairwise);
 		if (wpa_auth_set_key(sm->wpa_auth, 0, alg, sm->addr, 0,
-				     sm->PTK.tk1, klen)) {
+				     sm->PTK.tk, klen)) {
 			wpa_sta_disconnect(sm->wpa_auth, sm->addr);
 			return;
 		}
@@ -2331,7 +2510,8 @@
 {
 	u8 rsc[WPA_KEY_RSC_LEN];
 	struct wpa_group *gsm = sm->group;
-	u8 *kde, *pos, hdr[2];
+	const u8 *kde;
+	u8 *kde_buf = NULL, *pos, hdr[2];
 	size_t kde_len;
 	u8 *gtk, dummy_gtk[32];
 
@@ -2367,28 +2547,29 @@
 	if (sm->wpa == WPA_VERSION_WPA2) {
 		kde_len = 2 + RSN_SELECTOR_LEN + 2 + gsm->GTK_len +
 			ieee80211w_kde_len(sm);
-		kde = os_malloc(kde_len);
-		if (kde == NULL)
+		kde_buf = os_malloc(kde_len);
+		if (kde_buf == NULL)
 			return;
 
-		pos = kde;
+		kde = pos = kde_buf;
 		hdr[0] = gsm->GN & 0x03;
 		hdr[1] = 0;
 		pos = wpa_add_kde(pos, RSN_KEY_DATA_GROUPKEY, hdr, 2,
 				  gtk, gsm->GTK_len);
 		pos = ieee80211w_kde_add(sm, pos);
+		kde_len = pos - kde;
 	} else {
 		kde = gtk;
-		pos = kde + gsm->GTK_len;
+		kde_len = gsm->GTK_len;
 	}
 
 	wpa_send_eapol(sm->wpa_auth, sm,
 		       WPA_KEY_INFO_SECURE | WPA_KEY_INFO_MIC |
 		       WPA_KEY_INFO_ACK |
 		       (!sm->Pair ? WPA_KEY_INFO_INSTALL : 0),
-		       rsc, gsm->GNonce, kde, pos - kde, gsm->GN, 1);
-	if (sm->wpa == WPA_VERSION_WPA2)
-		os_free(kde);
+		       rsc, gsm->GNonce, kde, kde_len, gsm->GN, 1);
+
+	os_free(kde_buf);
 }
 
 
@@ -2828,9 +3009,9 @@
 }
 
 
-static const char * wpa_bool_txt(int bool)
+static const char * wpa_bool_txt(int val)
 {
-	return bool ? "TRUE" : "FALSE";
+	return val ? "TRUE" : "FALSE";
 }
 
 
@@ -2859,7 +3040,7 @@
 			  wpa_bool_txt(preauth),
 			  wpa_bool_txt(wpa_auth->conf.wpa & WPA_PROTO_RSN),
 			  wpa_bool_txt(wpa_auth->conf.rsn_preauth));
-	if (ret < 0 || (size_t) ret >= buflen - len)
+	if (os_snprintf_error(buflen - len, ret))
 		return len;
 	len += ret;
 
@@ -2909,7 +3090,7 @@
 		RSN_SUITE_ARG(wpa_auth->dot11RSNAGroupCipherRequested),
 		wpa_auth->dot11RSNATKIPCounterMeasuresInvoked,
 		wpa_auth->dot11RSNA4WayHandshakeFailures);
-	if (ret < 0 || (size_t) ret >= buflen - len)
+	if (os_snprintf_error(buflen - len, ret))
 		return len;
 	len += ret;
 
@@ -2919,7 +3100,7 @@
 	/* Private MIB */
 	ret = os_snprintf(buf + len, buflen - len, "hostapdWPAGroupState=%d\n",
 			  wpa_auth->group->wpa_group_state);
-	if (ret < 0 || (size_t) ret >= buflen - len)
+	if (os_snprintf_error(buflen - len, ret))
 		return len;
 	len += ret;
 
@@ -2961,7 +3142,7 @@
 		RSN_SUITE_ARG(pairwise),
 		sm->dot11RSNAStatsTKIPLocalMICFailures,
 		sm->dot11RSNAStatsTKIPRemoteMICFailures);
-	if (ret < 0 || (size_t) ret >= buflen - len)
+	if (os_snprintf_error(buflen - len, ret))
 		return len;
 	len += ret;
 
@@ -2971,7 +3152,7 @@
 			  "hostapdWPAPTKGroupState=%d\n",
 			  sm->wpa_ptk_state,
 			  sm->wpa_ptk_group_state);
-	if (ret < 0 || (size_t) ret >= buflen - len)
+	if (os_snprintf_error(buflen - len, ret))
 		return len;
 	len += ret;
 
@@ -3055,6 +3236,7 @@
 		return -1;
 
 	if (pmksa_cache_auth_add(sm->wpa_auth->pmksa, pmk, PMK_LEN,
+				 sm->PTK.kck, sm->PTK.kck_len,
 				 sm->wpa_auth->addr, sm->addr, session_timeout,
 				 eapol, sm->wpa_key_mgmt))
 		return 0;
@@ -3071,7 +3253,9 @@
 	if (wpa_auth == NULL)
 		return -1;
 
-	if (pmksa_cache_auth_add(wpa_auth->pmksa, pmk, len, wpa_auth->addr,
+	if (pmksa_cache_auth_add(wpa_auth->pmksa, pmk, len,
+				 NULL, 0,
+				 wpa_auth->addr,
 				 sta_addr, session_timeout, eapol,
 				 WPA_KEY_MGMT_IEEE8021X))
 		return 0;
@@ -3080,6 +3264,22 @@
 }
 
 
+int wpa_auth_pmksa_add_sae(struct wpa_authenticator *wpa_auth, const u8 *addr,
+			   const u8 *pmk)
+{
+	if (wpa_auth->conf.disable_pmksa_caching)
+		return -1;
+
+	if (pmksa_cache_auth_add(wpa_auth->pmksa, pmk, PMK_LEN,
+				 NULL, 0,
+				 wpa_auth->addr, addr, 0, NULL,
+				 WPA_KEY_MGMT_SAE))
+		return 0;
+
+	return -1;
+}
+
+
 void wpa_auth_pmksa_remove(struct wpa_authenticator *wpa_auth,
 			   const u8 *sta_addr)
 {
@@ -3096,6 +3296,63 @@
 }
 
 
+/*
+ * Remove and free the group from wpa_authenticator. This is triggered by a
+ * callback to make sure nobody is currently iterating the group list while it
+ * gets modified.
+ */
+static void wpa_group_free(struct wpa_authenticator *wpa_auth,
+			   struct wpa_group *group)
+{
+	struct wpa_group *prev = wpa_auth->group;
+
+	wpa_printf(MSG_DEBUG, "WPA: Remove group state machine for VLAN-ID %d",
+		   group->vlan_id);
+
+	while (prev) {
+		if (prev->next == group) {
+			/* This never frees the special first group as needed */
+			prev->next = group->next;
+			os_free(group);
+			break;
+		}
+		prev = prev->next;
+	}
+
+}
+
+
+/* Increase the reference counter for group */
+static void wpa_group_get(struct wpa_authenticator *wpa_auth,
+			  struct wpa_group *group)
+{
+	/* Skip the special first group */
+	if (wpa_auth->group == group)
+		return;
+
+	group->references++;
+}
+
+
+/* Decrease the reference counter and maybe free the group */
+static void wpa_group_put(struct wpa_authenticator *wpa_auth,
+			  struct wpa_group *group)
+{
+	/* Skip the special first group */
+	if (wpa_auth->group == group)
+		return;
+
+	group->references--;
+	if (group->references)
+		return;
+	wpa_group_free(wpa_auth, group);
+}
+
+
+/*
+ * Add a group that has its references counter set to zero. Caller needs to
+ * call wpa_group_get() on the return value to mark the entry in use.
+ */
 static struct wpa_group *
 wpa_auth_add_group(struct wpa_authenticator *wpa_auth, int vlan_id)
 {
@@ -3146,7 +3403,10 @@
 	wpa_printf(MSG_DEBUG, "WPA: Moving STA " MACSTR " to use group state "
 		   "machine for VLAN ID %d", MAC2STR(sm->addr), vlan_id);
 
+	wpa_group_get(sm->wpa_auth, group);
+	wpa_group_put(sm->wpa_auth, sm->group);
 	sm->group = group;
+
 	return 0;
 }
 
@@ -3206,3 +3466,21 @@
 	return 0;
 }
 #endif /* CONFIG_P2P */
+
+
+int wpa_auth_radius_das_disconnect_pmksa(struct wpa_authenticator *wpa_auth,
+					 struct radius_das_attrs *attr)
+{
+	return pmksa_cache_auth_radius_das_disconnect(wpa_auth->pmksa, attr);
+}
+
+
+void wpa_auth_reconfig_group_keys(struct wpa_authenticator *wpa_auth)
+{
+	struct wpa_group *group;
+
+	if (!wpa_auth)
+		return;
+	for (group = wpa_auth->group; group; group = group->next)
+		wpa_group_config_group_keys(wpa_auth, group);
+}
diff --git a/src/ap/wpa_auth.h b/src/ap/wpa_auth.h
index 929a253..e747806 100644
--- a/src/ap/wpa_auth.h
+++ b/src/ap/wpa_auth.h
@@ -12,6 +12,7 @@
 #include "common/defs.h"
 #include "common/eapol_common.h"
 #include "common/wpa_common.h"
+#include "common/ieee802_11_defs.h"
 
 #ifdef _MSC_VER
 #pragma pack(push, 1)
@@ -146,8 +147,7 @@
 	int group_mgmt_cipher;
 #endif /* CONFIG_IEEE80211W */
 #ifdef CONFIG_IEEE80211R
-#define SSID_LEN 32
-	u8 ssid[SSID_LEN];
+	u8 ssid[SSID_MAX_LEN];
 	size_t ssid_len;
 	u8 mobility_domain[MOBILITY_DOMAIN_ID_LEN];
 	u8 r0_key_holder[FT_R0KH_ID_MAX_LEN];
@@ -189,6 +189,7 @@
 		       const char *txt);
 	void (*disconnect)(void *ctx, const u8 *addr, u16 reason);
 	int (*mic_failure_report)(void *ctx, const u8 *addr);
+	void (*psk_failure_report)(void *ctx, const u8 *addr);
 	void (*set_eapol)(void *ctx, const u8 *addr, wpa_eapol_variable var,
 			  int value);
 	int (*get_eapol)(void *ctx, const u8 *addr, wpa_eapol_variable var);
@@ -213,6 +214,9 @@
 	int (*add_tspec)(void *ctx, const u8 *sta_addr, u8 *tspec_ie,
 			 size_t tspec_ielen);
 #endif /* CONFIG_IEEE80211R */
+#ifdef CONFIG_MESH
+	int (*start_ampe)(void *ctx, const u8 *sta_addr);
+#endif /* CONFIG_MESH */
 };
 
 struct wpa_authenticator * wpa_init(const u8 *addr,
@@ -276,6 +280,8 @@
 			       const u8 *pmk, size_t len, const u8 *sta_addr,
 			       int session_timeout,
 			       struct eapol_state_machine *eapol);
+int wpa_auth_pmksa_add_sae(struct wpa_authenticator *wpa_auth, const u8 *addr,
+			   const u8 *pmk);
 void wpa_auth_pmksa_remove(struct wpa_authenticator *wpa_auth,
 			   const u8 *sta_addr);
 int wpa_auth_sta_set_vlan(struct wpa_state_machine *sm, int vlan_id);
@@ -310,4 +316,9 @@
 
 int wpa_auth_get_ip_addr(struct wpa_state_machine *sm, u8 *addr);
 
+struct radius_das_attrs;
+int wpa_auth_radius_das_disconnect_pmksa(struct wpa_authenticator *wpa_auth,
+					 struct radius_das_attrs *attr);
+void wpa_auth_reconfig_group_keys(struct wpa_authenticator *wpa_auth);
+
 #endif /* WPA_AUTH_H */
diff --git a/src/ap/wpa_auth_ft.c b/src/ap/wpa_auth_ft.c
index 781f15f..eeaffbf 100644
--- a/src/ap/wpa_auth_ft.c
+++ b/src/ap/wpa_auth_ft.c
@@ -1,6 +1,6 @@
 /*
  * hostapd - IEEE 802.11r - Fast BSS Transition
- * Copyright (c) 2004-2014, 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.
@@ -10,6 +10,7 @@
 
 #include "utils/common.h"
 #include "utils/eloop.h"
+#include "utils/list.h"
 #include "common/ieee802_11_defs.h"
 #include "common/ieee802_11_common.h"
 #include "crypto/aes_wrap.h"
@@ -361,7 +362,7 @@
 
 
 int wpa_auth_derive_ptk_ft(struct wpa_state_machine *sm, const u8 *pmk,
-			   struct wpa_ptk *ptk, size_t ptk_len)
+			   struct wpa_ptk *ptk)
 {
 	u8 pmk_r0[PMK_LEN], pmk_r0_name[WPA_PMK_NAME_LEN];
 	u8 pmk_r1[PMK_LEN];
@@ -373,7 +374,6 @@
 	const u8 *ssid = sm->wpa_auth->conf.ssid;
 	size_t ssid_len = sm->wpa_auth->conf.ssid_len;
 
-
 	if (sm->xxkey_len == 0) {
 		wpa_printf(MSG_DEBUG, "FT: XXKey not available for key "
 			   "derivation");
@@ -395,13 +395,9 @@
 	wpa_ft_store_pmk_r1(sm->wpa_auth, sm->addr, pmk_r1, sm->pmk_r1_name,
 			    sm->pairwise);
 
-	wpa_pmk_r1_to_ptk(pmk_r1, sm->SNonce, sm->ANonce, sm->addr,
-			  sm->wpa_auth->addr, sm->pmk_r1_name,
-			  (u8 *) ptk, ptk_len, ptk_name);
-	wpa_hexdump_key(MSG_DEBUG, "FT: PTK", (u8 *) ptk, ptk_len);
-	wpa_hexdump(MSG_DEBUG, "FT: PTKName", ptk_name, WPA_PMK_NAME_LEN);
-
-	return 0;
+	return wpa_pmk_r1_to_ptk(pmk_r1, sm->SNonce, sm->ANonce, sm->addr,
+				 sm->wpa_auth->addr, sm->pmk_r1_name,
+				 ptk, ptk_name, sm->wpa_key_mgmt, sm->pairwise);
 }
 
 
@@ -460,7 +456,8 @@
 	WPA_PUT_LE16(&subelem[2], gsm->GN & 0x03);
 	subelem[4] = gsm->GTK_len;
 	wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN, subelem + 5);
-	if (aes_wrap(sm->PTK.kek, 16, key_len / 8, key, subelem + 13)) {
+	if (aes_wrap(sm->PTK.kek, sm->PTK.kek_len, key_len / 8, key,
+		     subelem + 13)) {
 		os_free(subelem);
 		return NULL;
 	}
@@ -492,7 +489,7 @@
 	wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN_igtk, pos);
 	pos += 6;
 	*pos++ = WPA_IGTK_LEN;
-	if (aes_wrap(sm->PTK.kek, 16, WPA_IGTK_LEN / 8,
+	if (aes_wrap(sm->PTK.kek, sm->PTK.kek_len, WPA_IGTK_LEN / 8,
 		     gsm->IGTK[gsm->GN_igtk - 4], pos)) {
 		os_free(subelem);
 		return NULL;
@@ -537,10 +534,8 @@
 		return pos;
 	}
 
-#ifdef NEED_AP_MLME
-	if (parse.wmm_tspec && sm->wpa_auth->conf.ap_mlme) {
+	if (parse.wmm_tspec) {
 		struct wmm_tspec_element *tspec;
-		int res;
 
 		if (parse.wmm_tspec_len + 2 < (int) sizeof(*tspec)) {
 			wpa_printf(MSG_DEBUG, "FT: Too short WMM TSPEC IE "
@@ -558,7 +553,13 @@
 		}
 		tspec = (struct wmm_tspec_element *) pos;
 		os_memcpy(tspec, parse.wmm_tspec - 2, sizeof(*tspec));
-		res = wmm_process_tspec(tspec);
+	}
+
+#ifdef NEED_AP_MLME
+	if (parse.wmm_tspec && sm->wpa_auth->conf.ap_mlme) {
+		int res;
+
+		res = wmm_process_tspec((struct wmm_tspec_element *) pos);
 		wpa_printf(MSG_DEBUG, "FT: ADDTS processing result: %d", res);
 		if (res == WMM_ADDTS_STATUS_INVALID_PARAMETERS)
 			rdie->status_code =
@@ -569,20 +570,17 @@
 		else {
 			/* TSPEC accepted; include updated TSPEC in response */
 			rdie->descr_count = 1;
-			pos += sizeof(*tspec);
+			pos += sizeof(struct wmm_tspec_element);
 		}
 		return pos;
 	}
 #endif /* NEED_AP_MLME */
 
 	if (parse.wmm_tspec && !sm->wpa_auth->conf.ap_mlme) {
-		struct wmm_tspec_element *tspec;
 		int res;
 
-		tspec = (struct wmm_tspec_element *) pos;
-		os_memcpy(tspec, parse.wmm_tspec - 2, sizeof(*tspec));
 		res = wpa_ft_add_tspec(sm->wpa_auth, sm->addr, pos,
-				       sizeof(*tspec));
+				       sizeof(struct wmm_tspec_element));
 		if (res >= 0) {
 			if (res)
 				rdie->status_code = host_to_le16(res);
@@ -590,7 +588,7 @@
 				/* TSPEC accepted; include updated TSPEC in
 				 * response */
 				rdie->descr_count = 1;
-				pos += sizeof(*tspec);
+				pos += sizeof(struct wmm_tspec_element);
 			}
 			return pos;
 		}
@@ -744,7 +742,8 @@
 		ric_start = NULL;
 
 	if (auth_alg == WLAN_AUTH_FT &&
-	    wpa_ft_mic(sm->PTK.kck, sm->addr, sm->wpa_auth->addr, 6,
+	    wpa_ft_mic(sm->PTK.kck, sm->PTK.kck_len, sm->addr,
+		       sm->wpa_auth->addr, 6,
 		       mdie, mdie_len, ftie, ftie_len,
 		       rsnie, rsnie_len,
 		       ric_start, ric_start ? pos - ric_start : 0,
@@ -788,7 +787,7 @@
 	 * optimized by adding the STA entry earlier.
 	 */
 	if (wpa_auth_set_key(sm->wpa_auth, 0, alg, sm->addr, 0,
-			     sm->PTK.tk1, klen))
+			     sm->PTK.tk, klen))
 		return;
 
 	/* FIX: MLME-SetProtection.Request(TA, Tx_Rx) */
@@ -806,7 +805,7 @@
 	u8 ptk_name[WPA_PMK_NAME_LEN];
 	struct wpa_auth_config *conf;
 	struct wpa_ft_ies parse;
-	size_t buflen, ptk_len;
+	size_t buflen;
 	int ret;
 	u8 *pos, *end;
 	int pairwise;
@@ -891,13 +890,11 @@
 	wpa_hexdump(MSG_DEBUG, "FT: Generated ANonce",
 		    sm->ANonce, WPA_NONCE_LEN);
 
-	ptk_len = pairwise == WPA_CIPHER_TKIP ? 64 : 48;
-	wpa_pmk_r1_to_ptk(pmk_r1, sm->SNonce, sm->ANonce, sm->addr,
-			  sm->wpa_auth->addr, pmk_r1_name,
-			  (u8 *) &sm->PTK, ptk_len, ptk_name);
-	wpa_hexdump_key(MSG_DEBUG, "FT: PTK",
-			(u8 *) &sm->PTK, ptk_len);
-	wpa_hexdump(MSG_DEBUG, "FT: PTKName", ptk_name, WPA_PMK_NAME_LEN);
+	if (wpa_pmk_r1_to_ptk(pmk_r1, sm->SNonce, sm->ANonce, sm->addr,
+			      sm->wpa_auth->addr, pmk_r1_name,
+			      &sm->PTK, ptk_name, sm->wpa_key_mgmt,
+			      pairwise) < 0)
+		return WLAN_STATUS_UNSPECIFIED_FAILURE;
 
 	sm->pairwise = pairwise;
 	sm->PTK_valid = TRUE;
@@ -992,7 +989,8 @@
 	struct wpa_ft_ies parse;
 	struct rsn_mdie *mdie;
 	struct rsn_ftie *ftie;
-	u8 mic[16];
+	u8 mic[WPA_EAPOL_KEY_MIC_MAX_LEN];
+	size_t mic_len = 16;
 	unsigned int count;
 
 	if (sm == NULL)
@@ -1107,7 +1105,8 @@
 		return -1;
 	}
 
-	if (wpa_ft_mic(sm->PTK.kck, sm->addr, sm->wpa_auth->addr, 5,
+	if (wpa_ft_mic(sm->PTK.kck, sm->PTK.kck_len, sm->addr,
+		       sm->wpa_auth->addr, 5,
 		       parse.mdie - 2, parse.mdie_len + 2,
 		       parse.ftie - 2, parse.ftie_len + 2,
 		       parse.rsn - 2, parse.rsn_len + 2,
@@ -1117,12 +1116,13 @@
 		return WLAN_STATUS_UNSPECIFIED_FAILURE;
 	}
 
-	if (os_memcmp_const(mic, ftie->mic, 16) != 0) {
+	if (os_memcmp_const(mic, ftie->mic, mic_len) != 0) {
 		wpa_printf(MSG_DEBUG, "FT: Invalid MIC in FTIE");
 		wpa_printf(MSG_DEBUG, "FT: addr=" MACSTR " auth_addr=" MACSTR,
 			   MAC2STR(sm->addr), MAC2STR(sm->wpa_auth->addr));
-		wpa_hexdump(MSG_MSGDUMP, "FT: Received MIC", ftie->mic, 16);
-		wpa_hexdump(MSG_MSGDUMP, "FT: Calculated MIC", mic, 16);
+		wpa_hexdump(MSG_MSGDUMP, "FT: Received MIC",
+			    ftie->mic, mic_len);
+		wpa_hexdump(MSG_MSGDUMP, "FT: Calculated MIC", mic, mic_len);
 		wpa_hexdump(MSG_MSGDUMP, "FT: MDIE",
 			    parse.mdie - 2, parse.mdie_len + 2);
 		wpa_hexdump(MSG_MSGDUMP, "FT: FTIE",
@@ -1310,7 +1310,9 @@
 			      const u8 *src_addr,
 			      const u8 *data, size_t data_len)
 {
-	struct ft_r0kh_r1kh_pull_frame *frame, f;
+	struct ft_r0kh_r1kh_pull_frame f;
+	const u8 *crypt;
+	u8 *plain;
 	struct ft_remote_r1kh *r1kh;
 	struct ft_r0kh_r1kh_resp_frame resp, r;
 	u8 pmk_r0[PMK_LEN];
@@ -1318,7 +1320,7 @@
 
 	wpa_printf(MSG_DEBUG, "FT: Received PMK-R1 pull");
 
-	if (data_len < sizeof(*frame))
+	if (data_len < sizeof(f))
 		return -1;
 
 	r1kh = wpa_auth->conf.r1kh_list;
@@ -1334,12 +1336,14 @@
 		return -1;
 	}
 
-	frame = (struct ft_r0kh_r1kh_pull_frame *) data;
+	crypt = data + offsetof(struct ft_r0kh_r1kh_pull_frame, nonce);
+	os_memset(&f, 0, sizeof(f));
+	plain = ((u8 *) &f) + offsetof(struct ft_r0kh_r1kh_pull_frame, nonce);
 	/* aes_unwrap() does not support inplace decryption, so use a temporary
 	 * buffer for the data. */
 	if (aes_unwrap(r1kh->key, sizeof(r1kh->key),
 		       (FT_R0KH_R1KH_PULL_DATA_LEN + 7) / 8,
-		       frame->nonce, f.nonce) < 0) {
+		       crypt, plain) < 0) {
 		wpa_printf(MSG_DEBUG, "FT: Failed to decrypt PMK-R1 pull "
 			   "request from " MACSTR, MAC2STR(src_addr));
 		return -1;
@@ -1442,13 +1446,15 @@
 			      const u8 *src_addr,
 			      const u8 *data, size_t data_len)
 {
-	struct ft_r0kh_r1kh_resp_frame *frame, f;
+	struct ft_r0kh_r1kh_resp_frame f;
+	const u8 *crypt;
+	u8 *plain;
 	struct ft_remote_r0kh *r0kh;
 	int pairwise, res;
 
 	wpa_printf(MSG_DEBUG, "FT: Received PMK-R1 pull response");
 
-	if (data_len < sizeof(*frame))
+	if (data_len < sizeof(f))
 		return -1;
 
 	r0kh = wpa_auth->conf.r0kh_list;
@@ -1464,12 +1470,14 @@
 		return -1;
 	}
 
-	frame = (struct ft_r0kh_r1kh_resp_frame *) data;
+	crypt = data + offsetof(struct ft_r0kh_r1kh_resp_frame, nonce);
+	os_memset(&f, 0, sizeof(f));
+	plain = ((u8 *) &f) + offsetof(struct ft_r0kh_r1kh_resp_frame, nonce);
 	/* aes_unwrap() does not support inplace decryption, so use a temporary
 	 * buffer for the data. */
 	if (aes_unwrap(r0kh->key, sizeof(r0kh->key),
 		       (FT_R0KH_R1KH_RESP_DATA_LEN + 7) / 8,
-		       frame->nonce, f.nonce) < 0) {
+		       crypt, plain) < 0) {
 		wpa_printf(MSG_DEBUG, "FT: Failed to decrypt PMK-R1 pull "
 			   "response from " MACSTR, MAC2STR(src_addr));
 		return -1;
@@ -1507,7 +1515,9 @@
 			      const u8 *src_addr,
 			      const u8 *data, size_t data_len)
 {
-	struct ft_r0kh_r1kh_push_frame *frame, f;
+	struct ft_r0kh_r1kh_push_frame f;
+	const u8 *crypt;
+	u8 *plain;
 	struct ft_remote_r0kh *r0kh;
 	struct os_time now;
 	os_time_t tsend;
@@ -1515,7 +1525,7 @@
 
 	wpa_printf(MSG_DEBUG, "FT: Received PMK-R1 push");
 
-	if (data_len < sizeof(*frame))
+	if (data_len < sizeof(f))
 		return -1;
 
 	r0kh = wpa_auth->conf.r0kh_list;
@@ -1531,12 +1541,15 @@
 		return -1;
 	}
 
-	frame = (struct ft_r0kh_r1kh_push_frame *) data;
+	crypt = data + offsetof(struct ft_r0kh_r1kh_push_frame, timestamp);
+	os_memset(&f, 0, sizeof(f));
+	plain = ((u8 *) &f) + offsetof(struct ft_r0kh_r1kh_push_frame,
+				       timestamp);
 	/* aes_unwrap() does not support inplace decryption, so use a temporary
 	 * buffer for the data. */
 	if (aes_unwrap(r0kh->key, sizeof(r0kh->key),
 		       (FT_R0KH_R1KH_PUSH_DATA_LEN + 7) / 8,
-		       frame->timestamp, f.timestamp) < 0) {
+		       crypt, plain) < 0) {
 		wpa_printf(MSG_DEBUG, "FT: Failed to decrypt PMK-R1 push from "
 			   MACSTR, MAC2STR(src_addr));
 		return -1;
@@ -1710,6 +1723,8 @@
 {
 	struct ft_r0kh_r1kh_push_frame frame, f;
 	struct os_time now;
+	const u8 *plain;
+	u8 *crypt;
 
 	os_memset(&frame, 0, sizeof(frame));
 	frame.frame_type = RSN_REMOTE_FRAME_TYPE_FT_RRB;
@@ -1732,9 +1747,13 @@
 	WPA_PUT_LE32(f.timestamp, now.sec);
 	f.pairwise = host_to_le16(pairwise);
 	os_memset(f.pad, 0, sizeof(f.pad));
+	plain = ((const u8 *) &f) + offsetof(struct ft_r0kh_r1kh_push_frame,
+					     timestamp);
+	crypt = ((u8 *) &frame) + offsetof(struct ft_r0kh_r1kh_push_frame,
+					   timestamp);
 	if (aes_wrap(r1kh->key, sizeof(r1kh->key),
 		     (FT_R0KH_R1KH_PUSH_DATA_LEN + 7) / 8,
-		     f.timestamp, frame.timestamp) < 0)
+		     plain, crypt) < 0)
 		return;
 
 	wpa_ft_rrb_send(wpa_auth, r1kh->addr, (u8 *) &frame, sizeof(frame));
diff --git a/src/ap/wpa_auth_glue.c b/src/ap/wpa_auth_glue.c
index 6ee9a4f..7cd0b6c 100644
--- a/src/ap/wpa_auth_glue.c
+++ b/src/ap/wpa_auth_glue.c
@@ -11,6 +11,7 @@
 #include "utils/common.h"
 #include "common/ieee802_11_defs.h"
 #include "common/sae.h"
+#include "common/wpa_ctrl.h"
 #include "eapol_auth/eapol_auth_sm.h"
 #include "eapol_auth/eapol_auth_sm_i.h"
 #include "eap_server/eap.h"
@@ -53,8 +54,8 @@
 #endif /* CONFIG_IEEE80211W */
 #ifdef CONFIG_IEEE80211R
 	wconf->ssid_len = conf->ssid.ssid_len;
-	if (wconf->ssid_len > SSID_LEN)
-		wconf->ssid_len = SSID_LEN;
+	if (wconf->ssid_len > SSID_MAX_LEN)
+		wconf->ssid_len = SSID_MAX_LEN;
 	os_memcpy(wconf->ssid, conf->ssid.ssid, wconf->ssid_len);
 	os_memcpy(wconf->mobility_domain, conf->mobility_domain,
 		  MOBILITY_DOMAIN_ID_LEN);
@@ -144,6 +145,14 @@
 }
 
 
+static void hostapd_wpa_auth_psk_failure_report(void *ctx, const u8 *addr)
+{
+	struct hostapd_data *hapd = ctx;
+	wpa_msg(hapd->msg_ctx, MSG_INFO, AP_STA_POSSIBLE_PSK_MISMATCH MACSTR,
+		MAC2STR(addr));
+}
+
+
 static void hostapd_wpa_auth_set_eapol(void *ctx, const u8 *addr,
 				       wpa_eapol_variable var, int value)
 {
@@ -249,12 +258,17 @@
 	struct sta_info *sta;
 
 	sta = ap_get_sta(hapd, addr);
-	if (sta == NULL)
+	if (sta == NULL) {
+		wpa_printf(MSG_DEBUG, "AUTH_GET_MSK: Cannot find STA");
 		return -1;
+	}
 
 	key = ieee802_1x_get_key(sta->eapol_sm, &keylen);
-	if (key == NULL)
+	if (key == NULL) {
+		wpa_printf(MSG_DEBUG, "AUTH_GET_MSK: Key is null, eapol_sm: %p",
+			   sta->eapol_sm);
 		return -1;
+	}
 
 	if (keylen > *len)
 		keylen = *len;
@@ -299,6 +313,21 @@
 	struct sta_info *sta;
 	u32 flags = 0;
 
+#ifdef CONFIG_TESTING_OPTIONS
+	if (hapd->ext_eapol_frame_io) {
+		size_t hex_len = 2 * data_len + 1;
+		char *hex = os_malloc(hex_len);
+
+		if (hex == NULL)
+			return -1;
+		wpa_snprintf_hex(hex, hex_len, data, data_len);
+		wpa_msg(hapd->msg_ctx, MSG_INFO, "EAPOL-TX " MACSTR " %s",
+			MAC2STR(addr), hex);
+		os_free(hex);
+		return 0;
+	}
+#endif /* CONFIG_TESTING_OPTIONS */
+
 	sta = ap_get_sta(hapd, addr);
 	if (sta)
 		flags = hostapd_sta_flags_to_drv(sta->flags);
@@ -404,6 +433,21 @@
 	struct l2_ethhdr *buf;
 	int ret;
 
+#ifdef CONFIG_TESTING_OPTIONS
+	if (hapd->ext_eapol_frame_io && proto == ETH_P_EAPOL) {
+		size_t hex_len = 2 * data_len + 1;
+		char *hex = os_malloc(hex_len);
+
+		if (hex == NULL)
+			return -1;
+		wpa_snprintf_hex(hex, hex_len, data, data_len);
+		wpa_msg(hapd->msg_ctx, MSG_INFO, "EAPOL-TX " MACSTR " %s",
+			MAC2STR(dst), hex);
+		os_free(hex);
+		return 0;
+	}
+#endif /* CONFIG_TESTING_OPTIONS */
+
 #ifdef CONFIG_IEEE80211R
 	if (proto == ETH_P_RRB && hapd->iface->interfaces &&
 	    hapd->iface->interfaces->for_each_interface) {
@@ -544,6 +588,7 @@
 	cb.logger = hostapd_wpa_auth_logger;
 	cb.disconnect = hostapd_wpa_auth_disconnect;
 	cb.mic_failure_report = hostapd_wpa_auth_mic_failure_report;
+	cb.psk_failure_report = hostapd_wpa_auth_psk_failure_report;
 	cb.set_eapol = hostapd_wpa_auth_set_eapol;
 	cb.get_eapol = hostapd_wpa_auth_get_eapol;
 	cb.get_psk = hostapd_wpa_auth_get_psk;
diff --git a/src/ap/wpa_auth_i.h b/src/ap/wpa_auth_i.h
index 6960ff3..57b098f 100644
--- a/src/ap/wpa_auth_i.h
+++ b/src/ap/wpa_auth_i.h
@@ -1,6 +1,6 @@
 /*
  * hostapd - IEEE 802.11i-2004 / WPA Authenticator: Internal definitions
- * 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.
@@ -58,6 +58,8 @@
 	Boolean GUpdateStationKeys;
 	u8 ANonce[WPA_NONCE_LEN];
 	u8 SNonce[WPA_NONCE_LEN];
+	u8 alt_SNonce[WPA_NONCE_LEN];
+	u8 alt_replay_counter[WPA_REPLAY_COUNTER_LEN];
 	u8 PMK[PMK_LEN];
 	struct wpa_ptk PTK;
 	Boolean PTK_valid;
@@ -84,6 +86,7 @@
 	unsigned int mgmt_frame_prot:1;
 	unsigned int rx_eapol_key_secure:1;
 	unsigned int update_snonce:1;
+	unsigned int alt_snonce_valid:1;
 #ifdef CONFIG_IEEE80211R
 	unsigned int ft_completed:1;
 	unsigned int pmk_r1_name_valid:1;
@@ -166,6 +169,8 @@
 	u8 IGTK[2][WPA_IGTK_MAX_LEN];
 	int GN_igtk, GM_igtk;
 #endif /* CONFIG_IEEE80211W */
+	/* Number of references except those in struct wpa_group->next */
+	unsigned int references;
 };
 
 
@@ -227,11 +232,14 @@
 int wpa_stsl_remove(struct wpa_authenticator *wpa_auth,
 		    struct wpa_stsl_negotiation *neg);
 void wpa_smk_error(struct wpa_authenticator *wpa_auth,
-		   struct wpa_state_machine *sm, struct wpa_eapol_key *key);
+		   struct wpa_state_machine *sm,
+		   const u8 *key_data, size_t key_data_len);
 void wpa_smk_m1(struct wpa_authenticator *wpa_auth,
-		struct wpa_state_machine *sm, struct wpa_eapol_key *key);
+		struct wpa_state_machine *sm, struct wpa_eapol_key *key,
+		const u8 *key_data, size_t key_data_len);
 void wpa_smk_m3(struct wpa_authenticator *wpa_auth,
-		struct wpa_state_machine *sm, struct wpa_eapol_key *key);
+		struct wpa_state_machine *sm, struct wpa_eapol_key *key,
+		const u8 *key_data, size_t key_data_len);
 #endif /* CONFIG_PEERKEY */
 
 #ifdef CONFIG_IEEE80211R
@@ -242,7 +250,7 @@
 		   u8 *buf, size_t len, const u8 *subelem,
 		   size_t subelem_len);
 int wpa_auth_derive_ptk_ft(struct wpa_state_machine *sm, const u8 *pmk,
-			   struct wpa_ptk *ptk, size_t ptk_len);
+			   struct wpa_ptk *ptk);
 struct wpa_ft_pmk_cache * wpa_ft_pmk_cache_init(void);
 void wpa_ft_pmk_cache_deinit(struct wpa_ft_pmk_cache *cache);
 void wpa_ft_install_ptk(struct wpa_state_machine *sm);
diff --git a/src/ap/wpa_auth_ie.c b/src/ap/wpa_auth_ie.c
index 1e4defc..f287297 100644
--- a/src/ap/wpa_auth_ie.c
+++ b/src/ap/wpa_auth_ie.c
@@ -1,6 +1,6 @@
 /*
  * hostapd - WPA/RSN IE and KDE definitions
- * 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.
@@ -200,6 +200,16 @@
 		num_suites++;
 	}
 #endif /* CONFIG_SAE */
+	if (conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B) {
+		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_802_1X_SUITE_B);
+		pos += RSN_SELECTOR_LEN;
+		num_suites++;
+	}
+	if (conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B_192) {
+		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_802_1X_SUITE_B_192);
+		pos += RSN_SELECTOR_LEN;
+		num_suites++;
+	}
 
 #ifdef CONFIG_RSN_TESTING
 	if (rsn_testing) {
@@ -477,6 +487,10 @@
 		selector = RSN_AUTH_KEY_MGMT_UNSPEC_802_1X;
 		if (0) {
 		}
+		else if (data.key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B_192)
+			selector = RSN_AUTH_KEY_MGMT_802_1X_SUITE_B_192;
+		else if (data.key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B)
+			selector = RSN_AUTH_KEY_MGMT_802_1X_SUITE_B;
 #ifdef CONFIG_IEEE80211R
 		else if (data.key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X)
 			selector = RSN_AUTH_KEY_MGMT_FT_802_1X;
@@ -555,6 +569,10 @@
 	}
 	if (0) {
 	}
+	else if (key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B_192)
+		sm->wpa_key_mgmt = WPA_KEY_MGMT_IEEE8021X_SUITE_B_192;
+	else if (key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B)
+		sm->wpa_key_mgmt = WPA_KEY_MGMT_IEEE8021X_SUITE_B;
 #ifdef CONFIG_IEEE80211R
 	else if (key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X)
 		sm->wpa_key_mgmt = WPA_KEY_MGMT_FT_IEEE8021X;
diff --git a/src/ap/wps_hostapd.c b/src/ap/wps_hostapd.c
index 6f16f50..caed01e 100644
--- a/src/ap/wps_hostapd.c
+++ b/src/ap/wps_hostapd.c
@@ -185,7 +185,7 @@
 			  dev->model_number, dev->serial_number,
 			  wps_dev_type_bin2str(dev->pri_dev_type, devtype,
 					       sizeof(devtype)));
-	if (len > 0 && len < (int) sizeof(txt))
+	if (!os_snprintf_error(sizeof(txt), len))
 		wpa_msg(hapd->msg_ctx, MSG_INFO, "%s", txt);
 
 	if (hapd->conf->wps_pin_requests) {
@@ -324,7 +324,7 @@
 	wpa_printf(MSG_DEBUG, "WPS: Updating in-memory configuration");
 
 	bss->wps_state = 2;
-	if (cred->ssid_len <= HOSTAPD_MAX_SSID_LEN) {
+	if (cred->ssid_len <= SSID_MAX_LEN) {
 		os_memcpy(bss->ssid.ssid, cred->ssid, cred->ssid_len);
 		bss->ssid.ssid_len = cred->ssid_len;
 		bss->ssid.ssid_set = 1;
@@ -347,8 +347,12 @@
 			bss->wpa_key_mgmt = WPA_KEY_MGMT_PSK;
 
 		bss->wpa_pairwise = 0;
-		if (cred->encr_type & WPS_ENCR_AES)
-			bss->wpa_pairwise |= WPA_CIPHER_CCMP;
+		if (cred->encr_type & WPS_ENCR_AES) {
+			if (hapd->iconf->hw_mode == HOSTAPD_MODE_IEEE80211AD)
+				bss->wpa_pairwise |= WPA_CIPHER_GCMP;
+			else
+				bss->wpa_pairwise |= WPA_CIPHER_CCMP;
+		}
 		if (cred->encr_type & WPS_ENCR_TKIP)
 			bss->wpa_pairwise |= WPA_CIPHER_TKIP;
 		bss->rsn_pairwise = bss->wpa_pairwise;
@@ -362,10 +366,9 @@
 			if (bss->ssid.wpa_passphrase)
 				os_memcpy(bss->ssid.wpa_passphrase, cred->key,
 					  cred->key_len);
-			os_free(bss->ssid.wpa_psk);
-			bss->ssid.wpa_psk = NULL;
+			hostapd_config_clear_wpa_psk(&bss->ssid.wpa_psk);
 		} else if (cred->key_len == 64) {
-			os_free(bss->ssid.wpa_psk);
+			hostapd_config_clear_wpa_psk(&bss->ssid.wpa_psk);
 			bss->ssid.wpa_psk =
 				os_zalloc(sizeof(struct hostapd_wpa_psk));
 			if (bss->ssid.wpa_psk &&
@@ -531,7 +534,11 @@
 		fprintf(nconf, "wpa_pairwise=");
 		prefix = "";
 		if (cred->encr_type & WPS_ENCR_AES) {
-			fprintf(nconf, "CCMP");
+			if (hapd->iconf->hw_mode == HOSTAPD_MODE_IEEE80211AD)
+				fprintf(nconf, "GCMP");
+			else
+				fprintf(nconf, "CCMP");
+
 			prefix = " ";
 		}
 		if (cred->encr_type & WPS_ENCR_TKIP) {
@@ -845,7 +852,9 @@
 	struct hostapd_data *hapd = ctx;
 
 	return hapd->iconf->hw_mode == HOSTAPD_MODE_IEEE80211A ?
-		WPS_RF_50GHZ : WPS_RF_24GHZ; /* FIX: dualband AP */
+		WPS_RF_50GHZ :
+		hapd->iconf->hw_mode == HOSTAPD_MODE_IEEE80211AD ?
+		WPS_RF_60GHZ : WPS_RF_24GHZ; /* FIX: dualband AP */
 }
 
 
@@ -857,8 +866,10 @@
 	wpabuf_free(hapd->wps_probe_resp_ie);
 	hapd->wps_probe_resp_ie = NULL;
 
-	if (deinit_only)
+	if (deinit_only) {
+		hostapd_reset_ap_wps_ie(hapd);
 		return;
+	}
 
 	hostapd_set_ap_wps_ie(hapd);
 }
@@ -1040,7 +1051,9 @@
 	} else {
 		wps->dev.rf_bands =
 			hapd->iconf->hw_mode == HOSTAPD_MODE_IEEE80211A ?
-			WPS_RF_50GHZ : WPS_RF_24GHZ; /* FIX: dualband AP */
+			WPS_RF_50GHZ :
+			hapd->iconf->hw_mode == HOSTAPD_MODE_IEEE80211AD ?
+			WPS_RF_60GHZ : WPS_RF_24GHZ; /* FIX: dualband AP */
 	}
 
 	if (conf->wpa & WPA_PROTO_RSN) {
@@ -1049,7 +1062,7 @@
 		if (conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X)
 			wps->auth_types |= WPS_AUTH_WPA2;
 
-		if (conf->rsn_pairwise & WPA_CIPHER_CCMP)
+		if (conf->rsn_pairwise & (WPA_CIPHER_CCMP | WPA_CIPHER_GCMP))
 			wps->encr_types |= WPS_ENCR_AES;
 		if (conf->rsn_pairwise & WPA_CIPHER_TKIP)
 			wps->encr_types |= WPS_ENCR_TKIP;
@@ -1583,7 +1596,7 @@
 	int ret;
 
 	ret = os_snprintf(data.pin_txt, sizeof(data.pin_txt), "%s", pin);
-	if (ret < 0 || ret >= (int) sizeof(data.pin_txt))
+	if (os_snprintf_error(sizeof(data.pin_txt), ret))
 		return -1;
 	data.timeout = timeout;
 	return hostapd_wps_for_each(hapd, wps_ap_pin_set, &data);
diff --git a/src/ap/x_snoop.c b/src/ap/x_snoop.c
new file mode 100644
index 0000000..aef9a53
--- /dev/null
+++ b/src/ap/x_snoop.c
@@ -0,0 +1,131 @@
+/*
+ * Generic Snooping for Proxy ARP
+ * Copyright (c) 2014, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "hostapd.h"
+#include "sta_info.h"
+#include "ap_drv_ops.h"
+#include "x_snoop.h"
+
+
+int x_snoop_init(struct hostapd_data *hapd)
+{
+	struct hostapd_bss_config *conf = hapd->conf;
+
+	if (!conf->isolate) {
+		wpa_printf(MSG_DEBUG,
+			   "x_snoop: ap_isolate must be enabled for x_snoop");
+		return -1;
+	}
+
+	if (conf->bridge[0] == '\0') {
+		wpa_printf(MSG_DEBUG,
+			   "x_snoop: Bridge must be configured for x_snoop");
+		return -1;
+	}
+
+	if (hostapd_drv_br_port_set_attr(hapd, DRV_BR_PORT_ATTR_HAIRPIN_MODE,
+					 1)) {
+		wpa_printf(MSG_DEBUG,
+			   "x_snoop: Failed to enable hairpin_mode on the bridge port");
+		return -1;
+	}
+
+	if (hostapd_drv_br_port_set_attr(hapd, DRV_BR_PORT_ATTR_PROXYARP, 1)) {
+		wpa_printf(MSG_DEBUG,
+			   "x_snoop: Failed to enable proxyarp on the bridge port");
+		return -1;
+	}
+
+	if (hostapd_drv_br_set_net_param(hapd, DRV_BR_NET_PARAM_GARP_ACCEPT,
+					 1)) {
+		wpa_printf(MSG_DEBUG,
+			   "x_snoop: Failed to enable accepting gratuitous ARP on the bridge");
+		return -1;
+	}
+
+#ifdef CONFIG_IPV6
+	if (hostapd_drv_br_set_net_param(hapd, DRV_BR_MULTICAST_SNOOPING, 1)) {
+		wpa_printf(MSG_DEBUG,
+			   "x_snoop: Failed to enable multicast snooping on the bridge");
+		return -1;
+	}
+#endif /* CONFIG_IPV6 */
+
+	return 0;
+}
+
+
+struct l2_packet_data *
+x_snoop_get_l2_packet(struct hostapd_data *hapd,
+		      void (*handler)(void *ctx, const u8 *src_addr,
+				      const u8 *buf, size_t len),
+		      enum l2_packet_filter_type type)
+{
+	struct hostapd_bss_config *conf = hapd->conf;
+	struct l2_packet_data *l2;
+
+	l2 = l2_packet_init(conf->bridge, NULL, ETH_P_ALL, handler, hapd, 1);
+	if (l2 == NULL) {
+		wpa_printf(MSG_DEBUG,
+			   "x_snoop: Failed to initialize L2 packet processing %s",
+			   strerror(errno));
+		return NULL;
+	}
+
+	if (l2_packet_set_packet_filter(l2, type)) {
+		wpa_printf(MSG_DEBUG,
+			   "x_snoop: Failed to set L2 packet filter for type: %d",
+			   type);
+		l2_packet_deinit(l2);
+		return NULL;
+	}
+
+	return l2;
+}
+
+
+void x_snoop_mcast_to_ucast_convert_send(struct hostapd_data *hapd,
+					 struct sta_info *sta, u8 *buf,
+					 size_t len)
+{
+	int res;
+	u8 addr[ETH_ALEN];
+	u8 *dst_addr = buf;
+
+	if (!(dst_addr[0] & 0x01))
+		return;
+
+	wpa_printf(MSG_EXCESSIVE, "x_snoop: Multicast-to-unicast conversion "
+		   MACSTR " -> " MACSTR " (len %u)",
+		   MAC2STR(dst_addr), MAC2STR(sta->addr), (unsigned int) len);
+
+	/* save the multicast destination address for restoring it later */
+	os_memcpy(addr, buf, ETH_ALEN);
+
+	os_memcpy(buf, sta->addr, ETH_ALEN);
+	res = l2_packet_send(hapd->sock_dhcp, NULL, 0, buf, len);
+	if (res < 0) {
+		wpa_printf(MSG_DEBUG,
+			   "x_snoop: Failed to send mcast to ucast converted packet to "
+			   MACSTR, MAC2STR(sta->addr));
+	}
+
+	/* restore the multicast destination address */
+	os_memcpy(buf, addr, ETH_ALEN);
+}
+
+
+void x_snoop_deinit(struct hostapd_data *hapd)
+{
+	hostapd_drv_br_set_net_param(hapd, DRV_BR_NET_PARAM_GARP_ACCEPT, 0);
+	hostapd_drv_br_port_set_attr(hapd, DRV_BR_PORT_ATTR_PROXYARP, 0);
+	hostapd_drv_br_port_set_attr(hapd, DRV_BR_PORT_ATTR_HAIRPIN_MODE, 0);
+}
diff --git a/src/ap/x_snoop.h b/src/ap/x_snoop.h
new file mode 100644
index 0000000..e43a78d
--- /dev/null
+++ b/src/ap/x_snoop.h
@@ -0,0 +1,56 @@
+/*
+ * Generic Snooping for Proxy ARP
+ * Copyright (c) 2014, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef X_SNOOP_H
+#define X_SNOOP_H
+
+#include "l2_packet/l2_packet.h"
+
+#ifdef CONFIG_PROXYARP
+
+int x_snoop_init(struct hostapd_data *hapd);
+struct l2_packet_data *
+x_snoop_get_l2_packet(struct hostapd_data *hapd,
+		      void (*handler)(void *ctx, const u8 *src_addr,
+				      const u8 *buf, size_t len),
+		      enum l2_packet_filter_type type);
+void x_snoop_mcast_to_ucast_convert_send(struct hostapd_data *hapd,
+					 struct sta_info *sta, u8 *buf,
+					 size_t len);
+void x_snoop_deinit(struct hostapd_data *hapd);
+
+#else /* CONFIG_PROXYARP */
+
+static inline int x_snoop_init(struct hostapd_data *hapd)
+{
+	return 0;
+}
+
+static inline struct l2_packet_data *
+x_snoop_get_l2_packet(struct hostapd_data *hapd,
+		      void (*handler)(void *ctx, const u8 *src_addr,
+				      const u8 *buf, size_t len),
+		      enum l2_packet_filter_type type)
+{
+	return NULL;
+}
+
+static inline void
+x_snoop_mcast_to_ucast_convert_send(struct hostapd_data *hapd,
+				    struct sta_info *sta, void *buf,
+				    size_t len)
+{
+}
+
+static inline void x_snoop_deinit(struct hostapd_data *hapd)
+{
+}
+
+#endif /* CONFIG_PROXYARP */
+
+#endif /* X_SNOOP_H */
diff --git a/src/common/Makefile b/src/common/Makefile
index adfd3df..e703630 100644
--- a/src/common/Makefile
+++ b/src/common/Makefile
@@ -1,8 +1,28 @@
-all:
-	@echo Nothing to be made.
+all: libcommon.a
 
 clean:
-	rm -f *~ *.o *.d *.gcno *.gcda *.gcov
+	rm -f *~ *.o *.d *.gcno *.gcda *.gcov libcommon.a
 
 install:
 	@echo Nothing to be made.
+
+include ../lib.rules
+
+CFLAGS += -DCONFIG_IEEE80211R
+CFLAGS += -DCONFIG_IEEE80211W
+CFLAGS += -DCONFIG_HS20
+CFLAGS += -DCONFIG_SAE
+CFLAGS += -DCONFIG_SUITE
+CFLAGS += -DCONFIG_SUITEB
+
+LIB_OBJS= \
+	gas.o \
+	hw_features_common.o \
+	ieee802_11_common.o \
+	sae.o \
+	wpa_common.o
+
+libcommon.a: $(LIB_OBJS)
+	$(AR) crT $@ $?
+
+-include $(OBJS:%.o=%.d)
diff --git a/src/common/common_module_tests.c b/src/common/common_module_tests.c
index 56b1122..d69448b 100644
--- a/src/common/common_module_tests.c
+++ b/src/common/common_module_tests.c
@@ -1,6 +1,6 @@
 /*
  * common module tests
- * Copyright (c) 2014, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2014-2015, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -10,6 +10,8 @@
 
 #include "utils/common.h"
 #include "ieee802_11_common.h"
+#include "ieee802_11_defs.h"
+#include "gas.h"
 #include "wpa_common.h"
 
 
@@ -46,6 +48,10 @@
 	{ (u8 *) "\x6e\x00", 2, ParseOK, 1 },
 	{ (u8 *) "\xc7\x00", 2, ParseOK, 1 },
 	{ (u8 *) "\xc7\x01\x00", 3, ParseOK, 1 },
+	{ (u8 *) "\x03\x00\x2a\x00\x36\x00\x37\x00\x38\x00\x2d\x00\x3d\x00\xbf\x00\xc0\x00",
+	  18, ParseOK, 9 },
+	{ (u8 *) "\x8b\x00", 2, ParseOK, 1 },
+	{ (u8 *) "\xdd\x04\x00\x90\x4c\x04", 6, ParseUnknown, 1 },
 	{ NULL, 0, ParseOK, 0 }
 };
 
@@ -158,6 +164,34 @@
 }
 
 
+static int gas_tests(void)
+{
+	struct wpabuf *buf;
+
+	wpa_printf(MSG_INFO, "gas tests");
+	gas_anqp_set_len(NULL);
+
+	buf = wpabuf_alloc(1);
+	if (buf == NULL)
+		return -1;
+	gas_anqp_set_len(buf);
+	wpabuf_free(buf);
+
+	buf = wpabuf_alloc(20);
+	if (buf == NULL)
+		return -1;
+	wpabuf_put_u8(buf, WLAN_ACTION_PUBLIC);
+	wpabuf_put_u8(buf, WLAN_PA_GAS_INITIAL_REQ);
+	wpabuf_put_u8(buf, 0);
+	wpabuf_put_be32(buf, 0);
+	wpabuf_put_u8(buf, 0);
+	gas_anqp_set_len(buf);
+	wpabuf_free(buf);
+
+	return 0;
+}
+
+
 int common_module_tests(void)
 {
 	int ret = 0;
@@ -165,6 +199,7 @@
 	wpa_printf(MSG_INFO, "common module tests");
 
 	if (ieee802_11_parse_tests() < 0 ||
+	    gas_tests() < 0 ||
 	    rsn_ie_parse_tests() < 0)
 		ret = -1;
 
diff --git a/src/common/defs.h b/src/common/defs.h
index d4091e3..5b2d7c4 100644
--- a/src/common/defs.h
+++ b/src/common/defs.h
@@ -1,6 +1,6 @@
 /*
  * WPA Supplicant - Common definitions
- * 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.
@@ -49,6 +49,8 @@
 #define WPA_KEY_MGMT_WAPI_CERT BIT(13)
 #define WPA_KEY_MGMT_CCKM BIT(14)
 #define WPA_KEY_MGMT_OSEN BIT(15)
+#define WPA_KEY_MGMT_IEEE8021X_SUITE_B BIT(16)
+#define WPA_KEY_MGMT_IEEE8021X_SUITE_B_192 BIT(17)
 
 static inline int wpa_key_mgmt_wpa_ieee8021x(int akm)
 {
@@ -56,7 +58,9 @@
 			 WPA_KEY_MGMT_FT_IEEE8021X |
 			 WPA_KEY_MGMT_CCKM |
 			 WPA_KEY_MGMT_OSEN |
-			 WPA_KEY_MGMT_IEEE8021X_SHA256));
+			 WPA_KEY_MGMT_IEEE8021X_SHA256 |
+			 WPA_KEY_MGMT_IEEE8021X_SUITE_B |
+			 WPA_KEY_MGMT_IEEE8021X_SUITE_B_192));
 }
 
 static inline int wpa_key_mgmt_wpa_psk(int akm)
@@ -85,7 +89,19 @@
 {
 	return !!(akm & (WPA_KEY_MGMT_PSK_SHA256 |
 			 WPA_KEY_MGMT_IEEE8021X_SHA256 |
-			 WPA_KEY_MGMT_OSEN));
+			 WPA_KEY_MGMT_OSEN |
+			 WPA_KEY_MGMT_IEEE8021X_SUITE_B));
+}
+
+static inline int wpa_key_mgmt_sha384(int akm)
+{
+	return !!(akm & WPA_KEY_MGMT_IEEE8021X_SUITE_B_192);
+}
+
+static inline int wpa_key_mgmt_suite_b(int akm)
+{
+	return !!(akm & (WPA_KEY_MGMT_IEEE8021X_SUITE_B |
+			 WPA_KEY_MGMT_IEEE8021X_SUITE_B_192));
 }
 
 static inline int wpa_key_mgmt_wpa(int akm)
@@ -279,6 +295,7 @@
 	HOSTAPD_MODE_IEEE80211G,
 	HOSTAPD_MODE_IEEE80211A,
 	HOSTAPD_MODE_IEEE80211AD,
+	HOSTAPD_MODE_IEEE80211ANY,
 	NUM_HOSTAPD_MODES
 };
 
@@ -294,10 +311,21 @@
 	WPA_CTRL_REQ_EAP_OTP,
 	WPA_CTRL_REQ_EAP_PASSPHRASE,
 	WPA_CTRL_REQ_SIM,
+	WPA_CTRL_REQ_PSK_PASSPHRASE,
 	NUM_WPA_CTRL_REQS
 };
 
 /* Maximum number of EAP methods to store for EAP server user information */
 #define EAP_MAX_METHODS 8
 
+enum mesh_plink_state {
+	PLINK_LISTEN = 1,
+	PLINK_OPEN_SENT,
+	PLINK_OPEN_RCVD,
+	PLINK_CNF_RCVD,
+	PLINK_ESTAB,
+	PLINK_HOLDING,
+	PLINK_BLOCKED,
+};
+
 #endif /* DEFS_H */
diff --git a/src/common/hw_features_common.c b/src/common/hw_features_common.c
new file mode 100644
index 0000000..e589a1a
--- /dev/null
+++ b/src/common/hw_features_common.c
@@ -0,0 +1,457 @@
+/*
+ * Common hostapd/wpa_supplicant HW features
+ * Copyright (c) 2002-2013, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2015, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "defs.h"
+#include "ieee802_11_defs.h"
+#include "ieee802_11_common.h"
+#include "hw_features_common.h"
+
+
+struct hostapd_channel_data * hw_get_channel_chan(struct hostapd_hw_modes *mode,
+						  int chan, int *freq)
+{
+	int i;
+
+	if (freq)
+		*freq = 0;
+
+	if (!mode)
+		return NULL;
+
+	for (i = 0; i < mode->num_channels; i++) {
+		struct hostapd_channel_data *ch = &mode->channels[i];
+		if (ch->chan == chan) {
+			if (freq)
+				*freq = ch->freq;
+			return ch;
+		}
+	}
+
+	return NULL;
+}
+
+
+struct hostapd_channel_data * hw_get_channel_freq(struct hostapd_hw_modes *mode,
+						  int freq, int *chan)
+{
+	int i;
+
+	if (chan)
+		*chan = 0;
+
+	if (!mode)
+		return NULL;
+
+	for (i = 0; i < mode->num_channels; i++) {
+		struct hostapd_channel_data *ch = &mode->channels[i];
+		if (ch->freq == freq) {
+			if (chan)
+				*chan = ch->chan;
+			return ch;
+		}
+	}
+
+	return NULL;
+}
+
+
+int hw_get_freq(struct hostapd_hw_modes *mode, int chan)
+{
+	int freq;
+
+	hw_get_channel_chan(mode, chan, &freq);
+
+	return freq;
+}
+
+
+int hw_get_chan(struct hostapd_hw_modes *mode, int freq)
+{
+	int chan;
+
+	hw_get_channel_freq(mode, freq, &chan);
+
+	return chan;
+}
+
+
+int allowed_ht40_channel_pair(struct hostapd_hw_modes *mode, int pri_chan,
+			      int sec_chan)
+{
+	int ok, j, first;
+	int allowed[] = { 36, 44, 52, 60, 100, 108, 116, 124, 132, 140,
+			  149, 157, 184, 192 };
+	size_t k;
+
+	if (pri_chan == sec_chan || !sec_chan)
+		return 1; /* HT40 not used */
+
+	wpa_printf(MSG_DEBUG,
+		   "HT40: control channel: %d  secondary channel: %d",
+		   pri_chan, sec_chan);
+
+	/* Verify that HT40 secondary channel is an allowed 20 MHz
+	 * channel */
+	ok = 0;
+	for (j = 0; j < mode->num_channels; j++) {
+		struct hostapd_channel_data *chan = &mode->channels[j];
+		if (!(chan->flag & HOSTAPD_CHAN_DISABLED) &&
+		    chan->chan == sec_chan) {
+			ok = 1;
+			break;
+		}
+	}
+	if (!ok) {
+		wpa_printf(MSG_ERROR, "HT40 secondary channel %d not allowed",
+			   sec_chan);
+		return 0;
+	}
+
+	/*
+	 * Verify that HT40 primary,secondary channel pair is allowed per
+	 * IEEE 802.11n Annex J. This is only needed for 5 GHz band since
+	 * 2.4 GHz rules allow all cases where the secondary channel fits into
+	 * the list of allowed channels (already checked above).
+	 */
+	if (mode->mode != HOSTAPD_MODE_IEEE80211A)
+		return 1;
+
+	first = pri_chan < sec_chan ? pri_chan : sec_chan;
+
+	ok = 0;
+	for (k = 0; k < ARRAY_SIZE(allowed); k++) {
+		if (first == allowed[k]) {
+			ok = 1;
+			break;
+		}
+	}
+	if (!ok) {
+		wpa_printf(MSG_ERROR, "HT40 channel pair (%d, %d) not allowed",
+			   pri_chan, sec_chan);
+		return 0;
+	}
+
+	return 1;
+}
+
+
+void get_pri_sec_chan(struct wpa_scan_res *bss, int *pri_chan, int *sec_chan)
+{
+	struct ieee80211_ht_operation *oper;
+	struct ieee802_11_elems elems;
+
+	*pri_chan = *sec_chan = 0;
+
+	ieee802_11_parse_elems((u8 *) (bss + 1), bss->ie_len, &elems, 0);
+	if (elems.ht_operation) {
+		oper = (struct ieee80211_ht_operation *) elems.ht_operation;
+		*pri_chan = oper->primary_chan;
+		if (oper->ht_param & HT_INFO_HT_PARAM_STA_CHNL_WIDTH) {
+			int sec = oper->ht_param &
+				HT_INFO_HT_PARAM_SECONDARY_CHNL_OFF_MASK;
+			if (sec == HT_INFO_HT_PARAM_SECONDARY_CHNL_ABOVE)
+				*sec_chan = *pri_chan + 4;
+			else if (sec == HT_INFO_HT_PARAM_SECONDARY_CHNL_BELOW)
+				*sec_chan = *pri_chan - 4;
+		}
+	}
+}
+
+
+int check_40mhz_5g(struct hostapd_hw_modes *mode,
+		   struct wpa_scan_results *scan_res, int pri_chan,
+		   int sec_chan)
+{
+	int pri_freq, sec_freq, pri_bss, sec_bss;
+	int bss_pri_chan, bss_sec_chan;
+	size_t i;
+	int match;
+
+	if (!mode || !scan_res || !pri_chan || !sec_chan ||
+	    pri_chan == sec_chan)
+		return 0;
+
+	pri_freq = hw_get_freq(mode, pri_chan);
+	sec_freq = hw_get_freq(mode, sec_chan);
+
+	/*
+	 * Switch PRI/SEC channels if Beacons were detected on selected SEC
+	 * channel, but not on selected PRI channel.
+	 */
+	pri_bss = sec_bss = 0;
+	for (i = 0; i < scan_res->num; i++) {
+		struct wpa_scan_res *bss = scan_res->res[i];
+		if (bss->freq == pri_freq)
+			pri_bss++;
+		else if (bss->freq == sec_freq)
+			sec_bss++;
+	}
+	if (sec_bss && !pri_bss) {
+		wpa_printf(MSG_INFO,
+			   "Switch own primary and secondary channel to get secondary channel with no Beacons from other BSSes");
+		return 2;
+	}
+
+	/*
+	 * Match PRI/SEC channel with any existing HT40 BSS on the same
+	 * channels that we are about to use (if already mixed order in
+	 * existing BSSes, use own preference).
+	 */
+	match = 0;
+	for (i = 0; i < scan_res->num; i++) {
+		struct wpa_scan_res *bss = scan_res->res[i];
+		get_pri_sec_chan(bss, &bss_pri_chan, &bss_sec_chan);
+		if (pri_chan == bss_pri_chan &&
+		    sec_chan == bss_sec_chan) {
+			match = 1;
+			break;
+		}
+	}
+	if (!match) {
+		for (i = 0; i < scan_res->num; i++) {
+			struct wpa_scan_res *bss = scan_res->res[i];
+			get_pri_sec_chan(bss, &bss_pri_chan, &bss_sec_chan);
+			if (pri_chan == bss_sec_chan &&
+			    sec_chan == bss_pri_chan) {
+				wpa_printf(MSG_INFO, "Switch own primary and "
+					   "secondary channel due to BSS "
+					   "overlap with " MACSTR,
+					   MAC2STR(bss->bssid));
+				return 2;
+			}
+		}
+	}
+
+	return 1;
+}
+
+
+static int check_20mhz_bss(struct wpa_scan_res *bss, int pri_freq, int start,
+			   int end)
+{
+	struct ieee802_11_elems elems;
+	struct ieee80211_ht_operation *oper;
+
+	if (bss->freq < start || bss->freq > end || bss->freq == pri_freq)
+		return 0;
+
+	ieee802_11_parse_elems((u8 *) (bss + 1), bss->ie_len, &elems, 0);
+	if (!elems.ht_capabilities) {
+		wpa_printf(MSG_DEBUG, "Found overlapping legacy BSS: "
+			   MACSTR " freq=%d", MAC2STR(bss->bssid), bss->freq);
+		return 1;
+	}
+
+	if (elems.ht_operation) {
+		oper = (struct ieee80211_ht_operation *) elems.ht_operation;
+		if (oper->ht_param & HT_INFO_HT_PARAM_SECONDARY_CHNL_OFF_MASK)
+			return 0;
+
+		wpa_printf(MSG_DEBUG, "Found overlapping 20 MHz HT BSS: "
+			   MACSTR " freq=%d", MAC2STR(bss->bssid), bss->freq);
+		return 1;
+	}
+	return 0;
+}
+
+
+int check_40mhz_2g4(struct hostapd_hw_modes *mode,
+		    struct wpa_scan_results *scan_res, int pri_chan,
+		    int sec_chan)
+{
+	int pri_freq, sec_freq;
+	int affected_start, affected_end;
+	size_t i;
+
+	if (!mode || !scan_res || !pri_chan || !sec_chan)
+		return 0;
+
+	if (pri_chan == sec_chan)
+		return 0;
+
+	pri_freq = hw_get_freq(mode, pri_chan);
+	sec_freq = hw_get_freq(mode, sec_chan);
+
+	affected_start = (pri_freq + sec_freq) / 2 - 25;
+	affected_end = (pri_freq + sec_freq) / 2 + 25;
+	wpa_printf(MSG_DEBUG, "40 MHz affected channel range: [%d,%d] MHz",
+		   affected_start, affected_end);
+	for (i = 0; i < scan_res->num; i++) {
+		struct wpa_scan_res *bss = scan_res->res[i];
+		int pri = bss->freq;
+		int sec = pri;
+		struct ieee802_11_elems elems;
+
+		/* Check for overlapping 20 MHz BSS */
+		if (check_20mhz_bss(bss, pri_freq, affected_start,
+				    affected_end)) {
+			wpa_printf(MSG_DEBUG,
+				   "Overlapping 20 MHz BSS is found");
+			return 0;
+		}
+
+		get_pri_sec_chan(bss, &pri_chan, &sec_chan);
+
+		if (sec_chan) {
+			if (sec_chan < pri_chan)
+				sec = pri - 20;
+			else
+				sec = pri + 20;
+		}
+
+		if ((pri < affected_start || pri > affected_end) &&
+		    (sec < affected_start || sec > affected_end))
+			continue; /* not within affected channel range */
+
+		wpa_printf(MSG_DEBUG, "Neighboring BSS: " MACSTR
+			   " freq=%d pri=%d sec=%d",
+			   MAC2STR(bss->bssid), bss->freq, pri_chan, sec_chan);
+
+		if (sec_chan) {
+			if (pri_freq != pri || sec_freq != sec) {
+				wpa_printf(MSG_DEBUG,
+					   "40 MHz pri/sec mismatch with BSS "
+					   MACSTR
+					   " <%d,%d> (chan=%d%c) vs. <%d,%d>",
+					   MAC2STR(bss->bssid),
+					   pri, sec, pri_chan,
+					   sec > pri ? '+' : '-',
+					   pri_freq, sec_freq);
+				return 0;
+			}
+		}
+
+		ieee802_11_parse_elems((u8 *) (bss + 1), bss->ie_len, &elems,
+				       0);
+		if (elems.ht_capabilities) {
+			struct ieee80211_ht_capabilities *ht_cap =
+				(struct ieee80211_ht_capabilities *)
+				elems.ht_capabilities;
+
+			if (le_to_host16(ht_cap->ht_capabilities_info) &
+			    HT_CAP_INFO_40MHZ_INTOLERANT) {
+				wpa_printf(MSG_DEBUG,
+					   "40 MHz Intolerant is set on channel %d in BSS "
+					   MACSTR, pri, MAC2STR(bss->bssid));
+				return 0;
+			}
+		}
+	}
+
+	return 1;
+}
+
+
+int hostapd_set_freq_params(struct hostapd_freq_params *data,
+			    enum hostapd_hw_mode mode,
+			    int freq, int channel, int ht_enabled,
+			    int vht_enabled, int sec_channel_offset,
+			    int vht_oper_chwidth, int center_segment0,
+			    int center_segment1, u32 vht_caps)
+{
+	os_memset(data, 0, sizeof(*data));
+	data->mode = mode;
+	data->freq = freq;
+	data->channel = channel;
+	data->ht_enabled = ht_enabled;
+	data->vht_enabled = vht_enabled;
+	data->sec_channel_offset = sec_channel_offset;
+	data->center_freq1 = freq + sec_channel_offset * 10;
+	data->center_freq2 = 0;
+	data->bandwidth = sec_channel_offset ? 40 : 20;
+
+	if (data->vht_enabled) switch (vht_oper_chwidth) {
+	case VHT_CHANWIDTH_USE_HT:
+		if (center_segment1 ||
+		    (center_segment0 != 0 &&
+		     5000 + center_segment0 * 5 != data->center_freq1 &&
+		     2407 + center_segment0 * 5 != data->center_freq1))
+			return -1;
+		break;
+	case VHT_CHANWIDTH_80P80MHZ:
+		if (!(vht_caps & VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ)) {
+			wpa_printf(MSG_ERROR,
+				   "80+80 channel width is not supported!");
+			return -1;
+		}
+		if (center_segment1 == center_segment0 + 4 ||
+		    center_segment1 == center_segment0 - 4)
+			return -1;
+		data->center_freq2 = 5000 + center_segment1 * 5;
+		/* fall through */
+	case VHT_CHANWIDTH_80MHZ:
+		data->bandwidth = 80;
+		if ((vht_oper_chwidth == 1 && center_segment1) ||
+		    (vht_oper_chwidth == 3 && !center_segment1) ||
+		    !sec_channel_offset)
+			return -1;
+		if (!center_segment0) {
+			if (channel <= 48)
+				center_segment0 = 42;
+			else if (channel <= 64)
+				center_segment0 = 58;
+			else if (channel <= 112)
+				center_segment0 = 106;
+			else if (channel <= 128)
+				center_segment0 = 122;
+			else if (channel <= 144)
+				center_segment0 = 138;
+			else if (channel <= 161)
+				center_segment0 = 155;
+			data->center_freq1 = 5000 + center_segment0 * 5;
+		} else {
+			/*
+			 * Note: HT/VHT config and params are coupled. Check if
+			 * HT40 channel band is in VHT80 Pri channel band
+			 * configuration.
+			 */
+			if (center_segment0 == channel + 6 ||
+			    center_segment0 == channel + 2 ||
+			    center_segment0 == channel - 2 ||
+			    center_segment0 == channel - 6)
+				data->center_freq1 = 5000 + center_segment0 * 5;
+			else
+				return -1;
+		}
+		break;
+	case VHT_CHANWIDTH_160MHZ:
+		data->bandwidth = 160;
+		if (!(vht_caps & (VHT_CAP_SUPP_CHAN_WIDTH_160MHZ |
+				  VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ))) {
+			wpa_printf(MSG_ERROR,
+				   "160MHZ channel width is not supported!");
+			return -1;
+		}
+		if (center_segment1)
+			return -1;
+		if (!sec_channel_offset)
+			return -1;
+		/*
+		 * Note: HT/VHT config and params are coupled. Check if
+		 * HT40 channel band is in VHT160 channel band configuration.
+		 */
+		if (center_segment0 == channel + 14 ||
+		    center_segment0 == channel + 10 ||
+		    center_segment0 == channel + 6 ||
+		    center_segment0 == channel + 2 ||
+		    center_segment0 == channel - 2 ||
+		    center_segment0 == channel - 6 ||
+		    center_segment0 == channel - 10 ||
+		    center_segment0 == channel - 14)
+			data->center_freq1 = 5000 + center_segment0 * 5;
+		else
+			return -1;
+		break;
+	}
+
+	return 0;
+}
diff --git a/src/common/hw_features_common.h b/src/common/hw_features_common.h
new file mode 100644
index 0000000..7360b4e
--- /dev/null
+++ b/src/common/hw_features_common.h
@@ -0,0 +1,39 @@
+/*
+ * Common hostapd/wpa_supplicant HW features
+ * Copyright (c) 2002-2013, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2015, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef HW_FEATURES_COMMON_H
+#define HW_FEATURES_COMMON_H
+
+#include "drivers/driver.h"
+
+struct hostapd_channel_data * hw_get_channel_chan(struct hostapd_hw_modes *mode,
+						  int chan, int *freq);
+struct hostapd_channel_data * hw_get_channel_freq(struct hostapd_hw_modes *mode,
+						  int freq, int *chan);
+
+int hw_get_freq(struct hostapd_hw_modes *mode, int chan);
+int hw_get_chan(struct hostapd_hw_modes *mode, int freq);
+
+int allowed_ht40_channel_pair(struct hostapd_hw_modes *mode, int pri_chan,
+			      int sec_chan);
+void get_pri_sec_chan(struct wpa_scan_res *bss, int *pri_chan, int *sec_chan);
+int check_40mhz_5g(struct hostapd_hw_modes *mode,
+		   struct wpa_scan_results *scan_res, int pri_chan,
+		   int sec_chan);
+int check_40mhz_2g4(struct hostapd_hw_modes *mode,
+		    struct wpa_scan_results *scan_res, int pri_chan,
+		    int sec_chan);
+int hostapd_set_freq_params(struct hostapd_freq_params *data,
+			    enum hostapd_hw_mode mode,
+			    int freq, int channel, int ht_enabled,
+			    int vht_enabled, int sec_channel_offset,
+			    int vht_oper_chwidth, int center_segment0,
+			    int center_segment1, u32 vht_caps);
+
+#endif /* HW_FEATURES_COMMON_H */
diff --git a/src/common/ieee802_11_common.c b/src/common/ieee802_11_common.c
index 173a400..5385faf 100644
--- a/src/common/ieee802_11_common.c
+++ b/src/common/ieee802_11_common.c
@@ -1,6 +1,6 @@
 /*
  * IEEE 802.11 Common routines
- * Copyright (c) 2002-2013, 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.
@@ -10,6 +10,7 @@
 
 #include "common.h"
 #include "defs.h"
+#include "wpa_common.h"
 #include "ieee802_11_defs.h"
 #include "ieee802_11_common.h"
 
@@ -128,6 +129,15 @@
 			elems->vendor_ht_cap = pos;
 			elems->vendor_ht_cap_len = elen;
 			break;
+		case VENDOR_VHT_TYPE:
+			if (elen > 4 &&
+			    (pos[4] == VENDOR_VHT_SUBTYPE ||
+			     pos[4] == VENDOR_VHT_SUBTYPE2)) {
+				elems->vendor_vht = pos;
+				elems->vendor_vht_len = elen;
+			} else
+				return -1;
+			break;
 		default:
 			wpa_printf(MSG_EXCESSIVE, "Unknown Broadcom "
 				   "information element ignored "
@@ -187,6 +197,12 @@
 
 		switch (id) {
 		case WLAN_EID_SSID:
+			if (elen > SSID_MAX_LEN) {
+				wpa_printf(MSG_DEBUG,
+					   "Ignored too long SSID element (elen=%u)",
+					   elen);
+				break;
+			}
 			elems->ssid = pos;
 			elems->ssid_len = elen;
 			break;
@@ -195,8 +211,9 @@
 			elems->supp_rates_len = elen;
 			break;
 		case WLAN_EID_DS_PARAMS:
+			if (elen < 1)
+				break;
 			elems->ds_params = pos;
-			elems->ds_params_len = elen;
 			break;
 		case WLAN_EID_CF_PARAMS:
 		case WLAN_EID_TIM:
@@ -206,8 +223,9 @@
 			elems->challenge_len = elen;
 			break;
 		case WLAN_EID_ERP_INFO:
+			if (elen < 1)
+				break;
 			elems->erp_info = pos;
-			elems->erp_info_len = elen;
 			break;
 		case WLAN_EID_EXT_SUPP_RATES:
 			elems->ext_supp_rates = pos;
@@ -230,32 +248,53 @@
 			elems->supp_channels_len = elen;
 			break;
 		case WLAN_EID_MOBILITY_DOMAIN:
+			if (elen < sizeof(struct rsn_mdie))
+				break;
 			elems->mdie = pos;
 			elems->mdie_len = elen;
 			break;
 		case WLAN_EID_FAST_BSS_TRANSITION:
+			if (elen < sizeof(struct rsn_ftie))
+				break;
 			elems->ftie = pos;
 			elems->ftie_len = elen;
 			break;
 		case WLAN_EID_TIMEOUT_INTERVAL:
+			if (elen != 5)
+				break;
 			elems->timeout_int = pos;
-			elems->timeout_int_len = elen;
 			break;
 		case WLAN_EID_HT_CAP:
+			if (elen < sizeof(struct ieee80211_ht_capabilities))
+				break;
 			elems->ht_capabilities = pos;
-			elems->ht_capabilities_len = elen;
 			break;
 		case WLAN_EID_HT_OPERATION:
+			if (elen < sizeof(struct ieee80211_ht_operation))
+				break;
 			elems->ht_operation = pos;
-			elems->ht_operation_len = elen;
+			break;
+		case WLAN_EID_MESH_CONFIG:
+			elems->mesh_config = pos;
+			elems->mesh_config_len = elen;
+			break;
+		case WLAN_EID_MESH_ID:
+			elems->mesh_id = pos;
+			elems->mesh_id_len = elen;
+			break;
+		case WLAN_EID_PEER_MGMT:
+			elems->peer_mgmt = pos;
+			elems->peer_mgmt_len = elen;
 			break;
 		case WLAN_EID_VHT_CAP:
+			if (elen < sizeof(struct ieee80211_vht_capabilities))
+				break;
 			elems->vht_capabilities = pos;
-			elems->vht_capabilities_len = elen;
 			break;
 		case WLAN_EID_VHT_OPERATION:
+			if (elen < sizeof(struct ieee80211_vht_operation))
+				break;
 			elems->vht_operation = pos;
-			elems->vht_operation_len = elen;
 			break;
 		case WLAN_EID_VHT_OPERATING_MODE_NOTIFICATION:
 			if (elen != 1)
@@ -290,6 +329,16 @@
 			elems->ssid_list = pos;
 			elems->ssid_list_len = elen;
 			break;
+		case WLAN_EID_AMPE:
+			elems->ampe = pos;
+			elems->ampe_len = elen;
+			break;
+		case WLAN_EID_MIC:
+			elems->mic = pos;
+			elems->mic_len = elen;
+			/* after mic everything is encrypted, so stop. */
+			left = elen;
+			break;
 		default:
 			unknown++;
 			if (!show_errors)
@@ -455,14 +504,14 @@
 		ac->aifs = v;
 	} else if (os_strcmp(pos, "cwmin") == 0) {
 		v = atoi(val);
-		if (v < 0 || v > 12) {
+		if (v < 0 || v > 15) {
 			wpa_printf(MSG_ERROR, "Invalid cwMin value %d", v);
 			return -1;
 		}
 		ac->cwmin = v;
 	} else if (os_strcmp(pos, "cwmax") == 0) {
 		v = atoi(val);
-		if (v < 0 || v > 12) {
+		if (v < 0 || v > 15) {
 			wpa_printf(MSG_ERROR, "Invalid cwMax value %d", v);
 			return -1;
 		}
@@ -515,6 +564,300 @@
 }
 
 
+static const char *const us_op_class_cc[] = {
+	"US", "CA", NULL
+};
+
+static const char *const eu_op_class_cc[] = {
+	"AL", "AM", "AT", "AZ", "BA", "BE", "BG", "BY", "CH", "CY", "CZ", "DE",
+	"DK", "EE", "EL", "ES", "FI", "FR", "GE", "HR", "HU", "IE", "IS", "IT",
+	"LI", "LT", "LU", "LV", "MD", "ME", "MK", "MT", "NL", "NO", "PL", "PT",
+	"RO", "RS", "RU", "SE", "SI", "SK", "TR", "UA", "UK", NULL
+};
+
+static const char *const jp_op_class_cc[] = {
+	"JP", NULL
+};
+
+static const char *const cn_op_class_cc[] = {
+	"CN", NULL
+};
+
+
+static int country_match(const char *const cc[], const char *const country)
+{
+	int i;
+
+	if (country == NULL)
+		return 0;
+	for (i = 0; cc[i]; i++) {
+		if (cc[i][0] == country[0] && cc[i][1] == country[1])
+			return 1;
+	}
+
+	return 0;
+}
+
+
+static int ieee80211_chan_to_freq_us(u8 op_class, u8 chan)
+{
+	switch (op_class) {
+	case 12: /* channels 1..11 */
+	case 32: /* channels 1..7; 40 MHz */
+	case 33: /* channels 5..11; 40 MHz */
+		if (chan < 1 || chan > 11)
+			return -1;
+		return 2407 + 5 * chan;
+	case 1: /* channels 36,40,44,48 */
+	case 2: /* channels 52,56,60,64; dfs */
+	case 22: /* channels 36,44; 40 MHz */
+	case 23: /* channels 52,60; 40 MHz */
+	case 27: /* channels 40,48; 40 MHz */
+	case 28: /* channels 56,64; 40 MHz */
+		if (chan < 36 || chan > 64)
+			return -1;
+		return 5000 + 5 * chan;
+	case 4: /* channels 100-144 */
+	case 24: /* channels 100-140; 40 MHz */
+		if (chan < 100 || chan > 144)
+			return -1;
+		return 5000 + 5 * chan;
+	case 3: /* channels 149,153,157,161 */
+	case 25: /* channels 149,157; 40 MHz */
+	case 26: /* channels 149,157; 40 MHz */
+	case 30: /* channels 153,161; 40 MHz */
+	case 31: /* channels 153,161; 40 MHz */
+		if (chan < 149 || chan > 161)
+			return -1;
+		return 5000 + 5 * chan;
+	case 5: /* channels 149,153,157,161,165 */
+		if (chan < 149 || chan > 165)
+			return -1;
+		return 5000 + 5 * chan;
+	case 34: /* 60 GHz band, channels 1..3 */
+		if (chan < 1 || chan > 3)
+			return -1;
+		return 56160 + 2160 * chan;
+	}
+	return -1;
+}
+
+
+static int ieee80211_chan_to_freq_eu(u8 op_class, u8 chan)
+{
+	switch (op_class) {
+	case 4: /* channels 1..13 */
+	case 11: /* channels 1..9; 40 MHz */
+	case 12: /* channels 5..13; 40 MHz */
+		if (chan < 1 || chan > 13)
+			return -1;
+		return 2407 + 5 * chan;
+	case 1: /* channels 36,40,44,48 */
+	case 2: /* channels 52,56,60,64; dfs */
+	case 5: /* channels 36,44; 40 MHz */
+	case 6: /* channels 52,60; 40 MHz */
+	case 8: /* channels 40,48; 40 MHz */
+	case 9: /* channels 56,64; 40 MHz */
+		if (chan < 36 || chan > 64)
+			return -1;
+		return 5000 + 5 * chan;
+	case 3: /* channels 100-140 */
+	case 7: /* channels 100-132; 40 MHz */
+	case 10: /* channels 104-136; 40 MHz */
+	case 16: /* channels 100-140 */
+		if (chan < 100 || chan > 140)
+			return -1;
+		return 5000 + 5 * chan;
+	case 17: /* channels 149,153,157,161,165,169 */
+		if (chan < 149 || chan > 169)
+			return -1;
+		return 5000 + 5 * chan;
+	case 18: /* 60 GHz band, channels 1..4 */
+		if (chan < 1 || chan > 4)
+			return -1;
+		return 56160 + 2160 * chan;
+	}
+	return -1;
+}
+
+
+static int ieee80211_chan_to_freq_jp(u8 op_class, u8 chan)
+{
+	switch (op_class) {
+	case 30: /* channels 1..13 */
+	case 56: /* channels 1..9; 40 MHz */
+	case 57: /* channels 5..13; 40 MHz */
+		if (chan < 1 || chan > 13)
+			return -1;
+		return 2407 + 5 * chan;
+	case 31: /* channel 14 */
+		if (chan != 14)
+			return -1;
+		return 2414 + 5 * chan;
+	case 1: /* channels 34,38,42,46(old) or 36,40,44,48 */
+	case 32: /* channels 52,56,60,64 */
+	case 33: /* channels 52,56,60,64 */
+	case 36: /* channels 36,44; 40 MHz */
+	case 37: /* channels 52,60; 40 MHz */
+	case 38: /* channels 52,60; 40 MHz */
+	case 41: /* channels 40,48; 40 MHz */
+	case 42: /* channels 56,64; 40 MHz */
+	case 43: /* channels 56,64; 40 MHz */
+		if (chan < 34 || chan > 64)
+			return -1;
+		return 5000 + 5 * chan;
+	case 34: /* channels 100-140 */
+	case 35: /* channels 100-140 */
+	case 39: /* channels 100-132; 40 MHz */
+	case 40: /* channels 100-132; 40 MHz */
+	case 44: /* channels 104-136; 40 MHz */
+	case 45: /* channels 104-136; 40 MHz */
+	case 58: /* channels 100-140 */
+		if (chan < 100 || chan > 140)
+			return -1;
+		return 5000 + 5 * chan;
+	case 59: /* 60 GHz band, channels 1..4 */
+		if (chan < 1 || chan > 3)
+			return -1;
+		return 56160 + 2160 * chan;
+	}
+	return -1;
+}
+
+
+static int ieee80211_chan_to_freq_cn(u8 op_class, u8 chan)
+{
+	switch (op_class) {
+	case 7: /* channels 1..13 */
+	case 8: /* channels 1..9; 40 MHz */
+	case 9: /* channels 5..13; 40 MHz */
+		if (chan < 1 || chan > 13)
+			return -1;
+		return 2407 + 5 * chan;
+	case 1: /* channels 36,40,44,48 */
+	case 2: /* channels 52,56,60,64; dfs */
+	case 4: /* channels 36,44; 40 MHz */
+	case 5: /* channels 52,60; 40 MHz */
+		if (chan < 36 || chan > 64)
+			return -1;
+		return 5000 + 5 * chan;
+	case 3: /* channels 149,153,157,161,165 */
+	case 6: /* channels 149,157; 40 MHz */
+		if (chan < 149 || chan > 165)
+			return -1;
+		return 5000 + 5 * chan;
+	}
+	return -1;
+}
+
+
+static int ieee80211_chan_to_freq_global(u8 op_class, u8 chan)
+{
+	/* Table E-4 in IEEE Std 802.11-2012 - Global operating classes */
+	switch (op_class) {
+	case 81:
+		/* channels 1..13 */
+		if (chan < 1 || chan > 13)
+			return -1;
+		return 2407 + 5 * chan;
+	case 82:
+		/* channel 14 */
+		if (chan != 14)
+			return -1;
+		return 2414 + 5 * chan;
+	case 83: /* channels 1..9; 40 MHz */
+	case 84: /* channels 5..13; 40 MHz */
+		if (chan < 1 || chan > 13)
+			return -1;
+		return 2407 + 5 * chan;
+	case 115: /* channels 36,40,44,48; indoor only */
+	case 116: /* channels 36,44; 40 MHz; indoor only */
+	case 117: /* channels 40,48; 40 MHz; indoor only */
+	case 118: /* channels 52,56,60,64; dfs */
+	case 119: /* channels 52,60; 40 MHz; dfs */
+	case 120: /* channels 56,64; 40 MHz; dfs */
+		if (chan < 36 || chan > 64)
+			return -1;
+		return 5000 + 5 * chan;
+	case 121: /* channels 100-140 */
+	case 122: /* channels 100-142; 40 MHz */
+	case 123: /* channels 104-136; 40 MHz */
+		if (chan < 100 || chan > 140)
+			return -1;
+		return 5000 + 5 * chan;
+	case 124: /* channels 149,153,157,161 */
+	case 126: /* channels 149,157; 40 MHz */
+	case 127: /* channels 153,161; 40 MHz */
+		if (chan < 149 || chan > 161)
+			return -1;
+		return 5000 + 5 * chan;
+	case 125: /* channels 149,153,157,161,165,169 */
+		if (chan < 149 || chan > 169)
+			return -1;
+		return 5000 + 5 * chan;
+	case 128: /* center freqs 42, 58, 106, 122, 138, 155; 80 MHz */
+	case 130: /* center freqs 42, 58, 106, 122, 138, 155; 80 MHz */
+		if (chan < 36 || chan > 161)
+			return -1;
+		return 5000 + 5 * chan;
+	case 129: /* center freqs 50, 114; 160 MHz */
+		if (chan < 50 || chan > 114)
+			return -1;
+		return 5000 + 5 * chan;
+	case 180: /* 60 GHz band, channels 1..4 */
+		if (chan < 1 || chan > 4)
+			return -1;
+		return 56160 + 2160 * chan;
+	}
+	return -1;
+}
+
+/**
+ * ieee80211_chan_to_freq - Convert channel info to frequency
+ * @country: Country code, if known; otherwise, global operating class is used
+ * @op_class: Operating class
+ * @chan: Channel number
+ * Returns: Frequency in MHz or -1 if the specified channel is unknown
+ */
+int ieee80211_chan_to_freq(const char *country, u8 op_class, u8 chan)
+{
+	int freq;
+
+	if (country_match(us_op_class_cc, country)) {
+		freq = ieee80211_chan_to_freq_us(op_class, chan);
+		if (freq > 0)
+			return freq;
+	}
+
+	if (country_match(eu_op_class_cc, country)) {
+		freq = ieee80211_chan_to_freq_eu(op_class, chan);
+		if (freq > 0)
+			return freq;
+	}
+
+	if (country_match(jp_op_class_cc, country)) {
+		freq = ieee80211_chan_to_freq_jp(op_class, chan);
+		if (freq > 0)
+			return freq;
+	}
+
+	if (country_match(cn_op_class_cc, country)) {
+		freq = ieee80211_chan_to_freq_cn(op_class, chan);
+		if (freq > 0)
+			return freq;
+	}
+
+	return ieee80211_chan_to_freq_global(op_class, chan);
+}
+
+
+int ieee80211_is_dfs(int freq)
+{
+	/* TODO: this could be more accurate to better cover all domains */
+	return (freq >= 5260 && freq <= 5320) || (freq >= 5500 && freq <= 5700);
+}
+
+
 static int is_11b(u8 rate)
 {
 	return rate == 0x02 || rate == 0x04 || rate == 0x0b || rate == 0x16;
diff --git a/src/common/ieee802_11_common.h b/src/common/ieee802_11_common.h
index cf83057..c84d8a7 100644
--- a/src/common/ieee802_11_common.h
+++ b/src/common/ieee802_11_common.h
@@ -28,10 +28,14 @@
 	const u8 *timeout_int;
 	const u8 *ht_capabilities;
 	const u8 *ht_operation;
+	const u8 *mesh_config;
+	const u8 *mesh_id;
+	const u8 *peer_mgmt;
 	const u8 *vht_capabilities;
 	const u8 *vht_operation;
 	const u8 *vht_opmode_notif;
 	const u8 *vendor_ht_cap;
+	const u8 *vendor_vht;
 	const u8 *p2p;
 	const u8 *wfd;
 	const u8 *link_id;
@@ -42,12 +46,12 @@
 	const u8 *bss_max_idle_period;
 	const u8 *ssid_list;
 	const u8 *osen;
+	const u8 *ampe;
+	const u8 *mic;
 
 	u8 ssid_len;
 	u8 supp_rates_len;
-	u8 ds_params_len;
 	u8 challenge_len;
-	u8 erp_info_len;
 	u8 ext_supp_rates_len;
 	u8 wpa_ie_len;
 	u8 rsn_ie_len;
@@ -57,12 +61,11 @@
 	u8 supp_channels_len;
 	u8 mdie_len;
 	u8 ftie_len;
-	u8 timeout_int_len;
-	u8 ht_capabilities_len;
-	u8 ht_operation_len;
-	u8 vht_capabilities_len;
-	u8 vht_operation_len;
+	u8 mesh_config_len;
+	u8 mesh_id_len;
+	u8 peer_mgmt_len;
 	u8 vendor_ht_cap_len;
+	u8 vendor_vht_len;
 	u8 p2p_len;
 	u8 wfd_len;
 	u8 interworking_len;
@@ -71,6 +74,8 @@
 	u8 ext_capab_len;
 	u8 ssid_list_len;
 	u8 osen_len;
+	u8 ampe_len;
+	u8 mic_len;
 };
 
 typedef enum { ParseOK = 0, ParseUnknown = 1, ParseFailed = -1 } ParseRes;
@@ -95,6 +100,8 @@
 int hostapd_config_wmm_ac(struct hostapd_wmm_ac_params wmm_ac_params[],
 			  const char *name, const char *val);
 enum hostapd_hw_mode ieee80211_freq_to_chan(int freq, u8 *channel);
+int ieee80211_chan_to_freq(const char *country, u8 op_class, u8 chan);
+int ieee80211_is_dfs(int freq);
 
 int supp_rates_11b_only(struct ieee802_11_elems *elems);
 
diff --git a/src/common/ieee802_11_defs.h b/src/common/ieee802_11_defs.h
index 6de71e9..47b15de 100644
--- a/src/common/ieee802_11_defs.h
+++ b/src/common/ieee802_11_defs.h
@@ -1,6 +1,6 @@
 /*
  * IEEE 802.11 Frame type definitions
- * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2002-2015, Jouni Malinen <j@w1.fi>
  * Copyright (c) 2007-2008 Intel Corporation
  *
  * This software may be distributed under the terms of the BSD license.
@@ -10,6 +10,8 @@
 #ifndef IEEE802_11_DEFS_H
 #define IEEE802_11_DEFS_H
 
+#include <utils/common.h>
+
 /* IEEE 802.11 defines */
 
 #define WLAN_FC_PVER		0x0003
@@ -25,6 +27,8 @@
 #define WLAN_FC_GET_TYPE(fc)	(((fc) & 0x000c) >> 2)
 #define WLAN_FC_GET_STYPE(fc)	(((fc) & 0x00f0) >> 4)
 
+#define WLAN_INVALID_MGMT_SEQ   0xFFFF
+
 #define WLAN_GET_SEQ_FRAG(seq) ((seq) & (BIT(3) | BIT(2) | BIT(1) | BIT(0)))
 #define WLAN_GET_SEQ_SEQ(seq) \
 	(((seq) & (~(BIT(3) | BIT(2) | BIT(1) | BIT(0)))) >> 4)
@@ -194,6 +198,16 @@
 #define WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED 26
 /* IEEE 802.11e */
 #define WLAN_REASON_DISASSOC_LOW_ACK 34
+/* IEEE 802.11s */
+#define WLAN_REASON_MESH_PEERING_CANCELLED 52
+#define WLAN_REASON_MESH_MAX_PEERS 53
+#define WLAN_REASON_MESH_CONFIG_POLICY_VIOLATION 54
+#define WLAN_REASON_MESH_CLOSE_RCVD 55
+#define WLAN_REASON_MESH_MAX_RETRIES 56
+#define WLAN_REASON_MESH_CONFIRM_TIMEOUT 57
+#define WLAN_REASON_MESH_INVALID_GTK 58
+#define WLAN_REASON_MESH_INCONSISTENT_PARAMS 59
+#define WLAN_REASON_MESH_INVALID_SECURITY_CAP 60
 
 
 /* Information Element IDs */
@@ -234,6 +248,7 @@
 #define WLAN_EID_SECONDARY_CHANNEL_OFFSET 62
 #define WLAN_EID_WAPI 68
 #define WLAN_EID_TIME_ADVERTISEMENT 69
+#define WLAN_EID_RRM_ENABLED_CAPABILITIES 70
 #define WLAN_EID_20_40_BSS_COEXISTENCE 72
 #define WLAN_EID_20_40_BSS_INTOLERANT 73
 #define WLAN_EID_OVERLAPPING_BSS_SCAN_PARAMS 74
@@ -249,7 +264,12 @@
 #define WLAN_EID_ADV_PROTO 108
 #define WLAN_EID_QOS_MAP_SET 110
 #define WLAN_EID_ROAMING_CONSORTIUM 111
+#define WLAN_EID_MESH_CONFIG 113
+#define WLAN_EID_MESH_ID 114
+#define WLAN_EID_PEER_MGMT 117
 #define WLAN_EID_EXT_CAPAB 127
+#define WLAN_EID_AMPE 139
+#define WLAN_EID_MIC 140
 #define WLAN_EID_CCKM 156
 #define WLAN_EID_VHT_CAP 191
 #define WLAN_EID_VHT_OPERATION 192
@@ -277,6 +297,7 @@
 #define WLAN_ACTION_WNM 10
 #define WLAN_ACTION_UNPROTECTED_WNM 11
 #define WLAN_ACTION_TDLS 12
+#define WLAN_ACTION_SELF_PROTECTED 15
 #define WLAN_ACTION_WMM 17 /* WMM Specification 1.1 */
 #define WLAN_ACTION_VENDOR_SPECIFIC 127
 
@@ -321,6 +342,19 @@
 #define WLAN_TDLS_PEER_TRAFFIC_RESPONSE 9
 #define WLAN_TDLS_DISCOVERY_REQUEST 10
 
+/* Radio Measurement Action codes */
+#define WLAN_RRM_RADIO_MEASUREMENT_REQUEST 0
+#define WLAN_RRM_RADIO_MEASUREMENT_REPORT 1
+#define WLAN_RRM_LINK_MEASUREMENT_REQUEST 2
+#define WLAN_RRM_LINK_MEASUREMENT_REPORT 3
+#define WLAN_RRM_NEIGHBOR_REPORT_REQUEST 4
+#define WLAN_RRM_NEIGHBOR_REPORT_RESPONSE 5
+
+/* Radio Measurement capabilities (from RRM Capabilities IE) */
+/* byte 1 (out of 5) */
+#define WLAN_RRM_CAPS_LINK_MEASUREMENT BIT(0)
+#define WLAN_RRM_CAPS_NEIGHBOR_REPORT BIT(1)
+
 /* Timeout Interval Type */
 #define WLAN_TIMEOUT_REASSOC_DEADLINE 1
 #define WLAN_TIMEOUT_KEY_LIFETIME 2
@@ -438,35 +472,35 @@
 			le16 auth_transaction;
 			le16 status_code;
 			/* possibly followed by Challenge text */
-			u8 variable[0];
+			u8 variable[];
 		} STRUCT_PACKED auth;
 		struct {
 			le16 reason_code;
-			u8 variable[0];
+			u8 variable[];
 		} STRUCT_PACKED deauth;
 		struct {
 			le16 capab_info;
 			le16 listen_interval;
 			/* followed by SSID and Supported rates */
-			u8 variable[0];
+			u8 variable[];
 		} STRUCT_PACKED assoc_req;
 		struct {
 			le16 capab_info;
 			le16 status_code;
 			le16 aid;
 			/* followed by Supported rates */
-			u8 variable[0];
+			u8 variable[];
 		} STRUCT_PACKED assoc_resp, reassoc_resp;
 		struct {
 			le16 capab_info;
 			le16 listen_interval;
 			u8 current_ap[6];
 			/* followed by SSID and Supported rates */
-			u8 variable[0];
+			u8 variable[];
 		} STRUCT_PACKED reassoc_req;
 		struct {
 			le16 reason_code;
-			u8 variable[0];
+			u8 variable[];
 		} STRUCT_PACKED disassoc;
 		struct {
 			u8 timestamp[8];
@@ -474,7 +508,7 @@
 			le16 capab_info;
 			/* followed by some of SSID, Supported rates,
 			 * FH Params, DS Params, CF Params, IBSS Params, TIM */
-			u8 variable[0];
+			u8 variable[];
 		} STRUCT_PACKED beacon;
 		struct {
 			/* only variable items: SSID, Supported rates */
@@ -486,7 +520,7 @@
 			le16 capab_info;
 			/* followed by some of SSID, Supported rates,
 			 * FH Params, DS Params, CF Params, IBSS Params */
-			u8 variable[0];
+			u8 variable[];
 		} STRUCT_PACKED probe_resp;
 		struct {
 			u8 category;
@@ -495,7 +529,7 @@
 					u8 action_code;
 					u8 dialog_token;
 					u8 status_code;
-					u8 variable[0];
+					u8 variable[];
 				} STRUCT_PACKED wmm_action;
 				struct{
 					u8 action_code;
@@ -509,14 +543,14 @@
 					u8 action;
 					u8 sta_addr[ETH_ALEN];
 					u8 target_ap_addr[ETH_ALEN];
-					u8 variable[0]; /* FT Request */
+					u8 variable[]; /* FT Request */
 				} STRUCT_PACKED ft_action_req;
 				struct {
 					u8 action;
 					u8 sta_addr[ETH_ALEN];
 					u8 target_ap_addr[ETH_ALEN];
 					le16 status_code;
-					u8 variable[0]; /* FT Request */
+					u8 variable[]; /* FT Request */
 				} STRUCT_PACKED ft_action_resp;
 				struct {
 					u8 action;
@@ -529,23 +563,23 @@
 				struct {
 					u8 action;
 					u8 dialogtoken;
-					u8 variable[0];
+					u8 variable[];
 				} STRUCT_PACKED wnm_sleep_req;
 				struct {
 					u8 action;
 					u8 dialogtoken;
 					le16 keydata_len;
-					u8 variable[0];
+					u8 variable[];
 				} STRUCT_PACKED wnm_sleep_resp;
 				struct {
 					u8 action;
-					u8 variable[0];
+					u8 variable[];
 				} STRUCT_PACKED public_action;
 				struct {
 					u8 action; /* 9 */
 					u8 oui[3];
 					/* Vendor-specific content */
-					u8 variable[0];
+					u8 variable[];
 				} STRUCT_PACKED vs_public_action;
 				struct {
 					u8 action; /* 7 */
@@ -557,7 +591,7 @@
 					 * Session Information URL (optional),
 					 * BSS Transition Candidate List
 					 * Entries */
-					u8 variable[0];
+					u8 variable[];
 				} STRUCT_PACKED bss_tm_req;
 				struct {
 					u8 action; /* 8 */
@@ -567,7 +601,7 @@
 					/* Target BSSID (optional),
 					 * BSS Transition Candidate List
 					 * Entries (optional) */
-					u8 variable[0];
+					u8 variable[];
 				} STRUCT_PACKED bss_tm_resp;
 				struct {
 					u8 action; /* 6 */
@@ -575,8 +609,12 @@
 					u8 query_reason;
 					/* BSS Transition Candidate List
 					 * Entries (optional) */
-					u8 variable[0];
+					u8 variable[];
 				} STRUCT_PACKED bss_tm_query;
+				struct {
+					u8 action; /* 15 */
+					u8 variable[];
+				} STRUCT_PACKED slf_prot_action;
 			} u;
 		} STRUCT_PACKED action;
 	} u;
@@ -638,6 +676,15 @@
 	le16 vht_basic_mcs_set;
 } STRUCT_PACKED;
 
+struct ieee80211_ampe_ie {
+	u8 selected_pairwise_suite[4];
+	u8 local_nonce[32];
+	u8 peer_nonce[32];
+	u8 mgtk[16];
+	u8 key_rsc[8];
+	u8 key_expiration[4];
+} STRUCT_PACKED;
+
 #ifdef _MSC_VER
 #pragma pack(pop)
 #endif /* _MSC_VER */
@@ -754,6 +801,7 @@
 #define VHT_CAP_MAX_MPDU_LENGTH_7991                ((u32) BIT(0))
 #define VHT_CAP_MAX_MPDU_LENGTH_11454               ((u32) BIT(1))
 #define VHT_CAP_MAX_MPDU_LENGTH_MASK                ((u32) BIT(0) | BIT(1))
+#define VHT_CAP_MAX_MPDU_LENGTH_MASK_SHIFT          0
 #define VHT_CAP_SUPP_CHAN_WIDTH_160MHZ              ((u32) BIT(2))
 #define VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ     ((u32) BIT(3))
 #define VHT_CAP_SUPP_CHAN_WIDTH_MASK                ((u32) BIT(2) | BIT(3))
@@ -767,13 +815,16 @@
 #define VHT_CAP_RXSTBC_4                            ((u32) BIT(10))
 #define VHT_CAP_RXSTBC_MASK                         ((u32) BIT(8) | BIT(9) | \
 							   BIT(10))
+#define VHT_CAP_RXSTBC_MASK_SHIFT                   8
 #define VHT_CAP_SU_BEAMFORMER_CAPABLE               ((u32) BIT(11))
 #define VHT_CAP_SU_BEAMFORMEE_CAPABLE               ((u32) BIT(12))
 #define VHT_CAP_BEAMFORMEE_STS_MAX                  ((u32) BIT(13) | \
 							   BIT(14) | BIT(15))
+#define VHT_CAP_BEAMFORMEE_STS_MAX_SHIFT            13
 #define VHT_CAP_BEAMFORMEE_STS_OFFSET               13
 #define VHT_CAP_SOUNDING_DIMENSION_MAX              ((u32) BIT(16) | \
 							   BIT(17) | BIT(18))
+#define VHT_CAP_SOUNDING_DIMENSION_MAX_SHIFT        16
 #define VHT_CAP_SOUNDING_DIMENSION_OFFSET           16
 #define VHT_CAP_MU_BEAMFORMER_CAPABLE               ((u32) BIT(19))
 #define VHT_CAP_MU_BEAMFORMEE_CAPABLE               ((u32) BIT(20))
@@ -788,6 +839,7 @@
 #define VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_6        ((u32) BIT(24) | BIT(25))
 #define VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MAX      ((u32) BIT(23) | \
 							   BIT(24) | BIT(25))
+#define VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MAX_SHIFT 23
 #define VHT_CAP_VHT_LINK_ADAPTATION_VHT_UNSOL_MFB   ((u32) BIT(27))
 #define VHT_CAP_VHT_LINK_ADAPTATION_VHT_MRQ_MFB     ((u32) BIT(26) | BIT(27))
 #define VHT_CAP_RX_ANTENNA_PATTERN                  ((u32) BIT(28))
@@ -855,6 +907,8 @@
 
 } STRUCT_PACKED;
 
+#define WMM_QOSINFO_AP_UAPSD 0x80
+
 #define WMM_QOSINFO_STA_AC_MASK 0x0f
 #define WMM_QOSINFO_STA_SP_MASK 0x03
 #define WMM_QOSINFO_STA_SP_SHIFT 5
@@ -922,11 +976,12 @@
 
 
 /* Access Categories / ACI to AC coding */
-enum {
+enum wmm_ac {
 	WMM_AC_BE = 0 /* Best Effort */,
 	WMM_AC_BK = 1 /* Background */,
 	WMM_AC_VI = 2 /* Video */,
-	WMM_AC_VO = 3 /* Voice */
+	WMM_AC_VO = 3 /* Voice */,
+	WMM_AC_NUM = 4
 };
 
 
@@ -981,6 +1036,14 @@
 	P2P_ATTR_OPERATING_CHANNEL = 17,
 	P2P_ATTR_INVITATION_FLAGS = 18,
 	P2P_ATTR_OOB_GO_NEG_CHANNEL = 19,
+	P2P_ATTR_SERVICE_HASH = 21,
+	P2P_ATTR_SESSION_INFORMATION_DATA = 22,
+	P2P_ATTR_CONNECTION_CAPABILITY = 23,
+	P2P_ATTR_ADVERTISEMENT_ID = 24,
+	P2P_ATTR_ADVERTISED_SERVICE = 25,
+	P2P_ATTR_SESSION_ID = 26,
+	P2P_ATTR_FEATURE_CAPABILITY = 27,
+	P2P_ATTR_PERSISTENT_GROUP = 28,
 	P2P_ATTR_VENDOR_SPECIFIC = 221
 };
 
@@ -1025,6 +1088,7 @@
 	P2P_SC_FAIL_BOTH_GO_INTENT_15 = 9,
 	P2P_SC_FAIL_INCOMPATIBLE_PROV_METHOD = 10,
 	P2P_SC_FAIL_REJECTED_BY_USER = 11,
+	P2P_SC_SUCCESS_DEFERRED = 12,
 };
 
 enum p2p_role_indication {
@@ -1063,6 +1127,7 @@
 	P2P_SERV_UPNP = 2,
 	P2P_SERV_WS_DISCOVERY = 3,
 	P2P_SERV_WIFI_DISPLAY = 4,
+	P2P_SERV_P2PS = 11,
 	P2P_SERV_VENDOR_SPECIFIC = 255
 };
 
@@ -1087,8 +1152,24 @@
 	WFD_SUBELEM_SESSION_INFO = 9
 };
 
+/* 802.11s */
+#define MESH_SYNC_METHOD_NEIGHBOR_OFFSET 1
+#define MESH_SYNC_METHOD_VENDOR		255
+#define MESH_PATH_PROTOCOL_HWMP		1
+#define MESH_PATH_PROTOCOL_VENDOR	255
+#define MESH_PATH_METRIC_AIRTIME	1
+#define MESH_PATH_METRIC_VENDOR		255
+
+enum plink_action_field {
+	PLINK_OPEN = 1,
+	PLINK_CONFIRM,
+	PLINK_CLOSE
+};
 
 #define OUI_BROADCOM 0x00904c /* Broadcom (Epigram) */
+#define VENDOR_VHT_TYPE		0x04
+#define VENDOR_VHT_SUBTYPE	0x08
+#define VENDOR_VHT_SUBTYPE2	0x00
 
 #define VENDOR_HT_CAPAB_OUI_TYPE 0x33 /* 00-90-4c:0x33 */
 
@@ -1122,6 +1203,8 @@
 #define WLAN_AKM_SUITE_FT_PSK		0x000FAC04
 #define WLAN_AKM_SUITE_8021X_SHA256	0x000FAC05
 #define WLAN_AKM_SUITE_PSK_SHA256	0x000FAC06
+#define WLAN_AKM_SUITE_8021X_SUITE_B	0x000FAC11
+#define WLAN_AKM_SUITE_8021X_SUITE_B_192	0x000FAC12
 #define WLAN_AKM_SUITE_CCKM		0x00409600
 #define WLAN_AKM_SUITE_OSEN		0x506f9a01
 
@@ -1247,4 +1330,32 @@
 #define CHAN_SWITCH_MODE_ALLOW_TX	0
 #define CHAN_SWITCH_MODE_BLOCK_TX	1
 
+struct tpc_report {
+	u8 eid;
+	u8 len;
+	u8 tx_power;
+	u8 link_margin;
+} STRUCT_PACKED;
+
+/* IEEE Std 802.11-2012, 8.5.7.4 - Link Measurement Request frame format */
+struct rrm_link_measurement_request {
+	u8 dialog_token;
+	s8 tx_power;
+	s8 max_tp;
+	u8 variable[0];
+} STRUCT_PACKED;
+
+/* IEEE Std 802.11-2012, 8.5.7.5 - Link Measurement Report frame format */
+struct rrm_link_measurement_report {
+	u8 dialog_token;
+	struct tpc_report tpc;
+	u8 rx_ant_id;
+	u8 tx_ant_id;
+	u8 rcpi;
+	u8 rsni;
+	u8 variable[0];
+} STRUCT_PACKED;
+
+#define SSID_MAX_LEN 32
+
 #endif /* IEEE802_11_DEFS_H */
diff --git a/src/common/privsep_commands.h b/src/common/privsep_commands.h
index 858b51d..c6a472d 100644
--- a/src/common/privsep_commands.h
+++ b/src/common/privsep_commands.h
@@ -9,6 +9,8 @@
 #ifndef PRIVSEP_COMMANDS_H
 #define PRIVSEP_COMMANDS_H
 
+#include "common/ieee802_11_defs.h"
+
 enum privsep_cmd {
 	PRIVSEP_CMD_REGISTER,
 	PRIVSEP_CMD_UNREGISTER,
@@ -29,9 +31,11 @@
 struct privsep_cmd_associate
 {
 	u8 bssid[ETH_ALEN];
-	u8 ssid[32];
+	u8 ssid[SSID_MAX_LEN];
 	size_t ssid_len;
+	int hwmode;
 	int freq;
+	int channel;
 	int pairwise_suite;
 	int group_suite;
 	int key_mgmt_suite;
diff --git a/src/common/qca-vendor.h b/src/common/qca-vendor.h
index ad3bdfd..3c35e79 100644
--- a/src/common/qca-vendor.h
+++ b/src/common/qca-vendor.h
@@ -1,6 +1,6 @@
 /*
  * Qualcomm Atheros OUI and vendor specific assignments
- * Copyright (c) 2014, Qualcomm Atheros, Inc.
+ * Copyright (c) 2014-2015, Qualcomm Atheros, Inc.
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -50,6 +50,45 @@
  * @QCA_NL80211_VENDOR_SUBCMD_NAN: NAN command/event which is used to pass
  *	NAN Request/Response and NAN Indication messages. These messages are
  *	interpreted between the framework and the firmware component.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_KEY_MGMT_SET_KEY: Set key operation that can be
+ *	used to configure PMK to the driver even when not connected. This can
+ *	be used to request offloading of key management operations. Only used
+ *	if device supports QCA_WLAN_VENDOR_FEATURE_KEY_MGMT_OFFLOAD.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_KEY_MGMT_ROAM_AUTH: An extended version of
+ *	NL80211_CMD_ROAM event with optional attributes including information
+ *	from offloaded key management operation. Uses
+ *	enum qca_wlan_vendor_attr_roam_auth attributes. Only used
+ *	if device supports QCA_WLAN_VENDOR_FEATURE_KEY_MGMT_OFFLOAD.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_DO_ACS: ACS command/event which is used to
+ *	invoke the ACS function in device and pass selected channels to
+ *	hostapd.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_GET_FEATURES: Command to get the features
+ *	supported by the driver. enum qca_wlan_vendor_features defines
+ *	the possible features.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_CAC_STARTED: Event used by driver,
+ *	which supports DFS offloading, to indicate a channel availability check
+ *	start.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_CAC_FINISHED: Event used by driver,
+ *	which supports DFS offloading, to indicate a channel availability check
+ *	completion.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_CAC_ABORTED: Event used by driver,
+ *	which supports DFS offloading, to indicate that the channel availability
+ *	check aborted, no change to the channel status.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_CAC_NOP_FINISHED: Event used by
+ *	driver, which supports DFS offloading, to indicate that the
+ *	Non-Occupancy Period for this channel is over, channel becomes usable.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_RADAR_DETECTED: Event used by driver,
+ *	which supports DFS offloading, to indicate a radar pattern has been
+ *	detected. The channel is now unusable.
  */
 enum qca_nl80211_vendor_subcmds {
 	QCA_NL80211_VENDOR_SUBCMD_UNSPEC = 0,
@@ -60,7 +99,59 @@
 	QCA_NL80211_VENDOR_SUBCMD_DFS_CAPABILITY =  11,
 	QCA_NL80211_VENDOR_SUBCMD_NAN =  12,
 	QCA_NL80211_VENDOR_SUBMCD_STATS_EXT = 13,
-	/* 14..49 - reserved for QCA */
+	QCA_NL80211_VENDOR_SUBCMD_LL_STATS_SET = 14,
+	QCA_NL80211_VENDOR_SUBCMD_LL_STATS_GET = 15,
+	QCA_NL80211_VENDOR_SUBCMD_LL_STATS_CLR = 16,
+	QCA_NL80211_VENDOR_SUBCMD_LL_STATS_RADIO_RESULTS = 17,
+	QCA_NL80211_VENDOR_SUBCMD_LL_STATS_IFACE_RESULTS = 18,
+	QCA_NL80211_VENDOR_SUBCMD_LL_STATS_PEERS_RESULTS = 19,
+	QCA_NL80211_VENDOR_SUBCMD_GSCAN_START = 20,
+	QCA_NL80211_VENDOR_SUBCMD_GSCAN_STOP = 21,
+	QCA_NL80211_VENDOR_SUBCMD_GSCAN_GET_VALID_CHANNELS = 22,
+	QCA_NL80211_VENDOR_SUBCMD_GSCAN_GET_CAPABILITIES = 23,
+	QCA_NL80211_VENDOR_SUBCMD_GSCAN_GET_CACHED_RESULTS = 24,
+	QCA_NL80211_VENDOR_SUBCMD_GSCAN_SCAN_RESULTS_AVAILABLE = 25,
+	QCA_NL80211_VENDOR_SUBCMD_GSCAN_FULL_SCAN_RESULT = 26,
+	QCA_NL80211_VENDOR_SUBCMD_GSCAN_SCAN_EVENT = 27,
+	QCA_NL80211_VENDOR_SUBCMD_GSCAN_HOTLIST_AP_FOUND = 28,
+	QCA_NL80211_VENDOR_SUBCMD_GSCAN_SET_BSSID_HOTLIST = 29,
+	QCA_NL80211_VENDOR_SUBCMD_GSCAN_RESET_BSSID_HOTLIST = 30,
+	QCA_NL80211_VENDOR_SUBCMD_GSCAN_SIGNIFICANT_CHANGE = 31,
+	QCA_NL80211_VENDOR_SUBCMD_GSCAN_SET_SIGNIFICANT_CHANGE = 32,
+	QCA_NL80211_VENDOR_SUBCMD_GSCAN_RESET_SIGNIFICANT_CHANGE = 33,
+	QCA_NL80211_VENDOR_SUBCMD_TDLS_ENABLE = 34,
+	QCA_NL80211_VENDOR_SUBCMD_TDLS_DISABLE = 35,
+	QCA_NL80211_VENDOR_SUBCMD_TDLS_GET_STATUS = 36,
+	QCA_NL80211_VENDOR_SUBCMD_TDLS_STATE = 37,
+	QCA_NL80211_VENDOR_SUBCMD_GET_SUPPORTED_FEATURES = 38,
+	QCA_NL80211_VENDOR_SUBCMD_SCANNING_MAC_OUI = 39,
+	QCA_NL80211_VENDOR_SUBCMD_NO_DFS_FLAG = 40,
+	QCA_NL80211_VENDOR_SUBCMD_GSCAN_HOTLIST_AP_LOST = 41,
+	QCA_NL80211_VENDOR_SUBCMD_GET_CONCURRENCY_MATRIX = 42,
+	/* 43..49 - reserved for QCA */
+	QCA_NL80211_VENDOR_SUBCMD_KEY_MGMT_SET_KEY = 50,
+	QCA_NL80211_VENDOR_SUBCMD_KEY_MGMT_ROAM_AUTH = 51,
+	QCA_NL80211_VENDOR_SUBCMD_APFIND = 52,
+	/* 53 - reserved - was used by QCA, but not in use anymore */
+	QCA_NL80211_VENDOR_SUBCMD_DO_ACS = 54,
+	QCA_NL80211_VENDOR_SUBCMD_GET_FEATURES = 55,
+	QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_CAC_STARTED = 56,
+	QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_CAC_FINISHED = 57,
+	QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_CAC_ABORTED = 58,
+	QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_CAC_NOP_FINISHED = 59,
+	QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_RADAR_DETECTED = 60,
+	/* 61-90 - reserved for QCA */
+	QCA_NL80211_VENDOR_SUBCMD_DATA_OFFLOAD = 91,
+	QCA_NL80211_VENDOR_SUBCMD_OCB_SET_CONFIG = 92,
+	QCA_NL80211_VENDOR_SUBCMD_OCB_SET_UTC_TIME = 93,
+	QCA_NL80211_VENDOR_SUBCMD_OCB_START_TIMING_ADVERT = 94,
+	QCA_NL80211_VENDOR_SUBCMD_OCB_STOP_TIMING_ADVERT = 95,
+	QCA_NL80211_VENDOR_SUBCMD_OCB_GET_TSF_TIMER = 96,
+	QCA_NL80211_VENDOR_SUBCMD_DCC_GET_STATS = 97,
+	QCA_NL80211_VENDOR_SUBCMD_DCC_CLEAR_STATS = 98,
+	QCA_NL80211_VENDOR_SUBCMD_DCC_UPDATE_NDL = 99,
+	QCA_NL80211_VENDOR_SUBCMD_DCC_STATS_EVENT = 100,
+	QCA_NL80211_VENDOR_SUBCMD_LINK_PROPERTIES = 101,
 };
 
 
@@ -78,6 +169,9 @@
 	 * by enum qca_roaming_policy. */
 	QCA_WLAN_VENDOR_ATTR_ROAMING_POLICY = 5,
 	QCA_WLAN_VENDOR_ATTR_MAC_ADDR = 6,
+	/* used by QCA_NL80211_VENDOR_SUBCMD_GET_FEATURES */
+	QCA_WLAN_VENDOR_ATTR_FEATURE_FLAGS = 7,
+	QCA_WLAN_VENDOR_ATTR_TEST = 8,
 	/* keep last */
 	QCA_WLAN_VENDOR_ATTR_AFTER_LAST,
 	QCA_WLAN_VENDOR_ATTR_MAX	= QCA_WLAN_VENDOR_ATTR_AFTER_LAST - 1,
@@ -89,4 +183,83 @@
 	QCA_ROAMING_ALLOWED_WITHIN_ESS,
 };
 
+enum qca_wlan_vendor_attr_roam_auth {
+	QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_INVALID = 0,
+	QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_BSSID,
+	QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_REQ_IE,
+	QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_RESP_IE,
+	QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_AUTHORIZED,
+	QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_KEY_REPLAY_CTR,
+	QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_PTK_KCK,
+	QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_PTK_KEK,
+	/* keep last */
+	QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_AFTER_LAST,
+	QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_MAX =
+	QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_AFTER_LAST - 1
+};
+
+enum qca_wlan_vendor_attr_acs_offload {
+	QCA_WLAN_VENDOR_ATTR_ACS_CHANNEL_INVALID = 0,
+	QCA_WLAN_VENDOR_ATTR_ACS_PRIMARY_CHANNEL,
+	QCA_WLAN_VENDOR_ATTR_ACS_SECONDARY_CHANNEL,
+	QCA_WLAN_VENDOR_ATTR_ACS_HW_MODE,
+	QCA_WLAN_VENDOR_ATTR_ACS_HT_ENABLED,
+	QCA_WLAN_VENDOR_ATTR_ACS_HT40_ENABLED,
+	QCA_WLAN_VENDOR_ATTR_ACS_VHT_ENABLED,
+	QCA_WLAN_VENDOR_ATTR_ACS_CHWIDTH,
+	QCA_WLAN_VENDOR_ATTR_ACS_CH_LIST,
+	QCA_WLAN_VENDOR_ATTR_ACS_VHT_SEG0_CENTER_CHANNEL,
+	QCA_WLAN_VENDOR_ATTR_ACS_VHT_SEG1_CENTER_CHANNEL,
+	/* keep last */
+	QCA_WLAN_VENDOR_ATTR_ACS_AFTER_LAST,
+	QCA_WLAN_VENDOR_ATTR_ACS_MAX =
+	QCA_WLAN_VENDOR_ATTR_ACS_AFTER_LAST - 1
+};
+
+enum qca_wlan_vendor_acs_hw_mode {
+	QCA_ACS_MODE_IEEE80211B,
+	QCA_ACS_MODE_IEEE80211G,
+	QCA_ACS_MODE_IEEE80211A,
+	QCA_ACS_MODE_IEEE80211AD,
+	QCA_ACS_MODE_IEEE80211ANY,
+};
+
+/**
+ * enum qca_wlan_vendor_features - Vendor device/driver feature flags
+ *
+ * @QCA_WLAN_VENDOR_FEATURE_KEY_MGMT_OFFLOAD: Device supports key
+ *	management offload, a mechanism where the station's firmware
+ *	does the exchange with the AP to establish the temporal keys
+ *	after roaming, rather than having the user space wpa_supplicant do it.
+ * @QCA_WLAN_VENDOR_FEATURE_SUPPORT_HW_MODE_ANY: Device supports automatic
+ *	band selection based on channel selection results.
+ * @NUM_QCA_WLAN_VENDOR_FEATURES: Number of assigned feature bits
+ */
+enum qca_wlan_vendor_features {
+	QCA_WLAN_VENDOR_FEATURE_KEY_MGMT_OFFLOAD	= 0,
+	QCA_WLAN_VENDOR_FEATURE_SUPPORT_HW_MODE_ANY     = 1,
+	NUM_QCA_WLAN_VENDOR_FEATURES /* keep last */
+};
+
+/**
+ * enum qca_wlan_vendor_attr_data_offload_ind - Vendor Data Offload Indication
+ *
+ * @QCA_WLAN_VENDOR_ATTR_DATA_OFFLOAD_IND_SESSION: Session corresponding to
+ *	the offloaded data.
+ * @QCA_WLAN_VENDOR_ATTR_DATA_OFFLOAD_IND_PROTOCOL: Protocol of the offloaded
+ *	data.
+ * @QCA_WLAN_VENDOR_ATTR_DATA_OFFLOAD_IND_EVENT: Event type for the data offload
+ *	indication.
+ */
+enum qca_wlan_vendor_attr_data_offload_ind {
+	QCA_WLAN_VENDOR_ATTR_DATA_OFFLOAD_IND_INVALID = 0,
+	QCA_WLAN_VENDOR_ATTR_DATA_OFFLOAD_IND_SESSION,
+	QCA_WLAN_VENDOR_ATTR_DATA_OFFLOAD_IND_PROTOCOL,
+	QCA_WLAN_VENDOR_ATTR_DATA_OFFLOAD_IND_EVENT,
+
+	/* keep last */
+	QCA_WLAN_VENDOR_ATTR_DATA_OFFLOAD_IND_AFTER_LAST,
+	QCA_WLAN_VENDOR_ATTR_DATA_OFFLOAD_IND_MAX =
+	QCA_WLAN_VENDOR_ATTR_DATA_OFFLOAD_IND_AFTER_LAST - 1
+};
 #endif /* QCA_VENDOR_H */
diff --git a/src/common/sae.c b/src/common/sae.c
index b67623f..503fa1d 100644
--- a/src/common/sae.c
+++ b/src/common/sae.c
@@ -1,6 +1,6 @@
 /*
  * Simultaneous authentication of equals
- * Copyright (c) 2012-2013, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2012-2015, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -87,7 +87,8 @@
 	crypto_ec_point_deinit(tmp->pwe_ecc, 1);
 	crypto_ec_point_deinit(tmp->own_commit_element_ecc, 0);
 	crypto_ec_point_deinit(tmp->peer_commit_element_ecc, 0);
-	os_free(sae->tmp);
+	wpabuf_free(tmp->anti_clogging_token);
+	bin_clear_free(tmp, sizeof(*tmp));
 	sae->tmp = NULL;
 }
 
@@ -123,9 +124,7 @@
 		return NULL;
 
 	for (;;) {
-		if (iter++ > 100)
-			return NULL;
-		if (random_get_bytes(val, order_len) < 0)
+		if (iter++ > 100 || random_get_bytes(val, order_len) < 0)
 			return NULL;
 		if (order_len_bits % 8)
 			buf_shift_right(val, order_len, 8 - order_len_bits % 8);
@@ -170,17 +169,107 @@
 }
 
 
-static int sae_test_pwd_seed_ecc(struct sae_data *sae, const u8 *pwd_seed,
-				 struct crypto_ec_point *pwe)
+static struct crypto_bignum *
+get_rand_1_to_p_1(const u8 *prime, size_t prime_len, size_t prime_bits,
+		  int *r_odd)
 {
-	u8 pwd_value[SAE_MAX_ECC_PRIME_LEN], prime[SAE_MAX_ECC_PRIME_LEN];
-	struct crypto_bignum *x;
-	int y_bit;
+	for (;;) {
+		struct crypto_bignum *r;
+		u8 tmp[SAE_MAX_ECC_PRIME_LEN];
+
+		if (random_get_bytes(tmp, prime_len) < 0)
+			break;
+		if (prime_bits % 8)
+			buf_shift_right(tmp, prime_len, 8 - prime_bits % 8);
+		if (os_memcmp(tmp, prime, prime_len) >= 0)
+			continue;
+		r = crypto_bignum_init_set(tmp, prime_len);
+		if (!r)
+			break;
+		if (crypto_bignum_is_zero(r)) {
+			crypto_bignum_deinit(r, 0);
+			continue;
+		}
+
+		*r_odd = tmp[prime_len - 1] & 0x01;
+		return r;
+	}
+
+	return NULL;
+}
+
+
+static int is_quadratic_residue_blind(struct sae_data *sae,
+				      const u8 *prime, size_t bits,
+				      const struct crypto_bignum *qr,
+				      const struct crypto_bignum *qnr,
+				      const struct crypto_bignum *y_sqr)
+{
+	struct crypto_bignum *r, *num;
+	int r_odd, check, res = -1;
+
+	/*
+	 * Use the blinding technique to mask y_sqr while determining
+	 * whether it is a quadratic residue modulo p to avoid leaking
+	 * timing information while determining the Legendre symbol.
+	 *
+	 * v = y_sqr
+	 * r = a random number between 1 and p-1, inclusive
+	 * num = (v * r * r) modulo p
+	 */
+	r = get_rand_1_to_p_1(prime, sae->tmp->prime_len, bits, &r_odd);
+	if (!r)
+		return -1;
+
+	num = crypto_bignum_init();
+	if (!num ||
+	    crypto_bignum_mulmod(y_sqr, r, sae->tmp->prime, num) < 0 ||
+	    crypto_bignum_mulmod(num, r, sae->tmp->prime, num) < 0)
+		goto fail;
+
+	if (r_odd) {
+		/*
+		 * num = (num * qr) module p
+		 * LGR(num, p) = 1 ==> quadratic residue
+		 */
+		if (crypto_bignum_mulmod(num, qr, sae->tmp->prime, num) < 0)
+			goto fail;
+		check = 1;
+	} else {
+		/*
+		 * num = (num * qnr) module p
+		 * LGR(num, p) = -1 ==> quadratic residue
+		 */
+		if (crypto_bignum_mulmod(num, qnr, sae->tmp->prime, num) < 0)
+			goto fail;
+		check = -1;
+	}
+
+	res = crypto_bignum_legendre(num, sae->tmp->prime);
+	if (res == -2) {
+		res = -1;
+		goto fail;
+	}
+	res = res == check;
+fail:
+	crypto_bignum_deinit(num, 1);
+	crypto_bignum_deinit(r, 1);
+	return res;
+}
+
+
+static int sae_test_pwd_seed_ecc(struct sae_data *sae, const u8 *pwd_seed,
+				 const u8 *prime,
+				 const struct crypto_bignum *qr,
+				 const struct crypto_bignum *qnr,
+				 struct crypto_bignum **ret_x_cand)
+{
+	u8 pwd_value[SAE_MAX_ECC_PRIME_LEN];
+	struct crypto_bignum *y_sqr, *x_cand;
+	int res;
 	size_t bits;
 
-	if (crypto_bignum_to_bin(sae->tmp->prime, prime, sizeof(prime),
-				 sae->tmp->prime_len) < 0)
-		return -1;
+	*ret_x_cand = NULL;
 
 	wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-seed", pwd_seed, SHA256_MAC_LEN);
 
@@ -196,20 +285,23 @@
 	if (os_memcmp(pwd_value, prime, sae->tmp->prime_len) >= 0)
 		return 0;
 
-	y_bit = pwd_seed[SHA256_MAC_LEN - 1] & 0x01;
-
-	x = crypto_bignum_init_set(pwd_value, sae->tmp->prime_len);
-	if (x == NULL)
+	x_cand = crypto_bignum_init_set(pwd_value, sae->tmp->prime_len);
+	if (!x_cand)
 		return -1;
-	if (crypto_ec_point_solve_y_coord(sae->tmp->ec, pwe, x, y_bit) < 0) {
-		crypto_bignum_deinit(x, 0);
-		wpa_printf(MSG_DEBUG, "SAE: No solution found");
-		return 0;
+	y_sqr = crypto_ec_point_compute_y_sqr(sae->tmp->ec, x_cand);
+	if (!y_sqr) {
+		crypto_bignum_deinit(x_cand, 1);
+		return -1;
 	}
-	crypto_bignum_deinit(x, 0);
 
-	wpa_printf(MSG_DEBUG, "SAE: PWE found");
+	res = is_quadratic_residue_blind(sae, prime, bits, qr, qnr, y_sqr);
+	crypto_bignum_deinit(y_sqr, 1);
+	if (res <= 0) {
+		crypto_bignum_deinit(x_cand, 1);
+		return res;
+	}
 
+	*ret_x_cand = x_cand;
 	return 1;
 }
 
@@ -287,24 +379,77 @@
 }
 
 
+static int get_random_qr_qnr(const u8 *prime, size_t prime_len,
+			     const struct crypto_bignum *prime_bn,
+			     size_t prime_bits, struct crypto_bignum **qr,
+			     struct crypto_bignum **qnr)
+{
+	*qr = NULL;
+	*qnr = NULL;
+
+	while (!(*qr) || !(*qnr)) {
+		u8 tmp[SAE_MAX_ECC_PRIME_LEN];
+		struct crypto_bignum *q;
+		int res;
+
+		if (random_get_bytes(tmp, prime_len) < 0)
+			break;
+		if (prime_bits % 8)
+			buf_shift_right(tmp, prime_len, 8 - prime_bits % 8);
+		if (os_memcmp(tmp, prime, prime_len) >= 0)
+			continue;
+		q = crypto_bignum_init_set(tmp, prime_len);
+		if (!q)
+			break;
+		res = crypto_bignum_legendre(q, prime_bn);
+
+		if (res == 1 && !(*qr))
+			*qr = q;
+		else if (res == -1 && !(*qnr))
+			*qnr = q;
+		else
+			crypto_bignum_deinit(q, 0);
+	}
+
+	return (*qr && *qnr) ? 0 : -1;
+}
+
+
 static int sae_derive_pwe_ecc(struct sae_data *sae, const u8 *addr1,
 			      const u8 *addr2, const u8 *password,
 			      size_t password_len)
 {
-	u8 counter, k = 4;
+	u8 counter, k = 40;
 	u8 addrs[2 * ETH_ALEN];
 	const u8 *addr[2];
 	size_t len[2];
-	int found = 0;
-	struct crypto_ec_point *pwe_tmp;
+	u8 dummy_password[32];
+	size_t dummy_password_len;
+	int pwd_seed_odd = 0;
+	u8 prime[SAE_MAX_ECC_PRIME_LEN];
+	size_t prime_len;
+	struct crypto_bignum *x = NULL, *qr, *qnr;
+	size_t bits;
+	int res;
 
-	if (sae->tmp->pwe_ecc == NULL) {
-		sae->tmp->pwe_ecc = crypto_ec_point_init(sae->tmp->ec);
-		if (sae->tmp->pwe_ecc == NULL)
-			return -1;
-	}
-	pwe_tmp = crypto_ec_point_init(sae->tmp->ec);
-	if (pwe_tmp == NULL)
+	dummy_password_len = password_len;
+	if (dummy_password_len > sizeof(dummy_password))
+		dummy_password_len = sizeof(dummy_password);
+	if (random_get_bytes(dummy_password, dummy_password_len) < 0)
+		return -1;
+
+	prime_len = sae->tmp->prime_len;
+	if (crypto_bignum_to_bin(sae->tmp->prime, prime, sizeof(prime),
+				 prime_len) < 0)
+		return -1;
+	bits = crypto_ec_prime_len_bits(sae->tmp->ec);
+
+	/*
+	 * Create a random quadratic residue (qr) and quadratic non-residue
+	 * (qnr) modulo p for blinding purposes during the loop.
+	 */
+	if (get_random_qr_qnr(prime, prime_len, sae->tmp->prime, bits,
+			      &qr, &qnr) < 0)
 		return -1;
 
 	wpa_hexdump_ascii_key(MSG_DEBUG, "SAE: password",
@@ -312,8 +457,9 @@
 
 	/*
 	 * H(salt, ikm) = HMAC-SHA256(salt, ikm)
+	 * base = password
 	 * pwd-seed = H(MAX(STA-A-MAC, STA-B-MAC) || MIN(STA-A-MAC, STA-B-MAC),
-	 *              password || counter)
+	 *              base || counter)
 	 */
 	sae_pwd_seed_key(addr1, addr2, addrs);
 
@@ -327,9 +473,9 @@
 	 * attacks that attempt to determine the number of iterations required
 	 * in the loop.
 	 */
-	for (counter = 1; counter < k || !found; counter++) {
+	for (counter = 1; counter <= k || !x; counter++) {
 		u8 pwd_seed[SHA256_MAC_LEN];
-		int res;
+		struct crypto_bignum *x_cand;
 
 		if (counter > 200) {
 			/* This should not happen in practice */
@@ -341,25 +487,58 @@
 		if (hmac_sha256_vector(addrs, sizeof(addrs), 2, addr, len,
 				       pwd_seed) < 0)
 			break;
+
 		res = sae_test_pwd_seed_ecc(sae, pwd_seed,
-					    found ? pwe_tmp :
-					    sae->tmp->pwe_ecc);
+					    prime, qr, qnr, &x_cand);
 		if (res < 0)
-			break;
-		if (res == 0)
-			continue;
-		if (found) {
-			wpa_printf(MSG_DEBUG, "SAE: Ignore this PWE (one was "
-				   "already selected)");
-		} else {
-			wpa_printf(MSG_DEBUG, "SAE: Use this PWE");
-			found = 1;
+			goto fail;
+		if (res > 0 && !x) {
+			wpa_printf(MSG_DEBUG,
+				   "SAE: Selected pwd-seed with counter %u",
+				   counter);
+			x = x_cand;
+			pwd_seed_odd = pwd_seed[SHA256_MAC_LEN - 1] & 0x01;
+			os_memset(pwd_seed, 0, sizeof(pwd_seed));
+
+			/*
+			 * Use a dummy password for the following rounds, if
+			 * any.
+			 */
+			addr[0] = dummy_password;
+			len[0] = dummy_password_len;
+		} else if (res > 0) {
+			crypto_bignum_deinit(x_cand, 1);
 		}
 	}
 
-	crypto_ec_point_deinit(pwe_tmp, 1);
+	if (!x) {
+		wpa_printf(MSG_DEBUG, "SAE: Could not generate PWE");
+		res = -1;
+		goto fail;
+	}
 
-	return found ? 0 : -1;
+	if (!sae->tmp->pwe_ecc)
+		sae->tmp->pwe_ecc = crypto_ec_point_init(sae->tmp->ec);
+	if (!sae->tmp->pwe_ecc)
+		res = -1;
+	else
+		res = crypto_ec_point_solve_y_coord(sae->tmp->ec,
+						    sae->tmp->pwe_ecc, x,
+						    pwd_seed_odd);
+	crypto_bignum_deinit(x, 1);
+	if (res < 0) {
+		/*
+		 * This should not happen since we already checked that there
+		 * is a result.
+		 */
+		wpa_printf(MSG_DEBUG, "SAE: Could not solve y");
+	}
+
+fail:
+	crypto_bignum_deinit(qr, 0);
+	crypto_bignum_deinit(qnr, 0);
+
+	return res;
 }
 
 
@@ -471,27 +650,41 @@
 {
 	struct crypto_bignum *mask;
 	int ret = -1;
+	unsigned int counter = 0;
 
-	mask = sae_get_rand_and_mask(sae);
-	if (mask == NULL) {
-		wpa_printf(MSG_DEBUG, "SAE: Could not get rand/mask");
-		return -1;
-	}
+	do {
+		counter++;
+		if (counter > 100) {
+			/*
+			 * This cannot really happen in practice if the random
+			 * number generator is working. Anyway, to avoid even a
+			 * theoretical infinite loop, break out after 100
+			 * attemps.
+			 */
+			return -1;
+		}
 
-	/* commit-scalar = (rand + mask) modulo r */
-	if (!sae->tmp->own_commit_scalar) {
-		sae->tmp->own_commit_scalar = crypto_bignum_init();
-		if (!sae->tmp->own_commit_scalar)
-			goto fail;
-	}
-	crypto_bignum_add(sae->tmp->sae_rand, mask,
-			  sae->tmp->own_commit_scalar);
-	crypto_bignum_mod(sae->tmp->own_commit_scalar, sae->tmp->order,
-			  sae->tmp->own_commit_scalar);
+		mask = sae_get_rand_and_mask(sae);
+		if (mask == NULL) {
+			wpa_printf(MSG_DEBUG, "SAE: Could not get rand/mask");
+			return -1;
+		}
 
-	if (sae->tmp->ec && sae_derive_commit_element_ecc(sae, mask) < 0)
-		goto fail;
-	if (sae->tmp->dh && sae_derive_commit_element_ffc(sae, mask) < 0)
+		/* commit-scalar = (rand + mask) modulo r */
+		if (!sae->tmp->own_commit_scalar) {
+			sae->tmp->own_commit_scalar = crypto_bignum_init();
+			if (!sae->tmp->own_commit_scalar)
+				goto fail;
+		}
+		crypto_bignum_add(sae->tmp->sae_rand, mask,
+				  sae->tmp->own_commit_scalar);
+		crypto_bignum_mod(sae->tmp->own_commit_scalar, sae->tmp->order,
+				  sae->tmp->own_commit_scalar);
+	} while (crypto_bignum_is_zero(sae->tmp->own_commit_scalar) ||
+		 crypto_bignum_is_one(sae->tmp->own_commit_scalar));
+
+	if ((sae->tmp->ec && sae_derive_commit_element_ecc(sae, mask) < 0) ||
+	    (sae->tmp->dh && sae_derive_commit_element_ffc(sae, mask) < 0))
 		goto fail;
 
 	ret = 0;
@@ -505,15 +698,12 @@
 		       const u8 *password, size_t password_len,
 		       struct sae_data *sae)
 {
-	if (sae->tmp == NULL)
-		return -1;
-	if (sae->tmp->ec && sae_derive_pwe_ecc(sae, addr1, addr2, password,
-					  password_len) < 0)
-		return -1;
-	if (sae->tmp->dh && sae_derive_pwe_ffc(sae, addr1, addr2, password,
-					  password_len) < 0)
-		return -1;
-	if (sae_derive_commit(sae) < 0)
+	if (sae->tmp == NULL ||
+	    (sae->tmp->ec && sae_derive_pwe_ecc(sae, addr1, addr2, password,
+						password_len) < 0) ||
+	    (sae->tmp->dh && sae_derive_pwe_ffc(sae, addr1, addr2, password,
+						password_len) < 0) ||
+	    sae_derive_commit(sae) < 0)
 		return -1;
 	return 0;
 }
@@ -623,8 +813,10 @@
 	wpa_hexdump(MSG_DEBUG, "SAE: PMKID", val, SAE_PMKID_LEN);
 	sha256_prf(keyseed, sizeof(keyseed), "SAE KCK and PMK",
 		   val, sae->tmp->prime_len, keys, sizeof(keys));
+	os_memset(keyseed, 0, sizeof(keyseed));
 	os_memcpy(sae->tmp->kck, keys, SAE_KCK_LEN);
 	os_memcpy(sae->pmk, keys + SAE_KCK_LEN, SAE_PMK_LEN);
+	os_memset(keys, 0, sizeof(keys));
 	wpa_hexdump_key(MSG_DEBUG, "SAE: KCK", sae->tmp->kck, SAE_KCK_LEN);
 	wpa_hexdump_key(MSG_DEBUG, "SAE: PMK", sae->pmk, SAE_PMK_LEN);
 
@@ -656,8 +848,11 @@
 		return;
 
 	wpabuf_put_le16(buf, sae->group); /* Finite Cyclic Group */
-	if (token)
+	if (token) {
 		wpabuf_put_buf(buf, token);
+		wpa_hexdump(MSG_DEBUG, "SAE: Anti-clogging token",
+			    wpabuf_head(token), wpabuf_len(token));
+	}
 	pos = wpabuf_put(buf, sae->tmp->prime_len);
 	crypto_bignum_to_bin(sae->tmp->own_commit_scalar, pos,
 			     sae->tmp->prime_len, sae->tmp->prime_len);
@@ -682,8 +877,7 @@
 }
 
 
-static u16 sae_group_allowed(struct sae_data *sae, int *allowed_groups,
-			     u16 group)
+u16 sae_group_allowed(struct sae_data *sae, int *allowed_groups, u16 group)
 {
 	if (allowed_groups) {
 		int i;
@@ -775,8 +969,9 @@
 		return WLAN_STATUS_UNSPECIFIED_FAILURE;
 	}
 
-	/* 0 < scalar < r */
+	/* 1 < scalar < r */
 	if (crypto_bignum_is_zero(peer_scalar) ||
+	    crypto_bignum_is_one(peer_scalar) ||
 	    crypto_bignum_cmp(peer_scalar, sae->tmp->order) >= 0) {
 		wpa_printf(MSG_DEBUG, "SAE: Invalid peer scalar");
 		crypto_bignum_deinit(peer_scalar, 0);
@@ -842,7 +1037,8 @@
 static u16 sae_parse_commit_element_ffc(struct sae_data *sae, const u8 *pos,
 					const u8 *end)
 {
-	struct crypto_bignum *res;
+	struct crypto_bignum *res, *one;
+	const u8 one_bin[1] = { 0x01 };
 
 	if (pos + sae->tmp->prime_len > end) {
 		wpa_printf(MSG_DEBUG, "SAE: Not enough data for "
@@ -857,18 +1053,23 @@
 		crypto_bignum_init_set(pos, sae->tmp->prime_len);
 	if (sae->tmp->peer_commit_element_ffc == NULL)
 		return WLAN_STATUS_UNSPECIFIED_FAILURE;
-	if (crypto_bignum_is_zero(sae->tmp->peer_commit_element_ffc) ||
+	/* 1 < element < p - 1 */
+	res = crypto_bignum_init();
+	one = crypto_bignum_init_set(one_bin, sizeof(one_bin));
+	if (!res || !one ||
+	    crypto_bignum_sub(sae->tmp->prime, one, res) ||
+	    crypto_bignum_is_zero(sae->tmp->peer_commit_element_ffc) ||
 	    crypto_bignum_is_one(sae->tmp->peer_commit_element_ffc) ||
-	    crypto_bignum_cmp(sae->tmp->peer_commit_element_ffc,
-			      sae->tmp->prime) >= 0) {
+	    crypto_bignum_cmp(sae->tmp->peer_commit_element_ffc, res) >= 0) {
+		crypto_bignum_deinit(res, 0);
+		crypto_bignum_deinit(one, 0);
 		wpa_printf(MSG_DEBUG, "SAE: Invalid peer element");
 		return WLAN_STATUS_UNSPECIFIED_FAILURE;
 	}
+	crypto_bignum_deinit(one, 0);
 
 	/* scalar-op(r, ELEMENT) = 1 modulo p */
-	res = crypto_bignum_init();
-	if (res == NULL ||
-	    crypto_bignum_exptmod(sae->tmp->peer_commit_element_ffc,
+	if (crypto_bignum_exptmod(sae->tmp->peer_commit_element_ffc,
 				  sae->tmp->order, sae->tmp->prime, res) < 0 ||
 	    !crypto_bignum_is_one(res)) {
 		wpa_printf(MSG_DEBUG, "SAE: Invalid peer element (scalar-op)");
@@ -913,7 +1114,34 @@
 		return res;
 
 	/* commit-element */
-	return sae_parse_commit_element(sae, pos, end);
+	res = sae_parse_commit_element(sae, pos, end);
+	if (res != WLAN_STATUS_SUCCESS)
+		return res;
+
+	/*
+	 * Check whether peer-commit-scalar and PEER-COMMIT-ELEMENT are same as
+	 * the values we sent which would be evidence of a reflection attack.
+	 */
+	if (!sae->tmp->own_commit_scalar ||
+	    crypto_bignum_cmp(sae->tmp->own_commit_scalar,
+			      sae->peer_commit_scalar) != 0 ||
+	    (sae->tmp->dh &&
+	     (!sae->tmp->own_commit_element_ffc ||
+	      crypto_bignum_cmp(sae->tmp->own_commit_element_ffc,
+				sae->tmp->peer_commit_element_ffc) != 0)) ||
+	    (sae->tmp->ec &&
+	     (!sae->tmp->own_commit_element_ecc ||
+	      crypto_ec_point_cmp(sae->tmp->ec,
+				  sae->tmp->own_commit_element_ecc,
+				  sae->tmp->peer_commit_element_ecc) != 0)))
+		return WLAN_STATUS_SUCCESS; /* scalars/elements are different */
+
+	/*
+	 * This is a reflection attack - return special value to trigger caller
+	 * to silently discard the frame instead of replying with a specific
+	 * status code.
+	 */
+	return SAE_SILENTLY_DISCARD;
 }
 
 
diff --git a/src/common/sae.h b/src/common/sae.h
index d82a98e..c07026c 100644
--- a/src/common/sae.h
+++ b/src/common/sae.h
@@ -18,6 +18,9 @@
 #define SAE_COMMIT_MAX_LEN (2 + 3 * SAE_MAX_PRIME_LEN)
 #define SAE_CONFIRM_MAX_LEN (2 + SAE_MAX_PRIME_LEN)
 
+/* Special value returned by sae_parse_commit() */
+#define SAE_SILENTLY_DISCARD 65535
+
 struct sae_temporary_data {
 	u8 kck[SAE_KCK_LEN];
 	struct crypto_bignum *own_commit_scalar;
@@ -35,6 +38,7 @@
 	const struct crypto_bignum *order;
 	struct crypto_bignum *prime_buf;
 	struct crypto_bignum *order_buf;
+	struct wpabuf *anti_clogging_token;
 };
 
 struct sae_data {
@@ -43,6 +47,7 @@
 	u8 pmk[SAE_PMK_LEN];
 	struct crypto_bignum *peer_commit_scalar;
 	int group;
+	int sync;
 	struct sae_temporary_data *tmp;
 };
 
@@ -60,5 +65,6 @@
 		     const u8 **token, size_t *token_len, int *allowed_groups);
 void sae_write_confirm(struct sae_data *sae, struct wpabuf *buf);
 int sae_check_confirm(struct sae_data *sae, const u8 *data, size_t len);
+u16 sae_group_allowed(struct sae_data *sae, int *allowed_groups, u16 group);
 
 #endif /* SAE_H */
diff --git a/src/common/version.h b/src/common/version.h
index 726289d..5ddf617 100644
--- a/src/common/version.h
+++ b/src/common/version.h
@@ -5,6 +5,6 @@
 #define VERSION_STR_POSTFIX ""
 #endif /* VERSION_STR_POSTFIX */
 
-#define VERSION_STR "2.3" VERSION_STR_POSTFIX
+#define VERSION_STR "2.5-devel" VERSION_STR_POSTFIX
 
 #endif /* VERSION_H */
diff --git a/src/common/wpa_common.c b/src/common/wpa_common.c
index 998a51a..e485b5b 100644
--- a/src/common/wpa_common.c
+++ b/src/common/wpa_common.c
@@ -1,6 +1,6 @@
 /*
  * WPA/RSN - Shared functions for supplicant and authenticator
- * Copyright (c) 2002-2013, 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.
@@ -12,17 +12,43 @@
 #include "crypto/md5.h"
 #include "crypto/sha1.h"
 #include "crypto/sha256.h"
+#include "crypto/sha384.h"
 #include "crypto/aes_wrap.h"
 #include "crypto/crypto.h"
-#include "drivers/driver.h"
 #include "ieee802_11_defs.h"
 #include "defs.h"
 #include "wpa_common.h"
 
 
+static unsigned int wpa_kck_len(int akmp)
+{
+	if (akmp == WPA_KEY_MGMT_IEEE8021X_SUITE_B_192)
+		return 24;
+	return 16;
+}
+
+
+static unsigned int wpa_kek_len(int akmp)
+{
+	if (akmp == WPA_KEY_MGMT_IEEE8021X_SUITE_B_192)
+		return 32;
+	return 16;
+}
+
+
+unsigned int wpa_mic_len(int akmp)
+{
+	if (akmp == WPA_KEY_MGMT_IEEE8021X_SUITE_B_192)
+		return 24;
+	return 16;
+}
+
+
 /**
  * wpa_eapol_key_mic - Calculate EAPOL-Key MIC
  * @key: EAPOL-Key Key Confirmation Key (KCK)
+ * @key_len: KCK length in octets
+ * @akmp: WPA_KEY_MGMT_* used in key derivation
  * @ver: Key descriptor version (WPA_KEY_INFO_TYPE_*)
  * @buf: Pointer to the beginning of the EAPOL header (version field)
  * @len: Length of the EAPOL frame (from EAPOL header to the end of the frame)
@@ -38,18 +64,18 @@
  * happened during final editing of the standard and the correct behavior is
  * defined in the last draft (IEEE 802.11i/D10).
  */
-int wpa_eapol_key_mic(const u8 *key, int ver, const u8 *buf, size_t len,
-		      u8 *mic)
+int wpa_eapol_key_mic(const u8 *key, size_t key_len, int akmp, int ver,
+		      const u8 *buf, size_t len, u8 *mic)
 {
-	u8 hash[SHA1_MAC_LEN];
+	u8 hash[SHA384_MAC_LEN];
 
 	switch (ver) {
 #ifndef CONFIG_FIPS
 	case WPA_KEY_INFO_TYPE_HMAC_MD5_RC4:
-		return hmac_md5(key, 16, buf, len, mic);
+		return hmac_md5(key, key_len, buf, len, mic);
 #endif /* CONFIG_FIPS */
 	case WPA_KEY_INFO_TYPE_HMAC_SHA1_AES:
-		if (hmac_sha1(key, 16, buf, len, hash))
+		if (hmac_sha1(key, key_len, buf, len, hash))
 			return -1;
 		os_memcpy(mic, hash, MD5_MAC_LEN);
 		break;
@@ -57,11 +83,30 @@
 	case WPA_KEY_INFO_TYPE_AES_128_CMAC:
 		return omac1_aes_128(key, buf, len, mic);
 #endif /* CONFIG_IEEE80211R || CONFIG_IEEE80211W */
-#ifdef CONFIG_HS20
 	case WPA_KEY_INFO_TYPE_AKM_DEFINED:
-		/* FIX: This should be based on negotiated AKM */
-		return omac1_aes_128(key, buf, len, mic);
+		switch (akmp) {
+#ifdef CONFIG_HS20
+		case WPA_KEY_MGMT_OSEN:
+			return omac1_aes_128(key, buf, len, mic);
 #endif /* CONFIG_HS20 */
+#ifdef CONFIG_SUITEB
+		case WPA_KEY_MGMT_IEEE8021X_SUITE_B:
+			if (hmac_sha256(key, key_len, buf, len, hash))
+				return -1;
+			os_memcpy(mic, hash, MD5_MAC_LEN);
+			break;
+#endif /* CONFIG_SUITEB */
+#ifdef CONFIG_SUITEB192
+		case WPA_KEY_MGMT_IEEE8021X_SUITE_B_192:
+			if (hmac_sha384(key, key_len, buf, len, hash))
+				return -1;
+			os_memcpy(mic, hash, 24);
+			break;
+#endif /* CONFIG_SUITEB192 */
+		default:
+			return -1;
+		}
+		break;
 	default:
 		return -1;
 	}
@@ -80,8 +125,9 @@
  * @nonce1: ANonce or SNonce
  * @nonce2: SNonce or ANonce
  * @ptk: Buffer for pairwise transient key
- * @ptk_len: Length of PTK
- * @use_sha256: Whether to use SHA256-based KDF
+ * @akmp: Negotiated AKM
+ * @cipher: Negotiated pairwise cipher
+ * Returns: 0 on success, -1 on failure
  *
  * IEEE Std 802.11i-2004 - 8.5.1.2 Pairwise key hierarchy
  * PTK = PRF-X(PMK, "Pairwise key expansion",
@@ -92,12 +138,14 @@
  *             Min(MAC_I, MAC_P) || Max(MAC_I, MAC_P) ||
  *             Min(INonce, PNonce) || Max(INonce, PNonce))
  */
-void wpa_pmk_to_ptk(const u8 *pmk, size_t pmk_len, const char *label,
-		    const u8 *addr1, const u8 *addr2,
-		    const u8 *nonce1, const u8 *nonce2,
-		    u8 *ptk, size_t ptk_len, int use_sha256)
+int wpa_pmk_to_ptk(const u8 *pmk, size_t pmk_len, const char *label,
+		   const u8 *addr1, const u8 *addr2,
+		   const u8 *nonce1, const u8 *nonce2,
+		   struct wpa_ptk *ptk, int akmp, int cipher)
 {
 	u8 data[2 * ETH_ALEN + 2 * WPA_NONCE_LEN];
+	u8 tmp[WPA_KCK_MAX_LEN + WPA_KEK_MAX_LEN + WPA_TK_MAX_LEN];
+	size_t ptk_len;
 
 	if (os_memcmp(addr1, addr2, ETH_ALEN) < 0) {
 		os_memcpy(data, addr1, ETH_ALEN);
@@ -117,76 +165,111 @@
 			  WPA_NONCE_LEN);
 	}
 
+	ptk->kck_len = wpa_kck_len(akmp);
+	ptk->kek_len = wpa_kek_len(akmp);
+	ptk->tk_len = wpa_cipher_key_len(cipher);
+	ptk_len = ptk->kck_len + ptk->kek_len + ptk->tk_len;
+
 #ifdef CONFIG_IEEE80211W
-	if (use_sha256)
+	if (wpa_key_mgmt_sha256(akmp))
 		sha256_prf(pmk, pmk_len, label, data, sizeof(data),
-			   ptk, ptk_len);
+			   tmp, ptk_len);
 	else
 #endif /* CONFIG_IEEE80211W */
-		sha1_prf(pmk, pmk_len, label, data, sizeof(data), ptk,
-			 ptk_len);
+		sha1_prf(pmk, pmk_len, label, data, sizeof(data), tmp, ptk_len);
 
 	wpa_printf(MSG_DEBUG, "WPA: PTK derivation - A1=" MACSTR " A2=" MACSTR,
 		   MAC2STR(addr1), MAC2STR(addr2));
 	wpa_hexdump(MSG_DEBUG, "WPA: Nonce1", nonce1, WPA_NONCE_LEN);
 	wpa_hexdump(MSG_DEBUG, "WPA: Nonce2", nonce2, WPA_NONCE_LEN);
 	wpa_hexdump_key(MSG_DEBUG, "WPA: PMK", pmk, pmk_len);
-	wpa_hexdump_key(MSG_DEBUG, "WPA: PTK", ptk, ptk_len);
+	wpa_hexdump_key(MSG_DEBUG, "WPA: PTK", tmp, ptk_len);
+
+	os_memcpy(ptk->kck, tmp, ptk->kck_len);
+	wpa_hexdump_key(MSG_DEBUG, "WPA: KCK", ptk->kck, ptk->kck_len);
+
+	os_memcpy(ptk->kek, tmp + ptk->kck_len, ptk->kek_len);
+	wpa_hexdump_key(MSG_DEBUG, "WPA: KEK", ptk->kek, ptk->kek_len);
+
+	os_memcpy(ptk->tk, tmp + ptk->kck_len + ptk->kek_len, ptk->tk_len);
+	wpa_hexdump_key(MSG_DEBUG, "WPA: TK", ptk->tk, ptk->tk_len);
+
+	os_memset(tmp, 0, sizeof(tmp));
+	return 0;
 }
 
 
 #ifdef CONFIG_IEEE80211R
-int wpa_ft_mic(const u8 *kck, const u8 *sta_addr, const u8 *ap_addr,
-	       u8 transaction_seqnum, const u8 *mdie, size_t mdie_len,
+int wpa_ft_mic(const u8 *kck, size_t kck_len, const u8 *sta_addr,
+	       const u8 *ap_addr, u8 transaction_seqnum,
+	       const u8 *mdie, size_t mdie_len,
 	       const u8 *ftie, size_t ftie_len,
 	       const u8 *rsnie, size_t rsnie_len,
 	       const u8 *ric, size_t ric_len, u8 *mic)
 {
-	u8 *buf, *pos;
-	size_t buf_len;
+	const u8 *addr[9];
+	size_t len[9];
+	size_t i, num_elem = 0;
+	u8 zero_mic[16];
 
-	buf_len = 2 * ETH_ALEN + 1 + mdie_len + ftie_len + rsnie_len + ric_len;
-	buf = os_malloc(buf_len);
-	if (buf == NULL)
+	if (kck_len != 16) {
+		wpa_printf(MSG_WARNING, "FT: Unsupported KCK length %u",
+			   (unsigned int) kck_len);
 		return -1;
+	}
 
-	pos = buf;
-	os_memcpy(pos, sta_addr, ETH_ALEN);
-	pos += ETH_ALEN;
-	os_memcpy(pos, ap_addr, ETH_ALEN);
-	pos += ETH_ALEN;
-	*pos++ = transaction_seqnum;
+	addr[num_elem] = sta_addr;
+	len[num_elem] = ETH_ALEN;
+	num_elem++;
+
+	addr[num_elem] = ap_addr;
+	len[num_elem] = ETH_ALEN;
+	num_elem++;
+
+	addr[num_elem] = &transaction_seqnum;
+	len[num_elem] = 1;
+	num_elem++;
+
 	if (rsnie) {
-		os_memcpy(pos, rsnie, rsnie_len);
-		pos += rsnie_len;
+		addr[num_elem] = rsnie;
+		len[num_elem] = rsnie_len;
+		num_elem++;
 	}
 	if (mdie) {
-		os_memcpy(pos, mdie, mdie_len);
-		pos += mdie_len;
+		addr[num_elem] = mdie;
+		len[num_elem] = mdie_len;
+		num_elem++;
 	}
 	if (ftie) {
-		struct rsn_ftie *_ftie;
-		os_memcpy(pos, ftie, ftie_len);
-		if (ftie_len < 2 + sizeof(*_ftie)) {
-			os_free(buf);
+		if (ftie_len < 2 + sizeof(struct rsn_ftie))
 			return -1;
-		}
-		_ftie = (struct rsn_ftie *) (pos + 2);
-		os_memset(_ftie->mic, 0, sizeof(_ftie->mic));
-		pos += ftie_len;
+
+		/* IE hdr and mic_control */
+		addr[num_elem] = ftie;
+		len[num_elem] = 2 + 2;
+		num_elem++;
+
+		/* MIC field with all zeros */
+		os_memset(zero_mic, 0, sizeof(zero_mic));
+		addr[num_elem] = zero_mic;
+		len[num_elem] = sizeof(zero_mic);
+		num_elem++;
+
+		/* Rest of FTIE */
+		addr[num_elem] = ftie + 2 + 2 + 16;
+		len[num_elem] = ftie_len - (2 + 2 + 16);
+		num_elem++;
 	}
 	if (ric) {
-		os_memcpy(pos, ric, ric_len);
-		pos += ric_len;
+		addr[num_elem] = ric;
+		len[num_elem] = ric_len;
+		num_elem++;
 	}
 
-	wpa_hexdump(MSG_MSGDUMP, "FT: MIC data", buf, pos - buf);
-	if (omac1_aes_128(kck, buf, pos - buf, mic)) {
-		os_free(buf);
+	for (i = 0; i < num_elem; i++)
+		wpa_hexdump(MSG_MSGDUMP, "FT: MIC data", addr[i], len[i]);
+	if (omac1_aes_128_vector(kck, num_elem, addr, len, mic))
 		return -1;
-	}
-
-	os_free(buf);
 
 	return 0;
 }
@@ -273,6 +356,8 @@
 				parse->rsn_pmkid = data.pmkid;
 			break;
 		case WLAN_EID_MOBILITY_DOMAIN:
+			if (pos[1] < sizeof(struct rsn_mdie))
+				return -1;
 			parse->mdie = pos + 2;
 			parse->mdie_len = pos[1];
 			break;
@@ -285,6 +370,8 @@
 				return -1;
 			break;
 		case WLAN_EID_TIMEOUT_INTERVAL:
+			if (pos[1] != 5)
+				break;
 			parse->tie = pos + 2;
 			parse->tie_len = pos[1];
 			break;
@@ -345,14 +432,10 @@
 {
 	if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_NONE)
 		return WPA_CIPHER_NONE;
-	if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_WEP40)
-		return WPA_CIPHER_WEP40;
 	if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_TKIP)
 		return WPA_CIPHER_TKIP;
 	if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_CCMP)
 		return WPA_CIPHER_CCMP;
-	if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_WEP104)
-		return WPA_CIPHER_WEP104;
 #ifdef CONFIG_IEEE80211W
 	if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_AES_128_CMAC)
 		return WPA_CIPHER_AES_128_CMAC;
@@ -399,6 +482,12 @@
 	if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_FT_SAE)
 		return WPA_KEY_MGMT_FT_SAE;
 #endif /* CONFIG_SAE */
+	if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_802_1X_SUITE_B)
+		return WPA_KEY_MGMT_IEEE8021X_SUITE_B;
+	if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_802_1X_SUITE_B_192)
+		return WPA_KEY_MGMT_IEEE8021X_SUITE_B_192;
+	if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_OSEN)
+		return WPA_KEY_MGMT_OSEN;
 	return 0;
 }
 
@@ -406,8 +495,6 @@
 static int wpa_cipher_valid_group(int cipher)
 {
 	return wpa_cipher_valid_pairwise(cipher) ||
-		cipher == WPA_CIPHER_WEP104 ||
-		cipher == WPA_CIPHER_WEP40 ||
 		cipher == WPA_CIPHER_GTK_NOT_USED;
 }
 
@@ -433,7 +520,6 @@
 int wpa_parse_wpa_ie_rsn(const u8 *rsn_ie, size_t rsn_ie_len,
 			 struct wpa_ie_data *data)
 {
-	const struct rsn_ie_hdr *hdr;
 	const u8 *pos;
 	int left;
 	int i, count;
@@ -463,19 +549,30 @@
 		return -1;
 	}
 
-	hdr = (const struct rsn_ie_hdr *) rsn_ie;
+	if (rsn_ie_len >= 6 && rsn_ie[1] >= 4 &&
+	    rsn_ie[1] == rsn_ie_len - 2 &&
+	    WPA_GET_BE32(&rsn_ie[2]) == OSEN_IE_VENDOR_TYPE) {
+		pos = rsn_ie + 6;
+		left = rsn_ie_len - 6;
 
-	if (hdr->elem_id != WLAN_EID_RSN ||
-	    hdr->len != rsn_ie_len - 2 ||
-	    WPA_GET_LE16(hdr->version) != RSN_VERSION) {
-		wpa_printf(MSG_DEBUG, "%s: malformed ie or unknown version",
-			   __func__);
-		return -2;
+		data->proto = WPA_PROTO_OSEN;
+	} else {
+		const struct rsn_ie_hdr *hdr;
+
+		hdr = (const struct rsn_ie_hdr *) rsn_ie;
+
+		if (hdr->elem_id != WLAN_EID_RSN ||
+		    hdr->len != rsn_ie_len - 2 ||
+		    WPA_GET_LE16(hdr->version) != RSN_VERSION) {
+			wpa_printf(MSG_DEBUG, "%s: malformed ie or unknown version",
+				   __func__);
+			return -2;
+		}
+
+		pos = (const u8 *) (hdr + 1);
+		left = rsn_ie_len - sizeof(*hdr);
 	}
 
-	pos = (const u8 *) (hdr + 1);
-	left = rsn_ie_len - sizeof(*hdr);
-
 	if (left >= RSN_SELECTOR_LEN) {
 		data->group_cipher = rsn_selector_to_bitfield(pos);
 		if (!wpa_cipher_valid_group(data->group_cipher)) {
@@ -496,7 +593,7 @@
 		count = WPA_GET_LE16(pos);
 		pos += 2;
 		left -= 2;
-		if (count == 0 || left < count * RSN_SELECTOR_LEN) {
+		if (count == 0 || count > left / RSN_SELECTOR_LEN) {
 			wpa_printf(MSG_DEBUG, "%s: ie count botch (pairwise), "
 				   "count %u left %u", __func__, count, left);
 			return -4;
@@ -524,7 +621,7 @@
 		count = WPA_GET_LE16(pos);
 		pos += 2;
 		left -= 2;
-		if (count == 0 || left < count * RSN_SELECTOR_LEN) {
+		if (count == 0 || count > left / RSN_SELECTOR_LEN) {
 			wpa_printf(MSG_DEBUG, "%s: ie count botch (key mgmt), "
 				   "count %u left %u", __func__, count, left);
 			return -6;
@@ -547,17 +644,17 @@
 	}
 
 	if (left >= 2) {
-		data->num_pmkid = WPA_GET_LE16(pos);
+		u16 num_pmkid = WPA_GET_LE16(pos);
 		pos += 2;
 		left -= 2;
-		if (left < (int) data->num_pmkid * PMKID_LEN) {
+		if (num_pmkid > (unsigned int) left / PMKID_LEN) {
 			wpa_printf(MSG_DEBUG, "%s: PMKID underflow "
-				   "(num_pmkid=%lu left=%d)",
-				   __func__, (unsigned long) data->num_pmkid,
-				   left);
+				   "(num_pmkid=%u left=%d)",
+				   __func__, num_pmkid, left);
 			data->num_pmkid = 0;
 			return -9;
 		} else {
+			data->num_pmkid = num_pmkid;
 			data->pmkid = pos;
 			pos += data->num_pmkid * PMKID_LEN;
 			left -= data->num_pmkid * PMKID_LEN;
@@ -592,14 +689,10 @@
 {
 	if (RSN_SELECTOR_GET(s) == WPA_CIPHER_SUITE_NONE)
 		return WPA_CIPHER_NONE;
-	if (RSN_SELECTOR_GET(s) == WPA_CIPHER_SUITE_WEP40)
-		return WPA_CIPHER_WEP40;
 	if (RSN_SELECTOR_GET(s) == WPA_CIPHER_SUITE_TKIP)
 		return WPA_CIPHER_TKIP;
 	if (RSN_SELECTOR_GET(s) == WPA_CIPHER_SUITE_CCMP)
 		return WPA_CIPHER_CCMP;
-	if (RSN_SELECTOR_GET(s) == WPA_CIPHER_SUITE_WEP104)
-		return WPA_CIPHER_WEP104;
 	return 0;
 }
 
@@ -634,11 +727,6 @@
 	data->num_pmkid = 0;
 	data->mgmt_group_cipher = 0;
 
-	if (wpa_ie_len == 0) {
-		/* No WPA IE - fail silently */
-		return -1;
-	}
-
 	if (wpa_ie_len < sizeof(struct wpa_ie_hdr)) {
 		wpa_printf(MSG_DEBUG, "%s: ie len too short %lu",
 			   __func__, (unsigned long) wpa_ie_len);
@@ -674,7 +762,7 @@
 		count = WPA_GET_LE16(pos);
 		pos += 2;
 		left -= 2;
-		if (count == 0 || left < count * WPA_SELECTOR_LEN) {
+		if (count == 0 || count > left / WPA_SELECTOR_LEN) {
 			wpa_printf(MSG_DEBUG, "%s: ie count botch (pairwise), "
 				   "count %u left %u", __func__, count, left);
 			return -4;
@@ -695,7 +783,7 @@
 		count = WPA_GET_LE16(pos);
 		pos += 2;
 		left -= 2;
-		if (count == 0 || left < count * WPA_SELECTOR_LEN) {
+		if (count == 0 || count > left / WPA_SELECTOR_LEN) {
 			wpa_printf(MSG_DEBUG, "%s: ie count botch (key mgmt), "
 				   "count %u left %u", __func__, count, left);
 			return -6;
@@ -739,7 +827,7 @@
 		       const u8 *mdid, const u8 *r0kh_id, size_t r0kh_id_len,
 		       const u8 *s0kh_id, u8 *pmk_r0, u8 *pmk_r0_name)
 {
-	u8 buf[1 + WPA_MAX_SSID_LEN + MOBILITY_DOMAIN_ID_LEN + 1 +
+	u8 buf[1 + SSID_MAX_LEN + MOBILITY_DOMAIN_ID_LEN + 1 +
 	       FT_R0KH_ID_MAX_LEN + ETH_ALEN];
 	u8 *pos, r0_key_data[48], hash[32];
 	const u8 *addr[2];
@@ -753,7 +841,7 @@
 	 * PMK-R0 = L(R0-Key-Data, 0, 256)
 	 * PMK-R0Name-Salt = L(R0-Key-Data, 256, 128)
 	 */
-	if (ssid_len > WPA_MAX_SSID_LEN || r0kh_id_len > FT_R0KH_ID_MAX_LEN)
+	if (ssid_len > SSID_MAX_LEN || r0kh_id_len > FT_R0KH_ID_MAX_LEN)
 		return;
 	pos = buf;
 	*pos++ = ssid_len;
@@ -844,15 +932,17 @@
  *
  * IEEE Std 802.11r-2008 - 8.5.1.5.5
  */
-void wpa_pmk_r1_to_ptk(const u8 *pmk_r1, const u8 *snonce, const u8 *anonce,
-		       const u8 *sta_addr, const u8 *bssid,
-		       const u8 *pmk_r1_name,
-		       u8 *ptk, size_t ptk_len, u8 *ptk_name)
+int wpa_pmk_r1_to_ptk(const u8 *pmk_r1, const u8 *snonce, const u8 *anonce,
+		      const u8 *sta_addr, const u8 *bssid,
+		      const u8 *pmk_r1_name,
+		      struct wpa_ptk *ptk, u8 *ptk_name, int akmp, int cipher)
 {
 	u8 buf[2 * WPA_NONCE_LEN + 2 * ETH_ALEN];
 	u8 *pos, hash[32];
 	const u8 *addr[6];
 	size_t len[6];
+	u8 tmp[WPA_KCK_MAX_LEN + WPA_KEK_MAX_LEN + WPA_TK_MAX_LEN];
+	size_t ptk_len;
 
 	/*
 	 * PTK = KDF-PTKLen(PMK-R1, "FT-PTK", SNonce || ANonce ||
@@ -868,7 +958,12 @@
 	os_memcpy(pos, sta_addr, ETH_ALEN);
 	pos += ETH_ALEN;
 
-	sha256_prf(pmk_r1, PMK_LEN, "FT-PTK", buf, pos - buf, ptk, ptk_len);
+	ptk->kck_len = wpa_kck_len(akmp);
+	ptk->kek_len = wpa_kek_len(akmp);
+	ptk->tk_len = wpa_cipher_key_len(cipher);
+	ptk_len = ptk->kck_len + ptk->kek_len + ptk->tk_len;
+
+	sha256_prf(pmk_r1, PMK_LEN, "FT-PTK", buf, pos - buf, tmp, ptk_len);
 
 	/*
 	 * PTKName = Truncate-128(SHA-256(PMKR1Name || "FT-PTKN" || SNonce ||
@@ -889,6 +984,19 @@
 
 	sha256_vector(6, addr, len, hash);
 	os_memcpy(ptk_name, hash, WPA_PMK_NAME_LEN);
+
+	os_memcpy(ptk->kck, tmp, ptk->kck_len);
+	os_memcpy(ptk->kek, tmp + ptk->kck_len, ptk->kek_len);
+	os_memcpy(ptk->tk, tmp + ptk->kck_len + ptk->kek_len, ptk->tk_len);
+
+	wpa_hexdump_key(MSG_DEBUG, "FT: KCK", ptk->kck, ptk->kck_len);
+	wpa_hexdump_key(MSG_DEBUG, "FT: KEK", ptk->kek, ptk->kek_len);
+	wpa_hexdump_key(MSG_DEBUG, "FT: TK", ptk->tk, ptk->tk_len);
+	wpa_hexdump(MSG_DEBUG, "FT: PTKName", ptk_name, WPA_PMK_NAME_LEN);
+
+	os_memset(tmp, 0, sizeof(tmp));
+
+	return 0;
 }
 
 #endif /* CONFIG_IEEE80211R */
@@ -928,6 +1036,72 @@
 }
 
 
+#ifdef CONFIG_SUITEB
+/**
+ * rsn_pmkid_suite_b - Calculate PMK identifier for Suite B AKM
+ * @kck: Key confirmation key
+ * @kck_len: Length of kck in bytes
+ * @aa: Authenticator address
+ * @spa: Supplicant address
+ * @pmkid: Buffer for PMKID
+ * Returns: 0 on success, -1 on failure
+ *
+ * IEEE Std 802.11ac-2013 - 11.6.1.3 Pairwise key hierarchy
+ * PMKID = Truncate(HMAC-SHA-256(KCK, "PMK Name" || AA || SPA))
+ */
+int rsn_pmkid_suite_b(const u8 *kck, size_t kck_len, const u8 *aa,
+		      const u8 *spa, u8 *pmkid)
+{
+	char *title = "PMK Name";
+	const u8 *addr[3];
+	const size_t len[3] = { 8, ETH_ALEN, ETH_ALEN };
+	unsigned char hash[SHA256_MAC_LEN];
+
+	addr[0] = (u8 *) title;
+	addr[1] = aa;
+	addr[2] = spa;
+
+	if (hmac_sha256_vector(kck, kck_len, 3, addr, len, hash) < 0)
+		return -1;
+	os_memcpy(pmkid, hash, PMKID_LEN);
+	return 0;
+}
+#endif /* CONFIG_SUITEB */
+
+
+#ifdef CONFIG_SUITEB192
+/**
+ * rsn_pmkid_suite_b_192 - Calculate PMK identifier for Suite B AKM
+ * @kck: Key confirmation key
+ * @kck_len: Length of kck in bytes
+ * @aa: Authenticator address
+ * @spa: Supplicant address
+ * @pmkid: Buffer for PMKID
+ * Returns: 0 on success, -1 on failure
+ *
+ * IEEE Std 802.11ac-2013 - 11.6.1.3 Pairwise key hierarchy
+ * PMKID = Truncate(HMAC-SHA-384(KCK, "PMK Name" || AA || SPA))
+ */
+int rsn_pmkid_suite_b_192(const u8 *kck, size_t kck_len, const u8 *aa,
+			  const u8 *spa, u8 *pmkid)
+{
+	char *title = "PMK Name";
+	const u8 *addr[3];
+	const size_t len[3] = { 8, ETH_ALEN, ETH_ALEN };
+	unsigned char hash[SHA384_MAC_LEN];
+
+	addr[0] = (u8 *) title;
+	addr[1] = aa;
+	addr[2] = spa;
+
+	if (hmac_sha384_vector(kck, kck_len, 3, addr, len, hash) < 0)
+		return -1;
+	os_memcpy(pmkid, hash, PMKID_LEN);
+	return 0;
+}
+#endif /* CONFIG_SUITEB192 */
+
+
 /**
  * wpa_cipher_txt - Convert cipher suite to a text string
  * @cipher: Cipher suite (WPA_CIPHER_* enum)
@@ -997,6 +1171,18 @@
 	case WPA_KEY_MGMT_PSK_SHA256:
 		return "WPA2-PSK-SHA256";
 #endif /* CONFIG_IEEE80211W */
+	case WPA_KEY_MGMT_WPS:
+		return "WPS";
+	case WPA_KEY_MGMT_SAE:
+		return "SAE";
+	case WPA_KEY_MGMT_FT_SAE:
+		return "FT-SAE";
+	case WPA_KEY_MGMT_OSEN:
+		return "OSEN";
+	case WPA_KEY_MGMT_IEEE8021X_SUITE_B:
+		return "WPA2-EAP-SUITE-B";
+	case WPA_KEY_MGMT_IEEE8021X_SUITE_B_192:
+		return "WPA2-EAP-SUITE-B-192";
 	default:
 		return "UNKNOWN";
 	}
@@ -1023,6 +1209,10 @@
 		return WLAN_AKM_SUITE_CCKM;
 	if (akm & WPA_KEY_MGMT_OSEN)
 		return WLAN_AKM_SUITE_OSEN;
+	if (akm & WPA_KEY_MGMT_IEEE8021X_SUITE_B)
+		return WLAN_AKM_SUITE_8021X_SUITE_B;
+	if (akm & WPA_KEY_MGMT_IEEE8021X_SUITE_B_192)
+		return WLAN_AKM_SUITE_8021X_SUITE_B_192;
 	return 0;
 }
 
@@ -1158,10 +1348,6 @@
 		return 16;
 	case WPA_CIPHER_TKIP:
 		return 32;
-	case WPA_CIPHER_WEP104:
-		return 13;
-	case WPA_CIPHER_WEP40:
-		return 5;
 	}
 
 	return 0;
@@ -1177,9 +1363,6 @@
 	case WPA_CIPHER_GCMP:
 	case WPA_CIPHER_TKIP:
 		return 6;
-	case WPA_CIPHER_WEP104:
-	case WPA_CIPHER_WEP40:
-		return 0;
 	}
 
 	return 0;
@@ -1199,9 +1382,6 @@
 		return WPA_ALG_GCMP;
 	case WPA_CIPHER_TKIP:
 		return WPA_ALG_TKIP;
-	case WPA_CIPHER_WEP104:
-	case WPA_CIPHER_WEP40:
-		return WPA_ALG_WEP;
 	case WPA_CIPHER_AES_128_CMAC:
 		return WPA_ALG_IGTK;
 	case WPA_CIPHER_BIP_GMAC_128:
@@ -1239,12 +1419,6 @@
 	if (cipher & WPA_CIPHER_TKIP)
 		return (proto == WPA_PROTO_RSN ?
 			RSN_CIPHER_SUITE_TKIP : WPA_CIPHER_SUITE_TKIP);
-	if (cipher & WPA_CIPHER_WEP104)
-		return (proto == WPA_PROTO_RSN ?
-			RSN_CIPHER_SUITE_WEP104 : WPA_CIPHER_SUITE_WEP104);
-	if (cipher & WPA_CIPHER_WEP40)
-		return (proto == WPA_PROTO_RSN ?
-			RSN_CIPHER_SUITE_WEP40 : WPA_CIPHER_SUITE_WEP40);
 	if (cipher & WPA_CIPHER_NONE)
 		return (proto == WPA_PROTO_RSN ?
 			RSN_CIPHER_SUITE_NONE : WPA_CIPHER_SUITE_NONE);
@@ -1348,10 +1522,6 @@
 		return WPA_CIPHER_GTK_NOT_USED;
 	if (ciphers & WPA_CIPHER_TKIP)
 		return WPA_CIPHER_TKIP;
-	if (ciphers & WPA_CIPHER_WEP104)
-		return WPA_CIPHER_WEP104;
-	if (ciphers & WPA_CIPHER_WEP40)
-		return WPA_CIPHER_WEP40;
 	return -1;
 }
 
@@ -1417,56 +1587,42 @@
 	if (ciphers & WPA_CIPHER_CCMP_256) {
 		ret = os_snprintf(pos, end - pos, "%sCCMP-256",
 				  pos == start ? "" : delim);
-		if (ret < 0 || ret >= end - pos)
+		if (os_snprintf_error(end - pos, ret))
 			return -1;
 		pos += ret;
 	}
 	if (ciphers & WPA_CIPHER_GCMP_256) {
 		ret = os_snprintf(pos, end - pos, "%sGCMP-256",
 				  pos == start ? "" : delim);
-		if (ret < 0 || ret >= end - pos)
+		if (os_snprintf_error(end - pos, ret))
 			return -1;
 		pos += ret;
 	}
 	if (ciphers & WPA_CIPHER_CCMP) {
 		ret = os_snprintf(pos, end - pos, "%sCCMP",
 				  pos == start ? "" : delim);
-		if (ret < 0 || ret >= end - pos)
+		if (os_snprintf_error(end - pos, ret))
 			return -1;
 		pos += ret;
 	}
 	if (ciphers & WPA_CIPHER_GCMP) {
 		ret = os_snprintf(pos, end - pos, "%sGCMP",
 				  pos == start ? "" : delim);
-		if (ret < 0 || ret >= end - pos)
+		if (os_snprintf_error(end - pos, ret))
 			return -1;
 		pos += ret;
 	}
 	if (ciphers & WPA_CIPHER_TKIP) {
 		ret = os_snprintf(pos, end - pos, "%sTKIP",
 				  pos == start ? "" : delim);
-		if (ret < 0 || ret >= end - pos)
-			return -1;
-		pos += ret;
-	}
-	if (ciphers & WPA_CIPHER_WEP104) {
-		ret = os_snprintf(pos, end - pos, "%sWEP104",
-				  pos == start ? "" : delim);
-		if (ret < 0 || ret >= end - pos)
-			return -1;
-		pos += ret;
-	}
-	if (ciphers & WPA_CIPHER_WEP40) {
-		ret = os_snprintf(pos, end - pos, "%sWEP40",
-				  pos == start ? "" : delim);
-		if (ret < 0 || ret >= end - pos)
+		if (os_snprintf_error(end - pos, ret))
 			return -1;
 		pos += ret;
 	}
 	if (ciphers & WPA_CIPHER_NONE) {
 		ret = os_snprintf(pos, end - pos, "%sNONE",
 				  pos == start ? "" : delim);
-		if (ret < 0 || ret >= end - pos)
+		if (os_snprintf_error(end - pos, ret))
 			return -1;
 		pos += ret;
 	}
@@ -1497,78 +1653,3 @@
 		return WPA_CIPHER_CCMP_256;
 	return WPA_CIPHER_CCMP;
 }
-
-
-static int wpa_check_wowlan_trigger(const char *start, const char *trigger,
-				    int capa_trigger, u8 *param_trigger)
-{
-	if (os_strcmp(start, trigger) != 0)
-		return 0;
-	if (!capa_trigger)
-		return 0;
-
-	*param_trigger = 1;
-	return 1;
-}
-
-
-struct wowlan_triggers *wpa_get_wowlan_triggers(const char *wowlan_triggers,
-						struct wpa_driver_capa *capa)
-{
-	struct wowlan_triggers *triggers;
-	char *start, *end, *buf;
-	int last;
-
-	if (!wowlan_triggers)
-		return NULL;
-
-	buf = os_strdup(wowlan_triggers);
-	if (buf == NULL)
-		return NULL;
-
-	triggers = os_zalloc(sizeof(*triggers));
-	if (triggers == NULL)
-		goto out;
-
-#define CHECK_TRIGGER(trigger) \
-	wpa_check_wowlan_trigger(start, #trigger,			\
-				  capa->wowlan_triggers.trigger,	\
-				  &triggers->trigger)
-
-	start = buf;
-	while (*start != '\0') {
-		while (isblank(*start))
-			start++;
-		if (*start == '\0')
-			break;
-		end = start;
-		while (!isblank(*end) && *end != '\0')
-			end++;
-		last = *end == '\0';
-		*end = '\0';
-
-		if (!CHECK_TRIGGER(any) &&
-		    !CHECK_TRIGGER(disconnect) &&
-		    !CHECK_TRIGGER(magic_pkt) &&
-		    !CHECK_TRIGGER(gtk_rekey_failure) &&
-		    !CHECK_TRIGGER(eap_identity_req) &&
-		    !CHECK_TRIGGER(four_way_handshake) &&
-		    !CHECK_TRIGGER(rfkill_release)) {
-			wpa_printf(MSG_DEBUG,
-				   "Unknown/unsupported wowlan trigger '%s'",
-				   start);
-			os_free(triggers);
-			triggers = NULL;
-			goto out;
-		}
-
-		if (last)
-			break;
-		start = end + 1;
-	}
-#undef CHECK_TRIGGER
-
-out:
-	os_free(buf);
-	return triggers;
-}
diff --git a/src/common/wpa_common.h b/src/common/wpa_common.h
index 0ef5a9d..d7a590f 100644
--- a/src/common/wpa_common.h
+++ b/src/common/wpa_common.h
@@ -1,6 +1,6 @@
 /*
  * WPA definitions shared between hostapd and wpa_supplicant
- * Copyright (c) 2002-2013, 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.
@@ -9,8 +9,6 @@
 #ifndef WPA_COMMON_H
 #define WPA_COMMON_H
 
-#define WPA_MAX_SSID_LEN 32
-
 /* IEEE 802.11i */
 #define PMKID_LEN 16
 #define PMK_LEN 32
@@ -24,8 +22,8 @@
 (WPA_CIPHER_CCMP | WPA_CIPHER_GCMP | WPA_CIPHER_TKIP | WPA_CIPHER_NONE | \
 WPA_CIPHER_GCMP_256 | WPA_CIPHER_CCMP_256)
 #define WPA_ALLOWED_GROUP_CIPHERS \
-(WPA_CIPHER_CCMP | WPA_CIPHER_GCMP | WPA_CIPHER_TKIP | WPA_CIPHER_WEP104 | \
-WPA_CIPHER_WEP40 | WPA_CIPHER_GCMP_256 | WPA_CIPHER_CCMP_256 | \
+(WPA_CIPHER_CCMP | WPA_CIPHER_GCMP | WPA_CIPHER_TKIP | \
+WPA_CIPHER_GCMP_256 | WPA_CIPHER_CCMP_256 | \
 WPA_CIPHER_GTK_NOT_USED)
 
 #define WPA_SELECTOR_LEN 4
@@ -42,13 +40,8 @@
 #define WPA_AUTH_KEY_MGMT_PSK_OVER_802_1X RSN_SELECTOR(0x00, 0x50, 0xf2, 2)
 #define WPA_AUTH_KEY_MGMT_CCKM RSN_SELECTOR(0x00, 0x40, 0x96, 0)
 #define WPA_CIPHER_SUITE_NONE RSN_SELECTOR(0x00, 0x50, 0xf2, 0)
-#define WPA_CIPHER_SUITE_WEP40 RSN_SELECTOR(0x00, 0x50, 0xf2, 1)
 #define WPA_CIPHER_SUITE_TKIP RSN_SELECTOR(0x00, 0x50, 0xf2, 2)
-#if 0
-#define WPA_CIPHER_SUITE_WRAP RSN_SELECTOR(0x00, 0x50, 0xf2, 3)
-#endif
 #define WPA_CIPHER_SUITE_CCMP RSN_SELECTOR(0x00, 0x50, 0xf2, 4)
-#define WPA_CIPHER_SUITE_WEP104 RSN_SELECTOR(0x00, 0x50, 0xf2, 5)
 
 
 #define RSN_AUTH_KEY_MGMT_UNSPEC_802_1X RSN_SELECTOR(0x00, 0x0f, 0xac, 1)
@@ -63,20 +56,18 @@
 #define RSN_AUTH_KEY_MGMT_SAE RSN_SELECTOR(0x00, 0x0f, 0xac, 8)
 #define RSN_AUTH_KEY_MGMT_FT_SAE RSN_SELECTOR(0x00, 0x0f, 0xac, 9)
 #define RSN_AUTH_KEY_MGMT_802_1X_SUITE_B RSN_SELECTOR(0x00, 0x0f, 0xac, 11)
-#define RSN_AUTH_KEY_MGMT_802_1X_SUITE_B_384 RSN_SELECTOR(0x00, 0x0f, 0xac, 12)
-#define RSN_AUTH_KEY_MGMT_FT_802_1X_SUITE_B_384 \
+#define RSN_AUTH_KEY_MGMT_802_1X_SUITE_B_192 RSN_SELECTOR(0x00, 0x0f, 0xac, 12)
+#define RSN_AUTH_KEY_MGMT_FT_802_1X_SUITE_B_192 \
 RSN_SELECTOR(0x00, 0x0f, 0xac, 13)
 #define RSN_AUTH_KEY_MGMT_CCKM RSN_SELECTOR(0x00, 0x40, 0x96, 0x00)
 #define RSN_AUTH_KEY_MGMT_OSEN RSN_SELECTOR(0x50, 0x6f, 0x9a, 0x01)
 
 #define RSN_CIPHER_SUITE_NONE RSN_SELECTOR(0x00, 0x0f, 0xac, 0)
-#define RSN_CIPHER_SUITE_WEP40 RSN_SELECTOR(0x00, 0x0f, 0xac, 1)
 #define RSN_CIPHER_SUITE_TKIP RSN_SELECTOR(0x00, 0x0f, 0xac, 2)
 #if 0
 #define RSN_CIPHER_SUITE_WRAP RSN_SELECTOR(0x00, 0x0f, 0xac, 3)
 #endif
 #define RSN_CIPHER_SUITE_CCMP RSN_SELECTOR(0x00, 0x0f, 0xac, 4)
-#define RSN_CIPHER_SUITE_WEP104 RSN_SELECTOR(0x00, 0x0f, 0xac, 5)
 #define RSN_CIPHER_SUITE_AES_128_CMAC RSN_SELECTOR(0x00, 0x0f, 0xac, 6)
 #define RSN_CIPHER_SUITE_NO_GROUP_ADDRESSED RSN_SELECTOR(0x00, 0x0f, 0xac, 7)
 #define RSN_CIPHER_SUITE_GCMP RSN_SELECTOR(0x00, 0x0f, 0xac, 8)
@@ -191,22 +182,38 @@
 	/* followed by key_data_length bytes of key_data */
 } STRUCT_PACKED;
 
+struct wpa_eapol_key_192 {
+	u8 type;
+	/* Note: key_info, key_length, and key_data_length are unaligned */
+	u8 key_info[2]; /* big endian */
+	u8 key_length[2]; /* big endian */
+	u8 replay_counter[WPA_REPLAY_COUNTER_LEN];
+	u8 key_nonce[WPA_NONCE_LEN];
+	u8 key_iv[16];
+	u8 key_rsc[WPA_KEY_RSC_LEN];
+	u8 key_id[8]; /* Reserved in IEEE 802.11i/RSN */
+	u8 key_mic[24];
+	u8 key_data_length[2]; /* big endian */
+	/* followed by key_data_length bytes of key_data */
+} STRUCT_PACKED;
+
+#define WPA_EAPOL_KEY_MIC_MAX_LEN 24
+#define WPA_KCK_MAX_LEN 24
+#define WPA_KEK_MAX_LEN 32
+#define WPA_TK_MAX_LEN 32
+
 /**
  * struct wpa_ptk - WPA Pairwise Transient Key
  * IEEE Std 802.11i-2004 - 8.5.1.2 Pairwise key hierarchy
  */
 struct wpa_ptk {
-	u8 kck[16]; /* EAPOL-Key Key Confirmation Key (KCK) */
-	u8 kek[16]; /* EAPOL-Key Key Encryption Key (KEK) */
-	u8 tk1[16]; /* Temporal Key 1 (TK1) */
-	union {
-		u8 tk2[16]; /* Temporal Key 2 (TK2) */
-		struct {
-			u8 tx_mic_key[8];
-			u8 rx_mic_key[8];
-		} auth;
-	} u;
-} STRUCT_PACKED;
+	u8 kck[WPA_KCK_MAX_LEN]; /* EAPOL-Key Key Confirmation Key (KCK) */
+	u8 kek[WPA_KEK_MAX_LEN]; /* EAPOL-Key Key Encryption Key (KEK) */
+	u8 tk[WPA_TK_MAX_LEN]; /* Temporal Key (TK) */
+	size_t kck_len;
+	size_t kek_len;
+	size_t tk_len;
+};
 
 
 /* WPA IE version 1
@@ -292,7 +299,6 @@
 } STRUCT_PACKED;
 #endif /* CONFIG_IEEE80211W */
 
-#ifdef CONFIG_IEEE80211R
 struct rsn_mdie {
 	u8 mobility_domain[MOBILITY_DOMAIN_ID_LEN];
 	u8 ft_capab;
@@ -320,23 +326,23 @@
 	le16 status_code;
 } STRUCT_PACKED;
 
-#endif /* CONFIG_IEEE80211R */
 
 #ifdef _MSC_VER
 #pragma pack(pop)
 #endif /* _MSC_VER */
 
 
-int wpa_eapol_key_mic(const u8 *key, int ver, const u8 *buf, size_t len,
-		      u8 *mic);
-void wpa_pmk_to_ptk(const u8 *pmk, size_t pmk_len, const char *label,
-		    const u8 *addr1, const u8 *addr2,
-		    const u8 *nonce1, const u8 *nonce2,
-		    u8 *ptk, size_t ptk_len, int use_sha256);
+int wpa_eapol_key_mic(const u8 *key, size_t key_len, int akmp, int ver,
+		      const u8 *buf, size_t len, u8 *mic);
+int wpa_pmk_to_ptk(const u8 *pmk, size_t pmk_len, const char *label,
+		   const u8 *addr1, const u8 *addr2,
+		   const u8 *nonce1, const u8 *nonce2,
+		   struct wpa_ptk *ptk, int akmp, int cipher);
 
 #ifdef CONFIG_IEEE80211R
-int wpa_ft_mic(const u8 *kck, const u8 *sta_addr, const u8 *ap_addr,
-	       u8 transaction_seqnum, const u8 *mdie, size_t mdie_len,
+int wpa_ft_mic(const u8 *kck, size_t kck_len, const u8 *sta_addr,
+	       const u8 *ap_addr, u8 transaction_seqnum,
+	       const u8 *mdie, size_t mdie_len,
 	       const u8 *ftie, size_t ftie_len,
 	       const u8 *rsnie, size_t rsnie_len,
 	       const u8 *ric, size_t ric_len, u8 *mic);
@@ -349,10 +355,10 @@
 void wpa_derive_pmk_r1(const u8 *pmk_r0, const u8 *pmk_r0_name,
 		       const u8 *r1kh_id, const u8 *s1kh_id,
 		       u8 *pmk_r1, u8 *pmk_r1_name);
-void wpa_pmk_r1_to_ptk(const u8 *pmk_r1, const u8 *snonce, const u8 *anonce,
-		       const u8 *sta_addr, const u8 *bssid,
-		       const u8 *pmk_r1_name,
-		       u8 *ptk, size_t ptk_len, u8 *ptk_name);
+int wpa_pmk_r1_to_ptk(const u8 *pmk_r1, const u8 *snonce, const u8 *anonce,
+		      const u8 *sta_addr, const u8 *bssid,
+		      const u8 *pmk_r1_name,
+		      struct wpa_ptk *ptk, u8 *ptk_name, int akmp, int cipher);
 #endif /* CONFIG_IEEE80211R */
 
 struct wpa_ie_data {
@@ -374,6 +380,26 @@
 
 void rsn_pmkid(const u8 *pmk, size_t pmk_len, const u8 *aa, const u8 *spa,
 	       u8 *pmkid, int use_sha256);
+#ifdef CONFIG_SUITEB
+int rsn_pmkid_suite_b(const u8 *kck, size_t kck_len, const u8 *aa,
+		       const u8 *spa, u8 *pmkid);
+#else /* CONFIG_SUITEB */
+static inline int rsn_pmkid_suite_b(const u8 *kck, size_t kck_len, const u8 *aa,
+				    const u8 *spa, u8 *pmkid)
+{
+	return -1;
+}
+#endif /* CONFIG_SUITEB */
+#ifdef CONFIG_SUITEB192
+int rsn_pmkid_suite_b_192(const u8 *kck, size_t kck_len, const u8 *aa,
+			  const u8 *spa, u8 *pmkid);
+#else /* CONFIG_SUITEB192 */
+static inline int rsn_pmkid_suite_b_192(const u8 *kck, size_t kck_len,
+					const u8 *aa, const u8 *spa, u8 *pmkid)
+{
+	return -1;
+}
+#endif /* CONFIG_SUITEB192 */
 
 const char * wpa_cipher_txt(int cipher);
 const char * wpa_key_mgmt_txt(int key_mgmt, int proto);
@@ -419,5 +445,6 @@
 int wpa_parse_cipher(const char *value);
 int wpa_write_ciphers(char *start, char *end, int ciphers, const char *delim);
 int wpa_select_ap_group_cipher(int wpa, int wpa_pairwise, int rsn_pairwise);
+unsigned int wpa_mic_len(int akmp);
 
 #endif /* WPA_COMMON_H */
diff --git a/src/common/wpa_ctrl.c b/src/common/wpa_ctrl.c
index d0d0853..82d4655 100644
--- a/src/common/wpa_ctrl.c
+++ b/src/common/wpa_ctrl.c
@@ -95,10 +95,9 @@
 	if (ctrl_path == NULL)
 		return NULL;
 
-	ctrl = os_malloc(sizeof(*ctrl));
+	ctrl = os_zalloc(sizeof(*ctrl));
 	if (ctrl == NULL)
 		return NULL;
-	os_memset(ctrl, 0, sizeof(*ctrl));
 
 	ctrl->s = socket(PF_UNIX, SOCK_DGRAM, 0);
 	if (ctrl->s < 0) {
@@ -113,7 +112,7 @@
 			  CONFIG_CTRL_IFACE_CLIENT_DIR "/"
 			  CONFIG_CTRL_IFACE_CLIENT_PREFIX "%d-%d",
 			  (int) getpid(), counter);
-	if (ret < 0 || (size_t) ret >= sizeof(ctrl->local.sun_path)) {
+	if (os_snprintf_error(sizeof(ctrl->local.sun_path), ret)) {
 		close(ctrl->s);
 		os_free(ctrl);
 		return NULL;
@@ -284,10 +283,9 @@
 	struct hostent *h;
 #endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */
 
-	ctrl = os_malloc(sizeof(*ctrl));
+	ctrl = os_zalloc(sizeof(*ctrl));
 	if (ctrl == NULL)
 		return NULL;
-	os_memset(ctrl, 0, sizeof(*ctrl));
 
 #ifdef CONFIG_CTRL_IFACE_UDP_IPV6
 	ctrl->s = socket(PF_INET6, SOCK_DGRAM, 0);
@@ -644,7 +642,7 @@
 		ret = os_snprintf(name, 256, NAMED_PIPE_PREFIX "-%s",
 				  ctrl_path);
 #endif /* UNICODE */
-	if (ret < 0 || ret >= 256) {
+	if (os_snprintf_error(256, ret)) {
 		os_free(ctrl);
 		return NULL;
 	}
diff --git a/src/common/wpa_ctrl.h b/src/common/wpa_ctrl.h
index 4812f8d..e700523 100644
--- a/src/common/wpa_ctrl.h
+++ b/src/common/wpa_ctrl.h
@@ -42,6 +42,8 @@
 #define WPA_EVENT_EAP_METHOD "CTRL-EVENT-EAP-METHOD "
 /** EAP peer certificate from TLS */
 #define WPA_EVENT_EAP_PEER_CERT "CTRL-EVENT-EAP-PEER-CERT "
+/** EAP peer certificate alternative subject name component from TLS */
+#define WPA_EVENT_EAP_PEER_ALT "CTRL-EVENT-EAP-PEER-ALT "
 /** EAP TLS certificate chain validation error */
 #define WPA_EVENT_EAP_TLS_CERT_ERROR "CTRL-EVENT-EAP-TLS-CERT-ERROR "
 /** EAP status */
@@ -58,12 +60,16 @@
 #define WPA_EVENT_SCAN_STARTED "CTRL-EVENT-SCAN-STARTED "
 /** New scan results available */
 #define WPA_EVENT_SCAN_RESULTS "CTRL-EVENT-SCAN-RESULTS "
+/** Scan command failed */
+#define WPA_EVENT_SCAN_FAILED "CTRL-EVENT-SCAN-FAILED "
 /** wpa_supplicant state change */
 #define WPA_EVENT_STATE_CHANGE "CTRL-EVENT-STATE-CHANGE "
 /** A new BSS entry was added (followed by BSS entry id and BSSID) */
 #define WPA_EVENT_BSS_ADDED "CTRL-EVENT-BSS-ADDED "
 /** A BSS entry was removed (followed by BSS entry id and BSSID) */
 #define WPA_EVENT_BSS_REMOVED "CTRL-EVENT-BSS-REMOVED "
+/** No suitable network was found */
+#define WPA_EVENT_NETWORK_NOT_FOUND "CTRL-EVENT-NETWORK-NOT-FOUND "
 /** Change in the signal level was reported by the driver */
 #define WPA_EVENT_SIGNAL_CHANGE "CTRL-EVENT-SIGNAL-CHANGE "
 /** Regulatory domain channel */
@@ -118,6 +124,20 @@
 #define WPS_EVENT_ER_AP_SETTINGS "WPS-ER-AP-SETTINGS "
 #define WPS_EVENT_ER_SET_SEL_REG "WPS-ER-AP-SET-SEL-REG "
 
+/* MESH events */
+#define MESH_GROUP_STARTED "MESH-GROUP-STARTED "
+#define MESH_GROUP_REMOVED "MESH-GROUP-REMOVED "
+#define MESH_PEER_CONNECTED "MESH-PEER-CONNECTED "
+#define MESH_PEER_DISCONNECTED "MESH-PEER-DISCONNECTED "
+/** Mesh SAE authentication failure. Wrong password suspected. */
+#define MESH_SAE_AUTH_FAILURE "MESH-SAE-AUTH-FAILURE "
+#define MESH_SAE_AUTH_BLOCKED "MESH-SAE-AUTH-BLOCKED "
+
+/* WMM AC events */
+#define WMM_AC_EVENT_TSPEC_ADDED "TSPEC-ADDED "
+#define WMM_AC_EVENT_TSPEC_REMOVED "TSPEC-REMOVED "
+#define WMM_AC_EVENT_TSPEC_REQ_FAILED "TSPEC-REQ-FAILED "
+
 /** P2P device found */
 #define P2P_EVENT_DEVICE_FOUND "P2P-DEVICE-FOUND "
 
@@ -149,6 +169,7 @@
 #define P2P_EVENT_SERV_DISC_REQ "P2P-SERV-DISC-REQ "
 /* parameters: <src addr> <update indicator> <TLVs> */
 #define P2P_EVENT_SERV_DISC_RESP "P2P-SERV-DISC-RESP "
+#define P2P_EVENT_SERV_ASP_RESP "P2P-SERV-ASP-RESP "
 #define P2P_EVENT_INVITATION_RECEIVED "P2P-INVITATION-RECEIVED "
 #define P2P_EVENT_INVITATION_RESULT "P2P-INVITATION-RESULT "
 #define P2P_EVENT_FIND_STOPPED "P2P-FIND-STOPPED "
@@ -157,11 +178,16 @@
 #define P2P_EVENT_NFC_BOTH_GO "P2P-NFC-BOTH-GO "
 #define P2P_EVENT_NFC_PEER_CLIENT "P2P-NFC-PEER-CLIENT "
 #define P2P_EVENT_NFC_WHILE_CLIENT "P2P-NFC-WHILE-CLIENT "
+#define P2P_EVENT_FALLBACK_TO_GO_NEG "P2P-FALLBACK-TO-GO-NEG "
+#define P2P_EVENT_FALLBACK_TO_GO_NEG_ENABLED "P2P-FALLBACK-TO-GO-NEG-ENABLED "
 
 /* parameters: <PMF enabled> <timeout in ms> <Session Information URL> */
 #define ESS_DISASSOC_IMMINENT "ESS-DISASSOC-IMMINENT "
 #define P2P_EVENT_REMOVE_AND_REFORM_GROUP "P2P-REMOVE-AND-REFORM-GROUP "
 
+#define P2P_EVENT_P2PS_PROVISION_START "P2PS-PROV-START "
+#define P2P_EVENT_P2PS_PROVISION_DONE "P2PS-PROV-DONE "
+
 #define INTERWORKING_AP "INTERWORKING-AP "
 #define INTERWORKING_BLACKLISTED "INTERWORKING-BLACKLISTED "
 #define INTERWORKING_NO_MATCH "INTERWORKING-NO-MATCH "
@@ -181,12 +207,18 @@
 /* parameters: <addr> <dialog_token> <freq> <status_code> <result> */
 #define GAS_QUERY_DONE "GAS-QUERY-DONE "
 
+/* parameters: <addr> <result> */
+#define ANQP_QUERY_DONE "ANQP-QUERY-DONE "
+
 #define HS20_SUBSCRIPTION_REMEDIATION "HS20-SUBSCRIPTION-REMEDIATION "
 #define HS20_DEAUTH_IMMINENT_NOTICE "HS20-DEAUTH-IMMINENT-NOTICE "
 
 #define EXT_RADIO_WORK_START "EXT-RADIO-WORK-START "
 #define EXT_RADIO_WORK_TIMEOUT "EXT-RADIO-WORK-TIMEOUT "
 
+#define RRM_EVENT_NEIGHBOR_REP_RXED "RRM-NEIGHBOR-REP-RECEIVED "
+#define RRM_EVENT_NEIGHBOR_REP_FAILED "RRM-NEIGHBOR-REP-REQUEST-FAILED "
+
 /* hostapd control interface - fixed message prefixes */
 #define WPS_EVENT_PIN_NEEDED "WPS-PIN-NEEDED "
 #define WPS_EVENT_NEW_AP_SETTINGS "WPS-NEW-AP-SETTINGS "
@@ -197,6 +229,7 @@
 #define WPS_EVENT_AP_PIN_DISABLED "WPS-AP-PIN-DISABLED "
 #define AP_STA_CONNECTED "AP-STA-CONNECTED "
 #define AP_STA_DISCONNECTED "AP-STA-DISCONNECTED "
+#define AP_STA_POSSIBLE_PSK_MISMATCH "AP-STA-POSSIBLE-PSK-MISMATCH "
 
 #define AP_REJECTED_MAX_STA "AP-REJECTED-MAX-STA "
 #define AP_REJECTED_BLOCKED_STA "AP-REJECTED-BLOCKED-STA "
@@ -204,6 +237,9 @@
 #define AP_EVENT_ENABLED "AP-ENABLED "
 #define AP_EVENT_DISABLED "AP-DISABLED "
 
+#define INTERFACE_ENABLED "INTERFACE-ENABLED "
+#define INTERFACE_DISABLED "INTERFACE-DISABLED "
+
 #define ACS_EVENT_STARTED "ACS-STARTED "
 #define ACS_EVENT_COMPLETED "ACS-COMPLETED "
 #define ACS_EVENT_FAILED "ACS-FAILED "
@@ -216,6 +252,9 @@
 
 #define AP_CSA_FINISHED "AP-CSA-FINISHED "
 
+/* BSS Transition Management Response frame received */
+#define BSS_TM_RESP "BSS-TM-RESP "
+
 /* BSS command information masks */
 
 #define WPA_BSS_MASK_ALL		0xFFFDFFFF
@@ -237,6 +276,9 @@
 #define WPA_BSS_MASK_INTERNETW		BIT(15)
 #define WPA_BSS_MASK_WIFI_DISPLAY	BIT(16)
 #define WPA_BSS_MASK_DELIM		BIT(17)
+#define WPA_BSS_MASK_MESH_SCAN		BIT(18)
+#define WPA_BSS_MASK_SNR		BIT(19)
+#define WPA_BSS_MASK_EST_THROUGHPUT	BIT(20)
 
 
 /* VENDOR_ELEM_* frame id values */
@@ -254,6 +296,7 @@
 	VENDOR_ELEM_P2P_INV_RESP = 10,
 	VENDOR_ELEM_P2P_ASSOC_REQ = 11,
 	VENDOR_ELEM_P2P_ASSOC_RESP = 12,
+	VENDOR_ELEM_ASSOC_REQ = 13,
 	NUM_VENDOR_ELEM_FRAMES
 };
 
@@ -383,8 +426,6 @@
  */
 int wpa_ctrl_get_fd(struct wpa_ctrl *ctrl);
 
-char * wpa_ctrl_get_remote_ifname(struct wpa_ctrl *ctrl);
-
 #ifdef ANDROID
 /**
  * wpa_ctrl_cleanup() - Delete any local UNIX domain socket files that
@@ -402,6 +443,8 @@
 #define WPA_CTRL_IFACE_PORT_LIMIT 50 /* decremented from start */
 #define WPA_GLOBAL_CTRL_IFACE_PORT 9878
 #define WPA_GLOBAL_CTRL_IFACE_PORT_LIMIT 20 /* incremented from start */
+
+char * wpa_ctrl_get_remote_ifname(struct wpa_ctrl *ctrl);
 #endif /* CONFIG_CTRL_IFACE_UDP */
 
 
diff --git a/src/crypto/Makefile b/src/crypto/Makefile
index 2a92109..3e90350 100644
--- a/src/crypto/Makefile
+++ b/src/crypto/Makefile
@@ -26,6 +26,7 @@
 	aes-internal-dec.o \
 	aes-internal-enc.o \
 	aes-omac1.o \
+	aes-siv.o \
 	aes-unwrap.o \
 	aes-wrap.o \
 	des-internal.o \
diff --git a/src/crypto/aes-eax.c b/src/crypto/aes-eax.c
index 21941c6..15a09f8 100644
--- a/src/crypto/aes-eax.c
+++ b/src/crypto/aes-eax.c
@@ -71,7 +71,7 @@
 
 	ret = 0;
 fail:
-	os_free(buf);
+	bin_clear_free(buf, buf_len);
 
 	return ret;
 }
diff --git a/src/crypto/aes-omac1.c b/src/crypto/aes-omac1.c
index 27895eb..375db57 100644
--- a/src/crypto/aes-omac1.c
+++ b/src/crypto/aes-omac1.c
@@ -1,5 +1,5 @@
 /*
- * One-key CBC MAC (OMAC1) hash with AES-128
+ * One-key CBC MAC (OMAC1) hash with AES
  *
  * Copyright (c) 2003-2007, Jouni Malinen <j@w1.fi>
  *
@@ -27,8 +27,9 @@
 
 
 /**
- * omac1_aes_128_vector - One-Key CBC MAC (OMAC1) hash with AES-128
- * @key: 128-bit key for the hash operation
+ * omac1_aes_vector - One-Key CBC MAC (OMAC1) hash with AES
+ * @key: Key for the hash operation
+ * @key_len: Key length in octets
  * @num_elem: Number of elements in the data vector
  * @addr: Pointers to the data areas
  * @len: Lengths of the data blocks
@@ -39,15 +40,15 @@
  * OMAC1 was standardized with the name CMAC by NIST in a Special Publication
  * (SP) 800-38B.
  */
-int omac1_aes_128_vector(const u8 *key, size_t num_elem,
-			 const u8 *addr[], const size_t *len, u8 *mac)
+int omac1_aes_vector(const u8 *key, size_t key_len, size_t num_elem,
+		     const u8 *addr[], const size_t *len, u8 *mac)
 {
 	void *ctx;
 	u8 cbc[AES_BLOCK_SIZE], pad[AES_BLOCK_SIZE];
 	const u8 *pos, *end;
 	size_t i, e, left, total_len;
 
-	ctx = aes_encrypt_init(key, 16);
+	ctx = aes_encrypt_init(key, key_len);
 	if (ctx == NULL)
 		return -1;
 	os_memset(cbc, 0, AES_BLOCK_SIZE);
@@ -65,6 +66,13 @@
 		for (i = 0; i < AES_BLOCK_SIZE; i++) {
 			cbc[i] ^= *pos++;
 			if (pos >= end) {
+				/*
+				 * Stop if there are no more bytes to process
+				 * since there are no more entries in the array.
+				 */
+				if (i + 1 == AES_BLOCK_SIZE &&
+				    left == AES_BLOCK_SIZE)
+					break;
 				e++;
 				pos = addr[e];
 				end = pos + len[e];
@@ -83,6 +91,12 @@
 		for (i = 0; i < left; i++) {
 			cbc[i] ^= *pos++;
 			if (pos >= end) {
+				/*
+				 * Stop if there are no more bytes to process
+				 * since there are no more entries in the array.
+				 */
+				if (i + 1 == left)
+					break;
 				e++;
 				pos = addr[e];
 				end = pos + len[e];
@@ -101,6 +115,26 @@
 
 
 /**
+ * omac1_aes_128_vector - One-Key CBC MAC (OMAC1) hash with AES-128
+ * @key: 128-bit key for the hash operation
+ * @num_elem: Number of elements in the data vector
+ * @addr: Pointers to the data areas
+ * @len: Lengths of the data blocks
+ * @mac: Buffer for MAC (128 bits, i.e., 16 bytes)
+ * Returns: 0 on success, -1 on failure
+ *
+ * This is a mode for using block cipher (AES in this case) for authentication.
+ * OMAC1 was standardized with the name CMAC by NIST in a Special Publication
+ * (SP) 800-38B.
+ */
+int omac1_aes_128_vector(const u8 *key, size_t num_elem,
+			 const u8 *addr[], const size_t *len, u8 *mac)
+{
+	return omac1_aes_vector(key, 16, num_elem, addr, len, mac);
+}
+
+
+/**
  * omac1_aes_128 - One-Key CBC MAC (OMAC1) hash with AES-128 (aka AES-CMAC)
  * @key: 128-bit key for the hash operation
  * @data: Data buffer for which a MAC is determined
@@ -116,3 +150,21 @@
 {
 	return omac1_aes_128_vector(key, 1, &data, &data_len, mac);
 }
+
+
+/**
+ * omac1_aes_256 - One-Key CBC MAC (OMAC1) hash with AES-256 (aka AES-CMAC)
+ * @key: 256-bit key for the hash operation
+ * @data: Data buffer for which a MAC is determined
+ * @data_len: Length of data buffer in bytes
+ * @mac: Buffer for MAC (128 bits, i.e., 16 bytes)
+ * Returns: 0 on success, -1 on failure
+ *
+ * This is a mode for using block cipher (AES in this case) for authentication.
+ * OMAC1 was standardized with the name CMAC by NIST in a Special Publication
+ * (SP) 800-38B.
+ */
+int omac1_aes_256(const u8 *key, const u8 *data, size_t data_len, u8 *mac)
+{
+	return omac1_aes_vector(key, 32, 1, &data, &data_len, mac);
+}
diff --git a/src/crypto/aes-siv.c b/src/crypto/aes-siv.c
new file mode 100644
index 0000000..5ac82c2
--- /dev/null
+++ b/src/crypto/aes-siv.c
@@ -0,0 +1,188 @@
+/*
+ * AES SIV (RFC 5297)
+ * Copyright (c) 2013 Cozybit, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "aes.h"
+#include "aes_wrap.h"
+#include "aes_siv.h"
+
+
+static const u8 zero[AES_BLOCK_SIZE];
+
+
+static void dbl(u8 *pad)
+{
+	int i, carry;
+
+	carry = pad[0] & 0x80;
+	for (i = 0; i < AES_BLOCK_SIZE - 1; i++)
+		pad[i] = (pad[i] << 1) | (pad[i + 1] >> 7);
+	pad[AES_BLOCK_SIZE - 1] <<= 1;
+	if (carry)
+		pad[AES_BLOCK_SIZE - 1] ^= 0x87;
+}
+
+
+static void xor(u8 *a, const u8 *b)
+{
+	int i;
+
+	for (i = 0; i < AES_BLOCK_SIZE; i++)
+		*a++ ^= *b++;
+}
+
+
+static void xorend(u8 *a, int alen, const u8 *b, int blen)
+{
+	int i;
+
+	if (alen < blen)
+		return;
+
+	for (i = 0; i < blen; i++)
+		a[alen - blen + i] ^= b[i];
+}
+
+
+static void pad_block(u8 *pad, const u8 *addr, size_t len)
+{
+	os_memset(pad, 0, AES_BLOCK_SIZE);
+	os_memcpy(pad, addr, len);
+
+	if (len < AES_BLOCK_SIZE)
+		pad[len] = 0x80;
+}
+
+
+static int aes_s2v(const u8 *key, size_t num_elem, const u8 *addr[],
+		   size_t *len, u8 *mac)
+{
+	u8 tmp[AES_BLOCK_SIZE], tmp2[AES_BLOCK_SIZE];
+	u8 *buf = NULL;
+	int ret;
+	size_t i;
+
+	if (!num_elem) {
+		os_memcpy(tmp, zero, sizeof(zero));
+		tmp[AES_BLOCK_SIZE - 1] = 1;
+		return omac1_aes_128(key, tmp, sizeof(tmp), mac);
+	}
+
+	ret = omac1_aes_128(key, zero, sizeof(zero), tmp);
+	if (ret)
+		return ret;
+
+	for (i = 0; i < num_elem - 1; i++) {
+		ret = omac1_aes_128(key, addr[i], len[i], tmp2);
+		if (ret)
+			return ret;
+
+		dbl(tmp);
+		xor(tmp, tmp2);
+	}
+	if (len[i] >= AES_BLOCK_SIZE) {
+		buf = os_malloc(len[i]);
+		if (!buf)
+			return -ENOMEM;
+
+		os_memcpy(buf, addr[i], len[i]);
+		xorend(buf, len[i], tmp, AES_BLOCK_SIZE);
+		ret = omac1_aes_128(key, buf, len[i], mac);
+		bin_clear_free(buf, len[i]);
+		return ret;
+	}
+
+	dbl(tmp);
+	pad_block(tmp2, addr[i], len[i]);
+	xor(tmp, tmp2);
+
+	return omac1_aes_128(key, tmp, sizeof(tmp), mac);
+}
+
+
+int aes_siv_encrypt(const u8 *key, const u8 *pw,
+		    size_t pwlen, size_t num_elem,
+		    const u8 *addr[], const size_t *len, u8 *out)
+{
+	const u8 *_addr[6];
+	size_t _len[6];
+	const u8 *k1 = key, *k2 = key + 16;
+	u8 v[AES_BLOCK_SIZE];
+	size_t i;
+	u8 *iv, *crypt_pw;
+
+	if (num_elem > ARRAY_SIZE(_addr) - 1)
+		return -1;
+
+	for (i = 0; i < num_elem; i++) {
+		_addr[i] = addr[i];
+		_len[i] = len[i];
+	}
+	_addr[num_elem] = pw;
+	_len[num_elem] = pwlen;
+
+	if (aes_s2v(k1, num_elem + 1, _addr, _len, v))
+		return -1;
+
+	iv = out;
+	crypt_pw = out + AES_BLOCK_SIZE;
+
+	os_memcpy(iv, v, AES_BLOCK_SIZE);
+	os_memcpy(crypt_pw, pw, pwlen);
+
+	/* zero out 63rd and 31st bits of ctr (from right) */
+	v[8] &= 0x7f;
+	v[12] &= 0x7f;
+	return aes_128_ctr_encrypt(k2, v, crypt_pw, pwlen);
+}
+
+
+int aes_siv_decrypt(const u8 *key, const u8 *iv_crypt, size_t iv_c_len,
+		    size_t num_elem, const u8 *addr[], const size_t *len,
+		    u8 *out)
+{
+	const u8 *_addr[6];
+	size_t _len[6];
+	const u8 *k1 = key, *k2 = key + 16;
+	size_t crypt_len;
+	size_t i;
+	int ret;
+	u8 iv[AES_BLOCK_SIZE];
+	u8 check[AES_BLOCK_SIZE];
+
+	if (iv_c_len < AES_BLOCK_SIZE || num_elem > ARRAY_SIZE(_addr) - 1)
+		return -1;
+	crypt_len = iv_c_len - AES_BLOCK_SIZE;
+
+	for (i = 0; i < num_elem; i++) {
+		_addr[i] = addr[i];
+		_len[i] = len[i];
+	}
+	_addr[num_elem] = out;
+	_len[num_elem] = crypt_len;
+
+	os_memcpy(iv, iv_crypt, AES_BLOCK_SIZE);
+	os_memcpy(out, iv_crypt + AES_BLOCK_SIZE, crypt_len);
+
+	iv[8] &= 0x7f;
+	iv[12] &= 0x7f;
+
+	ret = aes_128_ctr_encrypt(k2, iv, out, crypt_len);
+	if (ret)
+		return ret;
+
+	ret = aes_s2v(k1, num_elem + 1, _addr, _len, check);
+	if (ret)
+		return ret;
+	if (os_memcmp(check, iv_crypt, AES_BLOCK_SIZE) == 0)
+		return 0;
+
+	return -1;
+}
diff --git a/src/crypto/aes_siv.h b/src/crypto/aes_siv.h
new file mode 100644
index 0000000..463cf65
--- /dev/null
+++ b/src/crypto/aes_siv.h
@@ -0,0 +1,19 @@
+/*
+ * AES SIV (RFC 5297)
+ * Copyright (c) 2013 Cozybit, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef AES_SIV_H
+#define AES_SIV_H
+
+int aes_siv_encrypt(const u8 *key, const u8 *pw,
+		    size_t pwlen, size_t num_elem,
+		    const u8 *addr[], const size_t *len, u8 *out);
+int aes_siv_decrypt(const u8 *key, const u8 *iv_crypt, size_t iv_c_len,
+		    size_t num_elem, const u8 *addr[], const size_t *len,
+		    u8 *out);
+
+#endif /* AES_SIV_H */
diff --git a/src/crypto/aes_wrap.h b/src/crypto/aes_wrap.h
index 6b3727c..4a14209 100644
--- a/src/crypto/aes_wrap.h
+++ b/src/crypto/aes_wrap.h
@@ -2,7 +2,7 @@
  * AES-based functions
  *
  * - AES Key Wrap Algorithm (RFC3394)
- * - One-Key CBC MAC (OMAC1) hash with AES-128
+ * - One-Key CBC MAC (OMAC1) hash with AES-128 and AES-256
  * - AES-128 CTR mode encryption
  * - AES-128 EAX mode encryption/decryption
  * - AES-128 CBC
@@ -22,11 +22,16 @@
 			  u8 *cipher);
 int __must_check aes_unwrap(const u8 *kek, size_t kek_len, int n,
 			    const u8 *cipher, u8 *plain);
+int __must_check omac1_aes_vector(const u8 *key, size_t key_len,
+				  size_t num_elem, const u8 *addr[],
+				  const size_t *len, u8 *mac);
 int __must_check omac1_aes_128_vector(const u8 *key, size_t num_elem,
 				      const u8 *addr[], const size_t *len,
 				      u8 *mac);
 int __must_check omac1_aes_128(const u8 *key, const u8 *data, size_t data_len,
 			       u8 *mac);
+int __must_check omac1_aes_256(const u8 *key, const u8 *data, size_t data_len,
+			       u8 *mac);
 int __must_check aes_128_encrypt_block(const u8 *key, const u8 *in, u8 *out);
 int __must_check aes_128_ctr_encrypt(const u8 *key, const u8 *nonce,
 				     u8 *data, size_t data_len);
diff --git a/src/crypto/crypto.h b/src/crypto/crypto.h
index f2d5662..534c4bd 100644
--- a/src/crypto/crypto.h
+++ b/src/crypto/crypto.h
@@ -614,6 +614,15 @@
 int crypto_bignum_is_one(const struct crypto_bignum *a);
 
 /**
+ * crypto_bignum_legendre - Compute the Legendre symbol (a/p)
+ * @a: Bignum
+ * @p: Bignum
+ * Returns: Legendre symbol -1,0,1 on success; -2 on calculation failure
+ */
+int crypto_bignum_legendre(const struct crypto_bignum *a,
+			   const struct crypto_bignum *p);
+
+/**
  * struct crypto_ec - Elliptic curve context
  *
  * Internal data structure for EC implementation. The contents is specific
@@ -758,6 +767,16 @@
 				  const struct crypto_bignum *x, int y_bit);
 
 /**
+ * crypto_ec_point_compute_y_sqr - Compute y^2 = x^3 + ax + b
+ * @e: EC context from crypto_ec_init()
+ * @x: x coordinate
+ * Returns: y^2 on success, %NULL failure
+ */
+struct crypto_bignum *
+crypto_ec_point_compute_y_sqr(struct crypto_ec *e,
+			      const struct crypto_bignum *x);
+
+/**
  * crypto_ec_point_is_at_infinity - Check whether EC point is neutral element
  * @e: EC context from crypto_ec_init()
  * @p: EC point
@@ -776,4 +795,15 @@
 int crypto_ec_point_is_on_curve(struct crypto_ec *e,
 				const struct crypto_ec_point *p);
 
+/**
+ * crypto_ec_point_cmp - Compare two EC points
+ * @e: EC context from crypto_ec_init()
+ * @a: EC point
+ * @b: EC point
+ * Returns: 0 on equal, non-zero otherwise
+ */
+int crypto_ec_point_cmp(const struct crypto_ec *e,
+			const struct crypto_ec_point *a,
+			const struct crypto_ec_point *b);
+
 #endif /* CRYPTO_H */
diff --git a/src/crypto/crypto_cryptoapi.c b/src/crypto/crypto_cryptoapi.c
deleted file mode 100644
index 55a069b..0000000
--- a/src/crypto/crypto_cryptoapi.c
+++ /dev/null
@@ -1,783 +0,0 @@
-/*
- * Crypto wrapper for Microsoft CryptoAPI
- * Copyright (c) 2005-2009, Jouni Malinen <j@w1.fi>
- *
- * This software may be distributed under the terms of the BSD license.
- * See README for more details.
- */
-
-#include "includes.h"
-#include <windows.h>
-#include <wincrypt.h>
-
-#include "common.h"
-#include "crypto.h"
-
-#ifndef MS_ENH_RSA_AES_PROV
-#ifdef UNICODE
-#define MS_ENH_RSA_AES_PROV \
-L"Microsoft Enhanced RSA and AES Cryptographic Provider (Prototype)"
-#else
-#define MS_ENH_RSA_AES_PROV \
-"Microsoft Enhanced RSA and AES Cryptographic Provider (Prototype)"
-#endif
-#endif /* MS_ENH_RSA_AES_PROV */
-
-#ifndef CALG_HMAC
-#define CALG_HMAC (ALG_CLASS_HASH | ALG_TYPE_ANY | ALG_SID_HMAC)
-#endif
-
-#ifdef __MINGW32_VERSION
-/*
- * MinGW does not yet include all the needed definitions for CryptoAPI, so
- * define here whatever extra is needed.
- */
-
-static BOOL WINAPI
-(*CryptImportPublicKeyInfo)(HCRYPTPROV hCryptProv, DWORD dwCertEncodingType,
-			    PCERT_PUBLIC_KEY_INFO pInfo, HCRYPTKEY *phKey)
-= NULL; /* to be loaded from crypt32.dll */
-
-
-static int mingw_load_crypto_func(void)
-{
-	HINSTANCE dll;
-
-	/* MinGW does not yet have full CryptoAPI support, so load the needed
-	 * function here. */
-
-	if (CryptImportPublicKeyInfo)
-		return 0;
-
-	dll = LoadLibrary("crypt32");
-	if (dll == NULL) {
-		wpa_printf(MSG_DEBUG, "CryptoAPI: Could not load crypt32 "
-			   "library");
-		return -1;
-	}
-
-	CryptImportPublicKeyInfo = GetProcAddress(
-		dll, "CryptImportPublicKeyInfo");
-	if (CryptImportPublicKeyInfo == NULL) {
-		wpa_printf(MSG_DEBUG, "CryptoAPI: Could not get "
-			   "CryptImportPublicKeyInfo() address from "
-			   "crypt32 library");
-		return -1;
-	}
-
-	return 0;
-}
-
-#else /* __MINGW32_VERSION */
-
-static int mingw_load_crypto_func(void)
-{
-	return 0;
-}
-
-#endif /* __MINGW32_VERSION */
-
-
-static void cryptoapi_report_error(const char *msg)
-{
-	char *s, *pos;
-	DWORD err = GetLastError();
-
-	if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
-			  FORMAT_MESSAGE_FROM_SYSTEM,
-			  NULL, err, 0, (LPTSTR) &s, 0, NULL) == 0) {
- 		wpa_printf(MSG_DEBUG, "CryptoAPI: %s: %d", msg, (int) err);
-	}
-
-	pos = s;
-	while (*pos) {
-		if (*pos == '\n' || *pos == '\r') {
-			*pos = '\0';
-			break;
-		}
-		pos++;
-	}
-
-	wpa_printf(MSG_DEBUG, "CryptoAPI: %s: %d: (%s)", msg, (int) err, s);
-	LocalFree(s);
-}
-
-
-int cryptoapi_hash_vector(ALG_ID alg, size_t hash_len, size_t num_elem,
-			  const u8 *addr[], const size_t *len, u8 *mac)
-{
-	HCRYPTPROV prov;
-	HCRYPTHASH hash;
-	size_t i;
-	DWORD hlen;
-	int ret = 0;
-
-	if (!CryptAcquireContext(&prov, NULL, NULL, PROV_RSA_FULL, 0)) {
-		cryptoapi_report_error("CryptAcquireContext");
-		return -1;
-	}
-
-	if (!CryptCreateHash(prov, alg, 0, 0, &hash)) {
-		cryptoapi_report_error("CryptCreateHash");
-		CryptReleaseContext(prov, 0);
-		return -1;
-	}
-
-	for (i = 0; i < num_elem; i++) {
-		if (!CryptHashData(hash, (BYTE *) addr[i], len[i], 0)) {
-			cryptoapi_report_error("CryptHashData");
-			CryptDestroyHash(hash);
-			CryptReleaseContext(prov, 0);
-		}
-	}
-
-	hlen = hash_len;
-	if (!CryptGetHashParam(hash, HP_HASHVAL, mac, &hlen, 0)) {
-		cryptoapi_report_error("CryptGetHashParam");
-		ret = -1;
-	}
-
-	CryptDestroyHash(hash);
-	CryptReleaseContext(prov, 0);
-
-	return ret;
-}
-
-
-int md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
-{
-	return cryptoapi_hash_vector(CALG_MD4, 16, num_elem, addr, len, mac);
-}
-
-
-void des_encrypt(const u8 *clear, const u8 *key, u8 *cypher)
-{
-	u8 next, tmp;
-	int i;
-	HCRYPTPROV prov;
-	HCRYPTKEY ckey;
-	DWORD dlen;
-	struct {
-		BLOBHEADER hdr;
-		DWORD len;
-		BYTE key[8];
-	} key_blob;
-	DWORD mode = CRYPT_MODE_ECB;
-
-	key_blob.hdr.bType = PLAINTEXTKEYBLOB;
-	key_blob.hdr.bVersion = CUR_BLOB_VERSION;
-	key_blob.hdr.reserved = 0;
-	key_blob.hdr.aiKeyAlg = CALG_DES;
-	key_blob.len = 8;
-
-	/* Add parity bits to the key */
-	next = 0;
-	for (i = 0; i < 7; i++) {
-		tmp = key[i];
-		key_blob.key[i] = (tmp >> i) | next | 1;
-		next = tmp << (7 - i);
-	}
-	key_blob.key[i] = next | 1;
-
-	if (!CryptAcquireContext(&prov, NULL, MS_ENHANCED_PROV, PROV_RSA_FULL,
-				 CRYPT_VERIFYCONTEXT)) {
- 		wpa_printf(MSG_DEBUG, "CryptoAPI: CryptAcquireContext failed: "
-			   "%d", (int) GetLastError());
-		return;
-	}
-
-	if (!CryptImportKey(prov, (BYTE *) &key_blob, sizeof(key_blob), 0, 0,
-			    &ckey)) {
- 		wpa_printf(MSG_DEBUG, "CryptoAPI: CryptImportKey failed: %d",
-			   (int) GetLastError());
-		CryptReleaseContext(prov, 0);
-		return;
-	}
-
-	if (!CryptSetKeyParam(ckey, KP_MODE, (BYTE *) &mode, 0)) {
- 		wpa_printf(MSG_DEBUG, "CryptoAPI: CryptSetKeyParam(KP_MODE) "
-			   "failed: %d", (int) GetLastError());
-		CryptDestroyKey(ckey);
-		CryptReleaseContext(prov, 0);
-		return;
-	}
-
-	os_memcpy(cypher, clear, 8);
-	dlen = 8;
-	if (!CryptEncrypt(ckey, 0, FALSE, 0, cypher, &dlen, 8)) {
-		wpa_printf(MSG_DEBUG, "CryptoAPI: CryptEncrypt failed: %d",
-			   (int) GetLastError());
-		os_memset(cypher, 0, 8);
-	}
-
-	CryptDestroyKey(ckey);
-	CryptReleaseContext(prov, 0);
-}
-
-
-int md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
-{
-	return cryptoapi_hash_vector(CALG_MD5, 16, num_elem, addr, len, mac);
-}
-
-
-int sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
-{
-	return cryptoapi_hash_vector(CALG_SHA, 20, num_elem, addr, len, mac);
-}
-
-
-struct aes_context {
-	HCRYPTPROV prov;
-	HCRYPTKEY ckey;
-};
-
-
-void * aes_encrypt_init(const u8 *key, size_t len)
-{
-	struct aes_context *akey;
-	struct {
-		BLOBHEADER hdr;
-		DWORD len;
-		BYTE key[16];
-	} key_blob;
-	DWORD mode = CRYPT_MODE_ECB;
-
-	if (len != 16)
-		return NULL;
-
-	key_blob.hdr.bType = PLAINTEXTKEYBLOB;
-	key_blob.hdr.bVersion = CUR_BLOB_VERSION;
-	key_blob.hdr.reserved = 0;
-	key_blob.hdr.aiKeyAlg = CALG_AES_128;
-	key_blob.len = len;
-	os_memcpy(key_blob.key, key, len);
-
-	akey = os_zalloc(sizeof(*akey));
-	if (akey == NULL)
-		return NULL;
-
-	if (!CryptAcquireContext(&akey->prov, NULL,
-				 MS_ENH_RSA_AES_PROV, PROV_RSA_AES,
-				 CRYPT_VERIFYCONTEXT)) {
- 		wpa_printf(MSG_DEBUG, "CryptoAPI: CryptAcquireContext failed: "
-			   "%d", (int) GetLastError());
-		os_free(akey);
-		return NULL;
-	}
-
-	if (!CryptImportKey(akey->prov, (BYTE *) &key_blob, sizeof(key_blob),
-			    0, 0, &akey->ckey)) {
- 		wpa_printf(MSG_DEBUG, "CryptoAPI: CryptImportKey failed: %d",
-			   (int) GetLastError());
-		CryptReleaseContext(akey->prov, 0);
-		os_free(akey);
-		return NULL;
-	}
-
-	if (!CryptSetKeyParam(akey->ckey, KP_MODE, (BYTE *) &mode, 0)) {
- 		wpa_printf(MSG_DEBUG, "CryptoAPI: CryptSetKeyParam(KP_MODE) "
-			   "failed: %d", (int) GetLastError());
-		CryptDestroyKey(akey->ckey);
-		CryptReleaseContext(akey->prov, 0);
-		os_free(akey);
-		return NULL;
-	}
-
-	return akey;
-}
-
-
-void aes_encrypt(void *ctx, const u8 *plain, u8 *crypt)
-{
-	struct aes_context *akey = ctx;
-	DWORD dlen;
-
-	os_memcpy(crypt, plain, 16);
-	dlen = 16;
-	if (!CryptEncrypt(akey->ckey, 0, FALSE, 0, crypt, &dlen, 16)) {
-		wpa_printf(MSG_DEBUG, "CryptoAPI: CryptEncrypt failed: %d",
-			   (int) GetLastError());
-		os_memset(crypt, 0, 16);
-	}
-}
-
-
-void aes_encrypt_deinit(void *ctx)
-{
-	struct aes_context *akey = ctx;
-	if (akey) {
-		CryptDestroyKey(akey->ckey);
-		CryptReleaseContext(akey->prov, 0);
-		os_free(akey);
-	}
-}
-
-
-void * aes_decrypt_init(const u8 *key, size_t len)
-{
-	return aes_encrypt_init(key, len);
-}
-
-
-void aes_decrypt(void *ctx, const u8 *crypt, u8 *plain)
-{
-	struct aes_context *akey = ctx;
-	DWORD dlen;
-
-	os_memcpy(plain, crypt, 16);
-	dlen = 16;
-
-	if (!CryptDecrypt(akey->ckey, 0, FALSE, 0, plain, &dlen)) {
-		wpa_printf(MSG_DEBUG, "CryptoAPI: CryptDecrypt failed: %d",
-			   (int) GetLastError());
-	}
-}
-
-
-void aes_decrypt_deinit(void *ctx)
-{
-	aes_encrypt_deinit(ctx);
-}
-
-
-struct crypto_hash {
-	enum crypto_hash_alg alg;
-	int error;
-	HCRYPTPROV prov;
-	HCRYPTHASH hash;
-	HCRYPTKEY key;
-};
-
-struct crypto_hash * crypto_hash_init(enum crypto_hash_alg alg, const u8 *key,
-				      size_t key_len)
-{
-	struct crypto_hash *ctx;
-	ALG_ID calg;
-	struct {
-		BLOBHEADER hdr;
-		DWORD len;
-		BYTE key[32];
-	} key_blob;
-
-	os_memset(&key_blob, 0, sizeof(key_blob));
-	switch (alg) {
-	case CRYPTO_HASH_ALG_MD5:
-		calg = CALG_MD5;
-		break;
-	case CRYPTO_HASH_ALG_SHA1:
-		calg = CALG_SHA;
-		break;
-	case CRYPTO_HASH_ALG_HMAC_MD5:
-	case CRYPTO_HASH_ALG_HMAC_SHA1:
-		calg = CALG_HMAC;
-		key_blob.hdr.bType = PLAINTEXTKEYBLOB;
-		key_blob.hdr.bVersion = CUR_BLOB_VERSION;
-		key_blob.hdr.reserved = 0;
-		/*
-		 * Note: RC2 is not really used, but that can be used to
-		 * import HMAC keys of up to 16 byte long.
-		 * CRYPT_IPSEC_HMAC_KEY flag for CryptImportKey() is needed to
-		 * be able to import longer keys (HMAC-SHA1 uses 20-byte key).
-		 */
-		key_blob.hdr.aiKeyAlg = CALG_RC2;
-		key_blob.len = key_len;
-		if (key_len > sizeof(key_blob.key))
-			return NULL;
-		os_memcpy(key_blob.key, key, key_len);
-		break;
-	default:
-		return NULL;
-	}
-
-	ctx = os_zalloc(sizeof(*ctx));
-	if (ctx == NULL)
-		return NULL;
-
-	ctx->alg = alg;
-
-	if (!CryptAcquireContext(&ctx->prov, NULL, NULL, PROV_RSA_FULL, 0)) {
-		cryptoapi_report_error("CryptAcquireContext");
-		os_free(ctx);
-		return NULL;
-	}
-
-	if (calg == CALG_HMAC) {
-#ifndef CRYPT_IPSEC_HMAC_KEY
-#define CRYPT_IPSEC_HMAC_KEY 0x00000100
-#endif
-		if (!CryptImportKey(ctx->prov, (BYTE *) &key_blob,
-				    sizeof(key_blob), 0, CRYPT_IPSEC_HMAC_KEY,
-				    &ctx->key)) {
-			cryptoapi_report_error("CryptImportKey");
-			CryptReleaseContext(ctx->prov, 0);
-			os_free(ctx);
-			return NULL;
-		}
-	}
-
-	if (!CryptCreateHash(ctx->prov, calg, ctx->key, 0, &ctx->hash)) {
-		cryptoapi_report_error("CryptCreateHash");
-		CryptReleaseContext(ctx->prov, 0);
-		os_free(ctx);
-		return NULL;
-	}
-
-	if (calg == CALG_HMAC) {
-		HMAC_INFO info;
-		os_memset(&info, 0, sizeof(info));
-		switch (alg) {
-		case CRYPTO_HASH_ALG_HMAC_MD5:
-			info.HashAlgid = CALG_MD5;
-			break;
-		case CRYPTO_HASH_ALG_HMAC_SHA1:
-			info.HashAlgid = CALG_SHA;
-			break;
-		default:
-			/* unreachable */
-			break;
-		}
-
-		if (!CryptSetHashParam(ctx->hash, HP_HMAC_INFO, (BYTE *) &info,
-				       0)) {
-			cryptoapi_report_error("CryptSetHashParam");
-			CryptDestroyHash(ctx->hash);
-			CryptReleaseContext(ctx->prov, 0);
-			os_free(ctx);
-			return NULL;
-		}
-	}
-
-	return ctx;
-}
-
-
-void crypto_hash_update(struct crypto_hash *ctx, const u8 *data, size_t len)
-{
-	if (ctx == NULL || ctx->error)
-		return;
-
-	if (!CryptHashData(ctx->hash, (BYTE *) data, len, 0)) {
-		cryptoapi_report_error("CryptHashData");
-		ctx->error = 1;
-	}
-}
-
-
-int crypto_hash_finish(struct crypto_hash *ctx, u8 *mac, size_t *len)
-{
-	int ret = 0;
-	DWORD hlen;
-
-	if (ctx == NULL)
-		return -2;
-
-	if (mac == NULL || len == NULL)
-		goto done;
-
-	if (ctx->error) {
-		ret = -2;
-		goto done;
-	}
-
-	hlen = *len;
-	if (!CryptGetHashParam(ctx->hash, HP_HASHVAL, mac, &hlen, 0)) {
-		cryptoapi_report_error("CryptGetHashParam");
-		ret = -2;
-	}
-	*len = hlen;
-
-done:
-	if (ctx->alg == CRYPTO_HASH_ALG_HMAC_SHA1 ||
-	    ctx->alg == CRYPTO_HASH_ALG_HMAC_MD5)
-		CryptDestroyKey(ctx->key);
-
-	os_free(ctx);
-
-	return ret;
-}
-
-
-struct crypto_cipher {
-	HCRYPTPROV prov;
-	HCRYPTKEY key;
-};
-
-
-struct crypto_cipher * crypto_cipher_init(enum crypto_cipher_alg alg,
-					  const u8 *iv, const u8 *key,
-					  size_t key_len)
-{	
-	struct crypto_cipher *ctx;
-	struct {
-		BLOBHEADER hdr;
-		DWORD len;
-		BYTE key[32];
-	} key_blob;
-	DWORD mode = CRYPT_MODE_CBC;
-
-	key_blob.hdr.bType = PLAINTEXTKEYBLOB;
-	key_blob.hdr.bVersion = CUR_BLOB_VERSION;
-	key_blob.hdr.reserved = 0;
-	key_blob.len = key_len;
-	if (key_len > sizeof(key_blob.key))
-		return NULL;
-	os_memcpy(key_blob.key, key, key_len);
-
-	switch (alg) {
-	case CRYPTO_CIPHER_ALG_AES:
-		if (key_len == 32)
-			key_blob.hdr.aiKeyAlg = CALG_AES_256;
-		else if (key_len == 24)
-			key_blob.hdr.aiKeyAlg = CALG_AES_192;
-		else
-			key_blob.hdr.aiKeyAlg = CALG_AES_128;
-		break;
-	case CRYPTO_CIPHER_ALG_3DES:
-		key_blob.hdr.aiKeyAlg = CALG_3DES;
-		break;
-	case CRYPTO_CIPHER_ALG_DES:
-		key_blob.hdr.aiKeyAlg = CALG_DES;
-		break;
-	case CRYPTO_CIPHER_ALG_RC2:
-		key_blob.hdr.aiKeyAlg = CALG_RC2;
-		break;
-	case CRYPTO_CIPHER_ALG_RC4:
-		key_blob.hdr.aiKeyAlg = CALG_RC4;
-		break;
-	default:
-		return NULL;
-	}
-
-	ctx = os_zalloc(sizeof(*ctx));
-	if (ctx == NULL)
-		return NULL;
-
-	if (!CryptAcquireContext(&ctx->prov, NULL, MS_ENH_RSA_AES_PROV,
-				 PROV_RSA_AES, CRYPT_VERIFYCONTEXT)) {
-		cryptoapi_report_error("CryptAcquireContext");
-		goto fail1;
-	}
-
-	if (!CryptImportKey(ctx->prov, (BYTE *) &key_blob,
-			    sizeof(key_blob), 0, 0, &ctx->key)) {
- 		cryptoapi_report_error("CryptImportKey");
-		goto fail2;
-	}
-
-	if (!CryptSetKeyParam(ctx->key, KP_MODE, (BYTE *) &mode, 0)) {
- 		cryptoapi_report_error("CryptSetKeyParam(KP_MODE)");
-		goto fail3;
-	}
-
-	if (iv && !CryptSetKeyParam(ctx->key, KP_IV, (BYTE *) iv, 0)) {
- 		cryptoapi_report_error("CryptSetKeyParam(KP_IV)");
-		goto fail3;
-	}
-
-	return ctx;
-
-fail3:
-	CryptDestroyKey(ctx->key);
-fail2:
-	CryptReleaseContext(ctx->prov, 0);
-fail1:
-	os_free(ctx);
-	return NULL;
-}
-
-
-int crypto_cipher_encrypt(struct crypto_cipher *ctx, const u8 *plain,
-			  u8 *crypt, size_t len)
-{
-	DWORD dlen;
-
-	os_memcpy(crypt, plain, len);
-	dlen = len;
-	if (!CryptEncrypt(ctx->key, 0, FALSE, 0, crypt, &dlen, len)) {
- 		cryptoapi_report_error("CryptEncrypt");
-		os_memset(crypt, 0, len);
-		return -1;
-	}
-
-	return 0;
-}
-
-
-int crypto_cipher_decrypt(struct crypto_cipher *ctx, const u8 *crypt,
-			  u8 *plain, size_t len)
-{
-	DWORD dlen;
-
-	os_memcpy(plain, crypt, len);
-	dlen = len;
-	if (!CryptDecrypt(ctx->key, 0, FALSE, 0, plain, &dlen)) {
- 		cryptoapi_report_error("CryptDecrypt");
-		return -1;
-	}
-
-	return 0;
-}
-
-
-void crypto_cipher_deinit(struct crypto_cipher *ctx)
-{
-	CryptDestroyKey(ctx->key);
-	CryptReleaseContext(ctx->prov, 0);
-	os_free(ctx);
-}
-
-
-struct crypto_public_key {
-	HCRYPTPROV prov;
-	HCRYPTKEY rsa;
-};
-
-struct crypto_private_key {
-	HCRYPTPROV prov;
-	HCRYPTKEY rsa;
-};
-
-
-struct crypto_public_key * crypto_public_key_import(const u8 *key, size_t len)
-{
-	/* Use crypto_public_key_from_cert() instead. */
-	return NULL;
-}
-
-
-struct crypto_private_key * crypto_private_key_import(const u8 *key,
-						      size_t len,
-						      const char *passwd)
-{
-	/* TODO */
-	return NULL;
-}
-
-
-struct crypto_public_key * crypto_public_key_from_cert(const u8 *buf,
-						       size_t len)
-{
-	struct crypto_public_key *pk;
-	PCCERT_CONTEXT cc;
-
-	pk = os_zalloc(sizeof(*pk));
-	if (pk == NULL)
-		return NULL;
-
-	cc = CertCreateCertificateContext(X509_ASN_ENCODING |
-					  PKCS_7_ASN_ENCODING, buf, len);
-	if (!cc) {
- 		cryptoapi_report_error("CryptCreateCertificateContext");
-		os_free(pk);
-		return NULL;
-	}
-
-	if (!CryptAcquireContext(&pk->prov, NULL, MS_DEF_PROV, PROV_RSA_FULL,
-				 0)) {
- 		cryptoapi_report_error("CryptAcquireContext");
-		os_free(pk);
-		CertFreeCertificateContext(cc);
-		return NULL;
-	}
-
-	if (!CryptImportPublicKeyInfo(pk->prov, X509_ASN_ENCODING |
-				      PKCS_7_ASN_ENCODING,
-				      &cc->pCertInfo->SubjectPublicKeyInfo,
-				      &pk->rsa)) {
- 		cryptoapi_report_error("CryptImportPublicKeyInfo");
-		CryptReleaseContext(pk->prov, 0);
-		os_free(pk);
-		CertFreeCertificateContext(cc);
-		return NULL;
-	}
-
-	CertFreeCertificateContext(cc);
-
-	return pk;
-}
-
-
-int crypto_public_key_encrypt_pkcs1_v15(struct crypto_public_key *key,
-					const u8 *in, size_t inlen,
-					u8 *out, size_t *outlen)
-{
-	DWORD clen;
-	u8 *tmp;
-	size_t i;
-
-	if (*outlen < inlen)
-		return -1;
-	tmp = malloc(*outlen);
-	if (tmp == NULL)
-		return -1;
-
-	os_memcpy(tmp, in, inlen);
-	clen = inlen;
-	if (!CryptEncrypt(key->rsa, 0, TRUE, 0, tmp, &clen, *outlen)) {
-		wpa_printf(MSG_DEBUG, "CryptoAPI: Failed to encrypt using "
-			   "public key: %d", (int) GetLastError());
-		os_free(tmp);
-		return -1;
-	}
-
-	*outlen = clen;
-
-	/* Reverse the output */
-	for (i = 0; i < *outlen; i++)
-		out[i] = tmp[*outlen - 1 - i];
-
-	os_free(tmp);
-
-	return 0;
-}
-
-
-int crypto_private_key_sign_pkcs1(struct crypto_private_key *key,
-				  const u8 *in, size_t inlen,
-				  u8 *out, size_t *outlen)
-{
-	/* TODO */
-	return -1;
-}
-
-
-void crypto_public_key_free(struct crypto_public_key *key)
-{
-	if (key) {
-		CryptDestroyKey(key->rsa);
-		CryptReleaseContext(key->prov, 0);
-		os_free(key);
-	}
-}
-
-
-void crypto_private_key_free(struct crypto_private_key *key)
-{
-	if (key) {
-		CryptDestroyKey(key->rsa);
-		CryptReleaseContext(key->prov, 0);
-		os_free(key);
-	}
-}
-
-
-int crypto_global_init(void)
-{
-	return mingw_load_crypto_func();
-}
-
-
-void crypto_global_deinit(void)
-{
-}
-
-
-int crypto_mod_exp(const u8 *base, size_t base_len,
-		   const u8 *power, size_t power_len,
-		   const u8 *modulus, size_t modulus_len,
-		   u8 *result, size_t *result_len)
-{
-	/* TODO */
-	return -1;
-}
diff --git a/src/crypto/crypto_module_tests.c b/src/crypto/crypto_module_tests.c
new file mode 100644
index 0000000..1d613c9
--- /dev/null
+++ b/src/crypto/crypto_module_tests.c
@@ -0,0 +1,1680 @@
+/*
+ * crypto module tests
+ * Copyright (c) 2014-2015, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "crypto/aes_siv.h"
+#include "crypto/aes_wrap.h"
+#include "crypto/aes.h"
+#include "crypto/ms_funcs.h"
+#include "crypto/crypto.h"
+#include "crypto/sha1.h"
+#include "crypto/sha256.h"
+
+
+static int test_siv(void)
+{
+#ifdef CONFIG_MESH
+	/* RFC 5297, A.1. Deterministic Authenticated Encryption Example */
+	u8 key[] = {
+		0xff, 0xfe, 0xfd, 0xfc, 0xfb, 0xfa, 0xf9, 0xf8,
+		0xf7, 0xf6, 0xf5, 0xf4, 0xf3, 0xf2, 0xf1, 0xf0,
+		0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
+		0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff
+	};
+	u8 ad[] = {
+		0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+		0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
+		0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27
+	};
+	u8 plaintext[] = {
+		0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88,
+		0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee
+	};
+	u8 iv_c[] = {
+		0x85, 0x63, 0x2d, 0x07, 0xc6, 0xe8, 0xf3, 0x7f,
+		0x95, 0x0a, 0xcd, 0x32, 0x0a, 0x2e, 0xcc, 0x93,
+		0x40, 0xc0, 0x2b, 0x96, 0x90, 0xc4, 0xdc, 0x04,
+		0xda, 0xef, 0x7f, 0x6a, 0xfe, 0x5c
+	};
+	/* RFC 5297, A.2. Nonce-Based Authenticated Encryption Example */
+	u8 key_2[] = {
+		0x7f, 0x7e, 0x7d, 0x7c, 0x7b, 0x7a, 0x79, 0x78,
+		0x77, 0x76, 0x75, 0x74, 0x73, 0x72, 0x71, 0x70,
+		0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
+		0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f
+	};
+	u8 ad1_2[] = {
+		0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
+		0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff,
+		0xde, 0xad, 0xda, 0xda, 0xde, 0xad, 0xda, 0xda,
+		0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88,
+		0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11, 0x00
+	};
+	u8 ad2_2[] = {
+		0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80,
+		0x90, 0xa0
+	};
+	u8 nonce_2[] = {
+		0x09, 0xf9, 0x11, 0x02, 0x9d, 0x74, 0xe3, 0x5b,
+		0xd8, 0x41, 0x56, 0xc5, 0x63, 0x56, 0x88, 0xc0
+	};
+	u8 plaintext_2[] = {
+		0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20,
+		0x73, 0x6f, 0x6d, 0x65, 0x20, 0x70, 0x6c, 0x61,
+		0x69, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x20, 0x74,
+		0x6f, 0x20, 0x65, 0x6e, 0x63, 0x72, 0x79, 0x70,
+		0x74, 0x20, 0x75, 0x73, 0x69, 0x6e, 0x67, 0x20,
+		0x53, 0x49, 0x56, 0x2d, 0x41, 0x45, 0x53
+	};
+	u8 iv_c_2[] = {
+		0x7b, 0xdb, 0x6e, 0x3b, 0x43, 0x26, 0x67, 0xeb,
+		0x06, 0xf4, 0xd1, 0x4b, 0xff, 0x2f, 0xbd, 0x0f,
+		0xcb, 0x90, 0x0f, 0x2f, 0xdd, 0xbe, 0x40, 0x43,
+		0x26, 0x60, 0x19, 0x65, 0xc8, 0x89, 0xbf, 0x17,
+		0xdb, 0xa7, 0x7c, 0xeb, 0x09, 0x4f, 0xa6, 0x63,
+		0xb7, 0xa3, 0xf7, 0x48, 0xba, 0x8a, 0xf8, 0x29,
+		0xea, 0x64, 0xad, 0x54, 0x4a, 0x27, 0x2e, 0x9c,
+		0x48, 0x5b, 0x62, 0xa3, 0xfd, 0x5c, 0x0d
+	};
+	u8 out[2 * AES_BLOCK_SIZE + sizeof(plaintext_2)];
+	const u8 *addr[3];
+	size_t len[3];
+
+	/* RFC 5297, A.1. Deterministic Authenticated Encryption Example */
+	addr[0] = ad;
+	len[0] = sizeof(ad);
+
+	if (aes_siv_encrypt(key, plaintext, sizeof(plaintext),
+			    1, addr, len, out)) {
+		wpa_printf(MSG_ERROR, "AES-SIV mode encryption failed");
+		return 1;
+	}
+	if (os_memcmp(out, iv_c, sizeof(iv_c)) != 0) {
+		wpa_printf(MSG_ERROR,
+			   "AES-SIV mode encryption returned invalid cipher text");
+		return 1;
+	}
+
+	if (aes_siv_decrypt(key, iv_c, sizeof(iv_c), 1, addr, len, out)) {
+		wpa_printf(MSG_ERROR, "AES-SIV mode decryption failed");
+		return 1;
+	}
+	if (os_memcmp(out, plaintext, sizeof(plaintext)) != 0) {
+		wpa_printf(MSG_ERROR,
+			   "AES-SIV mode decryption returned invalid plain text");
+		return 1;
+	}
+
+	/* RFC 5297, A.2. Nonce-Based Authenticated Encryption Example */
+	addr[0] = ad1_2;
+	len[0] = sizeof(ad1_2);
+	addr[1] = ad2_2;
+	len[1] = sizeof(ad2_2);
+	addr[2] = nonce_2;
+	len[2] = sizeof(nonce_2);
+
+	if (aes_siv_encrypt(key_2, plaintext_2, sizeof(plaintext_2),
+			    3, addr, len, out)) {
+		wpa_printf(MSG_ERROR, "AES-SIV mode encryption failed");
+		return 1;
+	}
+	if (os_memcmp(out, iv_c_2, sizeof(iv_c_2)) != 0) {
+		wpa_printf(MSG_ERROR,
+			   "AES-SIV mode encryption returned invalid cipher text");
+		return 1;
+	}
+
+	if (aes_siv_decrypt(key_2, iv_c_2, sizeof(iv_c_2), 3, addr, len, out)) {
+		wpa_printf(MSG_ERROR, "AES-SIV mode decryption failed");
+		return 1;
+	}
+	if (os_memcmp(out, plaintext_2, sizeof(plaintext_2)) != 0) {
+		wpa_printf(MSG_ERROR,
+			   "AES-SIV mode decryption returned invalid plain text");
+		return 1;
+	}
+
+	wpa_printf(MSG_INFO, "AES-SIV test cases passed");
+#endif /* CONFIG_MESH */
+
+	return 0;
+}
+
+
+/* OMAC1 AES-128 test vectors from
+ * http://csrc.nist.gov/CryptoToolkit/modes/proposedmodes/omac/omac-ad.pdf
+ * which are same as the examples from NIST SP800-38B
+ * http://csrc.nist.gov/CryptoToolkit/modes/800-38_Series_Publications/SP800-38B.pdf
+ */
+
+struct omac1_test_vector {
+	u8 k[16];
+	u8 msg[64];
+	int msg_len;
+	u8 tag[16];
+};
+
+static const struct omac1_test_vector omac1_test_vectors[] =
+{
+	{
+		{ 0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6,
+		  0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c },
+		{ },
+		0,
+		{ 0xbb, 0x1d, 0x69, 0x29, 0xe9, 0x59, 0x37, 0x28,
+		  0x7f, 0xa3, 0x7d, 0x12, 0x9b, 0x75, 0x67, 0x46 }
+	},
+	{
+		{ 0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6,
+		  0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c },
+		{ 0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96,
+		  0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a},
+		16,
+		{ 0x07, 0x0a, 0x16, 0xb4, 0x6b, 0x4d, 0x41, 0x44,
+		  0xf7, 0x9b, 0xdd, 0x9d, 0xd0, 0x4a, 0x28, 0x7c }
+	},
+	{
+		{ 0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6,
+		  0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c },
+		{ 0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96,
+		  0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a,
+		  0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c,
+		  0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51,
+		  0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11 },
+		40,
+		{ 0xdf, 0xa6, 0x67, 0x47, 0xde, 0x9a, 0xe6, 0x30,
+		  0x30, 0xca, 0x32, 0x61, 0x14, 0x97, 0xc8, 0x27 }
+	},
+	{
+		{ 0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6,
+		  0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c },
+		{ 0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96,
+		  0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a,
+		  0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c,
+		  0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51,
+		  0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11,
+		  0xe5, 0xfb, 0xc1, 0x19, 0x1a, 0x0a, 0x52, 0xef,
+		  0xf6, 0x9f, 0x24, 0x45, 0xdf, 0x4f, 0x9b, 0x17,
+		  0xad, 0x2b, 0x41, 0x7b, 0xe6, 0x6c, 0x37, 0x10 },
+		64,
+		{ 0x51, 0xf0, 0xbe, 0xbf, 0x7e, 0x3b, 0x9d, 0x92,
+		  0xfc, 0x49, 0x74, 0x17, 0x79, 0x36, 0x3c, 0xfe }
+	},
+};
+
+
+static int test_omac1_vector(const struct omac1_test_vector *tv,
+			     unsigned int i)
+{
+	u8 key[] = {
+		0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6,
+		0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c
+	};
+	u8 msg[] = { 0x12, 0x34, 0x56 };
+	u8 result[24], result2[24];
+	const u8 *addr[3];
+	size_t len[3];
+
+	if (omac1_aes_128(tv->k, tv->msg, tv->msg_len, result) ||
+	    os_memcmp(result, tv->tag, 16) != 0) {
+		wpa_printf(MSG_ERROR, "OMAC1-AES-128 test vector %u failed", i);
+		return 1;
+	}
+
+	if (tv->msg_len > 1) {
+
+		addr[0] = tv->msg;
+		len[0] = 1;
+		addr[1] = tv->msg + 1;
+		len[1] = tv->msg_len - 1;
+
+		if (omac1_aes_128_vector(tv->k, 2, addr, len, result) ||
+		    os_memcmp(result, tv->tag, 16) != 0) {
+			wpa_printf(MSG_ERROR,
+				   "OMAC1-AES-128(vector) test vector %u failed",
+				   i);
+			return 1;
+		}
+
+		addr[0] = tv->msg;
+		len[0] = tv->msg_len - 2;
+		addr[1] = tv->msg + tv->msg_len - 2;
+		len[1] = 1;
+		addr[2] = tv->msg + tv->msg_len - 1;
+		len[2] = 1;
+
+		if (omac1_aes_128_vector(tv->k, 3, addr, len, result) ||
+		    os_memcmp(result, tv->tag, 16) != 0) {
+			wpa_printf(MSG_ERROR,
+				   "OMAC1-AES-128(vector2) test vector %u failed",
+				   i);
+			return 1;
+		}
+	}
+
+	addr[0] = &msg[0];
+	len[0] = 1;
+	addr[1] = &msg[1];
+	len[1] = 1;
+	addr[2] = &msg[2];
+	len[2] = 1;
+	if (omac1_aes_128(key, msg, sizeof(msg), result) ||
+	    omac1_aes_128_vector(key, 3, addr, len, result2) ||
+	    os_memcmp(result, result2, 16) != 0) {
+		wpa_printf(MSG_ERROR, "OMAC1-AES-128 short test mismatch");
+		return 1;
+	}
+
+	return 0;
+}
+
+
+static int test_omac1(void)
+{
+	unsigned int i;
+
+	for (i = 0; i < ARRAY_SIZE(omac1_test_vectors); i++) {
+		if (test_omac1_vector(&omac1_test_vectors[i], i))
+			return 1;
+	}
+
+	wpa_printf(MSG_INFO, "OMAC1-AES-128 test cases passed");
+
+	return 0;
+}
+
+
+static int test_eax(void)
+{
+#ifdef EAP_PSK
+	u8 msg[] = { 0xF7, 0xFB };
+	u8 key[] = { 0x91, 0x94, 0x5D, 0x3F, 0x4D, 0xCB, 0xEE, 0x0B,
+		     0xF4, 0x5E, 0xF5, 0x22, 0x55, 0xF0, 0x95, 0xA4 };
+	u8 nonce[] = { 0xBE, 0xCA, 0xF0, 0x43, 0xB0, 0xA2, 0x3D, 0x84,
+		       0x31, 0x94, 0xBA, 0x97, 0x2C, 0x66, 0xDE, 0xBD };
+	u8 hdr[] = { 0xFA, 0x3B, 0xFD, 0x48, 0x06, 0xEB, 0x53, 0xFA };
+	u8 cipher[] = { 0x19, 0xDD, 0x5C, 0x4C, 0x93, 0x31, 0x04, 0x9D,
+			0x0B, 0xDA, 0xB0, 0x27, 0x74, 0x08, 0xF6, 0x79,
+			0x67, 0xE5 };
+	u8 data[sizeof(msg)], tag[AES_BLOCK_SIZE];
+
+	os_memcpy(data, msg, sizeof(msg));
+	if (aes_128_eax_encrypt(key, nonce, sizeof(nonce), hdr, sizeof(hdr),
+				data, sizeof(data), tag)) {
+		wpa_printf(MSG_ERROR, "AES-128 EAX mode encryption failed");
+		return 1;
+	}
+	if (os_memcmp(data, cipher, sizeof(data)) != 0) {
+		wpa_printf(MSG_ERROR,
+			   "AES-128 EAX mode encryption returned invalid cipher text");
+		return 1;
+	}
+	if (os_memcmp(tag, cipher + sizeof(data), AES_BLOCK_SIZE) != 0) {
+		wpa_printf(MSG_ERROR,
+			   "AES-128 EAX mode encryption returned invalid tag");
+		return 1;
+	}
+
+	if (aes_128_eax_decrypt(key, nonce, sizeof(nonce), hdr, sizeof(hdr),
+				data, sizeof(data), tag)) {
+		wpa_printf(MSG_ERROR, "AES-128 EAX mode decryption failed");
+		return 1;
+	}
+	if (os_memcmp(data, msg, sizeof(data)) != 0) {
+		wpa_printf(MSG_ERROR,
+			   "AES-128 EAX mode decryption returned invalid plain text");
+		return 1;
+	}
+
+	wpa_printf(MSG_INFO, "AES-128 EAX mode test cases passed");
+#endif /* EAP_PSK */
+
+	return 0;
+}
+
+
+static int test_cbc(void)
+{
+	struct cbc_test_vector {
+		u8 key[16];
+		u8 iv[16];
+		u8 plain[32];
+		u8 cipher[32];
+		size_t len;
+	} vectors[] = {
+		{
+			{ 0x06, 0xa9, 0x21, 0x40, 0x36, 0xb8, 0xa1, 0x5b,
+			  0x51, 0x2e, 0x03, 0xd5, 0x34, 0x12, 0x00, 0x06 },
+			{ 0x3d, 0xaf, 0xba, 0x42, 0x9d, 0x9e, 0xb4, 0x30,
+			  0xb4, 0x22, 0xda, 0x80, 0x2c, 0x9f, 0xac, 0x41 },
+			"Single block msg",
+			{ 0xe3, 0x53, 0x77, 0x9c, 0x10, 0x79, 0xae, 0xb8,
+			  0x27, 0x08, 0x94, 0x2d, 0xbe, 0x77, 0x18, 0x1a },
+			16
+		},
+		{
+			{ 0xc2, 0x86, 0x69, 0x6d, 0x88, 0x7c, 0x9a, 0xa0,
+			  0x61, 0x1b, 0xbb, 0x3e, 0x20, 0x25, 0xa4, 0x5a },
+			{ 0x56, 0x2e, 0x17, 0x99, 0x6d, 0x09, 0x3d, 0x28,
+			  0xdd, 0xb3, 0xba, 0x69, 0x5a, 0x2e, 0x6f, 0x58 },
+			{ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+			  0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+			  0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+			  0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f },
+			{ 0xd2, 0x96, 0xcd, 0x94, 0xc2, 0xcc, 0xcf, 0x8a,
+			  0x3a, 0x86, 0x30, 0x28, 0xb5, 0xe1, 0xdc, 0x0a,
+			  0x75, 0x86, 0x60, 0x2d, 0x25, 0x3c, 0xff, 0xf9,
+			  0x1b, 0x82, 0x66, 0xbe, 0xa6, 0xd6, 0x1a, 0xb1 },
+			32
+		}
+	};
+	int ret = 0;
+	u8 *buf;
+	unsigned int i;
+
+	for (i = 0; i < ARRAY_SIZE(vectors); i++) {
+		struct cbc_test_vector *tv = &vectors[i];
+
+		buf = os_malloc(tv->len);
+		if (buf == NULL) {
+			ret++;
+			break;
+		}
+
+		os_memcpy(buf, tv->plain, tv->len);
+		if (aes_128_cbc_encrypt(tv->key, tv->iv, buf, tv->len) ||
+		    os_memcmp(buf, tv->cipher, tv->len) != 0) {
+			wpa_printf(MSG_ERROR, "AES-CBC encrypt %d failed", i);
+			ret++;
+		}
+
+		os_memcpy(buf, tv->cipher, tv->len);
+		if (aes_128_cbc_decrypt(tv->key, tv->iv, buf, tv->len) ||
+		    os_memcmp(buf, tv->plain, tv->len) != 0) {
+			wpa_printf(MSG_ERROR, "AES-CBC decrypt %d failed", i);
+			ret++;
+		}
+
+		os_free(buf);
+	}
+
+	return ret;
+}
+
+
+static int test_ecb(void)
+{
+#ifdef EAP_PSK
+	struct ecb_test_vector {
+		char *key;
+		char *plaintext;
+		char *ciphertext;
+	} vectors[] = {
+		/* CAVS 11.1 - ECBGFSbox128.rsp */
+		{
+			"00000000000000000000000000000000",
+			"f34481ec3cc627bacd5dc3fb08f273e6",
+			"0336763e966d92595a567cc9ce537f5e"
+		},
+		{
+			"00000000000000000000000000000000",
+			"9798c4640bad75c7c3227db910174e72",
+			"a9a1631bf4996954ebc093957b234589"
+		},
+		{
+			"00000000000000000000000000000000",
+			"96ab5c2ff612d9dfaae8c31f30c42168",
+			"ff4f8391a6a40ca5b25d23bedd44a597"
+		},
+		{
+			"00000000000000000000000000000000",
+			"6a118a874519e64e9963798a503f1d35",
+			"dc43be40be0e53712f7e2bf5ca707209"
+		},
+		{
+			"00000000000000000000000000000000",
+			"cb9fceec81286ca3e989bd979b0cb284",
+			"92beedab1895a94faa69b632e5cc47ce"
+		},
+		{
+			"00000000000000000000000000000000",
+			"b26aeb1874e47ca8358ff22378f09144",
+			"459264f4798f6a78bacb89c15ed3d601"
+		},
+		{
+			"00000000000000000000000000000000",
+			"58c8e00b2631686d54eab84b91f0aca1",
+			"08a4e2efec8a8e3312ca7460b9040bbf"
+		},
+		/* CAVS 11.1 - ECBKeySbox128.rsp */
+		{
+			"10a58869d74be5a374cf867cfb473859",
+			"00000000000000000000000000000000",
+			"6d251e6944b051e04eaa6fb4dbf78465"
+		},
+		{
+			"caea65cdbb75e9169ecd22ebe6e54675",
+			"00000000000000000000000000000000",
+			"6e29201190152df4ee058139def610bb",
+		}
+	};
+	int ret = 0;
+	unsigned int i;
+	u8 key[16], plain[16], cipher[16], out[16];
+
+	for (i = 0; i < ARRAY_SIZE(vectors); i++) {
+		struct ecb_test_vector *tv = &vectors[i];
+
+		if (hexstr2bin(tv->key, key, sizeof(key)) ||
+		    hexstr2bin(tv->plaintext, plain, sizeof(plain)) ||
+		    hexstr2bin(tv->ciphertext, cipher, sizeof(cipher))) {
+			wpa_printf(MSG_ERROR, "Invalid AES-ECB test vector %u",
+				   i);
+			ret++;
+			continue;
+		}
+
+		if (aes_128_encrypt_block(key, plain, out) < 0 ||
+		    os_memcmp(out, cipher, 16) != 0) {
+			wpa_printf(MSG_ERROR, "AES-ECB encrypt %u failed", i);
+			ret++;
+		}
+	}
+
+	if (!ret)
+		wpa_printf(MSG_INFO, "AES ECB mode test cases passed");
+
+	return ret;
+#endif /* EAP_PSK */
+
+	return 0;
+}
+
+
+static int test_key_wrap(void)
+{
+	int ret = 0;
+
+	/* RFC 3394 - Test vector 4.1 */
+	u8 kek41[] = {
+		0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+		0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f
+	};
+	u8 plain41[] = {
+		0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
+		0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff
+	};
+	u8 crypt41[] = {
+		0x1F, 0xA6, 0x8B, 0x0A, 0x81, 0x12, 0xB4, 0x47,
+		0xAE, 0xF3, 0x4B, 0xD8, 0xFB, 0x5A, 0x7B, 0x82,
+		0x9D, 0x3E, 0x86, 0x23, 0x71, 0xD2, 0xCF, 0xE5
+	};
+	/* RFC 3394 - Test vector 4.2 */
+	u8 kek42[] = {
+		0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+		0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+		0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17
+	};
+	u8 plain42[] = {
+		0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
+		0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff
+	};
+	u8 crypt42[] = {
+		0x96, 0x77, 0x8B, 0x25, 0xAE, 0x6C, 0xA4, 0x35,
+		0xF9, 0x2B, 0x5B, 0x97, 0xC0, 0x50, 0xAE, 0xD2,
+		0x46, 0x8A, 0xB8, 0xA1, 0x7A, 0xD8, 0x4E, 0x5D
+	};
+	/* RFC 3394 - Test vector 4.3 */
+	u8 kek43[] = {
+		0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+		0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
+		0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+		0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F
+	};
+	u8 plain43[] = {
+		0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
+		0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff
+	};
+	u8 crypt43[] = {
+		0x64, 0xE8, 0xC3, 0xF9, 0xCE, 0x0F, 0x5B, 0xA2,
+		0x63, 0xE9, 0x77, 0x79, 0x05, 0x81, 0x8A, 0x2A,
+		0x93, 0xC8, 0x19, 0x1E, 0x7D, 0x6E, 0x8A, 0xE7,
+	};
+	/* RFC 3394 - Test vector 4.4 */
+	u8 kek44[] = {
+		0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+		0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+		0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17
+	};
+	u8 plain44[] = {
+		0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
+		0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff,
+		0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07
+	};
+	u8 crypt44[] = {
+		0x03, 0x1D, 0x33, 0x26, 0x4E, 0x15, 0xD3, 0x32,
+		0x68, 0xF2, 0x4E, 0xC2, 0x60, 0x74, 0x3E, 0xDC,
+		0xE1, 0xC6, 0xC7, 0xDD, 0xEE, 0x72, 0x5A, 0x93,
+		0x6B, 0xA8, 0x14, 0x91, 0x5C, 0x67, 0x62, 0xD2
+	};
+	/* RFC 3394 - Test vector 4.5 */
+	u8 kek45[] = {
+		0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+		0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
+		0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+		0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F
+	};
+	u8 plain45[] = {
+		0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
+		0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff,
+		0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07
+	};
+	u8 crypt45[] = {
+		0xA8, 0xF9, 0xBC, 0x16, 0x12, 0xC6, 0x8B, 0x3F,
+		0xF6, 0xE6, 0xF4, 0xFB, 0xE3, 0x0E, 0x71, 0xE4,
+		0x76, 0x9C, 0x8B, 0x80, 0xA3, 0x2C, 0xB8, 0x95,
+		0x8C, 0xD5, 0xD1, 0x7D, 0x6B, 0x25, 0x4D, 0xA1,
+	};
+	/* RFC 3394 - Test vector 4.6 */
+	u8 kek46[] = {
+		0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+		0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
+		0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+		0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F
+	};
+	u8 plain46[] = {
+		0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
+		0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF,
+		0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+		0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F
+	};
+	u8 crypt46[] = {
+		0x28, 0xC9, 0xF4, 0x04, 0xC4, 0xB8, 0x10, 0xF4,
+		0xCB, 0xCC, 0xB3, 0x5C, 0xFB, 0x87, 0xF8, 0x26,
+		0x3F, 0x57, 0x86, 0xE2, 0xD8, 0x0E, 0xD3, 0x26,
+		0xCB, 0xC7, 0xF0, 0xE7, 0x1A, 0x99, 0xF4, 0x3B,
+		0xFB, 0x98, 0x8B, 0x9B, 0x7A, 0x02, 0xDD, 0x21
+	};
+	u8 result[40];
+
+	wpa_printf(MSG_INFO, "RFC 3394 - Test vector 4.1");
+	if (aes_wrap(kek41, sizeof(kek41), sizeof(plain41) / 8, plain41,
+		     result)) {
+		wpa_printf(MSG_ERROR, "AES-WRAP-128 reported failure");
+		ret++;
+	}
+	if (os_memcmp(result, crypt41, sizeof(crypt41)) != 0) {
+		wpa_printf(MSG_ERROR, "AES-WRAP-128 failed");
+		ret++;
+	}
+	if (aes_unwrap(kek41, sizeof(kek41), sizeof(plain41) / 8, crypt41,
+		       result)) {
+		wpa_printf(MSG_ERROR, "AES-UNWRAP-128 reported failure");
+		ret++;
+	}
+	if (os_memcmp(result, plain41, sizeof(plain41)) != 0) {
+		wpa_printf(MSG_ERROR, "AES-UNWRAP-128 failed");
+		ret++;
+	}
+
+	wpa_printf(MSG_INFO, "RFC 3394 - Test vector 4.2");
+	if (aes_wrap(kek42, sizeof(kek42), sizeof(plain42) / 8, plain42,
+		     result)) {
+		wpa_printf(MSG_ERROR, "AES-WRAP-192 reported failure");
+		ret++;
+	}
+	if (os_memcmp(result, crypt42, sizeof(crypt42)) != 0) {
+		wpa_printf(MSG_ERROR, "AES-WRAP-192 failed");
+		ret++;
+	}
+	if (aes_unwrap(kek42, sizeof(kek42), sizeof(plain42) / 8, crypt42,
+		       result)) {
+		wpa_printf(MSG_ERROR, "AES-UNWRAP-192 reported failure");
+		ret++;
+	}
+	if (os_memcmp(result, plain42, sizeof(plain42)) != 0) {
+		wpa_printf(MSG_ERROR, "AES-UNWRAP-192 failed");
+		ret++;
+	}
+
+	wpa_printf(MSG_INFO, "RFC 3394 - Test vector 4.3");
+	if (aes_wrap(kek43, sizeof(kek43), sizeof(plain43) / 8, plain43,
+		     result)) {
+		wpa_printf(MSG_ERROR, "AES-WRAP-256 reported failure");
+		ret++;
+	}
+	if (os_memcmp(result, crypt43, sizeof(crypt43)) != 0) {
+		wpa_printf(MSG_ERROR, "AES-WRAP-256 failed");
+		ret++;
+	}
+	if (aes_unwrap(kek43, sizeof(kek43), sizeof(plain43) / 8, crypt43,
+		       result)) {
+		wpa_printf(MSG_ERROR, "AES-UNWRAP-256 reported failure");
+		ret++;
+	}
+	if (os_memcmp(result, plain43, sizeof(plain43)) != 0) {
+		wpa_printf(MSG_ERROR, "AES-UNWRAP-256 failed");
+		ret++;
+	}
+
+	wpa_printf(MSG_INFO, "RFC 3394 - Test vector 4.4");
+	if (aes_wrap(kek44, sizeof(kek44), sizeof(plain44) / 8, plain44,
+		     result)) {
+		wpa_printf(MSG_ERROR, "AES-WRAP-192 reported failure");
+		ret++;
+	}
+	if (os_memcmp(result, crypt44, sizeof(crypt44)) != 0) {
+		wpa_printf(MSG_ERROR, "AES-WRAP-192 failed");
+		ret++;
+	}
+	if (aes_unwrap(kek44, sizeof(kek44), sizeof(plain44) / 8, crypt44,
+		       result)) {
+		wpa_printf(MSG_ERROR, "AES-UNWRAP-192 reported failure");
+		ret++;
+	}
+	if (os_memcmp(result, plain44, sizeof(plain44)) != 0) {
+		wpa_printf(MSG_ERROR, "AES-UNWRAP-192 failed");
+		ret++;
+	}
+
+	wpa_printf(MSG_INFO, "RFC 3394 - Test vector 4.5");
+	if (aes_wrap(kek45, sizeof(kek45), sizeof(plain45) / 8, plain45,
+		     result)) {
+		wpa_printf(MSG_ERROR, "AES-WRAP-256 reported failure");
+		ret++;
+	}
+	if (os_memcmp(result, crypt45, sizeof(crypt45)) != 0) {
+		wpa_printf(MSG_ERROR, "AES-WRAP-256 failed");
+		ret++;
+	}
+	if (aes_unwrap(kek45, sizeof(kek45), sizeof(plain45) / 8, crypt45,
+		       result)) {
+		wpa_printf(MSG_ERROR, "AES-UNWRAP-256 reported failure");
+		ret++;
+	}
+	if (os_memcmp(result, plain45, sizeof(plain45)) != 0) {
+		wpa_printf(MSG_ERROR, "AES-UNWRAP-256 failed");
+		ret++;
+	}
+
+	wpa_printf(MSG_INFO, "RFC 3394 - Test vector 4.6");
+	if (aes_wrap(kek46, sizeof(kek46), sizeof(plain46) / 8, plain46,
+		     result)) {
+		wpa_printf(MSG_ERROR, "AES-WRAP-256 reported failure");
+		ret++;
+	}
+	if (os_memcmp(result, crypt46, sizeof(crypt46)) != 0) {
+		wpa_printf(MSG_ERROR, "AES-WRAP-256 failed");
+		ret++;
+	}
+	if (aes_unwrap(kek46, sizeof(kek46), sizeof(plain46) / 8, crypt46,
+		       result)) {
+		wpa_printf(MSG_ERROR, "AES-UNWRAP-256 reported failure");
+		ret++;
+	}
+	if (os_memcmp(result, plain46, sizeof(plain46)) != 0) {
+		wpa_printf(MSG_ERROR, "AES-UNWRAP-256 failed");
+		ret++;
+	}
+
+	if (!ret)
+		wpa_printf(MSG_INFO, "AES key wrap/unwrap test cases passed");
+
+	return ret;
+}
+
+
+static int test_md5(void)
+{
+	struct {
+		char *data;
+		char *hash;
+	} tests[] = {
+		{
+			"",
+			"\xd4\x1d\x8c\xd9\x8f\x00\xb2\x04"
+			"\xe9\x80\x09\x98\xec\xf8\x42\x7e"
+		},
+		{
+			"a",
+			"\x0c\xc1\x75\xb9\xc0\xf1\xb6\xa8"
+			"\x31\xc3\x99\xe2\x69\x77\x26\x61"
+		},
+		{
+			"abc",
+			"\x90\x01\x50\x98\x3c\xd2\x4f\xb0"
+			"\xd6\x96\x3f\x7d\x28\xe1\x7f\x72"
+		},
+		{
+			"message digest",
+			"\xf9\x6b\x69\x7d\x7c\xb7\x93\x8d"
+			"\x52\x5a\x2f\x31\xaa\xf1\x61\xd0"
+		},
+		{
+			"abcdefghijklmnopqrstuvwxyz",
+			"\xc3\xfc\xd3\xd7\x61\x92\xe4\x00"
+			"\x7d\xfb\x49\x6c\xca\x67\xe1\x3b"
+		},
+		{
+			"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
+			"0123456789",
+			"\xd1\x74\xab\x98\xd2\x77\xd9\xf5"
+			"\xa5\x61\x1c\x2c\x9f\x41\x9d\x9f"
+		},
+		{
+			"12345678901234567890123456789012345678901234567890"
+			"123456789012345678901234567890",
+			"\x57\xed\xf4\xa2\x2b\xe3\xc9\x55"
+			"\xac\x49\xda\x2e\x21\x07\xb6\x7a"
+		}
+	};
+	unsigned int i;
+	u8 hash[16];
+	const u8 *addr[2];
+	size_t len[2];
+	int errors = 0;
+
+	for (i = 0; i < ARRAY_SIZE(tests); i++) {
+		wpa_printf(MSG_INFO, "MD5 test case %d", i);
+
+		addr[0] = (u8 *) tests[i].data;
+		len[0] = strlen(tests[i].data);
+		if (md5_vector(1, addr, len, hash) < 0 ||
+		    os_memcmp(hash, tests[i].hash, 16) != 0) {
+			wpa_printf(MSG_INFO, " FAIL");
+			errors++;
+		} else
+			wpa_printf(MSG_INFO, " OK");
+
+		if (len[0]) {
+			addr[0] = (u8 *) tests[i].data;
+			len[0] = strlen(tests[i].data);
+			addr[1] = (u8 *) tests[i].data + 1;
+			len[1] = strlen(tests[i].data) - 1;
+			if (md5_vector(1, addr, len, hash) < 0 ||
+			    os_memcmp(hash, tests[i].hash, 16) != 0) {
+				wpa_printf(MSG_INFO, " FAIL");
+				errors++;
+			} else
+				wpa_printf(MSG_INFO, " OK");
+		}
+	}
+
+	if (!errors)
+		wpa_printf(MSG_INFO, "MD5 test cases passed");
+
+	return errors;
+}
+
+
+static int test_eap_fast(void)
+{
+#ifdef EAP_FAST
+	/* RFC 4851, Appendix B.1 */
+	const u8 pac_key[] = {
+		0x0B, 0x97, 0x39, 0x0F, 0x37, 0x51, 0x78, 0x09,
+		0x81, 0x1E, 0xFD, 0x9C, 0x6E, 0x65, 0x94, 0x2B,
+		0x63, 0x2C, 0xE9, 0x53, 0x89, 0x38, 0x08, 0xBA,
+		0x36, 0x0B, 0x03, 0x7C, 0xD1, 0x85, 0xE4, 0x14
+	};
+	const u8 seed[] = {
+		0x3F, 0xFB, 0x11, 0xC4, 0x6C, 0xBF, 0xA5, 0x7A,
+		0x54, 0x40, 0xDA, 0xE8, 0x22, 0xD3, 0x11, 0xD3,
+		0xF7, 0x6D, 0xE4, 0x1D, 0xD9, 0x33, 0xE5, 0x93,
+		0x70, 0x97, 0xEB, 0xA9, 0xB3, 0x66, 0xF4, 0x2A,
+		0x00, 0x00, 0x00, 0x02, 0x6A, 0x66, 0x43, 0x2A,
+		0x8D, 0x14, 0x43, 0x2C, 0xEC, 0x58, 0x2D, 0x2F,
+		0xC7, 0x9C, 0x33, 0x64, 0xBA, 0x04, 0xAD, 0x3A,
+		0x52, 0x54, 0xD6, 0xA5, 0x79, 0xAD, 0x1E, 0x00
+	};
+	const u8 master_secret[] = {
+		0x4A, 0x1A, 0x51, 0x2C, 0x01, 0x60, 0xBC, 0x02,
+		0x3C, 0xCF, 0xBC, 0x83, 0x3F, 0x03, 0xBC, 0x64,
+		0x88, 0xC1, 0x31, 0x2F, 0x0B, 0xA9, 0xA2, 0x77,
+		0x16, 0xA8, 0xD8, 0xE8, 0xBD, 0xC9, 0xD2, 0x29,
+		0x38, 0x4B, 0x7A, 0x85, 0xBE, 0x16, 0x4D, 0x27,
+		0x33, 0xD5, 0x24, 0x79, 0x87, 0xB1, 0xC5, 0xA2
+	};
+	const u8 key_block[] = {
+		0x59, 0x59, 0xBE, 0x8E, 0x41, 0x3A, 0x77, 0x74,
+		0x8B, 0xB2, 0xE5, 0xD3, 0x60, 0xAC, 0x4D, 0x35,
+		0xDF, 0xFB, 0xC8, 0x1E, 0x9C, 0x24, 0x9C, 0x8B,
+		0x0E, 0xC3, 0x1D, 0x72, 0xC8, 0x84, 0x9D, 0x57,
+		0x48, 0x51, 0x2E, 0x45, 0x97, 0x6C, 0x88, 0x70,
+		0xBE, 0x5F, 0x01, 0xD3, 0x64, 0xE7, 0x4C, 0xBB,
+		0x11, 0x24, 0xE3, 0x49, 0xE2, 0x3B, 0xCD, 0xEF,
+		0x7A, 0xB3, 0x05, 0x39, 0x5D, 0x64, 0x8A, 0x44,
+		0x11, 0xB6, 0x69, 0x88, 0x34, 0x2E, 0x8E, 0x29,
+		0xD6, 0x4B, 0x7D, 0x72, 0x17, 0x59, 0x28, 0x05,
+		0xAF, 0xF9, 0xB7, 0xFF, 0x66, 0x6D, 0xA1, 0x96,
+		0x8F, 0x0B, 0x5E, 0x06, 0x46, 0x7A, 0x44, 0x84,
+		0x64, 0xC1, 0xC8, 0x0C, 0x96, 0x44, 0x09, 0x98,
+		0xFF, 0x92, 0xA8, 0xB4, 0xC6, 0x42, 0x28, 0x71
+	};
+	const u8 sks[] = {
+		0xD6, 0x4B, 0x7D, 0x72, 0x17, 0x59, 0x28, 0x05,
+		0xAF, 0xF9, 0xB7, 0xFF, 0x66, 0x6D, 0xA1, 0x96,
+		0x8F, 0x0B, 0x5E, 0x06, 0x46, 0x7A, 0x44, 0x84,
+		0x64, 0xC1, 0xC8, 0x0C, 0x96, 0x44, 0x09, 0x98,
+		0xFF, 0x92, 0xA8, 0xB4, 0xC6, 0x42, 0x28, 0x71
+	};
+	const u8 isk[] = {
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+	};
+	const u8 imck[] = {
+		0x16, 0x15, 0x3C, 0x3F, 0x21, 0x55, 0xEF, 0xD9,
+		0x7F, 0x34, 0xAE, 0xC8, 0x1A, 0x4E, 0x66, 0x80,
+		0x4C, 0xC3, 0x76, 0xF2, 0x8A, 0xA9, 0x6F, 0x96,
+		0xC2, 0x54, 0x5F, 0x8C, 0xAB, 0x65, 0x02, 0xE1,
+		0x18, 0x40, 0x7B, 0x56, 0xBE, 0xEA, 0xA7, 0xC5,
+		0x76, 0x5D, 0x8F, 0x0B, 0xC5, 0x07, 0xC6, 0xB9,
+		0x04, 0xD0, 0x69, 0x56, 0x72, 0x8B, 0x6B, 0xB8,
+		0x15, 0xEC, 0x57, 0x7B
+	};
+	const u8 msk[] = {
+		0x4D, 0x83, 0xA9, 0xBE, 0x6F, 0x8A, 0x74, 0xED,
+		0x6A, 0x02, 0x66, 0x0A, 0x63, 0x4D, 0x2C, 0x33,
+		0xC2, 0xDA, 0x60, 0x15, 0xC6, 0x37, 0x04, 0x51,
+		0x90, 0x38, 0x63, 0xDA, 0x54, 0x3E, 0x14, 0xB9,
+		0x27, 0x99, 0x18, 0x1E, 0x07, 0xBF, 0x0F, 0x5A,
+		0x5E, 0x3C, 0x32, 0x93, 0x80, 0x8C, 0x6C, 0x49,
+		0x67, 0xED, 0x24, 0xFE, 0x45, 0x40, 0xA0, 0x59,
+		0x5E, 0x37, 0xC2, 0xE9, 0xD0, 0x5D, 0x0A, 0xE3
+	};
+	const u8 emsk[] = {
+		0x3A, 0xD4, 0xAB, 0xDB, 0x76, 0xB2, 0x7F, 0x3B,
+		0xEA, 0x32, 0x2C, 0x2B, 0x74, 0xF4, 0x28, 0x55,
+		0xEF, 0x2D, 0xBA, 0x78, 0xC9, 0x57, 0x2F, 0x0D,
+		0x06, 0xCD, 0x51, 0x7C, 0x20, 0x93, 0x98, 0xA9,
+		0x76, 0xEA, 0x70, 0x21, 0xD7, 0x0E, 0x25, 0x54,
+		0x97, 0xED, 0xB2, 0x8A, 0xF6, 0xED, 0xFD, 0x0A,
+		0x2A, 0xE7, 0xA1, 0x58, 0x90, 0x10, 0x50, 0x44,
+		0xB3, 0x82, 0x85, 0xDB, 0x06, 0x14, 0xD2, 0xF9
+	};
+	/* RFC 4851, Appendix B.2 */
+	u8 tlv[] = {
+		0x80, 0x0C, 0x00, 0x38, 0x00, 0x01, 0x01, 0x00,
+		0xD8, 0x6A, 0x8C, 0x68, 0x3C, 0x32, 0x31, 0xA8,
+		0x56, 0x63, 0xB6, 0x40, 0x21, 0xFE, 0x21, 0x14,
+		0x4E, 0xE7, 0x54, 0x20, 0x79, 0x2D, 0x42, 0x62,
+		0xC9, 0xBF, 0x53, 0x7F, 0x54, 0xFD, 0xAC, 0x58,
+		0x43, 0x24, 0x6E, 0x30, 0x92, 0x17, 0x6D, 0xCF,
+		0xE6, 0xE0, 0x69, 0xEB, 0x33, 0x61, 0x6A, 0xCC,
+		0x05, 0xC5, 0x5B, 0xB7
+	};
+	const u8 compound_mac[] = {
+		0x43, 0x24, 0x6E, 0x30, 0x92, 0x17, 0x6D, 0xCF,
+		0xE6, 0xE0, 0x69, 0xEB, 0x33, 0x61, 0x6A, 0xCC,
+		0x05, 0xC5, 0x5B, 0xB7
+	};
+	u8 buf[512];
+	const u8 *simck, *cmk;
+	int errors = 0;
+
+	wpa_printf(MSG_INFO, "EAP-FAST test cases");
+
+	wpa_printf(MSG_INFO, "- T-PRF (SHA1) test case / master_secret");
+	if (sha1_t_prf(pac_key, sizeof(pac_key),
+		       "PAC to master secret label hash",
+		       seed, sizeof(seed), buf, sizeof(master_secret)) < 0 ||
+	    os_memcmp(master_secret, buf, sizeof(master_secret)) != 0) {
+		wpa_printf(MSG_INFO, "T-PRF test - FAILED!");
+		errors++;
+	}
+
+	wpa_printf(MSG_INFO, "- PRF (TLS, SHA1/MD5) test case / key_block");
+	if (tls_prf_sha1_md5(master_secret, sizeof(master_secret),
+			     "key expansion", seed, sizeof(seed),
+			     buf, sizeof(key_block)) ||
+	    os_memcmp(key_block, buf, sizeof(key_block)) != 0) {
+		wpa_printf(MSG_INFO, "PRF test - FAILED!");
+		errors++;
+	}
+
+	wpa_printf(MSG_INFO, "- T-PRF (SHA1) test case / IMCK");
+	if (sha1_t_prf(sks, sizeof(sks), "Inner Methods Compound Keys",
+		       isk, sizeof(isk), buf, sizeof(imck)) < 0 ||
+	    os_memcmp(imck, buf, sizeof(imck)) != 0) {
+		wpa_printf(MSG_INFO, "T-PRF test - FAILED!");
+		errors++;
+	}
+
+	simck = imck;
+	cmk = imck + 40;
+
+	wpa_printf(MSG_INFO, "- T-PRF (SHA1) test case / MSK");
+	if (sha1_t_prf(simck, 40, "Session Key Generating Function",
+		       (u8 *) "", 0, buf, sizeof(msk)) < 0 ||
+	    os_memcmp(msk, buf, sizeof(msk)) != 0) {
+		wpa_printf(MSG_INFO, "T-PRF test - FAILED!");
+		errors++;
+	}
+
+	wpa_printf(MSG_INFO, "- T-PRF (SHA1) test case / EMSK");
+	if (sha1_t_prf(simck, 40, "Extended Session Key Generating Function",
+		       (u8 *) "", 0, buf, sizeof(msk)) < 0 ||
+	    os_memcmp(emsk, buf, sizeof(emsk)) != 0) {
+		wpa_printf(MSG_INFO, "T-PRF test - FAILED!");
+		errors++;
+	}
+
+	wpa_printf(MSG_INFO, "- Compound MAC test case");
+	os_memset(tlv + sizeof(tlv) - 20, 0, 20);
+	if (hmac_sha1(cmk, 20, tlv, sizeof(tlv), tlv + sizeof(tlv) - 20) < 0 ||
+	    os_memcmp(tlv + sizeof(tlv) - 20, compound_mac,
+		      sizeof(compound_mac)) != 0) {
+		wpa_printf(MSG_INFO, "Compound MAC test - FAILED!");
+		errors++;
+	}
+
+	return errors;
+#else /* EAP_FAST */
+	return 0;
+#endif /* EAP_FAST */
+}
+
+
+static const u8 key0[] =
+{
+	0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+	0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+	0x0b, 0x0b, 0x0b, 0x0b
+};
+static const u8 data0[] = "Hi There";
+static const u8 prf0[] =
+{
+	0xbc, 0xd4, 0xc6, 0x50, 0xb3, 0x0b, 0x96, 0x84,
+	0x95, 0x18, 0x29, 0xe0, 0xd7, 0x5f, 0x9d, 0x54,
+	0xb8, 0x62, 0x17, 0x5e, 0xd9, 0xf0, 0x06, 0x06,
+	0xe1, 0x7d, 0x8d, 0xa3, 0x54, 0x02, 0xff, 0xee,
+	0x75, 0xdf, 0x78, 0xc3, 0xd3, 0x1e, 0x0f, 0x88,
+	0x9f, 0x01, 0x21, 0x20, 0xc0, 0x86, 0x2b, 0xeb,
+	0x67, 0x75, 0x3e, 0x74, 0x39, 0xae, 0x24, 0x2e,
+	0xdb, 0x83, 0x73, 0x69, 0x83, 0x56, 0xcf, 0x5a
+};
+
+static const u8 key1[] = "Jefe";
+static const u8 data1[] = "what do ya want for nothing?";
+static const u8 prf1[] =
+{
+	0x51, 0xf4, 0xde, 0x5b, 0x33, 0xf2, 0x49, 0xad,
+	0xf8, 0x1a, 0xeb, 0x71, 0x3a, 0x3c, 0x20, 0xf4,
+	0xfe, 0x63, 0x14, 0x46, 0xfa, 0xbd, 0xfa, 0x58,
+	0x24, 0x47, 0x59, 0xae, 0x58, 0xef, 0x90, 0x09,
+	0xa9, 0x9a, 0xbf, 0x4e, 0xac, 0x2c, 0xa5, 0xfa,
+	0x87, 0xe6, 0x92, 0xc4, 0x40, 0xeb, 0x40, 0x02,
+	0x3e, 0x7b, 0xab, 0xb2, 0x06, 0xd6, 0x1d, 0xe7,
+	0xb9, 0x2f, 0x41, 0x52, 0x90, 0x92, 0xb8, 0xfc
+};
+
+
+static const u8 key2[] =
+{
+	0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+	0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+	0xaa, 0xaa, 0xaa, 0xaa
+};
+static const u8 data2[] =
+{
+	0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+	0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+	0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+	0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+	0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+	0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+	0xdd, 0xdd
+};
+static const u8 prf2[] =
+{
+	0xe1, 0xac, 0x54, 0x6e, 0xc4, 0xcb, 0x63, 0x6f,
+	0x99, 0x76, 0x48, 0x7b, 0xe5, 0xc8, 0x6b, 0xe1,
+	0x7a, 0x02, 0x52, 0xca, 0x5d, 0x8d, 0x8d, 0xf1,
+	0x2c, 0xfb, 0x04, 0x73, 0x52, 0x52, 0x49, 0xce,
+	0x9d, 0xd8, 0xd1, 0x77, 0xea, 0xd7, 0x10, 0xbc,
+	0x9b, 0x59, 0x05, 0x47, 0x23, 0x91, 0x07, 0xae,
+	0xf7, 0xb4, 0xab, 0xd4, 0x3d, 0x87, 0xf0, 0xa6,
+	0x8f, 0x1c, 0xbd, 0x9e, 0x2b, 0x6f, 0x76, 0x07
+};
+
+
+struct passphrase_test {
+	char *passphrase;
+	char *ssid;
+	char psk[32];
+};
+
+static const struct passphrase_test passphrase_tests[] =
+{
+	{
+		"password",
+		"IEEE",
+		{
+			0xf4, 0x2c, 0x6f, 0xc5, 0x2d, 0xf0, 0xeb, 0xef,
+			0x9e, 0xbb, 0x4b, 0x90, 0xb3, 0x8a, 0x5f, 0x90,
+			0x2e, 0x83, 0xfe, 0x1b, 0x13, 0x5a, 0x70, 0xe2,
+			0x3a, 0xed, 0x76, 0x2e, 0x97, 0x10, 0xa1, 0x2e
+		}
+	},
+	{
+		"ThisIsAPassword",
+		"ThisIsASSID",
+		{
+			0x0d, 0xc0, 0xd6, 0xeb, 0x90, 0x55, 0x5e, 0xd6,
+			0x41, 0x97, 0x56, 0xb9, 0xa1, 0x5e, 0xc3, 0xe3,
+			0x20, 0x9b, 0x63, 0xdf, 0x70, 0x7d, 0xd5, 0x08,
+			0xd1, 0x45, 0x81, 0xf8, 0x98, 0x27, 0x21, 0xaf
+		}
+	},
+	{
+		"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
+		"ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ",
+		{
+			0xbe, 0xcb, 0x93, 0x86, 0x6b, 0xb8, 0xc3, 0x83,
+			0x2c, 0xb7, 0x77, 0xc2, 0xf5, 0x59, 0x80, 0x7c,
+			0x8c, 0x59, 0xaf, 0xcb, 0x6e, 0xae, 0x73, 0x48,
+			0x85, 0x00, 0x13, 0x00, 0xa9, 0x81, 0xcc, 0x62
+		}
+	},
+};
+
+#define NUM_PASSPHRASE_TESTS ARRAY_SIZE(passphrase_tests)
+
+
+struct rfc6070_test {
+	char *p;
+	char *s;
+	int c;
+	char dk[32];
+	size_t dk_len;
+};
+
+static const struct rfc6070_test rfc6070_tests[] =
+{
+	{
+		"password",
+		"salt",
+		1,
+		{
+			0x0c, 0x60, 0xc8, 0x0f, 0x96, 0x1f, 0x0e, 0x71,
+			0xf3, 0xa9, 0xb5, 0x24, 0xaf, 0x60, 0x12, 0x06,
+			0x2f, 0xe0, 0x37, 0xa6
+		},
+		20
+	},
+	{
+		"password",
+		"salt",
+		2,
+		{
+			0xea, 0x6c, 0x01, 0x4d, 0xc7, 0x2d, 0x6f, 0x8c,
+			0xcd, 0x1e, 0xd9, 0x2a, 0xce, 0x1d, 0x41, 0xf0,
+			0xd8, 0xde, 0x89, 0x57
+		},
+		20
+	},
+	{
+		"password",
+		"salt",
+		4096,
+		{
+			0x4b, 0x00, 0x79, 0x01, 0xb7, 0x65, 0x48, 0x9a,
+			0xbe, 0xad, 0x49, 0xd9, 0x26, 0xf7, 0x21, 0xd0,
+			0x65, 0xa4, 0x29, 0xc1
+		},
+		20
+	},
+#if 0 /* This takes quite long to derive.. */
+	{
+		"password",
+		"salt",
+		16777216,
+		{
+			0xee, 0xfe, 0x3d, 0x61, 0xcd, 0x4d, 0xa4, 0xe4,
+			0xe9, 0x94, 0x5b, 0x3d, 0x6b, 0xa2, 0x15, 0x8c,
+			0x26, 0x34, 0xe9, 0x84
+		},
+		20
+	},
+#endif
+	{
+		"passwordPASSWORDpassword",
+		"saltSALTsaltSALTsaltSALTsaltSALTsalt",
+		4096,
+		{
+			0x3d, 0x2e, 0xec, 0x4f, 0xe4, 0x1c, 0x84, 0x9b,
+			0x80, 0xc8, 0xd8, 0x36, 0x62, 0xc0, 0xe4, 0x4a,
+			0x8b, 0x29, 0x1a, 0x96, 0x4c, 0xf2, 0xf0, 0x70,
+			0x38
+		},
+		25
+	},
+#if 0 /* \0 not currently supported in passphrase parameters.. */
+	{
+		"pass\0word",
+		"sa\0lt",
+		4096,
+		{
+			0x56, 0xfa, 0x6a, 0xa7, 0x55, 0x48, 0x09, 0x9d,
+			0xcc, 0x37, 0xd7, 0xf0, 0x34, 0x25, 0xe0, 0xc3
+		},
+		16
+	},
+#endif
+};
+
+#define NUM_RFC6070_TESTS ARRAY_SIZE(rfc6070_tests)
+
+
+static int test_sha1(void)
+{
+	u8 res[512];
+	int ret = 0;
+	unsigned int i;
+
+	wpa_printf(MSG_INFO, "PRF-SHA1 test cases:");
+
+	if (sha1_prf(key0, sizeof(key0), "prefix", data0, sizeof(data0) - 1,
+		     res, sizeof(prf0)) == 0 &&
+	    os_memcmp(res, prf0, sizeof(prf0)) == 0)
+		wpa_printf(MSG_INFO, "Test case 0 - OK");
+	else {
+		wpa_printf(MSG_INFO, "Test case 0 - FAILED!");
+		ret++;
+	}
+
+	if (sha1_prf(key1, sizeof(key1) - 1, "prefix", data1, sizeof(data1) - 1,
+		     res, sizeof(prf1)) == 0 &&
+	    os_memcmp(res, prf1, sizeof(prf1)) == 0)
+		wpa_printf(MSG_INFO, "Test case 1 - OK");
+	else {
+		wpa_printf(MSG_INFO, "Test case 1 - FAILED!");
+		ret++;
+	}
+
+	if (sha1_prf(key2, sizeof(key2), "prefix", data2, sizeof(data2),
+		     res, sizeof(prf2)) == 0 &&
+	    os_memcmp(res, prf2, sizeof(prf2)) == 0)
+		wpa_printf(MSG_INFO, "Test case 2 - OK");
+	else {
+		wpa_printf(MSG_INFO, "Test case 2 - FAILED!");
+		ret++;
+	}
+
+	ret += test_eap_fast();
+
+	wpa_printf(MSG_INFO, "PBKDF2-SHA1 Passphrase test cases:");
+	for (i = 0; i < NUM_PASSPHRASE_TESTS; i++) {
+		u8 psk[32];
+		const struct passphrase_test *test = &passphrase_tests[i];
+
+		if (pbkdf2_sha1(test->passphrase,
+				(const u8 *) test->ssid, strlen(test->ssid),
+				4096, psk, 32) == 0 &&
+		    os_memcmp(psk, test->psk, 32) == 0)
+			wpa_printf(MSG_INFO, "Test case %d - OK", i);
+		else {
+			wpa_printf(MSG_INFO, "Test case %d - FAILED!", i);
+			ret++;
+		}
+	}
+
+	wpa_printf(MSG_INFO, "PBKDF2-SHA1 test cases (RFC 6070):");
+	for (i = 0; i < NUM_RFC6070_TESTS; i++) {
+		u8 dk[25];
+		const struct rfc6070_test *test = &rfc6070_tests[i];
+
+		if (pbkdf2_sha1(test->p, (const u8 *) test->s, strlen(test->s),
+				test->c, dk, test->dk_len) == 0 &&
+		    os_memcmp(dk, test->dk, test->dk_len) == 0)
+			wpa_printf(MSG_INFO, "Test case %d - OK", i);
+		else {
+			wpa_printf(MSG_INFO, "Test case %d - FAILED!", i);
+			ret++;
+		}
+	}
+
+	if (!ret)
+		wpa_printf(MSG_INFO, "SHA1 test cases passed");
+	return ret;
+}
+
+
+const struct {
+	char *data;
+	u8 hash[32];
+} tests[] = {
+	{
+		"abc",
+		{
+			0xba, 0x78, 0x16, 0xbf, 0x8f, 0x01, 0xcf, 0xea,
+			0x41, 0x41, 0x40, 0xde, 0x5d, 0xae, 0x22, 0x23,
+			0xb0, 0x03, 0x61, 0xa3, 0x96, 0x17, 0x7a, 0x9c,
+			0xb4, 0x10, 0xff, 0x61, 0xf2, 0x00, 0x15, 0xad
+		}
+	},
+	{
+		"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq",
+		{
+			0x24, 0x8d, 0x6a, 0x61, 0xd2, 0x06, 0x38, 0xb8,
+			0xe5, 0xc0, 0x26, 0x93, 0x0c, 0x3e, 0x60, 0x39,
+			0xa3, 0x3c, 0xe4, 0x59, 0x64, 0xff, 0x21, 0x67,
+			0xf6, 0xec, 0xed, 0xd4, 0x19, 0xdb, 0x06, 0xc1
+		}
+	}
+};
+
+const struct hmac_test {
+	u8 key[80];
+	size_t key_len;
+	u8 data[128];
+	size_t data_len;
+	u8 hash[32];
+} hmac_tests[] = {
+	/* draft-ietf-ipsec-ciph-sha-256-01.txt */
+	{
+		{
+			0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
+			0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10,
+			0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
+			0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20
+		},
+		32,
+		"abc", 3,
+		{
+			0xa2, 0x1b, 0x1f, 0x5d, 0x4c, 0xf4, 0xf7, 0x3a,
+			0x4d, 0xd9, 0x39, 0x75, 0x0f, 0x7a, 0x06, 0x6a,
+			0x7f, 0x98, 0xcc, 0x13, 0x1c, 0xb1, 0x6a, 0x66,
+			0x92, 0x75, 0x90, 0x21, 0xcf, 0xab, 0x81, 0x81
+		}
+	},
+	{
+		{
+			0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
+			0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10,
+			0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
+			0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20
+		},
+		32,
+		"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq",
+		56,
+		{
+			0x10, 0x4f, 0xdc, 0x12, 0x57, 0x32, 0x8f, 0x08,
+			0x18, 0x4b, 0xa7, 0x31, 0x31, 0xc5, 0x3c, 0xae,
+			0xe6, 0x98, 0xe3, 0x61, 0x19, 0x42, 0x11, 0x49,
+			0xea, 0x8c, 0x71, 0x24, 0x56, 0x69, 0x7d, 0x30
+		}
+	},
+	{
+		{
+			0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
+			0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10,
+			0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
+			0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20
+		},
+		32,
+		"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"
+		"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq",
+		112,
+		{
+			0x47, 0x03, 0x05, 0xfc, 0x7e, 0x40, 0xfe, 0x34,
+			0xd3, 0xee, 0xb3, 0xe7, 0x73, 0xd9, 0x5a, 0xab,
+			0x73, 0xac, 0xf0, 0xfd, 0x06, 0x04, 0x47, 0xa5,
+			0xeb, 0x45, 0x95, 0xbf, 0x33, 0xa9, 0xd1, 0xa3
+		}
+	},
+	{
+		{
+			0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+			0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+			0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+			0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b
+		},
+		32,
+		"Hi There",
+		8,
+		{
+			0x19, 0x8a, 0x60, 0x7e, 0xb4, 0x4b, 0xfb, 0xc6,
+			0x99, 0x03, 0xa0, 0xf1, 0xcf, 0x2b, 0xbd, 0xc5,
+			0xba, 0x0a, 0xa3, 0xf3, 0xd9, 0xae, 0x3c, 0x1c,
+			0x7a, 0x3b, 0x16, 0x96, 0xa0, 0xb6, 0x8c, 0xf7
+		}
+	},
+	{
+		"Jefe",
+		4,
+		"what do ya want for nothing?",
+		28,
+		{
+			0x5b, 0xdc, 0xc1, 0x46, 0xbf, 0x60, 0x75, 0x4e,
+			0x6a, 0x04, 0x24, 0x26, 0x08, 0x95, 0x75, 0xc7,
+			0x5a, 0x00, 0x3f, 0x08, 0x9d, 0x27, 0x39, 0x83,
+			0x9d, 0xec, 0x58, 0xb9, 0x64, 0xec, 0x38, 0x43
+		}
+	},
+	{
+		{
+			0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+			0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+			0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+			0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa
+		},
+		32,
+		{
+			0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+			0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+			0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+			0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+			0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+			0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+			0xdd, 0xdd
+		},
+		50,
+		{
+			0xcd, 0xcb, 0x12, 0x20, 0xd1, 0xec, 0xcc, 0xea,
+			0x91, 0xe5, 0x3a, 0xba, 0x30, 0x92, 0xf9, 0x62,
+			0xe5, 0x49, 0xfe, 0x6c, 0xe9, 0xed, 0x7f, 0xdc,
+			0x43, 0x19, 0x1f, 0xbd, 0xe4, 0x5c, 0x30, 0xb0
+		}
+	},
+	{
+		{
+			0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
+			0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10,
+			0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
+			0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
+			0x21, 0x22, 0x23, 0x24, 0x25
+		},
+		37,
+		{
+			0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd,
+			0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd,
+			0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd,
+			0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd,
+			0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd,
+			0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd,
+			0xcd, 0xcd
+		},
+		50,
+		{
+			0xd4, 0x63, 0x3c, 0x17, 0xf6, 0xfb, 0x8d, 0x74,
+			0x4c, 0x66, 0xde, 0xe0, 0xf8, 0xf0, 0x74, 0x55,
+			0x6e, 0xc4, 0xaf, 0x55, 0xef, 0x07, 0x99, 0x85,
+			0x41, 0x46, 0x8e, 0xb4, 0x9b, 0xd2, 0xe9, 0x17
+		}
+	},
+	{
+		{
+			0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+			0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+			0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+			0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c
+		},
+		32,
+		"Test With Truncation",
+		20,
+		{
+			0x75, 0x46, 0xaf, 0x01, 0x84, 0x1f, 0xc0, 0x9b,
+			0x1a, 0xb9, 0xc3, 0x74, 0x9a, 0x5f, 0x1c, 0x17,
+			0xd4, 0xf5, 0x89, 0x66, 0x8a, 0x58, 0x7b, 0x27,
+			0x00, 0xa9, 0xc9, 0x7c, 0x11, 0x93, 0xcf, 0x42
+		}
+	},
+	{
+		{
+			0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+			0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+			0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+			0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+			0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+			0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+			0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+			0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+			0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+			0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa
+		},
+		80,
+		"Test Using Larger Than Block-Size Key - Hash Key First",
+		54,
+		{
+			0x69, 0x53, 0x02, 0x5e, 0xd9, 0x6f, 0x0c, 0x09,
+			0xf8, 0x0a, 0x96, 0xf7, 0x8e, 0x65, 0x38, 0xdb,
+			0xe2, 0xe7, 0xb8, 0x20, 0xe3, 0xdd, 0x97, 0x0e,
+			0x7d, 0xdd, 0x39, 0x09, 0x1b, 0x32, 0x35, 0x2f
+		}
+	},
+	{
+		{
+			0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+			0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+			0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+			0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+			0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+			0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+			0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+			0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+			0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+			0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa
+		},
+		80,
+		"Test Using Larger Than Block-Size Key and Larger Than One "
+		"Block-Size Data",
+		73,
+		{
+			0x63, 0x55, 0xac, 0x22, 0xe8, 0x90, 0xd0, 0xa3,
+			0xc8, 0x48, 0x1a, 0x5c, 0xa4, 0x82, 0x5b, 0xc8,
+			0x84, 0xd3, 0xe7, 0xa1, 0xff, 0x98, 0xa2, 0xfc,
+			0x2a, 0xc7, 0xd8, 0xe0, 0x64, 0xc3, 0xb2, 0xe6
+		}
+	}
+};
+
+
+static int test_sha256(void)
+{
+	unsigned int i;
+	u8 hash[32];
+	const u8 *addr[2];
+	size_t len[2];
+	int errors = 0;
+
+	for (i = 0; i < ARRAY_SIZE(tests); i++) {
+		wpa_printf(MSG_INFO, "SHA256 test case %d:", i + 1);
+
+		addr[0] = (u8 *) tests[i].data;
+		len[0] = strlen(tests[i].data);
+		sha256_vector(1, addr, len, hash);
+		if (memcmp(hash, tests[i].hash, 32) != 0) {
+			wpa_printf(MSG_INFO, " FAIL");
+			errors++;
+		} else
+			wpa_printf(MSG_INFO, " OK");
+
+		if (len[0]) {
+			addr[0] = (u8 *) tests[i].data;
+			len[0] = 1;
+			addr[1] = (u8 *) tests[i].data + 1;
+			len[1] = strlen(tests[i].data) - 1;
+			sha256_vector(2, addr, len, hash);
+			if (memcmp(hash, tests[i].hash, 32) != 0) {
+				wpa_printf(MSG_INFO, " FAIL");
+				errors++;
+			} else
+				wpa_printf(MSG_INFO, " OK");
+		}
+	}
+
+	for (i = 0; i < ARRAY_SIZE(hmac_tests); i++) {
+		const struct hmac_test *t = &hmac_tests[i];
+
+		wpa_printf(MSG_INFO, "HMAC-SHA256 test case %d:", i + 1);
+
+		if (hmac_sha256(t->key, t->key_len, t->data, t->data_len,
+				hash) < 0 ||
+		    os_memcmp(hash, t->hash, 32) != 0) {
+			wpa_printf(MSG_INFO, " FAIL");
+			errors++;
+		} else
+			wpa_printf(MSG_INFO, " OK");
+
+		addr[0] = t->data;
+		len[0] = t->data_len;
+		if (hmac_sha256_vector(t->key, t->key_len, 1, addr, len,
+				       hash) < 0 ||
+		    os_memcmp(hash, t->hash, 32) != 0) {
+			wpa_printf(MSG_INFO, " FAIL");
+			errors++;
+		} else
+			wpa_printf(MSG_INFO, " OK");
+
+		if (len[0]) {
+			addr[0] = t->data;
+			len[0] = 1;
+			addr[1] = t->data + 1;
+			len[1] = t->data_len - 1;
+			if (hmac_sha256_vector(t->key, t->key_len, 2, addr, len,
+					       hash) < 0 ||
+			    os_memcmp(hash, t->hash, 32) != 0) {
+				wpa_printf(MSG_INFO, " FAIL");
+				errors++;
+			} else
+				wpa_printf(MSG_INFO, " OK");
+		}
+	}
+
+	wpa_printf(MSG_INFO, "Test IEEE 802.11r KDF");
+	sha256_prf((u8 *) "abc", 3, "KDF test", (u8 *) "data", 4,
+		   hash, sizeof(hash));
+	/* TODO: add proper test case for this */
+
+	if (!errors)
+		wpa_printf(MSG_INFO, "SHA256 test cases passed");
+	return errors;
+}
+
+
+static int test_ms_funcs(void)
+{
+	/* Test vector from RFC2759 example */
+	char *username = "User";
+	char *password = "clientPass";
+	u8 auth_challenge[] = {
+		0x5B, 0x5D, 0x7C, 0x7D, 0x7B, 0x3F, 0x2F, 0x3E,
+		0x3C, 0x2C, 0x60, 0x21, 0x32, 0x26, 0x26, 0x28
+	};
+	u8 peer_challenge[] = {
+		0x21, 0x40, 0x23, 0x24, 0x25, 0x5E, 0x26, 0x2A,
+		0x28, 0x29, 0x5F, 0x2B, 0x3A, 0x33, 0x7C, 0x7E
+	};
+	u8 password_hash[] = {
+		0x44, 0xEB, 0xBA, 0x8D, 0x53, 0x12, 0xB8, 0xD6,
+		0x11, 0x47, 0x44, 0x11, 0xF5, 0x69, 0x89, 0xAE
+	};
+	u8 nt_response[] = {
+		0x82, 0x30, 0x9E, 0xCD, 0x8D, 0x70, 0x8B, 0x5E,
+		0xA0, 0x8F, 0xAA, 0x39, 0x81, 0xCD, 0x83, 0x54,
+		0x42, 0x33, 0x11, 0x4A, 0x3D, 0x85, 0xD6, 0xDF
+	};
+	u8 password_hash_hash[] = {
+		0x41, 0xC0, 0x0C, 0x58, 0x4B, 0xD2, 0xD9, 0x1C,
+		0x40, 0x17, 0xA2, 0xA1, 0x2F, 0xA5, 0x9F, 0x3F
+	};
+	u8 authenticator_response[] = {
+		0x40, 0x7A, 0x55, 0x89, 0x11, 0x5F, 0xD0, 0xD6,
+		0x20, 0x9F, 0x51, 0x0F, 0xE9, 0xC0, 0x45, 0x66,
+		0x93, 0x2C, 0xDA, 0x56
+	};
+	u8 master_key[] = {
+		0xFD, 0xEC, 0xE3, 0x71, 0x7A, 0x8C, 0x83, 0x8C,
+		0xB3, 0x88, 0xE5, 0x27, 0xAE, 0x3C, 0xDD, 0x31
+	};
+	u8 send_start_key[] = {
+		0x8B, 0x7C, 0xDC, 0x14, 0x9B, 0x99, 0x3A, 0x1B,
+		0xA1, 0x18, 0xCB, 0x15, 0x3F, 0x56, 0xDC, 0xCB
+	};
+	u8 buf[32];
+	int errors = 0;
+
+	if (nt_password_hash((u8 *) password, os_strlen(password), buf) ||
+	    os_memcmp(password_hash, buf, sizeof(password_hash)) != 0) {
+		wpa_printf(MSG_ERROR, "nt_password_hash failed");
+		errors++;
+	}
+
+	if (generate_nt_response(auth_challenge, peer_challenge,
+				 (u8 *) username, os_strlen(username),
+				 (u8 *) password, os_strlen(password), buf) ||
+	    os_memcmp(nt_response, buf, sizeof(nt_response)) != 0) {
+		wpa_printf(MSG_ERROR, "generate_nt_response failed");
+		errors++;
+	}
+
+	if (hash_nt_password_hash(password_hash, buf) ||
+	    os_memcmp(password_hash_hash, buf,
+		      sizeof(password_hash_hash)) != 0) {
+		wpa_printf(MSG_ERROR, "hash_nt_password_hash failed");
+		errors++;
+	}
+
+	if (generate_authenticator_response((u8 *) password,
+					    os_strlen(password),
+					    peer_challenge, auth_challenge,
+					    (u8 *) username,
+					    os_strlen(username),
+					    nt_response, buf) ||
+	    os_memcmp(authenticator_response, buf,
+		      sizeof(authenticator_response)) != 0) {
+		wpa_printf(MSG_ERROR, "generate_authenticator_response failed");
+		errors++;
+	}
+
+	if (get_master_key(password_hash_hash, nt_response, buf) ||
+	    os_memcmp(master_key, buf, sizeof(master_key)) != 0) {
+		wpa_printf(MSG_ERROR, "get_master_key failed");
+		errors++;
+	}
+
+	if (get_asymetric_start_key(master_key, buf, sizeof(send_start_key),
+				    1, 1) ||
+	    os_memcmp(send_start_key, buf, sizeof(send_start_key)) != 0) {
+		wpa_printf(MSG_ERROR, "get_asymetric_start_key failed");
+		errors++;
+	}
+
+	if (errors)
+		wpa_printf(MSG_ERROR, "ms_funcs: %d errors", errors);
+	else
+		wpa_printf(MSG_INFO, "ms_funcs test cases passed");
+
+	return errors;
+}
+
+
+int crypto_module_tests(void)
+{
+	int ret = 0;
+
+	wpa_printf(MSG_INFO, "crypto module tests");
+	if (test_siv() ||
+	    test_omac1() ||
+	    test_eax() ||
+	    test_cbc() ||
+	    test_ecb() ||
+	    test_key_wrap() ||
+	    test_md5() ||
+	    test_sha1() ||
+	    test_sha256() ||
+	    test_ms_funcs())
+		ret = -1;
+
+	return ret;
+}
diff --git a/src/crypto/crypto_nss.c b/src/crypto/crypto_nss.c
deleted file mode 100644
index acd0a55..0000000
--- a/src/crypto/crypto_nss.c
+++ /dev/null
@@ -1,207 +0,0 @@
-/*
- * Crypto wrapper functions for NSS
- * Copyright (c) 2009, Jouni Malinen <j@w1.fi>
- *
- * This software may be distributed under the terms of the BSD license.
- * See README for more details.
- */
-
-#include "includes.h"
-#include <nspr/prtypes.h>
-#include <nspr/plarenas.h>
-#include <nspr/plhash.h>
-#include <nspr/prtime.h>
-#include <nspr/prinrval.h>
-#include <nspr/prclist.h>
-#include <nspr/prlock.h>
-#include <nss/sechash.h>
-#include <nss/pk11pub.h>
-
-#include "common.h"
-#include "crypto.h"
-
-
-static int nss_hash(HASH_HashType type, unsigned int max_res_len,
-		    size_t num_elem, const u8 *addr[], const size_t *len,
-		    u8 *mac)
-{
-	HASHContext *ctx;
-	size_t i;
-	unsigned int reslen;
-
-	ctx = HASH_Create(type);
-	if (ctx == NULL)
-		return -1;
-
-	HASH_Begin(ctx);
-	for (i = 0; i < num_elem; i++)
-		HASH_Update(ctx, addr[i], len[i]);
-	HASH_End(ctx, mac, &reslen, max_res_len);
-	HASH_Destroy(ctx);
-
-	return 0;
-}
-
-
-void des_encrypt(const u8 *clear, const u8 *key, u8 *cypher)
-{
-	PK11Context *ctx = NULL;
-	PK11SlotInfo *slot;
-	SECItem *param = NULL;
-	PK11SymKey *symkey = NULL;
-	SECItem item;
-	int olen;
-	u8 pkey[8], next, tmp;
-	int i;
-
-	/* Add parity bits to the key */
-	next = 0;
-	for (i = 0; i < 7; i++) {
-		tmp = key[i];
-		pkey[i] = (tmp >> i) | next | 1;
-		next = tmp << (7 - i);
-	}
-	pkey[i] = next | 1;
-
-	slot = PK11_GetBestSlot(CKM_DES_ECB, NULL);
-	if (slot == NULL) {
-		wpa_printf(MSG_ERROR, "NSS: PK11_GetBestSlot failed");
-		goto out;
-	}
-
-	item.type = siBuffer;
-	item.data = pkey;
-	item.len = 8;
-	symkey = PK11_ImportSymKey(slot, CKM_DES_ECB, PK11_OriginDerive,
-				   CKA_ENCRYPT, &item, NULL);
-	if (symkey == NULL) {
-		wpa_printf(MSG_ERROR, "NSS: PK11_ImportSymKey failed");
-		goto out;
-	}
-
-	param = PK11_GenerateNewParam(CKM_DES_ECB, symkey);
-	if (param == NULL) {
-		wpa_printf(MSG_ERROR, "NSS: PK11_GenerateNewParam failed");
-		goto out;
-	}
-
-	ctx = PK11_CreateContextBySymKey(CKM_DES_ECB, CKA_ENCRYPT,
-					 symkey, param);
-	if (ctx == NULL) {
-		wpa_printf(MSG_ERROR, "NSS: PK11_CreateContextBySymKey("
-			   "CKM_DES_ECB) failed");
-		goto out;
-	}
-
-	if (PK11_CipherOp(ctx, cypher, &olen, 8, (void *) clear, 8) !=
-	    SECSuccess) {
-		wpa_printf(MSG_ERROR, "NSS: PK11_CipherOp failed");
-		goto out;
-	}
-
-out:
-	if (ctx)
-		PK11_DestroyContext(ctx, PR_TRUE);
-	if (symkey)
-		PK11_FreeSymKey(symkey);
-	if (param)
-		SECITEM_FreeItem(param, PR_TRUE);
-}
-
-
-int rc4_skip(const u8 *key, size_t keylen, size_t skip,
-	     u8 *data, size_t data_len)
-{
-	return -1;
-}
-
-
-int md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
-{
-	return nss_hash(HASH_AlgMD5, 16, num_elem, addr, len, mac);
-}
-
-
-int sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
-{
-	return nss_hash(HASH_AlgSHA1, 20, num_elem, addr, len, mac);
-}
-
-
-int sha256_vector(size_t num_elem, const u8 *addr[], const size_t *len,
-		  u8 *mac)
-{
-	return nss_hash(HASH_AlgSHA256, 32, num_elem, addr, len, mac);
-}
-
-
-void * aes_encrypt_init(const u8 *key, size_t len)
-{
-	return NULL;
-}
-
-
-void aes_encrypt(void *ctx, const u8 *plain, u8 *crypt)
-{
-}
-
-
-void aes_encrypt_deinit(void *ctx)
-{
-}
-
-
-void * aes_decrypt_init(const u8 *key, size_t len)
-{
-	return NULL;
-}
-
-
-void aes_decrypt(void *ctx, const u8 *crypt, u8 *plain)
-{
-}
-
-
-void aes_decrypt_deinit(void *ctx)
-{
-}
-
-
-int crypto_mod_exp(const u8 *base, size_t base_len,
-		   const u8 *power, size_t power_len,
-		   const u8 *modulus, size_t modulus_len,
-		   u8 *result, size_t *result_len)
-{
-	return -1;
-}
-
-
-struct crypto_cipher {
-};
-
-
-struct crypto_cipher * crypto_cipher_init(enum crypto_cipher_alg alg,
-					  const u8 *iv, const u8 *key,
-					  size_t key_len)
-{
-	return NULL;
-}
-
-
-int crypto_cipher_encrypt(struct crypto_cipher *ctx, const u8 *plain,
-			  u8 *crypt, size_t len)
-{
-	return -1;
-}
-
-
-int crypto_cipher_decrypt(struct crypto_cipher *ctx, const u8 *crypt,
-			  u8 *plain, size_t len)
-{
-	return -1;
-}
-
-
-void crypto_cipher_deinit(struct crypto_cipher *ctx)
-{
-}
diff --git a/src/crypto/crypto_openssl.c b/src/crypto/crypto_openssl.c
index b4c59d1..3703b93 100644
--- a/src/crypto/crypto_openssl.c
+++ b/src/crypto/crypto_openssl.c
@@ -1,6 +1,6 @@
 /*
  * Wrapper functions for OpenSSL libcrypto
- * 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.
@@ -28,19 +28,12 @@
 #include "dh_group5.h"
 #include "sha1.h"
 #include "sha256.h"
+#include "sha384.h"
 #include "crypto.h"
 
-#if OPENSSL_VERSION_NUMBER < 0x00907000
-#define DES_key_schedule des_key_schedule
-#define DES_cblock des_cblock
-#define DES_set_key(key, schedule) des_set_key((key), *(schedule))
-#define DES_ecb_encrypt(input, output, ks, enc) \
-	des_ecb_encrypt((input), (output), *(ks), (enc))
-#endif /* openssl < 0.9.7 */
-
 static BIGNUM * get_group5_prime(void)
 {
-#if OPENSSL_VERSION_NUMBER < 0x00908000 || defined(OPENSSL_IS_BORINGSSL)
+#ifdef OPENSSL_IS_BORINGSSL
 	static const unsigned char RFC3526_PRIME_1536[] = {
 		0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xC9,0x0F,0xDA,0xA2,
 		0x21,0x68,0xC2,0x34,0xC4,0xC6,0x62,0x8B,0x80,0xDC,0x1C,0xD1,
@@ -60,20 +53,11 @@
 		0xCA,0x23,0x73,0x27,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
 	};
         return BN_bin2bn(RFC3526_PRIME_1536, sizeof(RFC3526_PRIME_1536), NULL);
-#else /* openssl < 0.9.8 */
+#else /* OPENSSL_IS_BORINGSSL */
 	return get_rfc3526_prime_1536(NULL);
-#endif /* openssl < 0.9.8 */
+#endif /* OPENSSL_IS_BORINGSSL */
 }
 
-#if OPENSSL_VERSION_NUMBER < 0x00908000
-#ifndef OPENSSL_NO_SHA256
-#ifndef OPENSSL_FIPS
-#define NO_SHA256_WRAPPER
-#endif
-#endif
-
-#endif /* openssl < 0.9.8 */
-
 #ifdef OPENSSL_NO_SHA256
 #define NO_SHA256_WRAPPER
 #endif
@@ -258,7 +242,7 @@
 			   "in AES encrypt", len);
 	}
 	EVP_CIPHER_CTX_cleanup(c);
-	os_free(c);
+	bin_clear_free(c, sizeof(*c));
 }
 
 
@@ -309,7 +293,84 @@
 			   "in AES decrypt", len);
 	}
 	EVP_CIPHER_CTX_cleanup(c);
-	os_free(ctx);
+	bin_clear_free(c, sizeof(*c));
+}
+
+
+int aes_wrap(const u8 *kek, size_t kek_len, int n, const u8 *plain, u8 *cipher)
+{
+	AES_KEY actx;
+	int res;
+
+	if (AES_set_encrypt_key(kek, kek_len << 3, &actx))
+		return -1;
+	res = AES_wrap_key(&actx, NULL, cipher, plain, n * 8);
+	OPENSSL_cleanse(&actx, sizeof(actx));
+	return res <= 0 ? -1 : 0;
+}
+
+
+int aes_unwrap(const u8 *kek, size_t kek_len, int n, const u8 *cipher,
+	       u8 *plain)
+{
+	AES_KEY actx;
+	int res;
+
+	if (AES_set_decrypt_key(kek, kek_len << 3, &actx))
+		return -1;
+	res = AES_unwrap_key(&actx, NULL, plain, cipher, (n + 1) * 8);
+	OPENSSL_cleanse(&actx, sizeof(actx));
+	return res <= 0 ? -1 : 0;
+}
+
+
+int aes_128_cbc_encrypt(const u8 *key, const u8 *iv, u8 *data, size_t data_len)
+{
+	EVP_CIPHER_CTX ctx;
+	int clen, len;
+	u8 buf[16];
+
+	EVP_CIPHER_CTX_init(&ctx);
+	if (EVP_EncryptInit_ex(&ctx, EVP_aes_128_cbc(), NULL, key, iv) != 1)
+		return -1;
+	EVP_CIPHER_CTX_set_padding(&ctx, 0);
+
+	clen = data_len;
+	if (EVP_EncryptUpdate(&ctx, data, &clen, data, data_len) != 1 ||
+	    clen != (int) data_len)
+		return -1;
+
+	len = sizeof(buf);
+	if (EVP_EncryptFinal_ex(&ctx, buf, &len) != 1 || len != 0)
+		return -1;
+	EVP_CIPHER_CTX_cleanup(&ctx);
+
+	return 0;
+}
+
+
+int aes_128_cbc_decrypt(const u8 *key, const u8 *iv, u8 *data, size_t data_len)
+{
+	EVP_CIPHER_CTX ctx;
+	int plen, len;
+	u8 buf[16];
+
+	EVP_CIPHER_CTX_init(&ctx);
+	if (EVP_DecryptInit_ex(&ctx, EVP_aes_128_cbc(), NULL, key, iv) != 1)
+		return -1;
+	EVP_CIPHER_CTX_set_padding(&ctx, 0);
+
+	plen = data_len;
+	if (EVP_DecryptUpdate(&ctx, data, &plen, data, data_len) != 1 ||
+	    plen != (int) data_len)
+		return -1;
+
+	len = sizeof(buf);
+	if (EVP_DecryptFinal_ex(&ctx, buf, &len) != 1 || len != 0)
+		return -1;
+	EVP_CIPHER_CTX_cleanup(&ctx);
+
+	return 0;
 }
 
 
@@ -507,8 +568,8 @@
 	return dh;
 
 err:
-	wpabuf_free(pubkey);
-	wpabuf_free(privkey);
+	wpabuf_clear_free(pubkey);
+	wpabuf_clear_free(privkey);
 	DH_free(dh);
 	return NULL;
 }
@@ -581,7 +642,7 @@
 
 err:
 	BN_clear_free(pub_key);
-	wpabuf_free(res);
+	wpabuf_clear_free(res);
 	return NULL;
 }
 
@@ -638,7 +699,7 @@
 	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) {
-		os_free(ctx);
+		bin_clear_free(ctx, sizeof(*ctx));
 		return NULL;
 	}
 #endif /* openssl < 0.9.9 */
@@ -664,7 +725,7 @@
 		return -2;
 
 	if (mac == NULL || len == NULL) {
-		os_free(ctx);
+		bin_clear_free(ctx, sizeof(*ctx));
 		return 0;
 	}
 
@@ -676,7 +737,7 @@
 	res = HMAC_Final(&ctx->ctx, mac, &mdlen);
 #endif /* openssl < 0.9.9 */
 	HMAC_CTX_cleanup(&ctx->ctx);
-	os_free(ctx);
+	bin_clear_free(ctx, sizeof(*ctx));
 
 	if (res == 1) {
 		*len = mdlen;
@@ -687,43 +748,26 @@
 }
 
 
-int pbkdf2_sha1(const char *passphrase, const u8 *ssid, size_t ssid_len,
-		int iterations, u8 *buf, size_t buflen)
-{
-#if OPENSSL_VERSION_NUMBER < 0x00908000
-	if (PKCS5_PBKDF2_HMAC_SHA1(passphrase, os_strlen(passphrase),
-				   (unsigned char *) ssid,
-				   ssid_len, 4096, buflen, buf) != 1)
-		return -1;
-#else /* openssl < 0.9.8 */
-	if (PKCS5_PBKDF2_HMAC_SHA1(passphrase, os_strlen(passphrase), ssid,
-				   ssid_len, 4096, buflen, buf) != 1)
-		return -1;
-#endif /* openssl < 0.9.8 */
-	return 0;
-}
-
-
-int hmac_sha1_vector(const u8 *key, size_t key_len, size_t num_elem,
-		     const u8 *addr[], const size_t *len, u8 *mac)
+static int openssl_hmac_vector(const EVP_MD *type, const u8 *key,
+			       size_t key_len, size_t num_elem,
+			       const u8 *addr[], const size_t *len, u8 *mac,
+			       unsigned int mdlen)
 {
 	HMAC_CTX ctx;
 	size_t i;
-	unsigned int mdlen;
 	int res;
 
 	HMAC_CTX_init(&ctx);
 #if OPENSSL_VERSION_NUMBER < 0x00909000
-	HMAC_Init_ex(&ctx, key, key_len, EVP_sha1(), NULL);
+	HMAC_Init_ex(&ctx, key, key_len, type, NULL);
 #else /* openssl < 0.9.9 */
-	if (HMAC_Init_ex(&ctx, key, key_len, EVP_sha1(), NULL) != 1)
+	if (HMAC_Init_ex(&ctx, key, key_len, type, NULL) != 1)
 		return -1;
 #endif /* openssl < 0.9.9 */
 
 	for (i = 0; i < num_elem; i++)
 		HMAC_Update(&ctx, addr[i], len[i]);
 
-	mdlen = 20;
 #if OPENSSL_VERSION_NUMBER < 0x00909000
 	HMAC_Final(&ctx, mac, &mdlen);
 	res = 1;
@@ -736,6 +780,43 @@
 }
 
 
+#ifndef CONFIG_FIPS
+
+int hmac_md5_vector(const u8 *key, size_t key_len, size_t num_elem,
+		    const u8 *addr[], const size_t *len, u8 *mac)
+{
+	return openssl_hmac_vector(EVP_md5(), key ,key_len, num_elem, addr, len,
+				   mac, 16);
+}
+
+
+int hmac_md5(const u8 *key, size_t key_len, const u8 *data, size_t data_len,
+	     u8 *mac)
+{
+	return hmac_md5_vector(key, key_len, 1, &data, &data_len, mac);
+}
+
+#endif /* CONFIG_FIPS */
+
+
+int pbkdf2_sha1(const char *passphrase, const u8 *ssid, size_t ssid_len,
+		int iterations, u8 *buf, size_t buflen)
+{
+	if (PKCS5_PBKDF2_HMAC_SHA1(passphrase, os_strlen(passphrase), ssid,
+				   ssid_len, iterations, buflen, buf) != 1)
+		return -1;
+	return 0;
+}
+
+
+int hmac_sha1_vector(const u8 *key, size_t key_len, size_t num_elem,
+		     const u8 *addr[], const size_t *len, u8 *mac)
+{
+	return openssl_hmac_vector(EVP_sha1(), key, key_len, num_elem, addr,
+				   len, mac, 20);
+}
+
+
 int hmac_sha1(const u8 *key, size_t key_len, const u8 *data, size_t data_len,
 	       u8 *mac)
 {
@@ -748,32 +829,8 @@
 int hmac_sha256_vector(const u8 *key, size_t key_len, size_t num_elem,
 		       const u8 *addr[], const size_t *len, u8 *mac)
 {
-	HMAC_CTX ctx;
-	size_t i;
-	unsigned int mdlen;
-	int res;
-
-	HMAC_CTX_init(&ctx);
-#if OPENSSL_VERSION_NUMBER < 0x00909000
-	HMAC_Init_ex(&ctx, key, key_len, EVP_sha256(), NULL);
-#else /* openssl < 0.9.9 */
-	if (HMAC_Init_ex(&ctx, key, key_len, EVP_sha256(), NULL) != 1)
-		return -1;
-#endif /* openssl < 0.9.9 */
-
-	for (i = 0; i < num_elem; i++)
-		HMAC_Update(&ctx, addr[i], len[i]);
-
-	mdlen = 32;
-#if OPENSSL_VERSION_NUMBER < 0x00909000
-	HMAC_Final(&ctx, mac, &mdlen);
-	res = 1;
-#else /* openssl < 0.9.9 */
-	res = HMAC_Final(&ctx, mac, &mdlen);
-#endif /* openssl < 0.9.9 */
-	HMAC_CTX_cleanup(&ctx);
-
-	return res == 1 ? 0 : -1;
+	return openssl_hmac_vector(EVP_sha256(), key, key_len, num_elem, addr,
+				   len, mac, 32);
 }
 
 
@@ -786,6 +843,25 @@
 #endif /* CONFIG_SHA256 */
 
 
+#ifdef CONFIG_SHA384
+
+int hmac_sha384_vector(const u8 *key, size_t key_len, size_t num_elem,
+		       const u8 *addr[], const size_t *len, u8 *mac)
+{
+	return openssl_hmac_vector(EVP_sha384(), key, key_len, num_elem, addr,
+				   len, mac, 32);
+}
+
+
+int hmac_sha384(const u8 *key, size_t key_len, const u8 *data,
+		size_t data_len, u8 *mac)
+{
+	return hmac_sha384_vector(key, key_len, 1, &data, &data_len, mac);
+}
+
+#endif /* CONFIG_SHA384 */
+
+
 int crypto_get_random(void *buf, size_t len)
 {
 	if (RAND_bytes(buf, len) != 1)
@@ -795,8 +871,8 @@
 
 
 #ifdef CONFIG_OPENSSL_CMAC
-int omac1_aes_128_vector(const u8 *key, size_t num_elem,
-			 const u8 *addr[], const size_t *len, u8 *mac)
+int omac1_aes_vector(const u8 *key, size_t key_len, size_t num_elem,
+		     const u8 *addr[], const size_t *len, u8 *mac)
 {
 	CMAC_CTX *ctx;
 	int ret = -1;
@@ -806,8 +882,15 @@
 	if (ctx == NULL)
 		return -1;
 
-	if (!CMAC_Init(ctx, key, 16, EVP_aes_128_cbc(), NULL))
+	if (key_len == 32) {
+		if (!CMAC_Init(ctx, key, 32, EVP_aes_256_cbc(), NULL))
+			goto fail;
+	} else if (key_len == 16) {
+		if (!CMAC_Init(ctx, key, 16, EVP_aes_128_cbc(), NULL))
+			goto fail;
+	} else {
 		goto fail;
+	}
 	for (i = 0; i < num_elem; i++) {
 		if (!CMAC_Update(ctx, addr[i], len[i]))
 			goto fail;
@@ -822,10 +905,23 @@
 }
 
 
+int omac1_aes_128_vector(const u8 *key, size_t num_elem,
+			 const u8 *addr[], const size_t *len, u8 *mac)
+{
+	return omac1_aes_vector(key, 16, num_elem, addr, len, mac);
+}
+
+
 int omac1_aes_128(const u8 *key, const u8 *data, size_t data_len, u8 *mac)
 {
 	return omac1_aes_128_vector(key, 1, &data, &data_len, mac);
 }
+
+
+int omac1_aes_256(const u8 *key, const u8 *data, size_t data_len, u8 *mac)
+{
+	return omac1_aes_vector(key, 32, 1, &data, &data_len, mac);
+}
 #endif /* CONFIG_OPENSSL_CMAC */
 
 
@@ -1011,6 +1107,42 @@
 }
 
 
+int crypto_bignum_legendre(const struct crypto_bignum *a,
+			   const struct crypto_bignum *p)
+{
+	BN_CTX *bnctx;
+	BIGNUM *exp = NULL, *tmp = NULL;
+	int res = -2;
+
+	bnctx = BN_CTX_new();
+	if (bnctx == NULL)
+		return -2;
+
+	exp = BN_new();
+	tmp = BN_new();
+	if (!exp || !tmp ||
+	    /* exp = (p-1) / 2 */
+	    !BN_sub(exp, (const BIGNUM *) p, BN_value_one()) ||
+	    !BN_rshift1(exp, exp) ||
+	    !BN_mod_exp(tmp, (const BIGNUM *) a, exp, (const BIGNUM *) p,
+			bnctx))
+		goto fail;
+
+	if (BN_is_word(tmp, 1))
+		res = 1;
+	else if (BN_is_zero(tmp))
+		res = 0;
+	else
+		res = -1;
+
+fail:
+	BN_clear_free(tmp);
+	BN_clear_free(exp);
+	BN_CTX_free(bnctx);
+	return res;
+}
+
+
 #ifdef CONFIG_ECC
 
 struct crypto_ec {
@@ -1018,6 +1150,8 @@
 	BN_CTX *bnctx;
 	BIGNUM *prime;
 	BIGNUM *order;
+	BIGNUM *a;
+	BIGNUM *b;
 };
 
 struct crypto_ec * crypto_ec_init(int group)
@@ -1042,6 +1176,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:
 		return NULL;
 	}
@@ -1054,9 +1208,11 @@
 	e->group = EC_GROUP_new_by_curve_name(nid);
 	e->prime = BN_new();
 	e->order = BN_new();
+	e->a = BN_new();
+	e->b = BN_new();
 	if (e->group == NULL || e->bnctx == NULL || e->prime == NULL ||
-	    e->order == NULL ||
-	    !EC_GROUP_get_curve_GFp(e->group, e->prime, NULL, NULL, e->bnctx) ||
+	    e->order == NULL || e->a == NULL || e->b == NULL ||
+	    !EC_GROUP_get_curve_GFp(e->group, e->prime, e->a, e->b, e->bnctx) ||
 	    !EC_GROUP_get_order(e->group, e->order, e->bnctx)) {
 		crypto_ec_deinit(e);
 		e = NULL;
@@ -1070,6 +1226,8 @@
 {
 	if (e == NULL)
 		return;
+	BN_clear_free(e->b);
+	BN_clear_free(e->a);
 	BN_clear_free(e->order);
 	BN_clear_free(e->prime);
 	EC_GROUP_free(e->group);
@@ -1217,6 +1375,33 @@
 }
 
 
+struct crypto_bignum *
+crypto_ec_point_compute_y_sqr(struct crypto_ec *e,
+			      const struct crypto_bignum *x)
+{
+	BIGNUM *tmp, *tmp2, *y_sqr = NULL;
+
+	tmp = BN_new();
+	tmp2 = BN_new();
+
+	/* y^2 = x^3 + ax + b */
+	if (tmp && tmp2 &&
+	    BN_mod_sqr(tmp, (const BIGNUM *) x, e->prime, e->bnctx) &&
+	    BN_mod_mul(tmp, tmp, (const BIGNUM *) x, e->prime, e->bnctx) &&
+	    BN_mod_mul(tmp2, e->a, (const BIGNUM *) x, e->prime, e->bnctx) &&
+	    BN_mod_add_quick(tmp2, tmp2, tmp, e->prime) &&
+	    BN_mod_add_quick(tmp2, tmp2, e->b, e->prime)) {
+		y_sqr = tmp2;
+		tmp2 = NULL;
+	}
+
+	BN_clear_free(tmp);
+	BN_clear_free(tmp2);
+
+	return (struct crypto_bignum *) y_sqr;
+}
+
+
 int crypto_ec_point_is_at_infinity(struct crypto_ec *e,
 				   const struct crypto_ec_point *p)
 {
@@ -1227,7 +1412,17 @@
 int crypto_ec_point_is_on_curve(struct crypto_ec *e,
 				const struct crypto_ec_point *p)
 {
-	return EC_POINT_is_on_curve(e->group, (const EC_POINT *) p, e->bnctx);
+	return EC_POINT_is_on_curve(e->group, (const EC_POINT *) p,
+				    e->bnctx) == 1;
+}
+
+
+int crypto_ec_point_cmp(const struct crypto_ec *e,
+			const struct crypto_ec_point *a,
+			const struct crypto_ec_point *b)
+{
+	return EC_POINT_cmp(e->group, (const EC_POINT *) a,
+			    (const EC_POINT *) b, e->bnctx);
 }
 
 #endif /* CONFIG_ECC */
diff --git a/src/crypto/dh_groups.c b/src/crypto/dh_groups.c
index 58e94c3..3aeb2bb 100644
--- a/src/crypto/dh_groups.c
+++ b/src/crypto/dh_groups.c
@@ -1153,7 +1153,7 @@
 dh_group ## id ## _order, sizeof(dh_group ## id ## _order), safe }
 		
 
-static struct dh_group dh_groups[] = {
+static const struct dh_group dh_groups[] = {
 	DH_GROUP(5, 1),
 #ifdef ALL_DH_GROUPS
 	DH_GROUP(1, 1),
@@ -1198,14 +1198,14 @@
 	if (dh == NULL)
 		return NULL;
 
-	wpabuf_free(*priv);
+	wpabuf_clear_free(*priv);
 	*priv = wpabuf_alloc(dh->prime_len);
 	if (*priv == NULL)
 		return NULL;
 
 	if (random_get_bytes(wpabuf_put(*priv, dh->prime_len), dh->prime_len))
 	{
-		wpabuf_free(*priv);
+		wpabuf_clear_free(*priv);
 		*priv = NULL;
 		return NULL;
 	}
@@ -1224,7 +1224,7 @@
 			   wpabuf_head(*priv), wpabuf_len(*priv),
 			   dh->prime, dh->prime_len, wpabuf_mhead(pv),
 			   &pv_len) < 0) {
-		wpabuf_free(pv);
+		wpabuf_clear_free(pv);
 		wpa_printf(MSG_INFO, "DH: crypto_mod_exp failed");
 		return NULL;
 	}
@@ -1260,7 +1260,7 @@
 			   wpabuf_head(own_private), wpabuf_len(own_private),
 			   dh->prime, dh->prime_len,
 			   wpabuf_mhead(shared), &shared_len) < 0) {
-		wpabuf_free(shared);
+		wpabuf_clear_free(shared);
 		wpa_printf(MSG_INFO, "DH: crypto_mod_exp failed");
 		return NULL;
 	}
diff --git a/src/crypto/fips_prf_openssl.c b/src/crypto/fips_prf_openssl.c
index d69ecea..fb03efc 100644
--- a/src/crypto/fips_prf_openssl.c
+++ b/src/crypto/fips_prf_openssl.c
@@ -13,13 +13,21 @@
 #include "crypto.h"
 
 
-static void sha1_transform(u8 *state, const u8 data[64])
+static void sha1_transform(u32 *state, const u8 data[64])
 {
 	SHA_CTX context;
 	os_memset(&context, 0, sizeof(context));
-	os_memcpy(&context.h0, state, 5 * 4);
+	context.h0 = state[0];
+	context.h1 = state[1];
+	context.h2 = state[2];
+	context.h3 = state[3];
+	context.h4 = state[4];
 	SHA1_Transform(&context, data);
-	os_memcpy(state, &context.h0, 5 * 4);
+	state[0] = context.h0;
+	state[1] = context.h1;
+	state[2] = context.h2;
+	state[3] = context.h3;
+	state[4] = context.h4;
 }
 
 
@@ -53,7 +61,7 @@
 
 			/* w_i = G(t, XVAL) */
 			os_memcpy(_t, t, 20);
-			sha1_transform((u8 *) _t, xkey);
+			sha1_transform(_t, xkey);
 			_t[0] = host_to_be32(_t[0]);
 			_t[1] = host_to_be32(_t[1]);
 			_t[2] = host_to_be32(_t[2]);
diff --git a/src/crypto/md5.c b/src/crypto/md5.c
index db2b8cc..f64dfd3 100644
--- a/src/crypto/md5.c
+++ b/src/crypto/md5.c
@@ -30,6 +30,7 @@
 	u8 tk[16];
 	const u8 *_addr[6];
 	size_t i, _len[6];
+	int res;
 
 	if (num_elem > 5) {
 		/*
@@ -85,7 +86,10 @@
 	_len[0] = 64;
 	_addr[1] = mac;
 	_len[1] = MD5_MAC_LEN;
-	return md5_vector(2, _addr, _len, mac);
+	res = md5_vector(2, _addr, _len, mac);
+	os_memset(k_pad, 0, sizeof(k_pad));
+	os_memset(tk, 0, sizeof(tk));
+	return res;
 }
 
 
diff --git a/src/crypto/ms_funcs.c b/src/crypto/ms_funcs.c
index 49a5c1c..5f57656 100644
--- a/src/crypto/ms_funcs.c
+++ b/src/crypto/ms_funcs.c
@@ -78,9 +78,8 @@
  * @challenge: 8-octet Challenge (OUT)
  * Returns: 0 on success, -1 on failure
  */
-static int challenge_hash(const u8 *peer_challenge, const u8 *auth_challenge,
-			  const u8 *username, size_t username_len,
-			  u8 *challenge)
+int challenge_hash(const u8 *peer_challenge, const u8 *auth_challenge,
+		   const u8 *username, size_t username_len, u8 *challenge)
 {
 	u8 hash[SHA1_MAC_LEN];
 	const unsigned char *addr[3];
diff --git a/src/crypto/ms_funcs.h b/src/crypto/ms_funcs.h
index bd9bfee..b5b5918 100644
--- a/src/crypto/ms_funcs.h
+++ b/src/crypto/ms_funcs.h
@@ -33,6 +33,8 @@
 
 void challenge_response(const u8 *challenge, const u8 *password_hash,
 			u8 *response);
+int challenge_hash(const u8 *peer_challenge, const u8 *auth_challenge,
+		   const u8 *username, size_t username_len, u8 *challenge);
 int nt_password_hash(const u8 *password, size_t password_len,
 		     u8 *password_hash);
 int hash_nt_password_hash(const u8 *password_hash, u8 *password_hash_hash);
diff --git a/src/crypto/random.c b/src/crypto/random.c
index 053740e..bc758aa 100644
--- a/src/crypto/random.c
+++ b/src/crypto/random.c
@@ -232,12 +232,8 @@
 	 */
 	fd = open("/dev/random", O_RDONLY | O_NONBLOCK);
 	if (fd < 0) {
-#ifndef CONFIG_NO_STDOUT_DEBUG
-		int error = errno;
-		perror("open(/dev/random)");
 		wpa_printf(MSG_ERROR, "random: Cannot open /dev/random: %s",
-			   strerror(error));
-#endif /* CONFIG_NO_STDOUT_DEBUG */
+			   strerror(errno));
 		return -1;
 	}
 
@@ -417,12 +413,8 @@
 
 	random_fd = open("/dev/random", O_RDONLY | O_NONBLOCK);
 	if (random_fd < 0) {
-#ifndef CONFIG_NO_STDOUT_DEBUG
-		int error = errno;
-		perror("open(/dev/random)");
 		wpa_printf(MSG_ERROR, "random: Cannot open /dev/random: %s",
-			   strerror(error));
-#endif /* CONFIG_NO_STDOUT_DEBUG */
+			   strerror(errno));
 		return;
 	}
 	wpa_printf(MSG_DEBUG, "random: Trying to read entropy from "
diff --git a/src/crypto/sha1-prf.c b/src/crypto/sha1-prf.c
index 90b9e74..4b2d137 100644
--- a/src/crypto/sha1-prf.c
+++ b/src/crypto/sha1-prf.c
@@ -61,6 +61,7 @@
 		}
 		counter++;
 	}
+	os_memset(hash, 0, sizeof(hash));
 
 	return 0;
 }
diff --git a/src/crypto/sha1-tlsprf.c b/src/crypto/sha1-tlsprf.c
index 0effd9b..f9bc0eb 100644
--- a/src/crypto/sha1-tlsprf.c
+++ b/src/crypto/sha1-tlsprf.c
@@ -95,5 +95,10 @@
 		SHA1_pos++;
 	}
 
+	os_memset(A_MD5, 0, MD5_MAC_LEN);
+	os_memset(P_MD5, 0, MD5_MAC_LEN);
+	os_memset(A_SHA1, 0, SHA1_MAC_LEN);
+	os_memset(P_SHA1, 0, SHA1_MAC_LEN);
+
 	return 0;
 }
diff --git a/src/crypto/sha1-tprf.c b/src/crypto/sha1-tprf.c
index a529494..562510f 100644
--- a/src/crypto/sha1-tprf.c
+++ b/src/crypto/sha1-tprf.c
@@ -66,5 +66,7 @@
 		len[0] = SHA1_MAC_LEN;
 	}
 
+	os_memset(hash, 0, SHA1_MAC_LEN);
+
 	return 0;
 }
diff --git a/src/crypto/sha1.c b/src/crypto/sha1.c
index d48c77d..8fce139 100644
--- a/src/crypto/sha1.c
+++ b/src/crypto/sha1.c
@@ -30,6 +30,7 @@
 	unsigned char tk[20];
 	const u8 *_addr[6];
 	size_t _len[6], i;
+	int ret;
 
 	if (num_elem > 5) {
 		/*
@@ -84,7 +85,9 @@
 	_len[0] = 64;
 	_addr[1] = mac;
 	_len[1] = SHA1_MAC_LEN;
-	return sha1_vector(2, _addr, _len, mac);
+	ret = sha1_vector(2, _addr, _len, mac);
+	os_memset(k_pad, 0, sizeof(k_pad));
+	return ret;
 }
 
 
diff --git a/src/crypto/sha256-kdf.c b/src/crypto/sha256-kdf.c
new file mode 100644
index 0000000..e7509ce
--- /dev/null
+++ b/src/crypto/sha256-kdf.c
@@ -0,0 +1,79 @@
+/*
+ * HMAC-SHA256 KDF (RFC 5295)
+ * Copyright (c) 2014, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "sha256.h"
+
+
+/**
+ * hmac_sha256_kdf - HMAC-SHA256 based KDF (RFC 5295)
+ * @secret: Key for KDF
+ * @secret_len: Length of the key in bytes
+ * @label: A unique label for each purpose of the KDF
+ * @seed: Seed value to bind into the key
+ * @seed_len: Length of the seed
+ * @out: Buffer for the generated pseudo-random key
+ * @outlen: Number of bytes of key to generate
+ * Returns: 0 on success, -1 on failure.
+ *
+ * This function is used to derive new, cryptographically separate keys from a
+ * given key in ERP. This KDF is defined in RFC 5295, Chapter 3.1.2.
+ */
+int hmac_sha256_kdf(const u8 *secret, size_t secret_len,
+		    const char *label, const u8 *seed, size_t seed_len,
+		    u8 *out, size_t outlen)
+{
+	u8 T[SHA256_MAC_LEN];
+	u8 iter = 1;
+	const unsigned char *addr[4];
+	size_t len[4];
+	size_t pos, clen;
+
+	addr[0] = T;
+	len[0] = SHA256_MAC_LEN;
+	addr[1] = (const unsigned char *) label;
+	len[1] = os_strlen(label) + 1;
+	addr[2] = seed;
+	len[2] = seed_len;
+	addr[3] = &iter;
+	len[3] = 1;
+
+	if (hmac_sha256_vector(secret, secret_len, 3, &addr[1], &len[1], T) < 0)
+		return -1;
+
+	pos = 0;
+	for (;;) {
+		clen = outlen - pos;
+		if (clen > SHA256_MAC_LEN)
+			clen = SHA256_MAC_LEN;
+		os_memcpy(out + pos, T, clen);
+		pos += clen;
+
+		if (pos == outlen)
+			break;
+
+		if (iter == 255) {
+			os_memset(out, 0, outlen);
+			os_memset(T, 0, SHA256_MAC_LEN);
+			return -1;
+		}
+		iter++;
+
+		if (hmac_sha256_vector(secret, secret_len, 4, addr, len, T) < 0)
+		{
+			os_memset(out, 0, outlen);
+			os_memset(T, 0, SHA256_MAC_LEN);
+			return -1;
+		}
+	}
+
+	os_memset(T, 0, SHA256_MAC_LEN);
+	return 0;
+}
diff --git a/src/crypto/sha256-prf.c b/src/crypto/sha256-prf.c
index 9a11208..79791c0 100644
--- a/src/crypto/sha256-prf.c
+++ b/src/crypto/sha256-prf.c
@@ -95,4 +95,6 @@
 		u8 mask = 0xff << (8 - buf_len_bits % 8);
 		buf[pos - 1] &= mask;
 	}
+
+	os_memset(hash, 0, sizeof(hash));
 }
diff --git a/src/crypto/sha256.h b/src/crypto/sha256.h
index 7596a52..b15f511 100644
--- a/src/crypto/sha256.h
+++ b/src/crypto/sha256.h
@@ -1,6 +1,6 @@
 /*
  * SHA256 hash implementation and interface functions
- * Copyright (c) 2003-2013, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2003-2014, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -23,5 +23,8 @@
 void tls_prf_sha256(const u8 *secret, size_t secret_len,
 		    const char *label, const u8 *seed, size_t seed_len,
 		    u8 *out, size_t outlen);
+int hmac_sha256_kdf(const u8 *secret, size_t secret_len,
+		    const char *label, const u8 *seed, size_t seed_len,
+		    u8 *out, size_t outlen);
 
 #endif /* SHA256_H */
diff --git a/src/crypto/sha384.h b/src/crypto/sha384.h
new file mode 100644
index 0000000..e6a1fe4
--- /dev/null
+++ b/src/crypto/sha384.h
@@ -0,0 +1,19 @@
+/*
+ * SHA384 hash implementation and interface functions
+ * Copyright (c) 2015, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef SHA384_H
+#define SHA384_H
+
+#define SHA384_MAC_LEN 48
+
+int hmac_sha384_vector(const u8 *key, size_t key_len, size_t num_elem,
+		       const u8 *addr[], const size_t *len, u8 *mac);
+int hmac_sha384(const u8 *key, size_t key_len, const u8 *data,
+		size_t data_len, u8 *mac);
+
+#endif /* SHA384_H */
diff --git a/src/crypto/tls.h b/src/crypto/tls.h
index 65e0f79..d13657e 100644
--- a/src/crypto/tls.h
+++ b/src/crypto/tls.h
@@ -12,8 +12,6 @@
 struct tls_connection;
 
 struct tls_keys {
-	const u8 *master_key; /* TLS master secret */
-	size_t master_key_len;
 	const u8 *client_random;
 	size_t client_random_len;
 	const u8 *server_random;
@@ -41,9 +39,13 @@
 	TLS_FAIL_ALTSUBJECT_MISMATCH = 6,
 	TLS_FAIL_BAD_CERTIFICATE = 7,
 	TLS_FAIL_SERVER_CHAIN_PROBE = 8,
-	TLS_FAIL_DOMAIN_SUFFIX_MISMATCH = 9
+	TLS_FAIL_DOMAIN_SUFFIX_MISMATCH = 9,
+	TLS_FAIL_DOMAIN_MISMATCH = 10,
 };
 
+
+#define TLS_MAX_ALT_SUBJECT 10
+
 union tls_event_data {
 	struct {
 		int depth;
@@ -59,6 +61,8 @@
 		const struct wpabuf *cert;
 		const u8 *hash;
 		size_t hash_len;
+		const char *altsubject[TLS_MAX_ALT_SUBJECT];
+		int num_altsubject;
 	} peer_cert;
 
 	struct {
@@ -74,6 +78,7 @@
 	const char *pkcs11_module_path;
 	int fips_mode;
 	int cert_in_cb;
+	const char *openssl_ciphers;
 
 	void (*event_cb)(void *ctx, enum tls_event ev,
 			 union tls_event_data *data);
@@ -87,6 +92,7 @@
 #define TLS_CONN_REQUIRE_OCSP BIT(4)
 #define TLS_CONN_DISABLE_TLSv1_1 BIT(5)
 #define TLS_CONN_DISABLE_TLSv1_2 BIT(6)
+#define TLS_CONN_EAP_FAST BIT(7)
 
 /**
  * struct tls_connection_params - Parameters for TLS connection
@@ -100,7 +106,11 @@
  * @altsubject_match: String to match in the alternative subject of the peer
  * certificate or %NULL to allow all alternative subjects
  * @suffix_match: String to suffix match in the dNSName or CN of the peer
- * certificate or %NULL to allow all domain names
+ * certificate or %NULL to allow all domain names. This may allow subdomains an
+ * wildcard certificates. Each domain name label must have a full match.
+ * @domain_match: String to match in the dNSName or CN of the peer
+ * certificate or %NULL to allow all domain names. This requires a full,
+ * case-insensitive match.
  * @client_cert: File or reference name for client X.509 certificate in PEM or
  * DER format
  * @client_cert_blob: client_cert as inlined data or %NULL if not used
@@ -123,6 +133,7 @@
  * specific for now)
  * @cert_id: the certificate's id when using engine
  * @ca_cert_id: the CA certificate's id when using engine
+ * @openssl_ciphers: OpenSSL cipher configuration
  * @flags: Parameter options (TLS_CONN_*)
  * @ocsp_stapling_response: DER encoded file with cached OCSP stapling response
  *	or %NULL if OCSP is not enabled
@@ -143,6 +154,7 @@
 	const char *subject_match;
 	const char *altsubject_match;
 	const char *suffix_match;
+	const char *domain_match;
 	const char *client_cert;
 	const u8 *client_cert_blob;
 	size_t client_cert_blob_len;
@@ -161,6 +173,7 @@
 	const char *key_id;
 	const char *cert_id;
 	const char *ca_cert_id;
+	const char *openssl_ciphers;
 
 	unsigned int flags;
 	const char *ocsp_stapling_response;
@@ -240,6 +253,7 @@
 int tls_connection_shutdown(void *tls_ctx, struct tls_connection *conn);
 
 enum {
+	TLS_SET_PARAMS_ENGINE_PRV_BAD_PIN = -4,
 	TLS_SET_PARAMS_ENGINE_PRV_VERIFY_FAILED = -3,
 	TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED = -2
 };
@@ -250,10 +264,12 @@
  * @conn: Connection context data from tls_connection_init()
  * @params: Connection parameters
  * Returns: 0 on success, -1 on failure,
- * TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED (-2) on possible PIN error causing
- * PKCS#11 engine failure, or
+ * TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED (-2) on error causing PKCS#11 engine
+ * failure, or
  * TLS_SET_PARAMS_ENGINE_PRV_VERIFY_FAILED (-3) on failure to verify the
- * PKCS#11 engine private key.
+ * PKCS#11 engine private key, or
+ * TLS_SET_PARAMS_ENGINE_PRV_BAD_PIN (-4) on PIN error causing PKCS#11 engine
+ * failure.
  */
 int __must_check
 tls_connection_set_params(void *tls_ctx, struct tls_connection *conn,
@@ -264,10 +280,12 @@
  * @tls_ctx: TLS context data from tls_init()
  * @params: Global TLS parameters
  * Returns: 0 on success, -1 on failure,
- * TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED (-2) on possible PIN error causing
- * PKCS#11 engine failure, or
+ * TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED (-2) on error causing PKCS#11 engine
+ * failure, or
  * TLS_SET_PARAMS_ENGINE_PRV_VERIFY_FAILED (-3) on failure to verify the
- * PKCS#11 engine private key.
+ * PKCS#11 engine private key, or
+ * TLS_SET_PARAMS_ENGINE_PRV_BAD_PIN (-4) on PIN error causing PKCS#11 engine
+ * failure.
  */
 int __must_check tls_global_set_params(
 	void *tls_ctx, const struct tls_connection_params *params);
@@ -293,10 +311,10 @@
 					   int verify_peer);
 
 /**
- * tls_connection_get_keys - Get master key and random data from TLS connection
+ * tls_connection_get_keys - 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 key/random data (filled on success)
+ * @keys: 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,
@@ -310,6 +328,7 @@
  * @label: Label (e.g., description of the key) for PRF
  * @server_random_first: seed is 0 = client_random|server_random,
  * 1 = server_random|client_random
+ * @skip_keyblock: Skip TLS key block from the beginning of PRF output
  * @out: Buffer for output data from TLS-PRF
  * @out_len: Length of the output buffer
  * Returns: 0 on success, -1 on failure
@@ -327,6 +346,7 @@
 				     struct tls_connection *conn,
 				     const char *label,
 				     int server_random_first,
+				     int skip_keyblock,
 				     u8 *out, size_t out_len);
 
 /**
@@ -513,16 +533,6 @@
 				    struct tls_connection *conn);
 
 /**
- * tls_connection_get_keyblock_size - Get TLS key_block size
- * @tls_ctx: TLS context data from tls_init()
- * @conn: Connection context data from tls_connection_init()
- * Returns: Size of the key_block for the negotiated cipher suite or -1 on
- * failure
- */
-int tls_connection_get_keyblock_size(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_*)
@@ -552,4 +562,6 @@
 
 void tls_connection_set_test_flags(struct tls_connection *conn, u32 flags);
 
+int tls_get_library_version(char *buf, size_t buf_len);
+
 #endif /* TLS_H */
diff --git a/src/crypto/tls_gnutls.c b/src/crypto/tls_gnutls.c
index cb23eb9..c7f6464 100644
--- a/src/crypto/tls_gnutls.c
+++ b/src/crypto/tls_gnutls.c
@@ -12,61 +12,15 @@
 #ifdef PKCS12_FUNCS
 #include <gnutls/pkcs12.h>
 #endif /* PKCS12_FUNCS */
+#if GNUTLS_VERSION_NUMBER >= 0x030103
+#include <gnutls/ocsp.h>
+#endif /* 3.1.3 */
 
 #include "common.h"
+#include "crypto/crypto.h"
 #include "tls.h"
 
 
-#define WPA_TLS_RANDOM_SIZE 32
-#define WPA_TLS_MASTER_SIZE 48
-
-
-#if LIBGNUTLS_VERSION_NUMBER < 0x010302
-/* GnuTLS 1.3.2 added functions for using master secret. Older versions require
- * use of internal structures to get the master_secret and
- * {server,client}_random.
- */
-#define GNUTLS_INTERNAL_STRUCTURE_HACK
-#endif /* LIBGNUTLS_VERSION_NUMBER < 0x010302 */
-
-
-#ifdef GNUTLS_INTERNAL_STRUCTURE_HACK
-/*
- * It looks like gnutls does not provide access to client/server_random and
- * master_key. This is somewhat unfortunate since these are needed for key
- * derivation in EAP-{TLS,TTLS,PEAP,FAST}. Workaround for now is a horrible
- * hack that copies the gnutls_session_int definition from gnutls_int.h so that
- * we can get the needed information.
- */
-
-typedef u8 uint8;
-typedef unsigned char opaque;
-typedef struct {
-    uint8 suite[2];
-} cipher_suite_st;
-
-typedef struct {
-	gnutls_connection_end_t entity;
-	gnutls_kx_algorithm_t kx_algorithm;
-	gnutls_cipher_algorithm_t read_bulk_cipher_algorithm;
-	gnutls_mac_algorithm_t read_mac_algorithm;
-	gnutls_compression_method_t read_compression_algorithm;
-	gnutls_cipher_algorithm_t write_bulk_cipher_algorithm;
-	gnutls_mac_algorithm_t write_mac_algorithm;
-	gnutls_compression_method_t write_compression_algorithm;
-	cipher_suite_st current_cipher_suite;
-	opaque master_secret[WPA_TLS_MASTER_SIZE];
-	opaque client_random[WPA_TLS_RANDOM_SIZE];
-	opaque server_random[WPA_TLS_RANDOM_SIZE];
-	/* followed by stuff we are not interested in */
-} security_parameters_st;
-
-struct gnutls_session_int {
-	security_parameters_st security_parameters;
-	/* followed by things we are not interested in */
-};
-#endif /* LIBGNUTLS_VERSION_NUMBER < 0x010302 */
-
 static int tls_gnutls_ref_count = 0;
 
 struct tls_global {
@@ -78,17 +32,23 @@
 
 	int params_set;
 	gnutls_certificate_credentials_t xcred;
+
+	void (*event_cb)(void *ctx, enum tls_event ev,
+			 union tls_event_data *data);
+	void *cb_ctx;
+	int cert_in_cb;
 };
 
 struct tls_connection {
-	gnutls_session session;
-	char *subject_match, *altsubject_match;
+	struct tls_global *global;
+	gnutls_session_t session;
 	int read_alerts, write_alerts, failed;
 
 	u8 *pre_shared_secret;
 	size_t pre_shared_secret_len;
 	int established;
 	int verify_peer;
+	unsigned int disable_time_checks:1;
 
 	struct wpabuf *push_buf;
 	struct wpabuf *pull_buf;
@@ -96,9 +56,16 @@
 
 	int params_set;
 	gnutls_certificate_credentials_t xcred;
+
+	char *suffix_match;
+	char *domain_match;
+	unsigned int flags;
 };
 
 
+static int tls_connection_verify_peer(gnutls_session_t session);
+
+
 static void tls_log_func(int level, const char *msg)
 {
 	char *s, *pos;
@@ -129,17 +96,11 @@
 {
 	struct tls_global *global;
 
-#ifdef GNUTLS_INTERNAL_STRUCTURE_HACK
-	/* Because of the horrible hack to get master_secret and client/server
-	 * random, we need to make sure that the gnutls version is something
-	 * that is expected to have same structure definition for the session
-	 * data.. */
-	const char *ver;
-	const char *ok_ver[] = { "1.2.3", "1.2.4", "1.2.5", "1.2.6", "1.2.9",
-				 "1.3.2",
-				 NULL };
-	int i;
-#endif /* GNUTLS_INTERNAL_STRUCTURE_HACK */
+	if (tls_gnutls_ref_count == 0) {
+		wpa_printf(MSG_DEBUG,
+			   "GnuTLS: Library version %s (runtime) - %s (build)",
+			   gnutls_check_version(NULL), GNUTLS_VERSION);
+	}
 
 	global = os_zalloc(sizeof(*global));
 	if (global == NULL)
@@ -151,28 +112,16 @@
 	}
 	tls_gnutls_ref_count++;
 
-#ifdef GNUTLS_INTERNAL_STRUCTURE_HACK
-	ver = gnutls_check_version(NULL);
-	if (ver == NULL) {
-		tls_deinit(global);
-		return NULL;
-	}
-	wpa_printf(MSG_DEBUG, "%s - gnutls version %s", __func__, ver);
-	for (i = 0; ok_ver[i]; i++) {
-		if (strcmp(ok_ver[i], ver) == 0)
-			break;
-	}
-	if (ok_ver[i] == NULL) {
-		wpa_printf(MSG_INFO, "Untested gnutls version %s - this needs "
-			   "to be tested and enabled in tls_gnutls.c", ver);
-		tls_deinit(global);
-		return NULL;
-	}
-#endif /* GNUTLS_INTERNAL_STRUCTURE_HACK */
-
 	gnutls_global_set_log_function(tls_log_func);
 	if (wpa_debug_show_keys)
 		gnutls_global_set_log_level(11);
+
+	if (conf) {
+		global->event_cb = conf->event_cb;
+		global->cb_ctx = conf->cb_ctx;
+		global->cert_in_cb = conf->cert_in_cb;
+	}
+
 	return global;
 }
 
@@ -199,7 +148,7 @@
 }
 
 
-static ssize_t tls_pull_func(gnutls_transport_ptr ptr, void *buf,
+static ssize_t tls_pull_func(gnutls_transport_ptr_t ptr, void *buf,
 			     size_t len)
 {
 	struct tls_connection *conn = (struct tls_connection *) ptr;
@@ -228,7 +177,7 @@
 }
 
 
-static ssize_t tls_push_func(gnutls_transport_ptr ptr, const void *buf,
+static ssize_t tls_push_func(gnutls_transport_ptr_t ptr, const void *buf,
 			     size_t len)
 {
 	struct tls_connection *conn = (struct tls_connection *) ptr;
@@ -246,12 +195,7 @@
 static int tls_gnutls_init_session(struct tls_global *global,
 				   struct tls_connection *conn)
 {
-#if LIBGNUTLS_VERSION_NUMBER >= 0x020200
 	const char *err;
-#else /* LIBGNUTLS_VERSION_NUMBER >= 0x020200 */
-	const int cert_types[2] = { GNUTLS_CRT_X509, 0 };
-	const int protos[2] = { GNUTLS_TLS1, 0 };
-#endif /* LIBGNUTLS_VERSION_NUMBER < 0x020200 */
 	int ret;
 
 	ret = gnutls_init(&conn->session,
@@ -266,7 +210,6 @@
 	if (ret < 0)
 		goto fail;
 
-#if LIBGNUTLS_VERSION_NUMBER >= 0x020200
 	ret = gnutls_priority_set_direct(conn->session, "NORMAL:-VERS-SSL3.0",
 					 &err);
 	if (ret < 0) {
@@ -274,19 +217,11 @@
 			   "'%s'", err);
 		goto fail;
 	}
-#else /* LIBGNUTLS_VERSION_NUMBER >= 0x020200 */
-	ret = gnutls_certificate_type_set_priority(conn->session, cert_types);
-	if (ret < 0)
-		goto fail;
-
-	ret = gnutls_protocol_set_priority(conn->session, protos);
-	if (ret < 0)
-		goto fail;
-#endif /* LIBGNUTLS_VERSION_NUMBER < 0x020200 */
 
 	gnutls_transport_set_pull_function(conn->session, tls_pull_func);
 	gnutls_transport_set_push_function(conn->session, tls_push_func);
-	gnutls_transport_set_ptr(conn->session, (gnutls_transport_ptr) conn);
+	gnutls_transport_set_ptr(conn->session, (gnutls_transport_ptr_t) conn);
+	gnutls_session_set_ptr(conn->session, conn);
 
 	return 0;
 
@@ -307,6 +242,7 @@
 	conn = os_zalloc(sizeof(*conn));
 	if (conn == NULL)
 		return NULL;
+	conn->global = global;
 
 	if (tls_gnutls_init_session(global, conn)) {
 		os_free(conn);
@@ -342,10 +278,10 @@
 	gnutls_certificate_free_credentials(conn->xcred);
 	gnutls_deinit(conn->session);
 	os_free(conn->pre_shared_secret);
-	os_free(conn->subject_match);
-	os_free(conn->altsubject_match);
 	wpabuf_free(conn->push_buf);
 	wpabuf_free(conn->pull_buf);
+	os_free(conn->suffix_match);
+	os_free(conn->domain_match);
 	os_free(conn);
 }
 
@@ -403,104 +339,6 @@
 }
 
 
-#if 0
-static int tls_match_altsubject(X509 *cert, const char *match)
-{
-	GENERAL_NAME *gen;
-	char *field, *tmp;
-	void *ext;
-	int i, found = 0;
-	size_t len;
-
-	ext = X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL);
-
-	for (i = 0; ext && i < sk_GENERAL_NAME_num(ext); i++) {
-		gen = sk_GENERAL_NAME_value(ext, i);
-		switch (gen->type) {
-		case GEN_EMAIL:
-			field = "EMAIL";
-			break;
-		case GEN_DNS:
-			field = "DNS";
-			break;
-		case GEN_URI:
-			field = "URI";
-			break;
-		default:
-			field = NULL;
-			wpa_printf(MSG_DEBUG, "TLS: altSubjectName: "
-				   "unsupported type=%d", gen->type);
-			break;
-		}
-
-		if (!field)
-			continue;
-
-		wpa_printf(MSG_DEBUG, "TLS: altSubjectName: %s:%s",
-			   field, gen->d.ia5->data);
-		len = os_strlen(field) + 1 +
-			strlen((char *) gen->d.ia5->data) + 1;
-		tmp = os_malloc(len);
-		if (tmp == NULL)
-			continue;
-		snprintf(tmp, len, "%s:%s", field, gen->d.ia5->data);
-		if (strstr(tmp, match))
-			found++;
-		os_free(tmp);
-	}
-
-	return found;
-}
-#endif
-
-
-#if 0
-static int tls_verify_cb(int preverify_ok, X509_STORE_CTX *x509_ctx)
-{
-	char buf[256];
-	X509 *err_cert;
-	int err, depth;
-	SSL *ssl;
-	struct tls_connection *conn;
-	char *match, *altmatch;
-
-	err_cert = X509_STORE_CTX_get_current_cert(x509_ctx);
-	err = X509_STORE_CTX_get_error(x509_ctx);
-	depth = X509_STORE_CTX_get_error_depth(x509_ctx);
-	ssl = X509_STORE_CTX_get_ex_data(x509_ctx,
-					 SSL_get_ex_data_X509_STORE_CTX_idx());
-	X509_NAME_oneline(X509_get_subject_name(err_cert), buf, sizeof(buf));
-
-	conn = SSL_get_app_data(ssl);
-	match = conn ? conn->subject_match : NULL;
-	altmatch = conn ? conn->altsubject_match : NULL;
-
-	if (!preverify_ok) {
-		wpa_printf(MSG_WARNING, "TLS: Certificate verification failed,"
-			   " error %d (%s) depth %d for '%s'", err,
-			   X509_verify_cert_error_string(err), depth, buf);
-	} else {
-		wpa_printf(MSG_DEBUG, "TLS: tls_verify_cb - "
-			   "preverify_ok=%d err=%d (%s) depth=%d buf='%s'",
-			   preverify_ok, err,
-			   X509_verify_cert_error_string(err), depth, buf);
-		if (depth == 0 && match && strstr(buf, match) == NULL) {
-			wpa_printf(MSG_WARNING, "TLS: Subject '%s' did not "
-				   "match with '%s'", buf, match);
-			preverify_ok = 0;
-		} else if (depth == 0 && altmatch &&
-			   !tls_match_altsubject(err_cert, altmatch)) {
-			wpa_printf(MSG_WARNING, "TLS: altSubjectName match "
-				   "'%s' not found", altmatch);
-			preverify_ok = 0;
-		}
-	}
-
-	return preverify_ok;
-}
-#endif
-
-
 int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn,
 			      const struct tls_connection_params *params)
 {
@@ -509,73 +347,142 @@
 	if (conn == NULL || params == NULL)
 		return -1;
 
-	os_free(conn->subject_match);
-	conn->subject_match = NULL;
 	if (params->subject_match) {
-		conn->subject_match = os_strdup(params->subject_match);
-		if (conn->subject_match == NULL)
+		wpa_printf(MSG_INFO, "GnuTLS: subject_match not supported");
+		return -1;
+	}
+
+	if (params->altsubject_match) {
+		wpa_printf(MSG_INFO, "GnuTLS: altsubject_match not supported");
+		return -1;
+	}
+
+	os_free(conn->suffix_match);
+	conn->suffix_match = NULL;
+	if (params->suffix_match) {
+		conn->suffix_match = os_strdup(params->suffix_match);
+		if (conn->suffix_match == NULL)
 			return -1;
 	}
 
-	os_free(conn->altsubject_match);
-	conn->altsubject_match = NULL;
-	if (params->altsubject_match) {
-		conn->altsubject_match = os_strdup(params->altsubject_match);
-		if (conn->altsubject_match == NULL)
+#if GNUTLS_VERSION_NUMBER >= 0x030300
+	os_free(conn->domain_match);
+	conn->domain_match = NULL;
+	if (params->domain_match) {
+		conn->domain_match = os_strdup(params->domain_match);
+		if (conn->domain_match == NULL)
 			return -1;
 	}
+#else /* < 3.3.0 */
+	if (params->domain_match) {
+		wpa_printf(MSG_INFO, "GnuTLS: domain_match not supported");
+		return -1;
+	}
+#endif /* >= 3.3.0 */
+
+	conn->flags = params->flags;
+
+	if (params->openssl_ciphers) {
+		wpa_printf(MSG_INFO, "GnuTLS: openssl_ciphers not supported");
+		return -1;
+	}
 
 	/* TODO: gnutls_certificate_set_verify_flags(xcred, flags); 
 	 * to force peer validation(?) */
 
 	if (params->ca_cert) {
-		conn->verify_peer = 1;
+		wpa_printf(MSG_DEBUG, "GnuTLS: Try to parse %s in DER format",
+			   params->ca_cert);
 		ret = gnutls_certificate_set_x509_trust_file(
-			conn->xcred, params->ca_cert, GNUTLS_X509_FMT_PEM);
+			conn->xcred, params->ca_cert, GNUTLS_X509_FMT_DER);
 		if (ret < 0) {
-			wpa_printf(MSG_DEBUG, "Failed to read CA cert '%s' "
-				   "in PEM format: %s", params->ca_cert,
+			wpa_printf(MSG_DEBUG,
+				   "GnuTLS: Failed to read CA cert '%s' in DER format (%s) - try in PEM format",
+				   params->ca_cert,
 				   gnutls_strerror(ret));
 			ret = gnutls_certificate_set_x509_trust_file(
 				conn->xcred, params->ca_cert,
-				GNUTLS_X509_FMT_DER);
+				GNUTLS_X509_FMT_PEM);
 			if (ret < 0) {
-				wpa_printf(MSG_DEBUG, "Failed to read CA cert "
-					   "'%s' in DER format: %s",
+				wpa_printf(MSG_DEBUG,
+					   "Failed to read CA cert '%s' in PEM format: %s",
 					   params->ca_cert,
 					   gnutls_strerror(ret));
 				return -1;
 			}
 		}
+	} else if (params->ca_cert_blob) {
+		gnutls_datum_t ca;
+
+		ca.data = (unsigned char *) params->ca_cert_blob;
+		ca.size = params->ca_cert_blob_len;
+
+		ret = gnutls_certificate_set_x509_trust_mem(
+			conn->xcred, &ca, GNUTLS_X509_FMT_DER);
+		if (ret < 0) {
+			wpa_printf(MSG_DEBUG,
+				   "Failed to parse CA cert in DER format: %s",
+				   gnutls_strerror(ret));
+			ret = gnutls_certificate_set_x509_trust_mem(
+				conn->xcred, &ca, GNUTLS_X509_FMT_PEM);
+			if (ret < 0) {
+				wpa_printf(MSG_DEBUG,
+					   "Failed to parse CA cert in PEM format: %s",
+					   gnutls_strerror(ret));
+				return -1;
+			}
+		}
+	} else if (params->ca_path) {
+		wpa_printf(MSG_INFO, "GnuTLS: ca_path not supported");
+		return -1;
+	}
+
+	conn->disable_time_checks = 0;
+	if (params->ca_cert || params->ca_cert_blob) {
+		conn->verify_peer = 1;
+		gnutls_certificate_set_verify_function(
+			conn->xcred, tls_connection_verify_peer);
 
 		if (params->flags & TLS_CONN_ALLOW_SIGN_RSA_MD5) {
 			gnutls_certificate_set_verify_flags(
 				conn->xcred, GNUTLS_VERIFY_ALLOW_SIGN_RSA_MD5);
 		}
 
-#if LIBGNUTLS_VERSION_NUMBER >= 0x020800
 		if (params->flags & TLS_CONN_DISABLE_TIME_CHECKS) {
+			conn->disable_time_checks = 1;
 			gnutls_certificate_set_verify_flags(
 				conn->xcred,
 				GNUTLS_VERIFY_DISABLE_TIME_CHECKS);
 		}
-#endif /* LIBGNUTLS_VERSION_NUMBER >= 0x020800 */
 	}
 
 	if (params->client_cert && params->private_key) {
-		/* TODO: private_key_passwd? */
+#if GNUTLS_VERSION_NUMBER >= 0x03010b
+		ret = gnutls_certificate_set_x509_key_file2(
+			conn->xcred, params->client_cert, params->private_key,
+			GNUTLS_X509_FMT_DER, params->private_key_passwd, 0);
+#else
+		/* private_key_passwd not (easily) supported here */
 		ret = gnutls_certificate_set_x509_key_file(
 			conn->xcred, params->client_cert, params->private_key,
-			GNUTLS_X509_FMT_PEM);
+			GNUTLS_X509_FMT_DER);
+#endif
 		if (ret < 0) {
 			wpa_printf(MSG_DEBUG, "Failed to read client cert/key "
-				   "in PEM format: %s", gnutls_strerror(ret));
+				   "in DER format: %s", gnutls_strerror(ret));
+#if GNUTLS_VERSION_NUMBER >= 0x03010b
+			ret = gnutls_certificate_set_x509_key_file2(
+				conn->xcred, params->client_cert,
+				params->private_key, GNUTLS_X509_FMT_PEM,
+				params->private_key_passwd, 0);
+#else
 			ret = gnutls_certificate_set_x509_key_file(
 				conn->xcred, params->client_cert,
-				params->private_key, GNUTLS_X509_FMT_DER);
+				params->private_key, GNUTLS_X509_FMT_PEM);
+#endif
 			if (ret < 0) {
 				wpa_printf(MSG_DEBUG, "Failed to read client "
-					   "cert/key in DER format: %s",
+					   "cert/key in PEM format: %s",
 					   gnutls_strerror(ret));
 				return ret;
 			}
@@ -584,7 +491,6 @@
 		int pkcs12_ok = 0;
 #ifdef PKCS12_FUNCS
 		/* Try to load in PKCS#12 format */
-#if LIBGNUTLS_VERSION_NUMBER >= 0x010302
 		ret = gnutls_certificate_set_x509_simple_pkcs12_file(
 			conn->xcred, params->private_key, GNUTLS_X509_FMT_DER,
 			params->private_key_passwd);
@@ -594,7 +500,6 @@
 			return -1;
 		} else
 			pkcs12_ok = 1;
-#endif /* LIBGNUTLS_VERSION_NUMBER >= 0x010302 */
 #endif /* PKCS12_FUNCS */
 
 		if (!pkcs12_ok) {
@@ -602,8 +507,82 @@
 				   "included");
 			return -1;
 		}
+	} else if (params->client_cert_blob && params->private_key_blob) {
+		gnutls_datum_t cert, key;
+
+		cert.data = (unsigned char *) params->client_cert_blob;
+		cert.size = params->client_cert_blob_len;
+		key.data = (unsigned char *) params->private_key_blob;
+		key.size = params->private_key_blob_len;
+
+#if GNUTLS_VERSION_NUMBER >= 0x03010b
+		ret = gnutls_certificate_set_x509_key_mem2(
+			conn->xcred, &cert, &key, GNUTLS_X509_FMT_DER,
+			params->private_key_passwd, 0);
+#else
+		/* private_key_passwd not (easily) supported here */
+		ret = gnutls_certificate_set_x509_key_mem(
+			conn->xcred, &cert, &key, GNUTLS_X509_FMT_DER);
+#endif
+		if (ret < 0) {
+			wpa_printf(MSG_DEBUG, "Failed to read client cert/key "
+				   "in DER format: %s", gnutls_strerror(ret));
+#if GNUTLS_VERSION_NUMBER >= 0x03010b
+			ret = gnutls_certificate_set_x509_key_mem2(
+				conn->xcred, &cert, &key, GNUTLS_X509_FMT_PEM,
+				params->private_key_passwd, 0);
+#else
+			/* private_key_passwd not (easily) supported here */
+			ret = gnutls_certificate_set_x509_key_mem(
+				conn->xcred, &cert, &key, GNUTLS_X509_FMT_PEM);
+#endif
+			if (ret < 0) {
+				wpa_printf(MSG_DEBUG, "Failed to read client "
+					   "cert/key in PEM format: %s",
+					   gnutls_strerror(ret));
+				return ret;
+			}
+		}
+	} else if (params->private_key_blob) {
+#ifdef PKCS12_FUNCS
+		gnutls_datum_t key;
+
+		key.data = (unsigned char *) params->private_key_blob;
+		key.size = params->private_key_blob_len;
+
+		/* Try to load in PKCS#12 format */
+		ret = gnutls_certificate_set_x509_simple_pkcs12_mem(
+			conn->xcred, &key, GNUTLS_X509_FMT_DER,
+			params->private_key_passwd);
+		if (ret != 0) {
+			wpa_printf(MSG_DEBUG, "Failed to load private_key in "
+				   "PKCS#12 format: %s", gnutls_strerror(ret));
+			return -1;
+		}
+#else /* PKCS12_FUNCS */
+		wpa_printf(MSG_DEBUG, "GnuTLS: PKCS#12 support not included");
+		return -1;
+#endif /* PKCS12_FUNCS */
 	}
 
+#if GNUTLS_VERSION_NUMBER >= 0x030103
+	if (params->flags & (TLS_CONN_REQUEST_OCSP | TLS_CONN_REQUIRE_OCSP)) {
+		ret = gnutls_ocsp_status_request_enable_client(conn->session,
+							       NULL, 0, NULL);
+		if (ret != GNUTLS_E_SUCCESS) {
+			wpa_printf(MSG_INFO,
+				   "GnuTLS: Failed to enable OCSP client");
+			return -1;
+		}
+	}
+#else /* 3.1.3 */
+	if (params->flags & TLS_CONN_REQUIRE_OCSP) {
+		wpa_printf(MSG_INFO,
+			   "GnuTLS: OCSP not supported by this version of GnuTLS");
+		return -1;
+	}
+#endif /* 3.1.3 */
+
 	conn->params_set = 1;
 
 	ret = gnutls_credentials_set(conn->session, GNUTLS_CRD_CERTIFICATE,
@@ -641,17 +620,17 @@
 
 	if (params->ca_cert) {
 		ret = gnutls_certificate_set_x509_trust_file(
-			global->xcred, params->ca_cert, GNUTLS_X509_FMT_PEM);
+			global->xcred, params->ca_cert, GNUTLS_X509_FMT_DER);
 		if (ret < 0) {
 			wpa_printf(MSG_DEBUG, "Failed to read CA cert '%s' "
-				   "in PEM format: %s", params->ca_cert,
+				   "in DER format: %s", params->ca_cert,
 				   gnutls_strerror(ret));
 			ret = gnutls_certificate_set_x509_trust_file(
 				global->xcred, params->ca_cert,
-				GNUTLS_X509_FMT_DER);
+				GNUTLS_X509_FMT_PEM);
 			if (ret < 0) {
 				wpa_printf(MSG_DEBUG, "Failed to read CA cert "
-					   "'%s' in DER format: %s",
+					   "'%s' in PEM format: %s",
 					   params->ca_cert,
 					   gnutls_strerror(ret));
 				goto fail;
@@ -664,29 +643,27 @@
 				GNUTLS_VERIFY_ALLOW_SIGN_RSA_MD5);
 		}
 
-#if LIBGNUTLS_VERSION_NUMBER >= 0x020800
 		if (params->flags & TLS_CONN_DISABLE_TIME_CHECKS) {
 			gnutls_certificate_set_verify_flags(
 				global->xcred,
 				GNUTLS_VERIFY_DISABLE_TIME_CHECKS);
 		}
-#endif /* LIBGNUTLS_VERSION_NUMBER >= 0x020800 */
 	}
 
 	if (params->client_cert && params->private_key) {
 		/* TODO: private_key_passwd? */
 		ret = gnutls_certificate_set_x509_key_file(
 			global->xcred, params->client_cert,
-			params->private_key, GNUTLS_X509_FMT_PEM);
+			params->private_key, GNUTLS_X509_FMT_DER);
 		if (ret < 0) {
 			wpa_printf(MSG_DEBUG, "Failed to read client cert/key "
-				   "in PEM format: %s", gnutls_strerror(ret));
+				   "in DER format: %s", gnutls_strerror(ret));
 			ret = gnutls_certificate_set_x509_key_file(
 				global->xcred, params->client_cert,
-				params->private_key, GNUTLS_X509_FMT_DER);
+				params->private_key, GNUTLS_X509_FMT_PEM);
 			if (ret < 0) {
 				wpa_printf(MSG_DEBUG, "Failed to read client "
-					   "cert/key in DER format: %s",
+					   "cert/key in PEM format: %s",
 					   gnutls_strerror(ret));
 				goto fail;
 			}
@@ -695,7 +672,6 @@
 		int pkcs12_ok = 0;
 #ifdef PKCS12_FUNCS
 		/* Try to load in PKCS#12 format */
-#if LIBGNUTLS_VERSION_NUMBER >= 0x010302
 		ret = gnutls_certificate_set_x509_simple_pkcs12_file(
 			global->xcred, params->private_key,
 			GNUTLS_X509_FMT_DER, params->private_key_passwd);
@@ -705,7 +681,6 @@
 			goto fail;
 		} else
 			pkcs12_ok = 1;
-#endif /* LIBGNUTLS_VERSION_NUMBER >= 0x010302 */
 #endif /* PKCS12_FUNCS */
 
 		if (!pkcs12_ok) {
@@ -750,124 +725,341 @@
 int tls_connection_get_keys(void *ssl_ctx, struct tls_connection *conn,
 			    struct tls_keys *keys)
 {
-#ifdef GNUTLS_INTERNAL_STRUCTURE_HACK
-	security_parameters_st *sec;
-#endif /* GNUTLS_INTERNAL_STRUCTURE_HACK */
+#if GNUTLS_VERSION_NUMBER >= 0x030012
+	gnutls_datum_t client, server;
 
 	if (conn == NULL || conn->session == NULL || keys == NULL)
 		return -1;
 
 	os_memset(keys, 0, sizeof(*keys));
-
-#if LIBGNUTLS_VERSION_NUMBER < 0x020c00
-#ifdef GNUTLS_INTERNAL_STRUCTURE_HACK
-	sec = &conn->session->security_parameters;
-	keys->master_key = sec->master_secret;
-	keys->master_key_len = WPA_TLS_MASTER_SIZE;
-	keys->client_random = sec->client_random;
-	keys->server_random = sec->server_random;
-#else /* GNUTLS_INTERNAL_STRUCTURE_HACK */
-	keys->client_random =
-		(u8 *) gnutls_session_get_client_random(conn->session);
-	keys->server_random =
-		(u8 *) gnutls_session_get_server_random(conn->session);
-	/* No access to master_secret */
-#endif /* GNUTLS_INTERNAL_STRUCTURE_HACK */
-#endif /* LIBGNUTLS_VERSION_NUMBER < 0x020c00 */
-
-#if LIBGNUTLS_VERSION_NUMBER < 0x020c00
-	keys->client_random_len = WPA_TLS_RANDOM_SIZE;
-	keys->server_random_len = WPA_TLS_RANDOM_SIZE;
-#endif /* LIBGNUTLS_VERSION_NUMBER < 0x020c00 */
+	gnutls_session_get_random(conn->session, &client, &server);
+	keys->client_random = client.data;
+	keys->server_random = server.data;
+	keys->client_random_len = client.size;
+	keys->server_random_len = client.size;
 
 	return 0;
+#else /* 3.0.18 */
+	return -1;
+#endif /* 3.0.18 */
 }
 
 
 int tls_connection_prf(void *tls_ctx, struct tls_connection *conn,
 		       const char *label, int server_random_first,
-		       u8 *out, size_t out_len)
+		       int skip_keyblock, u8 *out, size_t out_len)
 {
-#if LIBGNUTLS_VERSION_NUMBER >= 0x010302
-	if (conn == NULL || conn->session == NULL)
+	if (conn == NULL || conn->session == NULL || skip_keyblock)
 		return -1;
 
 	return gnutls_prf(conn->session, os_strlen(label), label,
 			  server_random_first, 0, NULL, out_len, (char *) out);
-#else /* LIBGNUTLS_VERSION_NUMBER >= 0x010302 */
-	return -1;
-#endif /* LIBGNUTLS_VERSION_NUMBER >= 0x010302 */
 }
 
 
-static int tls_connection_verify_peer(struct tls_connection *conn,
-				      gnutls_alert_description_t *err)
+static void gnutls_tls_fail_event(struct tls_connection *conn,
+				  const gnutls_datum_t *cert, int depth,
+				  const char *subject, const char *err_str,
+				  enum tls_fail_reason reason)
 {
+	union tls_event_data ev;
+	struct tls_global *global = conn->global;
+	struct wpabuf *cert_buf = NULL;
+
+	if (global->event_cb == NULL)
+		return;
+
+	os_memset(&ev, 0, sizeof(ev));
+	ev.cert_fail.depth = depth;
+	ev.cert_fail.subject = subject ? subject : "";
+	ev.cert_fail.reason = reason;
+	ev.cert_fail.reason_txt = err_str;
+	if (cert) {
+		cert_buf = wpabuf_alloc_copy(cert->data, cert->size);
+		ev.cert_fail.cert = cert_buf;
+	}
+	global->event_cb(global->cb_ctx, TLS_CERT_CHAIN_FAILURE, &ev);
+	wpabuf_free(cert_buf);
+}
+
+
+#if GNUTLS_VERSION_NUMBER < 0x030300
+static int server_eku_purpose(gnutls_x509_crt_t cert)
+{
+	unsigned int i;
+
+	for (i = 0; ; i++) {
+		char oid[128];
+		size_t oid_size = sizeof(oid);
+		int res;
+
+		res = gnutls_x509_crt_get_key_purpose_oid(cert, i, oid,
+							  &oid_size, NULL);
+		if (res == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) {
+			if (i == 0) {
+				/* No EKU - assume any use allowed */
+				return 1;
+			}
+			break;
+		}
+
+		if (res < 0) {
+			wpa_printf(MSG_INFO, "GnuTLS: Failed to get EKU");
+			return 0;
+		}
+
+		wpa_printf(MSG_DEBUG, "GnuTLS: Certificate purpose: %s", oid);
+		if (os_strcmp(oid, GNUTLS_KP_TLS_WWW_SERVER) == 0 ||
+		    os_strcmp(oid, GNUTLS_KP_ANY) == 0)
+			return 1;
+	}
+
+	return 0;
+}
+#endif /* < 3.3.0 */
+
+
+static int check_ocsp(struct tls_connection *conn, gnutls_session_t session,
+		      gnutls_alert_description_t *err)
+{
+#if GNUTLS_VERSION_NUMBER >= 0x030103
+	gnutls_datum_t response, buf;
+	gnutls_ocsp_resp_t resp;
+	unsigned int cert_status;
+	int res;
+
+	if (!(conn->flags & (TLS_CONN_REQUEST_OCSP | TLS_CONN_REQUIRE_OCSP)))
+		return 0;
+
+	if (!gnutls_ocsp_status_request_is_checked(session, 0)) {
+		if (conn->flags & TLS_CONN_REQUIRE_OCSP) {
+			wpa_printf(MSG_INFO,
+				   "GnuTLS: No valid OCSP response received");
+			goto ocsp_error;
+		}
+
+		wpa_printf(MSG_DEBUG,
+			   "GnuTLS: Valid OCSP response was not received - continue since OCSP was not required");
+		return 0;
+	}
+
+	/*
+	 * GnuTLS has already verified the OCSP response in
+	 * check_ocsp_response() and rejected handshake if the certificate was
+	 * found to be revoked. However, if the response indicates that the
+	 * status is unknown, handshake continues and reaches here. We need to
+	 * re-import the OCSP response to check for unknown certificate status,
+	 * but we do not need to repeat gnutls_ocsp_resp_check_crt() and
+	 * gnutls_ocsp_resp_verify_direct() calls.
+	 */
+
+	res = gnutls_ocsp_status_request_get(session, &response);
+	if (res != GNUTLS_E_SUCCESS) {
+		wpa_printf(MSG_INFO,
+			   "GnuTLS: OCSP response was received, but it was not valid");
+		goto ocsp_error;
+	}
+
+	if (gnutls_ocsp_resp_init(&resp) != GNUTLS_E_SUCCESS)
+		goto ocsp_error;
+
+	res = gnutls_ocsp_resp_import(resp, &response);
+	if (res != GNUTLS_E_SUCCESS) {
+		wpa_printf(MSG_INFO,
+			   "GnuTLS: Could not parse received OCSP response: %s",
+			   gnutls_strerror(res));
+		gnutls_ocsp_resp_deinit(resp);
+		goto ocsp_error;
+	}
+
+	res = gnutls_ocsp_resp_print(resp, GNUTLS_OCSP_PRINT_FULL, &buf);
+	if (res == GNUTLS_E_SUCCESS) {
+		wpa_printf(MSG_DEBUG, "GnuTLS: %s", buf.data);
+		gnutls_free(buf.data);
+	}
+
+	res = gnutls_ocsp_resp_get_single(resp, 0, NULL, NULL, NULL,
+					  NULL, &cert_status, NULL,
+					  NULL, NULL, NULL);
+	gnutls_ocsp_resp_deinit(resp);
+	if (res != GNUTLS_E_SUCCESS) {
+		wpa_printf(MSG_INFO,
+			   "GnuTLS: Failed to extract OCSP information: %s",
+			   gnutls_strerror(res));
+		goto ocsp_error;
+	}
+
+	if (cert_status == GNUTLS_OCSP_CERT_GOOD) {
+		wpa_printf(MSG_DEBUG, "GnuTLS: OCSP cert status: good");
+	} else if (cert_status == GNUTLS_OCSP_CERT_REVOKED) {
+		wpa_printf(MSG_DEBUG,
+			   "GnuTLS: OCSP cert status: revoked");
+		goto ocsp_error;
+	} else {
+		wpa_printf(MSG_DEBUG,
+			   "GnuTLS: OCSP cert status: unknown");
+		if (conn->flags & TLS_CONN_REQUIRE_OCSP)
+			goto ocsp_error;
+		wpa_printf(MSG_DEBUG,
+			   "GnuTLS: OCSP was not required, so allow connection to continue");
+	}
+
+	return 0;
+
+ocsp_error:
+	gnutls_tls_fail_event(conn, NULL, 0, NULL,
+			      "bad certificate status response",
+			      TLS_FAIL_REVOKED);
+	*err = GNUTLS_A_CERTIFICATE_REVOKED;
+	return -1;
+#else /* GnuTLS 3.1.3 or newer */
+	return 0;
+#endif /* GnuTLS 3.1.3 or newer */
+}
+
+
+static int tls_connection_verify_peer(gnutls_session_t session)
+{
+	struct tls_connection *conn;
 	unsigned int status, num_certs, i;
 	struct os_time now;
 	const gnutls_datum_t *certs;
 	gnutls_x509_crt_t cert;
+	gnutls_alert_description_t err;
+	int res;
 
-	if (gnutls_certificate_verify_peers2(conn->session, &status) < 0) {
+	conn = gnutls_session_get_ptr(session);
+	if (!conn->verify_peer) {
+		wpa_printf(MSG_DEBUG,
+			   "GnuTLS: No peer certificate verification enabled");
+		return 0;
+	}
+
+	wpa_printf(MSG_DEBUG, "GnuTSL: Verifying peer certificate");
+
+#if GNUTLS_VERSION_NUMBER >= 0x030300
+	{
+		gnutls_typed_vdata_st data[1];
+		unsigned int elements = 0;
+
+		os_memset(data, 0, sizeof(data));
+		if (!conn->global->server) {
+			data[elements].type = GNUTLS_DT_KEY_PURPOSE_OID;
+			data[elements].data = (void *) GNUTLS_KP_TLS_WWW_SERVER;
+			elements++;
+		}
+		res = gnutls_certificate_verify_peers(session, data, 1,
+						      &status);
+	}
+#else /* < 3.3.0 */
+	res = gnutls_certificate_verify_peers2(session, &status);
+#endif
+	if (res < 0) {
 		wpa_printf(MSG_INFO, "TLS: Failed to verify peer "
 			   "certificate chain");
-		*err = GNUTLS_A_INTERNAL_ERROR;
-		return -1;
+		err = GNUTLS_A_INTERNAL_ERROR;
+		goto out;
+	}
+
+#if GNUTLS_VERSION_NUMBER >= 0x030104
+	{
+		gnutls_datum_t info;
+		int ret, type;
+
+		type = gnutls_certificate_type_get(session);
+		ret = gnutls_certificate_verification_status_print(status, type,
+								   &info, 0);
+		if (ret < 0) {
+			wpa_printf(MSG_DEBUG,
+				   "GnuTLS: Failed to print verification status");
+			err = GNUTLS_A_INTERNAL_ERROR;
+			goto out;
+		}
+		wpa_printf(MSG_DEBUG, "GnuTLS: %s", info.data);
+		gnutls_free(info.data);
+	}
+#endif /* GnuTLS 3.1.4 or newer */
+
+	certs = gnutls_certificate_get_peers(session, &num_certs);
+	if (certs == NULL || num_certs == 0) {
+		wpa_printf(MSG_INFO, "TLS: No peer certificate chain received");
+		err = GNUTLS_A_UNKNOWN_CA;
+		goto out;
 	}
 
 	if (conn->verify_peer && (status & GNUTLS_CERT_INVALID)) {
 		wpa_printf(MSG_INFO, "TLS: Peer certificate not trusted");
-		*err = GNUTLS_A_INTERNAL_ERROR;
 		if (status & GNUTLS_CERT_INSECURE_ALGORITHM) {
 			wpa_printf(MSG_INFO, "TLS: Certificate uses insecure "
 				   "algorithm");
-			*err = GNUTLS_A_INSUFFICIENT_SECURITY;
+			gnutls_tls_fail_event(conn, NULL, 0, NULL,
+					      "certificate uses insecure algorithm",
+					      TLS_FAIL_BAD_CERTIFICATE);
+			err = GNUTLS_A_INSUFFICIENT_SECURITY;
+			goto out;
 		}
-#if LIBGNUTLS_VERSION_NUMBER >= 0x020800
 		if (status & GNUTLS_CERT_NOT_ACTIVATED) {
 			wpa_printf(MSG_INFO, "TLS: Certificate not yet "
 				   "activated");
-			*err = GNUTLS_A_CERTIFICATE_EXPIRED;
+			gnutls_tls_fail_event(conn, NULL, 0, NULL,
+					      "certificate not yet valid",
+					      TLS_FAIL_NOT_YET_VALID);
+			err = GNUTLS_A_CERTIFICATE_EXPIRED;
+			goto out;
 		}
 		if (status & GNUTLS_CERT_EXPIRED) {
 			wpa_printf(MSG_INFO, "TLS: Certificate expired");
-			*err = GNUTLS_A_CERTIFICATE_EXPIRED;
+			gnutls_tls_fail_event(conn, NULL, 0, NULL,
+					      "certificate has expired",
+					      TLS_FAIL_EXPIRED);
+			err = GNUTLS_A_CERTIFICATE_EXPIRED;
+			goto out;
 		}
-#endif /* LIBGNUTLS_VERSION_NUMBER >= 0x020800 */
-		return -1;
+		gnutls_tls_fail_event(conn, NULL, 0, NULL,
+				      "untrusted certificate",
+				      TLS_FAIL_UNTRUSTED);
+		err = GNUTLS_A_INTERNAL_ERROR;
+		goto out;
 	}
 
 	if (status & GNUTLS_CERT_SIGNER_NOT_FOUND) {
 		wpa_printf(MSG_INFO, "TLS: Peer certificate does not have a "
 			   "known issuer");
-		*err = GNUTLS_A_UNKNOWN_CA;
-		return -1;
+		gnutls_tls_fail_event(conn, NULL, 0, NULL, "signed not found",
+				      TLS_FAIL_UNTRUSTED);
+		err = GNUTLS_A_UNKNOWN_CA;
+		goto out;
 	}
 
 	if (status & GNUTLS_CERT_REVOKED) {
 		wpa_printf(MSG_INFO, "TLS: Peer certificate has been revoked");
-		*err = GNUTLS_A_CERTIFICATE_REVOKED;
-		return -1;
+		gnutls_tls_fail_event(conn, NULL, 0, NULL,
+				      "certificate revoked",
+				      TLS_FAIL_REVOKED);
+		err = GNUTLS_A_CERTIFICATE_REVOKED;
+		goto out;
 	}
 
+	if (status != 0) {
+		wpa_printf(MSG_INFO, "TLS: Unknown verification status: %d",
+			   status);
+		err = GNUTLS_A_INTERNAL_ERROR;
+		goto out;
+	}
+
+	if (check_ocsp(conn, session, &err))
+		goto out;
+
 	os_get_time(&now);
 
-	certs = gnutls_certificate_get_peers(conn->session, &num_certs);
-	if (certs == NULL) {
-		wpa_printf(MSG_INFO, "TLS: No peer certificate chain "
-			   "received");
-		*err = GNUTLS_A_UNKNOWN_CA;
-		return -1;
-	}
-
 	for (i = 0; i < num_certs; i++) {
 		char *buf;
 		size_t len;
 		if (gnutls_x509_crt_init(&cert) < 0) {
 			wpa_printf(MSG_INFO, "TLS: Certificate initialization "
 				   "failed");
-			*err = GNUTLS_A_BAD_CERTIFICATE;
-			return -1;
+			err = GNUTLS_A_BAD_CERTIFICATE;
+			goto out;
 		}
 
 		if (gnutls_x509_crt_import(cert, &certs[i],
@@ -875,8 +1067,8 @@
 			wpa_printf(MSG_INFO, "TLS: Could not parse peer "
 				   "certificate %d/%d", i + 1, num_certs);
 			gnutls_x509_crt_deinit(cert);
-			*err = GNUTLS_A_BAD_CERTIFICATE;
-			return -1;
+			err = GNUTLS_A_BAD_CERTIFICATE;
+			goto out;
 		}
 
 		gnutls_x509_crt_get_dn(cert, NULL, &len);
@@ -889,26 +1081,128 @@
 		wpa_printf(MSG_DEBUG, "TLS: Peer cert chain %d/%d: %s",
 			   i + 1, num_certs, buf);
 
+		if (conn->global->event_cb) {
+			struct wpabuf *cert_buf = NULL;
+			union tls_event_data ev;
+#ifdef CONFIG_SHA256
+			u8 hash[32];
+			const u8 *_addr[1];
+			size_t _len[1];
+#endif /* CONFIG_SHA256 */
+
+			os_memset(&ev, 0, sizeof(ev));
+			if (conn->global->cert_in_cb) {
+				cert_buf = wpabuf_alloc_copy(certs[i].data,
+							     certs[i].size);
+				ev.peer_cert.cert = cert_buf;
+			}
+#ifdef CONFIG_SHA256
+			_addr[0] = certs[i].data;
+			_len[0] = certs[i].size;
+			if (sha256_vector(1, _addr, _len, hash) == 0) {
+				ev.peer_cert.hash = hash;
+				ev.peer_cert.hash_len = sizeof(hash);
+			}
+#endif /* CONFIG_SHA256 */
+			ev.peer_cert.depth = i;
+			ev.peer_cert.subject = buf;
+			conn->global->event_cb(conn->global->cb_ctx,
+					       TLS_PEER_CERTIFICATE, &ev);
+			wpabuf_free(cert_buf);
+		}
+
 		if (i == 0) {
-			/* TODO: validate subject_match and altsubject_match */
+			if (conn->suffix_match &&
+			    !gnutls_x509_crt_check_hostname(
+				    cert, conn->suffix_match)) {
+				wpa_printf(MSG_WARNING,
+					   "TLS: Domain suffix match '%s' not found",
+					   conn->suffix_match);
+				gnutls_tls_fail_event(
+					conn, &certs[i], i, buf,
+					"Domain suffix mismatch",
+					TLS_FAIL_DOMAIN_SUFFIX_MISMATCH);
+				err = GNUTLS_A_BAD_CERTIFICATE;
+				gnutls_x509_crt_deinit(cert);
+				os_free(buf);
+				goto out;
+			}
+
+#if GNUTLS_VERSION_NUMBER >= 0x030300
+			if (conn->domain_match &&
+			    !gnutls_x509_crt_check_hostname2(
+				    cert, conn->domain_match,
+				    GNUTLS_VERIFY_DO_NOT_ALLOW_WILDCARDS)) {
+				wpa_printf(MSG_WARNING,
+					   "TLS: Domain match '%s' not found",
+					   conn->domain_match);
+				gnutls_tls_fail_event(
+					conn, &certs[i], i, buf,
+					"Domain mismatch",
+					TLS_FAIL_DOMAIN_MISMATCH);
+				err = GNUTLS_A_BAD_CERTIFICATE;
+				gnutls_x509_crt_deinit(cert);
+				os_free(buf);
+				goto out;
+			}
+#endif /* >= 3.3.0 */
+
+			/* TODO: validate altsubject_match.
+			 * For now, any such configuration is rejected in
+			 * tls_connection_set_params() */
+
+#if GNUTLS_VERSION_NUMBER < 0x030300
+			/*
+			 * gnutls_certificate_verify_peers() not available, so
+			 * need to check EKU separately.
+			 */
+			if (!conn->global->server &&
+			    !server_eku_purpose(cert)) {
+				wpa_printf(MSG_WARNING,
+					   "GnuTLS: No server EKU");
+				gnutls_tls_fail_event(
+					conn, &certs[i], i, buf,
+					"No server EKU",
+					TLS_FAIL_BAD_CERTIFICATE);
+				err = GNUTLS_A_BAD_CERTIFICATE;
+				gnutls_x509_crt_deinit(cert);
+				os_free(buf);
+				goto out;
+			}
+#endif /* < 3.3.0 */
+		}
+
+		if (!conn->disable_time_checks &&
+		    (gnutls_x509_crt_get_expiration_time(cert) < now.sec ||
+		     gnutls_x509_crt_get_activation_time(cert) > now.sec)) {
+			wpa_printf(MSG_INFO, "TLS: Peer certificate %d/%d is "
+				   "not valid at this time",
+				   i + 1, num_certs);
+			gnutls_tls_fail_event(
+				conn, &certs[i], i, buf,
+				"Certificate is not valid at this time",
+				TLS_FAIL_EXPIRED);
+			gnutls_x509_crt_deinit(cert);
+			os_free(buf);
+			err = GNUTLS_A_CERTIFICATE_EXPIRED;
+			goto out;
 		}
 
 		os_free(buf);
 
-		if (gnutls_x509_crt_get_expiration_time(cert) < now.sec ||
-		    gnutls_x509_crt_get_activation_time(cert) > now.sec) {
-			wpa_printf(MSG_INFO, "TLS: Peer certificate %d/%d is "
-				   "not valid at this time",
-				   i + 1, num_certs);
-			gnutls_x509_crt_deinit(cert);
-			*err = GNUTLS_A_CERTIFICATE_EXPIRED;
-			return -1;
-		}
-
 		gnutls_x509_crt_deinit(cert);
 	}
 
+	if (conn->global->event_cb != NULL)
+		conn->global->event_cb(conn->global->cb_ctx,
+				       TLS_CERT_CHAIN_SUCCESS, NULL);
+
 	return 0;
+
+out:
+	conn->failed++;
+	gnutls_alert_send(session, GNUTLS_AL_FATAL, err);
+	return GNUTLS_E_CERTIFICATE_ERROR;
 }
 
 
@@ -966,6 +1260,8 @@
 
 	ret = gnutls_handshake(conn->session);
 	if (ret < 0) {
+		gnutls_alert_description_t alert;
+
 		switch (ret) {
 		case GNUTLS_E_AGAIN:
 			if (global->server && conn->established &&
@@ -976,10 +1272,20 @@
 			}
 			break;
 		case GNUTLS_E_FATAL_ALERT_RECEIVED:
+			alert = gnutls_alert_get(conn->session);
 			wpa_printf(MSG_DEBUG, "%s - received fatal '%s' alert",
-				   __func__, gnutls_alert_get_name(
-					   gnutls_alert_get(conn->session)));
+				   __func__, gnutls_alert_get_name(alert));
 			conn->read_alerts++;
+			if (conn->global->event_cb != NULL) {
+				union tls_event_data ev;
+
+				os_memset(&ev, 0, sizeof(ev));
+				ev.alert.is_local = 0;
+				ev.alert.type = gnutls_alert_get_name(alert);
+				ev.alert.description = ev.alert.type;
+				conn->global->event_cb(conn->global->cb_ctx,
+						       TLS_ALERT, &ev);
+			}
 			/* continue */
 		default:
 			wpa_printf(MSG_DEBUG, "%s - gnutls_handshake failed "
@@ -988,18 +1294,21 @@
 		}
 	} else {
 		size_t size;
-		gnutls_alert_description_t err;
-
-		if (conn->verify_peer &&
-		    tls_connection_verify_peer(conn, &err)) {
-			wpa_printf(MSG_INFO, "TLS: Peer certificate chain "
-				   "failed validation");
-			conn->failed++;
-			gnutls_alert_send(conn->session, GNUTLS_AL_FATAL, err);
-			goto out;
-		}
 
 		wpa_printf(MSG_DEBUG, "TLS: Handshake completed successfully");
+
+#if GNUTLS_VERSION_NUMBER >= 0x03010a
+		{
+			char *desc;
+
+			desc = gnutls_session_get_desc(conn->session);
+			if (desc) {
+				wpa_printf(MSG_DEBUG, "GnuTLS: %s", desc);
+				gnutls_free(desc);
+			}
+		}
+#endif /* GnuTLS 3.1.10 or newer */
+
 		conn->established = 1;
 		if (conn->push_buf == NULL) {
 			/* Need to return something to get final TLS ACK. */
@@ -1023,7 +1332,6 @@
 			*appl_data = gnutls_get_appl_data(conn);
 	}
 
-out:
 	out_data = conn->push_buf;
 	conn->push_buf = NULL;
 	return out_data;
@@ -1168,14 +1476,6 @@
 }
 
 
-int tls_connection_get_keyblock_size(void *tls_ctx,
-				     struct tls_connection *conn)
-{
-	/* TODO */
-	return -1;
-}
-
-
 unsigned int tls_capabilities(void *tls_ctx)
 {
 	return 0;
@@ -1188,3 +1488,10 @@
 {
 	return -1;
 }
+
+
+int tls_get_library_version(char *buf, size_t buf_len)
+{
+	return os_snprintf(buf, buf_len, "GnuTLS build=%s run=%s",
+			   GNUTLS_VERSION, gnutls_check_version(NULL));
+}
diff --git a/src/crypto/tls_internal.c b/src/crypto/tls_internal.c
index 6563ed2..afd4695 100644
--- a/src/crypto/tls_internal.c
+++ b/src/crypto/tls_internal.c
@@ -190,6 +190,36 @@
 	if (cred == NULL)
 		return -1;
 
+	if (params->subject_match) {
+		wpa_printf(MSG_INFO, "TLS: subject_match not supported");
+		tlsv1_cred_free(cred);
+		return -1;
+	}
+
+	if (params->altsubject_match) {
+		wpa_printf(MSG_INFO, "TLS: altsubject_match not supported");
+		tlsv1_cred_free(cred);
+		return -1;
+	}
+
+	if (params->suffix_match) {
+		wpa_printf(MSG_INFO, "TLS: suffix_match not supported");
+		tlsv1_cred_free(cred);
+		return -1;
+	}
+
+	if (params->domain_match) {
+		wpa_printf(MSG_INFO, "TLS: domain_match not supported");
+		tlsv1_cred_free(cred);
+		return -1;
+	}
+
+	if (params->openssl_ciphers) {
+		wpa_printf(MSG_INFO, "TLS: openssl_ciphers not supported");
+		tlsv1_cred_free(cred);
+		return -1;
+	}
+
 	if (tlsv1_set_ca_cert(cred, params->ca_cert,
 			      params->ca_cert_blob, params->ca_cert_blob_len,
 			      params->ca_path)) {
@@ -323,25 +353,57 @@
 }
 
 
-int tls_connection_prf(void *tls_ctx, struct tls_connection *conn,
-		       const char *label, int server_random_first,
-		       u8 *out, size_t out_len)
+static int tls_get_keyblock_size(struct tls_connection *conn)
 {
 #ifdef CONFIG_TLS_INTERNAL_CLIENT
+	if (conn->client)
+		return tlsv1_client_get_keyblock_size(conn->client);
+#endif /* CONFIG_TLS_INTERNAL_CLIENT */
+#ifdef CONFIG_TLS_INTERNAL_SERVER
+	if (conn->server)
+		return tlsv1_server_get_keyblock_size(conn->server);
+#endif /* CONFIG_TLS_INTERNAL_SERVER */
+	return -1;
+}
+
+
+int tls_connection_prf(void *tls_ctx, struct tls_connection *conn,
+		       const char *label, int server_random_first,
+		       int skip_keyblock, u8 *out, size_t out_len)
+{
+	int ret = -1, skip = 0;
+	u8 *tmp_out = NULL;
+	u8 *_out = out;
+
+	if (skip_keyblock) {
+		skip = tls_get_keyblock_size(conn);
+		if (skip < 0)
+			return -1;
+		tmp_out = os_malloc(skip + out_len);
+		if (!tmp_out)
+			return -1;
+		_out = tmp_out;
+	}
+
+#ifdef CONFIG_TLS_INTERNAL_CLIENT
 	if (conn->client) {
-		return tlsv1_client_prf(conn->client, label,
-					server_random_first,
-					out, out_len);
+		ret = tlsv1_client_prf(conn->client, label,
+				       server_random_first,
+				       _out, out_len);
 	}
 #endif /* CONFIG_TLS_INTERNAL_CLIENT */
 #ifdef CONFIG_TLS_INTERNAL_SERVER
 	if (conn->server) {
-		return tlsv1_server_prf(conn->server, label,
-					server_random_first,
-					out, out_len);
+		ret = tlsv1_server_prf(conn->server, label,
+				       server_random_first,
+				       _out, out_len);
 	}
 #endif /* CONFIG_TLS_INTERNAL_SERVER */
-	return -1;
+	if (ret == 0 && skip_keyblock)
+		os_memcpy(out, _out + skip, out_len);
+	bin_clear_free(tmp_out, skip);
+
+	return ret;
 }
 
 
@@ -612,21 +674,6 @@
 }
 
 
-int tls_connection_get_keyblock_size(void *tls_ctx,
-				     struct tls_connection *conn)
-{
-#ifdef CONFIG_TLS_INTERNAL_CLIENT
-	if (conn->client)
-		return tlsv1_client_get_keyblock_size(conn->client);
-#endif /* CONFIG_TLS_INTERNAL_CLIENT */
-#ifdef CONFIG_TLS_INTERNAL_SERVER
-	if (conn->server)
-		return tlsv1_server_get_keyblock_size(conn->server);
-#endif /* CONFIG_TLS_INTERNAL_SERVER */
-	return -1;
-}
-
-
 unsigned int tls_capabilities(void *tls_ctx)
 {
 	return 0;
@@ -652,3 +699,9 @@
 #endif /* CONFIG_TLS_INTERNAL_SERVER */
 	return -1;
 }
+
+
+int tls_get_library_version(char *buf, size_t buf_len)
+{
+	return os_snprintf(buf, buf_len, "internal");
+}
diff --git a/src/crypto/tls_none.c b/src/crypto/tls_none.c
index 1a1092a..1b1ba56 100644
--- a/src/crypto/tls_none.c
+++ b/src/crypto/tls_none.c
@@ -87,7 +87,7 @@
 
 int tls_connection_prf(void *tls_ctx, struct tls_connection *conn,
 		       const char *label, int server_random_first,
-		       u8 *out, size_t out_len)
+		       int skip_keyblock, u8 *out, size_t out_len)
 {
 	return -1;
 }
@@ -181,14 +181,13 @@
 }
 
 
-int tls_connection_get_keyblock_size(void *tls_ctx,
-				     struct tls_connection *conn)
-{
-	return -1;
-}
-
-
 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");
+}
diff --git a/src/crypto/tls_nss.c b/src/crypto/tls_nss.c
deleted file mode 100644
index c53c192..0000000
--- a/src/crypto/tls_nss.c
+++ /dev/null
@@ -1,645 +0,0 @@
-/*
- * SSL/TLS interface functions for NSS
- * Copyright (c) 2009, Jouni Malinen <j@w1.fi>
- *
- * This software may be distributed under the terms of the BSD license.
- * See README for more details.
- */
-
-#include "includes.h"
-#include <nspr/prtypes.h>
-#include <nspr/plarenas.h>
-#include <nspr/plhash.h>
-#include <nspr/prio.h>
-#include <nspr/prclist.h>
-#include <nspr/prlock.h>
-#include <nspr/prinit.h>
-#include <nspr/prerror.h>
-#include <nspr/prmem.h>
-#include <nss/nss.h>
-#include <nss/nssilckt.h>
-#include <nss/ssl.h>
-#include <nss/pk11func.h>
-#include <nss/secerr.h>
-
-#include "common.h"
-#include "tls.h"
-
-static int tls_nss_ref_count = 0;
-
-static PRDescIdentity nss_layer_id;
-
-
-struct tls_connection {
-	PRFileDesc *fd;
-
-	int established;
-	int verify_peer;
-	u8 *push_buf, *pull_buf, *pull_buf_offset;
-	size_t push_buf_len, pull_buf_len;
-};
-
-
-static PRStatus nss_io_close(PRFileDesc *fd)
-{
-	wpa_printf(MSG_DEBUG, "NSS: I/O close");
-	return PR_SUCCESS;
-}
-
-
-static PRInt32 nss_io_read(PRFileDesc *fd, void *buf, PRInt32 amount)
-{
-	wpa_printf(MSG_DEBUG, "NSS: I/O read(%d)", amount);
-	return PR_FAILURE;
-}
-
-
-static PRInt32 nss_io_write(PRFileDesc *fd, const void *buf, PRInt32 amount)
-{
-	wpa_printf(MSG_DEBUG, "NSS: I/O write(%d)", amount);
-	return PR_FAILURE;
-}
-
-
-static PRInt32 nss_io_writev(PRFileDesc *fd, const PRIOVec *iov,
-			     PRInt32 iov_size, PRIntervalTime timeout)
-{
-	wpa_printf(MSG_DEBUG, "NSS: I/O writev(%d)", iov_size);
-	return PR_FAILURE;
-}
-
-
-static PRInt32 nss_io_recv(PRFileDesc *fd, void *buf, PRInt32 amount,
-			   PRIntn flags, PRIntervalTime timeout)
-{
-	struct tls_connection *conn = (struct tls_connection *) fd->secret;
-	u8 *end;
-
-	wpa_printf(MSG_DEBUG, "NSS: I/O recv(%d)", amount);
-
-	if (conn->pull_buf == NULL) {
-		wpa_printf(MSG_DEBUG, "NSS: No data available to be read yet");
-		return PR_FAILURE;
-	}
-
-	end = conn->pull_buf + conn->pull_buf_len;
-	if (end - conn->pull_buf_offset < amount)
-		amount = end - conn->pull_buf_offset;
-	os_memcpy(buf, conn->pull_buf_offset, amount);
-	conn->pull_buf_offset += amount;
-	if (conn->pull_buf_offset == end) {
-		wpa_printf(MSG_DEBUG, "%s - pull_buf consumed", __func__);
-		os_free(conn->pull_buf);
-		conn->pull_buf = conn->pull_buf_offset = NULL;
-		conn->pull_buf_len = 0;
-	} else {
-		wpa_printf(MSG_DEBUG, "%s - %lu bytes remaining in pull_buf",
-			   __func__,
-			   (unsigned long) (end - conn->pull_buf_offset));
-	}
-	return amount;
-}
-
-
-static PRInt32 nss_io_send(PRFileDesc *fd, const void *buf, PRInt32 amount,
-			   PRIntn flags, PRIntervalTime timeout)
-{
-	struct tls_connection *conn = (struct tls_connection *) fd->secret;
-	u8 *nbuf;
-
-	wpa_printf(MSG_DEBUG, "NSS: I/O %s", __func__);
-	wpa_hexdump(MSG_MSGDUMP, "NSS: I/O send data", buf, amount);
-
-	nbuf = os_realloc(conn->push_buf, conn->push_buf_len + amount);
-	if (nbuf == NULL) {
-		wpa_printf(MSG_ERROR, "NSS: Failed to allocate memory for the "
-			   "data to be sent");
-		return PR_FAILURE;
-	}
-	os_memcpy(nbuf + conn->push_buf_len, buf, amount);
-	conn->push_buf = nbuf;
-	conn->push_buf_len += amount;
-
-	return amount;
-}
-
-
-static PRInt32 nss_io_recvfrom(PRFileDesc *fd, void *buf, PRInt32 amount,
-			       PRIntn flags, PRNetAddr *addr,
-			       PRIntervalTime timeout)
-{
-	wpa_printf(MSG_DEBUG, "NSS: I/O %s", __func__);
-	return PR_FAILURE;
-}
-
-
-static PRInt32 nss_io_sendto(PRFileDesc *fd, const void *buf, PRInt32 amount,
-			     PRIntn flags, const PRNetAddr *addr,
-			     PRIntervalTime timeout)
-{
-	wpa_printf(MSG_DEBUG, "NSS: I/O %s", __func__);
-	return PR_FAILURE;
-}
-
-
-static PRStatus nss_io_getpeername(PRFileDesc *fd, PRNetAddr *addr)
-{
-	wpa_printf(MSG_DEBUG, "NSS: I/O getpeername");
-
-	/*
-	 * It Looks like NSS only supports IPv4 and IPv6 TCP sockets. Provide a
-	 * fake IPv4 address to work around this even though we are not really
-	 * using TCP.
-	 */
-	os_memset(addr, 0, sizeof(*addr));
-	addr->inet.family = PR_AF_INET;
-
-	return PR_SUCCESS;
-}
-
-
-static PRStatus nss_io_getsocketoption(PRFileDesc *fd,
-				       PRSocketOptionData *data)
-{
-	switch (data->option) {
-	case PR_SockOpt_Nonblocking:
-		wpa_printf(MSG_DEBUG, "NSS: I/O getsocketoption(Nonblocking)");
-		data->value.non_blocking = PR_TRUE;
-		return PR_SUCCESS;
-	default:
-		wpa_printf(MSG_DEBUG, "NSS: I/O getsocketoption(%d)",
-			   data->option);
-		return PR_FAILURE;
-	}
-}
-
-
-static const PRIOMethods nss_io = {
-	PR_DESC_LAYERED,
-	nss_io_close,
-	nss_io_read,
-	nss_io_write,
-	NULL /* available */,
-	NULL /* available64 */,
-	NULL /* fsync */,
-	NULL /* fseek */,
-	NULL /* fseek64 */,
-	NULL /* fileinfo */,
-	NULL /* fileinfo64 */,
-	nss_io_writev,
-	NULL /* connect */,
-	NULL /* accept */,
-	NULL /* bind */,
-	NULL /* listen */,
-	NULL /* shutdown */,
-	nss_io_recv,
-	nss_io_send,
-	nss_io_recvfrom,
-	nss_io_sendto,
-	NULL /* poll */,
-	NULL /* acceptread */,
-	NULL /* transmitfile */,
-	NULL /* getsockname */,
-	nss_io_getpeername,
-	NULL /* reserved_fn_6 */,
-	NULL /* reserved_fn_5 */,
-	nss_io_getsocketoption,
-	NULL /* setsocketoption */,
-	NULL /* sendfile */,
-	NULL /* connectcontinue */,
-	NULL /* reserved_fn_3 */,
-	NULL /* reserved_fn_2 */,
-	NULL /* reserved_fn_1 */,
-	NULL /* reserved_fn_0 */
-};
-
-
-static char * nss_password_cb(PK11SlotInfo *slot, PRBool retry, void *arg)
-{
-	wpa_printf(MSG_ERROR, "NSS: TODO - %s", __func__);
-	return NULL;
-}
-
-
-void * tls_init(const struct tls_config *conf)
-{
-	char *dir;
-
-	tls_nss_ref_count++;
-	if (tls_nss_ref_count > 1)
-		return (void *) 1;
-
-	PR_Init(PR_SYSTEM_THREAD, PR_PRIORITY_NORMAL, 1);
-
-	nss_layer_id = PR_GetUniqueIdentity("wpa_supplicant");
-
-	PK11_SetPasswordFunc(nss_password_cb);
-
-	dir = getenv("SSL_DIR");
-	if (dir) {
-		if (NSS_Init(dir) != SECSuccess) {
-			wpa_printf(MSG_ERROR, "NSS: NSS_Init(cert_dir=%s) "
-				   "failed", dir);
-			return NULL;
-		}
-	} else {
-		if (NSS_NoDB_Init(NULL) != SECSuccess) {
-			wpa_printf(MSG_ERROR, "NSS: NSS_NoDB_Init(NULL) "
-				   "failed");
-			return NULL;
-		}
-	}
-
-	if (SSL_OptionSetDefault(SSL_V2_COMPATIBLE_HELLO, PR_FALSE) !=
-	    SECSuccess ||
-	    SSL_OptionSetDefault(SSL_ENABLE_SSL3, PR_FALSE) != SECSuccess ||
-	    SSL_OptionSetDefault(SSL_ENABLE_SSL2, PR_FALSE) != SECSuccess ||
-	    SSL_OptionSetDefault(SSL_ENABLE_TLS, PR_TRUE) != SECSuccess) {
-		wpa_printf(MSG_ERROR, "NSS: SSL_OptionSetDefault failed");
-		return NULL;
-	}
-
-	if (NSS_SetDomesticPolicy() != SECSuccess) {
-		wpa_printf(MSG_ERROR, "NSS: NSS_SetDomesticPolicy() failed");
-		return NULL;
-	}
-
-	return (void *) 1;
-}
-
-void tls_deinit(void *ssl_ctx)
-{
-	tls_nss_ref_count--;
-	if (tls_nss_ref_count == 0) {
-		if (NSS_Shutdown() != SECSuccess)
-			wpa_printf(MSG_ERROR, "NSS: NSS_Shutdown() failed");
-	}
-}
-
-
-int tls_get_errors(void *tls_ctx)
-{
-	return 0;
-}
-
-
-static SECStatus nss_bad_cert_cb(void *arg, PRFileDesc *fd)
-{
-	struct tls_connection *conn = arg;
-	SECStatus res = SECSuccess;
-	PRErrorCode err;
-	CERTCertificate *cert;
-	char *subject, *issuer;
-
-	err = PR_GetError();
-	if (IS_SEC_ERROR(err))
-		wpa_printf(MSG_DEBUG, "NSS: Bad Server Certificate (sec err "
-			   "%d)", err - SEC_ERROR_BASE);
-	else
-		wpa_printf(MSG_DEBUG, "NSS: Bad Server Certificate (err %d)",
-			   err);
-	cert = SSL_PeerCertificate(fd);
-	subject = CERT_NameToAscii(&cert->subject);
-	issuer = CERT_NameToAscii(&cert->issuer);
-	wpa_printf(MSG_DEBUG, "NSS: Peer certificate subject='%s' issuer='%s'",
-		   subject, issuer);
-	CERT_DestroyCertificate(cert);
-	PR_Free(subject);
-	PR_Free(issuer);
-	if (conn->verify_peer)
-		res = SECFailure;
-
-	return res;
-}
-
-
-static void nss_handshake_cb(PRFileDesc *fd, void *client_data)
-{
-	struct tls_connection *conn = client_data;
-	wpa_printf(MSG_DEBUG, "NSS: Handshake completed");
-	conn->established = 1;
-}
-
-
-struct tls_connection * tls_connection_init(void *tls_ctx)
-{
-	struct tls_connection *conn;
-
-	conn = os_zalloc(sizeof(*conn));
-	if (conn == NULL)
-		return NULL;
-
-	conn->fd = PR_CreateIOLayerStub(nss_layer_id, &nss_io);
-	if (conn->fd == NULL) {
-		os_free(conn);
-		return NULL;
-	}
-	conn->fd->secret = (void *) conn;
-
-	conn->fd = SSL_ImportFD(NULL, conn->fd);
-	if (conn->fd == NULL) {
-		os_free(conn);
-		return NULL;
-	}
-
-	if (SSL_OptionSet(conn->fd, SSL_SECURITY, PR_TRUE) != SECSuccess ||
-	    SSL_OptionSet(conn->fd, SSL_HANDSHAKE_AS_CLIENT, PR_TRUE) !=
-	    SECSuccess ||
-	    SSL_OptionSet(conn->fd, SSL_HANDSHAKE_AS_SERVER, PR_FALSE) !=
-	    SECSuccess ||
-	    SSL_OptionSet(conn->fd, SSL_ENABLE_TLS, PR_TRUE) != SECSuccess ||
-	    SSL_BadCertHook(conn->fd, nss_bad_cert_cb, conn) != SECSuccess ||
-	    SSL_HandshakeCallback(conn->fd, nss_handshake_cb, conn) !=
-	    SECSuccess) {
-		wpa_printf(MSG_ERROR, "NSS: Failed to set options");
-		PR_Close(conn->fd);
-		os_free(conn);
-		return NULL;
-	}
-
-	SSL_ResetHandshake(conn->fd, PR_FALSE);
-
-	return conn;
-}
-
-
-void tls_connection_deinit(void *tls_ctx, struct tls_connection *conn)
-{
-	PR_Close(conn->fd);
-	os_free(conn->push_buf);
-	os_free(conn->pull_buf);
-	os_free(conn);
-}
-
-
-int tls_connection_established(void *tls_ctx, struct tls_connection *conn)
-{
-	return conn->established;
-}
-
-
-int tls_connection_shutdown(void *tls_ctx, struct tls_connection *conn)
-{
-	return -1;
-}
-
-
-int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn,
-			      const struct tls_connection_params *params)
-{
-	wpa_printf(MSG_ERROR, "NSS: TODO - %s", __func__);
-	return 0;
-}
-
-
-int tls_global_set_params(void *tls_ctx,
-			  const struct tls_connection_params *params)
-{
-	return -1;
-}
-
-
-int tls_global_set_verify(void *tls_ctx, int check_crl)
-{
-	return -1;
-}
-
-
-int tls_connection_set_verify(void *tls_ctx, struct tls_connection *conn,
-			      int verify_peer)
-{
-	conn->verify_peer = verify_peer;
-	return 0;
-}
-
-
-int tls_connection_get_keys(void *tls_ctx, struct tls_connection *conn,
-			    struct tls_keys *keys)
-{
-	/* NSS does not export master secret or client/server random. */
-	return -1;
-}
-
-
-int tls_connection_prf(void *tls_ctx, struct tls_connection *conn,
-		       const char *label, int server_random_first,
-		       u8 *out, size_t out_len)
-{
-	if (conn == NULL || server_random_first) {
-		wpa_printf(MSG_INFO, "NSS: Unsupported PRF request "
-			   "(server_random_first=%d)",
-			   server_random_first);
-		return -1;
-	}
-
-	if (SSL_ExportKeyingMaterial(conn->fd, label, NULL, 0, out, out_len) !=
-	    SECSuccess) {
-		wpa_printf(MSG_INFO, "NSS: Failed to use TLS extractor "
-			   "(label='%s' out_len=%d", label, (int) out_len);
-		return -1;
-	}
-
-	return 0;
-}
-
-
-struct wpabuf * tls_connection_handshake(void *tls_ctx,
-					 struct tls_connection *conn,
-					 const struct wpabuf *in_data,
-					 struct wpabuf **appl_data)
-{
-	struct wpabuf *out_data;
-
-	wpa_printf(MSG_DEBUG, "NSS: handshake: in_len=%u",
-		   in_data ? (unsigned int) wpabuf_len(in_data) : 0);
-
-	if (appl_data)
-		*appl_data = NULL;
-
-	if (in_data && wpabuf_len(in_data) > 0) {
-		if (conn->pull_buf) {
-			wpa_printf(MSG_DEBUG, "%s - %lu bytes remaining in "
-				   "pull_buf", __func__,
-				   (unsigned long) conn->pull_buf_len);
-			os_free(conn->pull_buf);
-		}
-		conn->pull_buf = os_malloc(wpabuf_len(in_data));
-		if (conn->pull_buf == NULL)
-			return NULL;
-		os_memcpy(conn->pull_buf, wpabuf_head(in_data),
-			  wpabuf_len(in_data));
-		conn->pull_buf_offset = conn->pull_buf;
-		conn->pull_buf_len = wpabuf_len(in_data);
-	}
-
-	SSL_ForceHandshake(conn->fd);
-
-	if (conn->established && conn->push_buf == NULL) {
-		/* Need to return something to get final TLS ACK. */
-		conn->push_buf = os_malloc(1);
-	}
-
-	if (conn->push_buf == NULL)
-		return NULL;
-	out_data = wpabuf_alloc_ext_data(conn->push_buf, conn->push_buf_len);
-	if (out_data == NULL)
-		os_free(conn->push_buf);
-	conn->push_buf = NULL;
-	conn->push_buf_len = 0;
-	return out_data;
-}
-
-
-struct wpabuf * tls_connection_server_handshake(void *tls_ctx,
-						struct tls_connection *conn,
-						const struct wpabuf *in_data,
-						struct wpabuf **appl_data)
-{
-	return NULL;
-}
-
-
-struct wpabuf * tls_connection_encrypt(void *tls_ctx,
-				       struct tls_connection *conn,
-				       const struct wpabuf *in_data)
-{
-	PRInt32 res;
-	struct wpabuf *buf;
-
-	wpa_printf(MSG_DEBUG, "NSS: encrypt %d bytes",
-		   (int) wpabuf_len(in_data));
-	res = PR_Send(conn->fd, wpabuf_head(in_data), wpabuf_len(in_data), 0,
-		      0);
-	if (res < 0) {
-		wpa_printf(MSG_ERROR, "NSS: Encryption failed");
-		return NULL;
-	}
-	if (conn->push_buf == NULL)
-		return NULL;
-	buf = wpabuf_alloc_ext_data(conn->push_buf, conn->push_buf_len);
-	if (buf == NULL)
-		os_free(conn->push_buf);
-	conn->push_buf = NULL;
-	conn->push_buf_len = 0;
-	return buf;
-}
-
-
-struct wpabuf * tls_connection_decrypt(void *tls_ctx,
-				       struct tls_connection *conn,
-				       const struct wpabuf *in_data)
-{
-	PRInt32 res;
-	struct wpabuf *out;
-
-	wpa_printf(MSG_DEBUG, "NSS: decrypt %d bytes",
-		   (int) wpabuf_len(in_data));
-	if (conn->pull_buf) {
-		wpa_printf(MSG_DEBUG, "%s - %lu bytes remaining in "
-			   "pull_buf", __func__,
-			   (unsigned long) conn->pull_buf_len);
-		os_free(conn->pull_buf);
-	}
-	conn->pull_buf = os_malloc(wpabuf_len(in_data));
-	if (conn->pull_buf == NULL)
-		return NULL;
-	os_memcpy(conn->pull_buf, wpabuf_head(in_data), wpabuf_len(in_data));
-	conn->pull_buf_offset = conn->pull_buf;
-	conn->pull_buf_len = wpabuf_len(in_data);
-
-	/*
-	 * Even though we try to disable TLS compression, it is possible that
-	 * this cannot be done with all TLS libraries. Add extra buffer space
-	 * to handle the possibility of the decrypted data being longer than
-	 * input data.
-	 */
-	out = wpabuf_alloc((wpabuf_len(in_data) + 500) * 3);
-	if (out == NULL)
-		return NULL;
-
-	res = PR_Recv(conn->fd, wpabuf_mhead(out), wpabuf_size(out), 0, 0);
-	wpa_printf(MSG_DEBUG, "NSS: PR_Recv: %d", res);
-	if (res < 0) {
-		wpabuf_free(out);
-		return NULL;
-	}
-	wpabuf_put(out, res);
-
-	return out;
-}
-
-
-int tls_connection_resumed(void *tls_ctx, struct tls_connection *conn)
-{
-	return 0;
-}
-
-
-int tls_connection_set_cipher_list(void *tls_ctx, struct tls_connection *conn,
-				   u8 *ciphers)
-{
-	return -1;
-}
-
-
-int tls_get_cipher(void *tls_ctx, struct tls_connection *conn,
-		   char *buf, size_t buflen)
-{
-	return -1;
-}
-
-
-int tls_connection_enable_workaround(void *tls_ctx,
-				     struct tls_connection *conn)
-{
-	return -1;
-}
-
-
-int tls_connection_client_hello_ext(void *tls_ctx, struct tls_connection *conn,
-				    int ext_type, const u8 *data,
-				    size_t data_len)
-{
-	return -1;
-}
-
-
-int tls_connection_get_failed(void *tls_ctx, struct tls_connection *conn)
-{
-	return 0;
-}
-
-
-int tls_connection_get_read_alerts(void *tls_ctx, struct tls_connection *conn)
-{
-	return 0;
-}
-
-
-int tls_connection_get_write_alerts(void *tls_ctx,
-				    struct tls_connection *conn)
-{
-	return 0;
-}
-
-
-int tls_connection_get_keyblock_size(void *tls_ctx,
-				     struct tls_connection *conn)
-{
-	return -1;
-}
-
-
-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)
-{
-	return -1;
-}
diff --git a/src/crypto/tls_openssl.c b/src/crypto/tls_openssl.c
index e153422..db2d73e 100644
--- a/src/crypto/tls_openssl.c
+++ b/src/crypto/tls_openssl.c
@@ -26,18 +26,9 @@
 
 #include "common.h"
 #include "crypto.h"
+#include "sha1.h"
 #include "tls.h"
 
-#if OPENSSL_VERSION_NUMBER >= 0x0090800fL
-#define OPENSSL_d2i_TYPE const unsigned char **
-#else
-#define OPENSSL_d2i_TYPE unsigned char **
-#endif
-
-#if defined(SSL_CTX_get_app_data) && defined(SSL_CTX_set_app_data)
-#define OPENSSL_SUPPORTS_CTX_APP_DATA
-#endif
-
 #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
@@ -45,14 +36,6 @@
 #define ERR_remove_thread_state(tid) ERR_remove_state(0)
 #endif
 
-#if OPENSSL_VERSION_NUMBER >= 0x10000000L
-/*
- * Session ticket override patch was merged into OpenSSL 0.9.9 tree on
- * 2008-11-15. This version uses a bit different API compared to the old patch.
- */
-#define CONFIG_OPENSSL_TICKET_OVERRIDE
-#endif
-
 #if defined(OPENSSL_IS_BORINGSSL)
 /* stack_index_t is the return type of OpenSSL's sk_XXX_num() functions. */
 typedef size_t stack_index_t;
@@ -81,6 +64,7 @@
 	free(value);
 	return bio;
 }
+
 #endif /* ANDROID */
 
 static int tls_openssl_ref_count = 0;
@@ -98,13 +82,14 @@
 
 struct tls_connection {
 	struct tls_context *context;
+	SSL_CTX *ssl_ctx;
 	SSL *ssl;
 	BIO *ssl_in, *ssl_out;
-#ifndef OPENSSL_NO_ENGINE
+#if defined(ANDROID) || !defined(OPENSSL_NO_ENGINE)
 	ENGINE *engine;        /* functional reference to the engine */
 	EVP_PKEY *private_key; /* the private key if using engine */
 #endif /* OPENSSL_NO_ENGINE */
-	char *subject_match, *altsubject_match, *suffix_match;
+	char *subject_match, *altsubject_match, *suffix_match, *domain_match;
 	int read_alerts, write_alerts, failed;
 
 	tls_session_ticket_cb session_ticket_cb;
@@ -408,7 +393,8 @@
 		goto err;
 	}
 
-	cert = d2i_X509(NULL, (OPENSSL_d2i_TYPE) &priv->cert->pbCertEncoded,
+	cert = d2i_X509(NULL,
+			(const unsigned char **) &priv->cert->pbCertEncoded,
 			priv->cert->cbCertEncoded);
 	if (cert == NULL) {
 		wpa_printf(MSG_INFO, "CryptoAPI: Could not process X509 DER "
@@ -508,7 +494,8 @@
 	}
 
 	while ((ctx = CertEnumCertificatesInStore(cs, ctx))) {
-		cert = d2i_X509(NULL, (OPENSSL_d2i_TYPE) &ctx->pbCertEncoded,
+		cert = d2i_X509(NULL,
+				(const unsigned char **) &ctx->pbCertEncoded,
 				ctx->cbCertEncoded);
 		if (cert == NULL) {
 			wpa_printf(MSG_INFO, "CryptoAPI: Could not process "
@@ -700,12 +687,15 @@
 		NULL, NULL
 	};
 
-	if (!pkcs11_so_path || !pkcs11_module_path)
+	if (!pkcs11_so_path)
 		return 0;
 
 	pre_cmd[1] = pkcs11_so_path;
 	pre_cmd[3] = engine_id;
-	post_cmd[1] = pkcs11_module_path;
+	if (pkcs11_module_path)
+		post_cmd[1] = pkcs11_module_path;
+	else
+		post_cmd[0] = NULL;
 
 	wpa_printf(MSG_DEBUG, "ENGINE: Loading pkcs11 Engine from %s",
 		   pkcs11_so_path);
@@ -747,6 +737,7 @@
 {
 	SSL_CTX *ssl;
 	struct tls_context *context;
+	const char *ciphers;
 
 	if (tls_openssl_ref_count == 0) {
 		tls_global = context = tls_context_new(conf);
@@ -778,7 +769,7 @@
 #endif /* CONFIG_FIPS */
 		SSL_load_error_strings();
 		SSL_library_init();
-#if (OPENSSL_VERSION_NUMBER >= 0x0090800fL) && !defined(OPENSSL_NO_SHA256)
+#ifndef OPENSSL_NO_SHA256
 		EVP_add_digest(EVP_sha256());
 #endif /* OPENSSL_NO_SHA256 */
 		/* TODO: if /dev/urandom is available, PRNG is seeded
@@ -798,24 +789,17 @@
 		PKCS12_PBE_add();
 #endif  /* PKCS12_FUNCS */
 	} else {
-#ifdef OPENSSL_SUPPORTS_CTX_APP_DATA
-		/* Newer OpenSSL can store app-data per-SSL */
 		context = tls_context_new(conf);
 		if (context == NULL)
 			return NULL;
-#else /* OPENSSL_SUPPORTS_CTX_APP_DATA */
-		context = tls_global;
-#endif /* OPENSSL_SUPPORTS_CTX_APP_DATA */
 	}
 	tls_openssl_ref_count++;
 
-	ssl = SSL_CTX_new(TLSv1_method());
+	ssl = SSL_CTX_new(SSLv23_method());
 	if (ssl == NULL) {
 		tls_openssl_ref_count--;
-#ifdef OPENSSL_SUPPORTS_CTX_APP_DATA
 		if (context != tls_global)
 			os_free(context);
-#endif /* OPENSSL_SUPPORTS_CTX_APP_DATA */
 		if (tls_openssl_ref_count == 0) {
 			os_free(tls_global);
 			tls_global = NULL;
@@ -823,19 +807,20 @@
 		return NULL;
 	}
 
+	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);
-#ifdef OPENSSL_SUPPORTS_CTX_APP_DATA
 	SSL_CTX_set_app_data(ssl, context);
-#endif /* OPENSSL_SUPPORTS_CTX_APP_DATA */
 
 #ifndef OPENSSL_NO_ENGINE
+	wpa_printf(MSG_DEBUG, "ENGINE: Loading dynamic engine");
+	ERR_load_ENGINE_strings();
+	ENGINE_load_dynamic();
+
 	if (conf &&
 	    (conf->opensc_engine_path || conf->pkcs11_engine_path ||
 	     conf->pkcs11_module_path)) {
-		wpa_printf(MSG_DEBUG, "ENGINE: Loading dynamic engine");
-		ERR_load_ENGINE_strings();
-		ENGINE_load_dynamic();
-
 		if (tls_engine_load_dynamic_opensc(conf->opensc_engine_path) ||
 		    tls_engine_load_dynamic_pkcs11(conf->pkcs11_engine_path,
 						   conf->pkcs11_module_path)) {
@@ -845,6 +830,18 @@
 	}
 #endif /* OPENSSL_NO_ENGINE */
 
+	if (conf && conf->openssl_ciphers)
+		ciphers = conf->openssl_ciphers;
+	else
+		ciphers = "DEFAULT:!EXP:!LOW";
+	if (SSL_CTX_set_cipher_list(ssl, ciphers) != 1) {
+		wpa_printf(MSG_ERROR,
+			   "OpenSSL: Failed to set cipher string '%s'",
+			   ciphers);
+		tls_deinit(ssl);
+		return NULL;
+	}
+
 	return ssl;
 }
 
@@ -852,11 +849,9 @@
 void tls_deinit(void *ssl_ctx)
 {
 	SSL_CTX *ssl = ssl_ctx;
-#ifdef OPENSSL_SUPPORTS_CTX_APP_DATA
 	struct tls_context *context = SSL_CTX_get_app_data(ssl);
 	if (context != tls_global)
 		os_free(context);
-#endif /* OPENSSL_SUPPORTS_CTX_APP_DATA */
 	SSL_CTX_free(ssl);
 
 	tls_openssl_ref_count--;
@@ -875,27 +870,58 @@
 	}
 }
 
+#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
+
+/* Cryptoki return values */
+#define CKR_PIN_INCORRECT 0x000000a0
+#define CKR_PIN_INVALID 0x000000a1
+#define CKR_PIN_LEN_RANGE 0x000000a2
+
+/* libp11 */
+#define ERR_LIB_PKCS11	ERR_LIB_USER
+
+static int tls_is_pin_error(unsigned int err)
+{
+	return ERR_GET_LIB(err) == ERR_LIB_PKCS11 &&
+		(ERR_GET_REASON(err) == CKR_PIN_INCORRECT ||
+		 ERR_GET_REASON(err) == CKR_PIN_INVALID ||
+		 ERR_GET_REASON(err) == CKR_PIN_LEN_RANGE);
+}
+
+#endif /* OPENSSL_NO_ENGINE */
+
 
 static int tls_engine_init(struct tls_connection *conn, const char *engine_id,
 			   const char *pin, const char *key_id,
 			   const char *cert_id, const char *ca_cert_id)
 {
+#if defined(ANDROID) && defined(OPENSSL_IS_BORINGSSL)
+#if !defined(OPENSSL_NO_ENGINE)
+#error "This code depends on OPENSSL_NO_ENGINE being defined by BoringSSL."
+#endif
+
+	conn->engine = NULL;
+	conn->private_key = EVP_PKEY_from_keystore(key_id);
+	if (!conn->private_key) {
+		wpa_printf(MSG_ERROR,
+			   "ENGINE: cannot load private key with id '%s' [%s]",
+			   key_id,
+			   ERR_error_string(ERR_get_error(), NULL));
+		return TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED;
+	}
+#endif
+
 #ifndef OPENSSL_NO_ENGINE
 	int ret = -1;
 	if (engine_id == NULL) {
 		wpa_printf(MSG_ERROR, "ENGINE: Engine ID not set");
 		return -1;
 	}
-#ifndef ANDROID
-	if (pin == NULL) {
-		wpa_printf(MSG_ERROR, "ENGINE: Smartcard PIN not set");
-		return -1;
-	}
-#endif
-	if (key_id == NULL) {
-		wpa_printf(MSG_ERROR, "ENGINE: Key Id not set");
-		return -1;
-	}
 
 	ERR_clear_error();
 #ifdef ANDROID
@@ -916,21 +942,39 @@
 	wpa_printf(MSG_DEBUG, "ENGINE: engine initialized");
 
 #ifndef ANDROID
-	if (ENGINE_ctrl_cmd_string(conn->engine, "PIN", pin, 0) == 0) {
+	if (pin && ENGINE_ctrl_cmd_string(conn->engine, "PIN", pin, 0) == 0) {
 		wpa_printf(MSG_ERROR, "ENGINE: cannot set pin [%s]",
 			   ERR_error_string(ERR_get_error(), NULL));
 		goto err;
 	}
 #endif
-	/* load private key first in-case PIN is required for cert */
-	conn->private_key = ENGINE_load_private_key(conn->engine,
-						    key_id, NULL, NULL);
-	if (!conn->private_key) {
-		wpa_printf(MSG_ERROR, "ENGINE: cannot load private key with id"
-				" '%s' [%s]", key_id,
-			   ERR_error_string(ERR_get_error(), NULL));
-		ret = TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED;
-		goto err;
+	if (key_id) {
+		/*
+		 * Ensure that the ENGINE does not attempt to use the OpenSSL
+		 * UI system to obtain a PIN, if we didn't provide one.
+		 */
+		struct {
+			const void *password;
+			const char *prompt_info;
+		} key_cb = { "", NULL };
+
+		/* load private key first in-case PIN is required for cert */
+		conn->private_key = ENGINE_load_private_key(conn->engine,
+							    key_id, NULL,
+							    &key_cb);
+		if (!conn->private_key) {
+			unsigned long err = ERR_get_error();
+
+			wpa_printf(MSG_ERROR,
+				   "ENGINE: cannot load private key with id '%s' [%s]",
+				   key_id,
+				   ERR_error_string(err, NULL));
+			if (tls_is_pin_error(err))
+				ret = TLS_SET_PARAMS_ENGINE_PRV_BAD_PIN;
+			else
+				ret = TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED;
+			goto err;
+		}
 	}
 
 	/* handle a certificate and/or CA certificate */
@@ -969,14 +1013,16 @@
 
 static void tls_engine_deinit(struct tls_connection *conn)
 {
-#ifndef OPENSSL_NO_ENGINE
+#if defined(ANDROID) || !defined(OPENSSL_NO_ENGINE)
 	wpa_printf(MSG_DEBUG, "ENGINE: engine deinit");
 	if (conn->private_key) {
 		EVP_PKEY_free(conn->private_key);
 		conn->private_key = NULL;
 	}
 	if (conn->engine) {
+#if !defined(OPENSSL_IS_BORINGSSL)
 		ENGINE_finish(conn->engine);
+#endif
 		conn->engine = NULL;
 	}
 #endif /* OPENSSL_NO_ENGINE */
@@ -1022,15 +1068,12 @@
 	SSL_CTX *ssl = ssl_ctx;
 	struct tls_connection *conn;
 	long options;
-#ifdef OPENSSL_SUPPORTS_CTX_APP_DATA
 	struct tls_context *context = SSL_CTX_get_app_data(ssl);
-#else /* OPENSSL_SUPPORTS_CTX_APP_DATA */
-	struct tls_context *context = tls_global;
-#endif /* OPENSSL_SUPPORTS_CTX_APP_DATA */
 
 	conn = os_zalloc(sizeof(*conn));
 	if (conn == NULL)
 		return NULL;
+	conn->ssl_ctx = ssl_ctx;
 	conn->ssl = SSL_new(ssl);
 	if (conn->ssl == NULL) {
 		tls_show_errors(MSG_INFO, __func__,
@@ -1084,6 +1127,7 @@
 	os_free(conn->subject_match);
 	os_free(conn->altsubject_match);
 	os_free(conn->suffix_match);
+	os_free(conn->domain_match);
 	os_free(conn->session_ticket);
 	os_free(conn);
 }
@@ -1105,7 +1149,7 @@
 	 * and "close notify" shutdown alert would confuse AS. */
 	SSL_set_quiet_shutdown(conn->ssl, 1);
 	SSL_shutdown(conn->ssl);
-	return 0;
+	return SSL_clear(conn->ssl) == 1 ? 0 : -1;
 }
 
 
@@ -1176,7 +1220,8 @@
 
 
 #ifndef CONFIG_NATIVE_WINDOWS
-static int domain_suffix_match(const u8 *val, size_t len, const char *match)
+static int domain_suffix_match(const u8 *val, size_t len, const char *match,
+			       int full)
 {
 	size_t i, match_len;
 
@@ -1189,7 +1234,7 @@
 	}
 
 	match_len = os_strlen(match);
-	if (match_len > len)
+	if (match_len > len || (full && match_len != len))
 		return 0;
 
 	if (os_strncasecmp((const char *) val + len - match_len, match,
@@ -1208,7 +1253,7 @@
 #endif /* CONFIG_NATIVE_WINDOWS */
 
 
-static int tls_match_suffix(X509 *cert, const char *match)
+static int tls_match_suffix(X509 *cert, const char *match, int full)
 {
 #ifdef CONFIG_NATIVE_WINDOWS
 	/* wincrypt.h has conflicting X509_NAME definition */
@@ -1221,7 +1266,8 @@
 	int dns_name = 0;
 	X509_NAME *name;
 
-	wpa_printf(MSG_DEBUG, "TLS: Match domain against suffix %s", match);
+	wpa_printf(MSG_DEBUG, "TLS: Match domain against %s%s",
+		   full ? "": "suffix ", match);
 
 	ext = X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL);
 
@@ -1234,8 +1280,10 @@
 				  gen->d.dNSName->data,
 				  gen->d.dNSName->length);
 		if (domain_suffix_match(gen->d.dNSName->data,
-					gen->d.dNSName->length, match) == 1) {
-			wpa_printf(MSG_DEBUG, "TLS: Suffix match in dNSName found");
+					gen->d.dNSName->length, match, full) ==
+		    1) {
+			wpa_printf(MSG_DEBUG, "TLS: %s in dNSName found",
+				   full ? "Match" : "Suffix match");
 			return 1;
 		}
 	}
@@ -1262,13 +1310,16 @@
 			continue;
 		wpa_hexdump_ascii(MSG_DEBUG, "TLS: Certificate commonName",
 				  cn->data, cn->length);
-		if (domain_suffix_match(cn->data, cn->length, match) == 1) {
-			wpa_printf(MSG_DEBUG, "TLS: Suffix match in commonName found");
+		if (domain_suffix_match(cn->data, cn->length, match, full) == 1)
+		{
+			wpa_printf(MSG_DEBUG, "TLS: %s in commonName found",
+				   full ? "Match" : "Suffix match");
 			return 1;
 		}
 	}
 
-	wpa_printf(MSG_DEBUG, "TLS: No CommonName suffix match found");
+	wpa_printf(MSG_DEBUG, "TLS: No CommonName %smatch found",
+		   full ? "": "suffix ");
 	return 0;
 #endif /* CONFIG_NATIVE_WINDOWS */
 }
@@ -1363,6 +1414,11 @@
 	struct wpabuf *cert = NULL;
 	union tls_event_data ev;
 	struct tls_context *context = conn->context;
+	char *altsubject[TLS_MAX_ALT_SUBJECT];
+	int alt, num_altsubject = 0;
+	GENERAL_NAME *gen;
+	void *ext;
+	stack_index_t i;
 #ifdef CONFIG_SHA256
 	u8 hash[32];
 #endif /* CONFIG_SHA256 */
@@ -1389,8 +1445,52 @@
 #endif /* CONFIG_SHA256 */
 	ev.peer_cert.depth = depth;
 	ev.peer_cert.subject = subject;
+
+	ext = X509_get_ext_d2i(err_cert, NID_subject_alt_name, NULL, NULL);
+	for (i = 0; ext && i < sk_GENERAL_NAME_num(ext); i++) {
+		char *pos;
+
+		if (num_altsubject == TLS_MAX_ALT_SUBJECT)
+			break;
+		gen = sk_GENERAL_NAME_value(ext, i);
+		if (gen->type != GEN_EMAIL &&
+		    gen->type != GEN_DNS &&
+		    gen->type != GEN_URI)
+			continue;
+
+		pos = os_malloc(10 + gen->d.ia5->length + 1);
+		if (pos == NULL)
+			break;
+		altsubject[num_altsubject++] = pos;
+
+		switch (gen->type) {
+		case GEN_EMAIL:
+			os_memcpy(pos, "EMAIL:", 6);
+			pos += 6;
+			break;
+		case GEN_DNS:
+			os_memcpy(pos, "DNS:", 4);
+			pos += 4;
+			break;
+		case GEN_URI:
+			os_memcpy(pos, "URI:", 4);
+			pos += 4;
+			break;
+		}
+
+		os_memcpy(pos, gen->d.ia5->data, gen->d.ia5->length);
+		pos += gen->d.ia5->length;
+		*pos = '\0';
+	}
+
+	for (alt = 0; alt < num_altsubject; alt++)
+		ev.peer_cert.altsubject[alt] = altsubject[alt];
+	ev.peer_cert.num_altsubject = num_altsubject;
+
 	context->event_cb(context->cb_ctx, TLS_PEER_CERTIFICATE, &ev);
 	wpabuf_free(cert);
+	for (alt = 0; alt < num_altsubject; alt++)
+		os_free(altsubject[alt]);
 }
 
 
@@ -1402,7 +1502,7 @@
 	SSL *ssl;
 	struct tls_connection *conn;
 	struct tls_context *context;
-	char *match, *altmatch, *suffix_match;
+	char *match, *altmatch, *suffix_match, *domain_match;
 	const char *err_str;
 
 	err_cert = X509_STORE_CTX_get_current_cert(x509_ctx);
@@ -1430,6 +1530,7 @@
 	match = conn->subject_match;
 	altmatch = conn->altsubject_match;
 	suffix_match = conn->suffix_match;
+	domain_match = conn->domain_match;
 
 	if (!preverify_ok && !conn->ca_cert_verify)
 		preverify_ok = 1;
@@ -1446,7 +1547,11 @@
 	err_str = X509_verify_cert_error_string(err);
 
 #ifdef CONFIG_SHA256
-	if (preverify_ok && depth == 0 && conn->server_cert_only) {
+	/*
+	 * Do not require preverify_ok so we can explicity allow otherwise
+	 * invalid pinned server certificates.
+	 */
+	if (depth == 0 && conn->server_cert_only) {
 		struct wpabuf *cert;
 		cert = get_x509_cert(err_cert);
 		if (!cert) {
@@ -1464,6 +1569,14 @@
 				err_str = "Server certificate mismatch";
 				err = X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN;
 				preverify_ok = 0;
+			} else if (!preverify_ok) {
+				/*
+				 * Certificate matches pinned certificate, allow
+				 * regardless of other problems.
+				 */
+				wpa_printf(MSG_DEBUG,
+					   "OpenSSL: Ignore validation issues for a pinned server certificate");
+				preverify_ok = 1;
 			}
 			wpabuf_free(cert);
 		}
@@ -1499,13 +1612,21 @@
 				       "AltSubject mismatch",
 				       TLS_FAIL_ALTSUBJECT_MISMATCH);
 	} else if (depth == 0 && suffix_match &&
-		   !tls_match_suffix(err_cert, suffix_match)) {
+		   !tls_match_suffix(err_cert, suffix_match, 0)) {
 		wpa_printf(MSG_WARNING, "TLS: Domain suffix match '%s' not found",
 			   suffix_match);
 		preverify_ok = 0;
 		openssl_tls_fail_event(conn, err_cert, err, depth, buf,
 				       "Domain suffix mismatch",
 				       TLS_FAIL_DOMAIN_SUFFIX_MISMATCH);
+	} else if (depth == 0 && domain_match &&
+		   !tls_match_suffix(err_cert, domain_match, 1)) {
+		wpa_printf(MSG_WARNING, "TLS: Domain match '%s' not found",
+			   domain_match);
+		preverify_ok = 0;
+		openssl_tls_fail_event(conn, err_cert, err, depth, buf,
+				       "Domain mismatch",
+				       TLS_FAIL_DOMAIN_MISMATCH);
 	} else
 		openssl_tls_cert_event(conn, err_cert, depth, buf);
 
@@ -1533,7 +1654,7 @@
 	X509_LOOKUP *lookup;
 	int ret = 0;
 
-	lookup = X509_STORE_add_lookup(ssl_ctx->cert_store,
+	lookup = X509_STORE_add_lookup(SSL_CTX_get_cert_store(ssl_ctx),
 				       X509_LOOKUP_file());
 	if (lookup == NULL) {
 		tls_show_errors(MSG_WARNING, __func__,
@@ -1564,18 +1685,19 @@
 				  size_t ca_cert_blob_len, const char *ca_path)
 {
 	SSL_CTX *ssl_ctx = _ssl_ctx;
+	X509_STORE *store;
 
 	/*
 	 * Remove previously configured trusted CA certificates before adding
 	 * new ones.
 	 */
-	X509_STORE_free(ssl_ctx->cert_store);
-	ssl_ctx->cert_store = X509_STORE_new();
-	if (ssl_ctx->cert_store == NULL) {
+	store = X509_STORE_new();
+	if (store == NULL) {
 		wpa_printf(MSG_DEBUG, "OpenSSL: %s - failed to allocate new "
 			   "certificate store", __func__);
 		return -1;
 	}
+	SSL_CTX_set_cert_store(ssl_ctx, store);
 
 	SSL_set_verify(conn->ssl, SSL_VERIFY_PEER, tls_verify_cb);
 	conn->ca_cert_verify = 1;
@@ -1619,7 +1741,8 @@
 	}
 
 	if (ca_cert_blob) {
-		X509 *cert = d2i_X509(NULL, (OPENSSL_d2i_TYPE) &ca_cert_blob,
+		X509 *cert = d2i_X509(NULL,
+				      (const unsigned char **) &ca_cert_blob,
 				      ca_cert_blob_len);
 		if (cert == NULL) {
 			tls_show_errors(MSG_WARNING, __func__,
@@ -1627,7 +1750,8 @@
 			return -1;
 		}
 
-		if (!X509_STORE_add_cert(ssl_ctx->cert_store, cert)) {
+		if (!X509_STORE_add_cert(SSL_CTX_get_cert_store(ssl_ctx),
+					 cert)) {
 			unsigned long err = ERR_peek_error();
 			tls_show_errors(MSG_WARNING, __func__,
 					"Failed to add ca_cert_blob to "
@@ -1769,7 +1893,8 @@
 static int tls_connection_set_subject_match(struct tls_connection *conn,
 					    const char *subject_match,
 					    const char *altsubject_match,
-					    const char *suffix_match)
+					    const char *suffix_match,
+					    const char *domain_match)
 {
 	os_free(conn->subject_match);
 	conn->subject_match = NULL;
@@ -1795,6 +1920,14 @@
 			return -1;
 	}
 
+	os_free(conn->domain_match);
+	conn->domain_match = NULL;
+	if (domain_match) {
+		conn->domain_match = os_strdup(domain_match);
+		if (conn->domain_match == NULL)
+			return -1;
+	}
+
 	return 0;
 }
 
@@ -2049,7 +2182,7 @@
 #ifdef PKCS12_FUNCS
 	PKCS12 *p12;
 
-	p12 = d2i_PKCS12(NULL, (OPENSSL_d2i_TYPE) &blob, len);
+	p12 = d2i_PKCS12(NULL, (const unsigned char **) &blob, len);
 	if (p12 == NULL) {
 		tls_show_errors(MSG_INFO, __func__,
 				"Failed to use PKCS#12 blob");
@@ -2081,9 +2214,13 @@
 
 	if (!ENGINE_ctrl_cmd(conn->engine, "LOAD_CERT_CTRL",
 			     0, &params, NULL, 1)) {
+		unsigned long err = ERR_get_error();
+
 		wpa_printf(MSG_ERROR, "ENGINE: cannot load client cert with id"
 			   " '%s' [%s]", cert_id,
-			   ERR_error_string(ERR_get_error(), NULL));
+			   ERR_error_string(err, NULL));
+		if (tls_is_pin_error(err))
+			return TLS_SET_PARAMS_ENGINE_PRV_BAD_PIN;
 		return TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED;
 	}
 	if (!params.cert) {
@@ -2130,20 +2267,21 @@
 #ifndef OPENSSL_NO_ENGINE
 	X509 *cert;
 	SSL_CTX *ssl_ctx = _ssl_ctx;
+	X509_STORE *store;
 
 	if (tls_engine_get_cert(conn, ca_cert_id, &cert))
 		return -1;
 
 	/* start off the same as tls_connection_ca_cert */
-	X509_STORE_free(ssl_ctx->cert_store);
-	ssl_ctx->cert_store = X509_STORE_new();
-	if (ssl_ctx->cert_store == NULL) {
+	store = X509_STORE_new();
+	if (store == NULL) {
 		wpa_printf(MSG_DEBUG, "OpenSSL: %s - failed to allocate new "
 			   "certificate store", __func__);
 		X509_free(cert);
 		return -1;
 	}
-	if (!X509_STORE_add_cert(ssl_ctx->cert_store, cert)) {
+	SSL_CTX_set_cert_store(ssl_ctx, store);
+	if (!X509_STORE_add_cert(store, cert)) {
 		unsigned long err = ERR_peek_error();
 		tls_show_errors(MSG_WARNING, __func__,
 				"Failed to add CA certificate from engine "
@@ -2174,7 +2312,7 @@
 
 static int tls_connection_engine_private_key(struct tls_connection *conn)
 {
-#ifndef OPENSSL_NO_ENGINE
+#if defined(ANDROID) || !defined(OPENSSL_NO_ENGINE)
 	if (SSL_use_PrivateKey(conn->ssl, conn->private_key) != 1) {
 		tls_show_errors(MSG_ERROR, __func__,
 				"ENGINE: cannot use private key for TLS");
@@ -2529,8 +2667,6 @@
 		return -1;
 
 	os_memset(keys, 0, sizeof(*keys));
-	keys->master_key = ssl->session->master_key;
-	keys->master_key_len = ssl->session->master_key_length;
 	keys->client_random = ssl->s3->client_random;
 	keys->client_random_len = SSL3_RANDOM_SIZE;
 	keys->server_random = ssl->s3->server_random;
@@ -2541,16 +2677,124 @@
 }
 
 
+static int openssl_get_keyblock_size(SSL *ssl)
+{
+	const EVP_CIPHER *c;
+	const EVP_MD *h;
+	int md_size;
+
+	if (ssl->enc_read_ctx == NULL || ssl->enc_read_ctx->cipher == NULL ||
+	    ssl->read_hash == NULL)
+		return -1;
+
+	c = ssl->enc_read_ctx->cipher;
+#if OPENSSL_VERSION_NUMBER >= 0x00909000L
+	h = EVP_MD_CTX_md(ssl->read_hash);
+#else
+	h = ssl->read_hash;
+#endif
+	if (h)
+		md_size = EVP_MD_size(h);
+#if OPENSSL_VERSION_NUMBER >= 0x10000000L
+	else if (ssl->s3)
+		md_size = ssl->s3->tmp.new_mac_secret_size;
+#endif
+	else
+		return -1;
+
+	wpa_printf(MSG_DEBUG, "OpenSSL: keyblock size: key_len=%d MD_size=%d "
+		   "IV_len=%d", EVP_CIPHER_key_length(c), md_size,
+		   EVP_CIPHER_iv_length(c));
+	return 2 * (EVP_CIPHER_key_length(c) +
+		    md_size +
+		    EVP_CIPHER_iv_length(c));
+}
+
+
+static int openssl_tls_prf(void *tls_ctx, struct tls_connection *conn,
+			   const char *label, int server_random_first,
+			   int skip_keyblock, u8 *out, size_t out_len)
+{
+#ifdef CONFIG_FIPS
+	wpa_printf(MSG_ERROR, "OpenSSL: TLS keys cannot be exported in FIPS "
+		   "mode");
+	return -1;
+#else /* CONFIG_FIPS */
+	SSL *ssl;
+	u8 *rnd;
+	int ret = -1;
+	int skip = 0;
+	u8 *tmp_out = NULL;
+	u8 *_out = out;
+
+	/*
+	 * TLS library did not support key generation, so get the needed TLS
+	 * session parameters and use an internal implementation of TLS PRF to
+	 * derive the key.
+	 */
+
+	if (conn == NULL)
+		return -1;
+	ssl = conn->ssl;
+	if (ssl == NULL || ssl->s3 == NULL || ssl->session == NULL ||
+	    ssl->session->master_key_length <= 0)
+		return -1;
+
+	if (skip_keyblock) {
+		skip = openssl_get_keyblock_size(ssl);
+		if (skip < 0)
+			return -1;
+		tmp_out = os_malloc(skip + out_len);
+		if (!tmp_out)
+			return -1;
+		_out = tmp_out;
+	}
+
+	rnd = os_malloc(2 * SSL3_RANDOM_SIZE);
+	if (!rnd) {
+		os_free(tmp_out);
+		return -1;
+	}
+
+	if (server_random_first) {
+		os_memcpy(rnd, ssl->s3->server_random, SSL3_RANDOM_SIZE);
+		os_memcpy(rnd + SSL3_RANDOM_SIZE, ssl->s3->client_random,
+			SSL3_RANDOM_SIZE);
+	} else {
+		os_memcpy(rnd, ssl->s3->client_random, SSL3_RANDOM_SIZE);
+		os_memcpy(rnd + SSL3_RANDOM_SIZE, ssl->s3->server_random,
+			SSL3_RANDOM_SIZE);
+	}
+
+	/* 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)
+		ret = 0;
+	os_free(rnd);
+	if (ret == 0 && skip_keyblock)
+		os_memcpy(out, _out + skip, out_len);
+	bin_clear_free(tmp_out, skip);
+
+	return ret;
+#endif /* CONFIG_FIPS */
+}
+
+
 int tls_connection_prf(void *tls_ctx, struct tls_connection *conn,
 		       const char *label, int server_random_first,
-		       u8 *out, size_t out_len)
+		       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)
-		return -1;
+	if (server_random_first || skip_keyblock)
+		return openssl_tls_prf(tls_ctx, conn, label,
+				       server_random_first, skip_keyblock,
+				       out, out_len);
 	ssl = conn->ssl;
 	if (SSL_export_keying_material(ssl, out, out_len, label,
 				       os_strlen(label), NULL, 0, 0) == 1) {
@@ -2558,7 +2802,8 @@
 		return 0;
 	}
 #endif
-	return -1;
+	return openssl_tls_prf(tls_ctx, conn, label, server_random_first,
+			       skip_keyblock, out, out_len);
 }
 
 
@@ -2811,7 +3056,11 @@
 
 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
 }
 
 
@@ -2852,7 +3101,7 @@
 			return -1;
 		}
 		ret = os_snprintf(pos, end - pos, ":%s", suite);
-		if (ret < 0 || ret >= end - pos)
+		if (os_snprintf_error(end - pos, ret))
 			break;
 		pos += ret;
 
@@ -2907,15 +3156,9 @@
 	if (conn == NULL || conn->ssl == NULL || ext_type != 35)
 		return -1;
 
-#ifdef CONFIG_OPENSSL_TICKET_OVERRIDE
 	if (SSL_set_session_ticket_ext(conn->ssl, (void *) data,
 				       data_len) != 1)
 		return -1;
-#else /* CONFIG_OPENSSL_TICKET_OVERRIDE */
-	if (SSL_set_hello_extension(conn->ssl, ext_type, (void *) data,
-				    data_len) != 1)
-		return -1;
-#endif /* CONFIG_OPENSSL_TICKET_OVERRIDE */
 
 	return 0;
 }
@@ -3058,13 +3301,13 @@
 		return 0;
 	}
 
-	store = SSL_CTX_get_cert_store(s->ctx);
+	store = SSL_CTX_get_cert_store(conn->ssl_ctx);
 	if (conn->peer_issuer) {
 		debug_print_cert(conn->peer_issuer, "Add OCSP issuer");
 
 		if (X509_STORE_add_cert(store, conn->peer_issuer) != 1) {
 			tls_show_errors(MSG_INFO, __func__,
-					"OpenSSL: Could not add issuer to certificate store\n");
+					"OpenSSL: Could not add issuer to certificate store");
 		}
 		certs = sk_X509_new_null();
 		if (certs) {
@@ -3073,17 +3316,17 @@
 			if (cert && !sk_X509_push(certs, cert)) {
 				tls_show_errors(
 					MSG_INFO, __func__,
-					"OpenSSL: Could not add issuer to OCSP responder trust store\n");
+					"OpenSSL: Could not add issuer to OCSP responder trust store");
 				X509_free(cert);
 				sk_X509_free(certs);
 				certs = NULL;
 			}
-			if (conn->peer_issuer_issuer) {
+			if (certs && conn->peer_issuer_issuer) {
 				cert = X509_dup(conn->peer_issuer_issuer);
 				if (cert && !sk_X509_push(certs, cert)) {
 					tls_show_errors(
 						MSG_INFO, __func__,
-						"OpenSSL: Could not add issuer to OCSP responder trust store\n");
+						"OpenSSL: Could not add issuer's issuer to OCSP responder trust store");
 					X509_free(cert);
 				}
 			}
@@ -3201,32 +3444,77 @@
 {
 	int ret;
 	unsigned long err;
+	int can_pkcs11 = 0;
+	const char *key_id = params->key_id;
+	const char *cert_id = params->cert_id;
+	const char *ca_cert_id = params->ca_cert_id;
+	const char *engine_id = params->engine ? params->engine_id : NULL;
 
 	if (conn == NULL)
 		return -1;
 
+	/*
+	 * If the engine isn't explicitly configured, and any of the
+	 * cert/key fields are actually PKCS#11 URIs, then automatically
+	 * use the PKCS#11 ENGINE.
+	 */
+	if (!engine_id || os_strcmp(engine_id, "pkcs11") == 0)
+		can_pkcs11 = 1;
+
+	if (!key_id && params->private_key && can_pkcs11 &&
+	    os_strncmp(params->private_key, "pkcs11:", 7) == 0) {
+		can_pkcs11 = 2;
+		key_id = params->private_key;
+	}
+
+	if (!cert_id && params->client_cert && can_pkcs11 &&
+	    os_strncmp(params->client_cert, "pkcs11:", 7) == 0) {
+		can_pkcs11 = 2;
+		cert_id = params->client_cert;
+	}
+
+	if (!ca_cert_id && params->ca_cert && can_pkcs11 &&
+	    os_strncmp(params->ca_cert, "pkcs11:", 7) == 0) {
+		can_pkcs11 = 2;
+		ca_cert_id = params->ca_cert;
+	}
+
+	/* If we need to automatically enable the PKCS#11 ENGINE, do so. */
+	if (can_pkcs11 == 2 && !engine_id)
+		engine_id = "pkcs11";
+
+	if (params->flags & TLS_CONN_EAP_FAST) {
+		wpa_printf(MSG_DEBUG,
+			   "OpenSSL: Use TLSv1_method() for EAP-FAST");
+		if (SSL_set_ssl_method(conn->ssl, TLSv1_method()) != 1) {
+			tls_show_errors(MSG_INFO, __func__,
+					"Failed to set TLSv1_method() for EAP-FAST");
+			return -1;
+		}
+	}
+
 	while ((err = ERR_get_error())) {
 		wpa_printf(MSG_INFO, "%s: Clearing pending SSL error: %s",
 			   __func__, ERR_error_string(err, NULL));
 	}
 
-	if (params->engine) {
+	if (engine_id) {
 		wpa_printf(MSG_DEBUG, "SSL: Initializing TLS engine");
-		ret = tls_engine_init(conn, params->engine_id, params->pin,
-				      params->key_id, params->cert_id,
-				      params->ca_cert_id);
+		ret = tls_engine_init(conn, engine_id, params->pin,
+				      key_id, cert_id, ca_cert_id);
 		if (ret)
 			return ret;
 	}
 	if (tls_connection_set_subject_match(conn,
 					     params->subject_match,
 					     params->altsubject_match,
-					     params->suffix_match))
+					     params->suffix_match,
+					     params->domain_match))
 		return -1;
 
-	if (params->engine && params->ca_cert_id) {
+	if (engine_id && ca_cert_id) {
 		if (tls_connection_engine_ca_cert(tls_ctx, conn,
-						  params->ca_cert_id))
+						  ca_cert_id))
 			return TLS_SET_PARAMS_ENGINE_PRV_VERIFY_FAILED;
 	} else if (tls_connection_ca_cert(tls_ctx, conn, params->ca_cert,
 					  params->ca_cert_blob,
@@ -3234,15 +3522,15 @@
 					  params->ca_path))
 		return -1;
 
-	if (params->engine && params->cert_id) {
-		if (tls_connection_engine_client_cert(conn, params->cert_id))
+	if (engine_id && cert_id) {
+		if (tls_connection_engine_client_cert(conn, cert_id))
 			return TLS_SET_PARAMS_ENGINE_PRV_VERIFY_FAILED;
 	} else if (tls_connection_client_cert(conn, params->client_cert,
 					      params->client_cert_blob,
 					      params->client_cert_blob_len))
 		return -1;
 
-	if (params->engine && params->key_id) {
+	if (engine_id && key_id) {
 		wpa_printf(MSG_DEBUG, "TLS: Using private key from engine");
 		if (tls_connection_engine_private_key(conn))
 			return TLS_SET_PARAMS_ENGINE_PRV_VERIFY_FAILED;
@@ -3262,6 +3550,14 @@
 		return -1;
 	}
 
+	if (params->openssl_ciphers &&
+	    SSL_set_cipher_list(conn->ssl, params->openssl_ciphers) != 1) {
+		wpa_printf(MSG_INFO,
+			   "OpenSSL: Failed to set cipher string '%s'",
+			   params->openssl_ciphers);
+		return -1;
+	}
+
 #ifdef SSL_OP_NO_TICKET
 	if (params->flags & TLS_CONN_DISABLE_SESSION_TICKET)
 		SSL_set_options(conn->ssl, SSL_OP_NO_TICKET);
@@ -3328,6 +3624,14 @@
 		return -1;
 	}
 
+	if (params->openssl_ciphers &&
+	    SSL_CTX_set_cipher_list(ssl_ctx, params->openssl_ciphers) != 1) {
+		wpa_printf(MSG_INFO,
+			   "OpenSSL: Failed to set cipher string '%s'",
+			   params->openssl_ciphers);
+		return -1;
+	}
+
 #ifdef SSL_OP_NO_TICKET
 	if (params->flags & TLS_CONN_DISABLE_SESSION_TICKET)
 		SSL_CTX_set_options(ssl_ctx, SSL_OP_NO_TICKET);
@@ -3352,43 +3656,6 @@
 }
 
 
-int tls_connection_get_keyblock_size(void *tls_ctx,
-				     struct tls_connection *conn)
-{
-	const EVP_CIPHER *c;
-	const EVP_MD *h;
-	int md_size;
-
-	if (conn == NULL || conn->ssl == NULL ||
-	    conn->ssl->enc_read_ctx == NULL ||
-	    conn->ssl->enc_read_ctx->cipher == NULL ||
-	    conn->ssl->read_hash == NULL)
-		return -1;
-
-	c = conn->ssl->enc_read_ctx->cipher;
-#if OPENSSL_VERSION_NUMBER >= 0x00909000L
-	h = EVP_MD_CTX_md(conn->ssl->read_hash);
-#else
-	h = conn->ssl->read_hash;
-#endif
-	if (h)
-		md_size = EVP_MD_size(h);
-#if OPENSSL_VERSION_NUMBER >= 0x10000000L
-	else if (conn->ssl->s3)
-		md_size = conn->ssl->s3->tmp.new_mac_secret_size;
-#endif
-	else
-		return -1;
-
-	wpa_printf(MSG_DEBUG, "OpenSSL: keyblock size: key_len=%d MD_size=%d "
-		   "IV_len=%d", EVP_CIPHER_key_length(c), md_size,
-		   EVP_CIPHER_iv_length(c));
-	return 2 * (EVP_CIPHER_key_length(c) +
-		    md_size +
-		    EVP_CIPHER_iv_length(c));
-}
-
-
 unsigned int tls_capabilities(void *tls_ctx)
 {
 	return 0;
@@ -3432,7 +3699,6 @@
 }
 
 
-#ifdef CONFIG_OPENSSL_TICKET_OVERRIDE
 static int tls_session_ticket_ext_cb(SSL *s, const unsigned char *data,
 				     int len, void *arg)
 {
@@ -3458,62 +3724,6 @@
 
 	return 1;
 }
-#else /* CONFIG_OPENSSL_TICKET_OVERRIDE */
-#ifdef SSL_OP_NO_TICKET
-static void tls_hello_ext_cb(SSL *s, int client_server, int type,
-			     unsigned char *data, int len, void *arg)
-{
-	struct tls_connection *conn = arg;
-
-	if (conn == NULL || conn->session_ticket_cb == NULL)
-		return;
-
-	wpa_printf(MSG_DEBUG, "OpenSSL: %s: type=%d length=%d", __func__,
-		   type, len);
-
-	if (type == TLSEXT_TYPE_session_ticket && !client_server) {
-		os_free(conn->session_ticket);
-		conn->session_ticket = NULL;
-
-		wpa_hexdump(MSG_DEBUG, "OpenSSL: ClientHello SessionTicket "
-			    "extension", data, len);
-		conn->session_ticket = os_malloc(len);
-		if (conn->session_ticket == NULL)
-			return;
-
-		os_memcpy(conn->session_ticket, data, len);
-		conn->session_ticket_len = len;
-	}
-}
-#else /* SSL_OP_NO_TICKET */
-static int tls_hello_ext_cb(SSL *s, TLS_EXTENSION *ext, void *arg)
-{
-	struct tls_connection *conn = arg;
-
-	if (conn == NULL || conn->session_ticket_cb == NULL)
-		return 0;
-
-	wpa_printf(MSG_DEBUG, "OpenSSL: %s: type=%d length=%d", __func__,
-		   ext->type, ext->length);
-
-	os_free(conn->session_ticket);
-	conn->session_ticket = NULL;
-
-	if (ext->type == 35) {
-		wpa_hexdump(MSG_DEBUG, "OpenSSL: ClientHello SessionTicket "
-			    "extension", ext->data, ext->length);
-		conn->session_ticket = os_malloc(ext->length);
-		if (conn->session_ticket == NULL)
-			return SSL_AD_INTERNAL_ERROR;
-
-		os_memcpy(conn->session_ticket, ext->data, ext->length);
-		conn->session_ticket_len = ext->length;
-	}
-
-	return 0;
-}
-#endif /* SSL_OP_NO_TICKET */
-#endif /* CONFIG_OPENSSL_TICKET_OVERRIDE */
 #endif /* EAP_FAST || EAP_FAST_DYNAMIC || EAP_SERVER_FAST */
 
 
@@ -3530,33 +3740,12 @@
 		if (SSL_set_session_secret_cb(conn->ssl, tls_sess_sec_cb,
 					      conn) != 1)
 			return -1;
-#ifdef CONFIG_OPENSSL_TICKET_OVERRIDE
 		SSL_set_session_ticket_ext_cb(conn->ssl,
 					      tls_session_ticket_ext_cb, conn);
-#else /* CONFIG_OPENSSL_TICKET_OVERRIDE */
-#ifdef SSL_OP_NO_TICKET
-		SSL_set_tlsext_debug_callback(conn->ssl, tls_hello_ext_cb);
-		SSL_set_tlsext_debug_arg(conn->ssl, conn);
-#else /* SSL_OP_NO_TICKET */
-		if (SSL_set_hello_extension_cb(conn->ssl, tls_hello_ext_cb,
-					       conn) != 1)
-			return -1;
-#endif /* SSL_OP_NO_TICKET */
-#endif /* CONFIG_OPENSSL_TICKET_OVERRIDE */
 	} else {
 		if (SSL_set_session_secret_cb(conn->ssl, NULL, NULL) != 1)
 			return -1;
-#ifdef CONFIG_OPENSSL_TICKET_OVERRIDE
 		SSL_set_session_ticket_ext_cb(conn->ssl, NULL, NULL);
-#else /* CONFIG_OPENSSL_TICKET_OVERRIDE */
-#ifdef SSL_OP_NO_TICKET
-		SSL_set_tlsext_debug_callback(conn->ssl, NULL);
-		SSL_set_tlsext_debug_arg(conn->ssl, conn);
-#else /* SSL_OP_NO_TICKET */
-		if (SSL_set_hello_extension_cb(conn->ssl, NULL, NULL) != 1)
-			return -1;
-#endif /* SSL_OP_NO_TICKET */
-#endif /* CONFIG_OPENSSL_TICKET_OVERRIDE */
 	}
 
 	return 0;
@@ -3564,3 +3753,11 @@
 	return -1;
 #endif /* EAP_FAST || EAP_FAST_DYNAMIC || EAP_SERVER_FAST */
 }
+
+
+int tls_get_library_version(char *buf, size_t buf_len)
+{
+	return os_snprintf(buf, buf_len, "OpenSSL build=%s run=%s",
+			   OPENSSL_VERSION_TEXT,
+			   SSLeay_version(SSLEAY_VERSION));
+}
diff --git a/src/crypto/tls_schannel.c b/src/crypto/tls_schannel.c
deleted file mode 100644
index 2c2daa8..0000000
--- a/src/crypto/tls_schannel.c
+++ /dev/null
@@ -1,732 +0,0 @@
-/*
- * SSL/TLS interface functions for Microsoft Schannel
- * Copyright (c) 2005-2009, Jouni Malinen <j@w1.fi>
- *
- * This software may be distributed under the terms of the BSD license.
- * See README for more details.
- */
-
-/*
- * FIX: Go through all SSPI functions and verify what needs to be freed
- * FIX: session resumption
- * TODO: add support for server cert chain validation
- * TODO: add support for CA cert validation
- * TODO: add support for EAP-TLS (client cert/key conf)
- */
-
-#include "includes.h"
-#include <windows.h>
-#include <wincrypt.h>
-#include <schannel.h>
-#define SECURITY_WIN32
-#include <security.h>
-#include <sspi.h>
-
-#include "common.h"
-#include "tls.h"
-
-
-struct tls_global {
-	HMODULE hsecurity;
-	PSecurityFunctionTable sspi;
-	HCERTSTORE my_cert_store;
-};
-
-struct tls_connection {
-	int established, start;
-	int failed, read_alerts, write_alerts;
-
-	SCHANNEL_CRED schannel_cred;
-	CredHandle creds;
-	CtxtHandle context;
-
-	u8 eap_tls_prf[128];
-	int eap_tls_prf_set;
-};
-
-
-static int schannel_load_lib(struct tls_global *global)
-{
-	INIT_SECURITY_INTERFACE pInitSecurityInterface;
-
-	global->hsecurity = LoadLibrary(TEXT("Secur32.dll"));
-	if (global->hsecurity == NULL) {
-		wpa_printf(MSG_ERROR, "%s: Could not load Secur32.dll - 0x%x",
-			   __func__, (unsigned int) GetLastError());
-		return -1;
-	}
-
-	pInitSecurityInterface = (INIT_SECURITY_INTERFACE) GetProcAddress(
-		global->hsecurity, "InitSecurityInterfaceA");
-	if (pInitSecurityInterface == NULL) {
-		wpa_printf(MSG_ERROR, "%s: Could not find "
-			   "InitSecurityInterfaceA from Secur32.dll",
-			   __func__);
-		FreeLibrary(global->hsecurity);
-		global->hsecurity = NULL;
-		return -1;
-	}
-
-	global->sspi = pInitSecurityInterface();
-	if (global->sspi == NULL) {
-		wpa_printf(MSG_ERROR, "%s: Could not read security "
-			   "interface - 0x%x",
-			   __func__, (unsigned int) GetLastError());
-		FreeLibrary(global->hsecurity);
-		global->hsecurity = NULL;
-		return -1;
-	}
-
-	return 0;
-}
-
-
-void * tls_init(const struct tls_config *conf)
-{
-	struct tls_global *global;
-
-	global = os_zalloc(sizeof(*global));
-	if (global == NULL)
-		return NULL;
-	if (schannel_load_lib(global)) {
-		os_free(global);
-		return NULL;
-	}
-	return global;
-}
-
-
-void tls_deinit(void *ssl_ctx)
-{
-	struct tls_global *global = ssl_ctx;
-
-	if (global->my_cert_store)
-		CertCloseStore(global->my_cert_store, 0);
-	FreeLibrary(global->hsecurity);
-	os_free(global);
-}
-
-
-int tls_get_errors(void *ssl_ctx)
-{
-	return 0;
-}
-
-
-struct tls_connection * tls_connection_init(void *ssl_ctx)
-{
-	struct tls_connection *conn;
-
-	conn = os_zalloc(sizeof(*conn));
-	if (conn == NULL)
-		return NULL;
-	conn->start = 1;
-
-	return conn;
-}
-
-
-void tls_connection_deinit(void *ssl_ctx, struct tls_connection *conn)
-{
-	if (conn == NULL)
-		return;
-
-	os_free(conn);
-}
-
-
-int tls_connection_established(void *ssl_ctx, struct tls_connection *conn)
-{
-	return conn ? conn->established : 0;
-}
-
-
-int tls_connection_shutdown(void *ssl_ctx, struct tls_connection *conn)
-{
-	struct tls_global *global = ssl_ctx;
-	if (conn == NULL)
-		return -1;
-
-	conn->eap_tls_prf_set = 0;
-	conn->established = conn->failed = 0;
-	conn->read_alerts = conn->write_alerts = 0;
-	global->sspi->DeleteSecurityContext(&conn->context);
-	/* FIX: what else needs to be reseted? */
-
-	return 0;
-}
-
-
-int tls_global_set_params(void *tls_ctx,
-			  const struct tls_connection_params *params)
-{
-	return -1;
-}
-
-
-int tls_global_set_verify(void *ssl_ctx, int check_crl)
-{
-	return -1;
-}
-
-
-int tls_connection_set_verify(void *ssl_ctx, struct tls_connection *conn,
-			      int verify_peer)
-{
-	return -1;
-}
-
-
-int tls_connection_get_keys(void *ssl_ctx, struct tls_connection *conn,
-			    struct tls_keys *keys)
-{
-	/* Schannel does not export master secret or client/server random. */
-	return -1;
-}
-
-
-int tls_connection_prf(void *tls_ctx, struct tls_connection *conn,
-		       const char *label, int server_random_first,
-		       u8 *out, size_t out_len)
-{
-	/*
-	 * Cannot get master_key from Schannel, but EapKeyBlock can be used to
-	 * generate session keys for EAP-TLS and EAP-PEAPv0. EAP-PEAPv2 and
-	 * EAP-TTLS cannot use this, though, since they are using different
-	 * labels. The only option could be to implement TLSv1 completely here
-	 * and just use Schannel or CryptoAPI for low-level crypto
-	 * functionality..
-	 */
-
-	if (conn == NULL || !conn->eap_tls_prf_set || server_random_first ||
-	    os_strcmp(label, "client EAP encryption") != 0 ||
-	    out_len > sizeof(conn->eap_tls_prf))
-		return -1;
-
-	os_memcpy(out, conn->eap_tls_prf, out_len);
-
-	return 0;
-}
-
-
-static struct wpabuf * tls_conn_hs_clienthello(struct tls_global *global,
-					       struct tls_connection *conn)
-{
-	DWORD sspi_flags, sspi_flags_out;
-	SecBufferDesc outbuf;
-	SecBuffer outbufs[1];
-	SECURITY_STATUS status;
-	TimeStamp ts_expiry;
-
-	sspi_flags = ISC_REQ_REPLAY_DETECT |
-		ISC_REQ_CONFIDENTIALITY |
-		ISC_RET_EXTENDED_ERROR |
-		ISC_REQ_ALLOCATE_MEMORY |
-		ISC_REQ_MANUAL_CRED_VALIDATION;
-
-	wpa_printf(MSG_DEBUG, "%s: Generating ClientHello", __func__);
-
-	outbufs[0].pvBuffer = NULL;
-	outbufs[0].BufferType = SECBUFFER_TOKEN;
-	outbufs[0].cbBuffer = 0;
-
-	outbuf.cBuffers = 1;
-	outbuf.pBuffers = outbufs;
-	outbuf.ulVersion = SECBUFFER_VERSION;
-
-#ifdef UNICODE
-	status = global->sspi->InitializeSecurityContextW(
-		&conn->creds, NULL, NULL /* server name */, sspi_flags, 0,
-		SECURITY_NATIVE_DREP, NULL, 0, &conn->context,
-		&outbuf, &sspi_flags_out, &ts_expiry);
-#else /* UNICODE */
-	status = global->sspi->InitializeSecurityContextA(
-		&conn->creds, NULL, NULL /* server name */, sspi_flags, 0,
-		SECURITY_NATIVE_DREP, NULL, 0, &conn->context,
-		&outbuf, &sspi_flags_out, &ts_expiry);
-#endif /* UNICODE */
-	if (status != SEC_I_CONTINUE_NEEDED) {
-		wpa_printf(MSG_ERROR, "%s: InitializeSecurityContextA "
-			   "failed - 0x%x",
-			   __func__, (unsigned int) status);
-		return NULL;
-	}
-
-	if (outbufs[0].cbBuffer != 0 && outbufs[0].pvBuffer) {
-		struct wpabuf *buf;
-		wpa_hexdump(MSG_MSGDUMP, "SChannel - ClientHello",
-			    outbufs[0].pvBuffer, outbufs[0].cbBuffer);
-		conn->start = 0;
-		buf = wpabuf_alloc_copy(outbufs[0].pvBuffer,
-					outbufs[0].cbBuffer);
-		if (buf == NULL)
-			return NULL;
-		global->sspi->FreeContextBuffer(outbufs[0].pvBuffer);
-		return buf;
-	}
-
-	wpa_printf(MSG_ERROR, "SChannel: Failed to generate ClientHello");
-
-	return NULL;
-}
-
-
-#ifndef SECPKG_ATTR_EAP_KEY_BLOCK
-#define SECPKG_ATTR_EAP_KEY_BLOCK 0x5b
-
-typedef struct _SecPkgContext_EapKeyBlock {
-	BYTE rgbKeys[128];
-	BYTE rgbIVs[64];
-} SecPkgContext_EapKeyBlock, *PSecPkgContext_EapKeyBlock;
-#endif /* !SECPKG_ATTR_EAP_KEY_BLOCK */
-
-static int tls_get_eap(struct tls_global *global, struct tls_connection *conn)
-{
-	SECURITY_STATUS status;
-	SecPkgContext_EapKeyBlock kb;
-
-	/* Note: Windows NT and Windows Me/98/95 do not support getting
-	 * EapKeyBlock */
-
-	status = global->sspi->QueryContextAttributes(
-		&conn->context, SECPKG_ATTR_EAP_KEY_BLOCK, &kb);
-	if (status != SEC_E_OK) {
-		wpa_printf(MSG_DEBUG, "%s: QueryContextAttributes("
-			   "SECPKG_ATTR_EAP_KEY_BLOCK) failed (%d)",
-			   __func__, (int) status);
-		return -1;
-	}
-
-	wpa_hexdump_key(MSG_MSGDUMP, "Schannel - EapKeyBlock - rgbKeys",
-			kb.rgbKeys, sizeof(kb.rgbKeys));
-	wpa_hexdump_key(MSG_MSGDUMP, "Schannel - EapKeyBlock - rgbIVs",
-			kb.rgbIVs, sizeof(kb.rgbIVs));
-
-	os_memcpy(conn->eap_tls_prf, kb.rgbKeys, sizeof(kb.rgbKeys));
-	conn->eap_tls_prf_set = 1;
-	return 0;
-}
-
-
-struct wpabuf * tls_connection_handshake(void *tls_ctx,
-					 struct tls_connection *conn,
-					 const struct wpabuf *in_data,
-					 struct wpabuf **appl_data)
-{
-	struct tls_global *global = tls_ctx;
-	DWORD sspi_flags, sspi_flags_out;
-	SecBufferDesc inbuf, outbuf;
-	SecBuffer inbufs[2], outbufs[1];
-	SECURITY_STATUS status;
-	TimeStamp ts_expiry;
-	struct wpabuf *out_buf = NULL;
-
-	if (appl_data)
-		*appl_data = NULL;
-
-	if (conn->start)
-		return tls_conn_hs_clienthello(global, conn);
-
-	wpa_printf(MSG_DEBUG, "SChannel: %d bytes handshake data to process",
-		   (int) wpabuf_len(in_data));
-
-	sspi_flags = ISC_REQ_REPLAY_DETECT |
-		ISC_REQ_CONFIDENTIALITY |
-		ISC_RET_EXTENDED_ERROR |
-		ISC_REQ_ALLOCATE_MEMORY |
-		ISC_REQ_MANUAL_CRED_VALIDATION;
-
-	/* Input buffer for Schannel */
-	inbufs[0].pvBuffer = (u8 *) wpabuf_head(in_data);
-	inbufs[0].cbBuffer = wpabuf_len(in_data);
-	inbufs[0].BufferType = SECBUFFER_TOKEN;
-
-	/* Place for leftover data from Schannel */
-	inbufs[1].pvBuffer = NULL;
-	inbufs[1].cbBuffer = 0;
-	inbufs[1].BufferType = SECBUFFER_EMPTY;
-
-	inbuf.cBuffers = 2;
-	inbuf.pBuffers = inbufs;
-	inbuf.ulVersion = SECBUFFER_VERSION;
-
-	/* Output buffer for Schannel */
-	outbufs[0].pvBuffer = NULL;
-	outbufs[0].cbBuffer = 0;
-	outbufs[0].BufferType = SECBUFFER_TOKEN;
-
-	outbuf.cBuffers = 1;
-	outbuf.pBuffers = outbufs;
-	outbuf.ulVersion = SECBUFFER_VERSION;
-
-#ifdef UNICODE
-	status = global->sspi->InitializeSecurityContextW(
-		&conn->creds, &conn->context, NULL, sspi_flags, 0,
-		SECURITY_NATIVE_DREP, &inbuf, 0, NULL,
-		&outbuf, &sspi_flags_out, &ts_expiry);
-#else /* UNICODE */
-	status = global->sspi->InitializeSecurityContextA(
-		&conn->creds, &conn->context, NULL, sspi_flags, 0,
-		SECURITY_NATIVE_DREP, &inbuf, 0, NULL,
-		&outbuf, &sspi_flags_out, &ts_expiry);
-#endif /* UNICODE */
-
-	wpa_printf(MSG_MSGDUMP, "Schannel: InitializeSecurityContext -> "
-		   "status=%d inlen[0]=%d intype[0]=%d inlen[1]=%d "
-		   "intype[1]=%d outlen[0]=%d",
-		   (int) status, (int) inbufs[0].cbBuffer,
-		   (int) inbufs[0].BufferType, (int) inbufs[1].cbBuffer,
-		   (int) inbufs[1].BufferType,
-		   (int) outbufs[0].cbBuffer);
-	if (status == SEC_E_OK || status == SEC_I_CONTINUE_NEEDED ||
-	    (FAILED(status) && (sspi_flags_out & ISC_RET_EXTENDED_ERROR))) {
-		if (outbufs[0].cbBuffer != 0 && outbufs[0].pvBuffer) {
-			wpa_hexdump(MSG_MSGDUMP, "SChannel - output",
-				    outbufs[0].pvBuffer, outbufs[0].cbBuffer);
-			out_buf = wpabuf_alloc_copy(outbufs[0].pvBuffer,
-						    outbufs[0].cbBuffer);
-			global->sspi->FreeContextBuffer(outbufs[0].pvBuffer);
-			outbufs[0].pvBuffer = NULL;
-			if (out_buf == NULL)
-				return NULL;
-		}
-	}
-
-	switch (status) {
-	case SEC_E_INCOMPLETE_MESSAGE:
-		wpa_printf(MSG_DEBUG, "Schannel: SEC_E_INCOMPLETE_MESSAGE");
-		break;
-	case SEC_I_CONTINUE_NEEDED:
-		wpa_printf(MSG_DEBUG, "Schannel: SEC_I_CONTINUE_NEEDED");
-		break;
-	case SEC_E_OK:
-		/* TODO: verify server certificate chain */
-		wpa_printf(MSG_DEBUG, "Schannel: SEC_E_OK - Handshake "
-			   "completed successfully");
-		conn->established = 1;
-		tls_get_eap(global, conn);
-
-		/* Need to return something to get final TLS ACK. */
-		if (out_buf == NULL)
-			out_buf = wpabuf_alloc(0);
-
-		if (inbufs[1].BufferType == SECBUFFER_EXTRA) {
-			wpa_hexdump(MSG_MSGDUMP, "SChannel - Encrypted "
-				    "application data",
-				    inbufs[1].pvBuffer, inbufs[1].cbBuffer);
-			if (appl_data) {
-				*appl_data = wpabuf_alloc_copy(
-					outbufs[1].pvBuffer,
-					outbufs[1].cbBuffer);
-			}
-			global->sspi->FreeContextBuffer(inbufs[1].pvBuffer);
-			inbufs[1].pvBuffer = NULL;
-		}
-		break;
-	case SEC_I_INCOMPLETE_CREDENTIALS:
-		wpa_printf(MSG_DEBUG,
-			   "Schannel: SEC_I_INCOMPLETE_CREDENTIALS");
-		break;
-	case SEC_E_WRONG_PRINCIPAL:
-		wpa_printf(MSG_DEBUG, "Schannel: SEC_E_WRONG_PRINCIPAL");
-		break;
-	case SEC_E_INTERNAL_ERROR:
-		wpa_printf(MSG_DEBUG, "Schannel: SEC_E_INTERNAL_ERROR");
-		break;
-	}
-
-	if (FAILED(status)) {
-		wpa_printf(MSG_DEBUG, "Schannel: Handshake failed "
-			   "(out_buf=%p)", out_buf);
-		conn->failed++;
-		global->sspi->DeleteSecurityContext(&conn->context);
-		return out_buf;
-	}
-
-	if (inbufs[1].BufferType == SECBUFFER_EXTRA) {
-		/* TODO: Can this happen? What to do with this data? */
-		wpa_hexdump(MSG_MSGDUMP, "SChannel - Leftover data",
-			    inbufs[1].pvBuffer, inbufs[1].cbBuffer);
-		global->sspi->FreeContextBuffer(inbufs[1].pvBuffer);
-		inbufs[1].pvBuffer = NULL;
-	}
-
-	return out_buf;
-}
-
-
-struct wpabuf * tls_connection_server_handshake(void *tls_ctx,
-						struct tls_connection *conn,
-						const struct wpabuf *in_data,
-						struct wpabuf **appl_data)
-{
-	return NULL;
-}
-
-
-struct wpabuf * tls_connection_encrypt(void *tls_ctx,
-				       struct tls_connection *conn,
-				       const struct wpabuf *in_data)
-{
-	struct tls_global *global = tls_ctx;
-	SECURITY_STATUS status;
-	SecBufferDesc buf;
-	SecBuffer bufs[4];
-	SecPkgContext_StreamSizes sizes;
-	int i;
-	struct wpabuf *out;
-
-	status = global->sspi->QueryContextAttributes(&conn->context,
-						      SECPKG_ATTR_STREAM_SIZES,
-						      &sizes);
-	if (status != SEC_E_OK) {
-		wpa_printf(MSG_DEBUG, "%s: QueryContextAttributes failed",
-			   __func__);
-		return NULL;
-	}
-	wpa_printf(MSG_DEBUG, "%s: Stream sizes: header=%u trailer=%u",
-		   __func__,
-		   (unsigned int) sizes.cbHeader,
-		   (unsigned int) sizes.cbTrailer);
-
-	out = wpabuf_alloc(sizes.cbHeader + wpabuf_len(in_data) +
-			   sizes.cbTrailer);
-
-	os_memset(&bufs, 0, sizeof(bufs));
-	bufs[0].pvBuffer = wpabuf_put(out, sizes.cbHeader);
-	bufs[0].cbBuffer = sizes.cbHeader;
-	bufs[0].BufferType = SECBUFFER_STREAM_HEADER;
-
-	bufs[1].pvBuffer = wpabuf_put(out, 0);
-	wpabuf_put_buf(out, in_data);
-	bufs[1].cbBuffer = wpabuf_len(in_data);
-	bufs[1].BufferType = SECBUFFER_DATA;
-
-	bufs[2].pvBuffer = wpabuf_put(out, sizes.cbTrailer);
-	bufs[2].cbBuffer = sizes.cbTrailer;
-	bufs[2].BufferType = SECBUFFER_STREAM_TRAILER;
-
-	buf.ulVersion = SECBUFFER_VERSION;
-	buf.cBuffers = 3;
-	buf.pBuffers = bufs;
-
-	status = global->sspi->EncryptMessage(&conn->context, 0, &buf, 0);
-
-	wpa_printf(MSG_MSGDUMP, "Schannel: EncryptMessage -> "
-		   "status=%d len[0]=%d type[0]=%d len[1]=%d type[1]=%d "
-		   "len[2]=%d type[2]=%d",
-		   (int) status,
-		   (int) bufs[0].cbBuffer, (int) bufs[0].BufferType,
-		   (int) bufs[1].cbBuffer, (int) bufs[1].BufferType,
-		   (int) bufs[2].cbBuffer, (int) bufs[2].BufferType);
-	wpa_printf(MSG_MSGDUMP, "Schannel: EncryptMessage pointers: "
-		   "out_data=%p bufs %p %p %p",
-		   wpabuf_head(out), bufs[0].pvBuffer, bufs[1].pvBuffer,
-		   bufs[2].pvBuffer);
-
-	for (i = 0; i < 3; i++) {
-		if (bufs[i].pvBuffer && bufs[i].BufferType != SECBUFFER_EMPTY)
-		{
-			wpa_hexdump(MSG_MSGDUMP, "SChannel: bufs",
-				    bufs[i].pvBuffer, bufs[i].cbBuffer);
-		}
-	}
-
-	if (status == SEC_E_OK) {
-		wpa_printf(MSG_DEBUG, "%s: SEC_E_OK", __func__);
-		wpa_hexdump_buf_key(MSG_MSGDUMP, "Schannel: Encrypted data "
-				    "from EncryptMessage", out);
-		return out;
-	}
-
-	wpa_printf(MSG_DEBUG, "%s: Failed - status=%d",
-		   __func__, (int) status);
-	wpabuf_free(out);
-	return NULL;
-}
-
-
-struct wpabuf * tls_connection_decrypt(void *tls_ctx,
-				       struct tls_connection *conn,
-				       const struct wpabuf *in_data)
-{
-	struct tls_global *global = tls_ctx;
-	SECURITY_STATUS status;
-	SecBufferDesc buf;
-	SecBuffer bufs[4];
-	int i;
-	struct wpabuf *out, *tmp;
-
-	wpa_hexdump_buf(MSG_MSGDUMP,
-			"Schannel: Encrypted data to DecryptMessage", in_data);
-	os_memset(&bufs, 0, sizeof(bufs));
-	tmp = wpabuf_dup(in_data);
-	if (tmp == NULL)
-		return NULL;
-	bufs[0].pvBuffer = wpabuf_mhead(tmp);
-	bufs[0].cbBuffer = wpabuf_len(in_data);
-	bufs[0].BufferType = SECBUFFER_DATA;
-
-	bufs[1].BufferType = SECBUFFER_EMPTY;
-	bufs[2].BufferType = SECBUFFER_EMPTY;
-	bufs[3].BufferType = SECBUFFER_EMPTY;
-
-	buf.ulVersion = SECBUFFER_VERSION;
-	buf.cBuffers = 4;
-	buf.pBuffers = bufs;
-
-	status = global->sspi->DecryptMessage(&conn->context, &buf, 0,
-						    NULL);
-	wpa_printf(MSG_MSGDUMP, "Schannel: DecryptMessage -> "
-		   "status=%d len[0]=%d type[0]=%d len[1]=%d type[1]=%d "
-		   "len[2]=%d type[2]=%d len[3]=%d type[3]=%d",
-		   (int) status,
-		   (int) bufs[0].cbBuffer, (int) bufs[0].BufferType,
-		   (int) bufs[1].cbBuffer, (int) bufs[1].BufferType,
-		   (int) bufs[2].cbBuffer, (int) bufs[2].BufferType,
-		   (int) bufs[3].cbBuffer, (int) bufs[3].BufferType);
-	wpa_printf(MSG_MSGDUMP, "Schannel: DecryptMessage pointers: "
-		   "out_data=%p bufs %p %p %p %p",
-		   wpabuf_head(tmp), bufs[0].pvBuffer, bufs[1].pvBuffer,
-		   bufs[2].pvBuffer, bufs[3].pvBuffer);
-
-	switch (status) {
-	case SEC_E_INCOMPLETE_MESSAGE:
-		wpa_printf(MSG_DEBUG, "%s: SEC_E_INCOMPLETE_MESSAGE",
-			   __func__);
-		break;
-	case SEC_E_OK:
-		wpa_printf(MSG_DEBUG, "%s: SEC_E_OK", __func__);
-		for (i = 0; i < 4; i++) {
-			if (bufs[i].BufferType == SECBUFFER_DATA)
-				break;
-		}
-		if (i == 4) {
-			wpa_printf(MSG_DEBUG, "%s: No output data from "
-				   "DecryptMessage", __func__);
-			wpabuf_free(tmp);
-			return NULL;
-		}
-		wpa_hexdump_key(MSG_MSGDUMP, "Schannel: Decrypted data from "
-				"DecryptMessage",
-				bufs[i].pvBuffer, bufs[i].cbBuffer);
-		out = wpabuf_alloc_copy(bufs[i].pvBuffer, bufs[i].cbBuffer);
-		wpabuf_free(tmp);
-		return out;
-	}
-
-	wpa_printf(MSG_DEBUG, "%s: Failed - status=%d",
-		   __func__, (int) status);
-	wpabuf_free(tmp);
-	return NULL;
-}
-
-
-int tls_connection_resumed(void *ssl_ctx, struct tls_connection *conn)
-{
-	return 0;
-}
-
-
-int tls_connection_set_cipher_list(void *tls_ctx, struct tls_connection *conn,
-				   u8 *ciphers)
-{
-	return -1;
-}
-
-
-int tls_get_cipher(void *ssl_ctx, struct tls_connection *conn,
-		   char *buf, size_t buflen)
-{
-	return -1;
-}
-
-
-int tls_connection_enable_workaround(void *ssl_ctx,
-				     struct tls_connection *conn)
-{
-	return 0;
-}
-
-
-int tls_connection_client_hello_ext(void *ssl_ctx, struct tls_connection *conn,
-				    int ext_type, const u8 *data,
-				    size_t data_len)
-{
-	return -1;
-}
-
-
-int tls_connection_get_failed(void *ssl_ctx, struct tls_connection *conn)
-{
-	if (conn == NULL)
-		return -1;
-	return conn->failed;
-}
-
-
-int tls_connection_get_read_alerts(void *ssl_ctx, struct tls_connection *conn)
-{
-	if (conn == NULL)
-		return -1;
-	return conn->read_alerts;
-}
-
-
-int tls_connection_get_write_alerts(void *ssl_ctx, struct tls_connection *conn)
-{
-	if (conn == NULL)
-		return -1;
-	return conn->write_alerts;
-}
-
-
-int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn,
-			      const struct tls_connection_params *params)
-{
-	struct tls_global *global = tls_ctx;
-	ALG_ID algs[1];
-	SECURITY_STATUS status;
-	TimeStamp ts_expiry;
-
-	if (conn == NULL)
-		return -1;
-
-	if (global->my_cert_store == NULL &&
-	    (global->my_cert_store = CertOpenSystemStore(0, TEXT("MY"))) ==
-	    NULL) {
-		wpa_printf(MSG_ERROR, "%s: CertOpenSystemStore failed - 0x%x",
-			   __func__, (unsigned int) GetLastError());
-		return -1;
-	}
-
-	os_memset(&conn->schannel_cred, 0, sizeof(conn->schannel_cred));
-	conn->schannel_cred.dwVersion = SCHANNEL_CRED_VERSION;
-	conn->schannel_cred.grbitEnabledProtocols = SP_PROT_TLS1;
-	algs[0] = CALG_RSA_KEYX;
-	conn->schannel_cred.cSupportedAlgs = 1;
-	conn->schannel_cred.palgSupportedAlgs = algs;
-	conn->schannel_cred.dwFlags |= SCH_CRED_NO_DEFAULT_CREDS;
-#ifdef UNICODE
-	status = global->sspi->AcquireCredentialsHandleW(
-		NULL, UNISP_NAME_W, SECPKG_CRED_OUTBOUND, NULL,
-		&conn->schannel_cred, NULL, NULL, &conn->creds, &ts_expiry);
-#else /* UNICODE */
-	status = global->sspi->AcquireCredentialsHandleA(
-		NULL, UNISP_NAME_A, SECPKG_CRED_OUTBOUND, NULL,
-		&conn->schannel_cred, NULL, NULL, &conn->creds, &ts_expiry);
-#endif /* UNICODE */
-	if (status != SEC_E_OK) {
-		wpa_printf(MSG_DEBUG, "%s: AcquireCredentialsHandleA failed - "
-			   "0x%x", __func__, (unsigned int) status);
-		return -1;
-	}
-
-	return 0;
-}
-
-
-unsigned int tls_capabilities(void *tls_ctx)
-{
-	return 0;
-}
diff --git a/src/drivers/driver.h b/src/drivers/driver.h
index 6af7294..d452d8c 100644
--- a/src/drivers/driver.h
+++ b/src/drivers/driver.h
@@ -1,6 +1,6 @@
 /*
  * Driver interface definition
- * Copyright (c) 2003-2014, 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.
@@ -20,11 +20,11 @@
 #define WPA_SUPPLICANT_DRIVER_VERSION 4
 
 #include "common/defs.h"
+#include "common/ieee802_11_defs.h"
 #include "utils/list.h"
 
 #define HOSTAPD_CHAN_DISABLED 0x00000001
-#define HOSTAPD_CHAN_PASSIVE_SCAN 0x00000002
-#define HOSTAPD_CHAN_NO_IBSS 0x00000004
+#define HOSTAPD_CHAN_NO_IR 0x00000002
 #define HOSTAPD_CHAN_RADAR 0x00000008
 #define HOSTAPD_CHAN_HT40PLUS 0x00000010
 #define HOSTAPD_CHAN_HT40MINUS 0x00000020
@@ -42,6 +42,12 @@
 #define HOSTAPD_CHAN_VHT_50_30 0x00002000
 #define HOSTAPD_CHAN_VHT_70_10 0x00004000
 
+#define HOSTAPD_CHAN_INDOOR_ONLY 0x00010000
+#define HOSTAPD_CHAN_GO_CONCURRENT 0x00020000
+
+/**
+ * enum reg_change_initiator - Regulatory change initiator
+ */
 enum reg_change_initiator {
 	REGDOM_SET_BY_CORE,
 	REGDOM_SET_BY_USER,
@@ -50,6 +56,9 @@
 	REGDOM_BEACON_HINT,
 };
 
+/**
+ * enum reg_type - Regulatory change types
+ */
 enum reg_type {
 	REGDOM_TYPE_UNKNOWN,
 	REGDOM_TYPE_COUNTRY,
@@ -82,8 +91,8 @@
 	 */
 	u8 max_tx_power;
 
-	/*
-	 * survey_list - Linked list of surveys
+	/**
+	 * survey_list - Linked list of surveys (struct freq_survey)
 	 */
 	struct dl_list survey_list;
 
@@ -102,7 +111,9 @@
 	long double interference_factor;
 #endif /* CONFIG_ACS */
 
-	/* DFS CAC time in milliseconds */
+	/**
+	 * dfs_cac_ms - DFS CAC time in milliseconds
+	 */
 	unsigned int dfs_cac_ms;
 };
 
@@ -170,10 +181,12 @@
 #define IEEE80211_MODE_INFRA	0
 #define IEEE80211_MODE_IBSS	1
 #define IEEE80211_MODE_AP	2
+#define IEEE80211_MODE_MESH	5
 
 #define IEEE80211_CAP_ESS	0x0001
 #define IEEE80211_CAP_IBSS	0x0002
 #define IEEE80211_CAP_PRIVACY	0x0010
+#define IEEE80211_CAP_RRM	0x1000
 
 /* DMG (60 GHz) IEEE 802.11ad */
 /* type - bits 0..1 */
@@ -186,7 +199,6 @@
 #define WPA_SCAN_NOISE_INVALID		BIT(1)
 #define WPA_SCAN_LEVEL_INVALID		BIT(2)
 #define WPA_SCAN_LEVEL_DBM		BIT(3)
-#define WPA_SCAN_AUTHENTICATED		BIT(4)
 #define WPA_SCAN_ASSOCIATED		BIT(5)
 
 /**
@@ -202,6 +214,9 @@
  * @tsf: Timestamp
  * @age: Age of the information in milliseconds (i.e., how many milliseconds
  * ago the last Beacon or Probe Response frame was received)
+ * @est_throughput: Estimated throughput in kbps (this is calculated during
+ * scan result processing if left zero by the driver wrapper)
+ * @snr: Signal-to-noise ratio in dB (calculated during scan result processing)
  * @ie_len: length of the following IE field in octets
  * @beacon_ie_len: length of the following Beacon IE field in octets
  *
@@ -213,6 +228,11 @@
  * constructed of the IEs that are available. This field will also need to
  * include SSID in IE format. All drivers are encouraged to be extended to
  * report all IEs to make it easier to support future additions.
+ *
+ * This structure data is followed by ie_len octets of IEs from Probe Response
+ * frame (or if the driver does not indicate source of IEs, these may also be
+ * from Beacon frame). After the first set of IEs, another set of IEs may follow
+ * (with beacon_ie_len octets of data) if the driver provides both IE sets.
  */
 struct wpa_scan_res {
 	unsigned int flags;
@@ -225,15 +245,11 @@
 	int level;
 	u64 tsf;
 	unsigned int age;
+	unsigned int est_throughput;
+	int snr;
 	size_t ie_len;
 	size_t beacon_ie_len;
-	/*
-	 * Followed by ie_len octets of IEs from Probe Response frame (or if
-	 * the driver does not indicate source of IEs, these may also be from
-	 * Beacon frame). After the first set of IEs, another set of IEs may
-	 * follow (with beacon_ie_len octets of data) if the driver provides
-	 * both IE sets.
-	 */
+	/* Followed by ie_len + beacon_ie_len octets of IE data */
 };
 
 /**
@@ -326,7 +342,7 @@
 	 * is not needed anymore.
 	 */
 	struct wpa_driver_scan_filter {
-		u8 ssid[32];
+		u8 ssid[SSID_MAX_LEN];
 		size_t ssid_len;
 	} *filter_ssids;
 
@@ -370,6 +386,27 @@
 	 */
 	unsigned int low_priority:1;
 
+	/**
+	 * mac_addr_rand - Requests driver to randomize MAC address
+	 */
+	unsigned int mac_addr_rand:1;
+
+	/**
+	 * mac_addr - MAC address used with randomization. The address cannot be
+	 * a multicast one, i.e., bit 0 of byte 0 should not be set.
+	 */
+	const u8 *mac_addr;
+
+	/**
+	 * mac_addr_mask - MAC address mask used with randomization.
+	 *
+	 * Bits that are 0 in the mask should be randomized. Bits that are 1 in
+	 * the mask should be taken as is from mac_addr. The mask should not
+	 * allow the generation of a multicast address, i.e., bit 0 of byte 0
+	 * must be set.
+	 */
+	const u8 *mac_addr_mask;
+
 	/*
 	 * NOTE: Whenever adding new parameters here, please make sure
 	 * wpa_scan_clone_params() and wpa_scan_free_params() get updated with
@@ -399,34 +436,95 @@
 	 */
 	int p2p;
 
+	/**
+	 * sae_data - SAE elements for Authentication frame
+	 *
+	 * This buffer starts with the Authentication transaction sequence
+	 * number field. If SAE is not used, this pointer is %NULL.
+	 */
 	const u8 *sae_data;
+
+	/**
+	 * sae_data_len - Length of sae_data buffer in octets
+	 */
 	size_t sae_data_len;
-
 };
 
+/**
+ * enum wps_mode - WPS mode
+ */
 enum wps_mode {
-	WPS_MODE_NONE /* no WPS provisioning being used */,
-	WPS_MODE_OPEN /* WPS provisioning with AP that is in open mode */,
-	WPS_MODE_PRIVACY /* WPS provisioning with AP that is using protection
-			  */
+	/**
+	 * WPS_MODE_NONE - No WPS provisioning being used
+	 */
+	WPS_MODE_NONE,
+
+	/**
+	 * WPS_MODE_OPEN - WPS provisioning with AP that is in open mode
+	 */
+	WPS_MODE_OPEN,
+
+	/**
+	 * WPS_MODE_PRIVACY - WPS provisioning with AP that is using protection
+	 */
+	WPS_MODE_PRIVACY
 };
 
+/**
+ * struct hostapd_freq_params - Channel parameters
+ */
 struct hostapd_freq_params {
-	int mode;
-	int freq;
-	int channel;
-	/* for HT */
-	int ht_enabled;
-	int sec_channel_offset; /* 0 = HT40 disabled, -1 = HT40 enabled,
-				 * secondary channel below primary, 1 = HT40
-				 * enabled, secondary channel above primary */
+	/**
+	 * mode - Mode/band (HOSTAPD_MODE_IEEE80211A, ..)
+	 */
+	enum hostapd_hw_mode mode;
 
-	/* for VHT */
+	/**
+	 * freq - Primary channel center frequency in MHz
+	 */
+	int freq;
+
+	/**
+	 * channel - Channel number
+	 */
+	int channel;
+
+	/**
+	 * ht_enabled - Whether HT is enabled
+	 */
+	int ht_enabled;
+
+	/**
+	 * sec_channel_offset - Secondary channel offset for HT40
+	 *
+	 * 0 = HT40 disabled,
+	 * -1 = HT40 enabled, secondary channel below primary,
+	 * 1 = HT40 enabled, secondary channel above primary
+	 */
+	int sec_channel_offset;
+
+	/**
+	 * vht_enabled - Whether VHT is enabled
+	 */
 	int vht_enabled;
 
-	/* valid for both HT and VHT, center_freq2 is non-zero
-	 * only for bandwidth 80 and an 80+80 channel */
-	int center_freq1, center_freq2;
+	/**
+	 * center_freq1 - Segment 0 center frequency in MHz
+	 *
+	 * Valid for both HT and VHT.
+	 */
+	int center_freq1;
+
+	/**
+	 * center_freq2 - Segment 1 center frequency in MHz
+	 *
+	 * Non-zero only for bandwidth 80 and an 80+80 channel
+	 */
+	int center_freq2;
+
+	/**
+	 * bandwidth - Channel bandwidth in MHz (20, 40, 80, 160)
+	 */
 	int bandwidth;
 };
 
@@ -675,17 +773,34 @@
 	int fixed_bssid;
 
 	/**
+	 * fixed_freq - Fix control channel in IBSS mode
+	 * 0 = don't fix control channel (default)
+	 * 1 = fix control channel; this prevents IBSS merging with another
+	 *	channel
+	 */
+	int fixed_freq;
+
+	/**
 	 * disable_ht - Disable HT (IEEE 802.11n) for this connection
 	 */
 	int disable_ht;
 
 	/**
-	 * HT Capabilities over-rides. Only bits set in the mask will be used,
-	 * and not all values are used by the kernel anyway. Currently, MCS,
-	 * MPDU and MSDU fields are used.
+	 * htcaps - HT Capabilities over-rides
+	 *
+	 * Only bits set in the mask will be used, and not all values are used
+	 * by the kernel anyway. Currently, MCS, MPDU and MSDU fields are used.
+	 *
+	 * Pointer to struct ieee80211_ht_capabilities.
 	 */
-	const u8 *htcaps;       /* struct ieee80211_ht_capabilities * */
-	const u8 *htcaps_mask;  /* struct ieee80211_ht_capabilities * */
+	const u8 *htcaps;
+
+	/**
+	 * htcaps_mask - HT Capabilities over-rides mask
+	 *
+	 * Pointer to struct ieee80211_ht_capabilities.
+	 */
+	const u8 *htcaps_mask;
 
 #ifdef CONFIG_VHT_OVERRIDES
 	/**
@@ -699,6 +814,20 @@
 	const struct ieee80211_vht_capabilities *vhtcaps;
 	const struct ieee80211_vht_capabilities *vhtcaps_mask;
 #endif /* CONFIG_VHT_OVERRIDES */
+
+	/**
+	 * req_key_mgmt_offload - Request key management offload for connection
+	 *
+	 * Request key management offload for this connection if the device
+	 * supports it.
+	 */
+	int req_key_mgmt_offload;
+
+	/**
+	 * Flag for indicating whether this association includes support for
+	 * RRM (Radio Resource Measurements)
+	 */
+	int rrm_used;
 };
 
 enum hide_ssid {
@@ -895,6 +1024,19 @@
 	int ap_max_inactivity;
 
 	/**
+	 * ctwindow - Client Traffic Window (in TUs)
+	 */
+	u8 p2p_go_ctwindow;
+
+	/**
+	 * smps_mode - SMPS mode
+	 *
+	 * SMPS mode to be used by the AP, specified as the relevant bits of
+	 * ht_capab (i.e. HT_CAP_INFO_SMPS_*).
+	 */
+	unsigned int smps_mode;
+
+	/**
 	 * disable_dgaf - Whether group-addressed frames are disabled
 	 */
 	int disable_dgaf;
@@ -908,6 +1050,38 @@
 	 * freq - Channel parameters for dynamic bandwidth changes
 	 */
 	struct hostapd_freq_params *freq;
+
+	/**
+	 * reenable - Whether this is to re-enable beaconing
+	 */
+	int reenable;
+};
+
+struct wpa_driver_mesh_bss_params {
+#define WPA_DRIVER_MESH_CONF_FLAG_AUTO_PLINKS	0x00000001
+	/*
+	 * TODO: Other mesh configuration parameters would go here.
+	 * See NL80211_MESHCONF_* for all the mesh config parameters.
+	 */
+	unsigned int flags;
+	int peer_link_timeout;
+};
+
+struct wpa_driver_mesh_join_params {
+	const u8 *meshid;
+	int meshid_len;
+	const int *basic_rates;
+	const u8 *ies;
+	int ie_len;
+	struct hostapd_freq_params freq;
+	int beacon_int;
+	int max_peer_links;
+	struct wpa_driver_mesh_bss_params conf;
+#define WPA_DRIVER_MESH_FLAG_USER_MPM	0x00000001
+#define WPA_DRIVER_MESH_FLAG_DRIVER_MPM	0x00000002
+#define WPA_DRIVER_MESH_FLAG_SAE_AUTH	0x00000004
+#define WPA_DRIVER_MESH_FLAG_AMPE	0x00000008
+	unsigned int flags;
 };
 
 /**
@@ -922,6 +1096,9 @@
 #define WPA_DRIVER_CAPA_KEY_MGMT_FT		0x00000020
 #define WPA_DRIVER_CAPA_KEY_MGMT_FT_PSK		0x00000040
 #define WPA_DRIVER_CAPA_KEY_MGMT_WAPI_PSK	0x00000080
+#define WPA_DRIVER_CAPA_KEY_MGMT_SUITE_B	0x00000100
+#define WPA_DRIVER_CAPA_KEY_MGMT_SUITE_B_192	0x00000200
+	/** Bitfield of supported key management suites */
 	unsigned int key_mgmt;
 
 #define WPA_DRIVER_CAPA_ENC_WEP40	0x00000001
@@ -937,94 +1114,127 @@
 #define WPA_DRIVER_CAPA_ENC_BIP_GMAC_256	0x00000400
 #define WPA_DRIVER_CAPA_ENC_BIP_CMAC_256	0x00000800
 #define WPA_DRIVER_CAPA_ENC_GTK_NOT_USED	0x00001000
+	/** Bitfield of supported cipher suites */
 	unsigned int enc;
 
 #define WPA_DRIVER_AUTH_OPEN		0x00000001
 #define WPA_DRIVER_AUTH_SHARED		0x00000002
 #define WPA_DRIVER_AUTH_LEAP		0x00000004
+	/** Bitfield of supported IEEE 802.11 authentication algorithms */
 	unsigned int auth;
 
-/* Driver generated WPA/RSN IE */
+/** Driver generated WPA/RSN IE */
 #define WPA_DRIVER_FLAGS_DRIVER_IE	0x00000001
-/* Driver needs static WEP key setup after association command */
+/** Driver needs static WEP key setup after association command */
 #define WPA_DRIVER_FLAGS_SET_KEYS_AFTER_ASSOC 0x00000002
-/* Driver takes care of all DFS operations */
+/** Driver takes care of all DFS operations */
 #define WPA_DRIVER_FLAGS_DFS_OFFLOAD			0x00000004
-/* Driver takes care of RSN 4-way handshake internally; PMK is configured with
+/** Driver takes care of RSN 4-way handshake internally; PMK is configured with
  * struct wpa_driver_ops::set_key using alg = WPA_ALG_PMK */
 #define WPA_DRIVER_FLAGS_4WAY_HANDSHAKE 0x00000008
+/** Driver is for a wired Ethernet interface */
 #define WPA_DRIVER_FLAGS_WIRED		0x00000010
-/* Driver provides separate commands for authentication and association (SME in
+/** Driver provides separate commands for authentication and association (SME in
  * wpa_supplicant). */
 #define WPA_DRIVER_FLAGS_SME		0x00000020
-/* Driver supports AP mode */
+/** Driver supports AP mode */
 #define WPA_DRIVER_FLAGS_AP		0x00000040
-/* Driver needs static WEP key setup after association has been completed */
+/** Driver needs static WEP key setup after association has been completed */
 #define WPA_DRIVER_FLAGS_SET_KEYS_AFTER_ASSOC_DONE	0x00000080
-/* Driver supports dynamic HT 20/40 MHz channel changes during BSS lifetime */
+/** Driver supports dynamic HT 20/40 MHz channel changes during BSS lifetime */
 #define WPA_DRIVER_FLAGS_HT_2040_COEX			0x00000100
-/* Driver supports concurrent P2P operations */
+/** Driver supports concurrent P2P operations */
 #define WPA_DRIVER_FLAGS_P2P_CONCURRENT	0x00000200
-/*
+/**
  * Driver uses the initial interface as a dedicated management interface, i.e.,
  * it cannot be used for P2P group operations or non-P2P purposes.
  */
 #define WPA_DRIVER_FLAGS_P2P_DEDICATED_INTERFACE	0x00000400
-/* This interface is P2P capable (P2P GO or P2P Client) */
+/** This interface is P2P capable (P2P GO or P2P Client) */
 #define WPA_DRIVER_FLAGS_P2P_CAPABLE	0x00000800
-/* Driver supports station and key removal when stopping an AP */
+/** Driver supports station and key removal when stopping an AP */
 #define WPA_DRIVER_FLAGS_AP_TEARDOWN_SUPPORT		0x00001000
-/*
+/**
  * Driver uses the initial interface for P2P management interface and non-P2P
  * purposes (e.g., connect to infra AP), but this interface cannot be used for
  * P2P group operations.
  */
 #define WPA_DRIVER_FLAGS_P2P_MGMT_AND_NON_P2P		0x00002000
-/*
+/**
  * Driver is known to use sane error codes, i.e., when it indicates that
  * something (e.g., association) fails, there was indeed a failure and the
  * operation does not end up getting completed successfully later.
  */
 #define WPA_DRIVER_FLAGS_SANE_ERROR_CODES		0x00004000
-/* Driver supports off-channel TX */
+/** Driver supports off-channel TX */
 #define WPA_DRIVER_FLAGS_OFFCHANNEL_TX			0x00008000
-/* Driver indicates TX status events for EAPOL Data frames */
+/** Driver indicates TX status events for EAPOL Data frames */
 #define WPA_DRIVER_FLAGS_EAPOL_TX_STATUS		0x00010000
-/* Driver indicates TX status events for Deauth/Disassoc frames */
+/** Driver indicates TX status events for Deauth/Disassoc frames */
 #define WPA_DRIVER_FLAGS_DEAUTH_TX_STATUS		0x00020000
-/* Driver supports roaming (BSS selection) in firmware */
+/** Driver supports roaming (BSS selection) in firmware */
 #define WPA_DRIVER_FLAGS_BSS_SELECTION			0x00040000
-/* Driver supports operating as a TDLS peer */
+/** Driver supports operating as a TDLS peer */
 #define WPA_DRIVER_FLAGS_TDLS_SUPPORT			0x00080000
-/* Driver requires external TDLS setup/teardown/discovery */
+/** Driver requires external TDLS setup/teardown/discovery */
 #define WPA_DRIVER_FLAGS_TDLS_EXTERNAL_SETUP		0x00100000
-/* Driver indicates support for Probe Response offloading in AP mode */
+/** Driver indicates support for Probe Response offloading in AP mode */
 #define WPA_DRIVER_FLAGS_PROBE_RESP_OFFLOAD		0x00200000
-/* Driver supports U-APSD in AP mode */
+/** Driver supports U-APSD in AP mode */
 #define WPA_DRIVER_FLAGS_AP_UAPSD			0x00400000
-/* Driver supports inactivity timer in AP mode */
+/** Driver supports inactivity timer in AP mode */
 #define WPA_DRIVER_FLAGS_INACTIVITY_TIMER		0x00800000
-/* Driver expects user space implementation of MLME in AP mode */
+/** Driver expects user space implementation of MLME in AP mode */
 #define WPA_DRIVER_FLAGS_AP_MLME			0x01000000
-/* Driver supports SAE with user space SME */
+/** Driver supports SAE with user space SME */
 #define WPA_DRIVER_FLAGS_SAE				0x02000000
-/* Driver makes use of OBSS scan mechanism in wpa_supplicant */
+/** Driver makes use of OBSS scan mechanism in wpa_supplicant */
 #define WPA_DRIVER_FLAGS_OBSS_SCAN			0x04000000
-/* Driver supports IBSS (Ad-hoc) mode */
+/** Driver supports IBSS (Ad-hoc) mode */
 #define WPA_DRIVER_FLAGS_IBSS				0x08000000
-/* Driver supports radar detection */
+/** Driver supports radar detection */
 #define WPA_DRIVER_FLAGS_RADAR				0x10000000
-/* Driver supports a dedicated interface for P2P Device */
+/** Driver supports a dedicated interface for P2P Device */
 #define WPA_DRIVER_FLAGS_DEDICATED_P2P_DEVICE		0x20000000
-/* Driver supports QoS Mapping */
+/** Driver supports QoS Mapping */
 #define WPA_DRIVER_FLAGS_QOS_MAPPING			0x40000000
-/* Driver supports CSA in AP mode */
+/** Driver supports CSA in AP mode */
 #define WPA_DRIVER_FLAGS_AP_CSA				0x80000000
-	unsigned int flags;
+/** Driver supports mesh */
+#define WPA_DRIVER_FLAGS_MESH			0x0000000100000000ULL
+/** Driver support ACS offload */
+#define WPA_DRIVER_FLAGS_ACS_OFFLOAD		0x0000000200000000ULL
+/** Driver supports key management offload */
+#define WPA_DRIVER_FLAGS_KEY_MGMT_OFFLOAD	0x0000000400000000ULL
+/** Driver supports TDLS channel switching */
+#define WPA_DRIVER_FLAGS_TDLS_CHANNEL_SWITCH	0x0000000800000000ULL
+/** Driver supports IBSS with HT datarates */
+#define WPA_DRIVER_FLAGS_HT_IBSS		0x0000001000000000ULL
+/** Driver supports IBSS with VHT datarates */
+#define WPA_DRIVER_FLAGS_VHT_IBSS		0x0000002000000000ULL
+/** Driver supports automatic band selection */
+#define WPA_DRIVER_FLAGS_SUPPORT_HW_MODE_ANY	0x0000004000000000ULL
+	u64 flags;
 
+#define WPA_DRIVER_SMPS_MODE_STATIC			0x00000001
+#define WPA_DRIVER_SMPS_MODE_DYNAMIC			0x00000002
+	unsigned int smps_modes;
+
+	unsigned int wmm_ac_supported:1;
+
+	unsigned int mac_addr_rand_scan_supported:1;
+	unsigned int mac_addr_rand_sched_scan_supported:1;
+
+	/** Maximum number of supported active probe SSIDs */
 	int max_scan_ssids;
+
+	/** Maximum number of supported active probe SSIDs for sched_scan */
 	int max_sched_scan_ssids;
+
+	/** Whether sched_scan (offloaded scanning) is supported */
 	int sched_scan_supported;
+
+	/** Maximum number of supported match sets for sched_scan */
 	int max_match_sets;
 
 	/**
@@ -1042,13 +1252,13 @@
 	 * probe_resp_offloads - Bitmap of supported protocols by the driver
 	 * for Probe Response offloading.
 	 */
-/* Driver Probe Response offloading support for WPS ver. 1 */
+/** Driver Probe Response offloading support for WPS ver. 1 */
 #define WPA_DRIVER_PROBE_RESP_OFFLOAD_WPS		0x00000001
-/* Driver Probe Response offloading support for WPS ver. 2 */
+/** Driver Probe Response offloading support for WPS ver. 2 */
 #define WPA_DRIVER_PROBE_RESP_OFFLOAD_WPS2		0x00000002
-/* Driver Probe Response offloading support for P2P */
+/** Driver Probe Response offloading support for P2P */
 #define WPA_DRIVER_PROBE_RESP_OFFLOAD_P2P		0x00000004
-/* Driver Probe Response offloading support for IEEE 802.11u (Interworking) */
+/** Driver Probe Response offloading support for IEEE 802.11u (Interworking) */
 #define WPA_DRIVER_PROBE_RESP_OFFLOAD_INTERWORKING	0x00000008
 	unsigned int probe_resp_offloads;
 
@@ -1069,6 +1279,24 @@
 	unsigned int extended_capa_len;
 
 	struct wowlan_triggers wowlan_triggers;
+
+/** Driver adds the DS Params Set IE in Probe Request frames */
+#define WPA_DRIVER_FLAGS_DS_PARAM_SET_IE_IN_PROBES	0x00000001
+/** Driver adds the WFA TPC IE in Probe Request frames */
+#define WPA_DRIVER_FLAGS_WFA_TPC_IE_IN_PROBES		0x00000002
+/** Driver handles quiet period requests */
+#define WPA_DRIVER_FLAGS_QUIET				0x00000004
+/**
+ * Driver is capable of inserting the current TX power value into the body of
+ * transmitted frames.
+ * Background: Some Action frames include a TPC Report IE. This IE contains a
+ * TX power field, which has to be updated by lower layers. One such Action
+ * frame is Link Measurement Report (part of RRM). Another is TPC Report (part
+ * of spectrum management). Note that this insertion takes place at a fixed
+ * offset, namely the 6th byte in the Action frame body.
+ */
+#define WPA_DRIVER_FLAGS_TX_POWER_INSERTION		0x00000008
+	u32 rrm_flags;
 };
 
 
@@ -1098,6 +1326,10 @@
 	int vht_opmode_enabled;
 	u8 vht_opmode;
 	u32 flags; /* bitmask of WPA_STA_* flags */
+	u32 flags_mask; /* unset bits in flags */
+#ifdef CONFIG_MESH
+	enum mesh_plink_state plink_state;
+#endif /* CONFIG_MESH */
 	int set; /* Set STA parameters instead of add */
 	u8 qosinfo;
 	const u8 *ext_capab;
@@ -1159,16 +1391,19 @@
 	 * WPA_IF_P2P_DEVICE - P2P Device interface is used to indentify the
 	 * abstracted P2P Device function in the driver
 	 */
-	WPA_IF_P2P_DEVICE
+	WPA_IF_P2P_DEVICE,
+
+	/*
+	 * WPA_IF_MESH - Mesh interface
+	 */
+	WPA_IF_MESH,
 };
 
 struct wpa_init_params {
 	void *global_priv;
 	const u8 *bssid;
 	const char *ifname;
-	const u8 *ssid;
-	size_t ssid_len;
-	const char *test_socket;
+	const char *driver_params;
 	int use_pae_group_addr;
 	char **bridge;
 	size_t num_bridge;
@@ -1197,6 +1432,7 @@
 #define WPA_STA_SHORT_PREAMBLE BIT(2)
 #define WPA_STA_MFP BIT(3)
 #define WPA_STA_TDLS_PEER BIT(4)
+#define WPA_STA_AUTHENTICATED BIT(5)
 
 enum tdls_oper {
 	TDLS_DISCOVERY_REQ,
@@ -1244,6 +1480,7 @@
 	int above_threshold;
 	int current_signal;
 	int avg_signal;
+	int avg_beacon_signal;
 	int current_noise;
 	int current_txrate;
 	enum chan_width chanwidth;
@@ -1311,6 +1548,23 @@
 	TDLS_PEER_WMM = BIT(2),
 };
 
+/* valid info in the wmm_params struct */
+enum wmm_params_valid_info {
+	WMM_PARAMS_UAPSD_QUEUES_INFO = BIT(0),
+};
+
+/**
+ * struct wmm_params - WMM parameterss configured for this association
+ * @info_bitmap: Bitmap of valid wmm_params info; indicates what fields
+ *	of the struct contain valid information.
+ * @uapsd_queues: Bitmap of ACs configured for uapsd (valid only if
+ *	%WMM_PARAMS_UAPSD_QUEUES_INFO is set)
+ */
+struct wmm_params {
+	u8 info_bitmap;
+	u8 uapsd_queues;
+};
+
 #ifdef CONFIG_MACSEC
 struct macsec_init_params {
 	Boolean always_include_sci;
@@ -1319,6 +1573,37 @@
 };
 #endif /* CONFIG_MACSEC */
 
+enum drv_br_port_attr {
+	DRV_BR_PORT_ATTR_PROXYARP,
+	DRV_BR_PORT_ATTR_HAIRPIN_MODE,
+};
+
+enum drv_br_net_param {
+	DRV_BR_NET_PARAM_GARP_ACCEPT,
+	DRV_BR_MULTICAST_SNOOPING,
+};
+
+struct drv_acs_params {
+	/* Selected mode (HOSTAPD_MODE_*) */
+	enum hostapd_hw_mode hw_mode;
+
+	/* Indicates whether HT is enabled */
+	int ht_enabled;
+
+	/* Indicates whether HT40 is enabled */
+	int ht40_enabled;
+
+	/* Indicates whether VHT is enabled */
+	int vht_enabled;
+
+	/* Configured ACS channel width */
+	u16 ch_width;
+
+	/* ACS channel list info */
+	unsigned int ch_list_len;
+	const u8 *ch_list;
+};
+
 
 /**
  * struct wpa_driver_ops - Driver interface API definition
@@ -1605,27 +1890,6 @@
 	const u8 * (*get_mac_addr)(void *priv);
 
 	/**
-	 * send_eapol - Optional function for sending EAPOL packets
-	 * @priv: private driver interface data
-	 * @dest: Destination MAC address
-	 * @proto: Ethertype
-	 * @data: EAPOL packet starting with IEEE 802.1X header
-	 * @data_len: Size of the EAPOL packet
-	 *
-	 * Returns: 0 on success, -1 on failure
-	 *
-	 * This optional function can be used to override l2_packet operations
-	 * with driver specific functionality. If this function pointer is set,
-	 * l2_packet module is not used at all and the driver interface code is
-	 * responsible for receiving and sending all EAPOL packets. The
-	 * received EAPOL packets are sent to core code with EVENT_EAPOL_RX
-	 * event. The driver interface is required to implement get_mac_addr()
-	 * handler if send_eapol() is used.
-	 */
-	int (*send_eapol)(void *priv, const u8 *dest, u16 proto,
-			  const u8 *data, size_t data_len);
-
-	/**
 	 * set_operstate - Sets device operating state to DORMANT or UP
 	 * @priv: private driver interface data
 	 * @state: 0 = dormant, 1 = up
@@ -1676,10 +1940,12 @@
 	 * @data: IEEE 802.11 management frame with IEEE 802.11 header
 	 * @data_len: Size of the management frame
 	 * @noack: Do not wait for this frame to be acked (disable retries)
+	 * @freq: Frequency (in MHz) to send the frame on, or 0 to let the
+	 * driver decide
 	 * Returns: 0 on success, -1 on failure
 	 */
 	int (*send_mlme)(void *priv, const u8 *data, size_t data_len,
-			 int noack);
+			 int noack, unsigned int freq);
 
 	/**
 	 * update_ft_ies - Update FT (IEEE 802.11r) IEs
@@ -1700,22 +1966,6 @@
 			     size_t ies_len);
 
 	/**
-	 * send_ft_action - Send FT Action frame (IEEE 802.11r)
-	 * @priv: Private driver interface data
-	 * @action: Action field value
-	 * @target_ap: Target AP address
-	 * @ies: FT IEs (MDIE, FTIE, ...) (FT Request action frame body)
-	 * @ies_len: Length of FT IEs in bytes
-	 * Returns: 0 on success, -1 on failure
-	 *
-	 * The supplicant uses this callback to request the driver to transmit
-	 * an FT Action frame (action category 6) for over-the-DS fast BSS
-	 * transition.
-	 */
-	int (*send_ft_action)(void *priv, u8 action, const u8 *target_ap,
-			      const u8 *ies, size_t ies_len);
-
-	/**
 	 * get_scan_results2 - Fetch the latest scan results
 	 * @priv: private driver interface data
 	 *
@@ -2423,18 +2673,6 @@
 			  int encrypt);
 
 	/**
-	 * shared_freq - Get operating frequency of shared interface(s)
-	 * @priv: Private driver interface data
-	 * Returns: Operating frequency in MHz, 0 if no shared operation in
-	 * use, or -1 on failure
-	 *
-	 * This command can be used to request the current operating frequency
-	 * of any virtual interface that shares the same radio to provide
-	 * information for channel selection for other virtual interfaces.
-	 */
-	int (*shared_freq)(void *priv);
-
-	/**
 	 * get_noa - Get current Notice of Absence attribute payload
 	 * @priv: Private driver interface data
 	 * @buf: Buffer for returning NoA
@@ -2547,6 +2785,45 @@
 			   u8 qos_map_set_len);
 
 	/**
+	 * br_add_ip_neigh - Add a neigh to the bridge ip neigh table
+	 * @priv: Private driver interface data
+	 * @version: IP version of the IP address, 4 or 6
+	 * @ipaddr: IP address for the neigh entry
+	 * @prefixlen: IP address prefix length
+	 * @addr: Corresponding MAC address
+	 * Returns: 0 on success, negative (<0) on failure
+	 */
+	int (*br_add_ip_neigh)(void *priv, u8 version, const u8 *ipaddr,
+			       int prefixlen, const u8 *addr);
+
+	/**
+	 * br_delete_ip_neigh - Remove a neigh from the bridge ip neigh table
+	 * @priv: Private driver interface data
+	 * @version: IP version of the IP address, 4 or 6
+	 * @ipaddr: IP address for the neigh entry
+	 * Returns: 0 on success, negative (<0) on failure
+	 */
+	int (*br_delete_ip_neigh)(void *priv, u8 version, const u8 *ipaddr);
+
+	/**
+	 * br_port_set_attr - Set a bridge port attribute
+	 * @attr: Bridge port attribute to set
+	 * @val: Value to be set
+	 * Returns: 0 on success, negative (<0) on failure
+	 */
+	int (*br_port_set_attr)(void *priv, enum drv_br_port_attr attr,
+				unsigned int val);
+
+	/**
+	 * br_port_set_attr - Set a bridge network parameter
+	 * @param: Bridge parameter to set
+	 * @val: Value to be set
+	 * Returns: 0 on success, negative (<0) on failure
+	 */
+	int (*br_set_net_param)(void *priv, enum drv_br_net_param param,
+				unsigned int val);
+
+	/**
 	 * set_wowlan - Set wake-on-wireless triggers
 	 * @priv: Private driver interface data
 	 * @triggers: wowlan triggers
@@ -2614,14 +2891,17 @@
 	 * set_rekey_info - Set rekey information
 	 * @priv: Private driver interface data
 	 * @kek: Current KEK
+	 * @kek_len: KEK length in octets
 	 * @kck: Current KCK
+	 * @kck_len: KCK length in octets
 	 * @replay_ctr: Current EAPOL-Key Replay Counter
 	 *
 	 * This optional function can be used to provide information for the
 	 * driver/firmware to process EAPOL-Key frames in Group Key Handshake
 	 * while the host (including wpa_supplicant) is sleeping.
 	 */
-	void (*set_rekey_info)(void *priv, const u8 *kek, const u8 *kck,
+	void (*set_rekey_info)(void *priv, const u8 *kek, size_t kek_len,
+			       const u8 *kck, size_t kck_len,
 			       const u8 *replay_ctr);
 
 	/**
@@ -2751,6 +3031,55 @@
 	int (*switch_channel)(void *priv, struct csa_settings *settings);
 
 	/**
+	 * add_tx_ts - Add traffic stream
+	 * @priv: Private driver interface data
+	 * @tsid: Traffic stream ID
+	 * @addr: Receiver address
+	 * @user_prio: User priority of the traffic stream
+	 * @admitted_time: Admitted time for this TS in units of
+	 *	32 microsecond periods (per second).
+	 * Returns: 0 on success, -1 on failure
+	 */
+	int (*add_tx_ts)(void *priv, u8 tsid, const u8 *addr, u8 user_prio,
+			 u16 admitted_time);
+
+	/**
+	 * del_tx_ts - Delete traffic stream
+	 * @priv: Private driver interface data
+	 * @tsid: Traffic stream ID
+	 * @addr: Receiver address
+	 * Returns: 0 on success, -1 on failure
+	 */
+	int (*del_tx_ts)(void *priv, u8 tsid, const u8 *addr);
+
+	/**
+	 * Enable channel-switching with TDLS peer
+	 * @priv: Private driver interface data
+	 * @addr: MAC address of the TDLS peer
+	 * @oper_class: Operating class of the switch channel
+	 * @params: Channel specification
+	 * Returns: 0 on success, -1 on failure
+	 *
+	 * The function indicates to driver that it can start switching to a
+	 * different channel with a specified TDLS peer. The switching is
+	 * assumed on until canceled with tdls_disable_channel_switch().
+	 */
+	int (*tdls_enable_channel_switch)(
+		void *priv, const u8 *addr, u8 oper_class,
+		const struct hostapd_freq_params *params);
+
+	/**
+	 * Disable channel switching with TDLS peer
+	 * @priv: Private driver interface data
+	 * @addr: MAC address of the TDLS peer
+	 * Returns: 0 on success, -1 on failure
+	 *
+	 * This function indicates to the driver that it should stop switching
+	 * with a given TDLS peer.
+	 */
+	int (*tdls_disable_channel_switch)(void *priv, const u8 *addr);
+
+	/**
 	 * start_dfs_cac - Listen for radar interference on the channel
 	 * @priv: Private driver interface data
 	 * @freq: Channel parameters
@@ -3023,6 +3352,40 @@
 	 */
 	int (*disable_transmit_sa)(void *priv, u32 channel, u8 an);
 #endif /* CONFIG_MACSEC */
+
+	/**
+	 * init_mesh - Driver specific initialization for mesh
+	 * @priv: Private driver interface data
+	 * Returns: 0 on success, -1 on failure
+	 */
+	int (*init_mesh)(void *priv);
+
+	/**
+	 * join_mesh - Join a mesh network
+	 * @priv: Private driver interface data
+	 * @params: Mesh configuration parameters
+	 * Returns: 0 on success, -1 on failure
+	 */
+	int (*join_mesh)(void *priv,
+			 struct wpa_driver_mesh_join_params *params);
+
+	/**
+	 * leave_mesh - Leave a mesh network
+	 * @priv: Private driver interface data
+	 * Returns 0 on success, -1 on failure
+	 */
+	int (*leave_mesh)(void *priv);
+
+	/**
+	 * do_acs - Automatically select channel
+	 * @priv: Private driver interface data
+	 * @params: Parameters for ACS
+	 * Returns 0 on success, -1 on failure
+	 *
+	 * This command can be used to offload ACS to the driver if the driver
+	 * indicates support for such offloading (WPA_DRIVER_FLAGS_ACS_OFFLOAD).
+	 */
+	int (*do_acs)(void *priv, struct drv_acs_params *params);
 };
 
 
@@ -3211,11 +3574,6 @@
 	EVENT_ASSOC_TIMED_OUT,
 
 	/**
-	 * EVENT_FT_RRB_RX - FT (IEEE 802.11r) RRB frame received
-	 */
-	EVENT_FT_RRB_RX,
-
-	/**
 	 * EVENT_WPS_BUTTON_PUSHED - Report hardware push button press for WPS
 	 */
 	EVENT_WPS_BUTTON_PUSHED,
@@ -3255,13 +3613,6 @@
 	EVENT_CANCEL_REMAIN_ON_CHANNEL,
 
 	/**
-	 * EVENT_MLME_RX - Report reception of frame for MLME (test use only)
-	 *
-	 * This event is used only by driver_test.c and userspace MLME.
-	 */
-	EVENT_MLME_RX,
-
-	/**
 	 * EVENT_RX_PROBE_REQ - Indicate received Probe Request frame
 	 *
 	 * This event is used to indicate when a Probe Request frame has been
@@ -3289,9 +3640,7 @@
 	 * EVENT_EAPOL_RX - Report received EAPOL frame
 	 *
 	 * When in AP mode with hostapd, this event is required to be used to
-	 * deliver the receive EAPOL frames from the driver. With
-	 * %wpa_supplicant, this event is used only if the send_eapol() handler
-	 * is used to override the use of l2_packet for EAPOL frame TX.
+	 * deliver the receive EAPOL frames from the driver.
 	 */
 	EVENT_EAPOL_RX,
 
@@ -3439,7 +3788,7 @@
 	EVENT_CONNECT_FAILED_REASON,
 
 	/**
-	 * EVENT_RADAR_DETECTED - Notify of radar detection
+	 * EVENT_DFS_RADAR_DETECTED - Notify of radar detection
 	 *
 	 * A radar has been detected on the supplied frequency, hostapd should
 	 * react accordingly (e.g., change channel).
@@ -3447,14 +3796,14 @@
 	EVENT_DFS_RADAR_DETECTED,
 
 	/**
-	 * EVENT_CAC_FINISHED - Notify that channel availability check has been completed
+	 * EVENT_DFS_CAC_FINISHED - Notify that channel availability check has been completed
 	 *
 	 * After a successful CAC, the channel can be marked clear and used.
 	 */
 	EVENT_DFS_CAC_FINISHED,
 
 	/**
-	 * EVENT_CAC_ABORTED - Notify that channel availability check has been aborted
+	 * EVENT_DFS_CAC_ABORTED - Notify that channel availability check has been aborted
 	 *
 	 * The CAC was not successful, and the channel remains in the previous
 	 * state. This may happen due to a radar beeing detected or other
@@ -3463,7 +3812,7 @@
 	EVENT_DFS_CAC_ABORTED,
 
 	/**
-	 * EVENT_DFS_CAC_NOP_FINISHED - Notify that non-occupancy period is over
+	 * EVENT_DFS_NOP_FINISHED - Notify that non-occupancy period is over
 	 *
 	 * The channel which was previously unavailable is now available again.
 	 */
@@ -3498,7 +3847,29 @@
 	 * to reduce issues due to interference or internal co-existence
 	 * information in the driver.
 	 */
-	EVENT_AVOID_FREQUENCIES
+	EVENT_AVOID_FREQUENCIES,
+
+	/**
+	 * EVENT_NEW_PEER_CANDIDATE - new (unknown) mesh peer notification
+	 */
+	EVENT_NEW_PEER_CANDIDATE,
+
+	/**
+	 * EVENT_ACS_CHANNEL_SELECTED - Received selected channels by ACS
+	 *
+	 * Indicates a pair of primary and secondary channels chosen by ACS
+	 * in device.
+	 */
+	EVENT_ACS_CHANNEL_SELECTED,
+
+	/**
+	 * EVENT_DFS_CAC_STARTED - Notify that channel availability check has
+	 * been started.
+	 *
+	 * This event indicates that channel availability check has been started
+	 * on a DFS frequency by a driver that supports DFS Offload.
+	 */
+	EVENT_DFS_CAC_STARTED,
 };
 
 
@@ -3618,9 +3989,62 @@
 		unsigned int freq;
 
 		/**
+		 * wmm_params - WMM parameters used in this association.
+		 */
+		struct wmm_params wmm_params;
+
+		/**
 		 * addr - Station address (for AP mode)
 		 */
 		const u8 *addr;
+
+		/**
+		 * The following is the key management offload information
+		 * @authorized
+		 * @key_replay_ctr
+		 * @key_replay_ctr_len
+		 * @ptk_kck
+		 * @ptk_kek_len
+		 * @ptk_kek
+		 * @ptk_kek_len
+		 */
+
+		/**
+		 * authorized - Status of key management offload,
+		 * 1 = successful
+		 */
+		int authorized;
+
+		/**
+		 * key_replay_ctr - Key replay counter value last used
+		 * in a valid EAPOL-Key frame
+		 */
+		const u8 *key_replay_ctr;
+
+		/**
+		 * key_replay_ctr_len - The length of key_replay_ctr
+		 */
+		size_t key_replay_ctr_len;
+
+		/**
+		 * ptk_kck - The derived PTK KCK
+		 */
+		const u8 *ptk_kck;
+
+		/**
+		 * ptk_kek_len - The length of ptk_kck
+		 */
+		size_t ptk_kck_len;
+
+		/**
+		 * ptk_kek - The derived PTK KEK
+		 */
+		const u8 *ptk_kek;
+
+		/**
+		 * ptk_kek_len - The length of ptk_kek
+		 */
+		size_t ptk_kek_len;
 	} assoc_info;
 
 	/**
@@ -3729,7 +4153,8 @@
 		u8 peer[ETH_ALEN];
 		enum {
 			TDLS_REQUEST_SETUP,
-			TDLS_REQUEST_TEARDOWN
+			TDLS_REQUEST_TEARDOWN,
+			TDLS_REQUEST_DISCOVER,
 		} oper;
 		u16 reason_code; /* for teardown */
 	} tdls;
@@ -3830,15 +4255,6 @@
 	} timeout_event;
 
 	/**
-	 * struct ft_rrb_rx - Data for EVENT_FT_RRB_RX events
-	 */
-	struct ft_rrb_rx {
-		const u8 *src;
-		const u8 *data;
-		size_t data_len;
-	} ft_rrb_rx;
-
-	/**
 	 * struct tx_status - Data for EVENT_TX_STATUS events
 	 */
 	struct tx_status {
@@ -3922,17 +4338,6 @@
 	} scan_info;
 
 	/**
-	 * struct mlme_rx - Data for EVENT_MLME_RX events
-	 */
-	struct mlme_rx {
-		const u8 *buf;
-		size_t len;
-		int freq;
-		int channel;
-		int ssi;
-	} mlme_rx;
-
-	/**
 	 * struct rx_probe_req - Data for EVENT_RX_PROBE_REQ events
 	 */
 	struct rx_probe_req {
@@ -4112,7 +4517,7 @@
 	 * survey_results - Survey result data for EVENT_SURVEY
 	 * @freq_filter: Requested frequency survey filter, 0 if request
 	 *	was for all survey data
-	 * @survey_list: Linked list of survey data
+	 * @survey_list: Linked list of survey data (struct freq_survey)
 	 */
 	struct survey_results {
 		unsigned int freq_filter;
@@ -4137,6 +4542,41 @@
 	 * This is used as the data with EVENT_AVOID_FREQUENCIES.
 	 */
 	struct wpa_freq_range_list freq_range;
+
+	/**
+	 * struct mesh_peer
+	 *
+	 * @peer: Peer address
+	 * @ies: Beacon IEs
+	 * @ie_len: Length of @ies
+	 *
+	 * Notification of new candidate mesh peer.
+	 */
+	struct mesh_peer {
+		const u8 *peer;
+		const u8 *ies;
+		size_t ie_len;
+	} mesh_peer;
+
+	/**
+	 * struct acs_selected_channels - Data for EVENT_ACS_CHANNEL_SELECTED
+	 * @pri_channel: Selected primary channel
+	 * @sec_channel: Selected secondary channel
+	 * @vht_seg0_center_ch: VHT mode Segment0 center channel
+	 * @vht_seg1_center_ch: VHT mode Segment1 center channel
+	 * @ch_width: Selected Channel width by driver. Driver may choose to
+	 *	change hostapd configured ACS channel width due driver internal
+	 *	channel restrictions.
+	 * hw_mode: Selected band (used with hw_mode=any)
+	 */
+	struct acs_selected_channels {
+		u8 pri_channel;
+		u8 sec_channel;
+		u8 vht_seg0_center_ch;
+		u8 vht_seg1_center_ch;
+		u16 ch_width;
+		enum hostapd_hw_mode hw_mode;
+	} acs_selected_channels;
 };
 
 /**
@@ -4198,7 +4638,14 @@
 /* Convert chan_width to a string for logging and control interfaces */
 const char * channel_width_to_string(enum chan_width width);
 
+int ht_supported(const struct hostapd_hw_modes *mode);
+int vht_supported(const struct hostapd_hw_modes *mode);
+
+struct wowlan_triggers *
+wpa_get_wowlan_triggers(const char *wowlan_triggers,
+			const struct wpa_driver_capa *capa);
+
 /* NULL terminated array of linked in driver wrappers */
-extern struct wpa_driver_ops *wpa_drivers[];
+extern const struct wpa_driver_ops *const wpa_drivers[];
 
 #endif /* DRIVER_H */
diff --git a/src/drivers/driver_atheros.c b/src/drivers/driver_atheros.c
index b569a0a..b721c32 100644
--- a/src/drivers/driver_atheros.c
+++ b/src/drivers/driver_atheros.c
@@ -224,10 +224,10 @@
 	memcpy(iwr.u.name+sizeof(__u32), &arg, sizeof(arg));
 
 	if (ioctl(drv->ioctl_sock, IEEE80211_IOCTL_SETPARAM, &iwr) < 0) {
-		perror("ioctl[IEEE80211_IOCTL_SETPARAM]");
-		wpa_printf(MSG_DEBUG, "%s: %s: Failed to set parameter (op %d "
-			   "(%s) arg %d)", __func__, drv->iface, op,
-			   athr_get_param_name(op), arg);
+		wpa_printf(MSG_INFO,
+			   "%s: %s: Failed to set parameter (op %d (%s) arg %d): ioctl[IEEE80211_IOCTL_SETPARAM]: %s",
+			   __func__, drv->iface, op, athr_get_param_name(op),
+			   arg, strerror(errno));
 		return -1;
 	}
 	return 0;
@@ -290,14 +290,15 @@
 	}
 	wpa_printf(MSG_DEBUG, "%s: group key cipher=%d", __func__, v);
 	if (set80211param(drv, IEEE80211_PARAM_MCASTCIPHER, v)) {
-		printf("Unable to set group key cipher to %u\n", v);
+		wpa_printf(MSG_INFO, "Unable to set group key cipher to %u", v);
 		return -1;
 	}
 	if (v == IEEE80211_CIPHER_WEP) {
 		/* key length is done only for specific ciphers */
 		v = (params->wpa_group == WPA_CIPHER_WEP104 ? 13 : 5);
 		if (set80211param(drv, IEEE80211_PARAM_MCASTKEYLEN, v)) {
-			printf("Unable to set group key length to %u\n", v);
+			wpa_printf(MSG_INFO,
+				   "Unable to set group key length to %u", v);
 			return -1;
 		}
 	}
@@ -319,7 +320,8 @@
 		v |= 1<<IEEE80211_CIPHER_NONE;
 	wpa_printf(MSG_DEBUG, "%s: pairwise key ciphers=0x%x", __func__, v);
 	if (set80211param(drv, IEEE80211_PARAM_UCASTCIPHERS, v)) {
-		printf("Unable to set pairwise key ciphers to 0x%x\n", v);
+		wpa_printf(MSG_INFO,
+			   "Unable to set pairwise key ciphers to 0x%x", v);
 		return -1;
 	}
 
@@ -327,8 +329,9 @@
 		   __func__, params->wpa_key_mgmt);
 	if (set80211param(drv, IEEE80211_PARAM_KEYMGTALGS,
 			  params->wpa_key_mgmt)) {
-		printf("Unable to set key management algorithms to 0x%x\n",
-			params->wpa_key_mgmt);
+		wpa_printf(MSG_INFO,
+			   "Unable to set key management algorithms to 0x%x",
+			   params->wpa_key_mgmt);
 		return -1;
 	}
 
@@ -345,13 +348,14 @@
 
 	wpa_printf(MSG_DEBUG, "%s: rsn capabilities=0x%x", __func__, v);
 	if (set80211param(drv, IEEE80211_PARAM_RSNCAPS, v)) {
-		printf("Unable to set RSN capabilities to 0x%x\n", v);
+		wpa_printf(MSG_INFO, "Unable to set RSN capabilities to 0x%x",
+			   v);
 		return -1;
 	}
 
 	wpa_printf(MSG_DEBUG, "%s: enable WPA=0x%x", __func__, params->wpa);
 	if (set80211param(drv, IEEE80211_PARAM_WPA, params->wpa)) {
-		printf("Unable to set WPA to %u\n", params->wpa);
+		wpa_printf(MSG_INFO, "Unable to set WPA to %u", params->wpa);
 		return -1;
 	}
 	return 0;
@@ -518,14 +522,14 @@
 #endif /* ATH_GCM_SUPPORT */
 #endif /* CONFIG_IEEE80211W */
 	default:
-		printf("%s: unknown/unsupported algorithm %d\n",
-			__func__, alg);
+		wpa_printf(MSG_INFO, "%s: unknown/unsupported algorithm %d",
+			   __func__, alg);
 		return -1;
 	}
 
 	if (key_len > sizeof(wk.ik_keydata)) {
-		printf("%s: key length %lu too big\n", __func__,
-		       (unsigned long) key_len);
+		wpa_printf(MSG_INFO, "%s: key length %lu too big", __func__,
+			   (unsigned long) key_len);
 		return -3;
 	}
 
@@ -636,7 +640,8 @@
 			return 0;
 		}
 
-		printf("Failed to get station stats information element.\n");
+		wpa_printf(MSG_INFO,
+			   "Failed to get station stats information element");
 		return -1;
 	}
 
@@ -769,145 +774,6 @@
 	return ret;
 }
 
-#ifdef CONFIG_WPS
-static void atheros_raw_recv_wps(void *ctx, const u8 *src_addr, const u8 *buf,
-				 size_t len)
-{
-	struct atheros_driver_data *drv = ctx;
-	const struct ieee80211_mgmt *mgmt;
-	u16 fc;
-	union wpa_event_data event;
-
-	/* Send Probe Request information to WPS processing */
-
-	if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.probe_req))
-		return;
-	mgmt = (const struct ieee80211_mgmt *) buf;
-
-	fc = le_to_host16(mgmt->frame_control);
-	if (WLAN_FC_GET_TYPE(fc) != WLAN_FC_TYPE_MGMT ||
-	    WLAN_FC_GET_STYPE(fc) != WLAN_FC_STYPE_PROBE_REQ)
-		return;
-
-	os_memset(&event, 0, sizeof(event));
-	event.rx_probe_req.sa = mgmt->sa;
-	event.rx_probe_req.da = mgmt->da;
-	event.rx_probe_req.bssid = mgmt->bssid;
-	event.rx_probe_req.ie = mgmt->u.probe_req.variable;
-	event.rx_probe_req.ie_len =
-		len - (IEEE80211_HDRLEN + sizeof(mgmt->u.probe_req));
-	wpa_supplicant_event(drv->hapd, EVENT_RX_PROBE_REQ, &event);
-}
-#endif /* CONFIG_WPS */
-
-#ifdef CONFIG_IEEE80211R
-static void atheros_raw_recv_11r(void *ctx, const u8 *src_addr, const u8 *buf,
-				 size_t len)
-{
-	struct atheros_driver_data *drv = ctx;
-	union wpa_event_data event;
-	const struct ieee80211_mgmt *mgmt;
-	u16 fc;
-	u16 stype;
-	int ielen;
-	const u8 *iebuf;
-
-	/* Do 11R processing for ASSOC/AUTH/FT ACTION frames */
-	if (len < IEEE80211_HDRLEN)
-		return;
-	mgmt = (const struct ieee80211_mgmt *) buf;
-
-	fc = le_to_host16(mgmt->frame_control);
-
-	if (WLAN_FC_GET_TYPE(fc) != WLAN_FC_TYPE_MGMT)
-		return;
-	stype = WLAN_FC_GET_STYPE(fc);
-
-	wpa_printf(MSG_DEBUG, "%s: subtype 0x%x len %d", __func__, stype,
-		   (int) len);
-
-	if (os_memcmp(drv->own_addr, mgmt->bssid, ETH_ALEN) != 0) {
-		wpa_printf(MSG_DEBUG, "%s: BSSID does not match - ignore",
-			   __func__);
-		return;
-	}
-	switch (stype) {
-	case WLAN_FC_STYPE_ASSOC_REQ:
-		if (len - IEEE80211_HDRLEN < sizeof(mgmt->u.assoc_req))
-			break;
-		ielen = len - (IEEE80211_HDRLEN + sizeof(mgmt->u.assoc_req));
-		iebuf = mgmt->u.assoc_req.variable;
-		drv_event_assoc(drv->hapd, mgmt->sa, iebuf, ielen, 0);
-		break;
-	case WLAN_FC_STYPE_REASSOC_REQ:
-		if (len - IEEE80211_HDRLEN < sizeof(mgmt->u.reassoc_req))
-			break;
-		ielen = len - (IEEE80211_HDRLEN + sizeof(mgmt->u.reassoc_req));
-		iebuf = mgmt->u.reassoc_req.variable;
-		drv_event_assoc(drv->hapd, mgmt->sa, iebuf, ielen, 1);
-		break;
-	case WLAN_FC_STYPE_ACTION:
-		os_memset(&event, 0, sizeof(event));
-		event.rx_mgmt.frame = buf;
-		event.rx_mgmt.frame_len = len;
-		wpa_supplicant_event(drv->hapd, EVENT_RX_MGMT, &event);
-		break;
-	case WLAN_FC_STYPE_AUTH:
-		if (len - IEEE80211_HDRLEN < sizeof(mgmt->u.auth))
-			break;
-		os_memset(&event, 0, sizeof(event));
-		os_memcpy(event.auth.peer, mgmt->sa, ETH_ALEN);
-		os_memcpy(event.auth.bssid, mgmt->bssid, ETH_ALEN);
-		event.auth.auth_type = le_to_host16(mgmt->u.auth.auth_alg);
-		event.auth.status_code =
-			le_to_host16(mgmt->u.auth.status_code);
-		event.auth.auth_transaction =
-			le_to_host16(mgmt->u.auth.auth_transaction);
-		event.auth.ies = mgmt->u.auth.variable;
-		event.auth.ies_len = len - IEEE80211_HDRLEN -
-			sizeof(mgmt->u.auth);
-		wpa_supplicant_event(drv->hapd, EVENT_AUTH, &event);
-		break;
-	default:
-		break;
-	}
-}
-#endif /* CONFIG_IEEE80211R */
-
-#ifdef CONFIG_HS20
-static void atheros_raw_recv_hs20(void *ctx, const u8 *src_addr, const u8 *buf,
-				 size_t len)
-{
-	struct atheros_driver_data *drv = ctx;
-	const struct ieee80211_mgmt *mgmt;
-	u16 fc;
-	union wpa_event_data event;
-
-	/* Send the Action frame for HS20 processing */
-
-	if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.action.category) +
-	    sizeof(mgmt->u.action.u.public_action))
-		return;
-
-	mgmt = (const struct ieee80211_mgmt *) buf;
-
-	fc = le_to_host16(mgmt->frame_control);
-	if (WLAN_FC_GET_TYPE(fc) != WLAN_FC_TYPE_MGMT ||
-	    WLAN_FC_GET_STYPE(fc) != WLAN_FC_STYPE_ACTION ||
-	    mgmt->u.action.category != WLAN_ACTION_PUBLIC)
-		return;
-
-	wpa_printf(MSG_DEBUG, "%s:Received Public Action frame", __func__);
-
-	os_memset(&event, 0, sizeof(event));
-	event.rx_mgmt.frame = (const u8 *) mgmt;
-	event.rx_mgmt.frame_len = len;
-	wpa_supplicant_event(drv->hapd, EVENT_RX_MGMT, &event);
-}
-
-#endif /* CONFIG_HS20 */
-
-
 static int atheros_set_qos_map(void *ctx, const u8 *qos_map_set,
 			       u8 qos_map_set_len)
 {
@@ -947,9 +813,9 @@
 	}
 
 	if (ioctl(drv->ioctl_sock, IEEE80211_IOCTL_DBGREQ, &iwr) < 0) {
-		perror("ioctl[IEEE80211_IOCTL_DBGREQ]");
-		wpa_printf(MSG_DEBUG, "%s: %s: Failed to set QoS Map",
-			   __func__, drv->iface);
+		wpa_printf(MSG_ERROR,
+			   "%s: %s: Failed to set QoS Map: ioctl[IEEE80211_IOCTL_DBGREQ]: %s",
+			   __func__, drv->iface, strerror(errno));
 		return -1;
 	}
 #endif /* CONFIG_ATHEROS_QOS_MAP */
@@ -957,30 +823,47 @@
 	return 0;
 }
 
-#if defined(CONFIG_WNM) && !defined(CONFIG_IEEE80211R)
-static void atheros_raw_recv_11v(void *ctx, const u8 *src_addr, const u8 *buf,
-				 size_t len)
+#if defined(CONFIG_WPS) || defined(CONFIG_IEEE80211R) || defined(CONFIG_WNM) || defined(CONFIG_HS20)
+static void atheros_raw_receive(void *ctx, const u8 *src_addr, const u8 *buf,
+				size_t len)
 {
 	struct atheros_driver_data *drv = ctx;
-	union wpa_event_data event;
 	const struct ieee80211_mgmt *mgmt;
-	u16 fc;
-	u16 stype;
+	union wpa_event_data event;
+	u16 fc, stype;
+	int ielen;
+	const u8 *iebuf;
 
-	/* Do 11R processing for WNM ACTION frames */
 	if (len < IEEE80211_HDRLEN)
 		return;
+
 	mgmt = (const struct ieee80211_mgmt *) buf;
 
 	fc = le_to_host16(mgmt->frame_control);
 
 	if (WLAN_FC_GET_TYPE(fc) != WLAN_FC_TYPE_MGMT)
 		return;
+
 	stype = WLAN_FC_GET_STYPE(fc);
 
 	wpa_printf(MSG_DEBUG, "%s: subtype 0x%x len %d", __func__, stype,
 		   (int) len);
 
+	if (stype == WLAN_FC_STYPE_PROBE_REQ) {
+		if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.probe_req))
+			return;
+
+		os_memset(&event, 0, sizeof(event));
+		event.rx_probe_req.sa = mgmt->sa;
+		event.rx_probe_req.da = mgmt->da;
+		event.rx_probe_req.bssid = mgmt->bssid;
+		event.rx_probe_req.ie = mgmt->u.probe_req.variable;
+		event.rx_probe_req.ie_len =
+			len - (IEEE80211_HDRLEN + sizeof(mgmt->u.probe_req));
+		wpa_supplicant_event(drv->hapd, EVENT_RX_PROBE_REQ, &event);
+		return;
+	}
+
 	if (os_memcmp(drv->own_addr, mgmt->bssid, ETH_ALEN) != 0) {
 		wpa_printf(MSG_DEBUG, "%s: BSSID does not match - ignore",
 			   __func__);
@@ -988,36 +871,47 @@
 	}
 
 	switch (stype) {
+	case WLAN_FC_STYPE_ASSOC_REQ:
+		if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.assoc_req))
+			break;
+		ielen = len - (IEEE80211_HDRLEN + sizeof(mgmt->u.assoc_req));
+		iebuf = mgmt->u.assoc_req.variable;
+		drv_event_assoc(drv->hapd, mgmt->sa, iebuf, ielen, 0);
+		break;
+	case WLAN_FC_STYPE_REASSOC_REQ:
+		if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.reassoc_req))
+			break;
+		ielen = len - (IEEE80211_HDRLEN + sizeof(mgmt->u.reassoc_req));
+		iebuf = mgmt->u.reassoc_req.variable;
+		drv_event_assoc(drv->hapd, mgmt->sa, iebuf, ielen, 1);
+		break;
 	case WLAN_FC_STYPE_ACTION:
 		os_memset(&event, 0, sizeof(event));
 		event.rx_mgmt.frame = buf;
 		event.rx_mgmt.frame_len = len;
 		wpa_supplicant_event(drv->hapd, EVENT_RX_MGMT, &event);
 		break;
+	case WLAN_FC_STYPE_AUTH:
+		if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.auth))
+			break;
+		os_memset(&event, 0, sizeof(event));
+		os_memcpy(event.auth.peer, mgmt->sa, ETH_ALEN);
+		os_memcpy(event.auth.bssid, mgmt->bssid, ETH_ALEN);
+		event.auth.auth_type = le_to_host16(mgmt->u.auth.auth_alg);
+		event.auth.status_code =
+			le_to_host16(mgmt->u.auth.status_code);
+		event.auth.auth_transaction =
+			le_to_host16(mgmt->u.auth.auth_transaction);
+		event.auth.ies = mgmt->u.auth.variable;
+		event.auth.ies_len = len - IEEE80211_HDRLEN -
+			sizeof(mgmt->u.auth);
+		wpa_supplicant_event(drv->hapd, EVENT_AUTH, &event);
+		break;
 	default:
 		break;
 	}
 }
-#endif /* CONFIG_WNM */
-
-#if defined(CONFIG_WPS) || defined(CONFIG_IEEE80211R) || defined(CONFIG_WNM)
-static void atheros_raw_receive(void *ctx, const u8 *src_addr, const u8 *buf,
-				size_t len)
-{
-#ifdef CONFIG_WPS
-	atheros_raw_recv_wps(ctx, src_addr, buf, len);
-#endif /* CONFIG_WPS */
-#ifdef CONFIG_IEEE80211R
-	atheros_raw_recv_11r(ctx, src_addr, buf, len);
-#endif /* CONFIG_IEEE80211R */
-#if defined(CONFIG_WNM) && !defined(CONFIG_IEEE80211R)
-	atheros_raw_recv_11v(ctx, src_addr, buf, len);
-#endif /* CONFIG_WNM */
-#ifdef CONFIG_HS20
-	atheros_raw_recv_hs20(ctx, src_addr, buf, len);
-#endif /* CONFIG_HS20 */
-}
-#endif /* CONFIG_WPS || CONFIG_IEEE80211R */
+#endif
 
 static int atheros_receive_pkt(struct atheros_driver_data *drv)
 {
@@ -1606,8 +1500,9 @@
 		sizeof(range->enc_capa);
 
 	if (ioctl(drv->ioctl_sock, SIOCGIWRANGE, &iwr) < 0) {
-		perror("ioctl[SIOCGIWRANGE]");
-		free(range);
+		wpa_printf(MSG_ERROR, "ioctl[SIOCGIWRANGE]: %s",
+			   strerror(errno));
+		os_free(range);
 		return -1;
 	} else if (iwr.u.data.length >= minlen &&
 		   range->we_version_compiled >= 18) {
@@ -1667,8 +1562,9 @@
 	if (len > sizeof(buf)) {
 		bp = malloc(len);
 		if (bp == NULL) {
-			printf("EAPOL frame discarded, cannot malloc temp "
-			       "buffer of size %lu!\n", (unsigned long) len);
+			wpa_printf(MSG_INFO,
+				   "EAPOL frame discarded, cannot malloc temp buffer of size %lu!",
+				   (unsigned long) len);
 			return -1;
 		}
 	}
@@ -1705,14 +1601,16 @@
 
 	drv = os_zalloc(sizeof(struct atheros_driver_data));
 	if (drv == NULL) {
-		printf("Could not allocate memory for atheros driver data\n");
+		wpa_printf(MSG_INFO,
+			   "Could not allocate memory for atheros driver data");
 		return NULL;
 	}
 
 	drv->hapd = hapd;
 	drv->ioctl_sock = socket(PF_INET, SOCK_DGRAM, 0);
 	if (drv->ioctl_sock < 0) {
-		perror("socket[PF_INET,SOCK_DGRAM]");
+		wpa_printf(MSG_ERROR, "socket[PF_INET,SOCK_DGRAM]: %s",
+			   strerror(errno));
 		goto bad;
 	}
 	memcpy(drv->iface, params->ifname, sizeof(drv->iface));
@@ -1720,7 +1618,8 @@
 	memset(&ifr, 0, sizeof(ifr));
 	os_strlcpy(ifr.ifr_name, drv->iface, sizeof(ifr.ifr_name));
 	if (ioctl(drv->ioctl_sock, SIOCGIFINDEX, &ifr) != 0) {
-		perror("ioctl(SIOCGIFINDEX)");
+		wpa_printf(MSG_ERROR, "ioctl(SIOCGIFINDEX): %s",
+			   strerror(errno));
 		goto bad;
 	}
 	drv->ifindex = ifr.ifr_ifindex;
@@ -1756,8 +1655,9 @@
 	iwr.u.mode = IW_MODE_MASTER;
 
 	if (ioctl(drv->ioctl_sock, SIOCSIWMODE, &iwr) < 0) {
-		perror("ioctl[SIOCSIWMODE]");
-		printf("Could not set interface to master mode!\n");
+		wpa_printf(MSG_ERROR,
+			   "Could not set interface to master mode! ioctl[SIOCSIWMODE]: %s",
+			   strerror(errno));
 		goto bad;
 	}
 
@@ -1782,8 +1682,7 @@
 		l2_packet_deinit(drv->sock_xmit);
 	if (drv->ioctl_sock >= 0)
 		close(drv->ioctl_sock);
-	if (drv != NULL)
-		free(drv);
+	os_free(drv);
 	return NULL;
 }
 
@@ -1794,6 +1693,13 @@
 	struct atheros_driver_data *drv = priv;
 
 	atheros_reset_appfilter(drv);
+
+	if (drv->wpa_ie || drv->wps_beacon_ie || drv->wps_probe_resp_ie) {
+		wpabuf_free(drv->wpa_ie);
+		wpabuf_free(drv->wps_beacon_ie);
+		wpabuf_free(drv->wps_probe_resp_ie);
+		atheros_set_opt_ie(priv, NULL, 0);
+	}
 	netlink_deinit(drv->netlink);
 	(void) linux_set_iface_flags(drv->ioctl_sock, drv->iface, 0);
 	if (drv->ioctl_sock >= 0)
@@ -1804,10 +1710,7 @@
 		l2_packet_deinit(drv->sock_xmit);
 	if (drv->sock_raw)
 		l2_packet_deinit(drv->sock_raw);
-	wpabuf_free(drv->wpa_ie);
-	wpabuf_free(drv->wps_beacon_ie);
-	wpabuf_free(drv->wps_probe_resp_ie);
-	free(drv);
+	os_free(drv);
 }
 
 static int
@@ -1823,8 +1726,8 @@
 	iwr.u.essid.length = len + 1;
 
 	if (ioctl(drv->ioctl_sock, SIOCSIWESSID, &iwr) < 0) {
-		perror("ioctl[SIOCSIWESSID]");
-		printf("len=%d\n", len);
+		wpa_printf(MSG_ERROR, "ioctl[SIOCSIWESSID,len=%d]: %s",
+			   len, strerror(errno));
 		return -1;
 	}
 	return 0;
@@ -1844,7 +1747,8 @@
 		IW_ESSID_MAX_SIZE : len;
 
 	if (ioctl(drv->ioctl_sock, SIOCGIWESSID, &iwr) < 0) {
-		perror("ioctl[SIOCGIWESSID]");
+		wpa_printf(MSG_ERROR, "ioctl[SIOCGIWESSID]: %s",
+			   strerror(errno));
 		ret = -1;
 	} else
 		ret = iwr.u.essid.length;
@@ -1909,7 +1813,7 @@
 	wpa_hexdump_buf(MSG_DEBUG, "atheros: assocresp_ies",
 			params->assocresp_ies);
 
-#if defined(CONFIG_HS20) && defined(IEEE80211_PARAM_OSEN)
+#if defined(CONFIG_HS20) && (defined(IEEE80211_PARAM_OSEN) || defined(CONFIG_ATHEROS_OSEN))
 	if (params->osen) {
 		struct wpa_bss_params bss_params;
 
@@ -1935,7 +1839,7 @@
 #ifdef CONFIG_IEEE80211R
 
 static int atheros_send_mgmt(void *priv, const u8 *frm, size_t data_len,
-			     int noack)
+			     int noack, unsigned int freq)
 {
 	struct atheros_driver_data *drv = priv;
 	u8 buf[1510];
diff --git a/src/drivers/driver_bsd.c b/src/drivers/driver_bsd.c
index ca64d5c..9ac87b4 100644
--- a/src/drivers/driver_bsd.c
+++ b/src/drivers/driver_bsd.c
@@ -264,7 +264,8 @@
 	os_strlcpy(ifr.ifr_name, drv->ifname, sizeof(ifr.ifr_name));
 
 	if (ioctl(drv->sock, SIOCGIFFLAGS, &ifr) < 0) {
-		perror("ioctl[SIOCGIFFLAGS]");
+		wpa_printf(MSG_ERROR, "ioctl[SIOCGIFFLAGS]: %s",
+			   strerror(errno));
 		return -1;
 	}
 
@@ -279,7 +280,8 @@
 	}
 
 	if (ioctl(drv->sock, SIOCSIFFLAGS, &ifr) < 0) {
-		perror("ioctl[SIOCSIFFLAGS]");
+		wpa_printf(MSG_ERROR, "ioctl[SIOCSIFFLAGS]: %s",
+			   strerror(errno));
 		return -1;
 	}
 
@@ -404,22 +406,24 @@
 		v = IEEE80211_CIPHER_NONE;
 		break;
 	default:
-		printf("Unknown group key cipher %u\n",
-			params->wpa_group);
+		wpa_printf(MSG_INFO, "Unknown group key cipher %u",
+			   params->wpa_group);
 		return -1;
 	}
 	wpa_printf(MSG_DEBUG, "%s: group key cipher=%s (%u)",
 		   __func__, ciphernames[v], v);
 	if (set80211param(priv, IEEE80211_IOC_MCASTCIPHER, v)) {
-		printf("Unable to set group key cipher to %u (%s)\n",
-			v, ciphernames[v]);
+		wpa_printf(MSG_INFO,
+			   "Unable to set group key cipher to %u (%s)",
+			   v, ciphernames[v]);
 		return -1;
 	}
 	if (v == IEEE80211_CIPHER_WEP) {
 		/* key length is done only for specific ciphers */
 		v = (params->wpa_group == WPA_CIPHER_WEP104 ? 13 : 5);
 		if (set80211param(priv, IEEE80211_IOC_MCASTKEYLEN, v)) {
-			printf("Unable to set group key length to %u\n", v);
+			wpa_printf(MSG_INFO,
+				   "Unable to set group key length to %u", v);
 			return -1;
 		}
 	}
@@ -433,7 +437,8 @@
 		v |= 1<<IEEE80211_CIPHER_NONE;
 	wpa_printf(MSG_DEBUG, "%s: pairwise key ciphers=0x%x", __func__, v);
 	if (set80211param(priv, IEEE80211_IOC_UCASTCIPHERS, v)) {
-		printf("Unable to set pairwise key ciphers to 0x%x\n", v);
+		wpa_printf(MSG_INFO,
+			   "Unable to set pairwise key ciphers to 0x%x", v);
 		return -1;
 	}
 
@@ -441,8 +446,9 @@
 		   __func__, params->wpa_key_mgmt);
 	if (set80211param(priv, IEEE80211_IOC_KEYMGTALGS,
 			  params->wpa_key_mgmt)) {
-		printf("Unable to set key management algorithms to 0x%x\n",
-			params->wpa_key_mgmt);
+		wpa_printf(MSG_INFO,
+			   "Unable to set key management algorithms to 0x%x",
+			   params->wpa_key_mgmt);
 		return -1;
 	}
 
@@ -452,14 +458,15 @@
 	wpa_printf(MSG_DEBUG, "%s: rsn capabilities=0x%x",
 		   __func__, params->rsn_preauth);
 	if (set80211param(priv, IEEE80211_IOC_RSNCAPS, v)) {
-		printf("Unable to set RSN capabilities to 0x%x\n", v);
+		wpa_printf(MSG_INFO, "Unable to set RSN capabilities to 0x%x",
+			   v);
 		return -1;
 	}
 #endif /* IEEE80211_IOC_APPIE */
 
 	wpa_printf(MSG_DEBUG, "%s: enable WPA= 0x%x", __func__, params->wpa);
 	if (set80211param(priv, IEEE80211_IOC_WPA, params->wpa)) {
-		printf("Unable to set WPA to %u\n", params->wpa);
+		wpa_printf(MSG_INFO, "Unable to set WPA to %u", params->wpa);
 		return -1;
 	}
 	return 0;
@@ -507,7 +514,8 @@
 	memset(&ie, 0, sizeof(ie));
 	memcpy(ie.wpa_macaddr, addr, IEEE80211_ADDR_LEN);
 	if (get80211var(priv, IEEE80211_IOC_WPAIE, &ie, sizeof(ie)) < 0) {
-		printf("Failed to get WPA/RSN information element.\n");
+		wpa_printf(MSG_INFO,
+			   "Failed to get WPA/RSN information element");
 		goto no_ie;
 	}
 	iebuf = ie.wpa_ie;
@@ -594,7 +602,7 @@
 	int mib[6] = {CTL_NET, AF_ROUTE, 0, AF_INET, NET_RT_DUMP, 0};
 
 	if (sysctl(mib, 6, NULL, &len, NULL, 0) < 0) {
-		wpa_printf(MSG_WARNING, "%s failed: %s\n", __func__,
+		wpa_printf(MSG_WARNING, "%s failed: %s", __func__,
 			   strerror(errno));
 		len = 2048;
 	}
@@ -652,7 +660,7 @@
 	wk.ik_keyix = idx;
 
 	if (get80211var(priv, IEEE80211_IOC_WPAKEY, &wk, sizeof(wk)) < 0) {
-		printf("Failed to get encryption.\n");
+		wpa_printf(MSG_INFO, "Failed to get encryption");
 		return -1;
 	}
 
@@ -734,7 +742,7 @@
 	n = read(sock, drv->event_buf, drv->event_buf_len);
 	if (n < 0) {
 		if (errno != EINTR && errno != EAGAIN)
-			wpa_printf(MSG_ERROR, "%s read() failed: %s\n",
+			wpa_printf(MSG_ERROR, "%s read() failed: %s",
 				   __func__, strerror(errno));
 		return;
 	}
@@ -814,7 +822,8 @@
 	drv->hapd = hapd;
 	drv->sock = socket(PF_INET, SOCK_DGRAM, 0);
 	if (drv->sock < 0) {
-		perror("socket[PF_INET,SOCK_DGRAM]");
+		wpa_printf(MSG_ERROR, "socket[PF_INET,SOCK_DGRAM]: %s",
+			   strerror(errno));
 		goto bad;
 	}
 	os_strlcpy(drv->ifname, params->ifname, sizeof(drv->ifname));
@@ -832,7 +841,8 @@
 
 	drv->route = socket(PF_ROUTE, SOCK_RAW, 0);
 	if (drv->route < 0) {
-		perror("socket(PF_ROUTE,SOCK_RAW)");
+		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,
@@ -851,8 +861,7 @@
 	if (drv->sock >= 0)
 		close(drv->sock);
 	os_free(drv->event_buf);
-	if (drv != NULL)
-		os_free(drv);
+	os_free(drv);
 	return NULL;
 }
 
@@ -1189,7 +1198,7 @@
 	n = read(sock, drv->event_buf, drv->event_buf_len);
 	if (n < 0) {
 		if (errno != EINTR && errno != EAGAIN)
-			wpa_printf(MSG_ERROR, "%s read() failed: %s\n",
+			wpa_printf(MSG_ERROR, "%s read() failed: %s",
 				   __func__, strerror(errno));
 		return;
 	}
@@ -1334,7 +1343,12 @@
 	*pos++ = 1;
 	*pos++ = sr->isr_erp;
 
+#if defined(__DragonFly__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
+	os_memcpy(pos, (u8 *)(sr + 1) + sr->isr_ssid_len + sr->isr_meshid_len,
+		  sr->isr_ie_len);
+#else
 	os_memcpy(pos, (u8 *)(sr + 1) + sr->isr_ssid_len, sr->isr_ie_len);
+#endif
 	pos += sr->isr_ie_len;
 
 	result->ie_len = pos - (u8 *)(result + 1);
diff --git a/src/drivers/driver_common.c b/src/drivers/driver_common.c
index 77e6905..aebea8c 100644
--- a/src/drivers/driver_common.c
+++ b/src/drivers/driver_common.c
@@ -44,14 +44,12 @@
 	E2S(ASSOC_REJECT);
 	E2S(AUTH_TIMED_OUT);
 	E2S(ASSOC_TIMED_OUT);
-	E2S(FT_RRB_RX);
 	E2S(WPS_BUTTON_PUSHED);
 	E2S(TX_STATUS);
 	E2S(RX_FROM_UNKNOWN);
 	E2S(RX_MGMT);
 	E2S(REMAIN_ON_CHANNEL);
 	E2S(CANCEL_REMAIN_ON_CHANNEL);
-	E2S(MLME_RX);
 	E2S(RX_PROBE_REQ);
 	E2S(NEW_STA);
 	E2S(EAPOL_RX);
@@ -79,6 +77,9 @@
 	E2S(SURVEY);
 	E2S(SCAN_STARTED);
 	E2S(AVOID_FREQUENCIES);
+	E2S(NEW_PEER_CANDIDATE);
+	E2S(ACS_CHANNEL_SELECTED);
+	E2S(DFS_CAC_STARTED);
 	}
 
 	return "UNKNOWN";
@@ -105,3 +106,115 @@
 		return "unknown";
 	}
 }
+
+
+int ht_supported(const struct hostapd_hw_modes *mode)
+{
+	if (!(mode->flags & HOSTAPD_MODE_FLAG_HT_INFO_KNOWN)) {
+		/*
+		 * The driver did not indicate whether it supports HT. Assume
+		 * it does to avoid connection issues.
+		 */
+		return 1;
+	}
+
+	/*
+	 * IEEE Std 802.11n-2009 20.1.1:
+	 * An HT non-AP STA shall support all EQM rates for one spatial stream.
+	 */
+	return mode->mcs_set[0] == 0xff;
+}
+
+
+int vht_supported(const struct hostapd_hw_modes *mode)
+{
+	if (!(mode->flags & HOSTAPD_MODE_FLAG_VHT_INFO_KNOWN)) {
+		/*
+		 * The driver did not indicate whether it supports VHT. Assume
+		 * it does to avoid connection issues.
+		 */
+		return 1;
+	}
+
+	/*
+	 * A VHT non-AP STA shall support MCS 0-7 for one spatial stream.
+	 * TODO: Verify if this complies with the standard
+	 */
+	return (mode->vht_mcs_set[0] & 0x3) != 3;
+}
+
+
+static int wpa_check_wowlan_trigger(const char *start, const char *trigger,
+				    int capa_trigger, u8 *param_trigger)
+{
+	if (os_strcmp(start, trigger) != 0)
+		return 0;
+	if (!capa_trigger)
+		return 0;
+
+	*param_trigger = 1;
+	return 1;
+}
+
+
+struct wowlan_triggers *
+wpa_get_wowlan_triggers(const char *wowlan_triggers,
+			const struct wpa_driver_capa *capa)
+{
+	struct wowlan_triggers *triggers;
+	char *start, *end, *buf;
+	int last;
+
+	if (!wowlan_triggers)
+		return NULL;
+
+	buf = os_strdup(wowlan_triggers);
+	if (buf == NULL)
+		return NULL;
+
+	triggers = os_zalloc(sizeof(*triggers));
+	if (triggers == NULL)
+		goto out;
+
+#define CHECK_TRIGGER(trigger) \
+	wpa_check_wowlan_trigger(start, #trigger,			\
+				  capa->wowlan_triggers.trigger,	\
+				  &triggers->trigger)
+
+	start = buf;
+	while (*start != '\0') {
+		while (isblank(*start))
+			start++;
+		if (*start == '\0')
+			break;
+		end = start;
+		while (!isblank(*end) && *end != '\0')
+			end++;
+		last = *end == '\0';
+		*end = '\0';
+
+		if (!CHECK_TRIGGER(any) &&
+		    !CHECK_TRIGGER(disconnect) &&
+		    !CHECK_TRIGGER(magic_pkt) &&
+		    !CHECK_TRIGGER(gtk_rekey_failure) &&
+		    !CHECK_TRIGGER(eap_identity_req) &&
+		    !CHECK_TRIGGER(four_way_handshake) &&
+		    !CHECK_TRIGGER(rfkill_release)) {
+			wpa_printf(MSG_DEBUG,
+				   "Unknown/unsupported wowlan trigger '%s'",
+				   start);
+			os_free(triggers);
+			triggers = NULL;
+			goto out;
+		}
+
+		if (last)
+			break;
+		start = end + 1;
+	}
+#undef CHECK_TRIGGER
+
+out:
+	os_free(buf);
+	return triggers;
+}
diff --git a/src/drivers/driver_hostap.c b/src/drivers/driver_hostap.c
index 16f5563..8835005 100644
--- a/src/drivers/driver_hostap.c
+++ b/src/drivers/driver_hostap.c
@@ -214,7 +214,7 @@
 
 	len = recv(sock, buf, sizeof(buf), 0);
 	if (len < 0) {
-		perror("recv");
+		wpa_printf(MSG_ERROR, "recv: %s", strerror(errno));
 		return;
 	}
 
@@ -229,19 +229,21 @@
 
 	drv->sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
 	if (drv->sock < 0) {
-		perror("socket[PF_PACKET,SOCK_RAW]");
+		wpa_printf(MSG_ERROR, "socket[PF_PACKET,SOCK_RAW]: %s",
+			   strerror(errno));
 		return -1;
 	}
 
 	if (eloop_register_read_sock(drv->sock, handle_read, drv, NULL)) {
-		printf("Could not register read socket\n");
+		wpa_printf(MSG_ERROR, "Could not register read socket");
 		return -1;
 	}
 
         memset(&ifr, 0, sizeof(ifr));
         snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "%sap", drv->iface);
         if (ioctl(drv->sock, SIOCGIFINDEX, &ifr) != 0) {
-		perror("ioctl(SIOCGIFINDEX)");
+		wpa_printf(MSG_ERROR, "ioctl(SIOCGIFINDEX): %s",
+			   strerror(errno));
 		return -1;
         }
 
@@ -256,7 +258,7 @@
 		   addr.sll_ifindex);
 
 	if (bind(drv->sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
-		perror("bind");
+		wpa_printf(MSG_ERROR, "bind: %s", strerror(errno));
 		return -1;
 	}
 
@@ -264,7 +266,8 @@
 }
 
 
-static int hostap_send_mlme(void *priv, const u8 *msg, size_t len, int noack)
+static int hostap_send_mlme(void *priv, const u8 *msg, size_t len, int noack,
+			    unsigned int freq)
 {
 	struct hostap_driver_data *drv = priv;
 	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) msg;
@@ -319,7 +322,7 @@
 			   "failed: %d (%s)",
 			   (unsigned long) len, errno, strerror(errno));
 	}
-	free(hdr);
+	os_free(hdr);
 
 	return res;
 }
@@ -361,9 +364,9 @@
 		os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ);
 		ifr.ifr_mtu = HOSTAPD_MTU;
 		if (ioctl(drv->ioctl_sock, SIOCSIFMTU, &ifr) != 0) {
-			perror("ioctl[SIOCSIFMTU]");
-			printf("Setting MTU failed - trying to survive with "
-			       "current value\n");
+			wpa_printf(MSG_INFO,
+				   "Setting MTU failed - trying to survive with current value: ioctl[SIOCSIFMTU]: %s",
+				   strerror(errno));
 		}
 	}
 
@@ -383,7 +386,8 @@
 	iwr.u.data.length = len;
 
 	if (ioctl(drv->ioctl_sock, PRISM2_IOCTL_HOSTAPD, &iwr) < 0) {
-		perror("ioctl[PRISM2_IOCTL_HOSTAPD]");
+		wpa_printf(MSG_ERROR, "ioctl[PRISM2_IOCTL_HOSTAPD]: %s",
+			   strerror(errno));
 		return -1;
 	}
 
@@ -467,18 +471,18 @@
 	param = (struct prism2_hostapd_param *) buf;
 	param->cmd = PRISM2_GET_ENCRYPTION;
 	if (addr == NULL)
-		memset(param->sta_addr, 0xff, ETH_ALEN);
+		os_memset(param->sta_addr, 0xff, ETH_ALEN);
 	else
-		memcpy(param->sta_addr, addr, ETH_ALEN);
+		os_memcpy(param->sta_addr, addr, ETH_ALEN);
 	param->u.crypt.idx = idx;
 
 	if (hostapd_ioctl(drv, param, blen)) {
 		printf("Failed to get encryption.\n");
 		ret = -1;
 	} else {
-		memcpy(seq, param->u.crypt.seq, 8);
+		os_memcpy(seq, param->u.crypt.seq, 8);
 	}
-	free(buf);
+	os_free(buf);
 
 	return ret;
 }
@@ -497,7 +501,8 @@
 	*i++ = value;
 
 	if (ioctl(drv->ioctl_sock, PRISM2_IOCTL_PRISM2_PARAM, &iwr) < 0) {
-		perror("ioctl[PRISM2_IOCTL_PRISM2_PARAM]");
+		wpa_printf(MSG_ERROR, "ioctl[PRISM2_IOCTL_PRISM2_PARAM]: %s",
+			   strerror(errno));
 		return -1;
 	}
 
@@ -554,8 +559,8 @@
 	iwr.u.essid.length = len + 1;
 
 	if (ioctl(drv->ioctl_sock, SIOCSIWESSID, &iwr) < 0) {
-		perror("ioctl[SIOCSIWESSID]");
-		printf("len=%d\n", len);
+		wpa_printf(MSG_ERROR, "ioctl[SIOCSIWESSID,len=%d]: %s",
+			   len, strerror(errno));
 		return -1;
 	}
 
@@ -919,8 +924,9 @@
 		sizeof(range->enc_capa);
 
 	if (ioctl(drv->ioctl_sock, SIOCGIWRANGE, &iwr) < 0) {
-		perror("ioctl[SIOCGIWRANGE]");
-		free(range);
+		wpa_printf(MSG_ERROR, "ioctl[SIOCGIWRANGE]: %s",
+			   strerror(errno));
+		os_free(range);
 		return -1;
 	} else if (iwr.u.data.length >= minlen &&
 		   range->we_version_compiled >= 18) {
@@ -975,23 +981,25 @@
 
 	drv->ioctl_sock = socket(PF_INET, SOCK_DGRAM, 0);
 	if (drv->ioctl_sock < 0) {
-		perror("socket[PF_INET,SOCK_DGRAM]");
-		free(drv);
+		wpa_printf(MSG_ERROR, "socket[PF_INET,SOCK_DGRAM]: %s",
+			   strerror(errno));
+		os_free(drv);
 		return NULL;
 	}
 
 	if (hostap_ioctl_prism2param(drv, PRISM2_PARAM_HOSTAPD, 1)) {
-		printf("Could not enable hostapd mode for interface %s\n",
-		       drv->iface);
+		wpa_printf(MSG_ERROR,
+			   "Could not enable hostapd mode for interface %s",
+			   drv->iface);
 		close(drv->ioctl_sock);
-		free(drv);
+		os_free(drv);
 		return NULL;
 	}
 
 	if (hostap_init_sockets(drv, params->own_addr) ||
 	    hostap_wireless_event_init(drv)) {
 		close(drv->ioctl_sock);
-		free(drv);
+		os_free(drv);
 		return NULL;
 	}
 
@@ -1060,7 +1068,8 @@
 	iwr.u.freq.e = 0;
 
 	if (ioctl(drv->ioctl_sock, SIOCSIWFREQ, &iwr) < 0) {
-		perror("ioctl[SIOCSIWFREQ]");
+		wpa_printf(MSG_ERROR, "ioctl[SIOCSIWFREQ]: %s",
+			   strerror(errno));
 		return -1;
 	}
 
diff --git a/src/drivers/driver_hostap.h b/src/drivers/driver_hostap.h
index a9d3e76..4c1e6d6 100644
--- a/src/drivers/driver_hostap.h
+++ b/src/drivers/driver_hostap.h
@@ -192,7 +192,7 @@
 		} mlme;
 		struct {
 			u8 ssid_len;
-			u8 ssid[32];
+			u8 ssid[SSID_MAX_LEN];
 		} scan_req;
 	} u;
 };
diff --git a/src/drivers/driver_macsec_qca.c b/src/drivers/driver_macsec_qca.c
index cf24799..3eae2f8 100644
--- a/src/drivers/driver_macsec_qca.c
+++ b/src/drivers/driver_macsec_qca.c
@@ -91,7 +91,7 @@
 	if (setsockopt(sock, SOL_PACKET,
 		       add ? PACKET_ADD_MEMBERSHIP : PACKET_DROP_MEMBERSHIP,
 		       &mreq, sizeof(mreq)) < 0) {
-		perror("setsockopt");
+		wpa_printf(MSG_ERROR, "setsockopt: %s", strerror(errno));
 		return -1;
 	}
 	return 0;
@@ -131,14 +131,15 @@
 
 	s = socket(PF_INET, SOCK_DGRAM, 0);
 	if (s < 0) {
-		perror("socket");
+		wpa_printf(MSG_ERROR, "socket: %s", strerror(errno));
 		return -1;
 	}
 
 	os_memset(&ifr, 0, sizeof(ifr));
 	os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ);
 	if (ioctl(s, SIOCGIFFLAGS, (caddr_t) &ifr) < 0) {
-		perror("ioctl[SIOCGIFFLAGS]");
+		wpa_printf(MSG_ERROR, "ioctl[SIOCGIFFLAGS]: %s",
+			   strerror(errno));
 		close(s);
 		return -1;
 	}
@@ -155,7 +156,7 @@
 
 	s = socket(PF_INET, SOCK_DGRAM, 0);
 	if (s < 0) {
-		perror("socket");
+		wpa_printf(MSG_ERROR, "socket: %s", strerror(errno));
 		return -1;
 	}
 
@@ -163,7 +164,8 @@
 	os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ);
 	ifr.ifr_flags = flags & 0xffff;
 	if (ioctl(s, SIOCSIFFLAGS, (caddr_t) &ifr) < 0) {
-		perror("ioctl[SIOCSIFFLAGS]");
+		wpa_printf(MSG_ERROR, "ioctl[SIOCSIFFLAGS]: %s",
+			   strerror(errno));
 		close(s);
 		return -1;
 	}
@@ -180,14 +182,15 @@
 
 	s = socket(PF_INET, SOCK_DGRAM, 0);
 	if (s < 0) {
-		perror("socket");
+		wpa_print(MSG_ERROR, "socket: %s", strerror(errno));
 		return -1;
 	}
 
 	os_memset(&ifmr, 0, sizeof(ifmr));
 	os_strlcpy(ifmr.ifm_name, ifname, IFNAMSIZ);
 	if (ioctl(s, SIOCGIFMEDIA, (caddr_t) &ifmr) < 0) {
-		perror("ioctl[SIOCGIFMEDIA]");
+		wpa_printf(MSG_ERROR, "ioctl[SIOCGIFMEDIA]: %s",
+			   strerror(errno));
 		close(s);
 		return -1;
 	}
@@ -211,7 +214,7 @@
 
 	s = socket(PF_INET, SOCK_DGRAM, 0);
 	if (s < 0) {
-		perror("socket");
+		wpa_printf(MSG_ERROR, "socket: %s", strerror(errno));
 		return -1;
 	}
 
@@ -245,7 +248,8 @@
 #endif /* defined(__NetBSD__) || defined(__OpenBSD__) || defined(__APPLE__) */
 
 	if (ioctl(s, add ? SIOCADDMULTI : SIOCDELMULTI, (caddr_t) &ifr) < 0) {
-		perror("ioctl[SIOC{ADD/DEL}MULTI]");
+		wpa_printf(MSG_ERROR, "ioctl[SIOC{ADD/DEL}MULTI]: %s",
+			   strerror(errno));
 		close(s);
 		return -1;
 	}
@@ -323,7 +327,7 @@
 #ifdef __linux__
 	drv->pf_sock = socket(PF_PACKET, SOCK_DGRAM, 0);
 	if (drv->pf_sock < 0)
-		perror("socket(PF_PACKET)");
+		wpa_printf(MSG_ERROR, "socket(PF_PACKET): %s", strerror(errno));
 #else /* __linux__ */
 	drv->pf_sock = -1;
 #endif /* __linux__ */
diff --git a/src/drivers/driver_madwifi.c b/src/drivers/driver_madwifi.c
deleted file mode 100644
index 1635c1f..0000000
--- a/src/drivers/driver_madwifi.c
+++ /dev/null
@@ -1,1309 +0,0 @@
-/*
- * hostapd - driver interaction with MADWIFI 802.11 driver
- * Copyright (c) 2004, Sam Leffler <sam@errno.com>
- * Copyright (c) 2004, Video54 Technologies
- * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi>
- *
- * This software may be distributed under the terms of the BSD license.
- * See README for more details.
- *
- * This driver wrapper is only for hostapd AP mode functionality. Station
- * (wpa_supplicant) operations with madwifi are supported by the driver_wext.c
- * wrapper.
- */
-
-#include "includes.h"
-#include <sys/ioctl.h>
-
-#include "common.h"
-#include "driver.h"
-#include "driver_wext.h"
-#include "eloop.h"
-#include "common/ieee802_11_defs.h"
-#include "linux_wext.h"
-
-/*
- * Avoid conflicts with wpa_supplicant definitions by undefining a definition.
- */
-#undef WME_OUI_TYPE
-
-#include <include/compat.h>
-#include <net80211/ieee80211.h>
-#ifdef WME_NUM_AC
-/* Assume this is built against BSD branch of madwifi driver. */
-#define MADWIFI_BSD
-#include <net80211/_ieee80211.h>
-#endif /* WME_NUM_AC */
-#include <net80211/ieee80211_crypto.h>
-#include <net80211/ieee80211_ioctl.h>
-
-#ifdef CONFIG_WPS
-#ifdef IEEE80211_IOCTL_FILTERFRAME
-#include <netpacket/packet.h>
-
-#ifndef ETH_P_80211_RAW
-#define ETH_P_80211_RAW 0x0019
-#endif
-#endif /* IEEE80211_IOCTL_FILTERFRAME */
-#endif /* CONFIG_WPS */
-
-/*
- * Avoid conflicts with hostapd definitions by undefining couple of defines
- * from madwifi header files.
- */
-#undef RSN_VERSION
-#undef WPA_VERSION
-#undef WPA_OUI_TYPE
-#undef WME_OUI_TYPE
-
-
-#ifdef IEEE80211_IOCTL_SETWMMPARAMS
-/* Assume this is built against madwifi-ng */
-#define MADWIFI_NG
-#endif /* IEEE80211_IOCTL_SETWMMPARAMS */
-
-#define WPA_KEY_RSC_LEN 8
-
-#include "priv_netlink.h"
-#include "netlink.h"
-#include "linux_ioctl.h"
-#include "l2_packet/l2_packet.h"
-
-
-struct madwifi_driver_data {
-	struct hostapd_data *hapd;		/* back pointer */
-
-	char	iface[IFNAMSIZ + 1];
-	int     ifindex;
-	struct l2_packet_data *sock_xmit;	/* raw packet xmit socket */
-	struct l2_packet_data *sock_recv;	/* raw packet recv socket */
-	int	ioctl_sock;			/* socket for ioctl() use */
-	struct netlink_data *netlink;
-	int	we_version;
-	u8	acct_mac[ETH_ALEN];
-	struct hostap_sta_driver_data acct_data;
-
-	struct l2_packet_data *sock_raw; /* raw 802.11 management frames */
-};
-
-static int madwifi_sta_deauth(void *priv, const u8 *own_addr, const u8 *addr,
-			      int reason_code);
-
-static int
-set80211priv(struct madwifi_driver_data *drv, int op, void *data, int len)
-{
-	struct iwreq iwr;
-	int do_inline = len < IFNAMSIZ;
-
-	memset(&iwr, 0, sizeof(iwr));
-	os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ);
-#ifdef IEEE80211_IOCTL_FILTERFRAME
-	/* FILTERFRAME must be NOT inline, regardless of size. */
-	if (op == IEEE80211_IOCTL_FILTERFRAME)
-		do_inline = 0;
-#endif /* IEEE80211_IOCTL_FILTERFRAME */
-	if (op == IEEE80211_IOCTL_SET_APPIEBUF)
-		do_inline = 0;
-	if (do_inline) {
-		/*
-		 * Argument data fits inline; put it there.
-		 */
-		memcpy(iwr.u.name, data, len);
-	} else {
-		/*
-		 * Argument data too big for inline transfer; setup a
-		 * parameter block instead; the kernel will transfer
-		 * the data for the driver.
-		 */
-		iwr.u.data.pointer = data;
-		iwr.u.data.length = len;
-	}
-
-	if (ioctl(drv->ioctl_sock, op, &iwr) < 0) {
-#ifdef MADWIFI_NG
-		int first = IEEE80211_IOCTL_SETPARAM;
-		static const char *opnames[] = {
-			"ioctl[IEEE80211_IOCTL_SETPARAM]",
-			"ioctl[IEEE80211_IOCTL_GETPARAM]",
-			"ioctl[IEEE80211_IOCTL_SETMODE]",
-			"ioctl[IEEE80211_IOCTL_GETMODE]",
-			"ioctl[IEEE80211_IOCTL_SETWMMPARAMS]",
-			"ioctl[IEEE80211_IOCTL_GETWMMPARAMS]",
-			"ioctl[IEEE80211_IOCTL_SETCHANLIST]",
-			"ioctl[IEEE80211_IOCTL_GETCHANLIST]",
-			"ioctl[IEEE80211_IOCTL_CHANSWITCH]",
-			"ioctl[IEEE80211_IOCTL_GET_APPIEBUF]",
-			"ioctl[IEEE80211_IOCTL_SET_APPIEBUF]",
-			"ioctl[IEEE80211_IOCTL_GETSCANRESULTS]",
-			"ioctl[IEEE80211_IOCTL_FILTERFRAME]",
-			"ioctl[IEEE80211_IOCTL_GETCHANINFO]",
-			"ioctl[IEEE80211_IOCTL_SETOPTIE]",
-			"ioctl[IEEE80211_IOCTL_GETOPTIE]",
-			"ioctl[IEEE80211_IOCTL_SETMLME]",
-			NULL,
-			"ioctl[IEEE80211_IOCTL_SETKEY]",
-			NULL,
-			"ioctl[IEEE80211_IOCTL_DELKEY]",
-			NULL,
-			"ioctl[IEEE80211_IOCTL_ADDMAC]",
-			NULL,
-			"ioctl[IEEE80211_IOCTL_DELMAC]",
-			NULL,
-			"ioctl[IEEE80211_IOCTL_WDSMAC]",
-			NULL,
-			"ioctl[IEEE80211_IOCTL_WDSDELMAC]",
-			NULL,
-			"ioctl[IEEE80211_IOCTL_KICKMAC]",
-		};
-#else /* MADWIFI_NG */
-		int first = IEEE80211_IOCTL_SETPARAM;
-		static const char *opnames[] = {
-			"ioctl[IEEE80211_IOCTL_SETPARAM]",
-			"ioctl[IEEE80211_IOCTL_GETPARAM]",
-			"ioctl[IEEE80211_IOCTL_SETKEY]",
-			"ioctl[SIOCIWFIRSTPRIV+3]",
-			"ioctl[IEEE80211_IOCTL_DELKEY]",
-			"ioctl[SIOCIWFIRSTPRIV+5]",
-			"ioctl[IEEE80211_IOCTL_SETMLME]",
-			"ioctl[SIOCIWFIRSTPRIV+7]",
-			"ioctl[IEEE80211_IOCTL_SETOPTIE]",
-			"ioctl[IEEE80211_IOCTL_GETOPTIE]",
-			"ioctl[IEEE80211_IOCTL_ADDMAC]",
-			"ioctl[SIOCIWFIRSTPRIV+11]",
-			"ioctl[IEEE80211_IOCTL_DELMAC]",
-			"ioctl[SIOCIWFIRSTPRIV+13]",
-			"ioctl[IEEE80211_IOCTL_CHANLIST]",
-			"ioctl[SIOCIWFIRSTPRIV+15]",
-			"ioctl[IEEE80211_IOCTL_GETRSN]",
-			"ioctl[SIOCIWFIRSTPRIV+17]",
-			"ioctl[IEEE80211_IOCTL_GETKEY]",
-		};
-#endif /* MADWIFI_NG */
-		int idx = op - first;
-		if (first <= op &&
-		    idx < (int) ARRAY_SIZE(opnames) &&
-		    opnames[idx])
-			perror(opnames[idx]);
-		else
-			perror("ioctl[unknown???]");
-		return -1;
-	}
-	return 0;
-}
-
-static int
-set80211param(struct madwifi_driver_data *drv, int op, int arg)
-{
-	struct iwreq iwr;
-
-	memset(&iwr, 0, sizeof(iwr));
-	os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ);
-	iwr.u.mode = op;
-	memcpy(iwr.u.name+sizeof(__u32), &arg, sizeof(arg));
-
-	if (ioctl(drv->ioctl_sock, IEEE80211_IOCTL_SETPARAM, &iwr) < 0) {
-		perror("ioctl[IEEE80211_IOCTL_SETPARAM]");
-		wpa_printf(MSG_DEBUG, "%s: Failed to set parameter (op %d "
-			   "arg %d)", __func__, op, arg);
-		return -1;
-	}
-	return 0;
-}
-
-#ifndef CONFIG_NO_STDOUT_DEBUG
-static const char *
-ether_sprintf(const u8 *addr)
-{
-	static char buf[sizeof(MACSTR)];
-
-	if (addr != NULL)
-		snprintf(buf, sizeof(buf), MACSTR, MAC2STR(addr));
-	else
-		snprintf(buf, sizeof(buf), MACSTR, 0,0,0,0,0,0);
-	return buf;
-}
-#endif /* CONFIG_NO_STDOUT_DEBUG */
-
-/*
- * Configure WPA parameters.
- */
-static int
-madwifi_configure_wpa(struct madwifi_driver_data *drv,
-		      struct wpa_bss_params *params)
-{
-	int v;
-
-	switch (params->wpa_group) {
-	case WPA_CIPHER_CCMP:
-		v = IEEE80211_CIPHER_AES_CCM;
-		break;
-	case WPA_CIPHER_TKIP:
-		v = IEEE80211_CIPHER_TKIP;
-		break;
-	case WPA_CIPHER_WEP104:
-		v = IEEE80211_CIPHER_WEP;
-		break;
-	case WPA_CIPHER_WEP40:
-		v = IEEE80211_CIPHER_WEP;
-		break;
-	case WPA_CIPHER_NONE:
-		v = IEEE80211_CIPHER_NONE;
-		break;
-	default:
-		wpa_printf(MSG_ERROR, "Unknown group key cipher %u",
-			   params->wpa_group);
-		return -1;
-	}
-	wpa_printf(MSG_DEBUG, "%s: group key cipher=%d", __func__, v);
-	if (set80211param(drv, IEEE80211_PARAM_MCASTCIPHER, v)) {
-		printf("Unable to set group key cipher to %u\n", v);
-		return -1;
-	}
-	if (v == IEEE80211_CIPHER_WEP) {
-		/* key length is done only for specific ciphers */
-		v = (params->wpa_group == WPA_CIPHER_WEP104 ? 13 : 5);
-		if (set80211param(drv, IEEE80211_PARAM_MCASTKEYLEN, v)) {
-			printf("Unable to set group key length to %u\n", v);
-			return -1;
-		}
-	}
-
-	v = 0;
-	if (params->wpa_pairwise & WPA_CIPHER_CCMP)
-		v |= 1<<IEEE80211_CIPHER_AES_CCM;
-	if (params->wpa_pairwise & WPA_CIPHER_TKIP)
-		v |= 1<<IEEE80211_CIPHER_TKIP;
-	if (params->wpa_pairwise & WPA_CIPHER_NONE)
-		v |= 1<<IEEE80211_CIPHER_NONE;
-	wpa_printf(MSG_DEBUG, "%s: pairwise key ciphers=0x%x", __func__, v);
-	if (set80211param(drv, IEEE80211_PARAM_UCASTCIPHERS, v)) {
-		printf("Unable to set pairwise key ciphers to 0x%x\n", v);
-		return -1;
-	}
-
-	wpa_printf(MSG_DEBUG, "%s: key management algorithms=0x%x",
-		   __func__, params->wpa_key_mgmt);
-	if (set80211param(drv, IEEE80211_PARAM_KEYMGTALGS,
-			  params->wpa_key_mgmt)) {
-		printf("Unable to set key management algorithms to 0x%x\n",
-			params->wpa_key_mgmt);
-		return -1;
-	}
-
-	v = 0;
-	if (params->rsn_preauth)
-		v |= BIT(0);
-	wpa_printf(MSG_DEBUG, "%s: rsn capabilities=0x%x",
-		   __func__, params->rsn_preauth);
-	if (set80211param(drv, IEEE80211_PARAM_RSNCAPS, v)) {
-		printf("Unable to set RSN capabilities to 0x%x\n", v);
-		return -1;
-	}
-
-	wpa_printf(MSG_DEBUG, "%s: enable WPA=0x%x", __func__, params->wpa);
-	if (set80211param(drv, IEEE80211_PARAM_WPA, params->wpa)) {
-		printf("Unable to set WPA to %u\n", params->wpa);
-		return -1;
-	}
-	return 0;
-}
-
-static int
-madwifi_set_ieee8021x(void *priv, struct wpa_bss_params *params)
-{
-	struct madwifi_driver_data *drv = priv;
-
-	wpa_printf(MSG_DEBUG, "%s: enabled=%d", __func__, params->enabled);
-
-	if (!params->enabled) {
-		/* XXX restore state */
-		return set80211param(priv, IEEE80211_PARAM_AUTHMODE,
-			IEEE80211_AUTH_AUTO);
-	}
-	if (!params->wpa && !params->ieee802_1x) {
-		wpa_printf(MSG_WARNING, "No 802.1X or WPA enabled!");
-		return -1;
-	}
-	if (params->wpa && madwifi_configure_wpa(drv, params) != 0) {
-		wpa_printf(MSG_WARNING, "Error configuring WPA state!");
-		return -1;
-	}
-	if (set80211param(priv, IEEE80211_PARAM_AUTHMODE,
-		(params->wpa ? IEEE80211_AUTH_WPA : IEEE80211_AUTH_8021X))) {
-		wpa_printf(MSG_WARNING, "Error enabling WPA/802.1X!");
-		return -1;
-	}
-
-	return 0;
-}
-
-static int
-madwifi_set_privacy(void *priv, int enabled)
-{
-	struct madwifi_driver_data *drv = priv;
-
-	wpa_printf(MSG_DEBUG, "%s: enabled=%d", __func__, enabled);
-
-	return set80211param(drv, IEEE80211_PARAM_PRIVACY, enabled);
-}
-
-static int
-madwifi_set_sta_authorized(void *priv, const u8 *addr, int authorized)
-{
-	struct madwifi_driver_data *drv = priv;
-	struct ieee80211req_mlme mlme;
-	int ret;
-
-	wpa_printf(MSG_DEBUG, "%s: addr=%s authorized=%d",
-		   __func__, ether_sprintf(addr), authorized);
-
-	if (authorized)
-		mlme.im_op = IEEE80211_MLME_AUTHORIZE;
-	else
-		mlme.im_op = IEEE80211_MLME_UNAUTHORIZE;
-	mlme.im_reason = 0;
-	memcpy(mlme.im_macaddr, addr, IEEE80211_ADDR_LEN);
-	ret = set80211priv(drv, IEEE80211_IOCTL_SETMLME, &mlme, sizeof(mlme));
-	if (ret < 0) {
-		wpa_printf(MSG_DEBUG, "%s: Failed to %sauthorize STA " MACSTR,
-			   __func__, authorized ? "" : "un", MAC2STR(addr));
-	}
-
-	return ret;
-}
-
-static int
-madwifi_sta_set_flags(void *priv, const u8 *addr,
-		      int total_flags, int flags_or, int flags_and)
-{
-	/* For now, only support setting Authorized flag */
-	if (flags_or & WPA_STA_AUTHORIZED)
-		return madwifi_set_sta_authorized(priv, addr, 1);
-	if (!(flags_and & WPA_STA_AUTHORIZED))
-		return madwifi_set_sta_authorized(priv, addr, 0);
-	return 0;
-}
-
-static int
-madwifi_del_key(void *priv, const u8 *addr, int key_idx)
-{
-	struct madwifi_driver_data *drv = priv;
-	struct ieee80211req_del_key wk;
-	int ret;
-
-	wpa_printf(MSG_DEBUG, "%s: addr=%s key_idx=%d",
-		   __func__, ether_sprintf(addr), key_idx);
-
-	memset(&wk, 0, sizeof(wk));
-	if (addr != NULL) {
-		memcpy(wk.idk_macaddr, addr, IEEE80211_ADDR_LEN);
-		wk.idk_keyix = (u8) IEEE80211_KEYIX_NONE;
-	} else {
-		wk.idk_keyix = key_idx;
-	}
-
-	ret = set80211priv(drv, IEEE80211_IOCTL_DELKEY, &wk, sizeof(wk));
-	if (ret < 0) {
-		wpa_printf(MSG_DEBUG, "%s: Failed to delete key (addr %s"
-			   " key_idx %d)", __func__, ether_sprintf(addr),
-			   key_idx);
-	}
-
-	return ret;
-}
-
-static int
-wpa_driver_madwifi_set_key(const char *ifname, void *priv, enum wpa_alg alg,
-			   const u8 *addr, int key_idx, int set_tx,
-			   const u8 *seq, size_t seq_len,
-			   const u8 *key, size_t key_len)
-{
-	struct madwifi_driver_data *drv = priv;
-	struct ieee80211req_key wk;
-	u_int8_t cipher;
-	int ret;
-
-	if (alg == WPA_ALG_NONE)
-		return madwifi_del_key(drv, addr, key_idx);
-
-	wpa_printf(MSG_DEBUG, "%s: alg=%d addr=%s key_idx=%d",
-		   __func__, alg, ether_sprintf(addr), key_idx);
-
-	if (alg == WPA_ALG_WEP)
-		cipher = IEEE80211_CIPHER_WEP;
-	else if (alg == WPA_ALG_TKIP)
-		cipher = IEEE80211_CIPHER_TKIP;
-	else if (alg == WPA_ALG_CCMP)
-		cipher = IEEE80211_CIPHER_AES_CCM;
-	else {
-		printf("%s: unknown/unsupported algorithm %d\n",
-			__func__, alg);
-		return -1;
-	}
-
-	if (key_len > sizeof(wk.ik_keydata)) {
-		printf("%s: key length %lu too big\n", __func__,
-		       (unsigned long) key_len);
-		return -3;
-	}
-
-	memset(&wk, 0, sizeof(wk));
-	wk.ik_type = cipher;
-	wk.ik_flags = IEEE80211_KEY_RECV | IEEE80211_KEY_XMIT;
-	if (addr == NULL || is_broadcast_ether_addr(addr)) {
-		memset(wk.ik_macaddr, 0xff, IEEE80211_ADDR_LEN);
-		wk.ik_keyix = key_idx;
-		wk.ik_flags |= IEEE80211_KEY_DEFAULT;
-	} else {
-		memcpy(wk.ik_macaddr, addr, IEEE80211_ADDR_LEN);
-		wk.ik_keyix = IEEE80211_KEYIX_NONE;
-	}
-	wk.ik_keylen = key_len;
-	memcpy(wk.ik_keydata, key, key_len);
-
-	ret = set80211priv(drv, IEEE80211_IOCTL_SETKEY, &wk, sizeof(wk));
-	if (ret < 0) {
-		wpa_printf(MSG_DEBUG, "%s: Failed to set key (addr %s"
-			   " key_idx %d alg %d key_len %lu set_tx %d)",
-			   __func__, ether_sprintf(wk.ik_macaddr), key_idx,
-			   alg, (unsigned long) key_len, set_tx);
-	}
-
-	return ret;
-}
-
-
-static int
-madwifi_get_seqnum(const char *ifname, void *priv, const u8 *addr, int idx,
-		   u8 *seq)
-{
-	struct madwifi_driver_data *drv = priv;
-	struct ieee80211req_key wk;
-
-	wpa_printf(MSG_DEBUG, "%s: addr=%s idx=%d",
-		   __func__, ether_sprintf(addr), idx);
-
-	memset(&wk, 0, sizeof(wk));
-	if (addr == NULL)
-		memset(wk.ik_macaddr, 0xff, IEEE80211_ADDR_LEN);
-	else
-		memcpy(wk.ik_macaddr, addr, IEEE80211_ADDR_LEN);
-	wk.ik_keyix = idx;
-
-	if (set80211priv(drv, IEEE80211_IOCTL_GETKEY, &wk, sizeof(wk))) {
-		wpa_printf(MSG_DEBUG, "%s: Failed to get encryption data "
-			   "(addr " MACSTR " key_idx %d)",
-			   __func__, MAC2STR(wk.ik_macaddr), idx);
-		return -1;
-	}
-
-#ifdef WORDS_BIGENDIAN
-	{
-		/*
-		 * wk.ik_keytsc is in host byte order (big endian), need to
-		 * swap it to match with the byte order used in WPA.
-		 */
-		int i;
-		u8 tmp[WPA_KEY_RSC_LEN];
-		memcpy(tmp, &wk.ik_keytsc, sizeof(wk.ik_keytsc));
-		for (i = 0; i < WPA_KEY_RSC_LEN; i++) {
-			seq[i] = tmp[WPA_KEY_RSC_LEN - i - 1];
-		}
-	}
-#else /* WORDS_BIGENDIAN */
-	memcpy(seq, &wk.ik_keytsc, sizeof(wk.ik_keytsc));
-#endif /* WORDS_BIGENDIAN */
-	return 0;
-}
-
-
-static int 
-madwifi_flush(void *priv)
-{
-#ifdef MADWIFI_BSD
-	u8 allsta[IEEE80211_ADDR_LEN];
-	memset(allsta, 0xff, IEEE80211_ADDR_LEN);
-	return madwifi_sta_deauth(priv, NULL, allsta,
-				  IEEE80211_REASON_AUTH_LEAVE);
-#else /* MADWIFI_BSD */
-	return 0;		/* XXX */
-#endif /* MADWIFI_BSD */
-}
-
-
-static int
-madwifi_read_sta_driver_data(void *priv, struct hostap_sta_driver_data *data,
-			     const u8 *addr)
-{
-	struct madwifi_driver_data *drv = priv;
-
-#ifdef MADWIFI_BSD
-	struct ieee80211req_sta_stats stats;
-
-	memset(data, 0, sizeof(*data));
-
-	/*
-	 * Fetch statistics for station from the system.
-	 */
-	memset(&stats, 0, sizeof(stats));
-	memcpy(stats.is_u.macaddr, addr, IEEE80211_ADDR_LEN);
-	if (set80211priv(drv,
-#ifdef MADWIFI_NG
-			 IEEE80211_IOCTL_STA_STATS,
-#else /* MADWIFI_NG */
-			 IEEE80211_IOCTL_GETSTASTATS,
-#endif /* MADWIFI_NG */
-			 &stats, sizeof(stats))) {
-		wpa_printf(MSG_DEBUG, "%s: Failed to fetch STA stats (addr "
-			   MACSTR ")", __func__, MAC2STR(addr));
-		if (memcmp(addr, drv->acct_mac, ETH_ALEN) == 0) {
-			memcpy(data, &drv->acct_data, sizeof(*data));
-			return 0;
-		}
-
-		printf("Failed to get station stats information element.\n");
-		return -1;
-	}
-
-	data->rx_packets = stats.is_stats.ns_rx_data;
-	data->rx_bytes = stats.is_stats.ns_rx_bytes;
-	data->tx_packets = stats.is_stats.ns_tx_data;
-	data->tx_bytes = stats.is_stats.ns_tx_bytes;
-	return 0;
-
-#else /* MADWIFI_BSD */
-
-	char buf[1024], line[128], *pos;
-	FILE *f;
-	unsigned long val;
-
-	memset(data, 0, sizeof(*data));
-	snprintf(buf, sizeof(buf), "/proc/net/madwifi/%s/" MACSTR,
-		 drv->iface, MAC2STR(addr));
-
-	f = fopen(buf, "r");
-	if (!f) {
-		if (memcmp(addr, drv->acct_mac, ETH_ALEN) != 0)
-			return -1;
-		memcpy(data, &drv->acct_data, sizeof(*data));
-		return 0;
-	}
-	/* Need to read proc file with in one piece, so use large enough
-	 * buffer. */
-	setbuffer(f, buf, sizeof(buf));
-
-	while (fgets(line, sizeof(line), f)) {
-		pos = strchr(line, '=');
-		if (!pos)
-			continue;
-		*pos++ = '\0';
-		val = strtoul(pos, NULL, 10);
-		if (strcmp(line, "rx_packets") == 0)
-			data->rx_packets = val;
-		else if (strcmp(line, "tx_packets") == 0)
-			data->tx_packets = val;
-		else if (strcmp(line, "rx_bytes") == 0)
-			data->rx_bytes = val;
-		else if (strcmp(line, "tx_bytes") == 0)
-			data->tx_bytes = val;
-	}
-
-	fclose(f);
-
-	return 0;
-#endif /* MADWIFI_BSD */
-}
-
-
-static int
-madwifi_sta_clear_stats(void *priv, const u8 *addr)
-{
-#if defined(MADWIFI_BSD) && defined(IEEE80211_MLME_CLEAR_STATS)
-	struct madwifi_driver_data *drv = priv;
-	struct ieee80211req_mlme mlme;
-	int ret;
-
-	wpa_printf(MSG_DEBUG, "%s: addr=%s", __func__, ether_sprintf(addr));
-
-	mlme.im_op = IEEE80211_MLME_CLEAR_STATS;
-	memcpy(mlme.im_macaddr, addr, IEEE80211_ADDR_LEN);
-	ret = set80211priv(drv, IEEE80211_IOCTL_SETMLME, &mlme,
-			   sizeof(mlme));
-	if (ret < 0) {
-		wpa_printf(MSG_DEBUG, "%s: Failed to clear STA stats (addr "
-			   MACSTR ")", __func__, MAC2STR(addr));
-	}
-
-	return ret;
-#else /* MADWIFI_BSD && IEEE80211_MLME_CLEAR_STATS */
-	return 0; /* FIX */
-#endif /* MADWIFI_BSD && IEEE80211_MLME_CLEAR_STATS */
-}
-
-
-static int
-madwifi_set_opt_ie(void *priv, const u8 *ie, size_t ie_len)
-{
-	/*
-	 * Do nothing; we setup parameters at startup that define the
-	 * contents of the beacon information element.
-	 */
-	return 0;
-}
-
-static int
-madwifi_sta_deauth(void *priv, const u8 *own_addr, const u8 *addr,
-		   int reason_code)
-{
-	struct madwifi_driver_data *drv = priv;
-	struct ieee80211req_mlme mlme;
-	int ret;
-
-	wpa_printf(MSG_DEBUG, "%s: addr=%s reason_code=%d",
-		   __func__, ether_sprintf(addr), reason_code);
-
-	mlme.im_op = IEEE80211_MLME_DEAUTH;
-	mlme.im_reason = reason_code;
-	memcpy(mlme.im_macaddr, addr, IEEE80211_ADDR_LEN);
-	ret = set80211priv(drv, IEEE80211_IOCTL_SETMLME, &mlme, sizeof(mlme));
-	if (ret < 0) {
-		wpa_printf(MSG_DEBUG, "%s: Failed to deauth STA (addr " MACSTR
-			   " reason %d)",
-			   __func__, MAC2STR(addr), reason_code);
-	}
-
-	return ret;
-}
-
-static int
-madwifi_sta_disassoc(void *priv, const u8 *own_addr, const u8 *addr,
-		     int reason_code)
-{
-	struct madwifi_driver_data *drv = priv;
-	struct ieee80211req_mlme mlme;
-	int ret;
-
-	wpa_printf(MSG_DEBUG, "%s: addr=%s reason_code=%d",
-		   __func__, ether_sprintf(addr), reason_code);
-
-	mlme.im_op = IEEE80211_MLME_DISASSOC;
-	mlme.im_reason = reason_code;
-	memcpy(mlme.im_macaddr, addr, IEEE80211_ADDR_LEN);
-	ret = set80211priv(drv, IEEE80211_IOCTL_SETMLME, &mlme, sizeof(mlme));
-	if (ret < 0) {
-		wpa_printf(MSG_DEBUG, "%s: Failed to disassoc STA (addr "
-			   MACSTR " reason %d)",
-			   __func__, MAC2STR(addr), reason_code);
-	}
-
-	return ret;
-}
-
-#ifdef CONFIG_WPS
-#ifdef IEEE80211_IOCTL_FILTERFRAME
-static void madwifi_raw_receive(void *ctx, const u8 *src_addr, const u8 *buf,
-				size_t len)
-{
-	struct madwifi_driver_data *drv = ctx;
-	const struct ieee80211_mgmt *mgmt;
-	u16 fc;
-	union wpa_event_data event;
-
-	/* Send Probe Request information to WPS processing */
-
-	if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.probe_req))
-		return;
-	mgmt = (const struct ieee80211_mgmt *) buf;
-
-	fc = le_to_host16(mgmt->frame_control);
-	if (WLAN_FC_GET_TYPE(fc) != WLAN_FC_TYPE_MGMT ||
-	    WLAN_FC_GET_STYPE(fc) != WLAN_FC_STYPE_PROBE_REQ)
-		return;
-
-	os_memset(&event, 0, sizeof(event));
-	event.rx_probe_req.sa = mgmt->sa;
-	event.rx_probe_req.da = mgmt->da;
-	event.rx_probe_req.bssid = mgmt->bssid;
-	event.rx_probe_req.ie = mgmt->u.probe_req.variable;
-	event.rx_probe_req.ie_len =
-		len - (IEEE80211_HDRLEN + sizeof(mgmt->u.probe_req));
-	wpa_supplicant_event(drv->hapd, EVENT_RX_PROBE_REQ, &event);
-}
-#endif /* IEEE80211_IOCTL_FILTERFRAME */
-#endif /* CONFIG_WPS */
-
-static int madwifi_receive_probe_req(struct madwifi_driver_data *drv)
-{
-	int ret = 0;
-#ifdef CONFIG_WPS
-#ifdef IEEE80211_IOCTL_FILTERFRAME
-	struct ieee80211req_set_filter filt;
-
-	wpa_printf(MSG_DEBUG, "%s Enter", __func__);
-	filt.app_filterype = IEEE80211_FILTER_TYPE_PROBE_REQ;
-
-	ret = set80211priv(drv, IEEE80211_IOCTL_FILTERFRAME, &filt,
-			   sizeof(struct ieee80211req_set_filter));
-	if (ret)
-		return ret;
-
-	drv->sock_raw = l2_packet_init(drv->iface, NULL, ETH_P_80211_RAW,
-				       madwifi_raw_receive, drv, 1);
-	if (drv->sock_raw == NULL)
-		return -1;
-#endif /* IEEE80211_IOCTL_FILTERFRAME */
-#endif /* CONFIG_WPS */
-	return ret;
-}
-
-#ifdef CONFIG_WPS
-static int
-madwifi_set_wps_ie(void *priv, const u8 *ie, size_t len, u32 frametype)
-{
-	struct madwifi_driver_data *drv = priv;
-	u8 buf[256];
-	struct ieee80211req_getset_appiebuf *beac_ie;
-
-	wpa_printf(MSG_DEBUG, "%s buflen = %lu", __func__,
-		   (unsigned long) len);
-
-	beac_ie = (struct ieee80211req_getset_appiebuf *) buf;
-	beac_ie->app_frmtype = frametype;
-	beac_ie->app_buflen = len;
-	memcpy(&(beac_ie->app_buf[0]), ie, len);
-
-	return set80211priv(drv, IEEE80211_IOCTL_SET_APPIEBUF, beac_ie,
-			    sizeof(struct ieee80211req_getset_appiebuf) + len);
-}
-
-static int
-madwifi_set_ap_wps_ie(void *priv, const struct wpabuf *beacon,
-		      const struct wpabuf *proberesp,
-		      const struct wpabuf *assocresp)
-{
-	if (madwifi_set_wps_ie(priv, beacon ? wpabuf_head(beacon) : NULL,
-			       beacon ? wpabuf_len(beacon) : 0,
-			       IEEE80211_APPIE_FRAME_BEACON) < 0)
-		return -1;
-	return madwifi_set_wps_ie(priv,
-				  proberesp ? wpabuf_head(proberesp) : NULL,
-				  proberesp ? wpabuf_len(proberesp) : 0,
-				  IEEE80211_APPIE_FRAME_PROBE_RESP);
-}
-#else /* CONFIG_WPS */
-#define madwifi_set_ap_wps_ie NULL
-#endif /* CONFIG_WPS */
-
-static int madwifi_set_freq(void *priv, struct hostapd_freq_params *freq)
-{
-	struct madwifi_driver_data *drv = priv;
-	struct iwreq iwr;
-
-	os_memset(&iwr, 0, sizeof(iwr));
-	os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ);
-	iwr.u.freq.m = freq->channel;
-	iwr.u.freq.e = 0;
-
-	if (ioctl(drv->ioctl_sock, SIOCSIWFREQ, &iwr) < 0) {
-		perror("ioctl[SIOCSIWFREQ]");
-		return -1;
-	}
-
-	return 0;
-}
-
-static void
-madwifi_new_sta(struct madwifi_driver_data *drv, u8 addr[IEEE80211_ADDR_LEN])
-{
-	struct hostapd_data *hapd = drv->hapd;
-	struct ieee80211req_wpaie ie;
-	int ielen = 0;
-	u8 *iebuf = NULL;
-
-	/*
-	 * Fetch negotiated WPA/RSN parameters from the system.
-	 */
-	memset(&ie, 0, sizeof(ie));
-	memcpy(ie.wpa_macaddr, addr, IEEE80211_ADDR_LEN);
-	if (set80211priv(drv, IEEE80211_IOCTL_GETWPAIE, &ie, sizeof(ie))) {
-		wpa_printf(MSG_DEBUG, "%s: Failed to get WPA/RSN IE",
-			   __func__);
-		goto no_ie;
-	}
-	wpa_hexdump(MSG_MSGDUMP, "madwifi req WPA IE",
-		    ie.wpa_ie, IEEE80211_MAX_OPT_IE);
-	iebuf = ie.wpa_ie;
-	/* madwifi seems to return some random data if WPA/RSN IE is not set.
-	 * Assume the IE was not included if the IE type is unknown. */
-	if (iebuf[0] != WLAN_EID_VENDOR_SPECIFIC)
-		iebuf[1] = 0;
-#ifdef MADWIFI_NG
-	wpa_hexdump(MSG_MSGDUMP, "madwifi req RSN IE",
-		    ie.rsn_ie, IEEE80211_MAX_OPT_IE);
-	if (iebuf[1] == 0 && ie.rsn_ie[1] > 0) {
-		/* madwifi-ng svn #1453 added rsn_ie. Use it, if wpa_ie was not
-		 * set. This is needed for WPA2. */
-		iebuf = ie.rsn_ie;
-		if (iebuf[0] != WLAN_EID_RSN)
-			iebuf[1] = 0;
-	}
-#endif /* MADWIFI_NG */
-
-	ielen = iebuf[1];
-	if (ielen == 0)
-		iebuf = NULL;
-	else
-		ielen += 2;
-
-no_ie:
-	drv_event_assoc(hapd, addr, iebuf, ielen, 0);
-
-	if (memcmp(addr, drv->acct_mac, ETH_ALEN) == 0) {
-		/* Cached accounting data is not valid anymore. */
-		memset(drv->acct_mac, 0, ETH_ALEN);
-		memset(&drv->acct_data, 0, sizeof(drv->acct_data));
-	}
-}
-
-static void
-madwifi_wireless_event_wireless_custom(struct madwifi_driver_data *drv,
-				       char *custom)
-{
-	wpa_printf(MSG_DEBUG, "Custom wireless event: '%s'", custom);
-
-	if (strncmp(custom, "MLME-MICHAELMICFAILURE.indication", 33) == 0) {
-		char *pos;
-		u8 addr[ETH_ALEN];
-		pos = strstr(custom, "addr=");
-		if (pos == NULL) {
-			wpa_printf(MSG_DEBUG,
-				   "MLME-MICHAELMICFAILURE.indication "
-				   "without sender address ignored");
-			return;
-		}
-		pos += 5;
-		if (hwaddr_aton(pos, addr) == 0) {
-			union wpa_event_data data;
-			os_memset(&data, 0, sizeof(data));
-			data.michael_mic_failure.unicast = 1;
-			data.michael_mic_failure.src = addr;
-			wpa_supplicant_event(drv->hapd,
-					     EVENT_MICHAEL_MIC_FAILURE, &data);
-		} else {
-			wpa_printf(MSG_DEBUG,
-				   "MLME-MICHAELMICFAILURE.indication "
-				   "with invalid MAC address");
-		}
-	} else if (strncmp(custom, "STA-TRAFFIC-STAT", 16) == 0) {
-		char *key, *value;
-		u32 val;
-		key = custom;
-		while ((key = strchr(key, '\n')) != NULL) {
-			key++;
-			value = strchr(key, '=');
-			if (value == NULL)
-				continue;
-			*value++ = '\0';
-			val = strtoul(value, NULL, 10);
-			if (strcmp(key, "mac") == 0)
-				hwaddr_aton(value, drv->acct_mac);
-			else if (strcmp(key, "rx_packets") == 0)
-				drv->acct_data.rx_packets = val;
-			else if (strcmp(key, "tx_packets") == 0)
-				drv->acct_data.tx_packets = val;
-			else if (strcmp(key, "rx_bytes") == 0)
-				drv->acct_data.rx_bytes = val;
-			else if (strcmp(key, "tx_bytes") == 0)
-				drv->acct_data.tx_bytes = val;
-			key = value;
-		}
-	}
-}
-
-static void
-madwifi_wireless_event_wireless(struct madwifi_driver_data *drv,
-					    char *data, int len)
-{
-	struct iw_event iwe_buf, *iwe = &iwe_buf;
-	char *pos, *end, *custom, *buf;
-
-	pos = data;
-	end = data + len;
-
-	while (pos + IW_EV_LCP_LEN <= end) {
-		/* Event data may be unaligned, so make a local, aligned copy
-		 * before processing. */
-		memcpy(&iwe_buf, pos, IW_EV_LCP_LEN);
-		wpa_printf(MSG_MSGDUMP, "Wireless event: cmd=0x%x len=%d",
-			   iwe->cmd, iwe->len);
-		if (iwe->len <= IW_EV_LCP_LEN)
-			return;
-
-		custom = pos + IW_EV_POINT_LEN;
-		if (drv->we_version > 18 &&
-		    (iwe->cmd == IWEVMICHAELMICFAILURE ||
-		     iwe->cmd == IWEVCUSTOM)) {
-			/* WE-19 removed the pointer from struct iw_point */
-			char *dpos = (char *) &iwe_buf.u.data.length;
-			int dlen = dpos - (char *) &iwe_buf;
-			memcpy(dpos, pos + IW_EV_LCP_LEN,
-			       sizeof(struct iw_event) - dlen);
-		} else {
-			memcpy(&iwe_buf, pos, sizeof(struct iw_event));
-			custom += IW_EV_POINT_OFF;
-		}
-
-		switch (iwe->cmd) {
-		case IWEVEXPIRED:
-			drv_event_disassoc(drv->hapd,
-					   (u8 *) iwe->u.addr.sa_data);
-			break;
-		case IWEVREGISTERED:
-			madwifi_new_sta(drv, (u8 *) iwe->u.addr.sa_data);
-			break;
-		case IWEVCUSTOM:
-			if (custom + iwe->u.data.length > end)
-				return;
-			buf = malloc(iwe->u.data.length + 1);
-			if (buf == NULL)
-				return;		/* XXX */
-			memcpy(buf, custom, iwe->u.data.length);
-			buf[iwe->u.data.length] = '\0';
-			madwifi_wireless_event_wireless_custom(drv, buf);
-			free(buf);
-			break;
-		}
-
-		pos += iwe->len;
-	}
-}
-
-
-static void
-madwifi_wireless_event_rtm_newlink(void *ctx, struct ifinfomsg *ifi,
-				   u8 *buf, size_t len)
-{
-	struct madwifi_driver_data *drv = ctx;
-	int attrlen, rta_len;
-	struct rtattr *attr;
-
-	if (ifi->ifi_index != drv->ifindex)
-		return;
-
-	attrlen = len;
-	attr = (struct rtattr *) buf;
-
-	rta_len = RTA_ALIGN(sizeof(struct rtattr));
-	while (RTA_OK(attr, attrlen)) {
-		if (attr->rta_type == IFLA_WIRELESS) {
-			madwifi_wireless_event_wireless(
-				drv, ((char *) attr) + rta_len,
-				attr->rta_len - rta_len);
-		}
-		attr = RTA_NEXT(attr, attrlen);
-	}
-}
-
-
-static int
-madwifi_get_we_version(struct madwifi_driver_data *drv)
-{
-	struct iw_range *range;
-	struct iwreq iwr;
-	int minlen;
-	size_t buflen;
-
-	drv->we_version = 0;
-
-	/*
-	 * Use larger buffer than struct iw_range in order to allow the
-	 * structure to grow in the future.
-	 */
-	buflen = sizeof(struct iw_range) + 500;
-	range = os_zalloc(buflen);
-	if (range == NULL)
-		return -1;
-
-	memset(&iwr, 0, sizeof(iwr));
-	os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ);
-	iwr.u.data.pointer = (caddr_t) range;
-	iwr.u.data.length = buflen;
-
-	minlen = ((char *) &range->enc_capa) - (char *) range +
-		sizeof(range->enc_capa);
-
-	if (ioctl(drv->ioctl_sock, SIOCGIWRANGE, &iwr) < 0) {
-		perror("ioctl[SIOCGIWRANGE]");
-		free(range);
-		return -1;
-	} else if (iwr.u.data.length >= minlen &&
-		   range->we_version_compiled >= 18) {
-		wpa_printf(MSG_DEBUG, "SIOCGIWRANGE: WE(compiled)=%d "
-			   "WE(source)=%d enc_capa=0x%x",
-			   range->we_version_compiled,
-			   range->we_version_source,
-			   range->enc_capa);
-		drv->we_version = range->we_version_compiled;
-	}
-
-	free(range);
-	return 0;
-}
-
-
-static int
-madwifi_wireless_event_init(struct madwifi_driver_data *drv)
-{
-	struct netlink_config *cfg;
-
-	madwifi_get_we_version(drv);
-
-	cfg = os_zalloc(sizeof(*cfg));
-	if (cfg == NULL)
-		return -1;
-	cfg->ctx = drv;
-	cfg->newlink_cb = madwifi_wireless_event_rtm_newlink;
-	drv->netlink = netlink_init(cfg);
-	if (drv->netlink == NULL) {
-		os_free(cfg);
-		return -1;
-	}
-
-	return 0;
-}
-
-
-static int
-madwifi_send_eapol(void *priv, const u8 *addr, const u8 *data, size_t data_len,
-		   int encrypt, const u8 *own_addr, u32 flags)
-{
-	struct madwifi_driver_data *drv = priv;
-	unsigned char buf[3000];
-	unsigned char *bp = buf;
-	struct l2_ethhdr *eth;
-	size_t len;
-	int status;
-
-	/*
-	 * Prepend the Ethernet header.  If the caller left us
-	 * space at the front we could just insert it but since
-	 * we don't know we copy to a local buffer.  Given the frequency
-	 * and size of frames this probably doesn't matter.
-	 */
-	len = data_len + sizeof(struct l2_ethhdr);
-	if (len > sizeof(buf)) {
-		bp = malloc(len);
-		if (bp == NULL) {
-			printf("EAPOL frame discarded, cannot malloc temp "
-			       "buffer of size %lu!\n", (unsigned long) len);
-			return -1;
-		}
-	}
-	eth = (struct l2_ethhdr *) bp;
-	memcpy(eth->h_dest, addr, ETH_ALEN);
-	memcpy(eth->h_source, own_addr, ETH_ALEN);
-	eth->h_proto = host_to_be16(ETH_P_EAPOL);
-	memcpy(eth+1, data, data_len);
-
-	wpa_hexdump(MSG_MSGDUMP, "TX EAPOL", bp, len);
-
-	status = l2_packet_send(drv->sock_xmit, addr, ETH_P_EAPOL, bp, len);
-
-	if (bp != buf)
-		free(bp);
-	return status;
-}
-
-static void
-handle_read(void *ctx, const u8 *src_addr, const u8 *buf, size_t len)
-{
-	struct madwifi_driver_data *drv = ctx;
-	drv_event_eapol_rx(drv->hapd, src_addr, buf + sizeof(struct l2_ethhdr),
-			   len - sizeof(struct l2_ethhdr));
-}
-
-static void *
-madwifi_init(struct hostapd_data *hapd, struct wpa_init_params *params)
-{
-	struct madwifi_driver_data *drv;
-	struct ifreq ifr;
-	struct iwreq iwr;
-	char brname[IFNAMSIZ];
-
-	drv = os_zalloc(sizeof(struct madwifi_driver_data));
-	if (drv == NULL) {
-		printf("Could not allocate memory for madwifi driver data\n");
-		return NULL;
-	}
-
-	drv->hapd = hapd;
-	drv->ioctl_sock = socket(PF_INET, SOCK_DGRAM, 0);
-	if (drv->ioctl_sock < 0) {
-		perror("socket[PF_INET,SOCK_DGRAM]");
-		goto bad;
-	}
-	memcpy(drv->iface, params->ifname, sizeof(drv->iface));
-
-	memset(&ifr, 0, sizeof(ifr));
-	os_strlcpy(ifr.ifr_name, drv->iface, sizeof(ifr.ifr_name));
-	if (ioctl(drv->ioctl_sock, SIOCGIFINDEX, &ifr) != 0) {
-		perror("ioctl(SIOCGIFINDEX)");
-		goto bad;
-	}
-	drv->ifindex = ifr.ifr_ifindex;
-
-	drv->sock_xmit = l2_packet_init(drv->iface, NULL, ETH_P_EAPOL,
-					handle_read, drv, 1);
-	if (drv->sock_xmit == NULL)
-		goto bad;
-	if (l2_packet_get_own_addr(drv->sock_xmit, params->own_addr))
-		goto bad;
-	if (params->bridge[0]) {
-		wpa_printf(MSG_DEBUG, "Configure bridge %s for EAPOL traffic.",
-			   params->bridge[0]);
-		drv->sock_recv = l2_packet_init(params->bridge[0], NULL,
-						ETH_P_EAPOL, handle_read, drv,
-						1);
-		if (drv->sock_recv == NULL)
-			goto bad;
-	} else if (linux_br_get(brname, drv->iface) == 0) {
-		wpa_printf(MSG_DEBUG, "Interface in bridge %s; configure for "
-			   "EAPOL receive", brname);
-		drv->sock_recv = l2_packet_init(brname, NULL, ETH_P_EAPOL,
-						handle_read, drv, 1);
-		if (drv->sock_recv == NULL)
-			goto bad;
-	} else
-		drv->sock_recv = drv->sock_xmit;
-
-	memset(&iwr, 0, sizeof(iwr));
-	os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ);
-
-	iwr.u.mode = IW_MODE_MASTER;
-
-	if (ioctl(drv->ioctl_sock, SIOCSIWMODE, &iwr) < 0) {
-		perror("ioctl[SIOCSIWMODE]");
-		printf("Could not set interface to master mode!\n");
-		goto bad;
-	}
-
-	/* mark down during setup */
-	linux_set_iface_flags(drv->ioctl_sock, drv->iface, 0);
-	madwifi_set_privacy(drv, 0); /* default to no privacy */
-
-	madwifi_receive_probe_req(drv);
-
-	if (madwifi_wireless_event_init(drv))
-		goto bad;
-
-	return drv;
-bad:
-	if (drv->sock_xmit != NULL)
-		l2_packet_deinit(drv->sock_xmit);
-	if (drv->ioctl_sock >= 0)
-		close(drv->ioctl_sock);
-	if (drv != NULL)
-		free(drv);
-	return NULL;
-}
-
-
-static void
-madwifi_deinit(void *priv)
-{
-	struct madwifi_driver_data *drv = priv;
-
-	netlink_deinit(drv->netlink);
-	(void) linux_set_iface_flags(drv->ioctl_sock, drv->iface, 0);
-	if (drv->ioctl_sock >= 0)
-		close(drv->ioctl_sock);
-	if (drv->sock_recv != NULL && drv->sock_recv != drv->sock_xmit)
-		l2_packet_deinit(drv->sock_recv);
-	if (drv->sock_xmit != NULL)
-		l2_packet_deinit(drv->sock_xmit);
-	if (drv->sock_raw)
-		l2_packet_deinit(drv->sock_raw);
-	free(drv);
-}
-
-static int
-madwifi_set_ssid(void *priv, const u8 *buf, int len)
-{
-	struct madwifi_driver_data *drv = priv;
-	struct iwreq iwr;
-
-	memset(&iwr, 0, sizeof(iwr));
-	os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ);
-	iwr.u.essid.flags = 1; /* SSID active */
-	iwr.u.essid.pointer = (caddr_t) buf;
-	iwr.u.essid.length = len + 1;
-
-	if (ioctl(drv->ioctl_sock, SIOCSIWESSID, &iwr) < 0) {
-		perror("ioctl[SIOCSIWESSID]");
-		printf("len=%d\n", len);
-		return -1;
-	}
-	return 0;
-}
-
-static int
-madwifi_get_ssid(void *priv, u8 *buf, int len)
-{
-	struct madwifi_driver_data *drv = priv;
-	struct iwreq iwr;
-	int ret = 0;
-
-	memset(&iwr, 0, sizeof(iwr));
-	os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ);
-	iwr.u.essid.pointer = (caddr_t) buf;
-	iwr.u.essid.length = len;
-
-	if (ioctl(drv->ioctl_sock, SIOCGIWESSID, &iwr) < 0) {
-		perror("ioctl[SIOCGIWESSID]");
-		ret = -1;
-	} else
-		ret = iwr.u.essid.length;
-
-	return ret;
-}
-
-static int
-madwifi_set_countermeasures(void *priv, int enabled)
-{
-	struct madwifi_driver_data *drv = priv;
-	wpa_printf(MSG_DEBUG, "%s: enabled=%d", __FUNCTION__, enabled);
-	return set80211param(drv, IEEE80211_PARAM_COUNTERMEASURES, enabled);
-}
-
-static int
-madwifi_commit(void *priv)
-{
-	struct madwifi_driver_data *drv = priv;
-	return linux_set_iface_flags(drv->ioctl_sock, drv->iface, 1);
-}
-
-
-const struct wpa_driver_ops wpa_driver_madwifi_ops = {
-	.name			= "madwifi",
-	.desc			= "MADWIFI 802.11 support (Atheros, etc.)",
-	.set_key		= wpa_driver_madwifi_set_key,
-	.hapd_init		= madwifi_init,
-	.hapd_deinit		= madwifi_deinit,
-	.set_ieee8021x		= madwifi_set_ieee8021x,
-	.set_privacy		= madwifi_set_privacy,
-	.get_seqnum		= madwifi_get_seqnum,
-	.flush			= madwifi_flush,
-	.set_generic_elem	= madwifi_set_opt_ie,
-	.sta_set_flags		= madwifi_sta_set_flags,
-	.read_sta_data		= madwifi_read_sta_driver_data,
-	.hapd_send_eapol	= madwifi_send_eapol,
-	.sta_disassoc		= madwifi_sta_disassoc,
-	.sta_deauth		= madwifi_sta_deauth,
-	.hapd_set_ssid		= madwifi_set_ssid,
-	.hapd_get_ssid		= madwifi_get_ssid,
-	.hapd_set_countermeasures	= madwifi_set_countermeasures,
-	.sta_clear_stats        = madwifi_sta_clear_stats,
-	.commit			= madwifi_commit,
-	.set_ap_wps_ie		= madwifi_set_ap_wps_ie,
-	.set_freq		= madwifi_set_freq,
-};
diff --git a/src/drivers/driver_ndis.c b/src/drivers/driver_ndis.c
index 4953af6..669f1b8 100644
--- a/src/drivers/driver_ndis.c
+++ b/src/drivers/driver_ndis.c
@@ -709,11 +709,11 @@
 /* Disconnect by setting SSID to random (i.e., likely not used). */
 static int wpa_driver_ndis_disconnect(struct wpa_driver_ndis_data *drv)
 {
-	char ssid[32];
+	char ssid[SSID_MAX_LEN];
 	int i;
-	for (i = 0; i < 32; i++)
+	for (i = 0; i < SSID_MAX_LEN; i++)
 		ssid[i] = rand() & 0xff;
-	return wpa_driver_ndis_set_ssid(drv, (u8 *) ssid, 32);
+	return wpa_driver_ndis_set_ssid(drv, (u8 *) ssid, SSID_MAX_LEN);
 }
 
 
@@ -806,7 +806,7 @@
 	if (wpa_scan_get_ie(r, WLAN_EID_SSID))
 		return r; /* SSID IE already present */
 
-	if (ssid->SsidLength == 0 || ssid->SsidLength > 32)
+	if (ssid->SsidLength == 0 || ssid->SsidLength > SSID_MAX_LEN)
 		return r; /* No valid SSID inside scan data */
 
 	nr = os_realloc(r, sizeof(*r) + r->ie_len + 2 + ssid->SsidLength);
diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
index 4c8f29f..f74422b 100644
--- a/src/drivers/driver_nl80211.c
+++ b/src/drivers/driver_nl80211.c
@@ -1,6 +1,6 @@
 /*
  * Driver interaction with Linux nl80211/cfg80211
- * Copyright (c) 2002-2014, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2002-2015, Jouni Malinen <j@w1.fi>
  * Copyright (c) 2003-2004, Instant802 Networks, Inc.
  * Copyright (c) 2005-2006, Devicescape Software, Inc.
  * Copyright (c) 2007, Johannes Berg <johannes@sipsolutions.net>
@@ -11,67 +11,35 @@
  */
 
 #include "includes.h"
-#include <sys/ioctl.h>
 #include <sys/types.h>
-#include <sys/stat.h>
 #include <fcntl.h>
 #include <net/if.h>
 #include <netlink/genl/genl.h>
-#include <netlink/genl/family.h>
 #include <netlink/genl/ctrl.h>
 #ifdef CONFIG_LIBNL3_ROUTE
 #include <netlink/route/neighbour.h>
 #endif /* CONFIG_LIBNL3_ROUTE */
 #include <linux/rtnetlink.h>
 #include <netpacket/packet.h>
-#include <linux/filter.h>
 #include <linux/errqueue.h>
-#include "nl80211_copy.h"
 
 #include "common.h"
 #include "eloop.h"
-#include "utils/list.h"
 #include "common/qca-vendor.h"
 #include "common/qca-vendor-attr.h"
 #include "common/ieee802_11_defs.h"
 #include "common/ieee802_11_common.h"
 #include "l2_packet/l2_packet.h"
 #include "netlink.h"
+#include "linux_defines.h"
 #include "linux_ioctl.h"
 #include "radiotap.h"
 #include "radiotap_iter.h"
 #include "rfkill.h"
-#include "driver.h"
+#include "driver_nl80211.h"
 
-#ifndef SO_WIFI_STATUS
-# if defined(__sparc__)
-#  define SO_WIFI_STATUS	0x0025
-# elif defined(__parisc__)
-#  define SO_WIFI_STATUS	0x4022
-# else
-#  define SO_WIFI_STATUS	41
-# endif
 
-# define SCM_WIFI_STATUS	SO_WIFI_STATUS
-#endif
-
-#ifndef SO_EE_ORIGIN_TXSTATUS
-#define SO_EE_ORIGIN_TXSTATUS	4
-#endif
-
-#ifndef PACKET_TX_TIMESTAMP
-#define PACKET_TX_TIMESTAMP	16
-#endif
-
-#ifdef ANDROID
-#include "android_drv.h"
-#endif /* ANDROID */
-#ifdef CONFIG_LIBNL20
-/* libnl 2.0 compatibility code */
-#define nl_handle nl_sock
-#define nl80211_handle_alloc nl_socket_alloc_cb
-#define nl80211_handle_destroy nl_socket_free
-#else
+#ifndef CONFIG_LIBNL20
 /*
  * libnl 1.1 has a bug, it tries to allocate socket numbers densely
  * but when you free a socket again it will mess up its bitmap and
@@ -116,12 +84,9 @@
 
 #ifdef ANDROID
 /* system/core/libnl_2 does not include nl_socket_set_nonblocking() */
-static int android_nl_socket_set_nonblocking(struct nl_handle *handle)
-{
-	return fcntl(nl_socket_get_fd(handle), F_SETFL, O_NONBLOCK);
-}
 #undef nl_socket_set_nonblocking
 #define nl_socket_set_nonblocking(h) android_nl_socket_set_nonblocking(h)
+
 #endif /* ANDROID */
 
 
@@ -166,6 +131,22 @@
 					eloop_sock_handler handler,
 					void *eloop_data)
 {
+#ifdef CONFIG_LIBNL20
+	/*
+	 * libnl uses a pretty small buffer (32 kB that gets converted to 64 kB)
+	 * by default. It is possible to hit that limit in some cases where
+	 * operations are blocked, e.g., with a burst of Deauthentication frames
+	 * to hostapd and STA entry deletion. Try to increase the buffer to make
+	 * this less likely to occur.
+	 */
+	if (nl_socket_set_buffer_size(*handle, 262144, 0) < 0) {
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: Could not set nl_socket RX buffer size: %s",
+			   strerror(errno));
+		/* continue anyway with the default (smaller) buffer */
+	}
+#endif /* CONFIG_LIBNL20 */
+
 	nl_socket_set_nonblocking(*handle);
 	eloop_register_read_sock(nl_socket_get_fd(*handle), handler,
 				 eloop_data, *handle);
@@ -181,374 +162,42 @@
 }
 
 
-#ifndef IFF_LOWER_UP
-#define IFF_LOWER_UP   0x10000         /* driver signals L1 up         */
-#endif
-#ifndef IFF_DORMANT
-#define IFF_DORMANT    0x20000         /* driver signals dormant       */
-#endif
-
-#ifndef IF_OPER_DORMANT
-#define IF_OPER_DORMANT 5
-#endif
-#ifndef IF_OPER_UP
-#define IF_OPER_UP 6
-#endif
-
-struct nl80211_global {
-	struct dl_list interfaces;
-	int if_add_ifindex;
-	u64 if_add_wdevid;
-	int if_add_wdevid_set;
-	struct netlink_data *netlink;
-	struct nl_cb *nl_cb;
-	struct nl_handle *nl;
-	int nl80211_id;
-	int ioctl_sock; /* socket for ioctl() use */
-
-	struct nl_handle *nl_event;
-};
-
-struct nl80211_wiphy_data {
-	struct dl_list list;
-	struct dl_list bsss;
-	struct dl_list drvs;
-
-	struct nl_handle *nl_beacons;
-	struct nl_cb *nl_cb;
-
-	int wiphy_idx;
-};
-
 static void nl80211_global_deinit(void *priv);
-
-struct i802_bss {
-	struct wpa_driver_nl80211_data *drv;
-	struct i802_bss *next;
-	int ifindex;
-	u64 wdev_id;
-	char ifname[IFNAMSIZ + 1];
-	char brname[IFNAMSIZ];
-	unsigned int beacon_set:1;
-	unsigned int added_if_into_bridge:1;
-	unsigned int added_bridge:1;
-	unsigned int in_deinit:1;
-	unsigned int wdev_id_set:1;
-	unsigned int added_if:1;
-	unsigned int static_ap:1;
-
-	u8 addr[ETH_ALEN];
-
-	int freq;
-	int bandwidth;
-	int if_dynamic;
-
-	void *ctx;
-	struct nl_handle *nl_preq, *nl_mgmt;
-	struct nl_cb *nl_cb;
-
-	struct nl80211_wiphy_data *wiphy_data;
-	struct dl_list wiphy_list;
-};
-
-struct wpa_driver_nl80211_data {
-	struct nl80211_global *global;
-	struct dl_list list;
-	struct dl_list wiphy_list;
-	char phyname[32];
-	u8 perm_addr[ETH_ALEN];
-	void *ctx;
-	int ifindex;
-	int if_removed;
-	int if_disabled;
-	int ignore_if_down_event;
-	struct rfkill_data *rfkill;
-	struct wpa_driver_capa capa;
-	u8 *extended_capa, *extended_capa_mask;
-	unsigned int extended_capa_len;
-	int has_capability;
-
-	int operstate;
-
-	int scan_complete_events;
-	enum scan_states {
-		NO_SCAN, SCAN_REQUESTED, SCAN_STARTED, SCAN_COMPLETED,
-		SCAN_ABORTED, SCHED_SCAN_STARTED, SCHED_SCAN_STOPPED,
-		SCHED_SCAN_RESULTS
-	} scan_state;
-
-	struct nl_cb *nl_cb;
-
-	u8 auth_bssid[ETH_ALEN];
-	u8 auth_attempt_bssid[ETH_ALEN];
-	u8 bssid[ETH_ALEN];
-	u8 prev_bssid[ETH_ALEN];
-	int associated;
-	u8 ssid[32];
-	size_t ssid_len;
-	enum nl80211_iftype nlmode;
-	enum nl80211_iftype ap_scan_as_station;
-	unsigned int assoc_freq;
-
-	int monitor_sock;
-	int monitor_ifidx;
-	int monitor_refcount;
-
-	unsigned int disabled_11b_rates:1;
-	unsigned int pending_remain_on_chan:1;
-	unsigned int in_interface_list:1;
-	unsigned int device_ap_sme:1;
-	unsigned int poll_command_supported:1;
-	unsigned int data_tx_status:1;
-	unsigned int scan_for_auth:1;
-	unsigned int retry_auth:1;
-	unsigned int use_monitor:1;
-	unsigned int ignore_next_local_disconnect:1;
-	unsigned int ignore_next_local_deauth:1;
-	unsigned int allow_p2p_device:1;
-	unsigned int hostapd:1;
-	unsigned int start_mode_ap:1;
-	unsigned int start_iface_up:1;
-	unsigned int test_use_roc_tx:1;
-	unsigned int ignore_deauth_event:1;
-	unsigned int roaming_vendor_cmd_avail:1;
-	unsigned int dfs_vendor_cmd_avail:1;
-	unsigned int have_low_prio_scan:1;
-	unsigned int force_connect_cmd:1;
-	unsigned int addr_changed:1;
-
-	u64 remain_on_chan_cookie;
-	u64 send_action_cookie;
-
-	unsigned int last_mgmt_freq;
-
-	struct wpa_driver_scan_filter *filter_ssids;
-	size_t num_filter_ssids;
-
-	struct i802_bss *first_bss;
-
-	int eapol_tx_sock;
-
-	int eapol_sock; /* socket for EAPOL frames */
-
-	struct nl_handle *rtnl_sk; /* nl_sock for NETLINK_ROUTE */
-
-	int default_if_indices[16];
-	int *if_indices;
-	int num_if_indices;
-
-	/* From failed authentication command */
-	int auth_freq;
-	u8 auth_bssid_[ETH_ALEN];
-	u8 auth_ssid[32];
-	size_t auth_ssid_len;
-	int auth_alg;
-	u8 *auth_ie;
-	size_t auth_ie_len;
-	u8 auth_wep_key[4][16];
-	size_t auth_wep_key_len[4];
-	int auth_wep_tx_keyidx;
-	int auth_local_state_change;
-	int auth_p2p;
-};
-
+static void nl80211_check_global(struct nl80211_global *global);
 
 static void wpa_driver_nl80211_deinit(struct i802_bss *bss);
-static void wpa_driver_nl80211_scan_timeout(void *eloop_ctx,
-					    void *timeout_ctx);
-static int wpa_driver_nl80211_set_mode(struct i802_bss *bss,
-				       enum nl80211_iftype nlmode);
 static int wpa_driver_nl80211_set_mode_ibss(struct i802_bss *bss,
 					    struct hostapd_freq_params *freq);
 
 static int
 wpa_driver_nl80211_finish_drv_init(struct wpa_driver_nl80211_data *drv,
-				   const u8 *set_addr, int first);
-static int wpa_driver_nl80211_mlme(struct wpa_driver_nl80211_data *drv,
-				   const u8 *addr, int cmd, u16 reason_code,
-				   int local_state_change);
-static void nl80211_remove_monitor_interface(
-	struct wpa_driver_nl80211_data *drv);
+				   const u8 *set_addr, int first,
+				   const char *driver_params);
 static int nl80211_send_frame_cmd(struct i802_bss *bss,
 				  unsigned int freq, unsigned int wait,
 				  const u8 *buf, size_t buf_len, u64 *cookie,
 				  int no_cck, int no_ack, int offchanok);
-static int nl80211_register_frame(struct i802_bss *bss,
-				  struct nl_handle *hl_handle,
-				  u16 type, const u8 *match, size_t match_len);
 static int wpa_driver_nl80211_probe_req_report(struct i802_bss *bss,
 					       int report);
-#ifdef ANDROID
-static int android_pno_start(struct i802_bss *bss,
-			     struct wpa_driver_scan_params *params);
-static int android_pno_stop(struct i802_bss *bss);
-extern int wpa_driver_nl80211_driver_cmd(void *priv, char *cmd, char *buf,
-					 size_t buf_len);
-#endif /* ANDROID */
-#ifdef ANDROID_P2P
-#ifdef ANDROID_P2P_STUB
-int wpa_driver_set_p2p_noa(void *priv, u8 count, int start, int duration) {
-	return 0;
-}
-int wpa_driver_get_p2p_noa(void *priv, u8 *buf, size_t len) {
-	return 0;
-}
-int wpa_driver_set_p2p_ps(void *priv, int legacy_ps, int opp_ps, int ctwindow) {
-	return -1;
-}
-int wpa_driver_set_ap_wps_p2p_ie(void *priv, const struct wpabuf *beacon,
-				 const struct wpabuf *proberesp,
-				 const struct wpabuf *assocresp) {
-	return 0;
-}
-#else /* ANDROID_P2P_STUB */
-int wpa_driver_set_p2p_noa(void *priv, u8 count, int start, int duration);
-int wpa_driver_get_p2p_noa(void *priv, u8 *buf, size_t len);
-int wpa_driver_set_p2p_ps(void *priv, int legacy_ps, int opp_ps, int ctwindow);
-int wpa_driver_set_ap_wps_p2p_ie(void *priv, const struct wpabuf *beacon,
-				 const struct wpabuf *proberesp,
-				 const struct wpabuf *assocresp);
-#endif /* ANDROID_P2P_STUB */
-#endif /* ANDROID_P2P */
 
 static void add_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx);
 static void del_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx);
 static int have_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx);
-static int wpa_driver_nl80211_if_remove(struct i802_bss *bss,
-					enum wpa_driver_if_type type,
-					const char *ifname);
 
 static int nl80211_set_channel(struct i802_bss *bss,
 			       struct hostapd_freq_params *freq, int set_chan);
 static int nl80211_disable_11b_rates(struct wpa_driver_nl80211_data *drv,
 				     int ifindex, int disabled);
 
-static int nl80211_leave_ibss(struct wpa_driver_nl80211_data *drv);
-static int wpa_driver_nl80211_authenticate_retry(
-	struct wpa_driver_nl80211_data *drv);
+static int nl80211_leave_ibss(struct wpa_driver_nl80211_data *drv,
+			      int reset_mode);
 
-static int i802_set_freq(void *priv, struct hostapd_freq_params *freq);
 static int i802_set_iface_flags(struct i802_bss *bss, int up);
-
-
-static const char * nl80211_command_to_string(enum nl80211_commands cmd)
-{
-#define C2S(x) case x: return #x;
-	switch (cmd) {
-	C2S(NL80211_CMD_UNSPEC)
-	C2S(NL80211_CMD_GET_WIPHY)
-	C2S(NL80211_CMD_SET_WIPHY)
-	C2S(NL80211_CMD_NEW_WIPHY)
-	C2S(NL80211_CMD_DEL_WIPHY)
-	C2S(NL80211_CMD_GET_INTERFACE)
-	C2S(NL80211_CMD_SET_INTERFACE)
-	C2S(NL80211_CMD_NEW_INTERFACE)
-	C2S(NL80211_CMD_DEL_INTERFACE)
-	C2S(NL80211_CMD_GET_KEY)
-	C2S(NL80211_CMD_SET_KEY)
-	C2S(NL80211_CMD_NEW_KEY)
-	C2S(NL80211_CMD_DEL_KEY)
-	C2S(NL80211_CMD_GET_BEACON)
-	C2S(NL80211_CMD_SET_BEACON)
-	C2S(NL80211_CMD_START_AP)
-	C2S(NL80211_CMD_STOP_AP)
-	C2S(NL80211_CMD_GET_STATION)
-	C2S(NL80211_CMD_SET_STATION)
-	C2S(NL80211_CMD_NEW_STATION)
-	C2S(NL80211_CMD_DEL_STATION)
-	C2S(NL80211_CMD_GET_MPATH)
-	C2S(NL80211_CMD_SET_MPATH)
-	C2S(NL80211_CMD_NEW_MPATH)
-	C2S(NL80211_CMD_DEL_MPATH)
-	C2S(NL80211_CMD_SET_BSS)
-	C2S(NL80211_CMD_SET_REG)
-	C2S(NL80211_CMD_REQ_SET_REG)
-	C2S(NL80211_CMD_GET_MESH_CONFIG)
-	C2S(NL80211_CMD_SET_MESH_CONFIG)
-	C2S(NL80211_CMD_SET_MGMT_EXTRA_IE)
-	C2S(NL80211_CMD_GET_REG)
-	C2S(NL80211_CMD_GET_SCAN)
-	C2S(NL80211_CMD_TRIGGER_SCAN)
-	C2S(NL80211_CMD_NEW_SCAN_RESULTS)
-	C2S(NL80211_CMD_SCAN_ABORTED)
-	C2S(NL80211_CMD_REG_CHANGE)
-	C2S(NL80211_CMD_AUTHENTICATE)
-	C2S(NL80211_CMD_ASSOCIATE)
-	C2S(NL80211_CMD_DEAUTHENTICATE)
-	C2S(NL80211_CMD_DISASSOCIATE)
-	C2S(NL80211_CMD_MICHAEL_MIC_FAILURE)
-	C2S(NL80211_CMD_REG_BEACON_HINT)
-	C2S(NL80211_CMD_JOIN_IBSS)
-	C2S(NL80211_CMD_LEAVE_IBSS)
-	C2S(NL80211_CMD_TESTMODE)
-	C2S(NL80211_CMD_CONNECT)
-	C2S(NL80211_CMD_ROAM)
-	C2S(NL80211_CMD_DISCONNECT)
-	C2S(NL80211_CMD_SET_WIPHY_NETNS)
-	C2S(NL80211_CMD_GET_SURVEY)
-	C2S(NL80211_CMD_NEW_SURVEY_RESULTS)
-	C2S(NL80211_CMD_SET_PMKSA)
-	C2S(NL80211_CMD_DEL_PMKSA)
-	C2S(NL80211_CMD_FLUSH_PMKSA)
-	C2S(NL80211_CMD_REMAIN_ON_CHANNEL)
-	C2S(NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL)
-	C2S(NL80211_CMD_SET_TX_BITRATE_MASK)
-	C2S(NL80211_CMD_REGISTER_FRAME)
-	C2S(NL80211_CMD_FRAME)
-	C2S(NL80211_CMD_FRAME_TX_STATUS)
-	C2S(NL80211_CMD_SET_POWER_SAVE)
-	C2S(NL80211_CMD_GET_POWER_SAVE)
-	C2S(NL80211_CMD_SET_CQM)
-	C2S(NL80211_CMD_NOTIFY_CQM)
-	C2S(NL80211_CMD_SET_CHANNEL)
-	C2S(NL80211_CMD_SET_WDS_PEER)
-	C2S(NL80211_CMD_FRAME_WAIT_CANCEL)
-	C2S(NL80211_CMD_JOIN_MESH)
-	C2S(NL80211_CMD_LEAVE_MESH)
-	C2S(NL80211_CMD_UNPROT_DEAUTHENTICATE)
-	C2S(NL80211_CMD_UNPROT_DISASSOCIATE)
-	C2S(NL80211_CMD_NEW_PEER_CANDIDATE)
-	C2S(NL80211_CMD_GET_WOWLAN)
-	C2S(NL80211_CMD_SET_WOWLAN)
-	C2S(NL80211_CMD_START_SCHED_SCAN)
-	C2S(NL80211_CMD_STOP_SCHED_SCAN)
-	C2S(NL80211_CMD_SCHED_SCAN_RESULTS)
-	C2S(NL80211_CMD_SCHED_SCAN_STOPPED)
-	C2S(NL80211_CMD_SET_REKEY_OFFLOAD)
-	C2S(NL80211_CMD_PMKSA_CANDIDATE)
-	C2S(NL80211_CMD_TDLS_OPER)
-	C2S(NL80211_CMD_TDLS_MGMT)
-	C2S(NL80211_CMD_UNEXPECTED_FRAME)
-	C2S(NL80211_CMD_PROBE_CLIENT)
-	C2S(NL80211_CMD_REGISTER_BEACONS)
-	C2S(NL80211_CMD_UNEXPECTED_4ADDR_FRAME)
-	C2S(NL80211_CMD_SET_NOACK_MAP)
-	C2S(NL80211_CMD_CH_SWITCH_NOTIFY)
-	C2S(NL80211_CMD_START_P2P_DEVICE)
-	C2S(NL80211_CMD_STOP_P2P_DEVICE)
-	C2S(NL80211_CMD_CONN_FAILED)
-	C2S(NL80211_CMD_SET_MCAST_RATE)
-	C2S(NL80211_CMD_SET_MAC_ACL)
-	C2S(NL80211_CMD_RADAR_DETECT)
-	C2S(NL80211_CMD_GET_PROTOCOL_FEATURES)
-	C2S(NL80211_CMD_UPDATE_FT_IES)
-	C2S(NL80211_CMD_FT_EVENT)
-	C2S(NL80211_CMD_CRIT_PROTOCOL_START)
-	C2S(NL80211_CMD_CRIT_PROTOCOL_STOP)
-	C2S(NL80211_CMD_GET_COALESCE)
-	C2S(NL80211_CMD_SET_COALESCE)
-	C2S(NL80211_CMD_CHANNEL_SWITCH)
-	C2S(NL80211_CMD_VENDOR)
-	C2S(NL80211_CMD_SET_QOS_MAP)
-	default:
-		return "NL80211_CMD_UNKNOWN";
-	}
-#undef C2S
-}
+static int nl80211_set_param(void *priv, const char *param);
 
 
 /* Converts nl80211_chan_width to a common format */
-static enum chan_width convert2width(int width)
+enum chan_width convert2width(int width)
 {
 	switch (width) {
 	case NL80211_CHAN_WIDTH_20_NOHT:
@@ -568,14 +217,14 @@
 }
 
 
-static int is_ap_interface(enum nl80211_iftype nlmode)
+int is_ap_interface(enum nl80211_iftype nlmode)
 {
 	return nlmode == NL80211_IFTYPE_AP ||
 		nlmode == NL80211_IFTYPE_P2P_GO;
 }
 
 
-static int is_sta_interface(enum nl80211_iftype nlmode)
+int is_sta_interface(enum nl80211_iftype nlmode)
 {
 	return nlmode == NL80211_IFTYPE_STATION ||
 		nlmode == NL80211_IFTYPE_P2P_CLIENT;
@@ -589,8 +238,8 @@
 }
 
 
-static struct i802_bss * get_bss_ifindex(struct wpa_driver_nl80211_data *drv,
-					 int ifindex)
+struct i802_bss * get_bss_ifindex(struct wpa_driver_nl80211_data *drv,
+				  int ifindex)
 {
 	struct i802_bss *bss;
 
@@ -603,7 +252,13 @@
 }
 
 
-static void nl80211_mark_disconnected(struct wpa_driver_nl80211_data *drv)
+static int is_mesh_interface(enum nl80211_iftype nlmode)
+{
+	return nlmode == NL80211_IFTYPE_MESH_POINT;
+}
+
+
+void nl80211_mark_disconnected(struct wpa_driver_nl80211_data *drv)
 {
 	if (drv->associated)
 		os_memcpy(drv->prev_bssid, drv->bssid, ETH_ALEN);
@@ -612,17 +267,6 @@
 }
 
 
-struct nl80211_bss_info_arg {
-	struct wpa_driver_nl80211_data *drv;
-	struct wpa_scan_results *res;
-	unsigned int assoc_freq;
-	unsigned int ibss_freq;
-	u8 assoc_bssid[ETH_ALEN];
-};
-
-static int bss_info_handler(struct nl_msg *msg, void *arg);
-
-
 /* nl80211 code */
 static int ack_handler(struct nl_msg *msg, void *arg)
 {
@@ -653,6 +297,28 @@
 }
 
 
+static void nl80211_nlmsg_clear(struct nl_msg *msg)
+{
+	/*
+	 * Clear nlmsg data, e.g., to make sure key material is not left in
+	 * heap memory for unnecessarily long time.
+	 */
+	if (msg) {
+		struct nlmsghdr *hdr = nlmsg_hdr(msg);
+		void *data = nlmsg_data(hdr);
+		/*
+		 * This would use nlmsg_datalen() or the older nlmsg_len() if
+		 * only libnl were to maintain a stable API.. Neither will work
+		 * with all released versions, so just calculate the length
+		 * here.
+		 */
+		int len = hdr->nlmsg_len - NLMSG_HDRLEN;
+
+		os_memset(data, 0, len);
+	}
+}
+
+
 static int send_and_recv(struct nl80211_global *global,
 			 struct nl_handle *nl_handle, struct nl_msg *msg,
 			 int (*valid_handler)(struct nl_msg *, void *),
@@ -661,6 +327,9 @@
 	struct nl_cb *cb;
 	int err = -ENOMEM;
 
+	if (!msg)
+		return -ENOMEM;
+
 	cb = nl_cb_clone(global->nl_cb);
 	if (!cb)
 		goto out;
@@ -689,25 +358,17 @@
 	}
  out:
 	nl_cb_put(cb);
+	if (!valid_handler && valid_data == (void *) -1)
+		nl80211_nlmsg_clear(msg);
 	nlmsg_free(msg);
 	return err;
 }
 
 
-static int send_and_recv_msgs_global(struct nl80211_global *global,
-				     struct nl_msg *msg,
-				     int (*valid_handler)(struct nl_msg *, void *),
-				     void *valid_data)
-{
-	return send_and_recv(global, global->nl, msg, valid_handler,
-			     valid_data);
-}
-
-
-static int send_and_recv_msgs(struct wpa_driver_nl80211_data *drv,
-			      struct nl_msg *msg,
-			      int (*valid_handler)(struct nl_msg *, void *),
-			      void *valid_data)
+int send_and_recv_msgs(struct wpa_driver_nl80211_data *drv,
+		       struct nl_msg *msg,
+		       int (*valid_handler)(struct nl_msg *, void *),
+		       void *valid_data)
 {
 	return send_and_recv(drv->global, drv->global->nl, msg,
 			     valid_handler, valid_data);
@@ -720,19 +381,6 @@
 };
 
 
-static int nl80211_set_iface_id(struct nl_msg *msg, struct i802_bss *bss)
-{
-	if (bss->wdev_id_set)
-		NLA_PUT_U64(msg, NL80211_ATTR_WDEV, bss->wdev_id);
-	else
-		NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, bss->ifindex);
-	return 0;
-
-nla_put_failure:
-	return -1;
-}
-
-
 static int family_handler(struct nl_msg *msg, void *arg)
 {
 	struct family_data *res = arg;
@@ -768,35 +416,93 @@
 			       const char *family, const char *group)
 {
 	struct nl_msg *msg;
-	int ret = -1;
+	int ret;
 	struct family_data res = { group, -ENOENT };
 
 	msg = nlmsg_alloc();
 	if (!msg)
 		return -ENOMEM;
-	genlmsg_put(msg, 0, 0, genl_ctrl_resolve(global->nl, "nlctrl"),
-		    0, 0, CTRL_CMD_GETFAMILY, 0);
-	NLA_PUT_STRING(msg, CTRL_ATTR_FAMILY_NAME, family);
+	if (!genlmsg_put(msg, 0, 0, genl_ctrl_resolve(global->nl, "nlctrl"),
+			 0, 0, CTRL_CMD_GETFAMILY, 0) ||
+	    nla_put_string(msg, CTRL_ATTR_FAMILY_NAME, family)) {
+		nlmsg_free(msg);
+		return -1;
+	}
 
-	ret = send_and_recv_msgs_global(global, msg, family_handler, &res);
-	msg = NULL;
+	ret = send_and_recv(global, global->nl, msg, family_handler, &res);
 	if (ret == 0)
 		ret = res.id;
-
-nla_put_failure:
-	nlmsg_free(msg);
 	return ret;
 }
 
 
-static void * nl80211_cmd(struct wpa_driver_nl80211_data *drv,
-			  struct nl_msg *msg, int flags, uint8_t cmd)
+void * nl80211_cmd(struct wpa_driver_nl80211_data *drv,
+		   struct nl_msg *msg, int flags, uint8_t cmd)
 {
 	return genlmsg_put(msg, 0, 0, drv->global->nl80211_id,
 			   0, flags, cmd, 0);
 }
 
 
+static int nl80211_set_iface_id(struct nl_msg *msg, struct i802_bss *bss)
+{
+	if (bss->wdev_id_set)
+		return nla_put_u64(msg, NL80211_ATTR_WDEV, bss->wdev_id);
+	return nla_put_u32(msg, NL80211_ATTR_IFINDEX, bss->ifindex);
+}
+
+
+struct nl_msg * nl80211_cmd_msg(struct i802_bss *bss, int flags, uint8_t cmd)
+{
+	struct nl_msg *msg;
+
+	msg = nlmsg_alloc();
+	if (!msg)
+		return NULL;
+
+	if (!nl80211_cmd(bss->drv, msg, flags, cmd) ||
+	    nl80211_set_iface_id(msg, bss) < 0) {
+		nlmsg_free(msg);
+		return NULL;
+	}
+
+	return msg;
+}
+
+
+static struct nl_msg *
+nl80211_ifindex_msg(struct wpa_driver_nl80211_data *drv, int ifindex,
+		    int flags, uint8_t cmd)
+{
+	struct nl_msg *msg;
+
+	msg = nlmsg_alloc();
+	if (!msg)
+		return NULL;
+
+	if (!nl80211_cmd(drv, msg, flags, cmd) ||
+	    nla_put_u32(msg, NL80211_ATTR_IFINDEX, ifindex)) {
+		nlmsg_free(msg);
+		return NULL;
+	}
+
+	return msg;
+}
+
+
+struct nl_msg * nl80211_drv_msg(struct wpa_driver_nl80211_data *drv, int flags,
+				uint8_t cmd)
+{
+	return nl80211_ifindex_msg(drv, drv->ifindex, flags, cmd);
+}
+
+
+struct nl_msg * nl80211_bss_msg(struct i802_bss *bss, int flags, uint8_t cmd)
+{
+	return nl80211_ifindex_msg(bss->drv, bss->ifindex, flags, cmd);
+}
+
+
 struct wiphy_idx_data {
 	int wiphy_idx;
 	enum nl80211_iftype nlmode;
@@ -827,7 +533,7 @@
 }
 
 
-static int nl80211_get_wiphy_index(struct i802_bss *bss)
+int nl80211_get_wiphy_index(struct i802_bss *bss)
 {
 	struct nl_msg *msg;
 	struct wiphy_idx_data data = {
@@ -835,20 +541,11 @@
 		.macaddr = NULL,
 	};
 
-	msg = nlmsg_alloc();
-	if (!msg)
-		return NL80211_IFTYPE_UNSPECIFIED;
-
-	nl80211_cmd(bss->drv, msg, 0, NL80211_CMD_GET_INTERFACE);
-
-	if (nl80211_set_iface_id(msg, bss) < 0)
-		goto nla_put_failure;
+	if (!(msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_GET_INTERFACE)))
+		return -1;
 
 	if (send_and_recv_msgs(bss->drv, msg, netdev_info_handler, &data) == 0)
 		return data.wiphy_idx;
-	msg = NULL;
-nla_put_failure:
-	nlmsg_free(msg);
 	return -1;
 }
 
@@ -861,20 +558,11 @@
 		.macaddr = NULL,
 	};
 
-	msg = nlmsg_alloc();
-	if (!msg)
-		return -1;
-
-	nl80211_cmd(bss->drv, msg, 0, NL80211_CMD_GET_INTERFACE);
-
-	if (nl80211_set_iface_id(msg, bss) < 0)
-		goto nla_put_failure;
+	if (!(msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_GET_INTERFACE)))
+		return NL80211_IFTYPE_UNSPECIFIED;
 
 	if (send_and_recv_msgs(bss->drv, msg, netdev_info_handler, &data) == 0)
 		return data.nlmode;
-	msg = NULL;
-nla_put_failure:
-	nlmsg_free(msg);
 	return NL80211_IFTYPE_UNSPECIFIED;
 }
 
@@ -886,19 +574,10 @@
 		.macaddr = bss->addr,
 	};
 
-	msg = nlmsg_alloc();
-	if (!msg)
-		return NL80211_IFTYPE_UNSPECIFIED;
-
-	nl80211_cmd(bss->drv, msg, 0, NL80211_CMD_GET_INTERFACE);
-	if (nl80211_set_iface_id(msg, bss) < 0)
-		goto nla_put_failure;
+	if (!(msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_GET_INTERFACE)))
+		return -1;
 
 	return send_and_recv_msgs(bss->drv, msg, netdev_info_handler, &data);
-
-nla_put_failure:
-	nlmsg_free(msg);
-	return NL80211_IFTYPE_UNSPECIFIED;
 }
 
 
@@ -906,27 +585,24 @@
 				    struct nl80211_wiphy_data *w)
 {
 	struct nl_msg *msg;
-	int ret = -1;
+	int ret;
 
 	msg = nlmsg_alloc();
 	if (!msg)
 		return -1;
 
-	nl80211_cmd(drv, msg, 0, NL80211_CMD_REGISTER_BEACONS);
-
-	NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, w->wiphy_idx);
+	if (!nl80211_cmd(drv, msg, 0, NL80211_CMD_REGISTER_BEACONS) ||
+	    nla_put_u32(msg, NL80211_ATTR_WIPHY, w->wiphy_idx)) {
+		nlmsg_free(msg);
+		return -1;
+	}
 
 	ret = send_and_recv(drv->global, w->nl_beacons, msg, NULL, NULL);
-	msg = NULL;
 	if (ret) {
 		wpa_printf(MSG_DEBUG, "nl80211: Register beacons command "
 			   "failed: ret=%d (%s)",
 			   ret, strerror(-ret));
-		goto nla_put_failure;
 	}
-	ret = 0;
-nla_put_failure:
-	nlmsg_free(msg);
 	return ret;
 }
 
@@ -1104,7 +780,7 @@
 
 
 static void wpa_driver_nl80211_event_newlink(
-	struct wpa_driver_nl80211_data *drv, char *ifname)
+	struct wpa_driver_nl80211_data *drv, const char *ifname)
 {
 	union wpa_event_data event;
 
@@ -1130,7 +806,7 @@
 
 
 static void wpa_driver_nl80211_event_dellink(
-	struct wpa_driver_nl80211_data *drv, char *ifname)
+	struct wpa_driver_nl80211_data *drv, const char *ifname)
 {
 	union wpa_event_data event;
 
@@ -1188,9 +864,10 @@
 		return 1;
 
 	if (drv->if_removed && wpa_driver_nl80211_own_ifname(drv, buf, len)) {
+		nl80211_check_global(drv->global);
 		wpa_printf(MSG_DEBUG, "nl80211: Update ifindex for a removed "
 			   "interface");
-		wpa_driver_nl80211_finish_drv_init(drv, NULL, 0);
+		wpa_driver_nl80211_finish_drv_init(drv, NULL, 0, NULL);
 		return 1;
 	}
 
@@ -1276,15 +953,21 @@
 		   (ifi->ifi_flags & IFF_DORMANT) ? "[DORMANT]" : "");
 
 	if (!drv->if_disabled && !(ifi->ifi_flags & IFF_UP)) {
+		namebuf[0] = '\0';
 		if (if_indextoname(ifi->ifi_index, namebuf) &&
-		    linux_iface_up(drv->global->ioctl_sock,
-				   drv->first_bss->ifname) > 0) {
+		    linux_iface_up(drv->global->ioctl_sock, namebuf) > 0) {
 			wpa_printf(MSG_DEBUG, "nl80211: Ignore interface down "
 				   "event since interface %s is up", namebuf);
+			drv->ignore_if_down_event = 0;
 			return;
 		}
-		wpa_printf(MSG_DEBUG, "nl80211: Interface down");
-		if (drv->ignore_if_down_event) {
+		wpa_printf(MSG_DEBUG, "nl80211: Interface down (%s/%s)",
+			   namebuf, ifname);
+		if (os_strcmp(drv->first_bss->ifname, ifname) != 0) {
+			wpa_printf(MSG_DEBUG,
+				   "nl80211: Not the main interface (%s) - do not indicate interface down",
+				   drv->first_bss->ifname);
+		} else if (drv->ignore_if_down_event) {
 			wpa_printf(MSG_DEBUG, "nl80211: Ignore interface down "
 				   "event generated by mode change");
 			drv->ignore_if_down_event = 0;
@@ -1307,8 +990,7 @@
 
 	if (drv->if_disabled && (ifi->ifi_flags & IFF_UP)) {
 		if (if_indextoname(ifi->ifi_index, namebuf) &&
-		    linux_iface_up(drv->global->ioctl_sock,
-				   drv->first_bss->ifname) == 0) {
+		    linux_iface_up(drv->global->ioctl_sock, namebuf) == 0) {
 			wpa_printf(MSG_DEBUG, "nl80211: Ignore interface up "
 				   "event since interface %s is down",
 				   namebuf);
@@ -1368,11 +1050,25 @@
 		wpa_driver_nl80211_event_newlink(drv, ifname);
 
 	if (ifi->ifi_family == AF_BRIDGE && brid) {
+		struct i802_bss *bss;
+
 		/* device has been added to bridge */
-		if_indextoname(brid, namebuf);
+		if (!if_indextoname(brid, namebuf)) {
+			wpa_printf(MSG_DEBUG,
+				   "nl80211: Could not find bridge ifname for ifindex %u",
+				   brid);
+			return;
+		}
 		wpa_printf(MSG_DEBUG, "nl80211: Add ifindex %u for bridge %s",
 			   brid, namebuf);
 		add_ifidx(drv, brid);
+
+		for (bss = drv->first_bss; bss; bss = bss->next) {
+			if (os_strcmp(ifname, bss->ifname) == 0) {
+				os_strlcpy(bss->brname, namebuf, IFNAMSIZ);
+				break;
+			}
+		}
 	}
 }
 
@@ -1442,73 +1138,31 @@
 	if (ifi->ifi_family == AF_BRIDGE && brid) {
 		/* device has been removed from bridge */
 		char namebuf[IFNAMSIZ];
-		if_indextoname(brid, namebuf);
-		wpa_printf(MSG_DEBUG, "nl80211: Remove ifindex %u for bridge "
-			   "%s", brid, namebuf);
+
+		if (!if_indextoname(brid, namebuf)) {
+			wpa_printf(MSG_DEBUG,
+				   "nl80211: Could not find bridge ifname for ifindex %u",
+				   brid);
+		} else {
+			wpa_printf(MSG_DEBUG,
+				   "nl80211: Remove ifindex %u for bridge %s",
+				   brid, namebuf);
+		}
 		del_ifidx(drv, brid);
 	}
 }
 
 
-static void mlme_event_auth(struct wpa_driver_nl80211_data *drv,
-			    const u8 *frame, size_t len)
-{
-	const struct ieee80211_mgmt *mgmt;
-	union wpa_event_data event;
-
-	if (!(drv->capa.flags & WPA_DRIVER_FLAGS_SME) &&
-	    drv->force_connect_cmd) {
-		/*
-		 * Avoid reporting two association events that would confuse
-		 * the core code.
-		 */
-		wpa_printf(MSG_DEBUG,
-			   "nl80211: Ignore auth event when using driver SME");
-		return;
-	}
-
-	wpa_printf(MSG_DEBUG, "nl80211: Authenticate event");
-	mgmt = (const struct ieee80211_mgmt *) frame;
-	if (len < 24 + sizeof(mgmt->u.auth)) {
-		wpa_printf(MSG_DEBUG, "nl80211: Too short association event "
-			   "frame");
-		return;
-	}
-
-	os_memcpy(drv->auth_bssid, mgmt->sa, ETH_ALEN);
-	os_memset(drv->auth_attempt_bssid, 0, ETH_ALEN);
-	os_memset(&event, 0, sizeof(event));
-	os_memcpy(event.auth.peer, mgmt->sa, ETH_ALEN);
-	event.auth.auth_type = le_to_host16(mgmt->u.auth.auth_alg);
-	event.auth.auth_transaction =
-		le_to_host16(mgmt->u.auth.auth_transaction);
-	event.auth.status_code = le_to_host16(mgmt->u.auth.status_code);
-	if (len > 24 + sizeof(mgmt->u.auth)) {
-		event.auth.ies = mgmt->u.auth.variable;
-		event.auth.ies_len = len - 24 - sizeof(mgmt->u.auth);
-	}
-
-	wpa_supplicant_event(drv->ctx, EVENT_AUTH, &event);
-}
-
-
-static unsigned int nl80211_get_assoc_freq(struct wpa_driver_nl80211_data *drv)
+unsigned int nl80211_get_assoc_freq(struct wpa_driver_nl80211_data *drv)
 {
 	struct nl_msg *msg;
 	int ret;
 	struct nl80211_bss_info_arg arg;
 
+	msg = nl80211_drv_msg(drv, NLM_F_DUMP, NL80211_CMD_GET_SCAN);
 	os_memset(&arg, 0, sizeof(arg));
-	msg = nlmsg_alloc();
-	if (!msg)
-		goto nla_put_failure;
-
-	nl80211_cmd(drv, msg, NLM_F_DUMP, NL80211_CMD_GET_SCAN);
-	NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
-
 	arg.drv = drv;
 	ret = send_and_recv_msgs(drv, msg, bss_info_handler, &arg);
-	msg = NULL;
 	if (ret == 0) {
 		unsigned int freq = drv->nlmode == NL80211_IFTYPE_ADHOC ?
 			arg.ibss_freq : arg.assoc_freq;
@@ -1520,804 +1174,10 @@
 	}
 	wpa_printf(MSG_DEBUG, "nl80211: Scan result fetch failed: ret=%d "
 		   "(%s)", ret, strerror(-ret));
-nla_put_failure:
-	nlmsg_free(msg);
 	return drv->assoc_freq;
 }
 
 
-static void mlme_event_assoc(struct wpa_driver_nl80211_data *drv,
-			    const u8 *frame, size_t len)
-{
-	const struct ieee80211_mgmt *mgmt;
-	union wpa_event_data event;
-	u16 status;
-
-	if (!(drv->capa.flags & WPA_DRIVER_FLAGS_SME) &&
-	    drv->force_connect_cmd) {
-		/*
-		 * Avoid reporting two association events that would confuse
-		 * the core code.
-		 */
-		wpa_printf(MSG_DEBUG,
-			   "nl80211: Ignore assoc event when using driver SME");
-		return;
-	}
-
-	wpa_printf(MSG_DEBUG, "nl80211: Associate event");
-	mgmt = (const struct ieee80211_mgmt *) frame;
-	if (len < 24 + sizeof(mgmt->u.assoc_resp)) {
-		wpa_printf(MSG_DEBUG, "nl80211: Too short association event "
-			   "frame");
-		return;
-	}
-
-	status = le_to_host16(mgmt->u.assoc_resp.status_code);
-	if (status != WLAN_STATUS_SUCCESS) {
-		os_memset(&event, 0, sizeof(event));
-		event.assoc_reject.bssid = mgmt->bssid;
-		if (len > 24 + sizeof(mgmt->u.assoc_resp)) {
-			event.assoc_reject.resp_ies =
-				(u8 *) mgmt->u.assoc_resp.variable;
-			event.assoc_reject.resp_ies_len =
-				len - 24 - sizeof(mgmt->u.assoc_resp);
-		}
-		event.assoc_reject.status_code = status;
-
-		wpa_supplicant_event(drv->ctx, EVENT_ASSOC_REJECT, &event);
-		return;
-	}
-
-	drv->associated = 1;
-	os_memcpy(drv->bssid, mgmt->sa, ETH_ALEN);
-	os_memcpy(drv->prev_bssid, mgmt->sa, ETH_ALEN);
-
-	os_memset(&event, 0, sizeof(event));
-	if (len > 24 + sizeof(mgmt->u.assoc_resp)) {
-		event.assoc_info.resp_ies = (u8 *) mgmt->u.assoc_resp.variable;
-		event.assoc_info.resp_ies_len =
-			len - 24 - sizeof(mgmt->u.assoc_resp);
-	}
-
-	event.assoc_info.freq = drv->assoc_freq;
-
-	wpa_supplicant_event(drv->ctx, EVENT_ASSOC, &event);
-}
-
-
-static void mlme_event_connect(struct wpa_driver_nl80211_data *drv,
-			       enum nl80211_commands cmd, struct nlattr *status,
-			       struct nlattr *addr, struct nlattr *req_ie,
-			       struct nlattr *resp_ie)
-{
-	union wpa_event_data event;
-	u16 status_code;
-
-	if (drv->capa.flags & WPA_DRIVER_FLAGS_SME) {
-		/*
-		 * Avoid reporting two association events that would confuse
-		 * the core code.
-		 */
-		wpa_printf(MSG_DEBUG, "nl80211: Ignore connect event (cmd=%d) "
-			   "when using userspace SME", cmd);
-		return;
-	}
-
-	status_code = status ? nla_get_u16(status) : WLAN_STATUS_SUCCESS;
-
-	if (cmd == NL80211_CMD_CONNECT) {
-		wpa_printf(MSG_DEBUG,
-			   "nl80211: Connect event (status=%u ignore_next_local_disconnect=%d)",
-			   status_code, drv->ignore_next_local_disconnect);
-	} else if (cmd == NL80211_CMD_ROAM) {
-		wpa_printf(MSG_DEBUG, "nl80211: Roam event");
-	}
-
-	os_memset(&event, 0, sizeof(event));
-	if (cmd == NL80211_CMD_CONNECT && status_code != WLAN_STATUS_SUCCESS) {
-		if (addr)
-			event.assoc_reject.bssid = nla_data(addr);
-		if (drv->ignore_next_local_disconnect) {
-			drv->ignore_next_local_disconnect = 0;
-			if (!event.assoc_reject.bssid ||
-			    (os_memcmp(event.assoc_reject.bssid,
-				       drv->auth_attempt_bssid,
-				       ETH_ALEN) != 0)) {
-				/*
-				 * Ignore the event that came without a BSSID or
-				 * for the old connection since this is likely
-				 * not relevant to the new Connect command.
-				 */
-				wpa_printf(MSG_DEBUG,
-					   "nl80211: Ignore connection failure event triggered during reassociation");
-				return;
-			}
-		}
-		if (resp_ie) {
-			event.assoc_reject.resp_ies = nla_data(resp_ie);
-			event.assoc_reject.resp_ies_len = nla_len(resp_ie);
-		}
-		event.assoc_reject.status_code = status_code;
-		wpa_supplicant_event(drv->ctx, EVENT_ASSOC_REJECT, &event);
-		return;
-	}
-
-	drv->associated = 1;
-	if (addr) {
-		os_memcpy(drv->bssid, nla_data(addr), ETH_ALEN);
-		os_memcpy(drv->prev_bssid, drv->bssid, ETH_ALEN);
-	}
-
-	if (req_ie) {
-		event.assoc_info.req_ies = nla_data(req_ie);
-		event.assoc_info.req_ies_len = nla_len(req_ie);
-	}
-	if (resp_ie) {
-		event.assoc_info.resp_ies = nla_data(resp_ie);
-		event.assoc_info.resp_ies_len = nla_len(resp_ie);
-	}
-
-	event.assoc_info.freq = nl80211_get_assoc_freq(drv);
-
-	wpa_supplicant_event(drv->ctx, EVENT_ASSOC, &event);
-}
-
-
-static void mlme_event_disconnect(struct wpa_driver_nl80211_data *drv,
-				  struct nlattr *reason, struct nlattr *addr,
-				  struct nlattr *by_ap)
-{
-	union wpa_event_data data;
-	unsigned int locally_generated = by_ap == NULL;
-
-	if (drv->capa.flags & WPA_DRIVER_FLAGS_SME) {
-		/*
-		 * Avoid reporting two disassociation events that could
-		 * confuse the core code.
-		 */
-		wpa_printf(MSG_DEBUG, "nl80211: Ignore disconnect "
-			   "event when using userspace SME");
-		return;
-	}
-
-	if (drv->ignore_next_local_disconnect) {
-		drv->ignore_next_local_disconnect = 0;
-		if (locally_generated) {
-			wpa_printf(MSG_DEBUG, "nl80211: Ignore disconnect "
-				   "event triggered during reassociation");
-			return;
-		}
-		wpa_printf(MSG_WARNING, "nl80211: Was expecting local "
-			   "disconnect but got another disconnect "
-			   "event first");
-	}
-
-	wpa_printf(MSG_DEBUG, "nl80211: Disconnect event");
-	nl80211_mark_disconnected(drv);
-	os_memset(&data, 0, sizeof(data));
-	if (reason)
-		data.deauth_info.reason_code = nla_get_u16(reason);
-	data.deauth_info.locally_generated = by_ap == NULL;
-	wpa_supplicant_event(drv->ctx, EVENT_DEAUTH, &data);
-}
-
-
-static int calculate_chan_offset(int width, int freq, int cf1, int cf2)
-{
-	int freq1 = 0;
-
-	switch (convert2width(width)) {
-	case CHAN_WIDTH_20_NOHT:
-	case CHAN_WIDTH_20:
-		return 0;
-	case CHAN_WIDTH_40:
-		freq1 = cf1 - 10;
-		break;
-	case CHAN_WIDTH_80:
-		freq1 = cf1 - 30;
-		break;
-	case CHAN_WIDTH_160:
-		freq1 = cf1 - 70;
-		break;
-	case CHAN_WIDTH_UNKNOWN:
-	case CHAN_WIDTH_80P80:
-		/* FIXME: implement this */
-		return 0;
-	}
-
-	return (abs(freq - freq1) / 20) % 2 == 0 ? 1 : -1;
-}
-
-
-static void mlme_event_ch_switch(struct wpa_driver_nl80211_data *drv,
-				 struct nlattr *ifindex, struct nlattr *freq,
-				 struct nlattr *type, struct nlattr *bw,
-				 struct nlattr *cf1, struct nlattr *cf2)
-{
-	struct i802_bss *bss;
-	union wpa_event_data data;
-	int ht_enabled = 1;
-	int chan_offset = 0;
-	int ifidx;
-
-	wpa_printf(MSG_DEBUG, "nl80211: Channel switch event");
-
-	if (!freq)
-		return;
-
-	ifidx = nla_get_u32(ifindex);
-	bss = get_bss_ifindex(drv, ifidx);
-	if (bss == NULL) {
-		wpa_printf(MSG_WARNING, "nl80211: Unknown ifindex (%d) for channel switch, ignoring",
-			   ifidx);
-		return;
-	}
-
-	if (type) {
-		switch (nla_get_u32(type)) {
-		case NL80211_CHAN_NO_HT:
-			ht_enabled = 0;
-			break;
-		case NL80211_CHAN_HT20:
-			break;
-		case NL80211_CHAN_HT40PLUS:
-			chan_offset = 1;
-			break;
-		case NL80211_CHAN_HT40MINUS:
-			chan_offset = -1;
-			break;
-		}
-	} else if (bw && cf1) {
-		/* This can happen for example with VHT80 ch switch */
-		chan_offset = calculate_chan_offset(nla_get_u32(bw),
-						    nla_get_u32(freq),
-						    nla_get_u32(cf1),
-						    cf2 ? nla_get_u32(cf2) : 0);
-	} else {
-		wpa_printf(MSG_WARNING, "nl80211: Unknown secondary channel information - following channel definition calculations may fail");
-	}
-
-	os_memset(&data, 0, sizeof(data));
-	data.ch_switch.freq = nla_get_u32(freq);
-	data.ch_switch.ht_enabled = ht_enabled;
-	data.ch_switch.ch_offset = chan_offset;
-	if (bw)
-		data.ch_switch.ch_width = convert2width(nla_get_u32(bw));
-	if (cf1)
-		data.ch_switch.cf1 = nla_get_u32(cf1);
-	if (cf2)
-		data.ch_switch.cf2 = nla_get_u32(cf2);
-
-	bss->freq = data.ch_switch.freq;
-
-	wpa_supplicant_event(bss->ctx, EVENT_CH_SWITCH, &data);
-}
-
-
-static void mlme_timeout_event(struct wpa_driver_nl80211_data *drv,
-			       enum nl80211_commands cmd, struct nlattr *addr)
-{
-	union wpa_event_data event;
-	enum wpa_event_type ev;
-
-	if (nla_len(addr) != ETH_ALEN)
-		return;
-
-	wpa_printf(MSG_DEBUG, "nl80211: MLME event %d; timeout with " MACSTR,
-		   cmd, MAC2STR((u8 *) nla_data(addr)));
-
-	if (cmd == NL80211_CMD_AUTHENTICATE)
-		ev = EVENT_AUTH_TIMED_OUT;
-	else if (cmd == NL80211_CMD_ASSOCIATE)
-		ev = EVENT_ASSOC_TIMED_OUT;
-	else
-		return;
-
-	os_memset(&event, 0, sizeof(event));
-	os_memcpy(event.timeout_event.addr, nla_data(addr), ETH_ALEN);
-	wpa_supplicant_event(drv->ctx, ev, &event);
-}
-
-
-static void mlme_event_mgmt(struct i802_bss *bss,
-			    struct nlattr *freq, struct nlattr *sig,
-			    const u8 *frame, size_t len)
-{
-	struct wpa_driver_nl80211_data *drv = bss->drv;
-	const struct ieee80211_mgmt *mgmt;
-	union wpa_event_data event;
-	u16 fc, stype;
-	int ssi_signal = 0;
-	int rx_freq = 0;
-
-	wpa_printf(MSG_MSGDUMP, "nl80211: Frame event");
-	mgmt = (const struct ieee80211_mgmt *) frame;
-	if (len < 24) {
-		wpa_printf(MSG_DEBUG, "nl80211: Too short management frame");
-		return;
-	}
-
-	fc = le_to_host16(mgmt->frame_control);
-	stype = WLAN_FC_GET_STYPE(fc);
-
-	if (sig)
-		ssi_signal = (s32) nla_get_u32(sig);
-
-	os_memset(&event, 0, sizeof(event));
-	if (freq) {
-		event.rx_mgmt.freq = nla_get_u32(freq);
-		rx_freq = drv->last_mgmt_freq = event.rx_mgmt.freq;
-	}
-	wpa_printf(MSG_DEBUG,
-		   "nl80211: RX frame sa=" MACSTR
-		   " freq=%d ssi_signal=%d stype=%u (%s) len=%u",
-		   MAC2STR(mgmt->sa), rx_freq, ssi_signal, stype, fc2str(fc),
-		   (unsigned int) len);
-	event.rx_mgmt.frame = frame;
-	event.rx_mgmt.frame_len = len;
-	event.rx_mgmt.ssi_signal = ssi_signal;
-	event.rx_mgmt.drv_priv = bss;
-	wpa_supplicant_event(drv->ctx, EVENT_RX_MGMT, &event);
-}
-
-
-static void mlme_event_mgmt_tx_status(struct wpa_driver_nl80211_data *drv,
-				      struct nlattr *cookie, const u8 *frame,
-				      size_t len, struct nlattr *ack)
-{
-	union wpa_event_data event;
-	const struct ieee80211_hdr *hdr;
-	u16 fc;
-
-	wpa_printf(MSG_DEBUG, "nl80211: Frame TX status event");
-	if (!is_ap_interface(drv->nlmode)) {
-		u64 cookie_val;
-
-		if (!cookie)
-			return;
-
-		cookie_val = nla_get_u64(cookie);
-		wpa_printf(MSG_DEBUG, "nl80211: Action TX status:"
-			   " cookie=0%llx%s (ack=%d)",
-			   (long long unsigned int) cookie_val,
-			   cookie_val == drv->send_action_cookie ?
-			   " (match)" : " (unknown)", ack != NULL);
-		if (cookie_val != drv->send_action_cookie)
-			return;
-	}
-
-	hdr = (const struct ieee80211_hdr *) frame;
-	fc = le_to_host16(hdr->frame_control);
-
-	os_memset(&event, 0, sizeof(event));
-	event.tx_status.type = WLAN_FC_GET_TYPE(fc);
-	event.tx_status.stype = WLAN_FC_GET_STYPE(fc);
-	event.tx_status.dst = hdr->addr1;
-	event.tx_status.data = frame;
-	event.tx_status.data_len = len;
-	event.tx_status.ack = ack != NULL;
-	wpa_supplicant_event(drv->ctx, EVENT_TX_STATUS, &event);
-}
-
-
-static void mlme_event_deauth_disassoc(struct wpa_driver_nl80211_data *drv,
-				       enum wpa_event_type type,
-				       const u8 *frame, size_t len)
-{
-	const struct ieee80211_mgmt *mgmt;
-	union wpa_event_data event;
-	const u8 *bssid = NULL;
-	u16 reason_code = 0;
-
-	if (type == EVENT_DEAUTH)
-		wpa_printf(MSG_DEBUG, "nl80211: Deauthenticate event");
-	else
-		wpa_printf(MSG_DEBUG, "nl80211: Disassociate event");
-
-	mgmt = (const struct ieee80211_mgmt *) frame;
-	if (len >= 24) {
-		bssid = mgmt->bssid;
-
-		if ((drv->capa.flags & WPA_DRIVER_FLAGS_SME) &&
-		    !drv->associated &&
-		    os_memcmp(bssid, drv->auth_bssid, ETH_ALEN) != 0 &&
-		    os_memcmp(bssid, drv->auth_attempt_bssid, ETH_ALEN) != 0 &&
-		    os_memcmp(bssid, drv->prev_bssid, ETH_ALEN) == 0) {
-			/*
-			 * Avoid issues with some roaming cases where
-			 * disconnection event for the old AP may show up after
-			 * we have started connection with the new AP.
-			 */
-			wpa_printf(MSG_DEBUG, "nl80211: Ignore deauth/disassoc event from old AP " MACSTR " when already authenticating with " MACSTR,
-				   MAC2STR(bssid),
-				   MAC2STR(drv->auth_attempt_bssid));
-			return;
-		}
-
-		if (drv->associated != 0 &&
-		    os_memcmp(bssid, drv->bssid, ETH_ALEN) != 0 &&
-		    os_memcmp(bssid, drv->auth_bssid, ETH_ALEN) != 0) {
-			/*
-			 * We have presumably received this deauth as a
-			 * response to a clear_state_mismatch() outgoing
-			 * deauth.  Don't let it take us offline!
-			 */
-			wpa_printf(MSG_DEBUG, "nl80211: Deauth received "
-				   "from Unknown BSSID " MACSTR " -- ignoring",
-				   MAC2STR(bssid));
-			return;
-		}
-	}
-
-	nl80211_mark_disconnected(drv);
-	os_memset(&event, 0, sizeof(event));
-
-	/* Note: Same offset for Reason Code in both frame subtypes */
-	if (len >= 24 + sizeof(mgmt->u.deauth))
-		reason_code = le_to_host16(mgmt->u.deauth.reason_code);
-
-	if (type == EVENT_DISASSOC) {
-		event.disassoc_info.locally_generated =
-			!os_memcmp(mgmt->sa, drv->first_bss->addr, ETH_ALEN);
-		event.disassoc_info.addr = bssid;
-		event.disassoc_info.reason_code = reason_code;
-		if (frame + len > mgmt->u.disassoc.variable) {
-			event.disassoc_info.ie = mgmt->u.disassoc.variable;
-			event.disassoc_info.ie_len = frame + len -
-				mgmt->u.disassoc.variable;
-		}
-	} else {
-		if (drv->ignore_deauth_event) {
-			wpa_printf(MSG_DEBUG, "nl80211: Ignore deauth event due to previous forced deauth-during-auth");
-			drv->ignore_deauth_event = 0;
-			return;
-		}
-		event.deauth_info.locally_generated =
-			!os_memcmp(mgmt->sa, drv->first_bss->addr, ETH_ALEN);
-		if (drv->ignore_next_local_deauth) {
-			drv->ignore_next_local_deauth = 0;
-			if (event.deauth_info.locally_generated) {
-				wpa_printf(MSG_DEBUG, "nl80211: Ignore deauth event triggered due to own deauth request");
-				return;
-			}
-			wpa_printf(MSG_WARNING, "nl80211: Was expecting local deauth but got another disconnect event first");
-		}
-		event.deauth_info.addr = bssid;
-		event.deauth_info.reason_code = reason_code;
-		if (frame + len > mgmt->u.deauth.variable) {
-			event.deauth_info.ie = mgmt->u.deauth.variable;
-			event.deauth_info.ie_len = frame + len -
-				mgmt->u.deauth.variable;
-		}
-	}
-
-	wpa_supplicant_event(drv->ctx, type, &event);
-}
-
-
-static void mlme_event_unprot_disconnect(struct wpa_driver_nl80211_data *drv,
-					 enum wpa_event_type type,
-					 const u8 *frame, size_t len)
-{
-	const struct ieee80211_mgmt *mgmt;
-	union wpa_event_data event;
-	u16 reason_code = 0;
-
-	if (type == EVENT_UNPROT_DEAUTH)
-		wpa_printf(MSG_DEBUG, "nl80211: Unprot Deauthenticate event");
-	else
-		wpa_printf(MSG_DEBUG, "nl80211: Unprot Disassociate event");
-
-	if (len < 24)
-		return;
-
-	mgmt = (const struct ieee80211_mgmt *) frame;
-
-	os_memset(&event, 0, sizeof(event));
-	/* Note: Same offset for Reason Code in both frame subtypes */
-	if (len >= 24 + sizeof(mgmt->u.deauth))
-		reason_code = le_to_host16(mgmt->u.deauth.reason_code);
-
-	if (type == EVENT_UNPROT_DISASSOC) {
-		event.unprot_disassoc.sa = mgmt->sa;
-		event.unprot_disassoc.da = mgmt->da;
-		event.unprot_disassoc.reason_code = reason_code;
-	} else {
-		event.unprot_deauth.sa = mgmt->sa;
-		event.unprot_deauth.da = mgmt->da;
-		event.unprot_deauth.reason_code = reason_code;
-	}
-
-	wpa_supplicant_event(drv->ctx, type, &event);
-}
-
-
-static void mlme_event(struct i802_bss *bss,
-		       enum nl80211_commands cmd, struct nlattr *frame,
-		       struct nlattr *addr, struct nlattr *timed_out,
-		       struct nlattr *freq, struct nlattr *ack,
-		       struct nlattr *cookie, struct nlattr *sig)
-{
-	struct wpa_driver_nl80211_data *drv = bss->drv;
-	const u8 *data;
-	size_t len;
-
-	if (timed_out && addr) {
-		mlme_timeout_event(drv, cmd, addr);
-		return;
-	}
-
-	if (frame == NULL) {
-		wpa_printf(MSG_DEBUG,
-			   "nl80211: MLME event %d (%s) without frame data",
-			   cmd, nl80211_command_to_string(cmd));
-		return;
-	}
-
-	data = nla_data(frame);
-	len = nla_len(frame);
-	if (len < 4 + 2 * ETH_ALEN) {
-		wpa_printf(MSG_MSGDUMP, "nl80211: MLME event %d (%s) on %s("
-			   MACSTR ") - too short",
-			   cmd, nl80211_command_to_string(cmd), bss->ifname,
-			   MAC2STR(bss->addr));
-		return;
-	}
-	wpa_printf(MSG_MSGDUMP, "nl80211: MLME event %d (%s) on %s(" MACSTR
-		   ") A1=" MACSTR " A2=" MACSTR, cmd,
-		   nl80211_command_to_string(cmd), bss->ifname,
-		   MAC2STR(bss->addr), MAC2STR(data + 4),
-		   MAC2STR(data + 4 + ETH_ALEN));
-	if (cmd != NL80211_CMD_FRAME_TX_STATUS && !(data[4] & 0x01) &&
-	    os_memcmp(bss->addr, data + 4, ETH_ALEN) != 0 &&
-	    os_memcmp(bss->addr, data + 4 + ETH_ALEN, ETH_ALEN) != 0) {
-		wpa_printf(MSG_MSGDUMP, "nl80211: %s: Ignore MLME frame event "
-			   "for foreign address", bss->ifname);
-		return;
-	}
-	wpa_hexdump(MSG_MSGDUMP, "nl80211: MLME event frame",
-		    nla_data(frame), nla_len(frame));
-
-	switch (cmd) {
-	case NL80211_CMD_AUTHENTICATE:
-		mlme_event_auth(drv, nla_data(frame), nla_len(frame));
-		break;
-	case NL80211_CMD_ASSOCIATE:
-		mlme_event_assoc(drv, nla_data(frame), nla_len(frame));
-		break;
-	case NL80211_CMD_DEAUTHENTICATE:
-		mlme_event_deauth_disassoc(drv, EVENT_DEAUTH,
-					   nla_data(frame), nla_len(frame));
-		break;
-	case NL80211_CMD_DISASSOCIATE:
-		mlme_event_deauth_disassoc(drv, EVENT_DISASSOC,
-					   nla_data(frame), nla_len(frame));
-		break;
-	case NL80211_CMD_FRAME:
-		mlme_event_mgmt(bss, freq, sig, nla_data(frame),
-				nla_len(frame));
-		break;
-	case NL80211_CMD_FRAME_TX_STATUS:
-		mlme_event_mgmt_tx_status(drv, cookie, nla_data(frame),
-					  nla_len(frame), ack);
-		break;
-	case NL80211_CMD_UNPROT_DEAUTHENTICATE:
-		mlme_event_unprot_disconnect(drv, EVENT_UNPROT_DEAUTH,
-					     nla_data(frame), nla_len(frame));
-		break;
-	case NL80211_CMD_UNPROT_DISASSOCIATE:
-		mlme_event_unprot_disconnect(drv, EVENT_UNPROT_DISASSOC,
-					     nla_data(frame), nla_len(frame));
-		break;
-	default:
-		break;
-	}
-}
-
-
-static void mlme_event_michael_mic_failure(struct i802_bss *bss,
-					   struct nlattr *tb[])
-{
-	union wpa_event_data data;
-
-	wpa_printf(MSG_DEBUG, "nl80211: MLME event Michael MIC failure");
-	os_memset(&data, 0, sizeof(data));
-	if (tb[NL80211_ATTR_MAC]) {
-		wpa_hexdump(MSG_DEBUG, "nl80211: Source MAC address",
-			    nla_data(tb[NL80211_ATTR_MAC]),
-			    nla_len(tb[NL80211_ATTR_MAC]));
-		data.michael_mic_failure.src = nla_data(tb[NL80211_ATTR_MAC]);
-	}
-	if (tb[NL80211_ATTR_KEY_SEQ]) {
-		wpa_hexdump(MSG_DEBUG, "nl80211: TSC",
-			    nla_data(tb[NL80211_ATTR_KEY_SEQ]),
-			    nla_len(tb[NL80211_ATTR_KEY_SEQ]));
-	}
-	if (tb[NL80211_ATTR_KEY_TYPE]) {
-		enum nl80211_key_type key_type =
-			nla_get_u32(tb[NL80211_ATTR_KEY_TYPE]);
-		wpa_printf(MSG_DEBUG, "nl80211: Key Type %d", key_type);
-		if (key_type == NL80211_KEYTYPE_PAIRWISE)
-			data.michael_mic_failure.unicast = 1;
-	} else
-		data.michael_mic_failure.unicast = 1;
-
-	if (tb[NL80211_ATTR_KEY_IDX]) {
-		u8 key_id = nla_get_u8(tb[NL80211_ATTR_KEY_IDX]);
-		wpa_printf(MSG_DEBUG, "nl80211: Key Id %d", key_id);
-	}
-
-	wpa_supplicant_event(bss->ctx, EVENT_MICHAEL_MIC_FAILURE, &data);
-}
-
-
-static void mlme_event_join_ibss(struct wpa_driver_nl80211_data *drv,
-				 struct nlattr *tb[])
-{
-	unsigned int freq;
-
-	if (tb[NL80211_ATTR_MAC] == NULL) {
-		wpa_printf(MSG_DEBUG, "nl80211: No address in IBSS joined "
-			   "event");
-		return;
-	}
-	os_memcpy(drv->bssid, nla_data(tb[NL80211_ATTR_MAC]), ETH_ALEN);
-
-	drv->associated = 1;
-	wpa_printf(MSG_DEBUG, "nl80211: IBSS " MACSTR " joined",
-		   MAC2STR(drv->bssid));
-
-	freq = nl80211_get_assoc_freq(drv);
-	if (freq) {
-		wpa_printf(MSG_DEBUG, "nl80211: IBSS on frequency %u MHz",
-			   freq);
-		drv->first_bss->freq = freq;
-	}
-
-	wpa_supplicant_event(drv->ctx, EVENT_ASSOC, NULL);
-}
-
-
-static void mlme_event_remain_on_channel(struct wpa_driver_nl80211_data *drv,
-					 int cancel_event, struct nlattr *tb[])
-{
-	unsigned int freq, chan_type, duration;
-	union wpa_event_data data;
-	u64 cookie;
-
-	if (tb[NL80211_ATTR_WIPHY_FREQ])
-		freq = nla_get_u32(tb[NL80211_ATTR_WIPHY_FREQ]);
-	else
-		freq = 0;
-
-	if (tb[NL80211_ATTR_WIPHY_CHANNEL_TYPE])
-		chan_type = nla_get_u32(tb[NL80211_ATTR_WIPHY_CHANNEL_TYPE]);
-	else
-		chan_type = 0;
-
-	if (tb[NL80211_ATTR_DURATION])
-		duration = nla_get_u32(tb[NL80211_ATTR_DURATION]);
-	else
-		duration = 0;
-
-	if (tb[NL80211_ATTR_COOKIE])
-		cookie = nla_get_u64(tb[NL80211_ATTR_COOKIE]);
-	else
-		cookie = 0;
-
-	wpa_printf(MSG_DEBUG, "nl80211: Remain-on-channel event (cancel=%d "
-		   "freq=%u channel_type=%u duration=%u cookie=0x%llx (%s))",
-		   cancel_event, freq, chan_type, duration,
-		   (long long unsigned int) cookie,
-		   cookie == drv->remain_on_chan_cookie ? "match" : "unknown");
-
-	if (cookie != drv->remain_on_chan_cookie)
-		return; /* not for us */
-
-	if (cancel_event)
-		drv->pending_remain_on_chan = 0;
-
-	os_memset(&data, 0, sizeof(data));
-	data.remain_on_channel.freq = freq;
-	data.remain_on_channel.duration = duration;
-	wpa_supplicant_event(drv->ctx, cancel_event ?
-			     EVENT_CANCEL_REMAIN_ON_CHANNEL :
-			     EVENT_REMAIN_ON_CHANNEL, &data);
-}
-
-
-static void mlme_event_ft_event(struct wpa_driver_nl80211_data *drv,
-				struct nlattr *tb[])
-{
-	union wpa_event_data data;
-
-	os_memset(&data, 0, sizeof(data));
-
-	if (tb[NL80211_ATTR_IE]) {
-		data.ft_ies.ies = nla_data(tb[NL80211_ATTR_IE]);
-		data.ft_ies.ies_len = nla_len(tb[NL80211_ATTR_IE]);
-	}
-
-	if (tb[NL80211_ATTR_IE_RIC]) {
-		data.ft_ies.ric_ies = nla_data(tb[NL80211_ATTR_IE_RIC]);
-		data.ft_ies.ric_ies_len = nla_len(tb[NL80211_ATTR_IE_RIC]);
-	}
-
-	if (tb[NL80211_ATTR_MAC])
-		os_memcpy(data.ft_ies.target_ap,
-			  nla_data(tb[NL80211_ATTR_MAC]), ETH_ALEN);
-
-	wpa_printf(MSG_DEBUG, "nl80211: FT event target_ap " MACSTR,
-		   MAC2STR(data.ft_ies.target_ap));
-
-	wpa_supplicant_event(drv->ctx, EVENT_FT_RESPONSE, &data);
-}
-
-
-static void send_scan_event(struct wpa_driver_nl80211_data *drv, int aborted,
-			    struct nlattr *tb[])
-{
-	union wpa_event_data event;
-	struct nlattr *nl;
-	int rem;
-	struct scan_info *info;
-#define MAX_REPORT_FREQS 50
-	int freqs[MAX_REPORT_FREQS];
-	int num_freqs = 0;
-
-	if (drv->scan_for_auth) {
-		drv->scan_for_auth = 0;
-		wpa_printf(MSG_DEBUG, "nl80211: Scan results for missing "
-			   "cfg80211 BSS entry");
-		wpa_driver_nl80211_authenticate_retry(drv);
-		return;
-	}
-
-	os_memset(&event, 0, sizeof(event));
-	info = &event.scan_info;
-	info->aborted = aborted;
-
-	if (tb[NL80211_ATTR_SCAN_SSIDS]) {
-		nla_for_each_nested(nl, tb[NL80211_ATTR_SCAN_SSIDS], rem) {
-			struct wpa_driver_scan_ssid *s =
-				&info->ssids[info->num_ssids];
-			s->ssid = nla_data(nl);
-			s->ssid_len = nla_len(nl);
-			wpa_printf(MSG_DEBUG, "nl80211: Scan probed for SSID '%s'",
-				   wpa_ssid_txt(s->ssid, s->ssid_len));
-			info->num_ssids++;
-			if (info->num_ssids == WPAS_MAX_SCAN_SSIDS)
-				break;
-		}
-	}
-	if (tb[NL80211_ATTR_SCAN_FREQUENCIES]) {
-		char msg[200], *pos, *end;
-		int res;
-
-		pos = msg;
-		end = pos + sizeof(msg);
-		*pos = '\0';
-
-		nla_for_each_nested(nl, tb[NL80211_ATTR_SCAN_FREQUENCIES], rem)
-		{
-			freqs[num_freqs] = nla_get_u32(nl);
-			res = os_snprintf(pos, end - pos, " %d",
-					  freqs[num_freqs]);
-			if (res > 0 && end - pos > res)
-				pos += res;
-			num_freqs++;
-			if (num_freqs == MAX_REPORT_FREQS - 1)
-				break;
-		}
-		info->freqs = freqs;
-		info->num_freqs = num_freqs;
-		wpa_printf(MSG_DEBUG, "nl80211: Scan included frequencies:%s",
-			   msg);
-	}
-	wpa_supplicant_event(drv->ctx, EVENT_SCAN_RESULTS, &event);
-}
-
-
 static int get_link_signal(struct nl_msg *msg, void *arg)
 {
 	struct nlattr *tb[NL80211_ATTR_MAX + 1];
@@ -2326,6 +1186,7 @@
 	static struct nla_policy policy[NL80211_STA_INFO_MAX + 1] = {
 		[NL80211_STA_INFO_SIGNAL] = { .type = NLA_U8 },
 		[NL80211_STA_INFO_SIGNAL_AVG] = { .type = NLA_U8 },
+		[NL80211_STA_INFO_BEACON_SIGNAL_AVG] = { .type = NLA_U8 },
 	};
 	struct nlattr *rinfo[NL80211_RATE_INFO_MAX + 1];
 	static struct nla_policy rate_policy[NL80211_RATE_INFO_MAX + 1] = {
@@ -2354,6 +1215,13 @@
 	else
 		sig_change->avg_signal = 0;
 
+	if (sinfo[NL80211_STA_INFO_BEACON_SIGNAL_AVG])
+		sig_change->avg_beacon_signal =
+			(s8)
+			nla_get_u8(sinfo[NL80211_STA_INFO_BEACON_SIGNAL_AVG]);
+	else
+		sig_change->avg_beacon_signal = 0;
+
 	if (sinfo[NL80211_STA_INFO_TX_BITRATE]) {
 		if (nla_parse_nested(rinfo, NL80211_RATE_INFO_MAX,
 				     sinfo[NL80211_STA_INFO_TX_BITRATE],
@@ -2372,27 +1240,21 @@
 }
 
 
-static int nl80211_get_link_signal(struct wpa_driver_nl80211_data *drv,
-				   struct wpa_signal_info *sig)
+int nl80211_get_link_signal(struct wpa_driver_nl80211_data *drv,
+			    struct wpa_signal_info *sig)
 {
 	struct nl_msg *msg;
 
 	sig->current_signal = -9999;
 	sig->current_txrate = 0;
 
-	msg = nlmsg_alloc();
-	if (!msg)
-		return -ENOMEM;
-
-	nl80211_cmd(drv, msg, 0, NL80211_CMD_GET_STATION);
-
-	NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
-	NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, drv->bssid);
+	if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_GET_STATION)) ||
+	    nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, drv->bssid)) {
+		nlmsg_free(msg);
+		return -ENOBUFS;
+	}
 
 	return send_and_recv_msgs(drv, msg, get_link_signal, sig);
- nla_put_failure:
-	nlmsg_free(msg);
-	return -ENOBUFS;
 }
 
 
@@ -2440,946 +1302,16 @@
 }
 
 
-static int nl80211_get_link_noise(struct wpa_driver_nl80211_data *drv,
-				  struct wpa_signal_info *sig_change)
+int nl80211_get_link_noise(struct wpa_driver_nl80211_data *drv,
+			   struct wpa_signal_info *sig_change)
 {
 	struct nl_msg *msg;
 
 	sig_change->current_noise = 9999;
 	sig_change->frequency = drv->assoc_freq;
 
-	msg = nlmsg_alloc();
-	if (!msg)
-		return -ENOMEM;
-
-	nl80211_cmd(drv, msg, NLM_F_DUMP, NL80211_CMD_GET_SURVEY);
-
-	NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
-
+	msg = nl80211_drv_msg(drv, NLM_F_DUMP, NL80211_CMD_GET_SURVEY);
 	return send_and_recv_msgs(drv, msg, get_link_noise, sig_change);
- nla_put_failure:
-	nlmsg_free(msg);
-	return -ENOBUFS;
-}
-
-
-static int get_noise_for_scan_results(struct nl_msg *msg, void *arg)
-{
-	struct nlattr *tb[NL80211_ATTR_MAX + 1];
-	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
-	struct nlattr *sinfo[NL80211_SURVEY_INFO_MAX + 1];
-	static struct nla_policy survey_policy[NL80211_SURVEY_INFO_MAX + 1] = {
-		[NL80211_SURVEY_INFO_FREQUENCY] = { .type = NLA_U32 },
-		[NL80211_SURVEY_INFO_NOISE] = { .type = NLA_U8 },
-	};
-	struct wpa_scan_results *scan_results = arg;
-	struct wpa_scan_res *scan_res;
-	size_t i;
-
-	nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
-		  genlmsg_attrlen(gnlh, 0), NULL);
-
-	if (!tb[NL80211_ATTR_SURVEY_INFO]) {
-		wpa_printf(MSG_DEBUG, "nl80211: Survey data missing");
-		return NL_SKIP;
-	}
-
-	if (nla_parse_nested(sinfo, NL80211_SURVEY_INFO_MAX,
-			     tb[NL80211_ATTR_SURVEY_INFO],
-			     survey_policy)) {
-		wpa_printf(MSG_DEBUG, "nl80211: Failed to parse nested "
-			   "attributes");
-		return NL_SKIP;
-	}
-
-	if (!sinfo[NL80211_SURVEY_INFO_NOISE])
-		return NL_SKIP;
-
-	if (!sinfo[NL80211_SURVEY_INFO_FREQUENCY])
-		return NL_SKIP;
-
-	for (i = 0; i < scan_results->num; ++i) {
-		scan_res = scan_results->res[i];
-		if (!scan_res)
-			continue;
-		if ((int) nla_get_u32(sinfo[NL80211_SURVEY_INFO_FREQUENCY]) !=
-		    scan_res->freq)
-			continue;
-		if (!(scan_res->flags & WPA_SCAN_NOISE_INVALID))
-			continue;
-		scan_res->noise = (s8)
-			nla_get_u8(sinfo[NL80211_SURVEY_INFO_NOISE]);
-		scan_res->flags &= ~WPA_SCAN_NOISE_INVALID;
-	}
-
-	return NL_SKIP;
-}
-
-
-static int nl80211_get_noise_for_scan_results(
-	struct wpa_driver_nl80211_data *drv,
-	struct wpa_scan_results *scan_res)
-{
-	struct nl_msg *msg;
-
-	msg = nlmsg_alloc();
-	if (!msg)
-		return -ENOMEM;
-
-	nl80211_cmd(drv, msg, NLM_F_DUMP, NL80211_CMD_GET_SURVEY);
-
-	NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
-
-	return send_and_recv_msgs(drv, msg, get_noise_for_scan_results,
-				  scan_res);
- nla_put_failure:
-	nlmsg_free(msg);
-	return -ENOBUFS;
-}
-
-
-static void nl80211_cqm_event(struct wpa_driver_nl80211_data *drv,
-			      struct nlattr *tb[])
-{
-	static struct nla_policy cqm_policy[NL80211_ATTR_CQM_MAX + 1] = {
-		[NL80211_ATTR_CQM_RSSI_THOLD] = { .type = NLA_U32 },
-		[NL80211_ATTR_CQM_RSSI_HYST] = { .type = NLA_U8 },
-		[NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT] = { .type = NLA_U32 },
-		[NL80211_ATTR_CQM_PKT_LOSS_EVENT] = { .type = NLA_U32 },
-	};
-	struct nlattr *cqm[NL80211_ATTR_CQM_MAX + 1];
-	enum nl80211_cqm_rssi_threshold_event event;
-	union wpa_event_data ed;
-	struct wpa_signal_info sig;
-	int res;
-
-	if (tb[NL80211_ATTR_CQM] == NULL ||
-	    nla_parse_nested(cqm, NL80211_ATTR_CQM_MAX, tb[NL80211_ATTR_CQM],
-			     cqm_policy)) {
-		wpa_printf(MSG_DEBUG, "nl80211: Ignore invalid CQM event");
-		return;
-	}
-
-	os_memset(&ed, 0, sizeof(ed));
-
-	if (cqm[NL80211_ATTR_CQM_PKT_LOSS_EVENT]) {
-		if (!tb[NL80211_ATTR_MAC])
-			return;
-		os_memcpy(ed.low_ack.addr, nla_data(tb[NL80211_ATTR_MAC]),
-			  ETH_ALEN);
-		wpa_supplicant_event(drv->ctx, EVENT_STATION_LOW_ACK, &ed);
-		return;
-	}
-
-	if (cqm[NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT] == NULL)
-		return;
-	event = nla_get_u32(cqm[NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT]);
-
-	if (event == NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH) {
-		wpa_printf(MSG_DEBUG, "nl80211: Connection quality monitor "
-			   "event: RSSI high");
-		ed.signal_change.above_threshold = 1;
-	} else if (event == NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW) {
-		wpa_printf(MSG_DEBUG, "nl80211: Connection quality monitor "
-			   "event: RSSI low");
-		ed.signal_change.above_threshold = 0;
-	} else
-		return;
-
-	res = nl80211_get_link_signal(drv, &sig);
-	if (res == 0) {
-		ed.signal_change.current_signal = sig.current_signal;
-		ed.signal_change.current_txrate = sig.current_txrate;
-		wpa_printf(MSG_DEBUG, "nl80211: Signal: %d dBm  txrate: %d",
-			   sig.current_signal, sig.current_txrate);
-	}
-
-	res = nl80211_get_link_noise(drv, &sig);
-	if (res == 0) {
-		ed.signal_change.current_noise = sig.current_noise;
-		wpa_printf(MSG_DEBUG, "nl80211: Noise: %d dBm",
-			   sig.current_noise);
-	}
-
-	wpa_supplicant_event(drv->ctx, EVENT_SIGNAL_CHANGE, &ed);
-}
-
-
-static void nl80211_new_station_event(struct wpa_driver_nl80211_data *drv,
-				      struct nlattr **tb)
-{
-	u8 *addr;
-	union wpa_event_data data;
-
-	if (tb[NL80211_ATTR_MAC] == NULL)
-		return;
-	addr = nla_data(tb[NL80211_ATTR_MAC]);
-	wpa_printf(MSG_DEBUG, "nl80211: New station " MACSTR, MAC2STR(addr));
-
-	if (is_ap_interface(drv->nlmode) && drv->device_ap_sme) {
-		u8 *ies = NULL;
-		size_t ies_len = 0;
-		if (tb[NL80211_ATTR_IE]) {
-			ies = nla_data(tb[NL80211_ATTR_IE]);
-			ies_len = nla_len(tb[NL80211_ATTR_IE]);
-		}
-		wpa_hexdump(MSG_DEBUG, "nl80211: Assoc Req IEs", ies, ies_len);
-		drv_event_assoc(drv->ctx, addr, ies, ies_len, 0);
-		return;
-	}
-
-	if (drv->nlmode != NL80211_IFTYPE_ADHOC)
-		return;
-
-	os_memset(&data, 0, sizeof(data));
-	os_memcpy(data.ibss_rsn_start.peer, addr, ETH_ALEN);
-	wpa_supplicant_event(drv->ctx, EVENT_IBSS_RSN_START, &data);
-}
-
-
-static void nl80211_del_station_event(struct wpa_driver_nl80211_data *drv,
-				      struct nlattr **tb)
-{
-	u8 *addr;
-	union wpa_event_data data;
-
-	if (tb[NL80211_ATTR_MAC] == NULL)
-		return;
-	addr = nla_data(tb[NL80211_ATTR_MAC]);
-	wpa_printf(MSG_DEBUG, "nl80211: Delete station " MACSTR,
-		   MAC2STR(addr));
-
-	if (is_ap_interface(drv->nlmode) && drv->device_ap_sme) {
-		drv_event_disassoc(drv->ctx, addr);
-		return;
-	}
-
-	if (drv->nlmode != NL80211_IFTYPE_ADHOC)
-		return;
-
-	os_memset(&data, 0, sizeof(data));
-	os_memcpy(data.ibss_peer_lost.peer, addr, ETH_ALEN);
-	wpa_supplicant_event(drv->ctx, EVENT_IBSS_PEER_LOST, &data);
-}
-
-
-static void nl80211_rekey_offload_event(struct wpa_driver_nl80211_data *drv,
-					struct nlattr **tb)
-{
-	struct nlattr *rekey_info[NUM_NL80211_REKEY_DATA];
-	static struct nla_policy rekey_policy[NUM_NL80211_REKEY_DATA] = {
-		[NL80211_REKEY_DATA_KEK] = {
-			.minlen = NL80211_KEK_LEN,
-			.maxlen = NL80211_KEK_LEN,
-		},
-		[NL80211_REKEY_DATA_KCK] = {
-			.minlen = NL80211_KCK_LEN,
-			.maxlen = NL80211_KCK_LEN,
-		},
-		[NL80211_REKEY_DATA_REPLAY_CTR] = {
-			.minlen = NL80211_REPLAY_CTR_LEN,
-			.maxlen = NL80211_REPLAY_CTR_LEN,
-		},
-	};
-	union wpa_event_data data;
-
-	if (!tb[NL80211_ATTR_MAC])
-		return;
-	if (!tb[NL80211_ATTR_REKEY_DATA])
-		return;
-	if (nla_parse_nested(rekey_info, MAX_NL80211_REKEY_DATA,
-			     tb[NL80211_ATTR_REKEY_DATA], rekey_policy))
-		return;
-	if (!rekey_info[NL80211_REKEY_DATA_REPLAY_CTR])
-		return;
-
-	os_memset(&data, 0, sizeof(data));
-	data.driver_gtk_rekey.bssid = nla_data(tb[NL80211_ATTR_MAC]);
-	wpa_printf(MSG_DEBUG, "nl80211: Rekey offload event for BSSID " MACSTR,
-		   MAC2STR(data.driver_gtk_rekey.bssid));
-	data.driver_gtk_rekey.replay_ctr =
-		nla_data(rekey_info[NL80211_REKEY_DATA_REPLAY_CTR]);
-	wpa_hexdump(MSG_DEBUG, "nl80211: Rekey offload - Replay Counter",
-		    data.driver_gtk_rekey.replay_ctr, NL80211_REPLAY_CTR_LEN);
-	wpa_supplicant_event(drv->ctx, EVENT_DRIVER_GTK_REKEY, &data);
-}
-
-
-static void nl80211_pmksa_candidate_event(struct wpa_driver_nl80211_data *drv,
-					  struct nlattr **tb)
-{
-	struct nlattr *cand[NUM_NL80211_PMKSA_CANDIDATE];
-	static struct nla_policy cand_policy[NUM_NL80211_PMKSA_CANDIDATE] = {
-		[NL80211_PMKSA_CANDIDATE_INDEX] = { .type = NLA_U32 },
-		[NL80211_PMKSA_CANDIDATE_BSSID] = {
-			.minlen = ETH_ALEN,
-			.maxlen = ETH_ALEN,
-		},
-		[NL80211_PMKSA_CANDIDATE_PREAUTH] = { .type = NLA_FLAG },
-	};
-	union wpa_event_data data;
-
-	wpa_printf(MSG_DEBUG, "nl80211: PMKSA candidate event");
-
-	if (!tb[NL80211_ATTR_PMKSA_CANDIDATE])
-		return;
-	if (nla_parse_nested(cand, MAX_NL80211_PMKSA_CANDIDATE,
-			     tb[NL80211_ATTR_PMKSA_CANDIDATE], cand_policy))
-		return;
-	if (!cand[NL80211_PMKSA_CANDIDATE_INDEX] ||
-	    !cand[NL80211_PMKSA_CANDIDATE_BSSID])
-		return;
-
-	os_memset(&data, 0, sizeof(data));
-	os_memcpy(data.pmkid_candidate.bssid,
-		  nla_data(cand[NL80211_PMKSA_CANDIDATE_BSSID]), ETH_ALEN);
-	data.pmkid_candidate.index =
-		nla_get_u32(cand[NL80211_PMKSA_CANDIDATE_INDEX]);
-	data.pmkid_candidate.preauth =
-		cand[NL80211_PMKSA_CANDIDATE_PREAUTH] != NULL;
-	wpa_supplicant_event(drv->ctx, EVENT_PMKID_CANDIDATE, &data);
-}
-
-
-static void nl80211_client_probe_event(struct wpa_driver_nl80211_data *drv,
-				       struct nlattr **tb)
-{
-	union wpa_event_data data;
-
-	wpa_printf(MSG_DEBUG, "nl80211: Probe client event");
-
-	if (!tb[NL80211_ATTR_MAC] || !tb[NL80211_ATTR_ACK])
-		return;
-
-	os_memset(&data, 0, sizeof(data));
-	os_memcpy(data.client_poll.addr,
-		  nla_data(tb[NL80211_ATTR_MAC]), ETH_ALEN);
-
-	wpa_supplicant_event(drv->ctx, EVENT_DRIVER_CLIENT_POLL_OK, &data);
-}
-
-
-static void nl80211_tdls_oper_event(struct wpa_driver_nl80211_data *drv,
-				    struct nlattr **tb)
-{
-	union wpa_event_data data;
-
-	wpa_printf(MSG_DEBUG, "nl80211: TDLS operation event");
-
-	if (!tb[NL80211_ATTR_MAC] || !tb[NL80211_ATTR_TDLS_OPERATION])
-		return;
-
-	os_memset(&data, 0, sizeof(data));
-	os_memcpy(data.tdls.peer, nla_data(tb[NL80211_ATTR_MAC]), ETH_ALEN);
-	switch (nla_get_u8(tb[NL80211_ATTR_TDLS_OPERATION])) {
-	case NL80211_TDLS_SETUP:
-		wpa_printf(MSG_DEBUG, "nl80211: TDLS setup request for peer "
-			   MACSTR, MAC2STR(data.tdls.peer));
-		data.tdls.oper = TDLS_REQUEST_SETUP;
-		break;
-	case NL80211_TDLS_TEARDOWN:
-		wpa_printf(MSG_DEBUG, "nl80211: TDLS teardown request for peer "
-			   MACSTR, MAC2STR(data.tdls.peer));
-		data.tdls.oper = TDLS_REQUEST_TEARDOWN;
-		break;
-	default:
-		wpa_printf(MSG_DEBUG, "nl80211: Unsupported TDLS operatione "
-			   "event");
-		return;
-	}
-	if (tb[NL80211_ATTR_REASON_CODE]) {
-		data.tdls.reason_code =
-			nla_get_u16(tb[NL80211_ATTR_REASON_CODE]);
-	}
-
-	wpa_supplicant_event(drv->ctx, EVENT_TDLS, &data);
-}
-
-
-static void nl80211_stop_ap(struct wpa_driver_nl80211_data *drv,
-			    struct nlattr **tb)
-{
-	wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_UNAVAILABLE, NULL);
-}
-
-
-static void nl80211_connect_failed_event(struct wpa_driver_nl80211_data *drv,
-					 struct nlattr **tb)
-{
-	union wpa_event_data data;
-	u32 reason;
-
-	wpa_printf(MSG_DEBUG, "nl80211: Connect failed event");
-
-	if (!tb[NL80211_ATTR_MAC] || !tb[NL80211_ATTR_CONN_FAILED_REASON])
-		return;
-
-	os_memset(&data, 0, sizeof(data));
-	os_memcpy(data.connect_failed_reason.addr,
-		  nla_data(tb[NL80211_ATTR_MAC]), ETH_ALEN);
-
-	reason = nla_get_u32(tb[NL80211_ATTR_CONN_FAILED_REASON]);
-	switch (reason) {
-	case NL80211_CONN_FAIL_MAX_CLIENTS:
-		wpa_printf(MSG_DEBUG, "nl80211: Max client reached");
-		data.connect_failed_reason.code = MAX_CLIENT_REACHED;
-		break;
-	case NL80211_CONN_FAIL_BLOCKED_CLIENT:
-		wpa_printf(MSG_DEBUG, "nl80211: Blocked client " MACSTR
-			   " tried to connect",
-			   MAC2STR(data.connect_failed_reason.addr));
-		data.connect_failed_reason.code = BLOCKED_CLIENT;
-		break;
-	default:
-		wpa_printf(MSG_DEBUG, "nl8021l: Unknown connect failed reason "
-			   "%u", reason);
-		return;
-	}
-
-	wpa_supplicant_event(drv->ctx, EVENT_CONNECT_FAILED_REASON, &data);
-}
-
-
-static void nl80211_radar_event(struct wpa_driver_nl80211_data *drv,
-				struct nlattr **tb)
-{
-	union wpa_event_data data;
-	enum nl80211_radar_event event_type;
-
-	if (!tb[NL80211_ATTR_WIPHY_FREQ] || !tb[NL80211_ATTR_RADAR_EVENT])
-		return;
-
-	os_memset(&data, 0, sizeof(data));
-	data.dfs_event.freq = nla_get_u32(tb[NL80211_ATTR_WIPHY_FREQ]);
-	event_type = nla_get_u32(tb[NL80211_ATTR_RADAR_EVENT]);
-
-	/* Check HT params */
-	if (tb[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) {
-		data.dfs_event.ht_enabled = 1;
-		data.dfs_event.chan_offset = 0;
-
-		switch (nla_get_u32(tb[NL80211_ATTR_WIPHY_CHANNEL_TYPE])) {
-		case NL80211_CHAN_NO_HT:
-			data.dfs_event.ht_enabled = 0;
-			break;
-		case NL80211_CHAN_HT20:
-			break;
-		case NL80211_CHAN_HT40PLUS:
-			data.dfs_event.chan_offset = 1;
-			break;
-		case NL80211_CHAN_HT40MINUS:
-			data.dfs_event.chan_offset = -1;
-			break;
-		}
-	}
-
-	/* Get VHT params */
-	if (tb[NL80211_ATTR_CHANNEL_WIDTH])
-		data.dfs_event.chan_width =
-			convert2width(nla_get_u32(
-					      tb[NL80211_ATTR_CHANNEL_WIDTH]));
-	if (tb[NL80211_ATTR_CENTER_FREQ1])
-		data.dfs_event.cf1 = nla_get_u32(tb[NL80211_ATTR_CENTER_FREQ1]);
-	if (tb[NL80211_ATTR_CENTER_FREQ2])
-		data.dfs_event.cf2 = nla_get_u32(tb[NL80211_ATTR_CENTER_FREQ2]);
-
-	wpa_printf(MSG_DEBUG, "nl80211: DFS event on freq %d MHz, ht: %d, offset: %d, width: %d, cf1: %dMHz, cf2: %dMHz",
-		   data.dfs_event.freq, data.dfs_event.ht_enabled,
-		   data.dfs_event.chan_offset, data.dfs_event.chan_width,
-		   data.dfs_event.cf1, data.dfs_event.cf2);
-
-	switch (event_type) {
-	case NL80211_RADAR_DETECTED:
-		wpa_supplicant_event(drv->ctx, EVENT_DFS_RADAR_DETECTED, &data);
-		break;
-	case NL80211_RADAR_CAC_FINISHED:
-		wpa_supplicant_event(drv->ctx, EVENT_DFS_CAC_FINISHED, &data);
-		break;
-	case NL80211_RADAR_CAC_ABORTED:
-		wpa_supplicant_event(drv->ctx, EVENT_DFS_CAC_ABORTED, &data);
-		break;
-	case NL80211_RADAR_NOP_FINISHED:
-		wpa_supplicant_event(drv->ctx, EVENT_DFS_NOP_FINISHED, &data);
-		break;
-	default:
-		wpa_printf(MSG_DEBUG, "nl80211: Unknown radar event %d "
-			   "received", event_type);
-		break;
-	}
-}
-
-
-static void nl80211_spurious_frame(struct i802_bss *bss, struct nlattr **tb,
-				   int wds)
-{
-	struct wpa_driver_nl80211_data *drv = bss->drv;
-	union wpa_event_data event;
-
-	if (!tb[NL80211_ATTR_MAC])
-		return;
-
-	os_memset(&event, 0, sizeof(event));
-	event.rx_from_unknown.bssid = bss->addr;
-	event.rx_from_unknown.addr = nla_data(tb[NL80211_ATTR_MAC]);
-	event.rx_from_unknown.wds = wds;
-
-	wpa_supplicant_event(drv->ctx, EVENT_RX_FROM_UNKNOWN, &event);
-}
-
-
-static void qca_nl80211_avoid_freq(struct wpa_driver_nl80211_data *drv,
-				   const u8 *data, size_t len)
-{
-	u32 i, count;
-	union wpa_event_data event;
-	struct wpa_freq_range *range = NULL;
-	const struct qca_avoid_freq_list *freq_range;
-
-	freq_range = (const struct qca_avoid_freq_list *) data;
-	if (len < sizeof(freq_range->count))
-		return;
-
-	count = freq_range->count;
-	if (len < sizeof(freq_range->count) +
-	    count * sizeof(struct qca_avoid_freq_range)) {
-		wpa_printf(MSG_DEBUG, "nl80211: Ignored too short avoid frequency list (len=%u)",
-			   (unsigned int) len);
-		return;
-	}
-
-	if (count > 0) {
-		range = os_calloc(count, sizeof(struct wpa_freq_range));
-		if (range == NULL)
-			return;
-	}
-
-	os_memset(&event, 0, sizeof(event));
-	for (i = 0; i < count; i++) {
-		unsigned int idx = event.freq_range.num;
-		range[idx].min = freq_range->range[i].start_freq;
-		range[idx].max = freq_range->range[i].end_freq;
-		wpa_printf(MSG_DEBUG, "nl80211: Avoid frequency range: %u-%u",
-			   range[idx].min, range[idx].max);
-		if (range[idx].min > range[idx].max) {
-			wpa_printf(MSG_DEBUG, "nl80211: Ignore invalid frequency range");
-			continue;
-		}
-		event.freq_range.num++;
-	}
-	event.freq_range.range = range;
-
-	wpa_supplicant_event(drv->ctx, EVENT_AVOID_FREQUENCIES, &event);
-
-	os_free(range);
-}
-
-
-static void nl80211_vendor_event_qca(struct wpa_driver_nl80211_data *drv,
-				     u32 subcmd, u8 *data, size_t len)
-{
-	switch (subcmd) {
-	case QCA_NL80211_VENDOR_SUBCMD_AVOID_FREQUENCY:
-		qca_nl80211_avoid_freq(drv, data, len);
-		break;
-	default:
-		wpa_printf(MSG_DEBUG,
-			   "nl80211: Ignore unsupported QCA vendor event %u",
-			   subcmd);
-		break;
-	}
-}
-
-
-static void nl80211_vendor_event(struct wpa_driver_nl80211_data *drv,
-				 struct nlattr **tb)
-{
-	u32 vendor_id, subcmd, wiphy = 0;
-	int wiphy_idx;
-	u8 *data = NULL;
-	size_t len = 0;
-
-	if (!tb[NL80211_ATTR_VENDOR_ID] ||
-	    !tb[NL80211_ATTR_VENDOR_SUBCMD])
-		return;
-
-	vendor_id = nla_get_u32(tb[NL80211_ATTR_VENDOR_ID]);
-	subcmd = nla_get_u32(tb[NL80211_ATTR_VENDOR_SUBCMD]);
-
-	if (tb[NL80211_ATTR_WIPHY])
-		wiphy = nla_get_u32(tb[NL80211_ATTR_WIPHY]);
-
-	wpa_printf(MSG_DEBUG, "nl80211: Vendor event: wiphy=%u vendor_id=0x%x subcmd=%u",
-		   wiphy, vendor_id, subcmd);
-
-	if (tb[NL80211_ATTR_VENDOR_DATA]) {
-		data = nla_data(tb[NL80211_ATTR_VENDOR_DATA]);
-		len = nla_len(tb[NL80211_ATTR_VENDOR_DATA]);
-		wpa_hexdump(MSG_MSGDUMP, "nl80211: Vendor data", data, len);
-	}
-
-	wiphy_idx = nl80211_get_wiphy_index(drv->first_bss);
-	if (wiphy_idx >= 0 && wiphy_idx != (int) wiphy) {
-		wpa_printf(MSG_DEBUG, "nl80211: Ignore vendor event for foreign wiphy %u (own: %d)",
-			   wiphy, wiphy_idx);
-		return;
-	}
-
-	switch (vendor_id) {
-	case OUI_QCA:
-		nl80211_vendor_event_qca(drv, subcmd, data, len);
-		break;
-	default:
-		wpa_printf(MSG_DEBUG, "nl80211: Ignore unsupported vendor event");
-		break;
-	}
-}
-
-
-static void nl80211_reg_change_event(struct wpa_driver_nl80211_data *drv,
-				     struct nlattr *tb[])
-{
-	union wpa_event_data data;
-	enum nl80211_reg_initiator init;
-
-	wpa_printf(MSG_DEBUG, "nl80211: Regulatory domain change");
-
-	if (tb[NL80211_ATTR_REG_INITIATOR] == NULL)
-		return;
-
-	os_memset(&data, 0, sizeof(data));
-	init = nla_get_u8(tb[NL80211_ATTR_REG_INITIATOR]);
-	wpa_printf(MSG_DEBUG, " * initiator=%d", init);
-	switch (init) {
-	case NL80211_REGDOM_SET_BY_CORE:
-		data.channel_list_changed.initiator = REGDOM_SET_BY_CORE;
-		break;
-	case NL80211_REGDOM_SET_BY_USER:
-		data.channel_list_changed.initiator = REGDOM_SET_BY_USER;
-		break;
-	case NL80211_REGDOM_SET_BY_DRIVER:
-		data.channel_list_changed.initiator = REGDOM_SET_BY_DRIVER;
-		break;
-	case NL80211_REGDOM_SET_BY_COUNTRY_IE:
-		data.channel_list_changed.initiator = REGDOM_SET_BY_COUNTRY_IE;
-		break;
-	}
-
-	if (tb[NL80211_ATTR_REG_TYPE]) {
-		enum nl80211_reg_type type;
-		type = nla_get_u8(tb[NL80211_ATTR_REG_TYPE]);
-		wpa_printf(MSG_DEBUG, " * type=%d", type);
-		switch (type) {
-		case NL80211_REGDOM_TYPE_COUNTRY:
-			data.channel_list_changed.type = REGDOM_TYPE_COUNTRY;
-			break;
-		case NL80211_REGDOM_TYPE_WORLD:
-			data.channel_list_changed.type = REGDOM_TYPE_WORLD;
-			break;
-		case NL80211_REGDOM_TYPE_CUSTOM_WORLD:
-			data.channel_list_changed.type =
-				REGDOM_TYPE_CUSTOM_WORLD;
-			break;
-		case NL80211_REGDOM_TYPE_INTERSECTION:
-			data.channel_list_changed.type =
-				REGDOM_TYPE_INTERSECTION;
-			break;
-		}
-	}
-
-	if (tb[NL80211_ATTR_REG_ALPHA2]) {
-		os_strlcpy(data.channel_list_changed.alpha2,
-			   nla_get_string(tb[NL80211_ATTR_REG_ALPHA2]),
-			   sizeof(data.channel_list_changed.alpha2));
-		wpa_printf(MSG_DEBUG, " * alpha2=%s",
-			   data.channel_list_changed.alpha2);
-	}
-
-	wpa_supplicant_event(drv->ctx, EVENT_CHANNEL_LIST_CHANGED, &data);
-}
-
-
-static void do_process_drv_event(struct i802_bss *bss, int cmd,
-				 struct nlattr **tb)
-{
-	struct wpa_driver_nl80211_data *drv = bss->drv;
-	union wpa_event_data data;
-
-	wpa_printf(MSG_DEBUG, "nl80211: Drv Event %d (%s) received for %s",
-		   cmd, nl80211_command_to_string(cmd), bss->ifname);
-
-	if (drv->ap_scan_as_station != NL80211_IFTYPE_UNSPECIFIED &&
-	    (cmd == NL80211_CMD_NEW_SCAN_RESULTS ||
-	     cmd == NL80211_CMD_SCAN_ABORTED)) {
-		wpa_driver_nl80211_set_mode(drv->first_bss,
-					    drv->ap_scan_as_station);
-		drv->ap_scan_as_station = NL80211_IFTYPE_UNSPECIFIED;
-	}
-
-	switch (cmd) {
-	case NL80211_CMD_TRIGGER_SCAN:
-		wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: Scan trigger");
-		drv->scan_state = SCAN_STARTED;
-		if (drv->scan_for_auth) {
-			/*
-			 * Cannot indicate EVENT_SCAN_STARTED here since we skip
-			 * EVENT_SCAN_RESULTS in scan_for_auth case and the
-			 * upper layer implementation could get confused about
-			 * scanning state.
-			 */
-			wpa_printf(MSG_DEBUG, "nl80211: Do not indicate scan-start event due to internal scan_for_auth");
-			break;
-		}
-		wpa_supplicant_event(drv->ctx, EVENT_SCAN_STARTED, NULL);
-		break;
-	case NL80211_CMD_START_SCHED_SCAN:
-		wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: Sched scan started");
-		drv->scan_state = SCHED_SCAN_STARTED;
-		break;
-	case NL80211_CMD_SCHED_SCAN_STOPPED:
-		wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: Sched scan stopped");
-		drv->scan_state = SCHED_SCAN_STOPPED;
-		wpa_supplicant_event(drv->ctx, EVENT_SCHED_SCAN_STOPPED, NULL);
-		break;
-	case NL80211_CMD_NEW_SCAN_RESULTS:
-		wpa_dbg(drv->ctx, MSG_DEBUG,
-			"nl80211: New scan results available");
-		drv->scan_state = SCAN_COMPLETED;
-		drv->scan_complete_events = 1;
-		eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, drv,
-				     drv->ctx);
-		send_scan_event(drv, 0, tb);
-		break;
-	case NL80211_CMD_SCHED_SCAN_RESULTS:
-		wpa_dbg(drv->ctx, MSG_DEBUG,
-			"nl80211: New sched scan results available");
-		drv->scan_state = SCHED_SCAN_RESULTS;
-		send_scan_event(drv, 0, tb);
-		break;
-	case NL80211_CMD_SCAN_ABORTED:
-		wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: Scan aborted");
-		drv->scan_state = SCAN_ABORTED;
-		/*
-		 * Need to indicate that scan results are available in order
-		 * not to make wpa_supplicant stop its scanning.
-		 */
-		eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, drv,
-				     drv->ctx);
-		send_scan_event(drv, 1, tb);
-		break;
-	case NL80211_CMD_AUTHENTICATE:
-	case NL80211_CMD_ASSOCIATE:
-	case NL80211_CMD_DEAUTHENTICATE:
-	case NL80211_CMD_DISASSOCIATE:
-	case NL80211_CMD_FRAME_TX_STATUS:
-	case NL80211_CMD_UNPROT_DEAUTHENTICATE:
-	case NL80211_CMD_UNPROT_DISASSOCIATE:
-		mlme_event(bss, cmd, tb[NL80211_ATTR_FRAME],
-			   tb[NL80211_ATTR_MAC], tb[NL80211_ATTR_TIMED_OUT],
-			   tb[NL80211_ATTR_WIPHY_FREQ], tb[NL80211_ATTR_ACK],
-			   tb[NL80211_ATTR_COOKIE],
-			   tb[NL80211_ATTR_RX_SIGNAL_DBM]);
-		break;
-	case NL80211_CMD_CONNECT:
-	case NL80211_CMD_ROAM:
-		mlme_event_connect(drv, cmd,
-				   tb[NL80211_ATTR_STATUS_CODE],
-				   tb[NL80211_ATTR_MAC],
-				   tb[NL80211_ATTR_REQ_IE],
-				   tb[NL80211_ATTR_RESP_IE]);
-		break;
-	case NL80211_CMD_CH_SWITCH_NOTIFY:
-		mlme_event_ch_switch(drv,
-				     tb[NL80211_ATTR_IFINDEX],
-				     tb[NL80211_ATTR_WIPHY_FREQ],
-				     tb[NL80211_ATTR_WIPHY_CHANNEL_TYPE],
-				     tb[NL80211_ATTR_CHANNEL_WIDTH],
-				     tb[NL80211_ATTR_CENTER_FREQ1],
-				     tb[NL80211_ATTR_CENTER_FREQ2]);
-		break;
-	case NL80211_CMD_DISCONNECT:
-		mlme_event_disconnect(drv, tb[NL80211_ATTR_REASON_CODE],
-				      tb[NL80211_ATTR_MAC],
-				      tb[NL80211_ATTR_DISCONNECTED_BY_AP]);
-		break;
-	case NL80211_CMD_MICHAEL_MIC_FAILURE:
-		mlme_event_michael_mic_failure(bss, tb);
-		break;
-	case NL80211_CMD_JOIN_IBSS:
-		mlme_event_join_ibss(drv, tb);
-		break;
-	case NL80211_CMD_REMAIN_ON_CHANNEL:
-		mlme_event_remain_on_channel(drv, 0, tb);
-		break;
-	case NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL:
-		mlme_event_remain_on_channel(drv, 1, tb);
-		break;
-	case NL80211_CMD_NOTIFY_CQM:
-		nl80211_cqm_event(drv, tb);
-		break;
-	case NL80211_CMD_REG_CHANGE:
-		nl80211_reg_change_event(drv, tb);
-		break;
-	case NL80211_CMD_REG_BEACON_HINT:
-		wpa_printf(MSG_DEBUG, "nl80211: Regulatory beacon hint");
-		os_memset(&data, 0, sizeof(data));
-		data.channel_list_changed.initiator = REGDOM_BEACON_HINT;
-		wpa_supplicant_event(drv->ctx, EVENT_CHANNEL_LIST_CHANGED,
-				     &data);
-		break;
-	case NL80211_CMD_NEW_STATION:
-		nl80211_new_station_event(drv, tb);
-		break;
-	case NL80211_CMD_DEL_STATION:
-		nl80211_del_station_event(drv, tb);
-		break;
-	case NL80211_CMD_SET_REKEY_OFFLOAD:
-		nl80211_rekey_offload_event(drv, tb);
-		break;
-	case NL80211_CMD_PMKSA_CANDIDATE:
-		nl80211_pmksa_candidate_event(drv, tb);
-		break;
-	case NL80211_CMD_PROBE_CLIENT:
-		nl80211_client_probe_event(drv, tb);
-		break;
-	case NL80211_CMD_TDLS_OPER:
-		nl80211_tdls_oper_event(drv, tb);
-		break;
-	case NL80211_CMD_CONN_FAILED:
-		nl80211_connect_failed_event(drv, tb);
-		break;
-	case NL80211_CMD_FT_EVENT:
-		mlme_event_ft_event(drv, tb);
-		break;
-	case NL80211_CMD_RADAR_DETECT:
-		nl80211_radar_event(drv, tb);
-		break;
-	case NL80211_CMD_STOP_AP:
-		nl80211_stop_ap(drv, tb);
-		break;
-	case NL80211_CMD_VENDOR:
-		nl80211_vendor_event(drv, tb);
-		break;
-	default:
-		wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: Ignored unknown event "
-			"(cmd=%d)", cmd);
-		break;
-	}
-}
-
-
-static int process_drv_event(struct nl_msg *msg, void *arg)
-{
-	struct wpa_driver_nl80211_data *drv = arg;
-	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
-	struct nlattr *tb[NL80211_ATTR_MAX + 1];
-	struct i802_bss *bss;
-	int ifidx = -1;
-
-	nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
-		  genlmsg_attrlen(gnlh, 0), NULL);
-
-	if (tb[NL80211_ATTR_IFINDEX]) {
-		ifidx = nla_get_u32(tb[NL80211_ATTR_IFINDEX]);
-
-		for (bss = drv->first_bss; bss; bss = bss->next)
-			if (ifidx == -1 || ifidx == bss->ifindex) {
-				do_process_drv_event(bss, gnlh->cmd, tb);
-				return NL_SKIP;
-			}
-		wpa_printf(MSG_DEBUG,
-			   "nl80211: Ignored event (cmd=%d) for foreign interface (ifindex %d)",
-			   gnlh->cmd, ifidx);
-	} else if (tb[NL80211_ATTR_WDEV]) {
-		u64 wdev_id = nla_get_u64(tb[NL80211_ATTR_WDEV]);
-		wpa_printf(MSG_DEBUG, "nl80211: Process event on P2P device");
-		for (bss = drv->first_bss; bss; bss = bss->next) {
-			if (bss->wdev_id_set && wdev_id == bss->wdev_id) {
-				do_process_drv_event(bss, gnlh->cmd, tb);
-				return NL_SKIP;
-			}
-		}
-		wpa_printf(MSG_DEBUG,
-			   "nl80211: Ignored event (cmd=%d) for foreign interface (wdev 0x%llx)",
-			   gnlh->cmd, (long long unsigned int) wdev_id);
-	}
-
-	return NL_SKIP;
-}
-
-
-static int process_global_event(struct nl_msg *msg, void *arg)
-{
-	struct nl80211_global *global = arg;
-	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
-	struct nlattr *tb[NL80211_ATTR_MAX + 1];
-	struct wpa_driver_nl80211_data *drv, *tmp;
-	int ifidx = -1;
-	struct i802_bss *bss;
-	u64 wdev_id = 0;
-	int wdev_id_set = 0;
-
-	nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
-		  genlmsg_attrlen(gnlh, 0), NULL);
-
-	if (tb[NL80211_ATTR_IFINDEX])
-		ifidx = nla_get_u32(tb[NL80211_ATTR_IFINDEX]);
-	else if (tb[NL80211_ATTR_WDEV]) {
-		wdev_id = nla_get_u64(tb[NL80211_ATTR_WDEV]);
-		wdev_id_set = 1;
-	}
-
-	dl_list_for_each_safe(drv, tmp, &global->interfaces,
-			      struct wpa_driver_nl80211_data, list) {
-		for (bss = drv->first_bss; bss; bss = bss->next) {
-			if ((ifidx == -1 && !wdev_id_set) ||
-			    ifidx == bss->ifindex ||
-			    (wdev_id_set && bss->wdev_id_set &&
-			     wdev_id == bss->wdev_id)) {
-				do_process_drv_event(bss, gnlh->cmd, tb);
-				return NL_SKIP;
-			}
-		}
-	}
-
-	return NL_SKIP;
-}
-
-
-static int process_bss_event(struct nl_msg *msg, void *arg)
-{
-	struct i802_bss *bss = arg;
-	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
-	struct nlattr *tb[NL80211_ATTR_MAX + 1];
-
-	nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
-		  genlmsg_attrlen(gnlh, 0), NULL);
-
-	wpa_printf(MSG_DEBUG, "nl80211: BSS Event %d (%s) received for %s",
-		   gnlh->cmd, nl80211_command_to_string(gnlh->cmd),
-		   bss->ifname);
-
-	switch (gnlh->cmd) {
-	case NL80211_CMD_FRAME:
-	case NL80211_CMD_FRAME_TX_STATUS:
-		mlme_event(bss, gnlh->cmd, tb[NL80211_ATTR_FRAME],
-			   tb[NL80211_ATTR_MAC], tb[NL80211_ATTR_TIMED_OUT],
-			   tb[NL80211_ATTR_WIPHY_FREQ], tb[NL80211_ATTR_ACK],
-			   tb[NL80211_ATTR_COOKIE],
-			   tb[NL80211_ATTR_RX_SIGNAL_DBM]);
-		break;
-	case NL80211_CMD_UNEXPECTED_FRAME:
-		nl80211_spurious_frame(bss, tb, 0);
-		break;
-	case NL80211_CMD_UNEXPECTED_4ADDR_FRAME:
-		nl80211_spurious_frame(bss, tb, 1);
-		break;
-	default:
-		wpa_printf(MSG_DEBUG, "nl80211: Ignored unknown event "
-			   "(cmd=%d)", gnlh->cmd);
-		break;
-	}
-
-	return NL_SKIP;
 }
 
 
@@ -3423,15 +1355,14 @@
 	alpha2[1] = alpha2_arg[1];
 	alpha2[2] = '\0';
 
-	nl80211_cmd(drv, msg, 0, NL80211_CMD_REQ_SET_REG);
-
-	NLA_PUT_STRING(msg, NL80211_ATTR_REG_ALPHA2, alpha2);
+	if (!nl80211_cmd(drv, msg, 0, NL80211_CMD_REQ_SET_REG) ||
+	    nla_put_string(msg, NL80211_ATTR_REG_ALPHA2, alpha2)) {
+		nlmsg_free(msg);
+		return -EINVAL;
+	}
 	if (send_and_recv_msgs(drv, msg, NULL, NULL))
 		return -EINVAL;
 	return 0;
-nla_put_failure:
-	nlmsg_free(msg);
-	return -EINVAL;
 }
 
 
@@ -3473,709 +1404,6 @@
 }
 
 
-static int protocol_feature_handler(struct nl_msg *msg, void *arg)
-{
-	u32 *feat = arg;
-	struct nlattr *tb_msg[NL80211_ATTR_MAX + 1];
-	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
-
-	nla_parse(tb_msg, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
-		  genlmsg_attrlen(gnlh, 0), NULL);
-
-	if (tb_msg[NL80211_ATTR_PROTOCOL_FEATURES])
-		*feat = nla_get_u32(tb_msg[NL80211_ATTR_PROTOCOL_FEATURES]);
-
-	return NL_SKIP;
-}
-
-
-static u32 get_nl80211_protocol_features(struct wpa_driver_nl80211_data *drv)
-{
-	u32 feat = 0;
-	struct nl_msg *msg;
-
-	msg = nlmsg_alloc();
-	if (!msg)
-		goto nla_put_failure;
-
-	nl80211_cmd(drv, msg, 0, NL80211_CMD_GET_PROTOCOL_FEATURES);
-	if (send_and_recv_msgs(drv, msg, protocol_feature_handler, &feat) == 0)
-		return feat;
-
-	msg = NULL;
-nla_put_failure:
-	nlmsg_free(msg);
-	return 0;
-}
-
-
-struct wiphy_info_data {
-	struct wpa_driver_nl80211_data *drv;
-	struct wpa_driver_capa *capa;
-
-	unsigned int num_multichan_concurrent;
-
-	unsigned int error:1;
-	unsigned int device_ap_sme:1;
-	unsigned int poll_command_supported:1;
-	unsigned int data_tx_status:1;
-	unsigned int monitor_supported:1;
-	unsigned int auth_supported:1;
-	unsigned int connect_supported:1;
-	unsigned int p2p_go_supported:1;
-	unsigned int p2p_client_supported:1;
-	unsigned int p2p_concurrent:1;
-	unsigned int channel_switch_supported:1;
-	unsigned int set_qos_map_supported:1;
-	unsigned int have_low_prio_scan:1;
-};
-
-
-static unsigned int probe_resp_offload_support(int supp_protocols)
-{
-	unsigned int prot = 0;
-
-	if (supp_protocols & NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS)
-		prot |= WPA_DRIVER_PROBE_RESP_OFFLOAD_WPS;
-	if (supp_protocols & NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS2)
-		prot |= WPA_DRIVER_PROBE_RESP_OFFLOAD_WPS2;
-	if (supp_protocols & NL80211_PROBE_RESP_OFFLOAD_SUPPORT_P2P)
-		prot |= WPA_DRIVER_PROBE_RESP_OFFLOAD_P2P;
-	if (supp_protocols & NL80211_PROBE_RESP_OFFLOAD_SUPPORT_80211U)
-		prot |= WPA_DRIVER_PROBE_RESP_OFFLOAD_INTERWORKING;
-
-	return prot;
-}
-
-
-static void wiphy_info_supported_iftypes(struct wiphy_info_data *info,
-					 struct nlattr *tb)
-{
-	struct nlattr *nl_mode;
-	int i;
-
-	if (tb == NULL)
-		return;
-
-	nla_for_each_nested(nl_mode, tb, i) {
-		switch (nla_type(nl_mode)) {
-		case NL80211_IFTYPE_AP:
-			info->capa->flags |= WPA_DRIVER_FLAGS_AP;
-			break;
-		case NL80211_IFTYPE_ADHOC:
-			info->capa->flags |= WPA_DRIVER_FLAGS_IBSS;
-			break;
-		case NL80211_IFTYPE_P2P_DEVICE:
-			info->capa->flags |=
-				WPA_DRIVER_FLAGS_DEDICATED_P2P_DEVICE;
-			break;
-		case NL80211_IFTYPE_P2P_GO:
-			info->p2p_go_supported = 1;
-			break;
-		case NL80211_IFTYPE_P2P_CLIENT:
-			info->p2p_client_supported = 1;
-			break;
-		case NL80211_IFTYPE_MONITOR:
-			info->monitor_supported = 1;
-			break;
-		}
-	}
-}
-
-
-static int wiphy_info_iface_comb_process(struct wiphy_info_data *info,
-					 struct nlattr *nl_combi)
-{
-	struct nlattr *tb_comb[NUM_NL80211_IFACE_COMB];
-	struct nlattr *tb_limit[NUM_NL80211_IFACE_LIMIT];
-	struct nlattr *nl_limit, *nl_mode;
-	int err, rem_limit, rem_mode;
-	int combination_has_p2p = 0, combination_has_mgd = 0;
-	static struct nla_policy
-	iface_combination_policy[NUM_NL80211_IFACE_COMB] = {
-		[NL80211_IFACE_COMB_LIMITS] = { .type = NLA_NESTED },
-		[NL80211_IFACE_COMB_MAXNUM] = { .type = NLA_U32 },
-		[NL80211_IFACE_COMB_STA_AP_BI_MATCH] = { .type = NLA_FLAG },
-		[NL80211_IFACE_COMB_NUM_CHANNELS] = { .type = NLA_U32 },
-		[NL80211_IFACE_COMB_RADAR_DETECT_WIDTHS] = { .type = NLA_U32 },
-	},
-	iface_limit_policy[NUM_NL80211_IFACE_LIMIT] = {
-		[NL80211_IFACE_LIMIT_TYPES] = { .type = NLA_NESTED },
-		[NL80211_IFACE_LIMIT_MAX] = { .type = NLA_U32 },
-	};
-
-	err = nla_parse_nested(tb_comb, MAX_NL80211_IFACE_COMB,
-			       nl_combi, iface_combination_policy);
-	if (err || !tb_comb[NL80211_IFACE_COMB_LIMITS] ||
-	    !tb_comb[NL80211_IFACE_COMB_MAXNUM] ||
-	    !tb_comb[NL80211_IFACE_COMB_NUM_CHANNELS])
-		return 0; /* broken combination */
-
-	if (tb_comb[NL80211_IFACE_COMB_RADAR_DETECT_WIDTHS])
-		info->capa->flags |= WPA_DRIVER_FLAGS_RADAR;
-
-	nla_for_each_nested(nl_limit, tb_comb[NL80211_IFACE_COMB_LIMITS],
-			    rem_limit) {
-		err = nla_parse_nested(tb_limit, MAX_NL80211_IFACE_LIMIT,
-				       nl_limit, iface_limit_policy);
-		if (err || !tb_limit[NL80211_IFACE_LIMIT_TYPES])
-			return 0; /* broken combination */
-
-		nla_for_each_nested(nl_mode,
-				    tb_limit[NL80211_IFACE_LIMIT_TYPES],
-				    rem_mode) {
-			int ift = nla_type(nl_mode);
-			if (ift == NL80211_IFTYPE_P2P_GO ||
-			    ift == NL80211_IFTYPE_P2P_CLIENT)
-				combination_has_p2p = 1;
-			if (ift == NL80211_IFTYPE_STATION)
-				combination_has_mgd = 1;
-		}
-		if (combination_has_p2p && combination_has_mgd)
-			break;
-	}
-
-	if (combination_has_p2p && combination_has_mgd) {
-		unsigned int num_channels =
-			nla_get_u32(tb_comb[NL80211_IFACE_COMB_NUM_CHANNELS]);
-
-		info->p2p_concurrent = 1;
-		if (info->num_multichan_concurrent < num_channels)
-			info->num_multichan_concurrent = num_channels;
-	}
-
-	return 0;
-}
-
-
-static void wiphy_info_iface_comb(struct wiphy_info_data *info,
-				  struct nlattr *tb)
-{
-	struct nlattr *nl_combi;
-	int rem_combi;
-
-	if (tb == NULL)
-		return;
-
-	nla_for_each_nested(nl_combi, tb, rem_combi) {
-		if (wiphy_info_iface_comb_process(info, nl_combi) > 0)
-			break;
-	}
-}
-
-
-static void wiphy_info_supp_cmds(struct wiphy_info_data *info,
-				 struct nlattr *tb)
-{
-	struct nlattr *nl_cmd;
-	int i;
-
-	if (tb == NULL)
-		return;
-
-	nla_for_each_nested(nl_cmd, tb, i) {
-		switch (nla_get_u32(nl_cmd)) {
-		case NL80211_CMD_AUTHENTICATE:
-			info->auth_supported = 1;
-			break;
-		case NL80211_CMD_CONNECT:
-			info->connect_supported = 1;
-			break;
-		case NL80211_CMD_START_SCHED_SCAN:
-			info->capa->sched_scan_supported = 1;
-			break;
-		case NL80211_CMD_PROBE_CLIENT:
-			info->poll_command_supported = 1;
-			break;
-		case NL80211_CMD_CHANNEL_SWITCH:
-			info->channel_switch_supported = 1;
-			break;
-		case NL80211_CMD_SET_QOS_MAP:
-			info->set_qos_map_supported = 1;
-			break;
-		}
-	}
-}
-
-
-static void wiphy_info_cipher_suites(struct wiphy_info_data *info,
-				     struct nlattr *tb)
-{
-	int i, num;
-	u32 *ciphers;
-
-	if (tb == NULL)
-		return;
-
-	num = nla_len(tb) / sizeof(u32);
-	ciphers = nla_data(tb);
-	for (i = 0; i < num; i++) {
-		u32 c = ciphers[i];
-
-		wpa_printf(MSG_DEBUG, "nl80211: Supported cipher %02x-%02x-%02x:%d",
-			   c >> 24, (c >> 16) & 0xff,
-			   (c >> 8) & 0xff, c & 0xff);
-		switch (c) {
-		case WLAN_CIPHER_SUITE_CCMP_256:
-			info->capa->enc |= WPA_DRIVER_CAPA_ENC_CCMP_256;
-			break;
-		case WLAN_CIPHER_SUITE_GCMP_256:
-			info->capa->enc |= WPA_DRIVER_CAPA_ENC_GCMP_256;
-			break;
-		case WLAN_CIPHER_SUITE_CCMP:
-			info->capa->enc |= WPA_DRIVER_CAPA_ENC_CCMP;
-			break;
-		case WLAN_CIPHER_SUITE_GCMP:
-			info->capa->enc |= WPA_DRIVER_CAPA_ENC_GCMP;
-			break;
-		case WLAN_CIPHER_SUITE_TKIP:
-			info->capa->enc |= WPA_DRIVER_CAPA_ENC_TKIP;
-			break;
-		case WLAN_CIPHER_SUITE_WEP104:
-			info->capa->enc |= WPA_DRIVER_CAPA_ENC_WEP104;
-			break;
-		case WLAN_CIPHER_SUITE_WEP40:
-			info->capa->enc |= WPA_DRIVER_CAPA_ENC_WEP40;
-			break;
-		case WLAN_CIPHER_SUITE_AES_CMAC:
-			info->capa->enc |= WPA_DRIVER_CAPA_ENC_BIP;
-			break;
-		case WLAN_CIPHER_SUITE_BIP_GMAC_128:
-			info->capa->enc |= WPA_DRIVER_CAPA_ENC_BIP_GMAC_128;
-			break;
-		case WLAN_CIPHER_SUITE_BIP_GMAC_256:
-			info->capa->enc |= WPA_DRIVER_CAPA_ENC_BIP_GMAC_256;
-			break;
-		case WLAN_CIPHER_SUITE_BIP_CMAC_256:
-			info->capa->enc |= WPA_DRIVER_CAPA_ENC_BIP_CMAC_256;
-			break;
-		case WLAN_CIPHER_SUITE_NO_GROUP_ADDR:
-			info->capa->enc |= WPA_DRIVER_CAPA_ENC_GTK_NOT_USED;
-			break;
-		}
-	}
-}
-
-
-static void wiphy_info_max_roc(struct wpa_driver_capa *capa,
-			       struct nlattr *tb)
-{
-	if (tb)
-		capa->max_remain_on_chan = nla_get_u32(tb);
-}
-
-
-static void wiphy_info_tdls(struct wpa_driver_capa *capa, struct nlattr *tdls,
-			    struct nlattr *ext_setup)
-{
-	if (tdls == NULL)
-		return;
-
-	wpa_printf(MSG_DEBUG, "nl80211: TDLS supported");
-	capa->flags |= WPA_DRIVER_FLAGS_TDLS_SUPPORT;
-
-	if (ext_setup) {
-		wpa_printf(MSG_DEBUG, "nl80211: TDLS external setup");
-		capa->flags |= WPA_DRIVER_FLAGS_TDLS_EXTERNAL_SETUP;
-	}
-}
-
-
-static void wiphy_info_feature_flags(struct wiphy_info_data *info,
-				     struct nlattr *tb)
-{
-	u32 flags;
-	struct wpa_driver_capa *capa = info->capa;
-
-	if (tb == NULL)
-		return;
-
-	flags = nla_get_u32(tb);
-
-	if (flags & NL80211_FEATURE_SK_TX_STATUS)
-		info->data_tx_status = 1;
-
-	if (flags & NL80211_FEATURE_INACTIVITY_TIMER)
-		capa->flags |= WPA_DRIVER_FLAGS_INACTIVITY_TIMER;
-
-	if (flags & NL80211_FEATURE_SAE)
-		capa->flags |= WPA_DRIVER_FLAGS_SAE;
-
-	if (flags & NL80211_FEATURE_NEED_OBSS_SCAN)
-		capa->flags |= WPA_DRIVER_FLAGS_OBSS_SCAN;
-
-	if (flags & NL80211_FEATURE_AP_MODE_CHAN_WIDTH_CHANGE)
-		capa->flags |= WPA_DRIVER_FLAGS_HT_2040_COEX;
-
-	if (flags & NL80211_FEATURE_LOW_PRIORITY_SCAN)
-		info->have_low_prio_scan = 1;
-}
-
-
-static void wiphy_info_probe_resp_offload(struct wpa_driver_capa *capa,
-					  struct nlattr *tb)
-{
-	u32 protocols;
-
-	if (tb == NULL)
-		return;
-
-	protocols = nla_get_u32(tb);
-	wpa_printf(MSG_DEBUG, "nl80211: Supports Probe Response offload in AP "
-		   "mode");
-	capa->flags |= WPA_DRIVER_FLAGS_PROBE_RESP_OFFLOAD;
-	capa->probe_resp_offloads = probe_resp_offload_support(protocols);
-}
-
-
-static void wiphy_info_wowlan_triggers(struct wpa_driver_capa *capa,
-				       struct nlattr *tb)
-{
-	struct nlattr *triggers[MAX_NL80211_WOWLAN_TRIG + 1];
-
-	if (tb == NULL)
-		return;
-
-	if (nla_parse_nested(triggers, MAX_NL80211_WOWLAN_TRIG,
-			     tb, NULL))
-		return;
-
-	if (triggers[NL80211_WOWLAN_TRIG_ANY])
-		capa->wowlan_triggers.any = 1;
-	if (triggers[NL80211_WOWLAN_TRIG_DISCONNECT])
-		capa->wowlan_triggers.disconnect = 1;
-	if (triggers[NL80211_WOWLAN_TRIG_MAGIC_PKT])
-		capa->wowlan_triggers.magic_pkt = 1;
-	if (triggers[NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE])
-		capa->wowlan_triggers.gtk_rekey_failure = 1;
-	if (triggers[NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST])
-		capa->wowlan_triggers.eap_identity_req = 1;
-	if (triggers[NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE])
-		capa->wowlan_triggers.four_way_handshake = 1;
-	if (triggers[NL80211_WOWLAN_TRIG_RFKILL_RELEASE])
-		capa->wowlan_triggers.rfkill_release = 1;
-}
-
-
-static int wiphy_info_handler(struct nl_msg *msg, void *arg)
-{
-	struct nlattr *tb[NL80211_ATTR_MAX + 1];
-	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
-	struct wiphy_info_data *info = arg;
-	struct wpa_driver_capa *capa = info->capa;
-	struct wpa_driver_nl80211_data *drv = info->drv;
-
-	nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
-		  genlmsg_attrlen(gnlh, 0), NULL);
-
-	if (tb[NL80211_ATTR_WIPHY_NAME])
-		os_strlcpy(drv->phyname,
-			   nla_get_string(tb[NL80211_ATTR_WIPHY_NAME]),
-			   sizeof(drv->phyname));
-	if (tb[NL80211_ATTR_MAX_NUM_SCAN_SSIDS])
-		capa->max_scan_ssids =
-			nla_get_u8(tb[NL80211_ATTR_MAX_NUM_SCAN_SSIDS]);
-
-	if (tb[NL80211_ATTR_MAX_NUM_SCHED_SCAN_SSIDS])
-		capa->max_sched_scan_ssids =
-			nla_get_u8(tb[NL80211_ATTR_MAX_NUM_SCHED_SCAN_SSIDS]);
-
-	if (tb[NL80211_ATTR_MAX_MATCH_SETS])
-		capa->max_match_sets =
-			nla_get_u8(tb[NL80211_ATTR_MAX_MATCH_SETS]);
-
-	if (tb[NL80211_ATTR_MAC_ACL_MAX])
-		capa->max_acl_mac_addrs =
-			nla_get_u8(tb[NL80211_ATTR_MAC_ACL_MAX]);
-
-	wiphy_info_supported_iftypes(info, tb[NL80211_ATTR_SUPPORTED_IFTYPES]);
-	wiphy_info_iface_comb(info, tb[NL80211_ATTR_INTERFACE_COMBINATIONS]);
-	wiphy_info_supp_cmds(info, tb[NL80211_ATTR_SUPPORTED_COMMANDS]);
-	wiphy_info_cipher_suites(info, tb[NL80211_ATTR_CIPHER_SUITES]);
-
-	if (tb[NL80211_ATTR_OFFCHANNEL_TX_OK]) {
-		wpa_printf(MSG_DEBUG, "nl80211: Using driver-based "
-			   "off-channel TX");
-		capa->flags |= WPA_DRIVER_FLAGS_OFFCHANNEL_TX;
-	}
-
-	if (tb[NL80211_ATTR_ROAM_SUPPORT]) {
-		wpa_printf(MSG_DEBUG, "nl80211: Using driver-based roaming");
-		capa->flags |= WPA_DRIVER_FLAGS_BSS_SELECTION;
-	}
-
-	wiphy_info_max_roc(capa,
-			   tb[NL80211_ATTR_MAX_REMAIN_ON_CHANNEL_DURATION]);
-
-	if (tb[NL80211_ATTR_SUPPORT_AP_UAPSD])
-		capa->flags |= WPA_DRIVER_FLAGS_AP_UAPSD;
-
-	wiphy_info_tdls(capa, tb[NL80211_ATTR_TDLS_SUPPORT],
-			tb[NL80211_ATTR_TDLS_EXTERNAL_SETUP]);
-
-	if (tb[NL80211_ATTR_DEVICE_AP_SME])
-		info->device_ap_sme = 1;
-
-	wiphy_info_feature_flags(info, tb[NL80211_ATTR_FEATURE_FLAGS]);
-	wiphy_info_probe_resp_offload(capa,
-				      tb[NL80211_ATTR_PROBE_RESP_OFFLOAD]);
-
-	if (tb[NL80211_ATTR_EXT_CAPA] && tb[NL80211_ATTR_EXT_CAPA_MASK] &&
-	    drv->extended_capa == NULL) {
-		drv->extended_capa =
-			os_malloc(nla_len(tb[NL80211_ATTR_EXT_CAPA]));
-		if (drv->extended_capa) {
-			os_memcpy(drv->extended_capa,
-				  nla_data(tb[NL80211_ATTR_EXT_CAPA]),
-				  nla_len(tb[NL80211_ATTR_EXT_CAPA]));
-			drv->extended_capa_len =
-				nla_len(tb[NL80211_ATTR_EXT_CAPA]);
-		}
-		drv->extended_capa_mask =
-			os_malloc(nla_len(tb[NL80211_ATTR_EXT_CAPA]));
-		if (drv->extended_capa_mask) {
-			os_memcpy(drv->extended_capa_mask,
-				  nla_data(tb[NL80211_ATTR_EXT_CAPA]),
-				  nla_len(tb[NL80211_ATTR_EXT_CAPA]));
-		} else {
-			os_free(drv->extended_capa);
-			drv->extended_capa = NULL;
-			drv->extended_capa_len = 0;
-		}
-	}
-
-	if (tb[NL80211_ATTR_VENDOR_DATA]) {
-		struct nlattr *nl;
-		int rem;
-
-		nla_for_each_nested(nl, tb[NL80211_ATTR_VENDOR_DATA], rem) {
-			struct nl80211_vendor_cmd_info *vinfo;
-			if (nla_len(nl) != sizeof(*vinfo)) {
-				wpa_printf(MSG_DEBUG, "nl80211: Unexpected vendor data info");
-				continue;
-			}
-			vinfo = nla_data(nl);
-			switch (vinfo->subcmd) {
-			case QCA_NL80211_VENDOR_SUBCMD_ROAMING:
-				drv->roaming_vendor_cmd_avail = 1;
-				break;
-			case QCA_NL80211_VENDOR_SUBCMD_DFS_CAPABILITY:
-				drv->dfs_vendor_cmd_avail = 1;
-				break;
-			}
-
-			wpa_printf(MSG_DEBUG, "nl80211: Supported vendor command: vendor_id=0x%x subcmd=%u",
-				   vinfo->vendor_id, vinfo->subcmd);
-		}
-	}
-
-	if (tb[NL80211_ATTR_VENDOR_EVENTS]) {
-		struct nlattr *nl;
-		int rem;
-
-		nla_for_each_nested(nl, tb[NL80211_ATTR_VENDOR_EVENTS], rem) {
-			struct nl80211_vendor_cmd_info *vinfo;
-			if (nla_len(nl) != sizeof(*vinfo)) {
-				wpa_printf(MSG_DEBUG, "nl80211: Unexpected vendor data info");
-				continue;
-			}
-			vinfo = nla_data(nl);
-			wpa_printf(MSG_DEBUG, "nl80211: Supported vendor event: vendor_id=0x%x subcmd=%u",
-				   vinfo->vendor_id, vinfo->subcmd);
-		}
-	}
-
-	wiphy_info_wowlan_triggers(capa,
-				   tb[NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED]);
-
-	if (tb[NL80211_ATTR_MAX_AP_ASSOC_STA])
-		capa->max_stations =
-			nla_get_u32(tb[NL80211_ATTR_MAX_AP_ASSOC_STA]);
-
-	return NL_SKIP;
-}
-
-
-static int wpa_driver_nl80211_get_info(struct wpa_driver_nl80211_data *drv,
-				       struct wiphy_info_data *info)
-{
-	u32 feat;
-	struct nl_msg *msg;
-
-	os_memset(info, 0, sizeof(*info));
-	info->capa = &drv->capa;
-	info->drv = drv;
-
-	msg = nlmsg_alloc();
-	if (!msg)
-		return -1;
-
-	feat = get_nl80211_protocol_features(drv);
-	if (feat & NL80211_PROTOCOL_FEATURE_SPLIT_WIPHY_DUMP)
-		nl80211_cmd(drv, msg, NLM_F_DUMP, NL80211_CMD_GET_WIPHY);
-	else
-		nl80211_cmd(drv, msg, 0, NL80211_CMD_GET_WIPHY);
-
-	NLA_PUT_FLAG(msg, NL80211_ATTR_SPLIT_WIPHY_DUMP);
-	if (nl80211_set_iface_id(msg, drv->first_bss) < 0)
-		goto nla_put_failure;
-
-	if (send_and_recv_msgs(drv, msg, wiphy_info_handler, info))
-		return -1;
-
-	if (info->auth_supported)
-		drv->capa.flags |= WPA_DRIVER_FLAGS_SME;
-	else if (!info->connect_supported) {
-		wpa_printf(MSG_INFO, "nl80211: Driver does not support "
-			   "authentication/association or connect commands");
-		info->error = 1;
-	}
-
-	if (info->p2p_go_supported && info->p2p_client_supported)
-		drv->capa.flags |= WPA_DRIVER_FLAGS_P2P_CAPABLE;
-	if (info->p2p_concurrent) {
-		wpa_printf(MSG_DEBUG, "nl80211: Use separate P2P group "
-			   "interface (driver advertised support)");
-		drv->capa.flags |= WPA_DRIVER_FLAGS_P2P_CONCURRENT;
-		drv->capa.flags |= WPA_DRIVER_FLAGS_P2P_MGMT_AND_NON_P2P;
-	}
-	if (info->num_multichan_concurrent > 1) {
-		wpa_printf(MSG_DEBUG, "nl80211: Enable multi-channel "
-			   "concurrent (driver advertised support)");
-		drv->capa.num_multichan_concurrent =
-			info->num_multichan_concurrent;
-	}
-
-	/* default to 5000 since early versions of mac80211 don't set it */
-	if (!drv->capa.max_remain_on_chan)
-		drv->capa.max_remain_on_chan = 5000;
-
-	if (info->channel_switch_supported)
-		drv->capa.flags |= WPA_DRIVER_FLAGS_AP_CSA;
-
-	return 0;
-nla_put_failure:
-	nlmsg_free(msg);
-	return -1;
-}
-
-
-static int wpa_driver_nl80211_capa(struct wpa_driver_nl80211_data *drv)
-{
-	struct wiphy_info_data info;
-	if (wpa_driver_nl80211_get_info(drv, &info))
-		return -1;
-
-	if (info.error)
-		return -1;
-
-	drv->has_capability = 1;
-	drv->capa.key_mgmt = WPA_DRIVER_CAPA_KEY_MGMT_WPA |
-		WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK |
-		WPA_DRIVER_CAPA_KEY_MGMT_WPA2 |
-		WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK;
-	drv->capa.auth = WPA_DRIVER_AUTH_OPEN |
-		WPA_DRIVER_AUTH_SHARED |
-		WPA_DRIVER_AUTH_LEAP;
-
-	drv->capa.flags |= WPA_DRIVER_FLAGS_SANE_ERROR_CODES;
-	drv->capa.flags |= WPA_DRIVER_FLAGS_SET_KEYS_AFTER_ASSOC_DONE;
-	drv->capa.flags |= WPA_DRIVER_FLAGS_EAPOL_TX_STATUS;
-
-	/*
-	 * As all cfg80211 drivers must support cases where the AP interface is
-	 * removed without the knowledge of wpa_supplicant/hostapd, e.g., in
-	 * case that the user space daemon has crashed, they must be able to
-	 * cleanup all stations and key entries in the AP tear down flow. Thus,
-	 * this flag can/should always be set for cfg80211 drivers.
-	 */
-	drv->capa.flags |= WPA_DRIVER_FLAGS_AP_TEARDOWN_SUPPORT;
-
-	if (!info.device_ap_sme) {
-		drv->capa.flags |= WPA_DRIVER_FLAGS_DEAUTH_TX_STATUS;
-
-		/*
-		 * No AP SME is currently assumed to also indicate no AP MLME
-		 * in the driver/firmware.
-		 */
-		drv->capa.flags |= WPA_DRIVER_FLAGS_AP_MLME;
-	}
-
-	drv->device_ap_sme = info.device_ap_sme;
-	drv->poll_command_supported = info.poll_command_supported;
-	drv->data_tx_status = info.data_tx_status;
-	if (info.set_qos_map_supported)
-		drv->capa.flags |= WPA_DRIVER_FLAGS_QOS_MAPPING;
-	drv->have_low_prio_scan = info.have_low_prio_scan;
-
-	/*
-	 * If poll command and tx status are supported, mac80211 is new enough
-	 * to have everything we need to not need monitor interfaces.
-	 */
-	drv->use_monitor = !info.poll_command_supported || !info.data_tx_status;
-
-	if (drv->device_ap_sme && drv->use_monitor) {
-		/*
-		 * Non-mac80211 drivers may not support monitor interface.
-		 * Make sure we do not get stuck with incorrect capability here
-		 * by explicitly testing this.
-		 */
-		if (!info.monitor_supported) {
-			wpa_printf(MSG_DEBUG, "nl80211: Disable use_monitor "
-				   "with device_ap_sme since no monitor mode "
-				   "support detected");
-			drv->use_monitor = 0;
-		}
-	}
-
-	/*
-	 * If we aren't going to use monitor interfaces, but the
-	 * driver doesn't support data TX status, we won't get TX
-	 * status for EAPOL frames.
-	 */
-	if (!drv->use_monitor && !info.data_tx_status)
-		drv->capa.flags &= ~WPA_DRIVER_FLAGS_EAPOL_TX_STATUS;
-
-	return 0;
-}
-
-
-#ifdef ANDROID
-static int android_genl_ctrl_resolve(struct nl_handle *handle,
-				     const char *name)
-{
-	/*
-	 * Android ICS has very minimal genl_ctrl_resolve() implementation, so
-	 * need to work around that.
-	 */
-	struct nl_cache *cache = NULL;
-	struct genl_family *nl80211 = NULL;
-	int id = -1;
-
-	if (genl_ctrl_alloc_cache(handle, &cache) < 0) {
-		wpa_printf(MSG_ERROR, "nl80211: Failed to allocate generic "
-			   "netlink cache");
-		goto fail;
-	}
-
-	nl80211 = genl_ctrl_search_by_name(cache, name);
-	if (nl80211 == NULL)
-		goto fail;
-
-	id = genl_family_get_id(nl80211);
-
-fail:
-	if (nl80211)
-		genl_family_put(nl80211);
-	if (cache)
-		nl_cache_free(cache);
-
-	return id;
-}
-#define genl_ctrl_resolve android_genl_ctrl_resolve
-#endif /* ANDROID */
-
-
 static int wpa_driver_nl80211_init_nl_global(struct nl80211_global *global)
 {
 	int ret;
@@ -4262,20 +1490,30 @@
 }
 
 
-static int wpa_driver_nl80211_init_nl(struct wpa_driver_nl80211_data *drv)
+static void nl80211_check_global(struct nl80211_global *global)
 {
-	drv->nl_cb = nl_cb_alloc(NL_CB_DEFAULT);
-	if (!drv->nl_cb) {
-		wpa_printf(MSG_ERROR, "nl80211: Failed to alloc cb struct");
-		return -1;
+	struct nl_handle *handle;
+	const char *groups[] = { "scan", "mlme", "regulatory", "vendor", NULL };
+	int ret;
+	unsigned int i;
+
+	/*
+	 * Try to re-add memberships to handle case of cfg80211 getting reloaded
+	 * and all registration having been cleared.
+	 */
+	handle = (void *) (((intptr_t) global->nl_event) ^
+			   ELOOP_SOCKET_INVALID);
+
+	for (i = 0; groups[i]; i++) {
+		ret = nl_get_multicast_id(global, "nl80211", groups[i]);
+		if (ret >= 0)
+			ret = nl_socket_add_membership(handle, ret);
+		if (ret < 0) {
+			wpa_printf(MSG_INFO,
+				   "nl80211: Could not re-add multicast membership for %s events: %d (%s)",
+				   groups[i], ret, strerror(-ret));
+		}
 	}
-
-	nl_cb_set(drv->nl_cb, NL_CB_SEQ_CHECK, NL_CB_CUSTOM,
-		  no_seq_check, NULL);
-	nl_cb_set(drv->nl_cb, NL_CB_VALID, NL_CB_CUSTOM,
-		  process_drv_event, drv);
-
-	return 0;
 }
 
 
@@ -4385,7 +1623,8 @@
 
 static void * wpa_driver_nl80211_drv_init(void *ctx, const char *ifname,
 					  void *global_priv, int hostapd,
-					  const u8 *set_addr)
+					  const u8 *set_addr,
+					  const char *driver_params)
 {
 	struct wpa_driver_nl80211_data *drv;
 	struct rfkill_config *rcfg;
@@ -4400,6 +1639,14 @@
 	drv->ctx = ctx;
 	drv->hostapd = !!hostapd;
 	drv->eapol_sock = -1;
+
+	/*
+	 * There is no driver capability flag for this, so assume it is
+	 * supported and disable this on first attempt to use if the driver
+	 * rejects the command due to missing support.
+	 */
+	drv->set_rekey_offload = 1;
+
 	drv->num_if_indices = sizeof(drv->default_if_indices) / sizeof(int);
 	drv->if_indices = drv->default_if_indices;
 
@@ -4418,11 +1665,6 @@
 	drv->eapol_tx_sock = -1;
 	drv->ap_scan_as_station = NL80211_IFTYPE_UNSPECIFIED;
 
-	if (wpa_driver_nl80211_init_nl(drv)) {
-		os_free(drv);
-		return NULL;
-	}
-
 	if (nl80211_init_bss(bss))
 		goto failed;
 
@@ -4442,7 +1684,7 @@
 	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))
+	if (wpa_driver_nl80211_finish_drv_init(drv, set_addr, 1, driver_params))
 		goto failed;
 
 	drv->eapol_tx_sock = socket(PF_PACKET, SOCK_DGRAM, 0);
@@ -4468,6 +1710,7 @@
 	}
 
 	if (drv->global) {
+		nl80211_check_global(drv->global);
 		dl_list_add(&drv->global->interfaces, &drv->list);
 		drv->in_interface_list = 1;
 	}
@@ -4491,7 +1734,8 @@
 static void * wpa_driver_nl80211_init(void *ctx, const char *ifname,
 				      void *global_priv)
 {
-	return wpa_driver_nl80211_drv_init(ctx, ifname, global_priv, 0, NULL);
+	return wpa_driver_nl80211_drv_init(ctx, ifname, global_priv, 0, NULL,
+					   NULL);
 }
 
 
@@ -4501,54 +1745,42 @@
 {
 	struct wpa_driver_nl80211_data *drv = bss->drv;
 	struct nl_msg *msg;
-	int ret = -1;
+	int ret;
 	char buf[30];
 
-	msg = nlmsg_alloc();
-	if (!msg)
-		return -1;
-
 	buf[0] = '\0';
 	wpa_snprintf_hex(buf, sizeof(buf), match, match_len);
 	wpa_printf(MSG_DEBUG, "nl80211: Register frame type=0x%x (%s) nl_handle=%p match=%s",
 		   type, fc2str(type), nl_handle, buf);
 
-	nl80211_cmd(drv, msg, 0, NL80211_CMD_REGISTER_ACTION);
-
-	if (nl80211_set_iface_id(msg, bss) < 0)
-		goto nla_put_failure;
-
-	NLA_PUT_U16(msg, NL80211_ATTR_FRAME_TYPE, type);
-	NLA_PUT(msg, NL80211_ATTR_FRAME_MATCH, match_len, match);
+	if (!(msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_REGISTER_ACTION)) ||
+	    nla_put_u16(msg, NL80211_ATTR_FRAME_TYPE, type) ||
+	    nla_put(msg, NL80211_ATTR_FRAME_MATCH, match_len, match)) {
+		nlmsg_free(msg);
+		return -1;
+	}
 
 	ret = send_and_recv(drv->global, nl_handle, msg, NULL, NULL);
-	msg = NULL;
 	if (ret) {
 		wpa_printf(MSG_DEBUG, "nl80211: Register frame command "
 			   "failed (type=%u): ret=%d (%s)",
 			   type, ret, strerror(-ret));
 		wpa_hexdump(MSG_DEBUG, "nl80211: Register frame match",
 			    match, match_len);
-		goto nla_put_failure;
 	}
-	ret = 0;
-nla_put_failure:
-	nlmsg_free(msg);
 	return ret;
 }
 
 
 static int nl80211_alloc_mgmt_handle(struct i802_bss *bss)
 {
-	struct wpa_driver_nl80211_data *drv = bss->drv;
-
 	if (bss->nl_mgmt) {
 		wpa_printf(MSG_DEBUG, "nl80211: Mgmt reporting "
 			   "already on! (nl_mgmt=%p)", bss->nl_mgmt);
 		return -1;
 	}
 
-	bss->nl_mgmt = nl_create_handle(drv->nl_cb, "mgmt");
+	bss->nl_mgmt = nl_create_handle(bss->nl_cb, "mgmt");
 	if (bss->nl_mgmt == NULL)
 		return -1;
 
@@ -4667,6 +1899,57 @@
 		ret = -1;
 #endif /* CONFIG_HS20 */
 
+	/* WMM-AC ADDTS Response */
+	if (nl80211_register_action_frame(bss, (u8 *) "\x11\x01", 2) < 0)
+		ret = -1;
+
+	/* WMM-AC DELTS */
+	if (nl80211_register_action_frame(bss, (u8 *) "\x11\x02", 2) < 0)
+		ret = -1;
+
+	/* Radio Measurement - Neighbor Report Response */
+	if (nl80211_register_action_frame(bss, (u8 *) "\x05\x05", 2) < 0)
+		ret = -1;
+
+	/* Radio Measurement - Link Measurement Request */
+	if ((drv->capa.rrm_flags & WPA_DRIVER_FLAGS_TX_POWER_INSERTION) &&
+	    (nl80211_register_action_frame(bss, (u8 *) "\x05\x02", 2) < 0))
+		ret = -1;
+
+	nl80211_mgmt_handle_register_eloop(bss);
+
+	return ret;
+}
+
+
+static int nl80211_mgmt_subscribe_mesh(struct i802_bss *bss)
+{
+	int ret = 0;
+
+	if (nl80211_alloc_mgmt_handle(bss))
+		return -1;
+
+	wpa_printf(MSG_DEBUG,
+		   "nl80211: Subscribe to mgmt frames with mesh handle %p",
+		   bss->nl_mgmt);
+
+	/* Auth frames for mesh SAE */
+	if (nl80211_register_frame(bss, bss->nl_mgmt,
+				   (WLAN_FC_TYPE_MGMT << 2) |
+				   (WLAN_FC_STYPE_AUTH << 4),
+				   NULL, 0) < 0)
+		ret = -1;
+
+	/* Mesh peering open */
+	if (nl80211_register_action_frame(bss, (u8 *) "\x0f\x01", 2) < 0)
+		ret = -1;
+	/* Mesh peering confirm */
+	if (nl80211_register_action_frame(bss, (u8 *) "\x0f\x02", 2) < 0)
+		ret = -1;
+	/* Mesh peering close */
+	if (nl80211_register_action_frame(bss, (u8 *) "\x0f\x03", 2) < 0)
+		ret = -1;
+
 	nl80211_mgmt_handle_register_eloop(bss);
 
 	return ret;
@@ -4675,29 +1958,16 @@
 
 static int nl80211_register_spurious_class3(struct i802_bss *bss)
 {
-	struct wpa_driver_nl80211_data *drv = bss->drv;
 	struct nl_msg *msg;
-	int ret = -1;
+	int ret;
 
-	msg = nlmsg_alloc();
-	if (!msg)
-		return -1;
-
-	nl80211_cmd(drv, msg, 0, NL80211_CMD_UNEXPECTED_FRAME);
-
-	NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, bss->ifindex);
-
-	ret = send_and_recv(drv->global, bss->nl_mgmt, msg, NULL, NULL);
-	msg = NULL;
+	msg = nl80211_bss_msg(bss, 0, NL80211_CMD_UNEXPECTED_FRAME);
+	ret = send_and_recv(bss->drv->global, bss->nl_mgmt, msg, NULL, NULL);
 	if (ret) {
 		wpa_printf(MSG_DEBUG, "nl80211: Register spurious class3 "
 			   "failed: ret=%d (%s)",
 			   ret, strerror(-ret));
-		goto nla_put_failure;
 	}
-	ret = 0;
-nla_put_failure:
-	nlmsg_free(msg);
 	return ret;
 }
 
@@ -4792,56 +2062,31 @@
 
 static void nl80211_del_p2pdev(struct i802_bss *bss)
 {
-	struct wpa_driver_nl80211_data *drv = bss->drv;
 	struct nl_msg *msg;
 	int ret;
 
-	msg = nlmsg_alloc();
-	if (!msg)
-		return;
-
-	nl80211_cmd(drv, msg, 0, NL80211_CMD_DEL_INTERFACE);
-	NLA_PUT_U64(msg, NL80211_ATTR_WDEV, bss->wdev_id);
-
-	ret = send_and_recv_msgs(drv, msg, NULL, NULL);
-	msg = NULL;
+	msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_DEL_INTERFACE);
+	ret = send_and_recv_msgs(bss->drv, msg, NULL, NULL);
 
 	wpa_printf(MSG_DEBUG, "nl80211: Delete P2P Device %s (0x%llx): %s",
 		   bss->ifname, (long long unsigned int) bss->wdev_id,
 		   strerror(-ret));
-
-nla_put_failure:
-	nlmsg_free(msg);
 }
 
 
 static int nl80211_set_p2pdev(struct i802_bss *bss, int start)
 {
-	struct wpa_driver_nl80211_data *drv = bss->drv;
 	struct nl_msg *msg;
-	int ret = -1;
+	int ret;
 
-	msg = nlmsg_alloc();
-	if (!msg)
-		return -1;
-
-	if (start)
-		nl80211_cmd(drv, msg, 0, NL80211_CMD_START_P2P_DEVICE);
-	else
-		nl80211_cmd(drv, msg, 0, NL80211_CMD_STOP_P2P_DEVICE);
-
-	NLA_PUT_U64(msg, NL80211_ATTR_WDEV, bss->wdev_id);
-
-	ret = send_and_recv_msgs(drv, msg, NULL, NULL);
-	msg = NULL;
+	msg = nl80211_cmd_msg(bss, 0, start ? NL80211_CMD_START_P2P_DEVICE :
+			      NL80211_CMD_STOP_P2P_DEVICE);
+	ret = send_and_recv_msgs(bss->drv, msg, NULL, NULL);
 
 	wpa_printf(MSG_DEBUG, "nl80211: %s P2P Device %s (0x%llx): %s",
 		   start ? "Start" : "Stop",
 		   bss->ifname, (long long unsigned int) bss->wdev_id,
 		   strerror(-ret));
-
-nla_put_failure:
-	nlmsg_free(msg);
 	return ret;
 }
 
@@ -4861,9 +2106,64 @@
 }
 
 
+#ifdef CONFIG_TESTING_OPTIONS
+static int qca_vendor_test_cmd_handler(struct nl_msg *msg, void *arg)
+{
+	/* struct wpa_driver_nl80211_data *drv = arg; */
+	struct nlattr *tb[NL80211_ATTR_MAX + 1];
+	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+
+
+	wpa_printf(MSG_DEBUG,
+		   "nl80211: QCA vendor test command response received");
+
+	nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+		  genlmsg_attrlen(gnlh, 0), NULL);
+	if (!tb[NL80211_ATTR_VENDOR_DATA]) {
+		wpa_printf(MSG_DEBUG, "nl80211: No vendor data attribute");
+		return NL_SKIP;
+	}
+
+	wpa_hexdump(MSG_DEBUG,
+		    "nl80211: Received QCA vendor test command response",
+		    nla_data(tb[NL80211_ATTR_VENDOR_DATA]),
+		    nla_len(tb[NL80211_ATTR_VENDOR_DATA]));
+
+	return NL_SKIP;
+}
+#endif /* CONFIG_TESTING_OPTIONS */
+
+
+static void qca_vendor_test(struct wpa_driver_nl80211_data *drv)
+{
+#ifdef CONFIG_TESTING_OPTIONS
+	struct nl_msg *msg;
+	struct nlattr *params;
+	int ret;
+
+	if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
+	    nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
+	    nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
+			QCA_NL80211_VENDOR_SUBCMD_TEST) ||
+	    !(params = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA)) ||
+	    nla_put_u32(msg, QCA_WLAN_VENDOR_ATTR_TEST, 123)) {
+		nlmsg_free(msg);
+		return;
+	}
+	nla_nest_end(msg, params);
+
+	ret = send_and_recv_msgs(drv, msg, qca_vendor_test_cmd_handler, drv);
+	wpa_printf(MSG_DEBUG,
+		   "nl80211: QCA vendor test command returned %d (%s)",
+		   ret, strerror(-ret));
+#endif /* CONFIG_TESTING_OPTIONS */
+}
+
+
 static int
 wpa_driver_nl80211_finish_drv_init(struct wpa_driver_nl80211_data *drv,
-				   const u8 *set_addr, int first)
+				   const u8 *set_addr, int first,
+				   const char *driver_params)
 {
 	struct i802_bss *bss = drv->first_bss;
 	int send_rfkill_event = 0;
@@ -4884,6 +2184,9 @@
 	if (wpa_driver_nl80211_capa(drv))
 		return -1;
 
+	if (driver_params && nl80211_set_param(bss, driver_params) < 0)
+		return -1;
+
 	wpa_printf(MSG_DEBUG, "nl80211: interface %s in phy %s",
 		   bss->ifname, drv->phyname);
 
@@ -4943,6 +2246,9 @@
 				       drv, drv->ctx);
 	}
 
+	if (drv->vendor_cmd_test_avail)
+		qca_vendor_test(drv);
+
 	return 0;
 }
 
@@ -4951,19 +2257,10 @@
 {
 	struct nl_msg *msg;
 
-	msg = nlmsg_alloc();
-	if (!msg)
-		return -ENOMEM;
-
 	wpa_printf(MSG_DEBUG, "nl80211: Remove beacon (ifindex=%d)",
 		   drv->ifindex);
-	nl80211_cmd(drv, msg, 0, NL80211_CMD_DEL_BEACON);
-	NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
-
+	msg = nl80211_drv_msg(drv, 0, NL80211_CMD_DEL_BEACON);
 	return send_and_recv_msgs(drv, msg, NULL, NULL);
- nla_put_failure:
-	nlmsg_free(msg);
-	return -ENOBUFS;
 }
 
 
@@ -4978,6 +2275,9 @@
 {
 	struct wpa_driver_nl80211_data *drv = bss->drv;
 
+	wpa_printf(MSG_INFO, "nl80211: deinit ifname=%s disabled_11b_rates=%d",
+		   bss->ifname, drv->disabled_11b_rates);
+
 	bss->in_deinit = 1;
 	if (drv->data_tx_status)
 		eloop_unregister_read_sock(drv->eapol_tx_sock);
@@ -4996,6 +2296,11 @@
 			nl80211_handle_destroy(drv->rtnl_sk);
 	}
 	if (bss->added_bridge) {
+		if (linux_set_iface_flags(drv->global->ioctl_sock, bss->brname,
+					  0) < 0)
+			wpa_printf(MSG_INFO,
+				   "nl80211: Could not set bridge %s down",
+				   bss->brname);
 		if (linux_br_del(drv->global->ioctl_sock, bss->brname) < 0)
 			wpa_printf(MSG_INFO, "nl80211: Failed to remove "
 				   "bridge %s: %s",
@@ -5029,7 +2334,11 @@
 		(void) i802_set_iface_flags(bss, 0);
 
 	if (drv->addr_changed) {
-		linux_set_iface_flags(drv->global->ioctl_sock, bss->ifname, 0);
+		if (linux_set_iface_flags(drv->global->ioctl_sock, bss->ifname,
+					  0) < 0) {
+			wpa_printf(MSG_DEBUG,
+				   "nl80211: Could not set interface down to restore permanent MAC address");
+		}
 		if (linux_set_ifhwaddr(drv->global->ioctl_sock, bss->ifname,
 				       drv->perm_addr) < 0) {
 			wpa_printf(MSG_DEBUG,
@@ -5046,7 +2355,6 @@
 		nl80211_mgmt_unsubscribe(bss, "deinit");
 		nl80211_del_p2pdev(bss);
 	}
-	nl_cb_put(drv->nl_cb);
 
 	nl80211_destroy_bss(drv->first_bss);
 
@@ -5064,720 +2372,6 @@
 }
 
 
-/**
- * wpa_driver_nl80211_scan_timeout - Scan timeout to report scan completion
- * @eloop_ctx: Driver private data
- * @timeout_ctx: ctx argument given to wpa_driver_nl80211_init()
- *
- * This function can be used as registered timeout when starting a scan to
- * generate a scan completed event if the driver does not report this.
- */
-static void wpa_driver_nl80211_scan_timeout(void *eloop_ctx, void *timeout_ctx)
-{
-	struct wpa_driver_nl80211_data *drv = eloop_ctx;
-	if (drv->ap_scan_as_station != NL80211_IFTYPE_UNSPECIFIED) {
-		wpa_driver_nl80211_set_mode(drv->first_bss,
-					    drv->ap_scan_as_station);
-		drv->ap_scan_as_station = NL80211_IFTYPE_UNSPECIFIED;
-	}
-	wpa_printf(MSG_DEBUG, "Scan timeout - try to get results");
-	wpa_supplicant_event(timeout_ctx, EVENT_SCAN_RESULTS, NULL);
-}
-
-
-static struct nl_msg *
-nl80211_scan_common(struct wpa_driver_nl80211_data *drv, u8 cmd,
-		    struct wpa_driver_scan_params *params, u64 *wdev_id)
-{
-	struct nl_msg *msg;
-	size_t i;
-	u32 scan_flags = 0;
-
-	msg = nlmsg_alloc();
-	if (!msg)
-		return NULL;
-
-	nl80211_cmd(drv, msg, 0, cmd);
-
-	if (!wdev_id)
-		NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
-	else
-		NLA_PUT_U64(msg, NL80211_ATTR_WDEV, *wdev_id);
-
-	if (params->num_ssids) {
-		struct nlattr *ssids;
-
-		ssids = nla_nest_start(msg, NL80211_ATTR_SCAN_SSIDS);
-		if (ssids == NULL)
-			goto fail;
-		for (i = 0; i < params->num_ssids; i++) {
-			wpa_hexdump_ascii(MSG_MSGDUMP, "nl80211: Scan SSID",
-					  params->ssids[i].ssid,
-					  params->ssids[i].ssid_len);
-			if (nla_put(msg, i + 1, params->ssids[i].ssid_len,
-				    params->ssids[i].ssid) < 0)
-				goto fail;
-		}
-		nla_nest_end(msg, ssids);
-	}
-
-	if (params->extra_ies) {
-		wpa_hexdump(MSG_MSGDUMP, "nl80211: Scan extra IEs",
-			    params->extra_ies, params->extra_ies_len);
-		if (nla_put(msg, NL80211_ATTR_IE, params->extra_ies_len,
-			    params->extra_ies) < 0)
-			goto fail;
-	}
-
-	if (params->freqs) {
-		struct nlattr *freqs;
-		freqs = nla_nest_start(msg, NL80211_ATTR_SCAN_FREQUENCIES);
-		if (freqs == NULL)
-			goto fail;
-		for (i = 0; params->freqs[i]; i++) {
-			wpa_printf(MSG_MSGDUMP, "nl80211: Scan frequency %u "
-				   "MHz", params->freqs[i]);
-			if (nla_put_u32(msg, i + 1, params->freqs[i]) < 0)
-				goto fail;
-		}
-		nla_nest_end(msg, freqs);
-	}
-
-	os_free(drv->filter_ssids);
-	drv->filter_ssids = params->filter_ssids;
-	params->filter_ssids = NULL;
-	drv->num_filter_ssids = params->num_filter_ssids;
-
-	if (params->only_new_results) {
-		wpa_printf(MSG_DEBUG, "nl80211: Add NL80211_SCAN_FLAG_FLUSH");
-		scan_flags |= NL80211_SCAN_FLAG_FLUSH;
-	}
-
-	if (params->low_priority && drv->have_low_prio_scan) {
-		wpa_printf(MSG_DEBUG,
-			   "nl80211: Add NL80211_SCAN_FLAG_LOW_PRIORITY");
-		scan_flags |= NL80211_SCAN_FLAG_LOW_PRIORITY;
-	}
-
-	if (scan_flags)
-		NLA_PUT_U32(msg, NL80211_ATTR_SCAN_FLAGS, scan_flags);
-
-	return msg;
-
-fail:
-nla_put_failure:
-	nlmsg_free(msg);
-	return NULL;
-}
-
-
-/**
- * wpa_driver_nl80211_scan - Request the driver to initiate scan
- * @bss: Pointer to private driver data from wpa_driver_nl80211_init()
- * @params: Scan parameters
- * Returns: 0 on success, -1 on failure
- */
-static int wpa_driver_nl80211_scan(struct i802_bss *bss,
-				   struct wpa_driver_scan_params *params)
-{
-	struct wpa_driver_nl80211_data *drv = bss->drv;
-	int ret = -1, timeout;
-	struct nl_msg *msg = NULL;
-
-	wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: scan request");
-	drv->scan_for_auth = 0;
-
-	msg = nl80211_scan_common(drv, NL80211_CMD_TRIGGER_SCAN, params,
-				  bss->wdev_id_set ? &bss->wdev_id : NULL);
-	if (!msg)
-		return -1;
-
-	if (params->p2p_probe) {
-		struct nlattr *rates;
-
-		wpa_printf(MSG_DEBUG, "nl80211: P2P probe - mask SuppRates");
-
-		rates = nla_nest_start(msg, NL80211_ATTR_SCAN_SUPP_RATES);
-		if (rates == NULL)
-			goto nla_put_failure;
-
-		/*
-		 * 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.
-		 */
-		NLA_PUT(msg, NL80211_BAND_2GHZ, 8,
-			"\x0c\x12\x18\x24\x30\x48\x60\x6c");
-		nla_nest_end(msg, rates);
-
-		NLA_PUT_FLAG(msg, NL80211_ATTR_TX_NO_CCK_RATE);
-	}
-
-	ret = send_and_recv_msgs(drv, msg, NULL, NULL);
-	msg = NULL;
-	if (ret) {
-		wpa_printf(MSG_DEBUG, "nl80211: Scan trigger failed: ret=%d "
-			   "(%s)", ret, strerror(-ret));
-		if (drv->hostapd && is_ap_interface(drv->nlmode)) {
-			enum nl80211_iftype old_mode = drv->nlmode;
-
-			/*
-			 * mac80211 does not allow scan requests in AP mode, so
-			 * try to do this in station mode.
-			 */
-			if (wpa_driver_nl80211_set_mode(
-				    bss, NL80211_IFTYPE_STATION))
-				goto nla_put_failure;
-
-			if (wpa_driver_nl80211_scan(bss, params)) {
-				wpa_driver_nl80211_set_mode(bss, drv->nlmode);
-				goto nla_put_failure;
-			}
-
-			/* Restore AP mode when processing scan results */
-			drv->ap_scan_as_station = old_mode;
-			ret = 0;
-		} else
-			goto nla_put_failure;
-	}
-
-	drv->scan_state = SCAN_REQUESTED;
-	/* Not all drivers generate "scan completed" wireless event, so try to
-	 * read results after a timeout. */
-	timeout = 10;
-	if (drv->scan_complete_events) {
-		/*
-		 * The driver seems to deliver events to notify when scan is
-		 * complete, so use longer timeout to avoid race conditions
-		 * with scanning and following association request.
-		 */
-		timeout = 30;
-	}
-	wpa_printf(MSG_DEBUG, "Scan requested (ret=%d) - scan timeout %d "
-		   "seconds", ret, timeout);
-	eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, drv, drv->ctx);
-	eloop_register_timeout(timeout, 0, wpa_driver_nl80211_scan_timeout,
-			       drv, drv->ctx);
-
-nla_put_failure:
-	nlmsg_free(msg);
-	return ret;
-}
-
-
-/**
- * wpa_driver_nl80211_sched_scan - Initiate a scheduled scan
- * @priv: Pointer to private driver data from wpa_driver_nl80211_init()
- * @params: Scan parameters
- * @interval: Interval between scan cycles in milliseconds
- * Returns: 0 on success, -1 on failure or if not supported
- */
-static int wpa_driver_nl80211_sched_scan(void *priv,
-					 struct wpa_driver_scan_params *params,
-					 u32 interval)
-{
-	struct i802_bss *bss = priv;
-	struct wpa_driver_nl80211_data *drv = bss->drv;
-	int ret = -1;
-	struct nl_msg *msg;
-	size_t i;
-
-	wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: sched_scan request");
-
-#ifdef ANDROID
-	if (!drv->capa.sched_scan_supported)
-		return android_pno_start(bss, params);
-#endif /* ANDROID */
-
-	msg = nl80211_scan_common(drv, NL80211_CMD_START_SCHED_SCAN, params,
-				  bss->wdev_id_set ? &bss->wdev_id : NULL);
-	if (!msg)
-		goto nla_put_failure;
-
-	NLA_PUT_U32(msg, NL80211_ATTR_SCHED_SCAN_INTERVAL, interval);
-
-	if ((drv->num_filter_ssids &&
-	    (int) drv->num_filter_ssids <= drv->capa.max_match_sets) ||
-	    params->filter_rssi) {
-		struct nlattr *match_sets;
-		match_sets = nla_nest_start(msg, NL80211_ATTR_SCHED_SCAN_MATCH);
-		if (match_sets == NULL)
-			goto nla_put_failure;
-
-		for (i = 0; i < drv->num_filter_ssids; i++) {
-			struct nlattr *match_set_ssid;
-			wpa_hexdump_ascii(MSG_MSGDUMP,
-					  "nl80211: Sched scan filter SSID",
-					  drv->filter_ssids[i].ssid,
-					  drv->filter_ssids[i].ssid_len);
-
-			match_set_ssid = nla_nest_start(msg, i + 1);
-			if (match_set_ssid == NULL)
-				goto nla_put_failure;
-			NLA_PUT(msg, NL80211_ATTR_SCHED_SCAN_MATCH_SSID,
-				drv->filter_ssids[i].ssid_len,
-				drv->filter_ssids[i].ssid);
-			if (params->filter_rssi)
-				NLA_PUT_U32(msg,
-					    NL80211_SCHED_SCAN_MATCH_ATTR_RSSI,
-					    params->filter_rssi);
-
-			nla_nest_end(msg, match_set_ssid);
-		}
-
-		/*
-		 * Due to backward compatibility code, newer kernels treat this
-		 * matchset (with only an RSSI filter) as the default for all
-		 * other matchsets, unless it's the only one, in which case the
-		 * matchset will actually allow all SSIDs above the RSSI.
-		 */
-		if (params->filter_rssi) {
-			struct nlattr *match_set_rssi;
-			match_set_rssi = nla_nest_start(msg, 0);
-			if (match_set_rssi == NULL)
-				goto nla_put_failure;
-			NLA_PUT_U32(msg, NL80211_SCHED_SCAN_MATCH_ATTR_RSSI,
-				    params->filter_rssi);
-			wpa_printf(MSG_MSGDUMP,
-				   "nl80211: Sched scan RSSI filter %d dBm",
-				   params->filter_rssi);
-			nla_nest_end(msg, match_set_rssi);
-		}
-
-		nla_nest_end(msg, match_sets);
-	}
-
-	ret = send_and_recv_msgs(drv, msg, NULL, NULL);
-
-	/* TODO: if we get an error here, we should fall back to normal scan */
-
-	msg = NULL;
-	if (ret) {
-		wpa_printf(MSG_DEBUG, "nl80211: Sched scan start failed: "
-			   "ret=%d (%s)", ret, strerror(-ret));
-		goto nla_put_failure;
-	}
-
-	wpa_printf(MSG_DEBUG, "nl80211: Sched scan requested (ret=%d) - "
-		   "scan interval %d msec", ret, interval);
-
-nla_put_failure:
-	nlmsg_free(msg);
-	return ret;
-}
-
-
-/**
- * wpa_driver_nl80211_stop_sched_scan - Stop a scheduled scan
- * @priv: Pointer to private driver data from wpa_driver_nl80211_init()
- * Returns: 0 on success, -1 on failure or if not supported
- */
-static int wpa_driver_nl80211_stop_sched_scan(void *priv)
-{
-	struct i802_bss *bss = priv;
-	struct wpa_driver_nl80211_data *drv = bss->drv;
-	int ret = 0;
-	struct nl_msg *msg;
-
-#ifdef ANDROID
-	if (!drv->capa.sched_scan_supported)
-		return android_pno_stop(bss);
-#endif /* ANDROID */
-
-	msg = nlmsg_alloc();
-	if (!msg)
-		return -1;
-
-	nl80211_cmd(drv, msg, 0, NL80211_CMD_STOP_SCHED_SCAN);
-
-	NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
-
-	ret = send_and_recv_msgs(drv, msg, NULL, NULL);
-	msg = NULL;
-	if (ret) {
-		wpa_printf(MSG_DEBUG, "nl80211: Sched scan stop failed: "
-			   "ret=%d (%s)", ret, strerror(-ret));
-		goto nla_put_failure;
-	}
-
-	wpa_printf(MSG_DEBUG, "nl80211: Sched scan stop sent (ret=%d)", ret);
-
-nla_put_failure:
-	nlmsg_free(msg);
-	return ret;
-}
-
-
-static const u8 * nl80211_get_ie(const u8 *ies, size_t ies_len, u8 ie)
-{
-	const u8 *end, *pos;
-
-	if (ies == NULL)
-		return NULL;
-
-	pos = ies;
-	end = ies + ies_len;
-
-	while (pos + 1 < end) {
-		if (pos + 2 + pos[1] > end)
-			break;
-		if (pos[0] == ie)
-			return pos;
-		pos += 2 + pos[1];
-	}
-
-	return NULL;
-}
-
-
-static int nl80211_scan_filtered(struct wpa_driver_nl80211_data *drv,
-				 const u8 *ie, size_t ie_len)
-{
-	const u8 *ssid;
-	size_t i;
-
-	if (drv->filter_ssids == NULL)
-		return 0;
-
-	ssid = nl80211_get_ie(ie, ie_len, WLAN_EID_SSID);
-	if (ssid == NULL)
-		return 1;
-
-	for (i = 0; i < drv->num_filter_ssids; i++) {
-		if (ssid[1] == drv->filter_ssids[i].ssid_len &&
-		    os_memcmp(ssid + 2, drv->filter_ssids[i].ssid, ssid[1]) ==
-		    0)
-			return 0;
-	}
-
-	return 1;
-}
-
-
-static int bss_info_handler(struct nl_msg *msg, void *arg)
-{
-	struct nlattr *tb[NL80211_ATTR_MAX + 1];
-	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
-	struct nlattr *bss[NL80211_BSS_MAX + 1];
-	static struct nla_policy bss_policy[NL80211_BSS_MAX + 1] = {
-		[NL80211_BSS_BSSID] = { .type = NLA_UNSPEC },
-		[NL80211_BSS_FREQUENCY] = { .type = NLA_U32 },
-		[NL80211_BSS_TSF] = { .type = NLA_U64 },
-		[NL80211_BSS_BEACON_INTERVAL] = { .type = NLA_U16 },
-		[NL80211_BSS_CAPABILITY] = { .type = NLA_U16 },
-		[NL80211_BSS_INFORMATION_ELEMENTS] = { .type = NLA_UNSPEC },
-		[NL80211_BSS_SIGNAL_MBM] = { .type = NLA_U32 },
-		[NL80211_BSS_SIGNAL_UNSPEC] = { .type = NLA_U8 },
-		[NL80211_BSS_STATUS] = { .type = NLA_U32 },
-		[NL80211_BSS_SEEN_MS_AGO] = { .type = NLA_U32 },
-		[NL80211_BSS_BEACON_IES] = { .type = NLA_UNSPEC },
-	};
-	struct nl80211_bss_info_arg *_arg = arg;
-	struct wpa_scan_results *res = _arg->res;
-	struct wpa_scan_res **tmp;
-	struct wpa_scan_res *r;
-	const u8 *ie, *beacon_ie;
-	size_t ie_len, beacon_ie_len;
-	u8 *pos;
-	size_t i;
-
-	nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
-		  genlmsg_attrlen(gnlh, 0), NULL);
-	if (!tb[NL80211_ATTR_BSS])
-		return NL_SKIP;
-	if (nla_parse_nested(bss, NL80211_BSS_MAX, tb[NL80211_ATTR_BSS],
-			     bss_policy))
-		return NL_SKIP;
-	if (bss[NL80211_BSS_STATUS]) {
-		enum nl80211_bss_status status;
-		status = nla_get_u32(bss[NL80211_BSS_STATUS]);
-		if (status == NL80211_BSS_STATUS_ASSOCIATED &&
-		    bss[NL80211_BSS_FREQUENCY]) {
-			_arg->assoc_freq =
-				nla_get_u32(bss[NL80211_BSS_FREQUENCY]);
-			wpa_printf(MSG_DEBUG, "nl80211: Associated on %u MHz",
-				   _arg->assoc_freq);
-		}
-		if (status == NL80211_BSS_STATUS_IBSS_JOINED &&
-		    bss[NL80211_BSS_FREQUENCY]) {
-			_arg->ibss_freq =
-				nla_get_u32(bss[NL80211_BSS_FREQUENCY]);
-			wpa_printf(MSG_DEBUG, "nl80211: IBSS-joined on %u MHz",
-				   _arg->ibss_freq);
-		}
-		if (status == NL80211_BSS_STATUS_ASSOCIATED &&
-		    bss[NL80211_BSS_BSSID]) {
-			os_memcpy(_arg->assoc_bssid,
-				  nla_data(bss[NL80211_BSS_BSSID]), ETH_ALEN);
-			wpa_printf(MSG_DEBUG, "nl80211: Associated with "
-				   MACSTR, MAC2STR(_arg->assoc_bssid));
-		}
-	}
-	if (!res)
-		return NL_SKIP;
-	if (bss[NL80211_BSS_INFORMATION_ELEMENTS]) {
-		ie = nla_data(bss[NL80211_BSS_INFORMATION_ELEMENTS]);
-		ie_len = nla_len(bss[NL80211_BSS_INFORMATION_ELEMENTS]);
-	} else {
-		ie = NULL;
-		ie_len = 0;
-	}
-	if (bss[NL80211_BSS_BEACON_IES]) {
-		beacon_ie = nla_data(bss[NL80211_BSS_BEACON_IES]);
-		beacon_ie_len = nla_len(bss[NL80211_BSS_BEACON_IES]);
-	} else {
-		beacon_ie = NULL;
-		beacon_ie_len = 0;
-	}
-
-	if (nl80211_scan_filtered(_arg->drv, ie ? ie : beacon_ie,
-				  ie ? ie_len : beacon_ie_len))
-		return NL_SKIP;
-
-	r = os_zalloc(sizeof(*r) + ie_len + beacon_ie_len);
-	if (r == NULL)
-		return NL_SKIP;
-	if (bss[NL80211_BSS_BSSID])
-		os_memcpy(r->bssid, nla_data(bss[NL80211_BSS_BSSID]),
-			  ETH_ALEN);
-	if (bss[NL80211_BSS_FREQUENCY])
-		r->freq = nla_get_u32(bss[NL80211_BSS_FREQUENCY]);
-	if (bss[NL80211_BSS_BEACON_INTERVAL])
-		r->beacon_int = nla_get_u16(bss[NL80211_BSS_BEACON_INTERVAL]);
-	if (bss[NL80211_BSS_CAPABILITY])
-		r->caps = nla_get_u16(bss[NL80211_BSS_CAPABILITY]);
-	r->flags |= WPA_SCAN_NOISE_INVALID;
-	if (bss[NL80211_BSS_SIGNAL_MBM]) {
-		r->level = nla_get_u32(bss[NL80211_BSS_SIGNAL_MBM]);
-		r->level /= 100; /* mBm to dBm */
-		r->flags |= WPA_SCAN_LEVEL_DBM | WPA_SCAN_QUAL_INVALID;
-	} else if (bss[NL80211_BSS_SIGNAL_UNSPEC]) {
-		r->level = nla_get_u8(bss[NL80211_BSS_SIGNAL_UNSPEC]);
-		r->flags |= WPA_SCAN_QUAL_INVALID;
-	} else
-		r->flags |= WPA_SCAN_LEVEL_INVALID | WPA_SCAN_QUAL_INVALID;
-	if (bss[NL80211_BSS_TSF])
-		r->tsf = nla_get_u64(bss[NL80211_BSS_TSF]);
-	if (bss[NL80211_BSS_SEEN_MS_AGO])
-		r->age = nla_get_u32(bss[NL80211_BSS_SEEN_MS_AGO]);
-	r->ie_len = ie_len;
-	pos = (u8 *) (r + 1);
-	if (ie) {
-		os_memcpy(pos, ie, ie_len);
-		pos += ie_len;
-	}
-	r->beacon_ie_len = beacon_ie_len;
-	if (beacon_ie)
-		os_memcpy(pos, beacon_ie, beacon_ie_len);
-
-	if (bss[NL80211_BSS_STATUS]) {
-		enum nl80211_bss_status status;
-		status = nla_get_u32(bss[NL80211_BSS_STATUS]);
-		switch (status) {
-		case NL80211_BSS_STATUS_AUTHENTICATED:
-			r->flags |= WPA_SCAN_AUTHENTICATED;
-			break;
-		case NL80211_BSS_STATUS_ASSOCIATED:
-			r->flags |= WPA_SCAN_ASSOCIATED;
-			break;
-		default:
-			break;
-		}
-	}
-
-	/*
-	 * cfg80211 maintains separate BSS table entries for APs if the same
-	 * BSSID,SSID pair is seen on multiple channels. wpa_supplicant does
-	 * not use frequency as a separate key in the BSS table, so filter out
-	 * duplicated entries. Prefer associated BSS entry in such a case in
-	 * order to get the correct frequency into the BSS table. Similarly,
-	 * prefer newer entries over older.
-	 */
-	for (i = 0; i < res->num; i++) {
-		const u8 *s1, *s2;
-		if (os_memcmp(res->res[i]->bssid, r->bssid, ETH_ALEN) != 0)
-			continue;
-
-		s1 = nl80211_get_ie((u8 *) (res->res[i] + 1),
-				    res->res[i]->ie_len, WLAN_EID_SSID);
-		s2 = nl80211_get_ie((u8 *) (r + 1), r->ie_len, WLAN_EID_SSID);
-		if (s1 == NULL || s2 == NULL || s1[1] != s2[1] ||
-		    os_memcmp(s1, s2, 2 + s1[1]) != 0)
-			continue;
-
-		/* Same BSSID,SSID was already included in scan results */
-		wpa_printf(MSG_DEBUG, "nl80211: Remove duplicated scan result "
-			   "for " MACSTR, MAC2STR(r->bssid));
-
-		if (((r->flags & WPA_SCAN_ASSOCIATED) &&
-		     !(res->res[i]->flags & WPA_SCAN_ASSOCIATED)) ||
-		    r->age < res->res[i]->age) {
-			os_free(res->res[i]);
-			res->res[i] = r;
-		} else
-			os_free(r);
-		return NL_SKIP;
-	}
-
-	tmp = os_realloc_array(res->res, res->num + 1,
-			       sizeof(struct wpa_scan_res *));
-	if (tmp == NULL) {
-		os_free(r);
-		return NL_SKIP;
-	}
-	tmp[res->num++] = r;
-	res->res = tmp;
-
-	return NL_SKIP;
-}
-
-
-static void clear_state_mismatch(struct wpa_driver_nl80211_data *drv,
-				 const u8 *addr)
-{
-	if (drv->capa.flags & WPA_DRIVER_FLAGS_SME) {
-		wpa_printf(MSG_DEBUG, "nl80211: Clear possible state "
-			   "mismatch (" MACSTR ")", MAC2STR(addr));
-		wpa_driver_nl80211_mlme(drv, addr,
-					NL80211_CMD_DEAUTHENTICATE,
-					WLAN_REASON_PREV_AUTH_NOT_VALID, 1);
-	}
-}
-
-
-static void wpa_driver_nl80211_check_bss_status(
-	struct wpa_driver_nl80211_data *drv, struct wpa_scan_results *res)
-{
-	size_t i;
-
-	for (i = 0; i < res->num; i++) {
-		struct wpa_scan_res *r = res->res[i];
-		if (r->flags & WPA_SCAN_AUTHENTICATED) {
-			wpa_printf(MSG_DEBUG, "nl80211: Scan results "
-				   "indicates BSS status with " MACSTR
-				   " as authenticated",
-				   MAC2STR(r->bssid));
-			if (is_sta_interface(drv->nlmode) &&
-			    os_memcmp(r->bssid, drv->bssid, ETH_ALEN) != 0 &&
-			    os_memcmp(r->bssid, drv->auth_bssid, ETH_ALEN) !=
-			    0) {
-				wpa_printf(MSG_DEBUG, "nl80211: Unknown BSSID"
-					   " in local state (auth=" MACSTR
-					   " assoc=" MACSTR ")",
-					   MAC2STR(drv->auth_bssid),
-					   MAC2STR(drv->bssid));
-				clear_state_mismatch(drv, r->bssid);
-			}
-		}
-
-		if (r->flags & WPA_SCAN_ASSOCIATED) {
-			wpa_printf(MSG_DEBUG, "nl80211: Scan results "
-				   "indicate BSS status with " MACSTR
-				   " as associated",
-				   MAC2STR(r->bssid));
-			if (is_sta_interface(drv->nlmode) &&
-			    !drv->associated) {
-				wpa_printf(MSG_DEBUG, "nl80211: Local state "
-					   "(not associated) does not match "
-					   "with BSS state");
-				clear_state_mismatch(drv, r->bssid);
-			} else if (is_sta_interface(drv->nlmode) &&
-				   os_memcmp(drv->bssid, r->bssid, ETH_ALEN) !=
-				   0) {
-				wpa_printf(MSG_DEBUG, "nl80211: Local state "
-					   "(associated with " MACSTR ") does "
-					   "not match with BSS state",
-					   MAC2STR(drv->bssid));
-				clear_state_mismatch(drv, r->bssid);
-				clear_state_mismatch(drv, drv->bssid);
-			}
-		}
-	}
-}
-
-
-static struct wpa_scan_results *
-nl80211_get_scan_results(struct wpa_driver_nl80211_data *drv)
-{
-	struct nl_msg *msg;
-	struct wpa_scan_results *res;
-	int ret;
-	struct nl80211_bss_info_arg arg;
-
-	res = os_zalloc(sizeof(*res));
-	if (res == NULL)
-		return NULL;
-	msg = nlmsg_alloc();
-	if (!msg)
-		goto nla_put_failure;
-
-	nl80211_cmd(drv, msg, NLM_F_DUMP, NL80211_CMD_GET_SCAN);
-	if (nl80211_set_iface_id(msg, drv->first_bss) < 0)
-		goto nla_put_failure;
-
-	arg.drv = drv;
-	arg.res = res;
-	ret = send_and_recv_msgs(drv, msg, bss_info_handler, &arg);
-	msg = NULL;
-	if (ret == 0) {
-		wpa_printf(MSG_DEBUG, "nl80211: Received scan results (%lu "
-			   "BSSes)", (unsigned long) res->num);
-		nl80211_get_noise_for_scan_results(drv, res);
-		return res;
-	}
-	wpa_printf(MSG_DEBUG, "nl80211: Scan result fetch failed: ret=%d "
-		   "(%s)", ret, strerror(-ret));
-nla_put_failure:
-	nlmsg_free(msg);
-	wpa_scan_results_free(res);
-	return NULL;
-}
-
-
-/**
- * wpa_driver_nl80211_get_scan_results - Fetch the latest scan results
- * @priv: Pointer to private wext data from wpa_driver_nl80211_init()
- * Returns: Scan results on success, -1 on failure
- */
-static struct wpa_scan_results *
-wpa_driver_nl80211_get_scan_results(void *priv)
-{
-	struct i802_bss *bss = priv;
-	struct wpa_driver_nl80211_data *drv = bss->drv;
-	struct wpa_scan_results *res;
-
-	res = nl80211_get_scan_results(drv);
-	if (res)
-		wpa_driver_nl80211_check_bss_status(drv, res);
-	return res;
-}
-
-
-static void nl80211_dump_scan(struct wpa_driver_nl80211_data *drv)
-{
-	struct wpa_scan_results *res;
-	size_t i;
-
-	res = nl80211_get_scan_results(drv);
-	if (res == NULL) {
-		wpa_printf(MSG_DEBUG, "nl80211: Failed to get scan results");
-		return;
-	}
-
-	wpa_printf(MSG_DEBUG, "nl80211: Scan result dump");
-	for (i = 0; i < res->num; i++) {
-		struct wpa_scan_res *r = res->res[i];
-		wpa_printf(MSG_DEBUG, "nl80211: %d/%d " MACSTR "%s%s",
-			   (int) i, (int) res->num, MAC2STR(r->bssid),
-			   r->flags & WPA_SCAN_AUTHENTICATED ? " [auth]" : "",
-			   r->flags & WPA_SCAN_ASSOCIATED ? " [assoc]" : "");
-	}
-
-	wpa_scan_results_free(res);
-}
-
-
 static u32 wpa_alg_to_cipher_suite(enum wpa_alg alg, size_t key_len)
 {
 	switch (alg) {
@@ -5869,6 +2463,35 @@
 }
 
 
+static int issue_key_mgmt_set_key(struct wpa_driver_nl80211_data *drv,
+				  const u8 *key, size_t key_len)
+{
+	struct nl_msg *msg;
+	int ret;
+
+	if (!(drv->capa.flags & WPA_DRIVER_FLAGS_KEY_MGMT_OFFLOAD))
+		return 0;
+
+	if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
+	    nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
+	    nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
+			QCA_NL80211_VENDOR_SUBCMD_KEY_MGMT_SET_KEY) ||
+	    nla_put(msg, NL80211_ATTR_VENDOR_DATA, key_len, key)) {
+		nl80211_nlmsg_clear(msg);
+		nlmsg_free(msg);
+		return -1;
+	}
+	ret = send_and_recv_msgs(drv, msg, NULL, (void *) -1);
+	if (ret) {
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: Key management set key failed: ret=%d (%s)",
+			   ret, strerror(-ret));
+	}
+
+	return ret;
+}
+
+
 static int wpa_driver_nl80211_set_key(const char *ifname, struct i802_bss *bss,
 				      enum wpa_alg alg, const u8 *addr,
 				      int key_idx, int set_tx,
@@ -5877,7 +2500,7 @@
 {
 	struct wpa_driver_nl80211_data *drv = bss->drv;
 	int ifindex;
-	struct nl_msg *msg;
+	struct nl_msg *msg = NULL;
 	int ret;
 	int tdls = 0;
 
@@ -5897,33 +2520,48 @@
 	}
 #endif /* CONFIG_TDLS */
 
-	msg = nlmsg_alloc();
-	if (!msg)
-		return -ENOMEM;
+	if (alg == WPA_ALG_PMK &&
+	    (drv->capa.flags & WPA_DRIVER_FLAGS_KEY_MGMT_OFFLOAD)) {
+		wpa_printf(MSG_DEBUG, "%s: calling issue_key_mgmt_set_key",
+			   __func__);
+		ret = issue_key_mgmt_set_key(drv, key, key_len);
+		return ret;
+	}
 
 	if (alg == WPA_ALG_NONE) {
-		nl80211_cmd(drv, msg, 0, NL80211_CMD_DEL_KEY);
+		msg = nl80211_ifindex_msg(drv, ifindex, 0, NL80211_CMD_DEL_KEY);
+		if (!msg)
+			return -ENOBUFS;
 	} else {
-		nl80211_cmd(drv, msg, 0, NL80211_CMD_NEW_KEY);
-		NLA_PUT(msg, NL80211_ATTR_KEY_DATA, key_len, key);
+		u32 suite;
+
+		suite = wpa_alg_to_cipher_suite(alg, key_len);
+		if (!suite)
+			goto fail;
+		msg = nl80211_ifindex_msg(drv, ifindex, 0, NL80211_CMD_NEW_KEY);
+		if (!msg ||
+		    nla_put(msg, NL80211_ATTR_KEY_DATA, key_len, key) ||
+		    nla_put_u32(msg, NL80211_ATTR_KEY_CIPHER, suite))
+			goto fail;
 		wpa_hexdump_key(MSG_DEBUG, "nl80211: KEY_DATA", key, key_len);
-		NLA_PUT_U32(msg, NL80211_ATTR_KEY_CIPHER,
-			    wpa_alg_to_cipher_suite(alg, key_len));
 	}
 
 	if (seq && seq_len) {
-		NLA_PUT(msg, NL80211_ATTR_KEY_SEQ, seq_len, seq);
+		if (nla_put(msg, NL80211_ATTR_KEY_SEQ, seq_len, seq))
+			goto fail;
 		wpa_hexdump(MSG_DEBUG, "nl80211: KEY_SEQ", seq, seq_len);
 	}
 
 	if (addr && !is_broadcast_ether_addr(addr)) {
 		wpa_printf(MSG_DEBUG, "   addr=" MACSTR, MAC2STR(addr));
-		NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr);
+		if (nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr))
+			goto fail;
 
 		if (alg != WPA_ALG_WEP && key_idx && !set_tx) {
 			wpa_printf(MSG_DEBUG, "   RSN IBSS RX GTK");
-			NLA_PUT_U32(msg, NL80211_ATTR_KEY_TYPE,
-				    NL80211_KEYTYPE_GROUP);
+			if (nla_put_u32(msg, NL80211_ATTR_KEY_TYPE,
+					NL80211_KEYTYPE_GROUP))
+				goto fail;
 		}
 	} else if (addr && is_broadcast_ether_addr(addr)) {
 		struct nlattr *types;
@@ -5931,15 +2569,15 @@
 		wpa_printf(MSG_DEBUG, "   broadcast key");
 
 		types = nla_nest_start(msg, NL80211_ATTR_KEY_DEFAULT_TYPES);
-		if (!types)
-			goto nla_put_failure;
-		NLA_PUT_FLAG(msg, NL80211_KEY_DEFAULT_TYPE_MULTICAST);
+		if (!types ||
+		    nla_put_flag(msg, NL80211_KEY_DEFAULT_TYPE_MULTICAST))
+			goto fail;
 		nla_nest_end(msg, types);
 	}
-	NLA_PUT_U8(msg, NL80211_ATTR_KEY_IDX, key_idx);
-	NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, ifindex);
+	if (nla_put_u8(msg, NL80211_ATTR_KEY_IDX, key_idx))
+		goto fail;
 
-	ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+	ret = send_and_recv_msgs(drv, msg, NULL, key ? (void *) -1 : NULL);
 	if ((ret == -ENOENT || ret == -ENOLINK) && alg == WPA_ALG_NONE)
 		ret = 0;
 	if (ret)
@@ -5956,32 +2594,31 @@
 	    !is_broadcast_ether_addr(addr))
 		return ret;
 
-	msg = nlmsg_alloc();
-	if (!msg)
-		return -ENOMEM;
-
-	nl80211_cmd(drv, msg, 0, NL80211_CMD_SET_KEY);
-	NLA_PUT_U8(msg, NL80211_ATTR_KEY_IDX, key_idx);
-	NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, ifindex);
-	if (alg == WPA_ALG_IGTK)
-		NLA_PUT_FLAG(msg, NL80211_ATTR_KEY_DEFAULT_MGMT);
-	else
-		NLA_PUT_FLAG(msg, NL80211_ATTR_KEY_DEFAULT);
+	msg = nl80211_ifindex_msg(drv, ifindex, 0, NL80211_CMD_SET_KEY);
+	if (!msg ||
+	    nla_put_u8(msg, NL80211_ATTR_KEY_IDX, key_idx) ||
+	    nla_put_flag(msg, (alg == WPA_ALG_IGTK ||
+			       alg == WPA_ALG_BIP_GMAC_128 ||
+			       alg == WPA_ALG_BIP_GMAC_256 ||
+			       alg == WPA_ALG_BIP_CMAC_256) ?
+			 NL80211_ATTR_KEY_DEFAULT_MGMT :
+			 NL80211_ATTR_KEY_DEFAULT))
+		goto fail;
 	if (addr && is_broadcast_ether_addr(addr)) {
 		struct nlattr *types;
 
 		types = nla_nest_start(msg, NL80211_ATTR_KEY_DEFAULT_TYPES);
-		if (!types)
-			goto nla_put_failure;
-		NLA_PUT_FLAG(msg, NL80211_KEY_DEFAULT_TYPE_MULTICAST);
+		if (!types ||
+		    nla_put_flag(msg, NL80211_KEY_DEFAULT_TYPE_MULTICAST))
+			goto fail;
 		nla_nest_end(msg, types);
 	} else if (addr) {
 		struct nlattr *types;
 
 		types = nla_nest_start(msg, NL80211_ATTR_KEY_DEFAULT_TYPES);
-		if (!types)
-			goto nla_put_failure;
-		NLA_PUT_FLAG(msg, NL80211_KEY_DEFAULT_TYPE_UNICAST);
+		if (!types ||
+		    nla_put_flag(msg, NL80211_KEY_DEFAULT_TYPE_UNICAST))
+			goto fail;
 		nla_nest_end(msg, types);
 	}
 
@@ -5993,7 +2630,8 @@
 			   "err=%d %s)", ret, strerror(-ret));
 	return ret;
 
-nla_put_failure:
+fail:
+	nl80211_nlmsg_clear(msg);
 	nlmsg_free(msg);
 	return -ENOBUFS;
 }
@@ -6005,29 +2643,33 @@
 		      const u8 *key, size_t key_len)
 {
 	struct nlattr *key_attr = nla_nest_start(msg, NL80211_ATTR_KEY);
+	u32 suite;
+
 	if (!key_attr)
 		return -1;
 
-	if (defkey && alg == WPA_ALG_IGTK)
-		NLA_PUT_FLAG(msg, NL80211_KEY_DEFAULT_MGMT);
-	else if (defkey)
-		NLA_PUT_FLAG(msg, NL80211_KEY_DEFAULT);
+	suite = wpa_alg_to_cipher_suite(alg, key_len);
+	if (!suite)
+		return -1;
 
-	NLA_PUT_U8(msg, NL80211_KEY_IDX, key_idx);
+	if (defkey && alg == WPA_ALG_IGTK) {
+		if (nla_put_flag(msg, NL80211_KEY_DEFAULT_MGMT))
+			return -1;
+	} else if (defkey) {
+		if (nla_put_flag(msg, NL80211_KEY_DEFAULT))
+			return -1;
+	}
 
-	NLA_PUT_U32(msg, NL80211_KEY_CIPHER,
-		    wpa_alg_to_cipher_suite(alg, key_len));
-
-	if (seq && seq_len)
-		NLA_PUT(msg, NL80211_KEY_SEQ, seq_len, seq);
-
-	NLA_PUT(msg, NL80211_KEY_DATA, key_len, key);
+	if (nla_put_u8(msg, NL80211_KEY_IDX, key_idx) ||
+	    nla_put_u32(msg, NL80211_KEY_CIPHER, suite) ||
+	    (seq && seq_len &&
+	     nla_put(msg, NL80211_KEY_SEQ, seq_len, seq)) ||
+	    nla_put(msg, NL80211_KEY_DATA, key_len, key))
+		return -1;
 
 	nla_nest_end(msg, key_attr);
 
 	return 0;
- nla_put_failure:
-	return -1;
 }
 
 
@@ -6052,77 +2694,60 @@
 	if (!privacy)
 		return 0;
 
-	NLA_PUT_FLAG(msg, NL80211_ATTR_PRIVACY);
+	if (nla_put_flag(msg, NL80211_ATTR_PRIVACY))
+		return -ENOBUFS;
 
 	nl_keys = nla_nest_start(msg, NL80211_ATTR_KEYS);
 	if (!nl_keys)
-		goto nla_put_failure;
+		return -ENOBUFS;
 
 	for (i = 0; i < 4; i++) {
 		if (!params->wep_key[i])
 			continue;
 
 		nl_key = nla_nest_start(msg, i);
-		if (!nl_key)
-			goto nla_put_failure;
-
-		NLA_PUT(msg, NL80211_KEY_DATA, params->wep_key_len[i],
-			params->wep_key[i]);
-		if (params->wep_key_len[i] == 5)
-			NLA_PUT_U32(msg, NL80211_KEY_CIPHER,
-				    WLAN_CIPHER_SUITE_WEP40);
-		else
-			NLA_PUT_U32(msg, NL80211_KEY_CIPHER,
-				    WLAN_CIPHER_SUITE_WEP104);
-
-		NLA_PUT_U8(msg, NL80211_KEY_IDX, i);
-
-		if (i == params->wep_tx_keyidx)
-			NLA_PUT_FLAG(msg, NL80211_KEY_DEFAULT);
+		if (!nl_key ||
+		    nla_put(msg, NL80211_KEY_DATA, params->wep_key_len[i],
+			    params->wep_key[i]) ||
+		    nla_put_u32(msg, NL80211_KEY_CIPHER,
+				params->wep_key_len[i] == 5 ?
+				WLAN_CIPHER_SUITE_WEP40 :
+				WLAN_CIPHER_SUITE_WEP104) ||
+		    nla_put_u8(msg, NL80211_KEY_IDX, i) ||
+		    (i == params->wep_tx_keyidx &&
+		     nla_put_flag(msg, NL80211_KEY_DEFAULT)))
+			return -ENOBUFS;
 
 		nla_nest_end(msg, nl_key);
 	}
 	nla_nest_end(msg, nl_keys);
 
 	return 0;
-
-nla_put_failure:
-	return -ENOBUFS;
 }
 
 
-static int wpa_driver_nl80211_mlme(struct wpa_driver_nl80211_data *drv,
-				   const u8 *addr, int cmd, u16 reason_code,
-				   int local_state_change)
+int wpa_driver_nl80211_mlme(struct wpa_driver_nl80211_data *drv,
+			    const u8 *addr, int cmd, u16 reason_code,
+			    int local_state_change)
 {
-	int ret = -1;
+	int ret;
 	struct nl_msg *msg;
 
-	msg = nlmsg_alloc();
-	if (!msg)
+	if (!(msg = nl80211_drv_msg(drv, 0, cmd)) ||
+	    nla_put_u16(msg, NL80211_ATTR_REASON_CODE, reason_code) ||
+	    (addr && nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr)) ||
+	    (local_state_change &&
+	     nla_put_flag(msg, NL80211_ATTR_LOCAL_STATE_CHANGE))) {
+		nlmsg_free(msg);
 		return -1;
-
-	nl80211_cmd(drv, msg, 0, cmd);
-
-	NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
-	NLA_PUT_U16(msg, NL80211_ATTR_REASON_CODE, reason_code);
-	if (addr)
-		NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr);
-	if (local_state_change)
-		NLA_PUT_FLAG(msg, NL80211_ATTR_LOCAL_STATE_CHANGE);
+	}
 
 	ret = send_and_recv_msgs(drv, msg, NULL, NULL);
-	msg = NULL;
 	if (ret) {
 		wpa_dbg(drv->ctx, MSG_DEBUG,
 			"nl80211: MLME command failed: reason=%u ret=%d (%s)",
 			reason_code, ret, strerror(-ret));
-		goto nla_put_failure;
 	}
-	ret = 0;
-
-nla_put_failure:
-	nlmsg_free(msg);
 	return ret;
 }
 
@@ -6155,7 +2780,7 @@
 
 	if (drv->nlmode == NL80211_IFTYPE_ADHOC) {
 		nl80211_mark_disconnected(drv);
-		return nl80211_leave_ibss(drv);
+		return nl80211_leave_ibss(drv, 1);
 	}
 	if (!(drv->capa.flags & WPA_DRIVER_FLAGS_SME))
 		return wpa_driver_nl80211_disconnect(drv, reason_code);
@@ -6219,6 +2844,25 @@
 }
 
 
+static void nl80211_unmask_11b_rates(struct i802_bss *bss)
+{
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+
+	if (is_p2p_net_interface(drv->nlmode) || !drv->disabled_11b_rates)
+		return;
+
+	/*
+	 * Looks like we failed to unmask 11b rates previously. This could
+	 * happen, e.g., if the interface was down at the point in time when a
+	 * P2P group was terminated.
+	 */
+	wpa_printf(MSG_DEBUG,
+		   "nl80211: Interface %s mode is for non-P2P, but 11b rates were disabled - re-enable them",
+		   bss->ifname);
+	nl80211_disable_11b_rates(drv, drv->ifindex, 0);
+}
+
+
 static int wpa_driver_nl80211_authenticate(
 	struct i802_bss *bss, struct wpa_driver_auth_params *params)
 {
@@ -6230,6 +2874,8 @@
 	int count = 0;
 	int is_retry;
 
+	nl80211_unmask_11b_rates(bss);
+
 	is_retry = drv->retry_auth;
 	drv->retry_auth = 0;
 	drv->ignore_deauth_event = 0;
@@ -6248,14 +2894,12 @@
 		return -1;
 
 retry:
-	msg = nlmsg_alloc();
-	if (!msg)
-		return -1;
-
 	wpa_printf(MSG_DEBUG, "nl80211: Authenticate (ifindex=%d)",
 		   drv->ifindex);
 
-	nl80211_cmd(drv, msg, 0, NL80211_CMD_AUTHENTICATE);
+	msg = nl80211_drv_msg(drv, 0, NL80211_CMD_AUTHENTICATE);
+	if (!msg)
+		goto fail;
 
 	for (i = 0; i < 4; i++) {
 		if (!params->wep_key[i])
@@ -6268,36 +2912,38 @@
 		if (params->wep_tx_keyidx != i)
 			continue;
 		if (nl_add_key(msg, WPA_ALG_WEP, i, 1, NULL, 0,
-			       params->wep_key[i], params->wep_key_len[i])) {
-			nlmsg_free(msg);
-			return -1;
-		}
+			       params->wep_key[i], params->wep_key_len[i]))
+			goto fail;
 	}
 
-	NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
 	if (params->bssid) {
 		wpa_printf(MSG_DEBUG, "  * bssid=" MACSTR,
 			   MAC2STR(params->bssid));
-		NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, params->bssid);
+		if (nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, params->bssid))
+			goto fail;
 	}
 	if (params->freq) {
 		wpa_printf(MSG_DEBUG, "  * freq=%d", params->freq);
-		NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, params->freq);
+		if (nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ, params->freq))
+			goto fail;
 	}
 	if (params->ssid) {
 		wpa_hexdump_ascii(MSG_DEBUG, "  * SSID",
 				  params->ssid, params->ssid_len);
-		NLA_PUT(msg, NL80211_ATTR_SSID, params->ssid_len,
-			params->ssid);
+		if (nla_put(msg, NL80211_ATTR_SSID, params->ssid_len,
+			    params->ssid))
+			goto fail;
 	}
 	wpa_hexdump(MSG_DEBUG, "  * IEs", params->ie, params->ie_len);
-	if (params->ie)
-		NLA_PUT(msg, NL80211_ATTR_IE, params->ie_len, params->ie);
+	if (params->ie &&
+	    nla_put(msg, NL80211_ATTR_IE, params->ie_len, params->ie))
+		goto fail;
 	if (params->sae_data) {
 		wpa_hexdump(MSG_DEBUG, "  * SAE data", params->sae_data,
 			    params->sae_data_len);
-		NLA_PUT(msg, NL80211_ATTR_SAE_DATA, params->sae_data_len,
-			params->sae_data);
+		if (nla_put(msg, NL80211_ATTR_SAE_DATA, params->sae_data_len,
+			    params->sae_data))
+			goto fail;
 	}
 	if (params->auth_alg & WPA_AUTH_ALG_OPEN)
 		type = NL80211_AUTHTYPE_OPEN_SYSTEM;
@@ -6310,12 +2956,14 @@
 	else if (params->auth_alg & WPA_AUTH_ALG_SAE)
 		type = NL80211_AUTHTYPE_SAE;
 	else
-		goto nla_put_failure;
+		goto fail;
 	wpa_printf(MSG_DEBUG, "  * Auth Type %d", type);
-	NLA_PUT_U32(msg, NL80211_ATTR_AUTH_TYPE, type);
+	if (nla_put_u32(msg, NL80211_ATTR_AUTH_TYPE, type))
+		goto fail;
 	if (params->local_state_change) {
 		wpa_printf(MSG_DEBUG, "  * Local state change only");
-		NLA_PUT_FLAG(msg, NL80211_ATTR_LOCAL_STATE_CHANGE);
+		if (nla_put_flag(msg, NL80211_ATTR_LOCAL_STATE_CHANGE))
+			goto fail;
 	}
 
 	ret = send_and_recv_msgs(drv, msg, NULL, NULL);
@@ -6383,21 +3031,18 @@
 			wpa_supplicant_event(drv->ctx, EVENT_AUTH_TIMED_OUT,
 					     &event);
 		}
-
-		goto nla_put_failure;
+	} else {
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: Authentication request send successfully");
 	}
-	ret = 0;
-	wpa_printf(MSG_DEBUG, "nl80211: Authentication request send "
-		   "successfully");
 
-nla_put_failure:
+fail:
 	nlmsg_free(msg);
 	return ret;
 }
 
 
-static int wpa_driver_nl80211_authenticate_retry(
-	struct wpa_driver_nl80211_data *drv)
+int wpa_driver_nl80211_authenticate_retry(struct wpa_driver_nl80211_data *drv)
 {
 	struct wpa_driver_auth_params params;
 	struct i802_bss *bss = drv->first_bss;
@@ -6435,726 +3080,6 @@
 }
 
 
-struct phy_info_arg {
-	u16 *num_modes;
-	struct hostapd_hw_modes *modes;
-	int last_mode, last_chan_idx;
-};
-
-static void phy_info_ht_capa(struct hostapd_hw_modes *mode, struct nlattr *capa,
-			     struct nlattr *ampdu_factor,
-			     struct nlattr *ampdu_density,
-			     struct nlattr *mcs_set)
-{
-	if (capa)
-		mode->ht_capab = nla_get_u16(capa);
-
-	if (ampdu_factor)
-		mode->a_mpdu_params |= nla_get_u8(ampdu_factor) & 0x03;
-
-	if (ampdu_density)
-		mode->a_mpdu_params |= nla_get_u8(ampdu_density) << 2;
-
-	if (mcs_set && nla_len(mcs_set) >= 16) {
-		u8 *mcs;
-		mcs = nla_data(mcs_set);
-		os_memcpy(mode->mcs_set, mcs, 16);
-	}
-}
-
-
-static void phy_info_vht_capa(struct hostapd_hw_modes *mode,
-			      struct nlattr *capa,
-			      struct nlattr *mcs_set)
-{
-	if (capa)
-		mode->vht_capab = nla_get_u32(capa);
-
-	if (mcs_set && nla_len(mcs_set) >= 8) {
-		u8 *mcs;
-		mcs = nla_data(mcs_set);
-		os_memcpy(mode->vht_mcs_set, mcs, 8);
-	}
-}
-
-
-static void phy_info_freq(struct hostapd_hw_modes *mode,
-			  struct hostapd_channel_data *chan,
-			  struct nlattr *tb_freq[])
-{
-	u8 channel;
-	chan->freq = nla_get_u32(tb_freq[NL80211_FREQUENCY_ATTR_FREQ]);
-	chan->flag = 0;
-	chan->dfs_cac_ms = 0;
-	if (ieee80211_freq_to_chan(chan->freq, &channel) != NUM_HOSTAPD_MODES)
-		chan->chan = channel;
-
-	if (tb_freq[NL80211_FREQUENCY_ATTR_DISABLED])
-		chan->flag |= HOSTAPD_CHAN_DISABLED;
-	if (tb_freq[NL80211_FREQUENCY_ATTR_NO_IR])
-		chan->flag |= HOSTAPD_CHAN_PASSIVE_SCAN | HOSTAPD_CHAN_NO_IBSS;
-	if (tb_freq[NL80211_FREQUENCY_ATTR_RADAR])
-		chan->flag |= HOSTAPD_CHAN_RADAR;
-
-	if (tb_freq[NL80211_FREQUENCY_ATTR_DFS_STATE]) {
-		enum nl80211_dfs_state state =
-			nla_get_u32(tb_freq[NL80211_FREQUENCY_ATTR_DFS_STATE]);
-
-		switch (state) {
-		case NL80211_DFS_USABLE:
-			chan->flag |= HOSTAPD_CHAN_DFS_USABLE;
-			break;
-		case NL80211_DFS_AVAILABLE:
-			chan->flag |= HOSTAPD_CHAN_DFS_AVAILABLE;
-			break;
-		case NL80211_DFS_UNAVAILABLE:
-			chan->flag |= HOSTAPD_CHAN_DFS_UNAVAILABLE;
-			break;
-		}
-	}
-
-	if (tb_freq[NL80211_FREQUENCY_ATTR_DFS_CAC_TIME]) {
-		chan->dfs_cac_ms = nla_get_u32(
-			tb_freq[NL80211_FREQUENCY_ATTR_DFS_CAC_TIME]);
-	}
-}
-
-
-static int phy_info_freqs(struct phy_info_arg *phy_info,
-			  struct hostapd_hw_modes *mode, struct nlattr *tb)
-{
-	static struct nla_policy freq_policy[NL80211_FREQUENCY_ATTR_MAX + 1] = {
-		[NL80211_FREQUENCY_ATTR_FREQ] = { .type = NLA_U32 },
-		[NL80211_FREQUENCY_ATTR_DISABLED] = { .type = NLA_FLAG },
-		[NL80211_FREQUENCY_ATTR_NO_IR] = { .type = NLA_FLAG },
-		[NL80211_FREQUENCY_ATTR_RADAR] = { .type = NLA_FLAG },
-		[NL80211_FREQUENCY_ATTR_MAX_TX_POWER] = { .type = NLA_U32 },
-		[NL80211_FREQUENCY_ATTR_DFS_STATE] = { .type = NLA_U32 },
-	};
-	int new_channels = 0;
-	struct hostapd_channel_data *channel;
-	struct nlattr *tb_freq[NL80211_FREQUENCY_ATTR_MAX + 1];
-	struct nlattr *nl_freq;
-	int rem_freq, idx;
-
-	if (tb == NULL)
-		return NL_OK;
-
-	nla_for_each_nested(nl_freq, tb, rem_freq) {
-		nla_parse(tb_freq, NL80211_FREQUENCY_ATTR_MAX,
-			  nla_data(nl_freq), nla_len(nl_freq), freq_policy);
-		if (!tb_freq[NL80211_FREQUENCY_ATTR_FREQ])
-			continue;
-		new_channels++;
-	}
-
-	channel = os_realloc_array(mode->channels,
-				   mode->num_channels + new_channels,
-				   sizeof(struct hostapd_channel_data));
-	if (!channel)
-		return NL_SKIP;
-
-	mode->channels = channel;
-	mode->num_channels += new_channels;
-
-	idx = phy_info->last_chan_idx;
-
-	nla_for_each_nested(nl_freq, tb, rem_freq) {
-		nla_parse(tb_freq, NL80211_FREQUENCY_ATTR_MAX,
-			  nla_data(nl_freq), nla_len(nl_freq), freq_policy);
-		if (!tb_freq[NL80211_FREQUENCY_ATTR_FREQ])
-			continue;
-		phy_info_freq(mode, &mode->channels[idx], tb_freq);
-		idx++;
-	}
-	phy_info->last_chan_idx = idx;
-
-	return NL_OK;
-}
-
-
-static int phy_info_rates(struct hostapd_hw_modes *mode, struct nlattr *tb)
-{
-	static struct nla_policy rate_policy[NL80211_BITRATE_ATTR_MAX + 1] = {
-		[NL80211_BITRATE_ATTR_RATE] = { .type = NLA_U32 },
-		[NL80211_BITRATE_ATTR_2GHZ_SHORTPREAMBLE] =
-		{ .type = NLA_FLAG },
-	};
-	struct nlattr *tb_rate[NL80211_BITRATE_ATTR_MAX + 1];
-	struct nlattr *nl_rate;
-	int rem_rate, idx;
-
-	if (tb == NULL)
-		return NL_OK;
-
-	nla_for_each_nested(nl_rate, tb, rem_rate) {
-		nla_parse(tb_rate, NL80211_BITRATE_ATTR_MAX,
-			  nla_data(nl_rate), nla_len(nl_rate),
-			  rate_policy);
-		if (!tb_rate[NL80211_BITRATE_ATTR_RATE])
-			continue;
-		mode->num_rates++;
-	}
-
-	mode->rates = os_calloc(mode->num_rates, sizeof(int));
-	if (!mode->rates)
-		return NL_SKIP;
-
-	idx = 0;
-
-	nla_for_each_nested(nl_rate, tb, rem_rate) {
-		nla_parse(tb_rate, NL80211_BITRATE_ATTR_MAX,
-			  nla_data(nl_rate), nla_len(nl_rate),
-			  rate_policy);
-		if (!tb_rate[NL80211_BITRATE_ATTR_RATE])
-			continue;
-		mode->rates[idx] = nla_get_u32(
-			tb_rate[NL80211_BITRATE_ATTR_RATE]);
-		idx++;
-	}
-
-	return NL_OK;
-}
-
-
-static int phy_info_band(struct phy_info_arg *phy_info, struct nlattr *nl_band)
-{
-	struct nlattr *tb_band[NL80211_BAND_ATTR_MAX + 1];
-	struct hostapd_hw_modes *mode;
-	int ret;
-
-	if (phy_info->last_mode != nl_band->nla_type) {
-		mode = os_realloc_array(phy_info->modes,
-					*phy_info->num_modes + 1,
-					sizeof(*mode));
-		if (!mode)
-			return NL_SKIP;
-		phy_info->modes = mode;
-
-		mode = &phy_info->modes[*(phy_info->num_modes)];
-		os_memset(mode, 0, sizeof(*mode));
-		mode->mode = NUM_HOSTAPD_MODES;
-		mode->flags = HOSTAPD_MODE_FLAG_HT_INFO_KNOWN |
-			HOSTAPD_MODE_FLAG_VHT_INFO_KNOWN;
-
-		/*
-		 * Unsupported VHT MCS stream is defined as value 3, so the VHT
-		 * MCS RX/TX map must be initialized with 0xffff to mark all 8
-		 * possible streams as unsupported. This will be overridden if
-		 * driver advertises VHT support.
-		 */
-		mode->vht_mcs_set[0] = 0xff;
-		mode->vht_mcs_set[1] = 0xff;
-		mode->vht_mcs_set[4] = 0xff;
-		mode->vht_mcs_set[5] = 0xff;
-
-		*(phy_info->num_modes) += 1;
-		phy_info->last_mode = nl_band->nla_type;
-		phy_info->last_chan_idx = 0;
-	} else
-		mode = &phy_info->modes[*(phy_info->num_modes) - 1];
-
-	nla_parse(tb_band, NL80211_BAND_ATTR_MAX, nla_data(nl_band),
-		  nla_len(nl_band), NULL);
-
-	phy_info_ht_capa(mode, tb_band[NL80211_BAND_ATTR_HT_CAPA],
-			 tb_band[NL80211_BAND_ATTR_HT_AMPDU_FACTOR],
-			 tb_band[NL80211_BAND_ATTR_HT_AMPDU_DENSITY],
-			 tb_band[NL80211_BAND_ATTR_HT_MCS_SET]);
-	phy_info_vht_capa(mode, tb_band[NL80211_BAND_ATTR_VHT_CAPA],
-			  tb_band[NL80211_BAND_ATTR_VHT_MCS_SET]);
-	ret = phy_info_freqs(phy_info, mode, tb_band[NL80211_BAND_ATTR_FREQS]);
-	if (ret != NL_OK)
-		return ret;
-	ret = phy_info_rates(mode, tb_band[NL80211_BAND_ATTR_RATES]);
-	if (ret != NL_OK)
-		return ret;
-
-	return NL_OK;
-}
-
-
-static int phy_info_handler(struct nl_msg *msg, void *arg)
-{
-	struct nlattr *tb_msg[NL80211_ATTR_MAX + 1];
-	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
-	struct phy_info_arg *phy_info = arg;
-	struct nlattr *nl_band;
-	int rem_band;
-
-	nla_parse(tb_msg, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
-		  genlmsg_attrlen(gnlh, 0), NULL);
-
-	if (!tb_msg[NL80211_ATTR_WIPHY_BANDS])
-		return NL_SKIP;
-
-	nla_for_each_nested(nl_band, tb_msg[NL80211_ATTR_WIPHY_BANDS], rem_band)
-	{
-		int res = phy_info_band(phy_info, nl_band);
-		if (res != NL_OK)
-			return res;
-	}
-
-	return NL_SKIP;
-}
-
-
-static struct hostapd_hw_modes *
-wpa_driver_nl80211_postprocess_modes(struct hostapd_hw_modes *modes,
-				     u16 *num_modes)
-{
-	u16 m;
-	struct hostapd_hw_modes *mode11g = NULL, *nmodes, *mode;
-	int i, mode11g_idx = -1;
-
-	/* heuristic to set up modes */
-	for (m = 0; m < *num_modes; m++) {
-		if (!modes[m].num_channels)
-			continue;
-		if (modes[m].channels[0].freq < 4000) {
-			modes[m].mode = HOSTAPD_MODE_IEEE80211B;
-			for (i = 0; i < modes[m].num_rates; i++) {
-				if (modes[m].rates[i] > 200) {
-					modes[m].mode = HOSTAPD_MODE_IEEE80211G;
-					break;
-				}
-			}
-		} else if (modes[m].channels[0].freq > 50000)
-			modes[m].mode = HOSTAPD_MODE_IEEE80211AD;
-		else
-			modes[m].mode = HOSTAPD_MODE_IEEE80211A;
-	}
-
-	/* If only 802.11g mode is included, use it to construct matching
-	 * 802.11b mode data. */
-
-	for (m = 0; m < *num_modes; m++) {
-		if (modes[m].mode == HOSTAPD_MODE_IEEE80211B)
-			return modes; /* 802.11b already included */
-		if (modes[m].mode == HOSTAPD_MODE_IEEE80211G)
-			mode11g_idx = m;
-	}
-
-	if (mode11g_idx < 0)
-		return modes; /* 2.4 GHz band not supported at all */
-
-	nmodes = os_realloc_array(modes, *num_modes + 1, sizeof(*nmodes));
-	if (nmodes == NULL)
-		return modes; /* Could not add 802.11b mode */
-
-	mode = &nmodes[*num_modes];
-	os_memset(mode, 0, sizeof(*mode));
-	(*num_modes)++;
-	modes = nmodes;
-
-	mode->mode = HOSTAPD_MODE_IEEE80211B;
-
-	mode11g = &modes[mode11g_idx];
-	mode->num_channels = mode11g->num_channels;
-	mode->channels = os_malloc(mode11g->num_channels *
-				   sizeof(struct hostapd_channel_data));
-	if (mode->channels == NULL) {
-		(*num_modes)--;
-		return modes; /* Could not add 802.11b mode */
-	}
-	os_memcpy(mode->channels, mode11g->channels,
-		  mode11g->num_channels * sizeof(struct hostapd_channel_data));
-
-	mode->num_rates = 0;
-	mode->rates = os_malloc(4 * sizeof(int));
-	if (mode->rates == NULL) {
-		os_free(mode->channels);
-		(*num_modes)--;
-		return modes; /* Could not add 802.11b mode */
-	}
-
-	for (i = 0; i < mode11g->num_rates; i++) {
-		if (mode11g->rates[i] != 10 && mode11g->rates[i] != 20 &&
-		    mode11g->rates[i] != 55 && mode11g->rates[i] != 110)
-			continue;
-		mode->rates[mode->num_rates] = mode11g->rates[i];
-		mode->num_rates++;
-		if (mode->num_rates == 4)
-			break;
-	}
-
-	if (mode->num_rates == 0) {
-		os_free(mode->channels);
-		os_free(mode->rates);
-		(*num_modes)--;
-		return modes; /* No 802.11b rates */
-	}
-
-	wpa_printf(MSG_DEBUG, "nl80211: Added 802.11b mode based on 802.11g "
-		   "information");
-
-	return modes;
-}
-
-
-static void nl80211_set_ht40_mode(struct hostapd_hw_modes *mode, int start,
-				  int end)
-{
-	int c;
-
-	for (c = 0; c < mode->num_channels; c++) {
-		struct hostapd_channel_data *chan = &mode->channels[c];
-		if (chan->freq - 10 >= start && chan->freq + 10 <= end)
-			chan->flag |= HOSTAPD_CHAN_HT40;
-	}
-}
-
-
-static void nl80211_set_ht40_mode_sec(struct hostapd_hw_modes *mode, int start,
-				      int end)
-{
-	int c;
-
-	for (c = 0; c < mode->num_channels; c++) {
-		struct hostapd_channel_data *chan = &mode->channels[c];
-		if (!(chan->flag & HOSTAPD_CHAN_HT40))
-			continue;
-		if (chan->freq - 30 >= start && chan->freq - 10 <= end)
-			chan->flag |= HOSTAPD_CHAN_HT40MINUS;
-		if (chan->freq + 10 >= start && chan->freq + 30 <= end)
-			chan->flag |= HOSTAPD_CHAN_HT40PLUS;
-	}
-}
-
-
-static void nl80211_reg_rule_max_eirp(u32 start, u32 end, u32 max_eirp,
-				      struct phy_info_arg *results)
-{
-	u16 m;
-
-	for (m = 0; m < *results->num_modes; m++) {
-		int c;
-		struct hostapd_hw_modes *mode = &results->modes[m];
-
-		for (c = 0; c < mode->num_channels; c++) {
-			struct hostapd_channel_data *chan = &mode->channels[c];
-			if ((u32) chan->freq - 10 >= start &&
-			    (u32) chan->freq + 10 <= end)
-				chan->max_tx_power = max_eirp;
-		}
-	}
-}
-
-
-static void nl80211_reg_rule_ht40(u32 start, u32 end,
-				  struct phy_info_arg *results)
-{
-	u16 m;
-
-	for (m = 0; m < *results->num_modes; m++) {
-		if (!(results->modes[m].ht_capab &
-		      HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET))
-			continue;
-		nl80211_set_ht40_mode(&results->modes[m], start, end);
-	}
-}
-
-
-static void nl80211_reg_rule_sec(struct nlattr *tb[],
-				 struct phy_info_arg *results)
-{
-	u32 start, end, max_bw;
-	u16 m;
-
-	if (tb[NL80211_ATTR_FREQ_RANGE_START] == NULL ||
-	    tb[NL80211_ATTR_FREQ_RANGE_END] == NULL ||
-	    tb[NL80211_ATTR_FREQ_RANGE_MAX_BW] == NULL)
-		return;
-
-	start = nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_START]) / 1000;
-	end = nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_END]) / 1000;
-	max_bw = nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_MAX_BW]) / 1000;
-
-	if (max_bw < 20)
-		return;
-
-	for (m = 0; m < *results->num_modes; m++) {
-		if (!(results->modes[m].ht_capab &
-		      HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET))
-			continue;
-		nl80211_set_ht40_mode_sec(&results->modes[m], start, end);
-	}
-}
-
-
-static void nl80211_set_vht_mode(struct hostapd_hw_modes *mode, int start,
-				 int end)
-{
-	int c;
-
-	for (c = 0; c < mode->num_channels; c++) {
-		struct hostapd_channel_data *chan = &mode->channels[c];
-		if (chan->freq - 10 >= start && chan->freq + 70 <= end)
-			chan->flag |= HOSTAPD_CHAN_VHT_10_70;
-
-		if (chan->freq - 30 >= start && chan->freq + 50 <= end)
-			chan->flag |= HOSTAPD_CHAN_VHT_30_50;
-
-		if (chan->freq - 50 >= start && chan->freq + 30 <= end)
-			chan->flag |= HOSTAPD_CHAN_VHT_50_30;
-
-		if (chan->freq - 70 >= start && chan->freq + 10 <= end)
-			chan->flag |= HOSTAPD_CHAN_VHT_70_10;
-	}
-}
-
-
-static void nl80211_reg_rule_vht(struct nlattr *tb[],
-				 struct phy_info_arg *results)
-{
-	u32 start, end, max_bw;
-	u16 m;
-
-	if (tb[NL80211_ATTR_FREQ_RANGE_START] == NULL ||
-	    tb[NL80211_ATTR_FREQ_RANGE_END] == NULL ||
-	    tb[NL80211_ATTR_FREQ_RANGE_MAX_BW] == NULL)
-		return;
-
-	start = nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_START]) / 1000;
-	end = nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_END]) / 1000;
-	max_bw = nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_MAX_BW]) / 1000;
-
-	if (max_bw < 80)
-		return;
-
-	for (m = 0; m < *results->num_modes; m++) {
-		if (!(results->modes[m].ht_capab &
-		      HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET))
-			continue;
-		/* TODO: use a real VHT support indication */
-		if (!results->modes[m].vht_capab)
-			continue;
-
-		nl80211_set_vht_mode(&results->modes[m], start, end);
-	}
-}
-
-
-static const char * dfs_domain_name(enum nl80211_dfs_regions region)
-{
-	switch (region) {
-	case NL80211_DFS_UNSET:
-		return "DFS-UNSET";
-	case NL80211_DFS_FCC:
-		return "DFS-FCC";
-	case NL80211_DFS_ETSI:
-		return "DFS-ETSI";
-	case NL80211_DFS_JP:
-		return "DFS-JP";
-	default:
-		return "DFS-invalid";
-	}
-}
-
-
-static int nl80211_get_reg(struct nl_msg *msg, void *arg)
-{
-	struct phy_info_arg *results = arg;
-	struct nlattr *tb_msg[NL80211_ATTR_MAX + 1];
-	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
-	struct nlattr *nl_rule;
-	struct nlattr *tb_rule[NL80211_FREQUENCY_ATTR_MAX + 1];
-	int rem_rule;
-	static struct nla_policy reg_policy[NL80211_FREQUENCY_ATTR_MAX + 1] = {
-		[NL80211_ATTR_REG_RULE_FLAGS] = { .type = NLA_U32 },
-		[NL80211_ATTR_FREQ_RANGE_START] = { .type = NLA_U32 },
-		[NL80211_ATTR_FREQ_RANGE_END] = { .type = NLA_U32 },
-		[NL80211_ATTR_FREQ_RANGE_MAX_BW] = { .type = NLA_U32 },
-		[NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN] = { .type = NLA_U32 },
-		[NL80211_ATTR_POWER_RULE_MAX_EIRP] = { .type = NLA_U32 },
-	};
-
-	nla_parse(tb_msg, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
-		  genlmsg_attrlen(gnlh, 0), NULL);
-	if (!tb_msg[NL80211_ATTR_REG_ALPHA2] ||
-	    !tb_msg[NL80211_ATTR_REG_RULES]) {
-		wpa_printf(MSG_DEBUG, "nl80211: No regulatory information "
-			   "available");
-		return NL_SKIP;
-	}
-
-	if (tb_msg[NL80211_ATTR_DFS_REGION]) {
-		enum nl80211_dfs_regions dfs_domain;
-		dfs_domain = nla_get_u8(tb_msg[NL80211_ATTR_DFS_REGION]);
-		wpa_printf(MSG_DEBUG, "nl80211: Regulatory information - country=%s (%s)",
-			   (char *) nla_data(tb_msg[NL80211_ATTR_REG_ALPHA2]),
-			   dfs_domain_name(dfs_domain));
-	} else {
-		wpa_printf(MSG_DEBUG, "nl80211: Regulatory information - country=%s",
-			   (char *) nla_data(tb_msg[NL80211_ATTR_REG_ALPHA2]));
-	}
-
-	nla_for_each_nested(nl_rule, tb_msg[NL80211_ATTR_REG_RULES], rem_rule)
-	{
-		u32 start, end, max_eirp = 0, max_bw = 0, flags = 0;
-		nla_parse(tb_rule, NL80211_FREQUENCY_ATTR_MAX,
-			  nla_data(nl_rule), nla_len(nl_rule), reg_policy);
-		if (tb_rule[NL80211_ATTR_FREQ_RANGE_START] == NULL ||
-		    tb_rule[NL80211_ATTR_FREQ_RANGE_END] == NULL)
-			continue;
-		start = nla_get_u32(tb_rule[NL80211_ATTR_FREQ_RANGE_START]) / 1000;
-		end = nla_get_u32(tb_rule[NL80211_ATTR_FREQ_RANGE_END]) / 1000;
-		if (tb_rule[NL80211_ATTR_POWER_RULE_MAX_EIRP])
-			max_eirp = nla_get_u32(tb_rule[NL80211_ATTR_POWER_RULE_MAX_EIRP]) / 100;
-		if (tb_rule[NL80211_ATTR_FREQ_RANGE_MAX_BW])
-			max_bw = nla_get_u32(tb_rule[NL80211_ATTR_FREQ_RANGE_MAX_BW]) / 1000;
-		if (tb_rule[NL80211_ATTR_REG_RULE_FLAGS])
-			flags = nla_get_u32(tb_rule[NL80211_ATTR_REG_RULE_FLAGS]);
-
-		wpa_printf(MSG_DEBUG, "nl80211: %u-%u @ %u MHz %u mBm%s%s%s%s%s%s%s%s",
-			   start, end, max_bw, max_eirp,
-			   flags & NL80211_RRF_NO_OFDM ? " (no OFDM)" : "",
-			   flags & NL80211_RRF_NO_CCK ? " (no CCK)" : "",
-			   flags & NL80211_RRF_NO_INDOOR ? " (no indoor)" : "",
-			   flags & NL80211_RRF_NO_OUTDOOR ? " (no outdoor)" :
-			   "",
-			   flags & NL80211_RRF_DFS ? " (DFS)" : "",
-			   flags & NL80211_RRF_PTP_ONLY ? " (PTP only)" : "",
-			   flags & NL80211_RRF_PTMP_ONLY ? " (PTMP only)" : "",
-			   flags & NL80211_RRF_NO_IR ? " (no IR)" : "");
-		if (max_bw >= 40)
-			nl80211_reg_rule_ht40(start, end, results);
-		if (tb_rule[NL80211_ATTR_POWER_RULE_MAX_EIRP])
-			nl80211_reg_rule_max_eirp(start, end, max_eirp,
-						  results);
-	}
-
-	nla_for_each_nested(nl_rule, tb_msg[NL80211_ATTR_REG_RULES], rem_rule)
-	{
-		nla_parse(tb_rule, NL80211_FREQUENCY_ATTR_MAX,
-			  nla_data(nl_rule), nla_len(nl_rule), reg_policy);
-		nl80211_reg_rule_sec(tb_rule, results);
-	}
-
-	nla_for_each_nested(nl_rule, tb_msg[NL80211_ATTR_REG_RULES], rem_rule)
-	{
-		nla_parse(tb_rule, NL80211_FREQUENCY_ATTR_MAX,
-			  nla_data(nl_rule), nla_len(nl_rule), reg_policy);
-		nl80211_reg_rule_vht(tb_rule, results);
-	}
-
-	return NL_SKIP;
-}
-
-
-static int nl80211_set_regulatory_flags(struct wpa_driver_nl80211_data *drv,
-					struct phy_info_arg *results)
-{
-	struct nl_msg *msg;
-
-	msg = nlmsg_alloc();
-	if (!msg)
-		return -ENOMEM;
-
-	nl80211_cmd(drv, msg, 0, NL80211_CMD_GET_REG);
-	return send_and_recv_msgs(drv, msg, nl80211_get_reg, results);
-}
-
-
-static struct hostapd_hw_modes *
-wpa_driver_nl80211_get_hw_feature_data(void *priv, u16 *num_modes, u16 *flags)
-{
-	u32 feat;
-	struct i802_bss *bss = priv;
-	struct wpa_driver_nl80211_data *drv = bss->drv;
-	struct nl_msg *msg;
-	struct phy_info_arg result = {
-		.num_modes = num_modes,
-		.modes = NULL,
-		.last_mode = -1,
-	};
-
-	*num_modes = 0;
-	*flags = 0;
-
-	msg = nlmsg_alloc();
-	if (!msg)
-		return NULL;
-
-	feat = get_nl80211_protocol_features(drv);
-	if (feat & NL80211_PROTOCOL_FEATURE_SPLIT_WIPHY_DUMP)
-		nl80211_cmd(drv, msg, NLM_F_DUMP, NL80211_CMD_GET_WIPHY);
-	else
-		nl80211_cmd(drv, msg, 0, NL80211_CMD_GET_WIPHY);
-
-	NLA_PUT_FLAG(msg, NL80211_ATTR_SPLIT_WIPHY_DUMP);
-	if (nl80211_set_iface_id(msg, bss) < 0)
-		goto nla_put_failure;
-
-	if (send_and_recv_msgs(drv, msg, phy_info_handler, &result) == 0) {
-		nl80211_set_regulatory_flags(drv, &result);
-		return wpa_driver_nl80211_postprocess_modes(result.modes,
-							    num_modes);
-	}
-	msg = NULL;
- nla_put_failure:
-	nlmsg_free(msg);
-	return NULL;
-}
-
-
-static int wpa_driver_nl80211_send_mntr(struct wpa_driver_nl80211_data *drv,
-					const void *data, size_t len,
-					int encrypt, int noack)
-{
-	__u8 rtap_hdr[] = {
-		0x00, 0x00, /* radiotap version */
-		0x0e, 0x00, /* radiotap length */
-		0x02, 0xc0, 0x00, 0x00, /* bmap: flags, tx and rx flags */
-		IEEE80211_RADIOTAP_F_FRAG, /* F_FRAG (fragment if required) */
-		0x00,       /* padding */
-		0x00, 0x00, /* RX and TX flags to indicate that */
-		0x00, 0x00, /* this is the injected frame directly */
-	};
-	struct iovec iov[2] = {
-		{
-			.iov_base = &rtap_hdr,
-			.iov_len = sizeof(rtap_hdr),
-		},
-		{
-			.iov_base = (void *) data,
-			.iov_len = len,
-		}
-	};
-	struct msghdr msg = {
-		.msg_name = NULL,
-		.msg_namelen = 0,
-		.msg_iov = iov,
-		.msg_iovlen = 2,
-		.msg_control = NULL,
-		.msg_controllen = 0,
-		.msg_flags = 0,
-	};
-	int res;
-	u16 txflags = 0;
-
-	if (encrypt)
-		rtap_hdr[8] |= IEEE80211_RADIOTAP_F_WEP;
-
-	if (drv->monitor_sock < 0) {
-		wpa_printf(MSG_DEBUG, "nl80211: No monitor socket available "
-			   "for %s", __func__);
-		return -1;
-	}
-
-	if (noack)
-		txflags |= IEEE80211_RADIOTAP_F_TX_NOACK;
-	WPA_PUT_LE16(&rtap_hdr[12], txflags);
-
-	res = sendmsg(drv->monitor_sock, &msg, 0);
-	if (res < 0) {
-		wpa_printf(MSG_INFO, "nl80211: sendmsg: %s", strerror(errno));
-		return -1;
-	}
-	return 0;
-}
-
-
 static int wpa_driver_nl80211_send_frame(struct i802_bss *bss,
 					 const void *data, size_t len,
 					 int encrypt, int noack,
@@ -7178,10 +3103,9 @@
 	}
 
 	if (drv->use_monitor) {
-		wpa_printf(MSG_DEBUG, "nl80211: send_frame(freq=%u bss->freq=%u) -> send_mntr",
+		wpa_printf(MSG_DEBUG, "nl80211: send_frame(freq=%u bss->freq=%u) -> send_monitor",
 			   freq, bss->freq);
-		return wpa_driver_nl80211_send_mntr(drv, data, len,
-						    encrypt, noack);
+		return nl80211_send_monitor(drv, data, len, encrypt, noack);
 	}
 
 	wpa_printf(MSG_DEBUG, "nl80211: send_frame -> send_frame_cmd");
@@ -7280,48 +3204,46 @@
 }
 
 
+static int nl80211_put_basic_rates(struct nl_msg *msg, const int *basic_rates)
+{
+	u8 rates[NL80211_MAX_SUPP_RATES];
+	u8 rates_len = 0;
+	int i;
+
+	if (!basic_rates)
+		return 0;
+
+	for (i = 0; i < NL80211_MAX_SUPP_RATES && basic_rates[i] >= 0; i++)
+		rates[rates_len++] = basic_rates[i] / 5;
+
+	return nla_put(msg, NL80211_ATTR_BSS_BASIC_RATES, rates_len, rates);
+}
+
+
 static int nl80211_set_bss(struct i802_bss *bss, int cts, int preamble,
 			   int slot, int ht_opmode, int ap_isolate,
-			   int *basic_rates)
+			   const int *basic_rates)
 {
 	struct wpa_driver_nl80211_data *drv = bss->drv;
 	struct nl_msg *msg;
 
-	msg = nlmsg_alloc();
-	if (!msg)
-		return -ENOMEM;
-
-	nl80211_cmd(drv, msg, 0, NL80211_CMD_SET_BSS);
-
-	if (cts >= 0)
-		NLA_PUT_U8(msg, NL80211_ATTR_BSS_CTS_PROT, cts);
-	if (preamble >= 0)
-		NLA_PUT_U8(msg, NL80211_ATTR_BSS_SHORT_PREAMBLE, preamble);
-	if (slot >= 0)
-		NLA_PUT_U8(msg, NL80211_ATTR_BSS_SHORT_SLOT_TIME, slot);
-	if (ht_opmode >= 0)
-		NLA_PUT_U16(msg, NL80211_ATTR_BSS_HT_OPMODE, ht_opmode);
-	if (ap_isolate >= 0)
-		NLA_PUT_U8(msg, NL80211_ATTR_AP_ISOLATE, ap_isolate);
-
-	if (basic_rates) {
-		u8 rates[NL80211_MAX_SUPP_RATES];
-		u8 rates_len = 0;
-		int i;
-
-		for (i = 0; i < NL80211_MAX_SUPP_RATES && basic_rates[i] >= 0;
-		     i++)
-			rates[rates_len++] = basic_rates[i] / 5;
-
-		NLA_PUT(msg, NL80211_ATTR_BSS_BASIC_RATES, rates_len, rates);
+	if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_SET_BSS)) ||
+	    (cts >= 0 &&
+	     nla_put_u8(msg, NL80211_ATTR_BSS_CTS_PROT, cts)) ||
+	    (preamble >= 0 &&
+	     nla_put_u8(msg, NL80211_ATTR_BSS_SHORT_PREAMBLE, preamble)) ||
+	    (slot >= 0 &&
+	     nla_put_u8(msg, NL80211_ATTR_BSS_SHORT_SLOT_TIME, slot)) ||
+	    (ht_opmode >= 0 &&
+	     nla_put_u16(msg, NL80211_ATTR_BSS_HT_OPMODE, ht_opmode)) ||
+	    (ap_isolate >= 0 &&
+	     nla_put_u8(msg, NL80211_ATTR_AP_ISOLATE, ap_isolate)) ||
+	    nl80211_put_basic_rates(msg, basic_rates)) {
+		nlmsg_free(msg);
+		return -ENOBUFS;
 	}
 
-	NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(bss->ifname));
-
 	return send_and_recv_msgs(drv, msg, NULL, NULL);
- nla_put_failure:
-	nlmsg_free(msg);
-	return -ENOBUFS;
 }
 
 
@@ -7333,7 +3255,7 @@
 	struct nl_msg *msg;
 	struct nlattr *acl;
 	unsigned int i;
-	int ret = 0;
+	int ret;
 
 	if (!(drv->capa.max_acl_mac_addrs))
 		return -ENOTSUP;
@@ -7341,44 +3263,49 @@
 	if (params->num_mac_acl > drv->capa.max_acl_mac_addrs)
 		return -ENOTSUP;
 
-	msg = nlmsg_alloc();
-	if (!msg)
-		return -ENOMEM;
-
 	wpa_printf(MSG_DEBUG, "nl80211: Set %s ACL (num_mac_acl=%u)",
 		   params->acl_policy ? "Accept" : "Deny", params->num_mac_acl);
 
-	nl80211_cmd(drv, msg, 0, NL80211_CMD_SET_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);
+		return -ENOMEM;
+	}
 
-	NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
-
-	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);
-	if (acl == NULL)
-		goto nla_put_failure;
-
-	for (i = 0; i < params->num_mac_acl; i++)
-		NLA_PUT(msg, i + 1, ETH_ALEN, params->mac_acl[i].addr);
+	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);
+			return -ENOMEM;
+		}
+	}
 
 	nla_nest_end(msg, acl);
 
 	ret = send_and_recv_msgs(drv, msg, NULL, NULL);
-	msg = NULL;
 	if (ret) {
 		wpa_printf(MSG_DEBUG, "nl80211: Failed to set MAC ACL: %d (%s)",
 			   ret, strerror(-ret));
 	}
 
-nla_put_failure:
-	nlmsg_free(msg);
-
 	return ret;
 }
 
 
+static int nl80211_put_beacon_int(struct nl_msg *msg, int beacon_int)
+{
+	if (beacon_int > 0) {
+		wpa_printf(MSG_DEBUG, "  * beacon_int=%d", beacon_int);
+		return nla_put_u32(msg, NL80211_ATTR_BEACON_INTERVAL,
+				   beacon_int);
+	}
+
+	return 0;
+}
+
+
 static int wpa_driver_nl80211_set_ap(void *priv,
 				     struct wpa_driver_ap_params *params)
 {
@@ -7388,75 +3315,80 @@
 	u8 cmd = NL80211_CMD_NEW_BEACON;
 	int ret;
 	int beacon_set;
-	int ifindex = if_nametoindex(bss->ifname);
 	int num_suites;
+	int smps_mode;
 	u32 suites[10], suite;
 	u32 ver;
 
-	beacon_set = bss->beacon_set;
-
-	msg = nlmsg_alloc();
-	if (!msg)
-		return -ENOMEM;
+	beacon_set = params->reenable ? 0 : bss->beacon_set;
 
 	wpa_printf(MSG_DEBUG, "nl80211: Set beacon (beacon_set=%d)",
 		   beacon_set);
 	if (beacon_set)
 		cmd = NL80211_CMD_SET_BEACON;
 
-	nl80211_cmd(drv, msg, 0, cmd);
 	wpa_hexdump(MSG_DEBUG, "nl80211: Beacon head",
 		    params->head, params->head_len);
-	NLA_PUT(msg, NL80211_ATTR_BEACON_HEAD, params->head_len, params->head);
 	wpa_hexdump(MSG_DEBUG, "nl80211: Beacon tail",
 		    params->tail, params->tail_len);
-	NLA_PUT(msg, NL80211_ATTR_BEACON_TAIL, params->tail_len, params->tail);
-	wpa_printf(MSG_DEBUG, "nl80211: ifindex=%d", ifindex);
-	NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, ifindex);
+	wpa_printf(MSG_DEBUG, "nl80211: ifindex=%d", bss->ifindex);
 	wpa_printf(MSG_DEBUG, "nl80211: beacon_int=%d", params->beacon_int);
-	NLA_PUT_U32(msg, NL80211_ATTR_BEACON_INTERVAL, params->beacon_int);
 	wpa_printf(MSG_DEBUG, "nl80211: dtim_period=%d", params->dtim_period);
-	NLA_PUT_U32(msg, NL80211_ATTR_DTIM_PERIOD, params->dtim_period);
 	wpa_hexdump_ascii(MSG_DEBUG, "nl80211: ssid",
 			  params->ssid, params->ssid_len);
-	NLA_PUT(msg, NL80211_ATTR_SSID, params->ssid_len,
-		params->ssid);
+	if (!(msg = nl80211_bss_msg(bss, 0, cmd)) ||
+	    nla_put(msg, NL80211_ATTR_BEACON_HEAD, params->head_len,
+		    params->head) ||
+	    nla_put(msg, NL80211_ATTR_BEACON_TAIL, params->tail_len,
+		    params->tail) ||
+	    nl80211_put_beacon_int(msg, params->beacon_int) ||
+	    nla_put_u32(msg, NL80211_ATTR_DTIM_PERIOD, params->dtim_period) ||
+	    nla_put(msg, NL80211_ATTR_SSID, params->ssid_len, params->ssid))
+		goto fail;
 	if (params->proberesp && params->proberesp_len) {
 		wpa_hexdump(MSG_DEBUG, "nl80211: proberesp (offload)",
 			    params->proberesp, params->proberesp_len);
-		NLA_PUT(msg, NL80211_ATTR_PROBE_RESP, params->proberesp_len,
-			params->proberesp);
+		if (nla_put(msg, NL80211_ATTR_PROBE_RESP, params->proberesp_len,
+			    params->proberesp))
+			goto fail;
 	}
 	switch (params->hide_ssid) {
 	case NO_SSID_HIDING:
 		wpa_printf(MSG_DEBUG, "nl80211: hidden SSID not in use");
-		NLA_PUT_U32(msg, NL80211_ATTR_HIDDEN_SSID,
-			    NL80211_HIDDEN_SSID_NOT_IN_USE);
+		if (nla_put_u32(msg, NL80211_ATTR_HIDDEN_SSID,
+				NL80211_HIDDEN_SSID_NOT_IN_USE))
+			goto fail;
 		break;
 	case HIDDEN_SSID_ZERO_LEN:
 		wpa_printf(MSG_DEBUG, "nl80211: hidden SSID zero len");
-		NLA_PUT_U32(msg, NL80211_ATTR_HIDDEN_SSID,
-			    NL80211_HIDDEN_SSID_ZERO_LEN);
+		if (nla_put_u32(msg, NL80211_ATTR_HIDDEN_SSID,
+				NL80211_HIDDEN_SSID_ZERO_LEN))
+			goto fail;
 		break;
 	case HIDDEN_SSID_ZERO_CONTENTS:
 		wpa_printf(MSG_DEBUG, "nl80211: hidden SSID zero contents");
-		NLA_PUT_U32(msg, NL80211_ATTR_HIDDEN_SSID,
-			    NL80211_HIDDEN_SSID_ZERO_CONTENTS);
+		if (nla_put_u32(msg, NL80211_ATTR_HIDDEN_SSID,
+				NL80211_HIDDEN_SSID_ZERO_CONTENTS))
+			goto fail;
 		break;
 	}
 	wpa_printf(MSG_DEBUG, "nl80211: privacy=%d", params->privacy);
-	if (params->privacy)
-		NLA_PUT_FLAG(msg, NL80211_ATTR_PRIVACY);
+	if (params->privacy &&
+	    nla_put_flag(msg, NL80211_ATTR_PRIVACY))
+		goto fail;
 	wpa_printf(MSG_DEBUG, "nl80211: auth_algs=0x%x", params->auth_algs);
 	if ((params->auth_algs & (WPA_AUTH_ALG_OPEN | WPA_AUTH_ALG_SHARED)) ==
 	    (WPA_AUTH_ALG_OPEN | WPA_AUTH_ALG_SHARED)) {
 		/* Leave out the attribute */
-	} else if (params->auth_algs & WPA_AUTH_ALG_SHARED)
-		NLA_PUT_U32(msg, NL80211_ATTR_AUTH_TYPE,
-			    NL80211_AUTHTYPE_SHARED_KEY);
-	else
-		NLA_PUT_U32(msg, NL80211_ATTR_AUTH_TYPE,
-			    NL80211_AUTHTYPE_OPEN_SYSTEM);
+	} else if (params->auth_algs & WPA_AUTH_ALG_SHARED) {
+		if (nla_put_u32(msg, NL80211_ATTR_AUTH_TYPE,
+				NL80211_AUTHTYPE_SHARED_KEY))
+			goto fail;
+	} else {
+		if (nla_put_u32(msg, NL80211_ATTR_AUTH_TYPE,
+				NL80211_AUTHTYPE_OPEN_SYSTEM))
+			goto fail;
+	}
 
 	wpa_printf(MSG_DEBUG, "nl80211: wpa_version=0x%x", params->wpa_version);
 	ver = 0;
@@ -7464,8 +3396,9 @@
 		ver |= NL80211_WPA_VERSION_1;
 	if (params->wpa_version & WPA_PROTO_RSN)
 		ver |= NL80211_WPA_VERSION_2;
-	if (ver)
-		NLA_PUT_U32(msg, NL80211_ATTR_WPA_VERSIONS, ver);
+	if (ver &&
+	    nla_put_u32(msg, NL80211_ATTR_WPA_VERSIONS, ver))
+		goto fail;
 
 	wpa_printf(MSG_DEBUG, "nl80211: key_mgmt_suites=0x%x",
 		   params->key_mgmt_suites);
@@ -7474,58 +3407,99 @@
 		suites[num_suites++] = WLAN_AKM_SUITE_8021X;
 	if (params->key_mgmt_suites & WPA_KEY_MGMT_PSK)
 		suites[num_suites++] = WLAN_AKM_SUITE_PSK;
-	if (num_suites) {
-		NLA_PUT(msg, NL80211_ATTR_AKM_SUITES,
-			num_suites * sizeof(u32), suites);
-	}
+	if (num_suites &&
+	    nla_put(msg, NL80211_ATTR_AKM_SUITES, num_suites * sizeof(u32),
+		    suites))
+		goto fail;
 
-	if (params->key_mgmt_suites & WPA_KEY_MGMT_IEEE8021X &&
-	    params->pairwise_ciphers & (WPA_CIPHER_WEP104 | WPA_CIPHER_WEP40))
-		NLA_PUT_FLAG(msg, NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT);
+	if (params->key_mgmt_suites & WPA_KEY_MGMT_IEEE8021X_NO_WPA &&
+	    params->pairwise_ciphers & (WPA_CIPHER_WEP104 | WPA_CIPHER_WEP40) &&
+	    nla_put_flag(msg, NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT))
+		goto fail;
 
 	wpa_printf(MSG_DEBUG, "nl80211: pairwise_ciphers=0x%x",
 		   params->pairwise_ciphers);
 	num_suites = wpa_cipher_to_cipher_suites(params->pairwise_ciphers,
 						 suites, ARRAY_SIZE(suites));
-	if (num_suites) {
-		NLA_PUT(msg, NL80211_ATTR_CIPHER_SUITES_PAIRWISE,
-			num_suites * sizeof(u32), suites);
-	}
+	if (num_suites &&
+	    nla_put(msg, NL80211_ATTR_CIPHER_SUITES_PAIRWISE,
+		    num_suites * sizeof(u32), suites))
+		goto fail;
 
 	wpa_printf(MSG_DEBUG, "nl80211: group_cipher=0x%x",
 		   params->group_cipher);
 	suite = wpa_cipher_to_cipher_suite(params->group_cipher);
-	if (suite)
-		NLA_PUT_U32(msg, NL80211_ATTR_CIPHER_SUITE_GROUP, suite);
+	if (suite &&
+	    nla_put_u32(msg, NL80211_ATTR_CIPHER_SUITE_GROUP, suite))
+		goto fail;
+
+	switch (params->smps_mode) {
+	case HT_CAP_INFO_SMPS_DYNAMIC:
+		wpa_printf(MSG_DEBUG, "nl80211: SMPS mode - dynamic");
+		smps_mode = NL80211_SMPS_DYNAMIC;
+		break;
+	case HT_CAP_INFO_SMPS_STATIC:
+		wpa_printf(MSG_DEBUG, "nl80211: SMPS mode - static");
+		smps_mode = NL80211_SMPS_STATIC;
+		break;
+	default:
+		/* invalid - fallback to smps off */
+	case HT_CAP_INFO_SMPS_DISABLED:
+		wpa_printf(MSG_DEBUG, "nl80211: SMPS mode - off");
+		smps_mode = NL80211_SMPS_OFF;
+		break;
+	}
+	if (nla_put_u32(msg, NL80211_ATTR_SMPS_MODE, smps_mode))
+		goto fail;
 
 	if (params->beacon_ies) {
 		wpa_hexdump_buf(MSG_DEBUG, "nl80211: beacon_ies",
 				params->beacon_ies);
-		NLA_PUT(msg, NL80211_ATTR_IE, wpabuf_len(params->beacon_ies),
-			wpabuf_head(params->beacon_ies));
+		if (nla_put(msg, NL80211_ATTR_IE,
+			    wpabuf_len(params->beacon_ies),
+			    wpabuf_head(params->beacon_ies)))
+			goto fail;
 	}
 	if (params->proberesp_ies) {
 		wpa_hexdump_buf(MSG_DEBUG, "nl80211: proberesp_ies",
 				params->proberesp_ies);
-		NLA_PUT(msg, NL80211_ATTR_IE_PROBE_RESP,
-			wpabuf_len(params->proberesp_ies),
-			wpabuf_head(params->proberesp_ies));
+		if (nla_put(msg, NL80211_ATTR_IE_PROBE_RESP,
+			    wpabuf_len(params->proberesp_ies),
+			    wpabuf_head(params->proberesp_ies)))
+			goto fail;
 	}
 	if (params->assocresp_ies) {
 		wpa_hexdump_buf(MSG_DEBUG, "nl80211: assocresp_ies",
 				params->assocresp_ies);
-		NLA_PUT(msg, NL80211_ATTR_IE_ASSOC_RESP,
-			wpabuf_len(params->assocresp_ies),
-			wpabuf_head(params->assocresp_ies));
+		if (nla_put(msg, NL80211_ATTR_IE_ASSOC_RESP,
+			    wpabuf_len(params->assocresp_ies),
+			    wpabuf_head(params->assocresp_ies)))
+			goto fail;
 	}
 
 	if (drv->capa.flags & WPA_DRIVER_FLAGS_INACTIVITY_TIMER)  {
 		wpa_printf(MSG_DEBUG, "nl80211: ap_max_inactivity=%d",
 			   params->ap_max_inactivity);
-		NLA_PUT_U16(msg, NL80211_ATTR_INACTIVITY_TIMEOUT,
-			    params->ap_max_inactivity);
+		if (nla_put_u16(msg, NL80211_ATTR_INACTIVITY_TIMEOUT,
+				params->ap_max_inactivity))
+			goto fail;
 	}
 
+#ifdef CONFIG_P2P
+	if (params->p2p_go_ctwindow > 0) {
+		if (drv->p2p_go_ctwindow_supported) {
+			wpa_printf(MSG_DEBUG, "nl80211: P2P GO ctwindow=%d",
+				   params->p2p_go_ctwindow);
+			if (nla_put_u8(msg, NL80211_ATTR_P2P_CTWINDOW,
+				       params->p2p_go_ctwindow))
+				goto fail;
+		} else {
+			wpa_printf(MSG_INFO,
+				   "nl80211: Driver does not support CTWindow configuration - ignore this parameter");
+		}
+	}
+#endif /* CONFIG_P2P */
+
 	ret = send_and_recv_msgs(drv, msg, NULL, NULL);
 	if (ret) {
 		wpa_printf(MSG_DEBUG, "nl80211: Beacon set failed: %d (%s)",
@@ -7561,65 +3535,80 @@
 		}
 	}
 	return ret;
- nla_put_failure:
+fail:
 	nlmsg_free(msg);
 	return -ENOBUFS;
 }
 
 
 static int nl80211_put_freq_params(struct nl_msg *msg,
-				   struct hostapd_freq_params *freq)
+				   const struct hostapd_freq_params *freq)
 {
-	NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, freq->freq);
+	wpa_printf(MSG_DEBUG, "  * freq=%d", freq->freq);
+	if (nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ, freq->freq))
+		return -ENOBUFS;
+
+	wpa_printf(MSG_DEBUG, "  * vht_enabled=%d", freq->vht_enabled);
+	wpa_printf(MSG_DEBUG, "  * ht_enabled=%d", freq->ht_enabled);
+
 	if (freq->vht_enabled) {
+		enum nl80211_chan_width cw;
+
+		wpa_printf(MSG_DEBUG, "  * bandwidth=%d", freq->bandwidth);
 		switch (freq->bandwidth) {
 		case 20:
-			NLA_PUT_U32(msg, NL80211_ATTR_CHANNEL_WIDTH,
-				    NL80211_CHAN_WIDTH_20);
+			cw = NL80211_CHAN_WIDTH_20;
 			break;
 		case 40:
-			NLA_PUT_U32(msg, NL80211_ATTR_CHANNEL_WIDTH,
-				    NL80211_CHAN_WIDTH_40);
+			cw = NL80211_CHAN_WIDTH_40;
 			break;
 		case 80:
 			if (freq->center_freq2)
-				NLA_PUT_U32(msg, NL80211_ATTR_CHANNEL_WIDTH,
-					    NL80211_CHAN_WIDTH_80P80);
+				cw = NL80211_CHAN_WIDTH_80P80;
 			else
-				NLA_PUT_U32(msg, NL80211_ATTR_CHANNEL_WIDTH,
-					    NL80211_CHAN_WIDTH_80);
+				cw = NL80211_CHAN_WIDTH_80;
 			break;
 		case 160:
-			NLA_PUT_U32(msg, NL80211_ATTR_CHANNEL_WIDTH,
-				    NL80211_CHAN_WIDTH_160);
+			cw = NL80211_CHAN_WIDTH_160;
 			break;
 		default:
 			return -EINVAL;
 		}
-		NLA_PUT_U32(msg, NL80211_ATTR_CENTER_FREQ1, freq->center_freq1);
-		if (freq->center_freq2)
-			NLA_PUT_U32(msg, NL80211_ATTR_CENTER_FREQ2,
-				    freq->center_freq2);
+
+		wpa_printf(MSG_DEBUG, "  * channel_width=%d", cw);
+		wpa_printf(MSG_DEBUG, "  * center_freq1=%d",
+			   freq->center_freq1);
+		wpa_printf(MSG_DEBUG, "  * center_freq2=%d",
+			   freq->center_freq2);
+		if (nla_put_u32(msg, NL80211_ATTR_CHANNEL_WIDTH, cw) ||
+		    nla_put_u32(msg, NL80211_ATTR_CENTER_FREQ1,
+				freq->center_freq1) ||
+		    (freq->center_freq2 &&
+		     nla_put_u32(msg, NL80211_ATTR_CENTER_FREQ2,
+				 freq->center_freq2)))
+			return -ENOBUFS;
 	} else if (freq->ht_enabled) {
+		enum nl80211_channel_type ct;
+
+		wpa_printf(MSG_DEBUG, "  * sec_channel_offset=%d",
+			   freq->sec_channel_offset);
 		switch (freq->sec_channel_offset) {
 		case -1:
-			NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE,
-				    NL80211_CHAN_HT40MINUS);
+			ct = NL80211_CHAN_HT40MINUS;
 			break;
 		case 1:
-			NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE,
-				    NL80211_CHAN_HT40PLUS);
+			ct = NL80211_CHAN_HT40PLUS;
 			break;
 		default:
-			NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE,
-				    NL80211_CHAN_HT20);
+			ct = NL80211_CHAN_HT20;
 			break;
 		}
+
+		wpa_printf(MSG_DEBUG, "  * channel_type=%d", ct);
+		if (nla_put_u32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE, ct))
+			return -ENOBUFS;
 	}
 	return 0;
-
-nla_put_failure:
-	return -ENOBUFS;
 }
 
 
@@ -7634,27 +3623,21 @@
 		   "nl80211: Set freq %d (ht_enabled=%d, vht_enabled=%d, bandwidth=%d MHz, cf1=%d MHz, cf2=%d MHz)",
 		   freq->freq, freq->ht_enabled, freq->vht_enabled,
 		   freq->bandwidth, freq->center_freq1, freq->center_freq2);
-	msg = nlmsg_alloc();
-	if (!msg)
+
+	msg = nl80211_drv_msg(drv, 0, set_chan ? NL80211_CMD_SET_CHANNEL :
+			      NL80211_CMD_SET_WIPHY);
+	if (!msg || nl80211_put_freq_params(msg, freq) < 0) {
+		nlmsg_free(msg);
 		return -1;
-
-	nl80211_cmd(drv, msg, 0, set_chan ? NL80211_CMD_SET_CHANNEL :
-		    NL80211_CMD_SET_WIPHY);
-
-	NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
-	if (nl80211_put_freq_params(msg, freq) < 0)
-		goto nla_put_failure;
+	}
 
 	ret = send_and_recv_msgs(drv, msg, NULL, NULL);
-	msg = NULL;
 	if (ret == 0) {
 		bss->freq = freq->freq;
 		return 0;
 	}
 	wpa_printf(MSG_DEBUG, "nl80211: Failed to set channel (freq=%d): "
 		   "%d (%s)", freq->freq, ret, strerror(-ret));
-nla_put_failure:
-	nlmsg_free(msg);
 	return -1;
 }
 
@@ -7673,11 +3656,40 @@
 		f |= BIT(NL80211_STA_FLAG_MFP);
 	if (flags & WPA_STA_TDLS_PEER)
 		f |= BIT(NL80211_STA_FLAG_TDLS_PEER);
+	if (flags & WPA_STA_AUTHENTICATED)
+		f |= BIT(NL80211_STA_FLAG_AUTHENTICATED);
 
 	return f;
 }
 
 
+#ifdef CONFIG_MESH
+static u32 sta_plink_state_nl80211(enum mesh_plink_state state)
+{
+	switch (state) {
+	case PLINK_LISTEN:
+		return NL80211_PLINK_LISTEN;
+	case PLINK_OPEN_SENT:
+		return NL80211_PLINK_OPN_SNT;
+	case PLINK_OPEN_RCVD:
+		return NL80211_PLINK_OPN_RCVD;
+	case PLINK_CNF_RCVD:
+		return NL80211_PLINK_CNF_RCVD;
+	case PLINK_ESTAB:
+		return NL80211_PLINK_ESTAB;
+	case PLINK_HOLDING:
+		return NL80211_PLINK_HOLDING;
+	case PLINK_BLOCKED:
+		return NL80211_PLINK_BLOCKED;
+	default:
+		wpa_printf(MSG_ERROR, "nl80211: Invalid mesh plink state %d",
+			   state);
+	}
+	return -1;
+}
+#endif /* CONFIG_MESH */
+
+
 static int wpa_driver_nl80211_sta_add(void *priv,
 				      struct hostapd_sta_add_params *params)
 {
@@ -7691,25 +3703,57 @@
 	    !(drv->capa.flags & WPA_DRIVER_FLAGS_TDLS_SUPPORT))
 		return -EOPNOTSUPP;
 
-	msg = nlmsg_alloc();
-	if (!msg)
-		return -ENOMEM;
-
 	wpa_printf(MSG_DEBUG, "nl80211: %s STA " MACSTR,
 		   params->set ? "Set" : "Add", MAC2STR(params->addr));
-	nl80211_cmd(drv, msg, 0, params->set ? NL80211_CMD_SET_STATION :
-		    NL80211_CMD_NEW_STATION);
+	msg = nl80211_bss_msg(bss, 0, params->set ? NL80211_CMD_SET_STATION :
+			      NL80211_CMD_NEW_STATION);
+	if (!msg || nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, params->addr))
+		goto fail;
 
-	NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(bss->ifname));
-	NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, params->addr);
-	NLA_PUT(msg, NL80211_ATTR_STA_SUPPORTED_RATES, params->supp_rates_len,
-		params->supp_rates);
-	wpa_hexdump(MSG_DEBUG, "  * supported rates", params->supp_rates,
-		    params->supp_rates_len);
+	if (!params->set || (params->flags & WPA_STA_TDLS_PEER)) {
+		wpa_hexdump(MSG_DEBUG, "  * supported rates",
+			    params->supp_rates, params->supp_rates_len);
+		wpa_printf(MSG_DEBUG, "  * capability=0x%x",
+			   params->capability);
+		if (nla_put(msg, NL80211_ATTR_STA_SUPPORTED_RATES,
+			    params->supp_rates_len, params->supp_rates) ||
+		    nla_put_u16(msg, NL80211_ATTR_STA_CAPABILITY,
+				params->capability))
+			goto fail;
+
+		if (params->ht_capabilities) {
+			wpa_hexdump(MSG_DEBUG, "  * ht_capabilities",
+				    (u8 *) params->ht_capabilities,
+				    sizeof(*params->ht_capabilities));
+			if (nla_put(msg, NL80211_ATTR_HT_CAPABILITY,
+				    sizeof(*params->ht_capabilities),
+				    params->ht_capabilities))
+				goto fail;
+		}
+
+		if (params->vht_capabilities) {
+			wpa_hexdump(MSG_DEBUG, "  * vht_capabilities",
+				    (u8 *) params->vht_capabilities,
+				    sizeof(*params->vht_capabilities));
+			if (nla_put(msg, NL80211_ATTR_VHT_CAPABILITY,
+				    sizeof(*params->vht_capabilities),
+				    params->vht_capabilities))
+				goto fail;
+		}
+
+		if (params->ext_capab) {
+			wpa_hexdump(MSG_DEBUG, "  * ext_capab",
+				    params->ext_capab, params->ext_capab_len);
+			if (nla_put(msg, NL80211_ATTR_STA_EXT_CAPABILITY,
+				    params->ext_capab_len, params->ext_capab))
+				goto fail;
+		}
+	}
 	if (!params->set) {
 		if (params->aid) {
 			wpa_printf(MSG_DEBUG, "  * aid=%u", params->aid);
-			NLA_PUT_U16(msg, NL80211_ATTR_STA_AID, params->aid);
+			if (nla_put_u16(msg, NL80211_ATTR_STA_AID, params->aid))
+				goto fail;
 		} else {
 			/*
 			 * cfg80211 validates that AID is non-zero, so we have
@@ -7717,85 +3761,71 @@
 			 * a dummy STA entry is used for now.
 			 */
 			wpa_printf(MSG_DEBUG, "  * aid=1 (TDLS workaround)");
-			NLA_PUT_U16(msg, NL80211_ATTR_STA_AID, 1);
+			if (nla_put_u16(msg, NL80211_ATTR_STA_AID, 1))
+				goto fail;
 		}
 		wpa_printf(MSG_DEBUG, "  * listen_interval=%u",
 			   params->listen_interval);
-		NLA_PUT_U16(msg, NL80211_ATTR_STA_LISTEN_INTERVAL,
-			    params->listen_interval);
+		if (nla_put_u16(msg, NL80211_ATTR_STA_LISTEN_INTERVAL,
+				params->listen_interval))
+			goto fail;
 	} else if (params->aid && (params->flags & WPA_STA_TDLS_PEER)) {
 		wpa_printf(MSG_DEBUG, "  * peer_aid=%u", params->aid);
-		NLA_PUT_U16(msg, NL80211_ATTR_PEER_AID, params->aid);
-	}
-	if (params->ht_capabilities) {
-		wpa_hexdump(MSG_DEBUG, "  * ht_capabilities",
-			    (u8 *) params->ht_capabilities,
-			    sizeof(*params->ht_capabilities));
-		NLA_PUT(msg, NL80211_ATTR_HT_CAPABILITY,
-			sizeof(*params->ht_capabilities),
-			params->ht_capabilities);
-	}
-
-	if (params->vht_capabilities) {
-		wpa_hexdump(MSG_DEBUG, "  * vht_capabilities",
-			    (u8 *) params->vht_capabilities,
-			    sizeof(*params->vht_capabilities));
-		NLA_PUT(msg, NL80211_ATTR_VHT_CAPABILITY,
-			sizeof(*params->vht_capabilities),
-			params->vht_capabilities);
+		if (nla_put_u16(msg, NL80211_ATTR_PEER_AID, params->aid))
+			goto fail;
 	}
 
 	if (params->vht_opmode_enabled) {
 		wpa_printf(MSG_DEBUG, "  * opmode=%u", params->vht_opmode);
-		NLA_PUT_U8(msg, NL80211_ATTR_OPMODE_NOTIF,
-			   params->vht_opmode);
-	}
-
-	wpa_printf(MSG_DEBUG, "  * capability=0x%x", params->capability);
-	NLA_PUT_U16(msg, NL80211_ATTR_STA_CAPABILITY, params->capability);
-
-	if (params->ext_capab) {
-		wpa_hexdump(MSG_DEBUG, "  * ext_capab",
-			    params->ext_capab, params->ext_capab_len);
-		NLA_PUT(msg, NL80211_ATTR_STA_EXT_CAPABILITY,
-			params->ext_capab_len, params->ext_capab);
+		if (nla_put_u8(msg, NL80211_ATTR_OPMODE_NOTIF,
+			       params->vht_opmode))
+			goto fail;
 	}
 
 	if (params->supp_channels) {
 		wpa_hexdump(MSG_DEBUG, "  * supported channels",
 			    params->supp_channels, params->supp_channels_len);
-		NLA_PUT(msg, NL80211_ATTR_STA_SUPPORTED_CHANNELS,
-			params->supp_channels_len, params->supp_channels);
+		if (nla_put(msg, NL80211_ATTR_STA_SUPPORTED_CHANNELS,
+			    params->supp_channels_len, params->supp_channels))
+			goto fail;
 	}
 
 	if (params->supp_oper_classes) {
 		wpa_hexdump(MSG_DEBUG, "  * supported operating classes",
 			    params->supp_oper_classes,
 			    params->supp_oper_classes_len);
-		NLA_PUT(msg, NL80211_ATTR_STA_SUPPORTED_OPER_CLASSES,
-			params->supp_oper_classes_len,
-			params->supp_oper_classes);
+		if (nla_put(msg, NL80211_ATTR_STA_SUPPORTED_OPER_CLASSES,
+			    params->supp_oper_classes_len,
+			    params->supp_oper_classes))
+			goto fail;
 	}
 
 	os_memset(&upd, 0, sizeof(upd));
-	upd.mask = sta_flags_nl80211(params->flags);
-	upd.set = upd.mask;
+	upd.set = sta_flags_nl80211(params->flags);
+	upd.mask = upd.set | sta_flags_nl80211(params->flags_mask);
 	wpa_printf(MSG_DEBUG, "  * flags set=0x%x mask=0x%x",
 		   upd.set, upd.mask);
-	NLA_PUT(msg, NL80211_ATTR_STA_FLAGS2, sizeof(upd), &upd);
+	if (nla_put(msg, NL80211_ATTR_STA_FLAGS2, sizeof(upd), &upd))
+		goto fail;
+
+#ifdef CONFIG_MESH
+	if (params->plink_state &&
+	    nla_put_u8(msg, NL80211_ATTR_STA_PLINK_STATE,
+		       sta_plink_state_nl80211(params->plink_state)))
+		goto fail;
+#endif /* CONFIG_MESH */
 
 	if (params->flags & WPA_STA_WMM) {
 		struct nlattr *wme = nla_nest_start(msg, NL80211_ATTR_STA_WME);
 
-		if (!wme)
-			goto nla_put_failure;
-
 		wpa_printf(MSG_DEBUG, "  * qosinfo=0x%x", params->qosinfo);
-		NLA_PUT_U8(msg, NL80211_STA_WME_UAPSD_QUEUES,
-				params->qosinfo & WMM_QOSINFO_STA_AC_MASK);
-		NLA_PUT_U8(msg, NL80211_STA_WME_MAX_SP,
-				(params->qosinfo >> WMM_QOSINFO_STA_SP_SHIFT) &
-				WMM_QOSINFO_STA_SP_MASK);
+		if (!wme ||
+		    nla_put_u8(msg, NL80211_STA_WME_UAPSD_QUEUES,
+			       params->qosinfo & WMM_QOSINFO_STA_AC_MASK) ||
+		    nla_put_u8(msg, NL80211_STA_WME_MAX_SP,
+			       (params->qosinfo >> WMM_QOSINFO_STA_SP_SHIFT) &
+			       WMM_QOSINFO_STA_SP_MASK))
+			goto fail;
 		nla_nest_end(msg, wme);
 	}
 
@@ -7807,7 +3837,7 @@
 			   strerror(-ret));
 	if (ret == -EEXIST)
 		ret = 0;
- nla_put_failure:
+fail:
 	nlmsg_free(msg);
 	return ret;
 }
@@ -7850,21 +3880,26 @@
 }
 
 
-static int wpa_driver_nl80211_sta_remove(struct i802_bss *bss, const u8 *addr)
+static int wpa_driver_nl80211_sta_remove(struct i802_bss *bss, const u8 *addr,
+					 int deauth, u16 reason_code)
 {
 	struct wpa_driver_nl80211_data *drv = bss->drv;
 	struct nl_msg *msg;
 	int ret;
 
-	msg = nlmsg_alloc();
-	if (!msg)
-		return -ENOMEM;
-
-	nl80211_cmd(drv, msg, 0, NL80211_CMD_DEL_STATION);
-
-	NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX,
-		    if_nametoindex(bss->ifname));
-	NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr);
+	if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_DEL_STATION)) ||
+	    nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr) ||
+	    (deauth == 0 &&
+	     nla_put_u8(msg, NL80211_ATTR_MGMT_SUBTYPE,
+			WLAN_FC_STYPE_DISASSOC)) ||
+	    (deauth == 1 &&
+	     nla_put_u8(msg, NL80211_ATTR_MGMT_SUBTYPE,
+			WLAN_FC_STYPE_DEAUTH)) ||
+	    (reason_code &&
+	     nla_put_u16(msg, NL80211_ATTR_REASON_CODE, reason_code))) {
+		nlmsg_free(msg);
+		return -ENOBUFS;
+	}
 
 	ret = send_and_recv_msgs(drv, msg, NULL, NULL);
 	wpa_printf(MSG_DEBUG, "nl80211: sta_remove -> DEL_STATION %s " MACSTR
@@ -7877,14 +3912,10 @@
 	if (ret == -ENOENT)
 		return 0;
 	return ret;
- nla_put_failure:
-	nlmsg_free(msg);
-	return -ENOBUFS;
 }
 
 
-static void nl80211_remove_iface(struct wpa_driver_nl80211_data *drv,
-				 int ifidx)
+void nl80211_remove_iface(struct wpa_driver_nl80211_data *drv, int ifidx)
 {
 	struct nl_msg *msg;
 	struct wpa_driver_nl80211_data *drv2;
@@ -7896,18 +3927,9 @@
 			 struct wpa_driver_nl80211_data, list)
 		del_ifidx(drv2, ifidx);
 
-	msg = nlmsg_alloc();
-	if (!msg)
-		goto nla_put_failure;
-
-	nl80211_cmd(drv, msg, 0, NL80211_CMD_DEL_INTERFACE);
-	NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, ifidx);
-
+	msg = nl80211_ifindex_msg(drv, ifidx, 0, NL80211_CMD_DEL_INTERFACE);
 	if (send_and_recv_msgs(drv, msg, NULL, NULL) == 0)
 		return;
-	msg = NULL;
- nla_put_failure:
-	nlmsg_free(msg);
 	wpa_printf(MSG_ERROR, "Failed to remove interface (ifidx=%d)", ifidx);
 }
 
@@ -7955,40 +3977,37 @@
 	wpa_printf(MSG_DEBUG, "nl80211: Create interface iftype %d (%s)",
 		   iftype, nl80211_iftype_str(iftype));
 
-	msg = nlmsg_alloc();
-	if (!msg)
-		return -1;
-
-	nl80211_cmd(drv, msg, 0, NL80211_CMD_NEW_INTERFACE);
-	if (nl80211_set_iface_id(msg, drv->first_bss) < 0)
-		goto nla_put_failure;
-	NLA_PUT_STRING(msg, NL80211_ATTR_IFNAME, ifname);
-	NLA_PUT_U32(msg, NL80211_ATTR_IFTYPE, iftype);
+	msg = nl80211_cmd_msg(drv->first_bss, 0, NL80211_CMD_NEW_INTERFACE);
+	if (!msg ||
+	    nla_put_string(msg, NL80211_ATTR_IFNAME, ifname) ||
+	    nla_put_u32(msg, NL80211_ATTR_IFTYPE, iftype))
+		goto fail;
 
 	if (iftype == NL80211_IFTYPE_MONITOR) {
 		struct nlattr *flags;
 
 		flags = nla_nest_start(msg, NL80211_ATTR_MNTR_FLAGS);
-		if (!flags)
-			goto nla_put_failure;
-
-		NLA_PUT_FLAG(msg, NL80211_MNTR_FLAG_COOK_FRAMES);
+		if (!flags ||
+		    nla_put_flag(msg, NL80211_MNTR_FLAG_COOK_FRAMES))
+			goto fail;
 
 		nla_nest_end(msg, flags);
 	} else if (wds) {
-		NLA_PUT_U8(msg, NL80211_ATTR_4ADDR, wds);
+		if (nla_put_u8(msg, NL80211_ATTR_4ADDR, wds))
+			goto fail;
 	}
 
 	/*
 	 * Tell cfg80211 that the interface belongs to the socket that created
 	 * it, and the interface should be deleted when the socket is closed.
 	 */
-	NLA_PUT_FLAG(msg, NL80211_ATTR_IFACE_SOCKET_OWNER);
+	if (nla_put_flag(msg, NL80211_ATTR_IFACE_SOCKET_OWNER))
+		goto fail;
 
 	ret = send_and_recv_msgs(drv, msg, handler, arg);
 	msg = NULL;
 	if (ret) {
- nla_put_failure:
+	fail:
 		nlmsg_free(msg);
 		wpa_printf(MSG_ERROR, "Failed to create interface %s: %d (%s)",
 			   ifname, ret, strerror(-ret));
@@ -8027,11 +4046,11 @@
 }
 
 
-static int nl80211_create_iface(struct wpa_driver_nl80211_data *drv,
-				const char *ifname, enum nl80211_iftype iftype,
-				const u8 *addr, int wds,
-				int (*handler)(struct nl_msg *, void *),
-				void *arg, int use_existing)
+int nl80211_create_iface(struct wpa_driver_nl80211_data *drv,
+			 const char *ifname, enum nl80211_iftype iftype,
+			 const u8 *addr, int wds,
+			 int (*handler)(struct nl_msg *, void *),
+			 void *arg, int use_existing)
 {
 	int ret;
 
@@ -8065,426 +4084,17 @@
 						wds, handler, arg);
 	}
 
-	if (ret >= 0 && is_p2p_net_interface(iftype))
+	if (ret >= 0 && is_p2p_net_interface(iftype)) {
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: Interface %s created for P2P - disable 11b rates",
+			   ifname);
 		nl80211_disable_11b_rates(drv, ret, 1);
+	}
 
 	return ret;
 }
 
 
-static void handle_tx_callback(void *ctx, u8 *buf, size_t len, int ok)
-{
-	struct ieee80211_hdr *hdr;
-	u16 fc;
-	union wpa_event_data event;
-
-	hdr = (struct ieee80211_hdr *) buf;
-	fc = le_to_host16(hdr->frame_control);
-
-	os_memset(&event, 0, sizeof(event));
-	event.tx_status.type = WLAN_FC_GET_TYPE(fc);
-	event.tx_status.stype = WLAN_FC_GET_STYPE(fc);
-	event.tx_status.dst = hdr->addr1;
-	event.tx_status.data = buf;
-	event.tx_status.data_len = len;
-	event.tx_status.ack = ok;
-	wpa_supplicant_event(ctx, EVENT_TX_STATUS, &event);
-}
-
-
-static void from_unknown_sta(struct wpa_driver_nl80211_data *drv,
-			     u8 *buf, size_t len)
-{
-	struct ieee80211_hdr *hdr = (void *)buf;
-	u16 fc;
-	union wpa_event_data event;
-
-	if (len < sizeof(*hdr))
-		return;
-
-	fc = le_to_host16(hdr->frame_control);
-
-	os_memset(&event, 0, sizeof(event));
-	event.rx_from_unknown.bssid = get_hdr_bssid(hdr, len);
-	event.rx_from_unknown.addr = hdr->addr2;
-	event.rx_from_unknown.wds = (fc & (WLAN_FC_FROMDS | WLAN_FC_TODS)) ==
-		(WLAN_FC_FROMDS | WLAN_FC_TODS);
-	wpa_supplicant_event(drv->ctx, EVENT_RX_FROM_UNKNOWN, &event);
-}
-
-
-static void handle_frame(struct wpa_driver_nl80211_data *drv,
-			 u8 *buf, size_t len, int datarate, int ssi_signal)
-{
-	struct ieee80211_hdr *hdr;
-	u16 fc;
-	union wpa_event_data event;
-
-	hdr = (struct ieee80211_hdr *) buf;
-	fc = le_to_host16(hdr->frame_control);
-
-	switch (WLAN_FC_GET_TYPE(fc)) {
-	case WLAN_FC_TYPE_MGMT:
-		os_memset(&event, 0, sizeof(event));
-		event.rx_mgmt.frame = buf;
-		event.rx_mgmt.frame_len = len;
-		event.rx_mgmt.datarate = datarate;
-		event.rx_mgmt.ssi_signal = ssi_signal;
-		wpa_supplicant_event(drv->ctx, EVENT_RX_MGMT, &event);
-		break;
-	case WLAN_FC_TYPE_CTRL:
-		/* can only get here with PS-Poll frames */
-		wpa_printf(MSG_DEBUG, "CTRL");
-		from_unknown_sta(drv, buf, len);
-		break;
-	case WLAN_FC_TYPE_DATA:
-		from_unknown_sta(drv, buf, len);
-		break;
-	}
-}
-
-
-static void handle_monitor_read(int sock, void *eloop_ctx, void *sock_ctx)
-{
-	struct wpa_driver_nl80211_data *drv = eloop_ctx;
-	int len;
-	unsigned char buf[3000];
-	struct ieee80211_radiotap_iterator iter;
-	int ret;
-	int datarate = 0, ssi_signal = 0;
-	int injected = 0, failed = 0, rxflags = 0;
-
-	len = recv(sock, buf, sizeof(buf), 0);
-	if (len < 0) {
-		wpa_printf(MSG_ERROR, "nl80211: Monitor socket recv failed: %s",
-			   strerror(errno));
-		return;
-	}
-
-	if (ieee80211_radiotap_iterator_init(&iter, (void *) buf, len, NULL)) {
-		wpa_printf(MSG_INFO, "nl80211: received invalid radiotap frame");
-		return;
-	}
-
-	while (1) {
-		ret = ieee80211_radiotap_iterator_next(&iter);
-		if (ret == -ENOENT)
-			break;
-		if (ret) {
-			wpa_printf(MSG_INFO, "nl80211: received invalid radiotap frame (%d)",
-				   ret);
-			return;
-		}
-		switch (iter.this_arg_index) {
-		case IEEE80211_RADIOTAP_FLAGS:
-			if (*iter.this_arg & IEEE80211_RADIOTAP_F_FCS)
-				len -= 4;
-			break;
-		case IEEE80211_RADIOTAP_RX_FLAGS:
-			rxflags = 1;
-			break;
-		case IEEE80211_RADIOTAP_TX_FLAGS:
-			injected = 1;
-			failed = le_to_host16((*(uint16_t *) iter.this_arg)) &
-					IEEE80211_RADIOTAP_F_TX_FAIL;
-			break;
-		case IEEE80211_RADIOTAP_DATA_RETRIES:
-			break;
-		case IEEE80211_RADIOTAP_CHANNEL:
-			/* TODO: convert from freq/flags to channel number */
-			break;
-		case IEEE80211_RADIOTAP_RATE:
-			datarate = *iter.this_arg * 5;
-			break;
-		case IEEE80211_RADIOTAP_DBM_ANTSIGNAL:
-			ssi_signal = (s8) *iter.this_arg;
-			break;
-		}
-	}
-
-	if (rxflags && injected)
-		return;
-
-	if (!injected)
-		handle_frame(drv, buf + iter._max_length,
-			     len - iter._max_length, datarate, ssi_signal);
-	else
-		handle_tx_callback(drv->ctx, buf + iter._max_length,
-				   len - iter._max_length, !failed);
-}
-
-
-/*
- * we post-process the filter code later and rewrite
- * this to the offset to the last instruction
- */
-#define PASS	0xFF
-#define FAIL	0xFE
-
-static struct sock_filter msock_filter_insns[] = {
-	/*
-	 * do a little-endian load of the radiotap length field
-	 */
-	/* load lower byte into A */
-	BPF_STMT(BPF_LD  | BPF_B | BPF_ABS, 2),
-	/* put it into X (== index register) */
-	BPF_STMT(BPF_MISC| BPF_TAX, 0),
-	/* load upper byte into A */
-	BPF_STMT(BPF_LD  | BPF_B | BPF_ABS, 3),
-	/* left-shift it by 8 */
-	BPF_STMT(BPF_ALU | BPF_LSH | BPF_K, 8),
-	/* or with X */
-	BPF_STMT(BPF_ALU | BPF_OR | BPF_X, 0),
-	/* put result into X */
-	BPF_STMT(BPF_MISC| BPF_TAX, 0),
-
-	/*
-	 * Allow management frames through, this also gives us those
-	 * management frames that we sent ourselves with status
-	 */
-	/* load the lower byte of the IEEE 802.11 frame control field */
-	BPF_STMT(BPF_LD  | BPF_B | BPF_IND, 0),
-	/* mask off frame type and version */
-	BPF_STMT(BPF_ALU | BPF_AND | BPF_K, 0xF),
-	/* accept frame if it's both 0, fall through otherwise */
-	BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0, PASS, 0),
-
-	/*
-	 * TODO: add a bit to radiotap RX flags that indicates
-	 * that the sending station is not associated, then
-	 * add a filter here that filters on our DA and that flag
-	 * to allow us to deauth frames to that bad station.
-	 *
-	 * For now allow all To DS data frames through.
-	 */
-	/* load the IEEE 802.11 frame control field */
-	BPF_STMT(BPF_LD  | BPF_H | BPF_IND, 0),
-	/* mask off frame type, version and DS status */
-	BPF_STMT(BPF_ALU | BPF_AND | BPF_K, 0x0F03),
-	/* accept frame if version 0, type 2 and To DS, fall through otherwise
-	 */
-	BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0x0801, PASS, 0),
-
-#if 0
-	/*
-	 * drop non-data frames
-	 */
-	/* load the lower byte of the frame control field */
-	BPF_STMT(BPF_LD   | BPF_B | BPF_IND, 0),
-	/* mask off QoS bit */
-	BPF_STMT(BPF_ALU  | BPF_AND | BPF_K, 0x0c),
-	/* drop non-data frames */
-	BPF_JUMP(BPF_JMP  | BPF_JEQ | BPF_K, 8, 0, FAIL),
-#endif
-	/* load the upper byte of the frame control field */
-	BPF_STMT(BPF_LD   | BPF_B | BPF_IND, 1),
-	/* mask off toDS/fromDS */
-	BPF_STMT(BPF_ALU  | BPF_AND | BPF_K, 0x03),
-	/* accept WDS frames */
-	BPF_JUMP(BPF_JMP  | BPF_JEQ | BPF_K, 3, PASS, 0),
-
-	/*
-	 * add header length to index
-	 */
-	/* load the lower byte of the frame control field */
-	BPF_STMT(BPF_LD   | BPF_B | BPF_IND, 0),
-	/* mask off QoS bit */
-	BPF_STMT(BPF_ALU  | BPF_AND | BPF_K, 0x80),
-	/* right shift it by 6 to give 0 or 2 */
-	BPF_STMT(BPF_ALU  | BPF_RSH | BPF_K, 6),
-	/* add data frame header length */
-	BPF_STMT(BPF_ALU  | BPF_ADD | BPF_K, 24),
-	/* add index, was start of 802.11 header */
-	BPF_STMT(BPF_ALU  | BPF_ADD | BPF_X, 0),
-	/* move to index, now start of LL header */
-	BPF_STMT(BPF_MISC | BPF_TAX, 0),
-
-	/*
-	 * Accept empty data frames, we use those for
-	 * polling activity.
-	 */
-	BPF_STMT(BPF_LD  | BPF_W | BPF_LEN, 0),
-	BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_X, 0, PASS, 0),
-
-	/*
-	 * Accept EAPOL frames
-	 */
-	BPF_STMT(BPF_LD  | BPF_W | BPF_IND, 0),
-	BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0xAAAA0300, 0, FAIL),
-	BPF_STMT(BPF_LD  | BPF_W | BPF_IND, 4),
-	BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0x0000888E, PASS, FAIL),
-
-	/* keep these last two statements or change the code below */
-	/* return 0 == "DROP" */
-	BPF_STMT(BPF_RET | BPF_K, 0),
-	/* return ~0 == "keep all" */
-	BPF_STMT(BPF_RET | BPF_K, ~0),
-};
-
-static struct sock_fprog msock_filter = {
-	.len = ARRAY_SIZE(msock_filter_insns),
-	.filter = msock_filter_insns,
-};
-
-
-static int add_monitor_filter(int s)
-{
-	int idx;
-
-	/* rewrite all PASS/FAIL jump offsets */
-	for (idx = 0; idx < msock_filter.len; idx++) {
-		struct sock_filter *insn = &msock_filter_insns[idx];
-
-		if (BPF_CLASS(insn->code) == BPF_JMP) {
-			if (insn->code == (BPF_JMP|BPF_JA)) {
-				if (insn->k == PASS)
-					insn->k = msock_filter.len - idx - 2;
-				else if (insn->k == FAIL)
-					insn->k = msock_filter.len - idx - 3;
-			}
-
-			if (insn->jt == PASS)
-				insn->jt = msock_filter.len - idx - 2;
-			else if (insn->jt == FAIL)
-				insn->jt = msock_filter.len - idx - 3;
-
-			if (insn->jf == PASS)
-				insn->jf = msock_filter.len - idx - 2;
-			else if (insn->jf == FAIL)
-				insn->jf = msock_filter.len - idx - 3;
-		}
-	}
-
-	if (setsockopt(s, SOL_SOCKET, SO_ATTACH_FILTER,
-		       &msock_filter, sizeof(msock_filter))) {
-		wpa_printf(MSG_ERROR, "nl80211: setsockopt(SO_ATTACH_FILTER) failed: %s",
-			   strerror(errno));
-		return -1;
-	}
-
-	return 0;
-}
-
-
-static void nl80211_remove_monitor_interface(
-	struct wpa_driver_nl80211_data *drv)
-{
-	if (drv->monitor_refcount > 0)
-		drv->monitor_refcount--;
-	wpa_printf(MSG_DEBUG, "nl80211: Remove monitor interface: refcount=%d",
-		   drv->monitor_refcount);
-	if (drv->monitor_refcount > 0)
-		return;
-
-	if (drv->monitor_ifidx >= 0) {
-		nl80211_remove_iface(drv, drv->monitor_ifidx);
-		drv->monitor_ifidx = -1;
-	}
-	if (drv->monitor_sock >= 0) {
-		eloop_unregister_read_sock(drv->monitor_sock);
-		close(drv->monitor_sock);
-		drv->monitor_sock = -1;
-	}
-}
-
-
-static int
-nl80211_create_monitor_interface(struct wpa_driver_nl80211_data *drv)
-{
-	char buf[IFNAMSIZ];
-	struct sockaddr_ll ll;
-	int optval;
-	socklen_t optlen;
-
-	if (drv->monitor_ifidx >= 0) {
-		drv->monitor_refcount++;
-		wpa_printf(MSG_DEBUG, "nl80211: Re-use existing monitor interface: refcount=%d",
-			   drv->monitor_refcount);
-		return 0;
-	}
-
-	if (os_strncmp(drv->first_bss->ifname, "p2p-", 4) == 0) {
-		/*
-		 * P2P interface name is of the format p2p-%s-%d. For monitor
-		 * interface name corresponding to P2P GO, replace "p2p-" with
-		 * "mon-" to retain the same interface name length and to
-		 * indicate that it is a monitor interface.
-		 */
-		snprintf(buf, IFNAMSIZ, "mon-%s", drv->first_bss->ifname + 4);
-	} else {
-		/* Non-P2P interface with AP functionality. */
-		snprintf(buf, IFNAMSIZ, "mon.%s", drv->first_bss->ifname);
-	}
-
-	buf[IFNAMSIZ - 1] = '\0';
-
-	drv->monitor_ifidx =
-		nl80211_create_iface(drv, buf, NL80211_IFTYPE_MONITOR, NULL,
-				     0, NULL, NULL, 0);
-
-	if (drv->monitor_ifidx == -EOPNOTSUPP) {
-		/*
-		 * This is backward compatibility for a few versions of
-		 * the kernel only that didn't advertise the right
-		 * attributes for the only driver that then supported
-		 * AP mode w/o monitor -- ath6kl.
-		 */
-		wpa_printf(MSG_DEBUG, "nl80211: Driver does not support "
-			   "monitor interface type - try to run without it");
-		drv->device_ap_sme = 1;
-	}
-
-	if (drv->monitor_ifidx < 0)
-		return -1;
-
-	if (linux_set_iface_flags(drv->global->ioctl_sock, buf, 1))
-		goto error;
-
-	memset(&ll, 0, sizeof(ll));
-	ll.sll_family = AF_PACKET;
-	ll.sll_ifindex = drv->monitor_ifidx;
-	drv->monitor_sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
-	if (drv->monitor_sock < 0) {
-		wpa_printf(MSG_ERROR, "nl80211: socket[PF_PACKET,SOCK_RAW] failed: %s",
-			   strerror(errno));
-		goto error;
-	}
-
-	if (add_monitor_filter(drv->monitor_sock)) {
-		wpa_printf(MSG_INFO, "Failed to set socket filter for monitor "
-			   "interface; do filtering in user space");
-		/* This works, but will cost in performance. */
-	}
-
-	if (bind(drv->monitor_sock, (struct sockaddr *) &ll, sizeof(ll)) < 0) {
-		wpa_printf(MSG_ERROR, "nl80211: monitor socket bind failed: %s",
-			   strerror(errno));
-		goto error;
-	}
-
-	optlen = sizeof(optval);
-	optval = 20;
-	if (setsockopt
-	    (drv->monitor_sock, SOL_SOCKET, SO_PRIORITY, &optval, optlen)) {
-		wpa_printf(MSG_ERROR, "nl80211: Failed to set socket priority: %s",
-			   strerror(errno));
-		goto error;
-	}
-
-	if (eloop_register_read_sock(drv->monitor_sock, handle_monitor_read,
-				     drv, NULL)) {
-		wpa_printf(MSG_INFO, "nl80211: Could not register monitor read socket");
-		goto error;
-	}
-
-	drv->monitor_refcount++;
-	return 0;
- error:
-	nl80211_remove_monitor_interface(drv);
-	return -1;
-}
-
-
 static int nl80211_setup_ap(struct i802_bss *bss)
 {
 	struct wpa_driver_nl80211_data *drv = bss->drv;
@@ -8643,7 +4253,6 @@
 					    int flags_or, int flags_and)
 {
 	struct i802_bss *bss = priv;
-	struct wpa_driver_nl80211_data *drv = bss->drv;
 	struct nl_msg *msg;
 	struct nlattr *flags;
 	struct nl80211_sta_flag_update upd;
@@ -8653,47 +4262,38 @@
 		   bss->ifname, MAC2STR(addr), total_flags, flags_or, flags_and,
 		   !!(total_flags & WPA_STA_AUTHORIZED));
 
-	msg = nlmsg_alloc();
-	if (!msg)
-		return -ENOMEM;
-
-	nl80211_cmd(drv, msg, 0, NL80211_CMD_SET_STATION);
-
-	NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX,
-		    if_nametoindex(bss->ifname));
-	NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr);
+	if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_SET_STATION)) ||
+	    nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr))
+		goto fail;
 
 	/*
 	 * Backwards compatibility version using NL80211_ATTR_STA_FLAGS. This
 	 * can be removed eventually.
 	 */
 	flags = nla_nest_start(msg, NL80211_ATTR_STA_FLAGS);
-	if (!flags)
-		goto nla_put_failure;
-	if (total_flags & WPA_STA_AUTHORIZED)
-		NLA_PUT_FLAG(msg, NL80211_STA_FLAG_AUTHORIZED);
-
-	if (total_flags & WPA_STA_WMM)
-		NLA_PUT_FLAG(msg, NL80211_STA_FLAG_WME);
-
-	if (total_flags & WPA_STA_SHORT_PREAMBLE)
-		NLA_PUT_FLAG(msg, NL80211_STA_FLAG_SHORT_PREAMBLE);
-
-	if (total_flags & WPA_STA_MFP)
-		NLA_PUT_FLAG(msg, NL80211_STA_FLAG_MFP);
-
-	if (total_flags & WPA_STA_TDLS_PEER)
-		NLA_PUT_FLAG(msg, NL80211_STA_FLAG_TDLS_PEER);
+	if (!flags ||
+	    ((total_flags & WPA_STA_AUTHORIZED) &&
+	     nla_put_flag(msg, NL80211_STA_FLAG_AUTHORIZED)) ||
+	    ((total_flags & WPA_STA_WMM) &&
+	     nla_put_flag(msg, NL80211_STA_FLAG_WME)) ||
+	    ((total_flags & WPA_STA_SHORT_PREAMBLE) &&
+	     nla_put_flag(msg, NL80211_STA_FLAG_SHORT_PREAMBLE)) ||
+	    ((total_flags & WPA_STA_MFP) &&
+	     nla_put_flag(msg, NL80211_STA_FLAG_MFP)) ||
+	    ((total_flags & WPA_STA_TDLS_PEER) &&
+	     nla_put_flag(msg, NL80211_STA_FLAG_TDLS_PEER)))
+		goto fail;
 
 	nla_nest_end(msg, flags);
 
 	os_memset(&upd, 0, sizeof(upd));
 	upd.mask = sta_flags_nl80211(flags_or | ~flags_and);
 	upd.set = sta_flags_nl80211(flags_or);
-	NLA_PUT(msg, NL80211_ATTR_STA_FLAGS2, sizeof(upd), &upd);
+	if (nla_put(msg, NL80211_ATTR_STA_FLAGS2, sizeof(upd), &upd))
+		goto fail;
 
-	return send_and_recv_msgs(drv, msg, NULL, NULL);
- nla_put_failure:
+	return send_and_recv_msgs(bss->drv, msg, NULL, NULL);
+fail:
 	nlmsg_free(msg);
 	return -ENOBUFS;
 }
@@ -8717,7 +4317,8 @@
 		return -1;
 	}
 
-	if (nl80211_set_channel(drv->first_bss, &params->freq, 0)) {
+	if (params->freq.freq &&
+	    nl80211_set_channel(drv->first_bss, &params->freq, 0)) {
 		if (old_mode != nlmode)
 			wpa_driver_nl80211_set_mode(drv->first_bss, old_mode);
 		nl80211_remove_monitor_interface(drv);
@@ -8728,40 +4329,75 @@
 }
 
 
-static int nl80211_leave_ibss(struct wpa_driver_nl80211_data *drv)
+static int nl80211_leave_ibss(struct wpa_driver_nl80211_data *drv,
+			      int reset_mode)
 {
 	struct nl_msg *msg;
-	int ret = -1;
+	int ret;
 
-	msg = nlmsg_alloc();
-	if (!msg)
-		return -1;
-
-	nl80211_cmd(drv, msg, 0, NL80211_CMD_LEAVE_IBSS);
-	NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
+	msg = nl80211_drv_msg(drv, 0, NL80211_CMD_LEAVE_IBSS);
 	ret = send_and_recv_msgs(drv, msg, NULL, NULL);
-	msg = NULL;
 	if (ret) {
 		wpa_printf(MSG_DEBUG, "nl80211: Leave IBSS failed: ret=%d "
 			   "(%s)", ret, strerror(-ret));
-		goto nla_put_failure;
+	} else {
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: Leave IBSS request sent successfully");
 	}
 
-	ret = 0;
-	wpa_printf(MSG_DEBUG, "nl80211: Leave IBSS request sent successfully");
-
-nla_put_failure:
-	if (wpa_driver_nl80211_set_mode(drv->first_bss,
+	if (reset_mode &&
+	    wpa_driver_nl80211_set_mode(drv->first_bss,
 					NL80211_IFTYPE_STATION)) {
 		wpa_printf(MSG_INFO, "nl80211: Failed to set interface into "
 			   "station mode");
 	}
 
-	nlmsg_free(msg);
 	return ret;
 }
 
 
+static int nl80211_ht_vht_overrides(struct nl_msg *msg,
+				    struct wpa_driver_associate_params *params)
+{
+	if (params->disable_ht && nla_put_flag(msg, NL80211_ATTR_DISABLE_HT))
+		return -1;
+
+	if (params->htcaps && params->htcaps_mask) {
+		int sz = sizeof(struct ieee80211_ht_capabilities);
+		wpa_hexdump(MSG_DEBUG, "  * htcaps", params->htcaps, sz);
+		wpa_hexdump(MSG_DEBUG, "  * htcaps_mask",
+			    params->htcaps_mask, sz);
+		if (nla_put(msg, NL80211_ATTR_HT_CAPABILITY, sz,
+			    params->htcaps) ||
+		    nla_put(msg, NL80211_ATTR_HT_CAPABILITY_MASK, sz,
+			    params->htcaps_mask))
+			return -1;
+	}
+
+#ifdef CONFIG_VHT_OVERRIDES
+	if (params->disable_vht) {
+		wpa_printf(MSG_DEBUG, "  * VHT disabled");
+		if (nla_put_flag(msg, NL80211_ATTR_DISABLE_VHT))
+			return -1;
+	}
+
+	if (params->vhtcaps && params->vhtcaps_mask) {
+		int sz = sizeof(struct ieee80211_vht_capabilities);
+		wpa_hexdump(MSG_DEBUG, "  * vhtcaps", params->vhtcaps, sz);
+		wpa_hexdump(MSG_DEBUG, "  * vhtcaps_mask",
+			    params->vhtcaps_mask, sz);
+		if (nla_put(msg, NL80211_ATTR_VHT_CAPABILITY, sz,
+			    params->vhtcaps) ||
+		    nla_put(msg, NL80211_ATTR_VHT_CAPABILITY_MASK, sz,
+			    params->vhtcaps_mask))
+			return -1;
+	}
+#endif /* CONFIG_VHT_OVERRIDES */
+
+	return 0;
+}
+
+
 static int wpa_driver_nl80211_ibss(struct wpa_driver_nl80211_data *drv,
 				   struct wpa_driver_associate_params *params)
 {
@@ -8778,48 +4414,36 @@
 	}
 
 retry:
-	msg = nlmsg_alloc();
-	if (!msg)
-		return -1;
-
-	nl80211_cmd(drv, msg, 0, NL80211_CMD_JOIN_IBSS);
-	NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
-
-	if (params->ssid == NULL || params->ssid_len > sizeof(drv->ssid))
-		goto nla_put_failure;
+	if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_JOIN_IBSS)) ||
+	    params->ssid == NULL || params->ssid_len > sizeof(drv->ssid))
+		goto fail;
 
 	wpa_hexdump_ascii(MSG_DEBUG, "  * SSID",
 			  params->ssid, params->ssid_len);
-	NLA_PUT(msg, NL80211_ATTR_SSID, params->ssid_len,
-		params->ssid);
+	if (nla_put(msg, NL80211_ATTR_SSID, params->ssid_len, params->ssid))
+		goto fail;
 	os_memcpy(drv->ssid, params->ssid, params->ssid_len);
 	drv->ssid_len = params->ssid_len;
 
-	wpa_printf(MSG_DEBUG, "  * freq=%d", params->freq.freq);
-	wpa_printf(MSG_DEBUG, "  * ht_enabled=%d", params->freq.ht_enabled);
-	wpa_printf(MSG_DEBUG, "  * sec_channel_offset=%d",
-		   params->freq.sec_channel_offset);
-	wpa_printf(MSG_DEBUG, "  * vht_enabled=%d", params->freq.vht_enabled);
-	wpa_printf(MSG_DEBUG, "  * center_freq1=%d", params->freq.center_freq1);
-	wpa_printf(MSG_DEBUG, "  * center_freq2=%d", params->freq.center_freq2);
-	wpa_printf(MSG_DEBUG, "  * bandwidth=%d", params->freq.bandwidth);
-	if (nl80211_put_freq_params(msg, &params->freq) < 0)
-		goto nla_put_failure;
-
-	if (params->beacon_int > 0) {
-		wpa_printf(MSG_DEBUG, "  * beacon_int=%d", params->beacon_int);
-		NLA_PUT_U32(msg, NL80211_ATTR_BEACON_INTERVAL,
-			    params->beacon_int);
-	}
+	if (nl80211_put_freq_params(msg, &params->freq) < 0 ||
+	    nl80211_put_beacon_int(msg, params->beacon_int))
+		goto fail;
 
 	ret = nl80211_set_conn_keys(params, msg);
 	if (ret)
-		goto nla_put_failure;
+		goto fail;
 
 	if (params->bssid && params->fixed_bssid) {
 		wpa_printf(MSG_DEBUG, "  * BSSID=" MACSTR,
 			   MAC2STR(params->bssid));
-		NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, params->bssid);
+		if (nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, params->bssid))
+			goto fail;
+	}
+
+	if (params->fixed_freq) {
+		wpa_printf(MSG_DEBUG, "  * fixed_freq");
+		if (nla_put_flag(msg, NL80211_ATTR_FREQ_FIXED))
+			goto fail;
 	}
 
 	if (params->key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X ||
@@ -8827,17 +4451,22 @@
 	    params->key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X_SHA256 ||
 	    params->key_mgmt_suite == WPA_KEY_MGMT_PSK_SHA256) {
 		wpa_printf(MSG_DEBUG, "  * control port");
-		NLA_PUT_FLAG(msg, NL80211_ATTR_CONTROL_PORT);
+		if (nla_put_flag(msg, NL80211_ATTR_CONTROL_PORT))
+			goto fail;
 	}
 
 	if (params->wpa_ie) {
 		wpa_hexdump(MSG_DEBUG,
 			    "  * Extra IEs for Beacon/Probe Response frames",
 			    params->wpa_ie, params->wpa_ie_len);
-		NLA_PUT(msg, NL80211_ATTR_IE, params->wpa_ie_len,
-			params->wpa_ie);
+		if (nla_put(msg, NL80211_ATTR_IE, params->wpa_ie_len,
+			    params->wpa_ie))
+			goto fail;
 	}
 
+	if (nl80211_ht_vht_overrides(msg, params) < 0)
+		return -1;
+
 	ret = send_and_recv_msgs(drv, msg, NULL, NULL);
 	msg = NULL;
 	if (ret) {
@@ -8847,17 +4476,16 @@
 		if (ret == -EALREADY && count == 1) {
 			wpa_printf(MSG_DEBUG, "nl80211: Retry IBSS join after "
 				   "forced leave");
-			nl80211_leave_ibss(drv);
+			nl80211_leave_ibss(drv, 0);
 			nlmsg_free(msg);
 			goto retry;
 		}
-
-		goto nla_put_failure;
+	} else {
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: Join IBSS request sent successfully");
 	}
-	ret = 0;
-	wpa_printf(MSG_DEBUG, "nl80211: Join IBSS request sent successfully");
 
-nla_put_failure:
+fail:
 	nlmsg_free(msg);
 	return ret;
 }
@@ -8867,56 +4495,61 @@
 				  struct wpa_driver_associate_params *params,
 				  struct nl_msg *msg)
 {
-	NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
-
 	if (params->bssid) {
 		wpa_printf(MSG_DEBUG, "  * bssid=" MACSTR,
 			   MAC2STR(params->bssid));
-		NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, params->bssid);
+		if (nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, params->bssid))
+			return -1;
 	}
 
 	if (params->bssid_hint) {
 		wpa_printf(MSG_DEBUG, "  * bssid_hint=" MACSTR,
 			   MAC2STR(params->bssid_hint));
-		NLA_PUT(msg, NL80211_ATTR_MAC_HINT, ETH_ALEN,
-			params->bssid_hint);
+		if (nla_put(msg, NL80211_ATTR_MAC_HINT, ETH_ALEN,
+			    params->bssid_hint))
+			return -1;
 	}
 
 	if (params->freq.freq) {
 		wpa_printf(MSG_DEBUG, "  * freq=%d", params->freq.freq);
-		NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, params->freq.freq);
+		if (nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ,
+				params->freq.freq))
+			return -1;
 		drv->assoc_freq = params->freq.freq;
 	} else
 		drv->assoc_freq = 0;
 
 	if (params->freq_hint) {
 		wpa_printf(MSG_DEBUG, "  * freq_hint=%d", params->freq_hint);
-		NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ_HINT,
-			    params->freq_hint);
+		if (nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ_HINT,
+				params->freq_hint))
+			return -1;
 	}
 
 	if (params->bg_scan_period >= 0) {
 		wpa_printf(MSG_DEBUG, "  * bg scan period=%d",
 			   params->bg_scan_period);
-		NLA_PUT_U16(msg, NL80211_ATTR_BG_SCAN_PERIOD,
-			    params->bg_scan_period);
+		if (nla_put_u16(msg, NL80211_ATTR_BG_SCAN_PERIOD,
+				params->bg_scan_period))
+			return -1;
 	}
 
 	if (params->ssid) {
 		wpa_hexdump_ascii(MSG_DEBUG, "  * SSID",
 				  params->ssid, params->ssid_len);
-		NLA_PUT(msg, NL80211_ATTR_SSID, params->ssid_len,
-			params->ssid);
+		if (nla_put(msg, NL80211_ATTR_SSID, params->ssid_len,
+			    params->ssid))
+			return -1;
 		if (params->ssid_len > sizeof(drv->ssid))
-			goto nla_put_failure;
+			return -1;
 		os_memcpy(drv->ssid, params->ssid, params->ssid_len);
 		drv->ssid_len = params->ssid_len;
 	}
 
 	wpa_hexdump(MSG_DEBUG, "  * IEs", params->wpa_ie, params->wpa_ie_len);
-	if (params->wpa_ie)
-		NLA_PUT(msg, NL80211_ATTR_IE, params->wpa_ie_len,
-			params->wpa_ie);
+	if (params->wpa_ie &&
+	    nla_put(msg, NL80211_ATTR_IE, params->wpa_ie_len, params->wpa_ie))
+		return -1;
 
 	if (params->wpa_proto) {
 		enum nl80211_wpa_versions ver = 0;
@@ -8927,13 +4560,16 @@
 			ver |= NL80211_WPA_VERSION_2;
 
 		wpa_printf(MSG_DEBUG, "  * WPA Versions 0x%x", ver);
-		NLA_PUT_U32(msg, NL80211_ATTR_WPA_VERSIONS, ver);
+		if (nla_put_u32(msg, NL80211_ATTR_WPA_VERSIONS, ver))
+			return -1;
 	}
 
 	if (params->pairwise_suite != WPA_CIPHER_NONE) {
 		u32 cipher = wpa_cipher_to_cipher_suite(params->pairwise_suite);
 		wpa_printf(MSG_DEBUG, "  * pairwise=0x%x", cipher);
-		NLA_PUT_U32(msg, NL80211_ATTR_CIPHER_SUITES_PAIRWISE, cipher);
+		if (nla_put_u32(msg, NL80211_ATTR_CIPHER_SUITES_PAIRWISE,
+				cipher))
+			return -1;
 	}
 
 	if (params->group_suite == WPA_CIPHER_GTK_NOT_USED &&
@@ -8946,7 +4582,8 @@
 	} else if (params->group_suite != WPA_CIPHER_NONE) {
 		u32 cipher = wpa_cipher_to_cipher_suite(params->group_suite);
 		wpa_printf(MSG_DEBUG, "  * group=0x%x", cipher);
-		NLA_PUT_U32(msg, NL80211_ATTR_CIPHER_SUITE_GROUP, cipher);
+		if (nla_put_u32(msg, NL80211_ATTR_CIPHER_SUITE_GROUP, cipher))
+			return -1;
 	}
 
 	if (params->key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X ||
@@ -8956,7 +4593,9 @@
 	    params->key_mgmt_suite == WPA_KEY_MGMT_CCKM ||
 	    params->key_mgmt_suite == WPA_KEY_MGMT_OSEN ||
 	    params->key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X_SHA256 ||
-	    params->key_mgmt_suite == WPA_KEY_MGMT_PSK_SHA256) {
+	    params->key_mgmt_suite == WPA_KEY_MGMT_PSK_SHA256 ||
+	    params->key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X_SUITE_B ||
+	    params->key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X_SUITE_B_192) {
 		int mgmt = WLAN_AKM_SUITE_PSK;
 
 		switch (params->key_mgmt_suite) {
@@ -8981,56 +4620,45 @@
 		case WPA_KEY_MGMT_OSEN:
 			mgmt = WLAN_AKM_SUITE_OSEN;
 			break;
+		case WPA_KEY_MGMT_IEEE8021X_SUITE_B:
+			mgmt = WLAN_AKM_SUITE_8021X_SUITE_B;
+			break;
+		case WPA_KEY_MGMT_IEEE8021X_SUITE_B_192:
+			mgmt = WLAN_AKM_SUITE_8021X_SUITE_B_192;
+			break;
 		case WPA_KEY_MGMT_PSK:
 		default:
 			mgmt = WLAN_AKM_SUITE_PSK;
 			break;
 		}
 		wpa_printf(MSG_DEBUG, "  * akm=0x%x", mgmt);
-		NLA_PUT_U32(msg, NL80211_ATTR_AKM_SUITES, mgmt);
+		if (nla_put_u32(msg, NL80211_ATTR_AKM_SUITES, mgmt))
+			return -1;
 	}
 
-	NLA_PUT_FLAG(msg, NL80211_ATTR_CONTROL_PORT);
+	if (nla_put_flag(msg, NL80211_ATTR_CONTROL_PORT))
+		return -1;
 
-	if (params->mgmt_frame_protection == MGMT_FRAME_PROTECTION_REQUIRED)
-		NLA_PUT_U32(msg, NL80211_ATTR_USE_MFP, NL80211_MFP_REQUIRED);
+	if (params->mgmt_frame_protection == MGMT_FRAME_PROTECTION_REQUIRED &&
+	    nla_put_u32(msg, NL80211_ATTR_USE_MFP, NL80211_MFP_REQUIRED))
+		return -1;
 
-	if (params->disable_ht)
-		NLA_PUT_FLAG(msg, NL80211_ATTR_DISABLE_HT);
-
-	if (params->htcaps && params->htcaps_mask) {
-		int sz = sizeof(struct ieee80211_ht_capabilities);
-		wpa_hexdump(MSG_DEBUG, "  * htcaps", params->htcaps, sz);
-		NLA_PUT(msg, NL80211_ATTR_HT_CAPABILITY, sz, params->htcaps);
-		wpa_hexdump(MSG_DEBUG, "  * htcaps_mask",
-			    params->htcaps_mask, sz);
-		NLA_PUT(msg, NL80211_ATTR_HT_CAPABILITY_MASK, sz,
-			params->htcaps_mask);
+	if (params->rrm_used) {
+		u32 drv_rrm_flags = drv->capa.rrm_flags;
+		if (!(drv_rrm_flags &
+		      WPA_DRIVER_FLAGS_DS_PARAM_SET_IE_IN_PROBES) ||
+		    !(drv_rrm_flags & WPA_DRIVER_FLAGS_QUIET) ||
+		    nla_put_flag(msg, NL80211_ATTR_USE_RRM))
+			return -1;
 	}
 
-#ifdef CONFIG_VHT_OVERRIDES
-	if (params->disable_vht) {
-		wpa_printf(MSG_DEBUG, "  * VHT disabled");
-		NLA_PUT_FLAG(msg, NL80211_ATTR_DISABLE_VHT);
-	}
-
-	if (params->vhtcaps && params->vhtcaps_mask) {
-		int sz = sizeof(struct ieee80211_vht_capabilities);
-		wpa_hexdump(MSG_DEBUG, "  * vhtcaps", params->vhtcaps, sz);
-		NLA_PUT(msg, NL80211_ATTR_VHT_CAPABILITY, sz, params->vhtcaps);
-		wpa_hexdump(MSG_DEBUG, "  * vhtcaps_mask",
-			    params->vhtcaps_mask, sz);
-		NLA_PUT(msg, NL80211_ATTR_VHT_CAPABILITY_MASK, sz,
-			params->vhtcaps_mask);
-	}
-#endif /* CONFIG_VHT_OVERRIDES */
+	if (nl80211_ht_vht_overrides(msg, params) < 0)
+		return -1;
 
 	if (params->p2p)
 		wpa_printf(MSG_DEBUG, "  * P2P group");
 
 	return 0;
-nla_put_failure:
-	return -1;
 }
 
 
@@ -9043,16 +4671,24 @@
 	int ret;
 	int algs;
 
-	msg = nlmsg_alloc();
+	if (params->req_key_mgmt_offload && params->psk &&
+	    (params->key_mgmt_suite == WPA_KEY_MGMT_PSK ||
+	     params->key_mgmt_suite == WPA_KEY_MGMT_PSK_SHA256 ||
+	     params->key_mgmt_suite == WPA_KEY_MGMT_FT_PSK)) {
+		wpa_printf(MSG_DEBUG, "nl80211: Key management set PSK");
+		ret = issue_key_mgmt_set_key(drv, params->psk, 32);
+		if (ret)
+			return ret;
+	}
+
+	wpa_printf(MSG_DEBUG, "nl80211: Connect (ifindex=%d)", drv->ifindex);
+	msg = nl80211_drv_msg(drv, 0, NL80211_CMD_CONNECT);
 	if (!msg)
 		return -1;
 
-	wpa_printf(MSG_DEBUG, "nl80211: Connect (ifindex=%d)", drv->ifindex);
-	nl80211_cmd(drv, msg, 0, NL80211_CMD_CONNECT);
-
 	ret = nl80211_connect_common(drv, params, msg);
 	if (ret)
-		goto nla_put_failure;
+		goto fail;
 
 	algs = 0;
 	if (params->auth_alg & WPA_AUTH_ALG_OPEN)
@@ -9076,27 +4712,28 @@
 	else if (params->auth_alg & WPA_AUTH_ALG_FT)
 		type = NL80211_AUTHTYPE_FT;
 	else
-		goto nla_put_failure;
+		goto fail;
 
 	wpa_printf(MSG_DEBUG, "  * Auth Type %d", type);
-	NLA_PUT_U32(msg, NL80211_ATTR_AUTH_TYPE, type);
+	if (nla_put_u32(msg, NL80211_ATTR_AUTH_TYPE, type))
+		goto fail;
 
 skip_auth_type:
 	ret = nl80211_set_conn_keys(params, msg);
 	if (ret)
-		goto nla_put_failure;
+		goto fail;
 
 	ret = send_and_recv_msgs(drv, msg, NULL, NULL);
 	msg = NULL;
 	if (ret) {
 		wpa_printf(MSG_DEBUG, "nl80211: MLME connect failed: ret=%d "
 			   "(%s)", ret, strerror(-ret));
-		goto nla_put_failure;
+	} else {
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: Connect request send successfully");
 	}
-	ret = 0;
-	wpa_printf(MSG_DEBUG, "nl80211: Connect request send successfully");
 
-nla_put_failure:
+fail:
 	nlmsg_free(msg);
 	return ret;
 
@@ -9139,9 +4776,11 @@
 {
 	struct i802_bss *bss = priv;
 	struct wpa_driver_nl80211_data *drv = bss->drv;
-	int ret;
+	int ret = -1;
 	struct nl_msg *msg;
 
+	nl80211_unmask_11b_rates(bss);
+
 	if (params->mode == IEEE80211_MODE_AP)
 		return wpa_driver_nl80211_ap(drv, params);
 
@@ -9159,23 +4798,22 @@
 
 	nl80211_mark_disconnected(drv);
 
-	msg = nlmsg_alloc();
+	wpa_printf(MSG_DEBUG, "nl80211: Associate (ifindex=%d)",
+		   drv->ifindex);
+	msg = nl80211_drv_msg(drv, 0, NL80211_CMD_ASSOCIATE);
 	if (!msg)
 		return -1;
 
-	wpa_printf(MSG_DEBUG, "nl80211: Associate (ifindex=%d)",
-		   drv->ifindex);
-	nl80211_cmd(drv, msg, 0, NL80211_CMD_ASSOCIATE);
-
 	ret = nl80211_connect_common(drv, params, msg);
 	if (ret)
-		goto nla_put_failure;
+		goto fail;
 
 	if (params->prev_bssid) {
 		wpa_printf(MSG_DEBUG, "  * prev_bssid=" MACSTR,
 			   MAC2STR(params->prev_bssid));
-		NLA_PUT(msg, NL80211_ATTR_PREV_BSSID, ETH_ALEN,
-			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);
@@ -9185,13 +4823,12 @@
 			"nl80211: MLME command failed (assoc): ret=%d (%s)",
 			ret, strerror(-ret));
 		nl80211_dump_scan(drv);
-		goto nla_put_failure;
+	} else {
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: Association request send successfully");
 	}
-	ret = 0;
-	wpa_printf(MSG_DEBUG, "nl80211: Association request send "
-		   "successfully");
 
-nla_put_failure:
+fail:
 	nlmsg_free(msg);
 	return ret;
 }
@@ -9206,20 +4843,15 @@
 	wpa_printf(MSG_DEBUG, "nl80211: Set mode ifindex %d iftype %d (%s)",
 		   ifindex, mode, nl80211_iftype_str(mode));
 
-	msg = nlmsg_alloc();
-	if (!msg)
-		return -ENOMEM;
-
-	nl80211_cmd(drv, msg, 0, NL80211_CMD_SET_INTERFACE);
-	if (nl80211_set_iface_id(msg, drv->first_bss) < 0)
-		goto nla_put_failure;
-	NLA_PUT_U32(msg, NL80211_ATTR_IFTYPE, mode);
+	msg = nl80211_cmd_msg(drv->first_bss, 0, NL80211_CMD_SET_INTERFACE);
+	if (!msg || nla_put_u32(msg, NL80211_ATTR_IFTYPE, mode))
+		goto fail;
 
 	ret = send_and_recv_msgs(drv, msg, NULL, NULL);
 	msg = NULL;
 	if (!ret)
 		return 0;
-nla_put_failure:
+fail:
 	nlmsg_free(msg);
 	wpa_printf(MSG_DEBUG, "nl80211: Failed to set interface %d to mode %d:"
 		   " %d (%s)", ifindex, mode, ret, strerror(-ret));
@@ -9281,7 +4913,7 @@
 		 * on a frequency that the mode is disallowed in.
 		 */
 		if (desired_freq_params) {
-			res = i802_set_freq(bss, desired_freq_params);
+			res = nl80211_set_channel(bss, desired_freq_params, 0);
 			if (res) {
 				wpa_printf(MSG_DEBUG,
 					   "nl80211: Failed to set frequency on interface");
@@ -9322,10 +4954,17 @@
 		return ret;
 	}
 
-	if (is_p2p_net_interface(nlmode))
+	if (is_p2p_net_interface(nlmode)) {
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: Interface %s mode change to P2P - disable 11b rates",
+			   bss->ifname);
 		nl80211_disable_11b_rates(drv, drv->ifindex, 1);
-	else if (drv->disabled_11b_rates)
+	} else if (drv->disabled_11b_rates) {
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: Interface %s mode changed to non-P2P - re-enable 11b rates",
+			   bss->ifname);
 		nl80211_disable_11b_rates(drv, drv->ifindex, 0);
+	}
 
 	if (is_ap_interface(nlmode)) {
 		nl80211_mgmt_unsubscribe(bss, "start AP");
@@ -9339,7 +4978,12 @@
 		nl80211_mgmt_unsubscribe(bss, "mode change");
 	}
 
+	if (is_mesh_interface(nlmode) &&
+	    nl80211_mgmt_subscribe_mesh(bss))
+		return -1;
+
 	if (!bss->in_deinit && !is_ap_interface(nlmode) &&
+	    !is_mesh_interface(nlmode) &&
 	    nl80211_mgmt_subscribe_non_ap(bss) < 0)
 		wpa_printf(MSG_DEBUG, "nl80211: Failed to register Action "
 			   "frame processing - ignore for now");
@@ -9348,37 +4992,8 @@
 }
 
 
-static int dfs_info_handler(struct nl_msg *msg, void *arg)
-{
-	struct nlattr *tb[NL80211_ATTR_MAX + 1];
-	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
-	int *dfs_capability_ptr = arg;
-
-	nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
-		  genlmsg_attrlen(gnlh, 0), NULL);
-
-	if (tb[NL80211_ATTR_VENDOR_DATA]) {
-		struct nlattr *nl_vend = tb[NL80211_ATTR_VENDOR_DATA];
-		struct nlattr *tb_vendor[QCA_WLAN_VENDOR_ATTR_MAX + 1];
-
-		nla_parse(tb_vendor, QCA_WLAN_VENDOR_ATTR_MAX,
-			  nla_data(nl_vend), nla_len(nl_vend), NULL);
-
-		if (tb_vendor[QCA_WLAN_VENDOR_ATTR_DFS]) {
-			u32 val;
-			val = nla_get_u32(tb_vendor[QCA_WLAN_VENDOR_ATTR_DFS]);
-			wpa_printf(MSG_DEBUG, "nl80211: DFS offload capability: %u",
-				   val);
-			*dfs_capability_ptr = val;
-		}
-	}
-
-	return NL_SKIP;
-}
-
-
-static int wpa_driver_nl80211_set_mode(struct i802_bss *bss,
-				       enum nl80211_iftype nlmode)
+int wpa_driver_nl80211_set_mode(struct i802_bss *bss,
+				enum nl80211_iftype nlmode)
 {
 	return wpa_driver_nl80211_set_mode_impl(bss, nlmode, NULL);
 }
@@ -9397,9 +5012,6 @@
 {
 	struct i802_bss *bss = priv;
 	struct wpa_driver_nl80211_data *drv = bss->drv;
-	struct nl_msg *msg;
-	int dfs_capability = 0;
-	int ret = 0;
 
 	if (!drv->has_capability)
 		return -1;
@@ -9410,37 +5022,7 @@
 		capa->extended_capa_len = drv->extended_capa_len;
 	}
 
-	if ((capa->flags & WPA_DRIVER_FLAGS_DEDICATED_P2P_DEVICE) &&
-	    !drv->allow_p2p_device) {
-		wpa_printf(MSG_DEBUG, "nl80211: Do not indicate P2P_DEVICE support (p2p_device=1 driver param not specified)");
-		capa->flags &= ~WPA_DRIVER_FLAGS_DEDICATED_P2P_DEVICE;
-	}
-
-	if (drv->dfs_vendor_cmd_avail == 1) {
-		msg = nlmsg_alloc();
-		if (!msg)
-			return -ENOMEM;
-
-		nl80211_cmd(drv, msg, 0, NL80211_CMD_VENDOR);
-
-		NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
-		NLA_PUT_U32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA);
-		NLA_PUT_U32(msg, NL80211_ATTR_VENDOR_SUBCMD,
-			    QCA_NL80211_VENDOR_SUBCMD_DFS_CAPABILITY);
-
-		ret = send_and_recv_msgs(drv, msg, dfs_info_handler,
-					 &dfs_capability);
-		if (!ret) {
-			if (dfs_capability)
-				capa->flags |= WPA_DRIVER_FLAGS_DFS_OFFLOAD;
-		}
-	}
-
-	return ret;
-
- nla_put_failure:
-	nlmsg_free(msg);
-	return -ENOBUFS;
+	return 0;
 }
 
 
@@ -9464,7 +5046,7 @@
 	struct wpa_driver_nl80211_data *drv = bss->drv;
 	struct nl_msg *msg;
 	struct nl80211_sta_flag_update upd;
-	int ret = -ENOBUFS;
+	int ret;
 
 	if (!drv->associated && is_zero_ether_addr(drv->bssid) && !authorized) {
 		wpa_printf(MSG_DEBUG, "nl80211: Skip set_supp_port(unauthorized) while not associated");
@@ -9474,28 +5056,21 @@
 	wpa_printf(MSG_DEBUG, "nl80211: Set supplicant port %sauthorized for "
 		   MACSTR, authorized ? "" : "un", MAC2STR(drv->bssid));
 
-	msg = nlmsg_alloc();
-	if (!msg)
-		return -ENOMEM;
-
-	nl80211_cmd(drv, msg, 0, NL80211_CMD_SET_STATION);
-
-	NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX,
-		    if_nametoindex(bss->ifname));
-	NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, drv->bssid);
-
 	os_memset(&upd, 0, sizeof(upd));
 	upd.mask = BIT(NL80211_STA_FLAG_AUTHORIZED);
 	if (authorized)
 		upd.set = BIT(NL80211_STA_FLAG_AUTHORIZED);
-	NLA_PUT(msg, NL80211_ATTR_STA_FLAGS2, sizeof(upd), &upd);
+
+	if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_SET_STATION)) ||
+	    nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, drv->bssid) ||
+	    nla_put(msg, NL80211_ATTR_STA_FLAGS2, sizeof(upd), &upd)) {
+		nlmsg_free(msg);
+		return -ENOBUFS;
+	}
 
 	ret = send_and_recv_msgs(drv, msg, NULL, NULL);
-	msg = NULL;
 	if (!ret)
 		return 0;
- nla_put_failure:
-	nlmsg_free(msg);
 	wpa_printf(MSG_DEBUG, "nl80211: Failed to set STA flag: %d (%s)",
 		   ret, strerror(-ret));
 	return ret;
@@ -9546,23 +5121,18 @@
 	struct wpa_driver_nl80211_data *drv = bss->drv;
 	struct nl_msg *msg;
 
-	msg = nlmsg_alloc();
-	if (!msg)
-		return -ENOMEM;
-
-	nl80211_cmd(drv, msg, 0, NL80211_CMD_GET_KEY);
-
-	if (addr)
-		NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr);
-	NLA_PUT_U8(msg, NL80211_ATTR_KEY_IDX, idx);
-	NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(iface));
+	msg = nl80211_ifindex_msg(drv, if_nametoindex(iface), 0,
+				  NL80211_CMD_GET_KEY);
+	if (!msg ||
+	    (addr && nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr)) ||
+	    nla_put_u8(msg, NL80211_ATTR_KEY_IDX, idx)) {
+		nlmsg_free(msg);
+		return -ENOBUFS;
+	}
 
 	memset(seq, 0, 6);
 
 	return send_and_recv_msgs(drv, msg, get_key_handler, seq);
- nla_put_failure:
-	nlmsg_free(msg);
-	return -ENOBUFS;
 }
 
 
@@ -9571,28 +5141,23 @@
 	struct i802_bss *bss = priv;
 	struct wpa_driver_nl80211_data *drv = bss->drv;
 	struct nl_msg *msg;
-	int ret = -ENOBUFS;
+	int ret;
 	u32 val;
 
-	msg = nlmsg_alloc();
-	if (!msg)
-		return -ENOMEM;
-
 	if (rts >= 2347)
 		val = (u32) -1;
 	else
 		val = rts;
 
-	nl80211_cmd(drv, msg, 0, NL80211_CMD_SET_WIPHY);
-	NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
-	NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_RTS_THRESHOLD, val);
+	if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_SET_WIPHY)) ||
+	    nla_put_u32(msg, NL80211_ATTR_WIPHY_RTS_THRESHOLD, val)) {
+		nlmsg_free(msg);
+		return -ENOBUFS;
+	}
 
 	ret = send_and_recv_msgs(drv, msg, NULL, NULL);
-	msg = NULL;
 	if (!ret)
 		return 0;
-nla_put_failure:
-	nlmsg_free(msg);
 	wpa_printf(MSG_DEBUG, "nl80211: Failed to set RTS threshold %d: "
 		   "%d (%s)", rts, ret, strerror(-ret));
 	return ret;
@@ -9604,28 +5169,23 @@
 	struct i802_bss *bss = priv;
 	struct wpa_driver_nl80211_data *drv = bss->drv;
 	struct nl_msg *msg;
-	int ret = -ENOBUFS;
+	int ret;
 	u32 val;
 
-	msg = nlmsg_alloc();
-	if (!msg)
-		return -ENOMEM;
-
 	if (frag >= 2346)
 		val = (u32) -1;
 	else
 		val = frag;
 
-	nl80211_cmd(drv, msg, 0, NL80211_CMD_SET_WIPHY);
-	NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
-	NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FRAG_THRESHOLD, val);
+	if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_SET_WIPHY)) ||
+	    nla_put_u32(msg, NL80211_ATTR_WIPHY_FRAG_THRESHOLD, val)) {
+		nlmsg_free(msg);
+		return -ENOBUFS;
+	}
 
 	ret = send_and_recv_msgs(drv, msg, NULL, NULL);
-	msg = NULL;
 	if (!ret)
 		return 0;
-nla_put_failure:
-	nlmsg_free(msg);
 	wpa_printf(MSG_DEBUG, "nl80211: Failed to set fragmentation threshold "
 		   "%d: %d (%s)", frag, ret, strerror(-ret));
 	return ret;
@@ -9635,33 +5195,22 @@
 static int i802_flush(void *priv)
 {
 	struct i802_bss *bss = priv;
-	struct wpa_driver_nl80211_data *drv = bss->drv;
 	struct nl_msg *msg;
 	int res;
 
-	msg = nlmsg_alloc();
-	if (!msg)
-		return -1;
-
 	wpa_printf(MSG_DEBUG, "nl80211: flush -> DEL_STATION %s (all)",
 		   bss->ifname);
-	nl80211_cmd(drv, msg, 0, NL80211_CMD_DEL_STATION);
 
 	/*
 	 * XXX: FIX! this needs to flush all VLANs too
 	 */
-	NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX,
-		    if_nametoindex(bss->ifname));
-
-	res = send_and_recv_msgs(drv, msg, NULL, NULL);
+	msg = nl80211_bss_msg(bss, 0, NL80211_CMD_DEL_STATION);
+	res = send_and_recv_msgs(bss->drv, msg, NULL, NULL);
 	if (res) {
 		wpa_printf(MSG_DEBUG, "nl80211: Station flush failed: ret=%d "
 			   "(%s)", res, strerror(-res));
 	}
 	return res;
- nla_put_failure:
-	nlmsg_free(msg);
-	return -ENOBUFS;
 }
 
 
@@ -9724,23 +5273,17 @@
 			      struct hostap_sta_driver_data *data,
 			      const u8 *addr)
 {
-	struct wpa_driver_nl80211_data *drv = bss->drv;
 	struct nl_msg *msg;
 
 	os_memset(data, 0, sizeof(*data));
-	msg = nlmsg_alloc();
-	if (!msg)
-		return -ENOMEM;
 
-	nl80211_cmd(drv, msg, 0, NL80211_CMD_GET_STATION);
+	if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_GET_STATION)) ||
+	    nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr)) {
+		nlmsg_free(msg);
+		return -ENOBUFS;
+	}
 
-	NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr);
-	NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(bss->ifname));
-
-	return send_and_recv_msgs(drv, msg, get_sta_handler, data);
- nla_put_failure:
-	nlmsg_free(msg);
-	return -ENOBUFS;
+	return send_and_recv_msgs(bss->drv, msg, get_sta_handler, data);
 }
 
 
@@ -9752,43 +5295,45 @@
 	struct nl_msg *msg;
 	struct nlattr *txq, *params;
 
-	msg = nlmsg_alloc();
+	msg = nl80211_bss_msg(bss, 0, NL80211_CMD_SET_WIPHY);
 	if (!msg)
 		return -1;
 
-	nl80211_cmd(drv, msg, 0, NL80211_CMD_SET_WIPHY);
-
-	NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(bss->ifname));
-
 	txq = nla_nest_start(msg, NL80211_ATTR_WIPHY_TXQ_PARAMS);
 	if (!txq)
-		goto nla_put_failure;
+		goto fail;
 
 	/* We are only sending parameters for a single TXQ at a time */
 	params = nla_nest_start(msg, 1);
 	if (!params)
-		goto nla_put_failure;
+		goto fail;
 
 	switch (queue) {
 	case 0:
-		NLA_PUT_U8(msg, NL80211_TXQ_ATTR_QUEUE, NL80211_TXQ_Q_VO);
+		if (nla_put_u8(msg, NL80211_TXQ_ATTR_QUEUE, NL80211_TXQ_Q_VO))
+			goto fail;
 		break;
 	case 1:
-		NLA_PUT_U8(msg, NL80211_TXQ_ATTR_QUEUE, NL80211_TXQ_Q_VI);
+		if (nla_put_u8(msg, NL80211_TXQ_ATTR_QUEUE, NL80211_TXQ_Q_VI))
+			goto fail;
 		break;
 	case 2:
-		NLA_PUT_U8(msg, NL80211_TXQ_ATTR_QUEUE, NL80211_TXQ_Q_BE);
+		if (nla_put_u8(msg, NL80211_TXQ_ATTR_QUEUE, NL80211_TXQ_Q_BE))
+			goto fail;
 		break;
 	case 3:
-		NLA_PUT_U8(msg, NL80211_TXQ_ATTR_QUEUE, NL80211_TXQ_Q_BK);
+		if (nla_put_u8(msg, NL80211_TXQ_ATTR_QUEUE, NL80211_TXQ_Q_BK))
+			goto fail;
 		break;
 	}
 	/* Burst time is configured in units of 0.1 msec and TXOP parameter in
 	 * 32 usec, so need to convert the value here. */
-	NLA_PUT_U16(msg, NL80211_TXQ_ATTR_TXOP, (burst_time * 100 + 16) / 32);
-	NLA_PUT_U16(msg, NL80211_TXQ_ATTR_CWMIN, cw_min);
-	NLA_PUT_U16(msg, NL80211_TXQ_ATTR_CWMAX, cw_max);
-	NLA_PUT_U8(msg, NL80211_TXQ_ATTR_AIFS, aifs);
+	if (nla_put_u16(msg, NL80211_TXQ_ATTR_TXOP,
+			(burst_time * 100 + 16) / 32) ||
+	    nla_put_u16(msg, NL80211_TXQ_ATTR_CWMIN, cw_min) ||
+	    nla_put_u16(msg, NL80211_TXQ_ATTR_CWMAX, cw_max) ||
+	    nla_put_u8(msg, NL80211_TXQ_ATTR_AIFS, aifs))
+		goto fail;
 
 	nla_nest_end(msg, params);
 
@@ -9797,7 +5342,7 @@
 	if (send_and_recv_msgs(drv, msg, NULL, NULL) == 0)
 		return 0;
 	msg = NULL;
- nla_put_failure:
+fail:
 	nlmsg_free(msg);
 	return -1;
 }
@@ -9808,34 +5353,26 @@
 {
 	struct wpa_driver_nl80211_data *drv = bss->drv;
 	struct nl_msg *msg;
-	int ret = -ENOBUFS;
-
-	msg = nlmsg_alloc();
-	if (!msg)
-		return -ENOMEM;
+	int ret;
 
 	wpa_printf(MSG_DEBUG, "nl80211: %s[%d]: set_sta_vlan(" MACSTR
 		   ", ifname=%s[%d], vlan_id=%d)",
 		   bss->ifname, if_nametoindex(bss->ifname),
 		   MAC2STR(addr), ifname, if_nametoindex(ifname), vlan_id);
-	nl80211_cmd(drv, msg, 0, NL80211_CMD_SET_STATION);
-
-	NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX,
-		    if_nametoindex(bss->ifname));
-	NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr);
-	NLA_PUT_U32(msg, NL80211_ATTR_STA_VLAN,
-		    if_nametoindex(ifname));
+	if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_SET_STATION)) ||
+	    nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr) ||
+	    nla_put_u32(msg, NL80211_ATTR_STA_VLAN, if_nametoindex(ifname))) {
+		nlmsg_free(msg);
+		return -ENOBUFS;
+	}
 
 	ret = send_and_recv_msgs(drv, msg, NULL, NULL);
-	msg = NULL;
 	if (ret < 0) {
 		wpa_printf(MSG_ERROR, "nl80211: NL80211_ATTR_STA_VLAN (addr="
 			   MACSTR " ifname=%s vlan_id=%d) failed: %d (%s)",
 			   MAC2STR(addr), ifname, vlan_id, ret,
 			   strerror(-ret));
 	}
- nla_put_failure:
-	nlmsg_free(msg);
 	return ret;
 }
 
@@ -9847,6 +5384,8 @@
 
 	data.inactive_msec = (unsigned long) -1;
 	ret = i802_read_sta_data(priv, &data, addr);
+	if (ret == -ENOENT)
+		return -ENOENT;
 	if (ret || data.inactive_msec == (unsigned long) -1)
 		return -1;
 	return data.inactive_msec / 1000;
@@ -9869,8 +5408,11 @@
 	struct wpa_driver_nl80211_data *drv = bss->drv;
 	struct ieee80211_mgmt mgmt;
 
+	if (is_mesh_interface(drv->nlmode))
+		return -1;
+
 	if (drv->device_ap_sme)
-		return wpa_driver_nl80211_sta_remove(bss, addr);
+		return wpa_driver_nl80211_sta_remove(bss, addr, 1, reason);
 
 	memset(&mgmt, 0, sizeof(mgmt));
 	mgmt.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
@@ -9893,8 +5435,11 @@
 	struct wpa_driver_nl80211_data *drv = bss->drv;
 	struct ieee80211_mgmt mgmt;
 
+	if (is_mesh_interface(drv->nlmode))
+		return -1;
+
 	if (drv->device_ap_sme)
-		return wpa_driver_nl80211_sta_remove(bss, addr);
+		return wpa_driver_nl80211_sta_remove(bss, addr, 0, reason);
 
 	memset(&mgmt, 0, sizeof(mgmt));
 	mgmt.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
@@ -9922,7 +5467,7 @@
 		if (!drv->if_indices[i])
 			continue;
 		res = os_snprintf(pos, end - pos, " %d", drv->if_indices[i]);
-		if (res < 0 || res >= end - pos)
+		if (os_snprintf_error(end - pos, res))
 			break;
 		pos += res;
 	}
@@ -10071,12 +5616,12 @@
 			     struct i802_bss *bss,
 			     const char *brname, const char *ifname)
 {
-	int ifindex;
+	int br_ifindex;
 	char in_br[IFNAMSIZ];
 
 	os_strlcpy(bss->brname, brname, IFNAMSIZ);
-	ifindex = if_nametoindex(brname);
-	if (ifindex == 0) {
+	br_ifindex = if_nametoindex(brname);
+	if (br_ifindex == 0) {
 		/*
 		 * Bridge was configured, but the bridge device does
 		 * not exist. Try to add it now.
@@ -10088,8 +5633,10 @@
 			return -1;
 		}
 		bss->added_bridge = 1;
-		add_ifidx(drv, if_nametoindex(brname));
+		br_ifindex = if_nametoindex(brname);
+		add_ifidx(drv, br_ifindex);
 	}
+	bss->br_ifindex = br_ifindex;
 
 	if (linux_br_get(in_br, ifname) == 0) {
 		if (os_strcmp(in_br, brname) == 0)
@@ -10133,7 +5680,7 @@
 
 	bss = wpa_driver_nl80211_drv_init(hapd, params->ifname,
 					  params->global_priv, 1,
-					  params->bssid);
+					  params->bssid, params->driver_params);
 	if (bss == NULL)
 		return NULL;
 
@@ -10143,10 +5690,12 @@
 		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);
 	} else {
 		brname[0] = '\0';
 		br_ifindex = 0;
 	}
+	bss->br_ifindex = br_ifindex;
 
 	for (i = 0; i < params->num_bridge; i++) {
 		if (params->bridge[i]) {
@@ -10157,16 +5706,21 @@
 				br_added = 1;
 		}
 	}
-	if (!br_added && br_ifindex &&
-	    (params->num_bridge == 0 || !params->bridge[0]))
-		add_ifidx(drv, br_ifindex);
 
 	/* start listening for EAPOL on the default AP interface */
 	add_ifidx(drv, drv->ifindex);
 
-	if (params->num_bridge && params->bridge[0] &&
-	    i802_check_bridge(drv, bss, params->bridge[0], params->ifname) < 0)
-		goto failed;
+	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)
+			br_added = 1;
+	}
+
+	if (!br_added && br_ifindex &&
+	    (params->num_bridge == 0 || !params->bridge[0]))
+		add_ifidx(drv, br_ifindex);
 
 #ifdef CONFIG_LIBNL3_ROUTE
 	if (bss->added_if_into_bridge) {
@@ -10236,13 +5790,13 @@
 		return NL80211_IFTYPE_P2P_GO;
 	case WPA_IF_P2P_DEVICE:
 		return NL80211_IFTYPE_P2P_DEVICE;
+	case WPA_IF_MESH:
+		return NL80211_IFTYPE_MESH_POINT;
 	}
 	return -1;
 }
 
 
-#ifdef CONFIG_P2P
-
 static int nl80211_addr_in_use(struct nl80211_global *global, const u8 *addr)
 {
 	struct wpa_driver_nl80211_data *drv;
@@ -10255,8 +5809,7 @@
 }
 
 
-static int nl80211_p2p_interface_addr(struct wpa_driver_nl80211_data *drv,
-				      u8 *new_addr)
+static int nl80211_vif_addr(struct wpa_driver_nl80211_data *drv, u8 *new_addr)
 {
 	unsigned int idx;
 
@@ -10273,14 +5826,12 @@
 	if (idx == 64)
 		return -1;
 
-	wpa_printf(MSG_DEBUG, "nl80211: Assigned new P2P Interface Address "
+	wpa_printf(MSG_DEBUG, "nl80211: Assigned new virtual interface address "
 		   MACSTR, MAC2STR(new_addr));
 
 	return 0;
 }
 
-#endif /* CONFIG_P2P */
-
 
 struct wdev_info {
 	u64 wdev_id;
@@ -10356,21 +5907,21 @@
 	}
 
 	if (!addr) {
-		if (drv->nlmode == NL80211_IFTYPE_P2P_DEVICE)
+		if (nlmode == NL80211_IFTYPE_P2P_DEVICE)
 			os_memcpy(if_addr, bss->addr, ETH_ALEN);
 		else if (linux_get_ifhwaddr(drv->global->ioctl_sock,
-					    bss->ifname, if_addr) < 0) {
+					    ifname, if_addr) < 0) {
 			if (added)
 				nl80211_remove_iface(drv, ifidx);
 			return -1;
 		}
 	}
 
-#ifdef CONFIG_P2P
 	if (!addr &&
 	    (type == WPA_IF_P2P_CLIENT || type == WPA_IF_P2P_GROUP ||
-	     type == WPA_IF_P2P_GO)) {
-		/* Enforce unique P2P Interface Address */
+	     type == WPA_IF_P2P_GO || type == WPA_IF_MESH ||
+	     type == WPA_IF_STATION)) {
+		/* Enforce unique address */
 		u8 new_addr[ETH_ALEN];
 
 		if (linux_get_ifhwaddr(drv->global->ioctl_sock, ifname,
@@ -10381,8 +5932,8 @@
 		}
 		if (nl80211_addr_in_use(drv->global, new_addr)) {
 			wpa_printf(MSG_DEBUG, "nl80211: Allocate new address "
-				   "for P2P group interface");
-			if (nl80211_p2p_interface_addr(drv, new_addr) < 0) {
+				   "for interface %s type %d", ifname, type);
+			if (nl80211_vif_addr(drv, new_addr) < 0) {
 				if (added)
 					nl80211_remove_iface(drv, ifidx);
 				return -1;
@@ -10396,7 +5947,6 @@
 		}
 		os_memcpy(if_addr, new_addr, ETH_ALEN);
 	}
-#endif /* CONFIG_P2P */
 
 	if (type == WPA_IF_AP_BSS) {
 		struct i802_bss *new_bss = os_zalloc(sizeof(*new_bss));
@@ -10560,31 +6110,21 @@
 	u64 cookie;
 	int ret = -1;
 
-	msg = nlmsg_alloc();
-	if (!msg)
-		return -1;
-
 	wpa_printf(MSG_MSGDUMP, "nl80211: CMD_FRAME freq=%u wait=%u no_cck=%d "
 		   "no_ack=%d offchanok=%d",
 		   freq, wait, no_cck, no_ack, offchanok);
 	wpa_hexdump(MSG_MSGDUMP, "CMD_FRAME", buf, buf_len);
-	nl80211_cmd(drv, msg, 0, NL80211_CMD_FRAME);
 
-	if (nl80211_set_iface_id(msg, bss) < 0)
-		goto nla_put_failure;
-	if (freq)
-		NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, freq);
-	if (wait)
-		NLA_PUT_U32(msg, NL80211_ATTR_DURATION, wait);
-	if (offchanok && ((drv->capa.flags & WPA_DRIVER_FLAGS_OFFCHANNEL_TX) ||
-			  drv->test_use_roc_tx))
-		NLA_PUT_FLAG(msg, NL80211_ATTR_OFFCHANNEL_TX_OK);
-	if (no_cck)
-		NLA_PUT_FLAG(msg, NL80211_ATTR_TX_NO_CCK_RATE);
-	if (no_ack)
-		NLA_PUT_FLAG(msg, NL80211_ATTR_DONT_WAIT_FOR_ACK);
-
-	NLA_PUT(msg, NL80211_ATTR_FRAME, buf_len, buf);
+	if (!(msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_FRAME)) ||
+	    (freq && nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ, freq)) ||
+	    (wait && nla_put_u32(msg, NL80211_ATTR_DURATION, wait)) ||
+	    (offchanok && ((drv->capa.flags & WPA_DRIVER_FLAGS_OFFCHANNEL_TX) ||
+			   drv->test_use_roc_tx) &&
+	     nla_put_flag(msg, NL80211_ATTR_OFFCHANNEL_TX_OK)) ||
+	    (no_cck && nla_put_flag(msg, NL80211_ATTR_TX_NO_CCK_RATE)) ||
+	    (no_ack && nla_put_flag(msg, NL80211_ATTR_DONT_WAIT_FOR_ACK)) ||
+	    nla_put(msg, NL80211_ATTR_FRAME, buf_len, buf))
+		goto fail;
 
 	cookie = 0;
 	ret = send_and_recv_msgs(drv, msg, cookie_handler, &cookie);
@@ -10593,16 +6133,16 @@
 		wpa_printf(MSG_DEBUG, "nl80211: Frame command failed: ret=%d "
 			   "(%s) (freq=%u wait=%u)", ret, strerror(-ret),
 			   freq, wait);
-		goto nla_put_failure;
+	} else {
+		wpa_printf(MSG_MSGDUMP, "nl80211: Frame TX command accepted%s; "
+			   "cookie 0x%llx", no_ack ? " (no ACK)" : "",
+			   (long long unsigned int) cookie);
+
+		if (cookie_out)
+			*cookie_out = no_ack ? (u64) -1 : cookie;
 	}
-	wpa_printf(MSG_MSGDUMP, "nl80211: Frame TX command accepted%s; "
-		   "cookie 0x%llx", no_ack ? " (no ACK)" : "",
-		   (long long unsigned int) cookie);
 
-	if (cookie_out)
-		*cookie_out = no_ack ? (u64) -1 : cookie;
-
-nla_put_failure:
+fail:
 	nlmsg_free(msg);
 	return ret;
 }
@@ -10661,26 +6201,18 @@
 	struct nl_msg *msg;
 	int ret;
 
-	msg = nlmsg_alloc();
-	if (!msg)
-		return;
-
 	wpa_printf(MSG_DEBUG, "nl80211: Cancel TX frame wait: cookie=0x%llx",
 		   (long long unsigned int) drv->send_action_cookie);
-	nl80211_cmd(drv, msg, 0, NL80211_CMD_FRAME_WAIT_CANCEL);
-
-	if (nl80211_set_iface_id(msg, bss) < 0)
-		goto nla_put_failure;
-	NLA_PUT_U64(msg, NL80211_ATTR_COOKIE, drv->send_action_cookie);
+	if (!(msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_FRAME_WAIT_CANCEL)) ||
+	    nla_put_u64(msg, NL80211_ATTR_COOKIE, drv->send_action_cookie)) {
+		nlmsg_free(msg);
+		return;
+	}
 
 	ret = send_and_recv_msgs(drv, msg, NULL, NULL);
-	msg = NULL;
 	if (ret)
 		wpa_printf(MSG_DEBUG, "nl80211: wait cancel failed: ret=%d "
 			   "(%s)", ret, strerror(-ret));
-
- nla_put_failure:
-	nlmsg_free(msg);
 }
 
 
@@ -10693,21 +6225,15 @@
 	int ret;
 	u64 cookie;
 
-	msg = nlmsg_alloc();
-	if (!msg)
+	if (!(msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_REMAIN_ON_CHANNEL)) ||
+	    nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ, freq) ||
+	    nla_put_u32(msg, NL80211_ATTR_DURATION, duration)) {
+		nlmsg_free(msg);
 		return -1;
-
-	nl80211_cmd(drv, msg, 0, NL80211_CMD_REMAIN_ON_CHANNEL);
-
-	if (nl80211_set_iface_id(msg, bss) < 0)
-		goto nla_put_failure;
-
-	NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, freq);
-	NLA_PUT_U32(msg, NL80211_ATTR_DURATION, duration);
+	}
 
 	cookie = 0;
 	ret = send_and_recv_msgs(drv, msg, cookie_handler, &cookie);
-	msg = NULL;
 	if (ret == 0) {
 		wpa_printf(MSG_DEBUG, "nl80211: Remain-on-channel cookie "
 			   "0x%llx for freq=%u MHz duration=%u",
@@ -10719,8 +6245,6 @@
 	wpa_printf(MSG_DEBUG, "nl80211: Failed to request remain-on-channel "
 		   "(freq=%d duration=%u): %d (%s)",
 		   freq, duration, ret, strerror(-ret));
-nla_put_failure:
-	nlmsg_free(msg);
 	return -1;
 }
 
@@ -10742,25 +6266,18 @@
 		   "0x%llx",
 		   (long long unsigned int) drv->remain_on_chan_cookie);
 
-	msg = nlmsg_alloc();
-	if (!msg)
+	msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL);
+	if (!msg ||
+	    nla_put_u64(msg, NL80211_ATTR_COOKIE, drv->remain_on_chan_cookie)) {
+		nlmsg_free(msg);
 		return -1;
-
-	nl80211_cmd(drv, msg, 0, NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL);
-
-	if (nl80211_set_iface_id(msg, bss) < 0)
-		goto nla_put_failure;
-
-	NLA_PUT_U64(msg, NL80211_ATTR_COOKIE, drv->remain_on_chan_cookie);
+	}
 
 	ret = send_and_recv_msgs(drv, msg, NULL, NULL);
-	msg = NULL;
 	if (ret == 0)
 		return 0;
 	wpa_printf(MSG_DEBUG, "nl80211: Failed to cancel remain-on-channel: "
 		   "%d (%s)", ret, strerror(-ret));
-nla_put_failure:
-	nlmsg_free(msg);
 	return -1;
 }
 
@@ -10825,16 +6342,19 @@
 	struct nlattr *bands, *band;
 	int ret;
 
-	msg = nlmsg_alloc();
+	wpa_printf(MSG_DEBUG,
+		   "nl80211: NL80211_CMD_SET_TX_BITRATE_MASK (ifindex=%d %s)",
+		   ifindex, disabled ? "NL80211_TXRATE_LEGACY=OFDM-only" :
+		   "no NL80211_TXRATE_LEGACY constraint");
+
+	msg = nl80211_ifindex_msg(drv, ifindex, 0,
+				  NL80211_CMD_SET_TX_BITRATE_MASK);
 	if (!msg)
 		return -1;
 
-	nl80211_cmd(drv, msg, 0, NL80211_CMD_SET_TX_BITRATE_MASK);
-	NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, ifindex);
-
 	bands = nla_nest_start(msg, NL80211_ATTR_TX_RATES);
 	if (!bands)
-		goto nla_put_failure;
+		goto fail;
 
 	/*
 	 * Disable 2 GHz rates 1, 2, 5.5, 11 Mbps by masking out everything
@@ -10842,18 +6362,15 @@
 	 * rates. All 5 GHz rates are left enabled.
 	 */
 	band = nla_nest_start(msg, NL80211_BAND_2GHZ);
-	if (!band)
-		goto nla_put_failure;
-	if (disabled) {
-		NLA_PUT(msg, NL80211_TXRATE_LEGACY, 8,
-			"\x0c\x12\x18\x24\x30\x48\x60\x6c");
-	}
+	if (!band ||
+	    (disabled && nla_put(msg, NL80211_TXRATE_LEGACY, 8,
+				 "\x0c\x12\x18\x24\x30\x48\x60\x6c")))
+		goto fail;
 	nla_nest_end(msg, band);
 
 	nla_nest_end(msg, bands);
 
 	ret = send_and_recv_msgs(drv, msg, NULL, NULL);
-	msg = NULL;
 	if (ret) {
 		wpa_printf(MSG_DEBUG, "nl80211: Set TX rates failed: ret=%d "
 			   "(%s)", ret, strerror(-ret));
@@ -10862,7 +6379,7 @@
 
 	return ret;
 
-nla_put_failure:
+fail:
 	nlmsg_free(msg);
 	return -1;
 }
@@ -10875,6 +6392,7 @@
 	if (!is_ap_interface(drv->nlmode))
 		return -1;
 	wpa_driver_nl80211_del_beacon(drv);
+	bss->beacon_set = 0;
 
 	/*
 	 * If the P2P GO interface was dynamically added, then it is
@@ -10926,86 +6444,26 @@
 }
 
 
-static int nl80211_send_ft_action(void *priv, u8 action, const u8 *target_ap,
-				  const u8 *ies, size_t ies_len)
-{
-	struct i802_bss *bss = priv;
-	struct wpa_driver_nl80211_data *drv = bss->drv;
-	int ret;
-	u8 *data, *pos;
-	size_t data_len;
-	const u8 *own_addr = bss->addr;
-
-	if (action != 1) {
-		wpa_printf(MSG_ERROR, "nl80211: Unsupported send_ft_action "
-			   "action %d", action);
-		return -1;
-	}
-
-	/*
-	 * Action frame payload:
-	 * Category[1] = 6 (Fast BSS Transition)
-	 * Action[1] = 1 (Fast BSS Transition Request)
-	 * STA Address
-	 * Target AP Address
-	 * FT IEs
-	 */
-
-	data_len = 2 + 2 * ETH_ALEN + ies_len;
-	data = os_malloc(data_len);
-	if (data == NULL)
-		return -1;
-	pos = data;
-	*pos++ = 0x06; /* FT Action category */
-	*pos++ = action;
-	os_memcpy(pos, own_addr, ETH_ALEN);
-	pos += ETH_ALEN;
-	os_memcpy(pos, target_ap, ETH_ALEN);
-	pos += ETH_ALEN;
-	os_memcpy(pos, ies, ies_len);
-
-	ret = wpa_driver_nl80211_send_action(bss, drv->assoc_freq, 0,
-					     drv->bssid, own_addr, drv->bssid,
-					     data, data_len, 0);
-	os_free(data);
-
-	return ret;
-}
-
-
 static int nl80211_signal_monitor(void *priv, int threshold, int hysteresis)
 {
 	struct i802_bss *bss = priv;
 	struct wpa_driver_nl80211_data *drv = bss->drv;
 	struct nl_msg *msg;
 	struct nlattr *cqm;
-	int ret = -1;
 
 	wpa_printf(MSG_DEBUG, "nl80211: Signal monitor threshold=%d "
 		   "hysteresis=%d", threshold, hysteresis);
 
-	msg = nlmsg_alloc();
-	if (!msg)
+	if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_SET_CQM)) ||
+	    !(cqm = nla_nest_start(msg, NL80211_ATTR_CQM)) ||
+	    nla_put_u32(msg, NL80211_ATTR_CQM_RSSI_THOLD, threshold) ||
+	    nla_put_u32(msg, NL80211_ATTR_CQM_RSSI_HYST, hysteresis)) {
+		nlmsg_free(msg);
 		return -1;
-
-	nl80211_cmd(drv, msg, 0, NL80211_CMD_SET_CQM);
-
-	NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, bss->ifindex);
-
-	cqm = nla_nest_start(msg, NL80211_ATTR_CQM);
-	if (cqm == NULL)
-		goto nla_put_failure;
-
-	NLA_PUT_U32(msg, NL80211_ATTR_CQM_RSSI_THOLD, threshold);
-	NLA_PUT_U32(msg, NL80211_ATTR_CQM_RSSI_HYST, hysteresis);
+	}
 	nla_nest_end(msg, cqm);
 
-	ret = send_and_recv_msgs(drv, msg, NULL, NULL);
-	msg = NULL;
-
-nla_put_failure:
-	nlmsg_free(msg);
-	return ret;
+	return send_and_recv_msgs(drv, msg, NULL, NULL);
 }
 
 
@@ -11042,18 +6500,8 @@
 {
 	struct nl_msg *msg;
 
-	msg = nlmsg_alloc();
-	if (!msg)
-		return -ENOMEM;
-
-	nl80211_cmd(drv, msg, 0, NL80211_CMD_GET_INTERFACE);
-	NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
-
+	msg = nl80211_drv_msg(drv, 0, NL80211_CMD_GET_INTERFACE);
 	return send_and_recv_msgs(drv, msg, get_channel_width, sig);
-
-nla_put_failure:
-	nlmsg_free(msg);
-	return -ENOBUFS;
 }
 
 
@@ -11076,47 +6524,6 @@
 }
 
 
-static int wpa_driver_nl80211_shared_freq(void *priv)
-{
-	struct i802_bss *bss = priv;
-	struct wpa_driver_nl80211_data *drv = bss->drv;
-	struct wpa_driver_nl80211_data *driver;
-	int freq = 0;
-
-	/*
-	 * If the same PHY is in connected state with some other interface,
-	 * then retrieve the assoc freq.
-	 */
-	wpa_printf(MSG_DEBUG, "nl80211: Get shared freq for PHY %s",
-		   drv->phyname);
-
-	dl_list_for_each(driver, &drv->global->interfaces,
-			 struct wpa_driver_nl80211_data, list) {
-		if (drv == driver ||
-		    os_strcmp(drv->phyname, driver->phyname) != 0 ||
-		    !driver->associated)
-			continue;
-
-		wpa_printf(MSG_DEBUG, "nl80211: Found a match for PHY %s - %s "
-			   MACSTR,
-			   driver->phyname, driver->first_bss->ifname,
-			   MAC2STR(driver->first_bss->addr));
-		if (is_ap_interface(driver->nlmode))
-			freq = driver->first_bss->freq;
-		else
-			freq = nl80211_get_assoc_freq(driver);
-		wpa_printf(MSG_DEBUG, "nl80211: Shared freq for PHY %s: %d",
-			   drv->phyname, freq);
-	}
-
-	if (!freq)
-		wpa_printf(MSG_DEBUG, "nl80211: No shared interface for "
-			   "PHY (%s) in associated state", drv->phyname);
-
-	return freq;
-}
-
-
 static int nl80211_send_frame(void *priv, const u8 *data, size_t data_len,
 			      int encrypt)
 {
@@ -11142,12 +6549,6 @@
 		drv->capa.flags |= WPA_DRIVER_FLAGS_P2P_CONCURRENT;
 		drv->capa.flags |= WPA_DRIVER_FLAGS_P2P_MGMT_AND_NON_P2P;
 	}
-
-	if (os_strstr(param, "p2p_device=1")) {
-		struct i802_bss *bss = priv;
-		struct wpa_driver_nl80211_data *drv = bss->drv;
-		drv->allow_p2p_device = 1;
-	}
 #endif /* CONFIG_P2P */
 
 	if (os_strstr(param, "use_monitor=1")) {
@@ -11258,22 +6659,14 @@
 {
 	struct nl_msg *msg;
 
-	msg = nlmsg_alloc();
-	if (!msg)
-		return -ENOMEM;
-
-	nl80211_cmd(bss->drv, msg, 0, cmd);
-
-	NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(bss->ifname));
-	if (pmkid)
-		NLA_PUT(msg, NL80211_ATTR_PMKID, 16, pmkid);
-	if (bssid)
-		NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, bssid);
+	if (!(msg = nl80211_bss_msg(bss, 0, cmd)) ||
+	    (pmkid && nla_put(msg, NL80211_ATTR_PMKID, 16, pmkid)) ||
+	    (bssid && nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, bssid))) {
+		nlmsg_free(msg);
+		return -ENOBUFS;
+	}
 
 	return send_and_recv_msgs(bss->drv, msg, NULL, NULL);
- nla_put_failure:
-	nlmsg_free(msg);
-	return -ENOBUFS;
 }
 
 
@@ -11444,7 +6837,7 @@
 	struct i802_bss *bss = priv;
 	struct wpa_driver_nl80211_data *drv = bss->drv;
 	struct nl_msg *msg;
-	int err = -ENOBUFS;
+	int err;
 	union wpa_event_data data;
 	struct survey_results *survey_results;
 
@@ -11453,13 +6846,9 @@
 
 	dl_list_init(&survey_results->survey_list);
 
-	msg = nlmsg_alloc();
+	msg = nl80211_drv_msg(drv, NLM_F_DUMP, NL80211_CMD_GET_SURVEY);
 	if (!msg)
-		goto nla_put_failure;
-
-	nl80211_cmd(drv, msg, NLM_F_DUMP, NL80211_CMD_GET_SURVEY);
-
-	NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
+		return -ENOBUFS;
 
 	if (freq)
 		data.survey_results.freq_filter = freq;
@@ -11470,51 +6859,49 @@
 					 survey_results);
 	} while (err > 0);
 
-	if (err) {
+	if (err)
 		wpa_printf(MSG_ERROR, "nl80211: Failed to process survey data");
-		goto out_clean;
-	}
+	else
+		wpa_supplicant_event(drv->ctx, EVENT_SURVEY, &data);
 
-	wpa_supplicant_event(drv->ctx, EVENT_SURVEY, &data);
-
-out_clean:
 	clean_survey_results(survey_results);
-nla_put_failure:
 	return err;
 }
 
 
-static void nl80211_set_rekey_info(void *priv, const u8 *kek, const u8 *kck,
+static void nl80211_set_rekey_info(void *priv, const u8 *kek, size_t kek_len,
+				   const u8 *kck, size_t kck_len,
 				   const u8 *replay_ctr)
 {
 	struct i802_bss *bss = priv;
 	struct wpa_driver_nl80211_data *drv = bss->drv;
 	struct nlattr *replay_nested;
 	struct nl_msg *msg;
+	int ret;
 
-	msg = nlmsg_alloc();
-	if (!msg)
+	if (!drv->set_rekey_offload)
 		return;
 
-	nl80211_cmd(drv, msg, 0, NL80211_CMD_SET_REKEY_OFFLOAD);
-
-	NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, bss->ifindex);
-
-	replay_nested = nla_nest_start(msg, NL80211_ATTR_REKEY_DATA);
-	if (!replay_nested)
-		goto nla_put_failure;
-
-	NLA_PUT(msg, NL80211_REKEY_DATA_KEK, NL80211_KEK_LEN, kek);
-	NLA_PUT(msg, NL80211_REKEY_DATA_KCK, NL80211_KCK_LEN, kck);
-	NLA_PUT(msg, NL80211_REKEY_DATA_REPLAY_CTR, NL80211_REPLAY_CTR_LEN,
-		replay_ctr);
+	wpa_printf(MSG_DEBUG, "nl80211: Set rekey offload");
+	if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_SET_REKEY_OFFLOAD)) ||
+	    !(replay_nested = nla_nest_start(msg, NL80211_ATTR_REKEY_DATA)) ||
+	    nla_put(msg, NL80211_REKEY_DATA_KEK, kek_len, kek) ||
+	    nla_put(msg, NL80211_REKEY_DATA_KCK, kck_len, kck) ||
+	    nla_put(msg, NL80211_REKEY_DATA_REPLAY_CTR, NL80211_REPLAY_CTR_LEN,
+		    replay_ctr)) {
+		nl80211_nlmsg_clear(msg);
+		nlmsg_free(msg);
+		return;
+	}
 
 	nla_nest_end(msg, replay_nested);
 
-	send_and_recv_msgs(drv, msg, NULL, NULL);
-	return;
- nla_put_failure:
-	nlmsg_free(msg);
+	ret = send_and_recv_msgs(drv, msg, NULL, (void *) -1);
+	if (ret == -EOPNOTSUPP) {
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: Driver does not support rekey offload");
+		drv->set_rekey_offload = 0;
+	}
 }
 
 
@@ -11562,25 +6949,25 @@
 	struct i802_bss *bss = priv;
 	struct wpa_driver_nl80211_data *drv = bss->drv;
 	struct nl_msg *msg;
+	int ret;
 
 	if (!drv->poll_command_supported) {
 		nl80211_send_null_frame(bss, own_addr, addr, qos);
 		return;
 	}
 
-	msg = nlmsg_alloc();
-	if (!msg)
+	if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_PROBE_CLIENT)) ||
+	    nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr)) {
+		nlmsg_free(msg);
 		return;
+	}
 
-	nl80211_cmd(drv, msg, 0, NL80211_CMD_PROBE_CLIENT);
-
-	NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, bss->ifindex);
-	NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr);
-
-	send_and_recv_msgs(drv, msg, NULL, NULL);
-	return;
- nla_put_failure:
-	nlmsg_free(msg);
+	ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+	if (ret < 0) {
+		wpa_printf(MSG_DEBUG, "nl80211: Client probe request for "
+			   MACSTR " failed: ret=%d (%s)",
+			   MAC2STR(addr), ret, strerror(-ret));
+	}
 }
 
 
@@ -11588,18 +6975,13 @@
 {
 	struct nl_msg *msg;
 
-	msg = nlmsg_alloc();
-	if (!msg)
-		return -ENOMEM;
-
-	nl80211_cmd(bss->drv, msg, 0, NL80211_CMD_SET_POWER_SAVE);
-	NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, bss->ifindex);
-	NLA_PUT_U32(msg, NL80211_ATTR_PS_STATE,
-		    enabled ? NL80211_PS_ENABLED : NL80211_PS_DISABLED);
+	if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_SET_POWER_SAVE)) ||
+	    nla_put_u32(msg, NL80211_ATTR_PS_STATE,
+			enabled ? NL80211_PS_ENABLED : NL80211_PS_DISABLED)) {
+		nlmsg_free(msg);
+		return -ENOBUFS;
+	}
 	return send_and_recv_msgs(bss->drv, msg, NULL, NULL);
-nla_put_failure:
-	nlmsg_free(msg);
-	return -ENOBUFS;
 }
 
 
@@ -11646,24 +7028,17 @@
 		return -1;
 	}
 
-	msg = nlmsg_alloc();
-	if (!msg)
+	if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_RADAR_DETECT)) ||
+	    nl80211_put_freq_params(msg, freq) < 0) {
+		nlmsg_free(msg);
 		return -1;
-
-	nl80211_cmd(bss->drv, msg, 0, NL80211_CMD_RADAR_DETECT);
-	NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
-
-	if (nl80211_put_freq_params(msg, freq) < 0)
-		goto nla_put_failure;
+	}
 
 	ret = send_and_recv_msgs(drv, msg, NULL, NULL);
-	msg = NULL;
 	if (ret == 0)
 		return 0;
 	wpa_printf(MSG_DEBUG, "nl80211: Failed to start radar detection: "
 		   "%d (%s)", ret, strerror(-ret));
-nla_put_failure:
-	nlmsg_free(msg);
 	return -1;
 }
 
@@ -11684,16 +7059,12 @@
 	if (!dst)
 		return -EINVAL;
 
-	msg = nlmsg_alloc();
-	if (!msg)
-		return -ENOMEM;
-
-	nl80211_cmd(drv, msg, 0, NL80211_CMD_TDLS_MGMT);
-	NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
-	NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, dst);
-	NLA_PUT_U8(msg, NL80211_ATTR_TDLS_ACTION, action_code);
-	NLA_PUT_U8(msg, NL80211_ATTR_TDLS_DIALOG_TOKEN, dialog_token);
-	NLA_PUT_U16(msg, NL80211_ATTR_STATUS_CODE, status_code);
+	if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_TDLS_MGMT)) ||
+	    nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, dst) ||
+	    nla_put_u8(msg, NL80211_ATTR_TDLS_ACTION, action_code) ||
+	    nla_put_u8(msg, NL80211_ATTR_TDLS_DIALOG_TOKEN, dialog_token) ||
+	    nla_put_u16(msg, NL80211_ATTR_STATUS_CODE, status_code))
+		goto fail;
 	if (peer_capab) {
 		/*
 		 * The internal enum tdls_peer_capability definition is
@@ -11701,15 +7072,18 @@
 		 * nl80211_tdls_peer_capability, so no conversion is needed
 		 * here.
 		 */
-		NLA_PUT_U32(msg, NL80211_ATTR_TDLS_PEER_CAPABILITY, peer_capab);
+		if (nla_put_u32(msg, NL80211_ATTR_TDLS_PEER_CAPABILITY,
+				peer_capab))
+			goto fail;
 	}
-	if (initiator)
-		NLA_PUT_FLAG(msg, NL80211_ATTR_TDLS_INITIATOR);
-	NLA_PUT(msg, NL80211_ATTR_IE, len, buf);
+	if ((initiator &&
+	     nla_put_flag(msg, NL80211_ATTR_TDLS_INITIATOR)) ||
+	    nla_put(msg, NL80211_ATTR_IE, len, buf))
+		goto fail;
 
 	return send_and_recv_msgs(drv, msg, NULL, NULL);
 
-nla_put_failure:
+fail:
 	nlmsg_free(msg);
 	return -ENOBUFS;
 }
@@ -11749,158 +7123,75 @@
 		return -EINVAL;
 	}
 
-	msg = nlmsg_alloc();
-	if (!msg)
-		return -ENOMEM;
-
-	nl80211_cmd(drv, msg, 0, NL80211_CMD_TDLS_OPER);
-	NLA_PUT_U8(msg, NL80211_ATTR_TDLS_OPERATION, nl80211_oper);
-	NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
-	NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, peer);
+	if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_TDLS_OPER)) ||
+	    nla_put_u8(msg, NL80211_ATTR_TDLS_OPERATION, nl80211_oper) ||
+	    nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, peer)) {
+		nlmsg_free(msg);
+		return -ENOBUFS;
+	}
 
 	return send_and_recv_msgs(drv, msg, NULL, NULL);
+}
 
-nla_put_failure:
-	nlmsg_free(msg);
-	return -ENOBUFS;
+
+static int
+nl80211_tdls_enable_channel_switch(void *priv, const u8 *addr, u8 oper_class,
+				   const struct hostapd_freq_params *params)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	struct nl_msg *msg;
+	int ret = -ENOBUFS;
+
+	if (!(drv->capa.flags & WPA_DRIVER_FLAGS_TDLS_SUPPORT) ||
+	    !(drv->capa.flags & WPA_DRIVER_FLAGS_TDLS_CHANNEL_SWITCH))
+		return -EOPNOTSUPP;
+
+	wpa_printf(MSG_DEBUG, "nl80211: Enable TDLS channel switch " MACSTR
+		   " oper_class=%u freq=%u",
+		   MAC2STR(addr), oper_class, params->freq);
+	msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_TDLS_CHANNEL_SWITCH);
+	if (!msg ||
+	    nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr) ||
+	    nla_put_u8(msg, NL80211_ATTR_OPER_CLASS, oper_class) ||
+	    (ret = nl80211_put_freq_params(msg, params))) {
+		nlmsg_free(msg);
+		wpa_printf(MSG_DEBUG, "nl80211: Could not build TDLS chan switch");
+		return ret;
+	}
+
+	return send_and_recv_msgs(drv, msg, NULL, NULL);
+}
+
+
+static int
+nl80211_tdls_disable_channel_switch(void *priv, const u8 *addr)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	struct nl_msg *msg;
+
+	if (!(drv->capa.flags & WPA_DRIVER_FLAGS_TDLS_SUPPORT) ||
+	    !(drv->capa.flags & WPA_DRIVER_FLAGS_TDLS_CHANNEL_SWITCH))
+		return -EOPNOTSUPP;
+
+	wpa_printf(MSG_DEBUG, "nl80211: Disable TDLS channel switch " MACSTR,
+		   MAC2STR(addr));
+	msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_TDLS_CANCEL_CHANNEL_SWITCH);
+	if (!msg ||
+	    nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr)) {
+		nlmsg_free(msg);
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: Could not build TDLS cancel chan switch");
+		return -ENOBUFS;
+	}
+
+	return send_and_recv_msgs(drv, msg, NULL, NULL);
 }
 
 #endif /* CONFIG TDLS */
 
 
-#ifdef ANDROID
-
-typedef struct android_wifi_priv_cmd {
-	char *buf;
-	int used_len;
-	int total_len;
-} android_wifi_priv_cmd;
-
-static int drv_errors = 0;
-
-static void wpa_driver_send_hang_msg(struct wpa_driver_nl80211_data *drv)
-{
-	drv_errors++;
-	if (drv_errors > DRV_NUMBER_SEQUENTIAL_ERRORS) {
-		drv_errors = 0;
-		wpa_msg(drv->ctx, MSG_INFO, WPA_EVENT_DRIVER_STATE "HANGED");
-	}
-}
-
-
-static int android_priv_cmd(struct i802_bss *bss, const char *cmd)
-{
-	struct wpa_driver_nl80211_data *drv = bss->drv;
-	struct ifreq ifr;
-	android_wifi_priv_cmd priv_cmd;
-	char buf[MAX_DRV_CMD_SIZE];
-	int ret;
-
-	os_memset(&ifr, 0, sizeof(ifr));
-	os_memset(&priv_cmd, 0, sizeof(priv_cmd));
-	os_strlcpy(ifr.ifr_name, bss->ifname, IFNAMSIZ);
-
-	os_memset(buf, 0, sizeof(buf));
-	os_strlcpy(buf, cmd, sizeof(buf));
-
-	priv_cmd.buf = buf;
-	priv_cmd.used_len = sizeof(buf);
-	priv_cmd.total_len = sizeof(buf);
-	ifr.ifr_data = &priv_cmd;
-
-	ret = ioctl(drv->global->ioctl_sock, SIOCDEVPRIVATE + 1, &ifr);
-	if (ret < 0) {
-		wpa_printf(MSG_ERROR, "%s: failed to issue private commands",
-			   __func__);
-		wpa_driver_send_hang_msg(drv);
-		return ret;
-	}
-
-	drv_errors = 0;
-	return 0;
-}
-
-
-static int android_pno_start(struct i802_bss *bss,
-			     struct wpa_driver_scan_params *params)
-{
-	struct wpa_driver_nl80211_data *drv = bss->drv;
-	struct ifreq ifr;
-	android_wifi_priv_cmd priv_cmd;
-	int ret = 0, i = 0, bp;
-	char buf[WEXT_PNO_MAX_COMMAND_SIZE];
-
-	bp = WEXT_PNOSETUP_HEADER_SIZE;
-	os_memcpy(buf, WEXT_PNOSETUP_HEADER, bp);
-	buf[bp++] = WEXT_PNO_TLV_PREFIX;
-	buf[bp++] = WEXT_PNO_TLV_VERSION;
-	buf[bp++] = WEXT_PNO_TLV_SUBVERSION;
-	buf[bp++] = WEXT_PNO_TLV_RESERVED;
-
-	while (i < WEXT_PNO_AMOUNT && (size_t) i < params->num_ssids) {
-		/* Check that there is enough space needed for 1 more SSID, the
-		 * other sections and null termination */
-		if ((bp + WEXT_PNO_SSID_HEADER_SIZE + MAX_SSID_LEN +
-		     WEXT_PNO_NONSSID_SECTIONS_SIZE + 1) >= (int) sizeof(buf))
-			break;
-		wpa_hexdump_ascii(MSG_DEBUG, "For PNO Scan",
-				  params->ssids[i].ssid,
-				  params->ssids[i].ssid_len);
-		buf[bp++] = WEXT_PNO_SSID_SECTION;
-		buf[bp++] = params->ssids[i].ssid_len;
-		os_memcpy(&buf[bp], params->ssids[i].ssid,
-			  params->ssids[i].ssid_len);
-		bp += params->ssids[i].ssid_len;
-		i++;
-	}
-
-	buf[bp++] = WEXT_PNO_SCAN_INTERVAL_SECTION;
-	os_snprintf(&buf[bp], WEXT_PNO_SCAN_INTERVAL_LENGTH + 1, "%x",
-		    WEXT_PNO_SCAN_INTERVAL);
-	bp += WEXT_PNO_SCAN_INTERVAL_LENGTH;
-
-	buf[bp++] = WEXT_PNO_REPEAT_SECTION;
-	os_snprintf(&buf[bp], WEXT_PNO_REPEAT_LENGTH + 1, "%x",
-		    WEXT_PNO_REPEAT);
-	bp += WEXT_PNO_REPEAT_LENGTH;
-
-	buf[bp++] = WEXT_PNO_MAX_REPEAT_SECTION;
-	os_snprintf(&buf[bp], WEXT_PNO_MAX_REPEAT_LENGTH + 1, "%x",
-		    WEXT_PNO_MAX_REPEAT);
-	bp += WEXT_PNO_MAX_REPEAT_LENGTH + 1;
-
-	memset(&ifr, 0, sizeof(ifr));
-	memset(&priv_cmd, 0, sizeof(priv_cmd));
-	os_strlcpy(ifr.ifr_name, bss->ifname, IFNAMSIZ);
-
-	priv_cmd.buf = buf;
-	priv_cmd.used_len = bp;
-	priv_cmd.total_len = bp;
-	ifr.ifr_data = &priv_cmd;
-
-	ret = ioctl(drv->global->ioctl_sock, SIOCDEVPRIVATE + 1, &ifr);
-
-	if (ret < 0) {
-		wpa_printf(MSG_ERROR, "ioctl[SIOCSIWPRIV] (pnosetup): %d",
-			   ret);
-		wpa_driver_send_hang_msg(drv);
-		return ret;
-	}
-
-	drv_errors = 0;
-
-	return android_priv_cmd(bss, "PNOFORCE 1");
-}
-
-
-static int android_pno_stop(struct i802_bss *bss)
-{
-	return android_priv_cmd(bss, "PNOFORCE 0");
-}
-
-#endif /* ANDROID */
-
-
 static int driver_nl80211_set_key(const char *ifname, void *priv,
 				  enum wpa_alg alg, const u8 *addr,
 				  int key_idx, int set_tx,
@@ -11953,18 +7244,19 @@
 
 
 static int driver_nl80211_send_mlme(void *priv, const u8 *data,
-				    size_t data_len, int noack)
+				    size_t data_len, int noack,
+				    unsigned int freq)
 {
 	struct i802_bss *bss = priv;
 	return wpa_driver_nl80211_send_mlme(bss, data, data_len, noack,
-					    0, 0, 0, 0);
+					    freq, 0, 0, 0);
 }
 
 
 static int driver_nl80211_sta_remove(void *priv, const u8 *addr)
 {
 	struct i802_bss *bss = priv;
-	return wpa_driver_nl80211_sta_remove(bss, addr);
+	return wpa_driver_nl80211_sta_remove(bss, addr, -1, 0);
 }
 
 
@@ -12014,15 +7306,13 @@
 	struct wpa_driver_nl80211_data *drv = bss->drv;
 	u16 mdid = WPA_GET_LE16(md);
 
-	msg = nlmsg_alloc();
-	if (!msg)
-		return -ENOMEM;
-
 	wpa_printf(MSG_DEBUG, "nl80211: Updating FT IEs");
-	nl80211_cmd(drv, msg, 0, NL80211_CMD_UPDATE_FT_IES);
-	NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
-	NLA_PUT(msg, NL80211_ATTR_IE, ies_len, ies);
-	NLA_PUT_U16(msg, NL80211_ATTR_MDID, mdid);
+	if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_UPDATE_FT_IES)) ||
+	    nla_put(msg, NL80211_ATTR_IE, ies_len, ies) ||
+	    nla_put_u16(msg, NL80211_ATTR_MDID, mdid)) {
+		nlmsg_free(msg);
+		return -ENOBUFS;
+	}
 
 	ret = send_and_recv_msgs(drv, msg, NULL, NULL);
 	if (ret) {
@@ -12031,10 +7321,6 @@
 	}
 
 	return ret;
-
-nla_put_failure:
-	nlmsg_free(msg);
-	return -ENOBUFS;
 }
 
 
@@ -12103,14 +7389,14 @@
 			  bss->added_bridge ? "added_bridge=1\n" : "",
 			  bss->in_deinit ? "in_deinit=1\n" : "",
 			  bss->if_dynamic ? "if_dynamic=1\n" : "");
-	if (res < 0 || res >= end - pos)
+	if (os_snprintf_error(end - pos, res))
 		return pos - buf;
 	pos += res;
 
 	if (bss->wdev_id_set) {
 		res = os_snprintf(pos, end - pos, "wdev_id=%llu\n",
 				  (unsigned long long) bss->wdev_id);
-		if (res < 0 || res >= end - pos)
+		if (os_snprintf_error(end - pos, res))
 			return pos - buf;
 		pos += res;
 	}
@@ -12132,7 +7418,7 @@
 			  "monitor_refcount=%d\n"
 			  "last_mgmt_freq=%u\n"
 			  "eapol_tx_sock=%d\n"
-			  "%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
+			  "%s%s%s%s%s%s%s%s%s%s%s%s%s",
 			  drv->phyname,
 			  MAC2STR(drv->perm_addr),
 			  drv->ifindex,
@@ -12168,9 +7454,8 @@
 			  drv->ignore_next_local_disconnect ?
 			  "ignore_next_local_disconnect=1\n" : "",
 			  drv->ignore_next_local_deauth ?
-			  "ignore_next_local_deauth=1\n" : "",
-			  drv->allow_p2p_device ? "allow_p2p_device=1\n" : "");
-	if (res < 0 || res >= end - pos)
+			  "ignore_next_local_deauth=1\n" : "");
+	if (os_snprintf_error(end - pos, res))
 		return pos - buf;
 	pos += res;
 
@@ -12179,7 +7464,8 @@
 				  "capa.key_mgmt=0x%x\n"
 				  "capa.enc=0x%x\n"
 				  "capa.auth=0x%x\n"
-				  "capa.flags=0x%x\n"
+				  "capa.flags=0x%llx\n"
+				  "capa.rrm_flags=0x%x\n"
 				  "capa.max_scan_ssids=%d\n"
 				  "capa.max_sched_scan_ssids=%d\n"
 				  "capa.sched_scan_supported=%d\n"
@@ -12188,11 +7474,14 @@
 				  "capa.max_stations=%u\n"
 				  "capa.probe_resp_offloads=0x%x\n"
 				  "capa.max_acl_mac_addrs=%u\n"
-				  "capa.num_multichan_concurrent=%u\n",
+				  "capa.num_multichan_concurrent=%u\n"
+				  "capa.mac_addr_rand_sched_scan_supported=%d\n"
+				  "capa.mac_addr_rand_scan_supported=%d\n",
 				  drv->capa.key_mgmt,
 				  drv->capa.enc,
 				  drv->capa.auth,
-				  drv->capa.flags,
+				  (unsigned long long) drv->capa.flags,
+				  drv->capa.rrm_flags,
 				  drv->capa.max_scan_ssids,
 				  drv->capa.max_sched_scan_ssids,
 				  drv->capa.sched_scan_supported,
@@ -12201,8 +7490,10 @@
 				  drv->capa.max_stations,
 				  drv->capa.probe_resp_offloads,
 				  drv->capa.max_acl_mac_addrs,
-				  drv->capa.num_multichan_concurrent);
-		if (res < 0 || res >= end - pos)
+				  drv->capa.num_multichan_concurrent,
+				  drv->capa.mac_addr_rand_sched_scan_supported,
+				  drv->capa.mac_addr_rand_scan_supported);
+		if (os_snprintf_error(end - pos, res))
 			return pos - buf;
 		pos += res;
 	}
@@ -12213,35 +7504,27 @@
 
 static int set_beacon_data(struct nl_msg *msg, struct beacon_data *settings)
 {
-	if (settings->head)
-		NLA_PUT(msg, NL80211_ATTR_BEACON_HEAD,
-			settings->head_len, settings->head);
-
-	if (settings->tail)
-		NLA_PUT(msg, NL80211_ATTR_BEACON_TAIL,
-			settings->tail_len, settings->tail);
-
-	if (settings->beacon_ies)
-		NLA_PUT(msg, NL80211_ATTR_IE,
-			settings->beacon_ies_len, settings->beacon_ies);
-
-	if (settings->proberesp_ies)
-		NLA_PUT(msg, NL80211_ATTR_IE_PROBE_RESP,
-			settings->proberesp_ies_len, settings->proberesp_ies);
-
-	if (settings->assocresp_ies)
-		NLA_PUT(msg,
-			NL80211_ATTR_IE_ASSOC_RESP,
-			settings->assocresp_ies_len, settings->assocresp_ies);
-
-	if (settings->probe_resp)
-		NLA_PUT(msg, NL80211_ATTR_PROBE_RESP,
-			settings->probe_resp_len, settings->probe_resp);
+	if ((settings->head &&
+	     nla_put(msg, NL80211_ATTR_BEACON_HEAD,
+		     settings->head_len, settings->head)) ||
+	    (settings->tail &&
+	     nla_put(msg, NL80211_ATTR_BEACON_TAIL,
+		     settings->tail_len, settings->tail)) ||
+	    (settings->beacon_ies &&
+	     nla_put(msg, NL80211_ATTR_IE,
+		     settings->beacon_ies_len, settings->beacon_ies)) ||
+	    (settings->proberesp_ies &&
+	     nla_put(msg, NL80211_ATTR_IE_PROBE_RESP,
+		     settings->proberesp_ies_len, settings->proberesp_ies)) ||
+	    (settings->assocresp_ies &&
+	     nla_put(msg, NL80211_ATTR_IE_ASSOC_RESP,
+		     settings->assocresp_ies_len, settings->assocresp_ies)) ||
+	    (settings->probe_resp &&
+	     nla_put(msg, NL80211_ATTR_PROBE_RESP,
+		     settings->probe_resp_len, settings->probe_resp)))
+		return -ENOBUFS;
 
 	return 0;
-
-nla_put_failure:
-	return -ENOBUFS;
 }
 
 
@@ -12283,20 +7566,14 @@
 	      settings->cs_count)))
 		return -EINVAL;
 
-	msg = nlmsg_alloc();
-	if (!msg)
-		return -ENOMEM;
-
-	nl80211_cmd(drv, msg, 0, NL80211_CMD_CHANNEL_SWITCH);
-	NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, bss->ifindex);
-	NLA_PUT_U32(msg, NL80211_ATTR_CH_SWITCH_COUNT, settings->cs_count);
-	ret = nl80211_put_freq_params(msg, &settings->freq_params);
-	if (ret)
+	if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_CHANNEL_SWITCH)) ||
+	    nla_put_u32(msg, NL80211_ATTR_CH_SWITCH_COUNT,
+			settings->cs_count) ||
+	    (ret = nl80211_put_freq_params(msg, &settings->freq_params)) ||
+	    (settings->block_tx &&
+	     nla_put_flag(msg, NL80211_ATTR_CH_SWITCH_BLOCK_TX)))
 		goto error;
 
-	if (settings->block_tx)
-		NLA_PUT_FLAG(msg, NL80211_ATTR_CH_SWITCH_BLOCK_TX);
-
 	/* beacon_after params */
 	ret = set_beacon_data(msg, &settings->beacon_after);
 	if (ret)
@@ -12305,18 +7582,18 @@
 	/* beacon_csa params */
 	beacon_csa = nla_nest_start(msg, NL80211_ATTR_CSA_IES);
 	if (!beacon_csa)
-		goto nla_put_failure;
+		goto fail;
 
 	ret = set_beacon_data(msg, &settings->beacon_csa);
 	if (ret)
 		goto error;
 
-	NLA_PUT_U16(msg, NL80211_ATTR_CSA_C_OFF_BEACON,
-		    settings->counter_offset_beacon);
-
-	if (settings->beacon_csa.probe_resp)
-		NLA_PUT_U16(msg, NL80211_ATTR_CSA_C_OFF_PRESP,
-			    settings->counter_offset_presp);
+	if (nla_put_u16(msg, NL80211_ATTR_CSA_C_OFF_BEACON,
+			settings->counter_offset_beacon) ||
+	    (settings->beacon_csa.probe_resp &&
+	     nla_put_u16(msg, NL80211_ATTR_CSA_C_OFF_PRESP,
+			 settings->counter_offset_presp)))
+		goto fail;
 
 	nla_nest_end(msg, beacon_csa);
 	ret = send_and_recv_msgs(drv, msg, NULL, NULL);
@@ -12326,7 +7603,7 @@
 	}
 	return ret;
 
-nla_put_failure:
+fail:
 	ret = -ENOBUFS;
 error:
 	nlmsg_free(msg);
@@ -12335,6 +7612,66 @@
 }
 
 
+static int nl80211_add_ts(void *priv, u8 tsid, const u8 *addr,
+			  u8 user_priority, u16 admitted_time)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	struct nl_msg *msg;
+	int ret;
+
+	wpa_printf(MSG_DEBUG,
+		   "nl80211: add_ts request: tsid=%u admitted_time=%u up=%d",
+		   tsid, admitted_time, user_priority);
+
+	if (!is_sta_interface(drv->nlmode))
+		return -ENOTSUP;
+
+	msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_ADD_TX_TS);
+	if (!msg ||
+	    nla_put_u8(msg, NL80211_ATTR_TSID, tsid) ||
+	    nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr) ||
+	    nla_put_u8(msg, NL80211_ATTR_USER_PRIO, user_priority) ||
+	    nla_put_u16(msg, NL80211_ATTR_ADMITTED_TIME, admitted_time)) {
+		nlmsg_free(msg);
+		return -ENOBUFS;
+	}
+
+	ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+	if (ret)
+		wpa_printf(MSG_DEBUG, "nl80211: add_ts failed err=%d (%s)",
+			   ret, strerror(-ret));
+	return ret;
+}
+
+
+static int nl80211_del_ts(void *priv, u8 tsid, const u8 *addr)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	struct nl_msg *msg;
+	int ret;
+
+	wpa_printf(MSG_DEBUG, "nl80211: del_ts request: tsid=%u", tsid);
+
+	if (!is_sta_interface(drv->nlmode))
+		return -ENOTSUP;
+
+	if (!(msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_DEL_TX_TS)) ||
+	    nla_put_u8(msg, NL80211_ATTR_TSID, tsid) ||
+	    nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr)) {
+		nlmsg_free(msg);
+		return -ENOBUFS;
+	}
+
+	ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+	if (ret)
+		wpa_printf(MSG_DEBUG, "nl80211: del_ts failed err=%d (%s)",
+			   ret, strerror(-ret));
+	return ret;
+}
+
+
 #ifdef CONFIG_TESTING_OPTIONS
 static int cmd_reply_handler(struct nl_msg *msg, void *arg)
 {
@@ -12397,16 +7734,16 @@
 	struct nl_msg *msg;
 	int ret;
 
-	msg = nlmsg_alloc();
-	if (!msg)
-		return -ENOMEM;
-
 #ifdef CONFIG_TESTING_OPTIONS
 	if (vendor_id == 0xffffffff) {
+		msg = nlmsg_alloc();
+		if (!msg)
+			return -ENOMEM;
+
 		nl80211_cmd(drv, msg, 0, subcmd);
 		if (nlmsg_append(msg, (void *) data, data_len, NLMSG_ALIGNTO) <
 		    0)
-			goto nla_put_failure;
+			goto fail;
 		ret = send_and_recv_msgs(drv, msg, cmd_reply_handler, buf);
 		if (ret)
 			wpa_printf(MSG_DEBUG, "nl80211: command failed err=%d",
@@ -12415,13 +7752,12 @@
 	}
 #endif /* CONFIG_TESTING_OPTIONS */
 
-	nl80211_cmd(drv, msg, 0, NL80211_CMD_VENDOR);
-	if (nl80211_set_iface_id(msg, bss) < 0)
-		goto nla_put_failure;
-	NLA_PUT_U32(msg, NL80211_ATTR_VENDOR_ID, vendor_id);
-	NLA_PUT_U32(msg, NL80211_ATTR_VENDOR_SUBCMD, subcmd);
-	if (data)
-		NLA_PUT(msg, NL80211_ATTR_VENDOR_DATA, data_len, data);
+	if (!(msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_VENDOR)) ||
+	    nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, vendor_id) ||
+	    nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, subcmd) ||
+	    (data &&
+	     nla_put(msg, NL80211_ATTR_VENDOR_DATA, data_len, data)))
+		goto fail;
 
 	ret = send_and_recv_msgs(drv, msg, vendor_reply_handler, buf);
 	if (ret)
@@ -12429,7 +7765,7 @@
 			   ret);
 	return ret;
 
-nla_put_failure:
+fail:
 	nlmsg_free(msg);
 	return -ENOBUFS;
 }
@@ -12443,26 +7779,20 @@
 	struct nl_msg *msg;
 	int ret;
 
-	msg = nlmsg_alloc();
-	if (!msg)
-		return -ENOMEM;
-
 	wpa_hexdump(MSG_DEBUG, "nl80211: Setting QoS Map",
 		    qos_map_set, qos_map_set_len);
 
-	nl80211_cmd(drv, msg, 0, NL80211_CMD_SET_QOS_MAP);
-	NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
-	NLA_PUT(msg, NL80211_ATTR_QOS_MAP, qos_map_set_len, qos_map_set);
+	if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_SET_QOS_MAP)) ||
+	    nla_put(msg, NL80211_ATTR_QOS_MAP, qos_map_set_len, qos_map_set)) {
+		nlmsg_free(msg);
+		return -ENOBUFS;
+	}
 
 	ret = send_and_recv_msgs(drv, msg, NULL, NULL);
 	if (ret)
 		wpa_printf(MSG_DEBUG, "nl80211: Setting QoS Map failed");
 
 	return ret;
-
-nla_put_failure:
-	nlmsg_free(msg);
-	return -ENOBUFS;
 }
 
 
@@ -12475,33 +7805,28 @@
 	struct nlattr *wowlan_triggers;
 	int ret;
 
-	msg = nlmsg_alloc();
-	if (!msg)
-		return -ENOMEM;
-
 	wpa_printf(MSG_DEBUG, "nl80211: Setting wowlan");
 
-	nl80211_cmd(drv, msg, 0, NL80211_CMD_SET_WOWLAN);
-	NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
-
-	wowlan_triggers = nla_nest_start(msg, NL80211_ATTR_WOWLAN_TRIGGERS);
-	if (!wowlan_triggers)
-		goto nla_put_failure;
-
-	if (triggers->any)
-		NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_ANY);
-	if (triggers->disconnect)
-		NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_DISCONNECT);
-	if (triggers->magic_pkt)
-		NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_MAGIC_PKT);
-	if (triggers->gtk_rekey_failure)
-		NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE);
-	if (triggers->eap_identity_req)
-		NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST);
-	if (triggers->four_way_handshake)
-		NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE);
-	if (triggers->rfkill_release)
-		NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_RFKILL_RELEASE);
+	if (!(msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_SET_WOWLAN)) ||
+	    !(wowlan_triggers = nla_nest_start(msg,
+					       NL80211_ATTR_WOWLAN_TRIGGERS)) ||
+	    (triggers->any &&
+	     nla_put_flag(msg, NL80211_WOWLAN_TRIG_ANY)) ||
+	    (triggers->disconnect &&
+	     nla_put_flag(msg, NL80211_WOWLAN_TRIG_DISCONNECT)) ||
+	    (triggers->magic_pkt &&
+	     nla_put_flag(msg, NL80211_WOWLAN_TRIG_MAGIC_PKT)) ||
+	    (triggers->gtk_rekey_failure &&
+	     nla_put_flag(msg, NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE)) ||
+	    (triggers->eap_identity_req &&
+	     nla_put_flag(msg, NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST)) ||
+	    (triggers->four_way_handshake &&
+	     nla_put_flag(msg, NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE)) ||
+	    (triggers->rfkill_release &&
+	     nla_put_flag(msg, NL80211_WOWLAN_TRIG_RFKILL_RELEASE))) {
+		nlmsg_free(msg);
+		return -ENOBUFS;
+	}
 
 	nla_nest_end(msg, wowlan_triggers);
 
@@ -12510,10 +7835,6 @@
 		wpa_printf(MSG_DEBUG, "nl80211: Setting wowlan failed");
 
 	return ret;
-
-nla_put_failure:
-	nlmsg_free(msg);
-	return -ENOBUFS;
 }
 
 
@@ -12532,32 +7853,22 @@
 		return -1;
 	}
 
-	msg = nlmsg_alloc();
-	if (!msg)
-		return -ENOMEM;
-
-	nl80211_cmd(drv, msg, 0, NL80211_CMD_VENDOR);
-
-	NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
-	NLA_PUT_U32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA);
-	NLA_PUT_U32(msg, NL80211_ATTR_VENDOR_SUBCMD,
-		    QCA_NL80211_VENDOR_SUBCMD_ROAMING);
-
-	params = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA);
-	if (!params)
-		goto nla_put_failure;
-	NLA_PUT_U32(msg, QCA_WLAN_VENDOR_ATTR_ROAMING_POLICY,
-		    allowed ? QCA_ROAMING_ALLOWED_WITHIN_ESS :
-		    QCA_ROAMING_NOT_ALLOWED);
-	if (bssid)
-		NLA_PUT(msg, QCA_WLAN_VENDOR_ATTR_MAC_ADDR, ETH_ALEN, bssid);
+	if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
+	    nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
+	    nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
+			QCA_NL80211_VENDOR_SUBCMD_ROAMING) ||
+	    !(params = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA)) ||
+	    nla_put_u32(msg, QCA_WLAN_VENDOR_ATTR_ROAMING_POLICY,
+			allowed ? QCA_ROAMING_ALLOWED_WITHIN_ESS :
+			QCA_ROAMING_NOT_ALLOWED) ||
+	    (bssid &&
+	     nla_put(msg, QCA_WLAN_VENDOR_ATTR_MAC_ADDR, ETH_ALEN, bssid))) {
+		nlmsg_free(msg);
+		return -1;
+	}
 	nla_nest_end(msg, params);
 
 	return send_and_recv_msgs(drv, msg, NULL, NULL);
-
- nla_put_failure:
-	nlmsg_free(msg);
-	return -1;
 }
 
 
@@ -12601,6 +7912,517 @@
 }
 
 
+#ifdef CONFIG_MESH
+
+static int wpa_driver_nl80211_init_mesh(void *priv)
+{
+	if (wpa_driver_nl80211_set_mode(priv, NL80211_IFTYPE_MESH_POINT)) {
+		wpa_printf(MSG_INFO,
+			   "nl80211: Failed to set interface into mesh mode");
+		return -1;
+	}
+	return 0;
+}
+
+
+static int nl80211_put_mesh_id(struct nl_msg *msg, const u8 *mesh_id,
+			       size_t mesh_id_len)
+{
+	if (mesh_id) {
+		wpa_hexdump_ascii(MSG_DEBUG, "  * Mesh ID (SSID)",
+				  mesh_id, mesh_id_len);
+		return nla_put(msg, NL80211_ATTR_MESH_ID, mesh_id_len, mesh_id);
+	}
+
+	return 0;
+}
+
+
+static int nl80211_join_mesh(struct i802_bss *bss,
+			     struct wpa_driver_mesh_join_params *params)
+{
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	struct nl_msg *msg;
+	struct nlattr *container;
+	int ret = -1;
+
+	wpa_printf(MSG_DEBUG, "nl80211: mesh join (ifindex=%d)", drv->ifindex);
+	msg = nl80211_drv_msg(drv, 0, NL80211_CMD_JOIN_MESH);
+	if (!msg ||
+	    nl80211_put_freq_params(msg, &params->freq) ||
+	    nl80211_put_basic_rates(msg, params->basic_rates) ||
+	    nl80211_put_mesh_id(msg, params->meshid, params->meshid_len) ||
+	    nl80211_put_beacon_int(msg, params->beacon_int))
+		goto fail;
+
+	wpa_printf(MSG_DEBUG, "  * flags=%08X", params->flags);
+
+	container = nla_nest_start(msg, NL80211_ATTR_MESH_SETUP);
+	if (!container)
+		goto fail;
+
+	if (params->ies) {
+		wpa_hexdump(MSG_DEBUG, "  * IEs", params->ies, params->ie_len);
+		if (nla_put(msg, NL80211_MESH_SETUP_IE, params->ie_len,
+			    params->ies))
+			goto fail;
+	}
+	/* WPA_DRIVER_MESH_FLAG_OPEN_AUTH is treated as default by nl80211 */
+	if (params->flags & WPA_DRIVER_MESH_FLAG_SAE_AUTH) {
+		if (nla_put_u8(msg, NL80211_MESH_SETUP_AUTH_PROTOCOL, 0x1) ||
+		    nla_put_flag(msg, NL80211_MESH_SETUP_USERSPACE_AUTH))
+			goto fail;
+	}
+	if ((params->flags & WPA_DRIVER_MESH_FLAG_AMPE) &&
+	    nla_put_flag(msg, NL80211_MESH_SETUP_USERSPACE_AMPE))
+		goto fail;
+	if ((params->flags & WPA_DRIVER_MESH_FLAG_USER_MPM) &&
+	    nla_put_flag(msg, NL80211_MESH_SETUP_USERSPACE_MPM))
+		goto fail;
+	nla_nest_end(msg, container);
+
+	container = nla_nest_start(msg, NL80211_ATTR_MESH_CONFIG);
+	if (!container)
+		goto fail;
+
+	if (!(params->conf.flags & WPA_DRIVER_MESH_CONF_FLAG_AUTO_PLINKS) &&
+	    nla_put_u32(msg, NL80211_MESHCONF_AUTO_OPEN_PLINKS, 0))
+		goto fail;
+	if ((params->conf.flags & WPA_DRIVER_MESH_FLAG_DRIVER_MPM) &&
+	    nla_put_u16(msg, NL80211_MESHCONF_MAX_PEER_LINKS,
+			params->max_peer_links))
+		goto fail;
+
+	/*
+	 * Set NL80211_MESHCONF_PLINK_TIMEOUT even if user mpm is used because
+	 * the timer could disconnect stations even in that case.
+	 */
+	if (nla_put_u32(msg, NL80211_MESHCONF_PLINK_TIMEOUT,
+			params->conf.peer_link_timeout)) {
+		wpa_printf(MSG_ERROR, "nl80211: Failed to set PLINK_TIMEOUT");
+		goto fail;
+	}
+
+	nla_nest_end(msg, container);
+
+	ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+	msg = NULL;
+	if (ret) {
+		wpa_printf(MSG_DEBUG, "nl80211: mesh join failed: ret=%d (%s)",
+			   ret, strerror(-ret));
+		goto fail;
+	}
+	ret = 0;
+	bss->freq = params->freq.freq;
+	wpa_printf(MSG_DEBUG, "nl80211: mesh join request send successfully");
+
+fail:
+	nlmsg_free(msg);
+	return ret;
+}
+
+
+static int
+wpa_driver_nl80211_join_mesh(void *priv,
+			     struct wpa_driver_mesh_join_params *params)
+{
+	struct i802_bss *bss = priv;
+	int ret, timeout;
+
+	timeout = params->conf.peer_link_timeout;
+
+	/* Disable kernel inactivity timer */
+	if (params->flags & WPA_DRIVER_MESH_FLAG_USER_MPM)
+		params->conf.peer_link_timeout = 0;
+
+	ret = nl80211_join_mesh(bss, params);
+	if (ret == -EINVAL && params->conf.peer_link_timeout == 0) {
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: Mesh join retry for peer_link_timeout");
+		/*
+		 * Old kernel does not support setting
+		 * NL80211_MESHCONF_PLINK_TIMEOUT to zero, so set 60 seconds
+		 * into future from peer_link_timeout.
+		 */
+		params->conf.peer_link_timeout = timeout + 60;
+		ret = nl80211_join_mesh(priv, params);
+	}
+
+	params->conf.peer_link_timeout = timeout;
+	return ret;
+}
+
+
+static int wpa_driver_nl80211_leave_mesh(void *priv)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	struct nl_msg *msg;
+	int ret;
+
+	wpa_printf(MSG_DEBUG, "nl80211: mesh leave (ifindex=%d)", drv->ifindex);
+	msg = nl80211_drv_msg(drv, 0, NL80211_CMD_LEAVE_MESH);
+	ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+	if (ret) {
+		wpa_printf(MSG_DEBUG, "nl80211: mesh leave failed: ret=%d (%s)",
+			   ret, strerror(-ret));
+	} else {
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: mesh leave request send successfully");
+	}
+
+	if (wpa_driver_nl80211_set_mode(drv->first_bss,
+					NL80211_IFTYPE_STATION)) {
+		wpa_printf(MSG_INFO,
+			   "nl80211: Failed to set interface into station mode");
+	}
+	return ret;
+}
+
+#endif /* CONFIG_MESH */
+
+
+static int wpa_driver_br_add_ip_neigh(void *priv, u8 version,
+				      const u8 *ipaddr, int prefixlen,
+				      const u8 *addr)
+{
+#ifdef CONFIG_LIBNL3_ROUTE
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	struct rtnl_neigh *rn;
+	struct nl_addr *nl_ipaddr = NULL;
+	struct nl_addr *nl_lladdr = NULL;
+	int family, addrsize;
+	int res;
+
+	if (!ipaddr || prefixlen == 0 || !addr)
+		return -EINVAL;
+
+	if (bss->br_ifindex == 0) {
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: bridge must be set before adding an ip neigh to it");
+		return -1;
+	}
+
+	if (!drv->rtnl_sk) {
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: nl_sock for NETLINK_ROUTE is not initialized");
+		return -1;
+	}
+
+	if (version == 4) {
+		family = AF_INET;
+		addrsize = 4;
+	} else if (version == 6) {
+		family = AF_INET6;
+		addrsize = 16;
+	} else {
+		return -EINVAL;
+	}
+
+	rn = rtnl_neigh_alloc();
+	if (rn == NULL)
+		return -ENOMEM;
+
+	/* set the destination ip address for neigh */
+	nl_ipaddr = nl_addr_build(family, (void *) ipaddr, addrsize);
+	if (nl_ipaddr == NULL) {
+		wpa_printf(MSG_DEBUG, "nl80211: nl_ipaddr build failed");
+		res = -ENOMEM;
+		goto errout;
+	}
+	nl_addr_set_prefixlen(nl_ipaddr, prefixlen);
+	res = rtnl_neigh_set_dst(rn, nl_ipaddr);
+	if (res) {
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: neigh set destination addr failed");
+		goto errout;
+	}
+
+	/* set the corresponding lladdr for neigh */
+	nl_lladdr = nl_addr_build(AF_BRIDGE, (u8 *) addr, ETH_ALEN);
+	if (nl_lladdr == NULL) {
+		wpa_printf(MSG_DEBUG, "nl80211: neigh set lladdr failed");
+		res = -ENOMEM;
+		goto errout;
+	}
+	rtnl_neigh_set_lladdr(rn, nl_lladdr);
+
+	rtnl_neigh_set_ifindex(rn, bss->br_ifindex);
+	rtnl_neigh_set_state(rn, NUD_PERMANENT);
+
+	res = rtnl_neigh_add(drv->rtnl_sk, rn, NLM_F_CREATE);
+	if (res) {
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: Adding bridge ip neigh failed: %s",
+			   strerror(errno));
+	}
+errout:
+	if (nl_lladdr)
+		nl_addr_put(nl_lladdr);
+	if (nl_ipaddr)
+		nl_addr_put(nl_ipaddr);
+	if (rn)
+		rtnl_neigh_put(rn);
+	return res;
+#else /* CONFIG_LIBNL3_ROUTE */
+	return -1;
+#endif /* CONFIG_LIBNL3_ROUTE */
+}
+
+
+static int wpa_driver_br_delete_ip_neigh(void *priv, u8 version,
+					 const u8 *ipaddr)
+{
+#ifdef CONFIG_LIBNL3_ROUTE
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	struct rtnl_neigh *rn;
+	struct nl_addr *nl_ipaddr;
+	int family, addrsize;
+	int res;
+
+	if (!ipaddr)
+		return -EINVAL;
+
+	if (version == 4) {
+		family = AF_INET;
+		addrsize = 4;
+	} else if (version == 6) {
+		family = AF_INET6;
+		addrsize = 16;
+	} else {
+		return -EINVAL;
+	}
+
+	if (bss->br_ifindex == 0) {
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: bridge must be set to delete an ip neigh");
+		return -1;
+	}
+
+	if (!drv->rtnl_sk) {
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: nl_sock for NETLINK_ROUTE is not initialized");
+		return -1;
+	}
+
+	rn = rtnl_neigh_alloc();
+	if (rn == NULL)
+		return -ENOMEM;
+
+	/* set the destination ip address for neigh */
+	nl_ipaddr = nl_addr_build(family, (void *) ipaddr, addrsize);
+	if (nl_ipaddr == NULL) {
+		wpa_printf(MSG_DEBUG, "nl80211: nl_ipaddr build failed");
+		res = -ENOMEM;
+		goto errout;
+	}
+	res = rtnl_neigh_set_dst(rn, nl_ipaddr);
+	if (res) {
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: neigh set destination addr failed");
+		goto errout;
+	}
+
+	rtnl_neigh_set_ifindex(rn, bss->br_ifindex);
+
+	res = rtnl_neigh_delete(drv->rtnl_sk, rn, 0);
+	if (res) {
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: Deleting bridge ip neigh failed: %s",
+			   strerror(errno));
+	}
+errout:
+	if (nl_ipaddr)
+		nl_addr_put(nl_ipaddr);
+	if (rn)
+		rtnl_neigh_put(rn);
+	return res;
+#else /* CONFIG_LIBNL3_ROUTE */
+	return -1;
+#endif /* CONFIG_LIBNL3_ROUTE */
+}
+
+
+static int linux_write_system_file(const char *path, unsigned int val)
+{
+	char buf[50];
+	int fd, len;
+
+	len = os_snprintf(buf, sizeof(buf), "%u\n", val);
+	if (os_snprintf_error(sizeof(buf), len))
+		return -1;
+
+	fd = open(path, O_WRONLY);
+	if (fd < 0)
+		return -1;
+
+	if (write(fd, buf, len) < 0) {
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: Failed to write Linux system file: %s with the value of %d",
+			   path, val);
+		close(fd);
+		return -1;
+	}
+	close(fd);
+
+	return 0;
+}
+
+
+static const char * drv_br_port_attr_str(enum drv_br_port_attr attr)
+{
+	switch (attr) {
+	case DRV_BR_PORT_ATTR_PROXYARP:
+		return "proxyarp_wifi";
+	case DRV_BR_PORT_ATTR_HAIRPIN_MODE:
+		return "hairpin_mode";
+	}
+
+	return NULL;
+}
+
+
+static int wpa_driver_br_port_set_attr(void *priv, enum drv_br_port_attr attr,
+				       unsigned int val)
+{
+	struct i802_bss *bss = priv;
+	char path[128];
+	const char *attr_txt;
+
+	attr_txt = drv_br_port_attr_str(attr);
+	if (attr_txt == NULL)
+		return -EINVAL;
+
+	os_snprintf(path, sizeof(path), "/sys/class/net/%s/brport/%s",
+		    bss->ifname, attr_txt);
+
+	if (linux_write_system_file(path, val))
+		return -1;
+
+	return 0;
+}
+
+
+static const char * drv_br_net_param_str(enum drv_br_net_param param)
+{
+	switch (param) {
+	case DRV_BR_NET_PARAM_GARP_ACCEPT:
+		return "arp_accept";
+	default:
+		return NULL;
+	}
+}
+
+
+static int wpa_driver_br_set_net_param(void *priv, enum drv_br_net_param param,
+				       unsigned int val)
+{
+	struct i802_bss *bss = priv;
+	char path[128];
+	const char *param_txt;
+	int ip_version = 4;
+
+	if (param == DRV_BR_MULTICAST_SNOOPING) {
+		os_snprintf(path, sizeof(path),
+			    "/sys/devices/virtual/net/%s/bridge/multicast_snooping",
+			    bss->brname);
+		goto set_val;
+	}
+
+	param_txt = drv_br_net_param_str(param);
+	if (param_txt == NULL)
+		return -EINVAL;
+
+	switch (param) {
+		case DRV_BR_NET_PARAM_GARP_ACCEPT:
+			ip_version = 4;
+			break;
+		default:
+			return -EINVAL;
+	}
+
+	os_snprintf(path, sizeof(path), "/proc/sys/net/ipv%d/conf/%s/%s",
+		    ip_version, bss->brname, param_txt);
+
+set_val:
+	if (linux_write_system_file(path, val))
+		return -1;
+
+	return 0;
+}
+
+
+static int hw_mode_to_qca_acs(enum hostapd_hw_mode hw_mode)
+{
+	switch (hw_mode) {
+	case HOSTAPD_MODE_IEEE80211B:
+		return QCA_ACS_MODE_IEEE80211B;
+	case HOSTAPD_MODE_IEEE80211G:
+		return QCA_ACS_MODE_IEEE80211G;
+	case HOSTAPD_MODE_IEEE80211A:
+		return QCA_ACS_MODE_IEEE80211A;
+	case HOSTAPD_MODE_IEEE80211AD:
+		return QCA_ACS_MODE_IEEE80211AD;
+	case HOSTAPD_MODE_IEEE80211ANY:
+		return QCA_ACS_MODE_IEEE80211ANY;
+	default:
+		return -1;
+	}
+}
+
+
+static int wpa_driver_do_acs(void *priv, struct drv_acs_params *params)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	struct nl_msg *msg;
+	struct nlattr *data;
+	int ret;
+	int mode;
+
+	mode = hw_mode_to_qca_acs(params->hw_mode);
+	if (mode < 0)
+		return -1;
+
+	if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
+	    nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
+	    nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
+			QCA_NL80211_VENDOR_SUBCMD_DO_ACS) ||
+	    !(data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA)) ||
+	    nla_put_u8(msg, QCA_WLAN_VENDOR_ATTR_ACS_HW_MODE, mode) ||
+	    (params->ht_enabled &&
+	     nla_put_flag(msg, QCA_WLAN_VENDOR_ATTR_ACS_HT_ENABLED)) ||
+	    (params->ht40_enabled &&
+	     nla_put_flag(msg, QCA_WLAN_VENDOR_ATTR_ACS_HT40_ENABLED)) ||
+	    (params->vht_enabled &&
+	     nla_put_flag(msg, QCA_WLAN_VENDOR_ATTR_ACS_VHT_ENABLED)) ||
+	    nla_put_u16(msg, QCA_WLAN_VENDOR_ATTR_ACS_CHWIDTH,
+			params->ch_width) ||
+	    (params->ch_list_len &&
+	     nla_put(msg, QCA_WLAN_VENDOR_ATTR_ACS_CH_LIST, params->ch_list_len,
+		     params->ch_list))) {
+		nlmsg_free(msg);
+		return -ENOBUFS;
+	}
+	nla_nest_end(msg, data);
+
+	wpa_printf(MSG_DEBUG,
+		   "nl80211: ACS Params: HW_MODE: %d HT: %d HT40: %d VHT: %d BW: %d CH_LIST_LEN: %u",
+		   params->hw_mode, params->ht_enabled, params->ht40_enabled,
+		   params->vht_enabled, params->ch_width, params->ch_list_len);
+
+	ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+	if (ret) {
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: Failed to invoke driver ACS function: %s",
+			   strerror(errno));
+	}
+	return ret;
+}
+
+
 const struct wpa_driver_ops wpa_driver_nl80211_ops = {
 	.name = "nl80211",
 	.desc = "Linux nl80211/cfg80211",
@@ -12628,7 +8450,7 @@
 	.if_add = wpa_driver_nl80211_if_add,
 	.if_remove = driver_nl80211_if_remove,
 	.send_mlme = driver_nl80211_send_mlme,
-	.get_hw_feature_data = wpa_driver_nl80211_get_hw_feature_data,
+	.get_hw_feature_data = nl80211_get_hw_feature_data,
 	.sta_add = wpa_driver_nl80211_sta_add,
 	.sta_remove = driver_nl80211_sta_remove,
 	.hapd_send_eapol = wpa_driver_nl80211_hapd_send_eapol,
@@ -12657,11 +8479,9 @@
 	.deinit_ap = wpa_driver_nl80211_deinit_ap,
 	.deinit_p2p_cli = wpa_driver_nl80211_deinit_p2p_cli,
 	.resume = wpa_driver_nl80211_resume,
-	.send_ft_action = nl80211_send_ft_action,
 	.signal_monitor = nl80211_signal_monitor,
 	.signal_poll = nl80211_signal_poll,
 	.send_frame = nl80211_send_frame,
-	.shared_freq = wpa_driver_nl80211_shared_freq,
 	.set_param = nl80211_set_param,
 	.get_radio_name = nl80211_get_radio_name,
 	.add_pmkid = nl80211_add_pmkid,
@@ -12675,6 +8495,8 @@
 #ifdef CONFIG_TDLS
 	.send_tdls_mgmt = nl80211_send_tdls_mgmt,
 	.tdls_oper = nl80211_tdls_oper,
+	.tdls_enable_channel_switch = nl80211_tdls_enable_channel_switch,
+	.tdls_disable_channel_switch = nl80211_tdls_disable_channel_switch,
 #endif /* CONFIG_TDLS */
 	.update_ft_ies = wpa_driver_nl80211_update_ft_ies,
 	.get_mac_addr = wpa_driver_nl80211_get_macaddr,
@@ -12687,11 +8509,25 @@
 	.set_ap_wps_ie = wpa_driver_set_ap_wps_p2p_ie,
 #endif /* ANDROID_P2P */
 #ifdef ANDROID
+#ifndef ANDROID_LIB_STUB
 	.driver_cmd = wpa_driver_nl80211_driver_cmd,
+#endif /* !ANDROID_LIB_STUB */
 #endif /* ANDROID */
 	.vendor_cmd = nl80211_vendor_cmd,
 	.set_qos_map = nl80211_set_qos_map,
 	.set_wowlan = nl80211_set_wowlan,
 	.roaming = nl80211_roaming,
 	.set_mac_addr = nl80211_set_mac_addr,
+#ifdef CONFIG_MESH
+	.init_mesh = wpa_driver_nl80211_init_mesh,
+	.join_mesh = wpa_driver_nl80211_join_mesh,
+	.leave_mesh = wpa_driver_nl80211_leave_mesh,
+#endif /* CONFIG_MESH */
+	.br_add_ip_neigh = wpa_driver_br_add_ip_neigh,
+	.br_delete_ip_neigh = wpa_driver_br_delete_ip_neigh,
+	.br_port_set_attr = wpa_driver_br_port_set_attr,
+	.br_set_net_param = wpa_driver_br_set_net_param,
+	.add_tx_ts = nl80211_add_ts,
+	.del_tx_ts = nl80211_del_ts,
+	.do_acs = wpa_driver_do_acs,
 };
diff --git a/src/drivers/driver_nl80211.h b/src/drivers/driver_nl80211.h
new file mode 100644
index 0000000..acfe959
--- /dev/null
+++ b/src/drivers/driver_nl80211.h
@@ -0,0 +1,274 @@
+/*
+ * Driver interaction with Linux nl80211/cfg80211 - definitions
+ * Copyright (c) 2002-2014, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2003-2004, Instant802 Networks, Inc.
+ * Copyright (c) 2005-2006, Devicescape Software, Inc.
+ * Copyright (c) 2007, Johannes Berg <johannes@sipsolutions.net>
+ * Copyright (c) 2009-2010, Atheros Communications
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef DRIVER_NL80211_H
+#define DRIVER_NL80211_H
+
+#include "nl80211_copy.h"
+#include "utils/list.h"
+#include "driver.h"
+
+#ifdef CONFIG_LIBNL20
+/* libnl 2.0 compatibility code */
+#define nl_handle nl_sock
+#define nl80211_handle_alloc nl_socket_alloc_cb
+#define nl80211_handle_destroy nl_socket_free
+#endif /* CONFIG_LIBNL20 */
+
+struct nl80211_global {
+	struct dl_list interfaces;
+	int if_add_ifindex;
+	u64 if_add_wdevid;
+	int if_add_wdevid_set;
+	struct netlink_data *netlink;
+	struct nl_cb *nl_cb;
+	struct nl_handle *nl;
+	int nl80211_id;
+	int ioctl_sock; /* socket for ioctl() use */
+
+	struct nl_handle *nl_event;
+};
+
+struct nl80211_wiphy_data {
+	struct dl_list list;
+	struct dl_list bsss;
+	struct dl_list drvs;
+
+	struct nl_handle *nl_beacons;
+	struct nl_cb *nl_cb;
+
+	int wiphy_idx;
+};
+
+struct i802_bss {
+	struct wpa_driver_nl80211_data *drv;
+	struct i802_bss *next;
+	int ifindex;
+	int br_ifindex;
+	u64 wdev_id;
+	char ifname[IFNAMSIZ + 1];
+	char brname[IFNAMSIZ];
+	unsigned int beacon_set:1;
+	unsigned int added_if_into_bridge:1;
+	unsigned int added_bridge:1;
+	unsigned int in_deinit:1;
+	unsigned int wdev_id_set:1;
+	unsigned int added_if:1;
+	unsigned int static_ap:1;
+
+	u8 addr[ETH_ALEN];
+
+	int freq;
+	int bandwidth;
+	int if_dynamic;
+
+	void *ctx;
+	struct nl_handle *nl_preq, *nl_mgmt;
+	struct nl_cb *nl_cb;
+
+	struct nl80211_wiphy_data *wiphy_data;
+	struct dl_list wiphy_list;
+};
+
+struct wpa_driver_nl80211_data {
+	struct nl80211_global *global;
+	struct dl_list list;
+	struct dl_list wiphy_list;
+	char phyname[32];
+	u8 perm_addr[ETH_ALEN];
+	void *ctx;
+	int ifindex;
+	int if_removed;
+	int if_disabled;
+	int ignore_if_down_event;
+	struct rfkill_data *rfkill;
+	struct wpa_driver_capa capa;
+	u8 *extended_capa, *extended_capa_mask;
+	unsigned int extended_capa_len;
+	int has_capability;
+
+	int operstate;
+
+	int scan_complete_events;
+	enum scan_states {
+		NO_SCAN, SCAN_REQUESTED, SCAN_STARTED, SCAN_COMPLETED,
+		SCAN_ABORTED, SCHED_SCAN_STARTED, SCHED_SCAN_STOPPED,
+		SCHED_SCAN_RESULTS
+	} scan_state;
+
+	u8 auth_bssid[ETH_ALEN];
+	u8 auth_attempt_bssid[ETH_ALEN];
+	u8 bssid[ETH_ALEN];
+	u8 prev_bssid[ETH_ALEN];
+	int associated;
+	u8 ssid[SSID_MAX_LEN];
+	size_t ssid_len;
+	enum nl80211_iftype nlmode;
+	enum nl80211_iftype ap_scan_as_station;
+	unsigned int assoc_freq;
+
+	int monitor_sock;
+	int monitor_ifidx;
+	int monitor_refcount;
+
+	unsigned int disabled_11b_rates:1;
+	unsigned int pending_remain_on_chan:1;
+	unsigned int in_interface_list:1;
+	unsigned int device_ap_sme:1;
+	unsigned int poll_command_supported:1;
+	unsigned int data_tx_status:1;
+	unsigned int scan_for_auth:1;
+	unsigned int retry_auth:1;
+	unsigned int use_monitor:1;
+	unsigned int ignore_next_local_disconnect:1;
+	unsigned int ignore_next_local_deauth:1;
+	unsigned int hostapd:1;
+	unsigned int start_mode_ap:1;
+	unsigned int start_iface_up:1;
+	unsigned int test_use_roc_tx:1;
+	unsigned int ignore_deauth_event:1;
+	unsigned int vendor_cmd_test_avail:1;
+	unsigned int roaming_vendor_cmd_avail:1;
+	unsigned int dfs_vendor_cmd_avail:1;
+	unsigned int have_low_prio_scan:1;
+	unsigned int force_connect_cmd:1;
+	unsigned int addr_changed:1;
+	unsigned int get_features_vendor_cmd_avail:1;
+	unsigned int set_rekey_offload:1;
+	unsigned int p2p_go_ctwindow_supported:1;
+
+	u64 remain_on_chan_cookie;
+	u64 send_action_cookie;
+
+	unsigned int last_mgmt_freq;
+
+	struct wpa_driver_scan_filter *filter_ssids;
+	size_t num_filter_ssids;
+
+	struct i802_bss *first_bss;
+
+	int eapol_tx_sock;
+
+	int eapol_sock; /* socket for EAPOL frames */
+
+	struct nl_handle *rtnl_sk; /* nl_sock for NETLINK_ROUTE */
+
+	int default_if_indices[16];
+	int *if_indices;
+	int num_if_indices;
+
+	/* From failed authentication command */
+	int auth_freq;
+	u8 auth_bssid_[ETH_ALEN];
+	u8 auth_ssid[SSID_MAX_LEN];
+	size_t auth_ssid_len;
+	int auth_alg;
+	u8 *auth_ie;
+	size_t auth_ie_len;
+	u8 auth_wep_key[4][16];
+	size_t auth_wep_key_len[4];
+	int auth_wep_tx_keyidx;
+	int auth_local_state_change;
+	int auth_p2p;
+};
+
+struct nl_msg;
+
+void * nl80211_cmd(struct wpa_driver_nl80211_data *drv,
+		   struct nl_msg *msg, int flags, uint8_t cmd);
+struct nl_msg * nl80211_cmd_msg(struct i802_bss *bss, int flags, uint8_t cmd);
+struct nl_msg * nl80211_drv_msg(struct wpa_driver_nl80211_data *drv, int flags,
+				uint8_t cmd);
+struct nl_msg * nl80211_bss_msg(struct i802_bss *bss, int flags, uint8_t cmd);
+int send_and_recv_msgs(struct wpa_driver_nl80211_data *drv, struct nl_msg *msg,
+		       int (*valid_handler)(struct nl_msg *, void *),
+		       void *valid_data);
+int nl80211_create_iface(struct wpa_driver_nl80211_data *drv,
+			 const char *ifname, enum nl80211_iftype iftype,
+			 const u8 *addr, int wds,
+			 int (*handler)(struct nl_msg *, void *),
+			 void *arg, int use_existing);
+void nl80211_remove_iface(struct wpa_driver_nl80211_data *drv, int ifidx);
+unsigned int nl80211_get_assoc_freq(struct wpa_driver_nl80211_data *drv);
+enum chan_width convert2width(int width);
+void nl80211_mark_disconnected(struct wpa_driver_nl80211_data *drv);
+struct i802_bss * get_bss_ifindex(struct wpa_driver_nl80211_data *drv,
+				  int ifindex);
+int is_ap_interface(enum nl80211_iftype nlmode);
+int is_sta_interface(enum nl80211_iftype nlmode);
+int wpa_driver_nl80211_authenticate_retry(struct wpa_driver_nl80211_data *drv);
+int nl80211_get_link_signal(struct wpa_driver_nl80211_data *drv,
+			    struct wpa_signal_info *sig);
+int nl80211_get_link_noise(struct wpa_driver_nl80211_data *drv,
+			   struct wpa_signal_info *sig_change);
+int nl80211_get_wiphy_index(struct i802_bss *bss);
+int wpa_driver_nl80211_set_mode(struct i802_bss *bss,
+				enum nl80211_iftype nlmode);
+int wpa_driver_nl80211_mlme(struct wpa_driver_nl80211_data *drv,
+			    const u8 *addr, int cmd, u16 reason_code,
+			    int local_state_change);
+
+int nl80211_create_monitor_interface(struct wpa_driver_nl80211_data *drv);
+void nl80211_remove_monitor_interface(struct wpa_driver_nl80211_data *drv);
+int nl80211_send_monitor(struct wpa_driver_nl80211_data *drv,
+			 const void *data, size_t len,
+			 int encrypt, int noack);
+
+int wpa_driver_nl80211_capa(struct wpa_driver_nl80211_data *drv);
+struct hostapd_hw_modes *
+nl80211_get_hw_feature_data(void *priv, u16 *num_modes, u16 *flags);
+
+int process_global_event(struct nl_msg *msg, void *arg);
+int process_bss_event(struct nl_msg *msg, void *arg);
+
+#ifdef ANDROID
+int android_nl_socket_set_nonblocking(struct nl_handle *handle);
+int android_pno_start(struct i802_bss *bss,
+		      struct wpa_driver_scan_params *params);
+int android_pno_stop(struct i802_bss *bss);
+extern int wpa_driver_nl80211_driver_cmd(void *priv, char *cmd, char *buf,
+					 size_t buf_len);
+
+#ifdef ANDROID_P2P
+int wpa_driver_set_p2p_noa(void *priv, u8 count, int start, int duration);
+int wpa_driver_get_p2p_noa(void *priv, u8 *buf, size_t len);
+int wpa_driver_set_p2p_ps(void *priv, int legacy_ps, int opp_ps, int ctwindow);
+int wpa_driver_set_ap_wps_p2p_ie(void *priv, const struct wpabuf *beacon,
+				 const struct wpabuf *proberesp,
+				 const struct wpabuf *assocresp);
+#endif /* ANDROID_P2P */
+#endif /* ANDROID */
+
+
+/* driver_nl80211_scan.c */
+
+struct nl80211_bss_info_arg {
+	struct wpa_driver_nl80211_data *drv;
+	struct wpa_scan_results *res;
+	unsigned int assoc_freq;
+	unsigned int ibss_freq;
+	u8 assoc_bssid[ETH_ALEN];
+};
+
+int bss_info_handler(struct nl_msg *msg, void *arg);
+void wpa_driver_nl80211_scan_timeout(void *eloop_ctx, void *timeout_ctx);
+int wpa_driver_nl80211_scan(struct i802_bss *bss,
+			    struct wpa_driver_scan_params *params);
+int wpa_driver_nl80211_sched_scan(void *priv,
+				  struct wpa_driver_scan_params *params,
+				  u32 interval);
+int wpa_driver_nl80211_stop_sched_scan(void *priv);
+struct wpa_scan_results * wpa_driver_nl80211_get_scan_results(void *priv);
+void nl80211_dump_scan(struct wpa_driver_nl80211_data *drv);
+const u8 * nl80211_get_ie(const u8 *ies, size_t ies_len, u8 ie);
+
+#endif /* DRIVER_NL80211_H */
diff --git a/src/drivers/driver_nl80211_android.c b/src/drivers/driver_nl80211_android.c
new file mode 100644
index 0000000..ba47888
--- /dev/null
+++ b/src/drivers/driver_nl80211_android.c
@@ -0,0 +1,190 @@
+/*
+ * Driver interaction with Linux nl80211/cfg80211 - Android specific
+ * Copyright (c) 2002-2014, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2007, Johannes Berg <johannes@sipsolutions.net>
+ * Copyright (c) 2009-2010, Atheros Communications
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+#include <sys/ioctl.h>
+#include <net/if.h>
+#include <netlink/genl/genl.h>
+#include <netlink/genl/family.h>
+#include <netlink/genl/ctrl.h>
+#include <fcntl.h>
+
+#include "utils/common.h"
+#include "driver_nl80211.h"
+#include "android_drv.h"
+
+
+typedef struct android_wifi_priv_cmd {
+	char *buf;
+	int used_len;
+	int total_len;
+} android_wifi_priv_cmd;
+
+static int drv_errors = 0;
+
+static void wpa_driver_send_hang_msg(struct wpa_driver_nl80211_data *drv)
+{
+	drv_errors++;
+	if (drv_errors > DRV_NUMBER_SEQUENTIAL_ERRORS) {
+		drv_errors = 0;
+		wpa_msg(drv->ctx, MSG_INFO, WPA_EVENT_DRIVER_STATE "HANGED");
+	}
+}
+
+
+static int android_priv_cmd(struct i802_bss *bss, const char *cmd)
+{
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	struct ifreq ifr;
+	android_wifi_priv_cmd priv_cmd;
+	char buf[MAX_DRV_CMD_SIZE];
+	int ret;
+
+	os_memset(&ifr, 0, sizeof(ifr));
+	os_memset(&priv_cmd, 0, sizeof(priv_cmd));
+	os_strlcpy(ifr.ifr_name, bss->ifname, IFNAMSIZ);
+
+	os_memset(buf, 0, sizeof(buf));
+	os_strlcpy(buf, cmd, sizeof(buf));
+
+	priv_cmd.buf = buf;
+	priv_cmd.used_len = sizeof(buf);
+	priv_cmd.total_len = sizeof(buf);
+	ifr.ifr_data = &priv_cmd;
+
+	ret = ioctl(drv->global->ioctl_sock, SIOCDEVPRIVATE + 1, &ifr);
+	if (ret < 0) {
+		wpa_printf(MSG_ERROR, "%s: failed to issue private commands",
+			   __func__);
+		wpa_driver_send_hang_msg(drv);
+		return ret;
+	}
+
+	drv_errors = 0;
+	return 0;
+}
+
+
+int android_pno_start(struct i802_bss *bss,
+		      struct wpa_driver_scan_params *params)
+{
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	struct ifreq ifr;
+	android_wifi_priv_cmd priv_cmd;
+	int ret = 0, i = 0, bp;
+	char buf[WEXT_PNO_MAX_COMMAND_SIZE];
+
+	bp = WEXT_PNOSETUP_HEADER_SIZE;
+	os_memcpy(buf, WEXT_PNOSETUP_HEADER, bp);
+	buf[bp++] = WEXT_PNO_TLV_PREFIX;
+	buf[bp++] = WEXT_PNO_TLV_VERSION;
+	buf[bp++] = WEXT_PNO_TLV_SUBVERSION;
+	buf[bp++] = WEXT_PNO_TLV_RESERVED;
+
+	while (i < WEXT_PNO_AMOUNT && (size_t) i < params->num_ssids) {
+		/* Check that there is enough space needed for 1 more SSID, the
+		 * other sections and null termination */
+		if ((bp + WEXT_PNO_SSID_HEADER_SIZE + MAX_SSID_LEN +
+		     WEXT_PNO_NONSSID_SECTIONS_SIZE + 1) >= (int) sizeof(buf))
+			break;
+		wpa_hexdump_ascii(MSG_DEBUG, "For PNO Scan",
+				  params->ssids[i].ssid,
+				  params->ssids[i].ssid_len);
+		buf[bp++] = WEXT_PNO_SSID_SECTION;
+		buf[bp++] = params->ssids[i].ssid_len;
+		os_memcpy(&buf[bp], params->ssids[i].ssid,
+			  params->ssids[i].ssid_len);
+		bp += params->ssids[i].ssid_len;
+		i++;
+	}
+
+	buf[bp++] = WEXT_PNO_SCAN_INTERVAL_SECTION;
+	os_snprintf(&buf[bp], WEXT_PNO_SCAN_INTERVAL_LENGTH + 1, "%x",
+		    WEXT_PNO_SCAN_INTERVAL);
+	bp += WEXT_PNO_SCAN_INTERVAL_LENGTH;
+
+	buf[bp++] = WEXT_PNO_REPEAT_SECTION;
+	os_snprintf(&buf[bp], WEXT_PNO_REPEAT_LENGTH + 1, "%x",
+		    WEXT_PNO_REPEAT);
+	bp += WEXT_PNO_REPEAT_LENGTH;
+
+	buf[bp++] = WEXT_PNO_MAX_REPEAT_SECTION;
+	os_snprintf(&buf[bp], WEXT_PNO_MAX_REPEAT_LENGTH + 1, "%x",
+		    WEXT_PNO_MAX_REPEAT);
+	bp += WEXT_PNO_MAX_REPEAT_LENGTH + 1;
+
+	memset(&ifr, 0, sizeof(ifr));
+	memset(&priv_cmd, 0, sizeof(priv_cmd));
+	os_strlcpy(ifr.ifr_name, bss->ifname, IFNAMSIZ);
+
+	priv_cmd.buf = buf;
+	priv_cmd.used_len = bp;
+	priv_cmd.total_len = bp;
+	ifr.ifr_data = &priv_cmd;
+
+	ret = ioctl(drv->global->ioctl_sock, SIOCDEVPRIVATE + 1, &ifr);
+
+	if (ret < 0) {
+		wpa_printf(MSG_ERROR, "ioctl[SIOCSIWPRIV] (pnosetup): %d",
+			   ret);
+		wpa_driver_send_hang_msg(drv);
+		return ret;
+	}
+
+	drv_errors = 0;
+
+	return android_priv_cmd(bss, "PNOFORCE 1");
+}
+
+
+int android_pno_stop(struct i802_bss *bss)
+{
+	return android_priv_cmd(bss, "PNOFORCE 0");
+}
+
+
+#ifdef ANDROID_P2P
+#ifdef ANDROID_LIB_STUB
+
+int wpa_driver_set_p2p_noa(void *priv, u8 count, int start, int duration)
+{
+	return 0;
+}
+
+
+int wpa_driver_get_p2p_noa(void *priv, u8 *buf, size_t len)
+{
+	return 0;
+}
+
+
+int wpa_driver_set_p2p_ps(void *priv, int legacy_ps, int opp_ps, int ctwindow)
+{
+	return -1;
+}
+
+
+int wpa_driver_set_ap_wps_p2p_ie(void *priv, const struct wpabuf *beacon,
+				 const struct wpabuf *proberesp,
+				 const struct wpabuf *assocresp)
+{
+	return 0;
+}
+
+#endif /* ANDROID_LIB_STUB */
+#endif /* ANDROID_P2P */
+
+
+int android_nl_socket_set_nonblocking(struct nl_handle *handle)
+{
+	return fcntl(nl_socket_get_fd(handle), F_SETFL, O_NONBLOCK);
+}
+
+
diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c
new file mode 100644
index 0000000..e23c57e
--- /dev/null
+++ b/src/drivers/driver_nl80211_capa.c
@@ -0,0 +1,1566 @@
+/*
+ * Driver interaction with Linux nl80211/cfg80211 - Capabilities
+ * Copyright (c) 2002-2015, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2007, Johannes Berg <johannes@sipsolutions.net>
+ * Copyright (c) 2009-2010, Atheros Communications
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+#include <netlink/genl/genl.h>
+
+#include "utils/common.h"
+#include "common/ieee802_11_defs.h"
+#include "common/ieee802_11_common.h"
+#include "common/qca-vendor.h"
+#include "common/qca-vendor-attr.h"
+#include "driver_nl80211.h"
+
+
+static int protocol_feature_handler(struct nl_msg *msg, void *arg)
+{
+	u32 *feat = arg;
+	struct nlattr *tb_msg[NL80211_ATTR_MAX + 1];
+	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+
+	nla_parse(tb_msg, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+		  genlmsg_attrlen(gnlh, 0), NULL);
+
+	if (tb_msg[NL80211_ATTR_PROTOCOL_FEATURES])
+		*feat = nla_get_u32(tb_msg[NL80211_ATTR_PROTOCOL_FEATURES]);
+
+	return NL_SKIP;
+}
+
+
+static u32 get_nl80211_protocol_features(struct wpa_driver_nl80211_data *drv)
+{
+	u32 feat = 0;
+	struct nl_msg *msg;
+
+	msg = nlmsg_alloc();
+	if (!msg)
+		return 0;
+
+	if (!nl80211_cmd(drv, msg, 0, NL80211_CMD_GET_PROTOCOL_FEATURES)) {
+		nlmsg_free(msg);
+		return 0;
+	}
+
+	if (send_and_recv_msgs(drv, msg, protocol_feature_handler, &feat) == 0)
+		return feat;
+
+	return 0;
+}
+
+
+struct wiphy_info_data {
+	struct wpa_driver_nl80211_data *drv;
+	struct wpa_driver_capa *capa;
+
+	unsigned int num_multichan_concurrent;
+
+	unsigned int error:1;
+	unsigned int device_ap_sme:1;
+	unsigned int poll_command_supported:1;
+	unsigned int data_tx_status:1;
+	unsigned int monitor_supported:1;
+	unsigned int auth_supported:1;
+	unsigned int connect_supported:1;
+	unsigned int p2p_go_supported:1;
+	unsigned int p2p_client_supported:1;
+	unsigned int p2p_go_ctwindow_supported:1;
+	unsigned int p2p_concurrent:1;
+	unsigned int channel_switch_supported:1;
+	unsigned int set_qos_map_supported:1;
+	unsigned int have_low_prio_scan:1;
+	unsigned int wmm_ac_supported:1;
+	unsigned int mac_addr_rand_scan_supported:1;
+	unsigned int mac_addr_rand_sched_scan_supported:1;
+};
+
+
+static unsigned int probe_resp_offload_support(int supp_protocols)
+{
+	unsigned int prot = 0;
+
+	if (supp_protocols & NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS)
+		prot |= WPA_DRIVER_PROBE_RESP_OFFLOAD_WPS;
+	if (supp_protocols & NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS2)
+		prot |= WPA_DRIVER_PROBE_RESP_OFFLOAD_WPS2;
+	if (supp_protocols & NL80211_PROBE_RESP_OFFLOAD_SUPPORT_P2P)
+		prot |= WPA_DRIVER_PROBE_RESP_OFFLOAD_P2P;
+	if (supp_protocols & NL80211_PROBE_RESP_OFFLOAD_SUPPORT_80211U)
+		prot |= WPA_DRIVER_PROBE_RESP_OFFLOAD_INTERWORKING;
+
+	return prot;
+}
+
+
+static void wiphy_info_supported_iftypes(struct wiphy_info_data *info,
+					 struct nlattr *tb)
+{
+	struct nlattr *nl_mode;
+	int i;
+
+	if (tb == NULL)
+		return;
+
+	nla_for_each_nested(nl_mode, tb, i) {
+		switch (nla_type(nl_mode)) {
+		case NL80211_IFTYPE_AP:
+			info->capa->flags |= WPA_DRIVER_FLAGS_AP;
+			break;
+		case NL80211_IFTYPE_MESH_POINT:
+			info->capa->flags |= WPA_DRIVER_FLAGS_MESH;
+			break;
+		case NL80211_IFTYPE_ADHOC:
+			info->capa->flags |= WPA_DRIVER_FLAGS_IBSS;
+			break;
+		case NL80211_IFTYPE_P2P_DEVICE:
+			info->capa->flags |=
+				WPA_DRIVER_FLAGS_DEDICATED_P2P_DEVICE;
+			break;
+		case NL80211_IFTYPE_P2P_GO:
+			info->p2p_go_supported = 1;
+			break;
+		case NL80211_IFTYPE_P2P_CLIENT:
+			info->p2p_client_supported = 1;
+			break;
+		case NL80211_IFTYPE_MONITOR:
+			info->monitor_supported = 1;
+			break;
+		}
+	}
+}
+
+
+static int wiphy_info_iface_comb_process(struct wiphy_info_data *info,
+					 struct nlattr *nl_combi)
+{
+	struct nlattr *tb_comb[NUM_NL80211_IFACE_COMB];
+	struct nlattr *tb_limit[NUM_NL80211_IFACE_LIMIT];
+	struct nlattr *nl_limit, *nl_mode;
+	int err, rem_limit, rem_mode;
+	int combination_has_p2p = 0, combination_has_mgd = 0;
+	static struct nla_policy
+	iface_combination_policy[NUM_NL80211_IFACE_COMB] = {
+		[NL80211_IFACE_COMB_LIMITS] = { .type = NLA_NESTED },
+		[NL80211_IFACE_COMB_MAXNUM] = { .type = NLA_U32 },
+		[NL80211_IFACE_COMB_STA_AP_BI_MATCH] = { .type = NLA_FLAG },
+		[NL80211_IFACE_COMB_NUM_CHANNELS] = { .type = NLA_U32 },
+		[NL80211_IFACE_COMB_RADAR_DETECT_WIDTHS] = { .type = NLA_U32 },
+	},
+	iface_limit_policy[NUM_NL80211_IFACE_LIMIT] = {
+		[NL80211_IFACE_LIMIT_TYPES] = { .type = NLA_NESTED },
+		[NL80211_IFACE_LIMIT_MAX] = { .type = NLA_U32 },
+	};
+
+	err = nla_parse_nested(tb_comb, MAX_NL80211_IFACE_COMB,
+			       nl_combi, iface_combination_policy);
+	if (err || !tb_comb[NL80211_IFACE_COMB_LIMITS] ||
+	    !tb_comb[NL80211_IFACE_COMB_MAXNUM] ||
+	    !tb_comb[NL80211_IFACE_COMB_NUM_CHANNELS])
+		return 0; /* broken combination */
+
+	if (tb_comb[NL80211_IFACE_COMB_RADAR_DETECT_WIDTHS])
+		info->capa->flags |= WPA_DRIVER_FLAGS_RADAR;
+
+	nla_for_each_nested(nl_limit, tb_comb[NL80211_IFACE_COMB_LIMITS],
+			    rem_limit) {
+		err = nla_parse_nested(tb_limit, MAX_NL80211_IFACE_LIMIT,
+				       nl_limit, iface_limit_policy);
+		if (err || !tb_limit[NL80211_IFACE_LIMIT_TYPES])
+			return 0; /* broken combination */
+
+		nla_for_each_nested(nl_mode,
+				    tb_limit[NL80211_IFACE_LIMIT_TYPES],
+				    rem_mode) {
+			int ift = nla_type(nl_mode);
+			if (ift == NL80211_IFTYPE_P2P_GO ||
+			    ift == NL80211_IFTYPE_P2P_CLIENT)
+				combination_has_p2p = 1;
+			if (ift == NL80211_IFTYPE_STATION)
+				combination_has_mgd = 1;
+		}
+		if (combination_has_p2p && combination_has_mgd)
+			break;
+	}
+
+	if (combination_has_p2p && combination_has_mgd) {
+		unsigned int num_channels =
+			nla_get_u32(tb_comb[NL80211_IFACE_COMB_NUM_CHANNELS]);
+
+		info->p2p_concurrent = 1;
+		if (info->num_multichan_concurrent < num_channels)
+			info->num_multichan_concurrent = num_channels;
+	}
+
+	return 0;
+}
+
+
+static void wiphy_info_iface_comb(struct wiphy_info_data *info,
+				  struct nlattr *tb)
+{
+	struct nlattr *nl_combi;
+	int rem_combi;
+
+	if (tb == NULL)
+		return;
+
+	nla_for_each_nested(nl_combi, tb, rem_combi) {
+		if (wiphy_info_iface_comb_process(info, nl_combi) > 0)
+			break;
+	}
+}
+
+
+static void wiphy_info_supp_cmds(struct wiphy_info_data *info,
+				 struct nlattr *tb)
+{
+	struct nlattr *nl_cmd;
+	int i;
+
+	if (tb == NULL)
+		return;
+
+	nla_for_each_nested(nl_cmd, tb, i) {
+		switch (nla_get_u32(nl_cmd)) {
+		case NL80211_CMD_AUTHENTICATE:
+			info->auth_supported = 1;
+			break;
+		case NL80211_CMD_CONNECT:
+			info->connect_supported = 1;
+			break;
+		case NL80211_CMD_START_SCHED_SCAN:
+			info->capa->sched_scan_supported = 1;
+			break;
+		case NL80211_CMD_PROBE_CLIENT:
+			info->poll_command_supported = 1;
+			break;
+		case NL80211_CMD_CHANNEL_SWITCH:
+			info->channel_switch_supported = 1;
+			break;
+		case NL80211_CMD_SET_QOS_MAP:
+			info->set_qos_map_supported = 1;
+			break;
+		}
+	}
+}
+
+
+static void wiphy_info_cipher_suites(struct wiphy_info_data *info,
+				     struct nlattr *tb)
+{
+	int i, num;
+	u32 *ciphers;
+
+	if (tb == NULL)
+		return;
+
+	num = nla_len(tb) / sizeof(u32);
+	ciphers = nla_data(tb);
+	for (i = 0; i < num; i++) {
+		u32 c = ciphers[i];
+
+		wpa_printf(MSG_DEBUG, "nl80211: Supported cipher %02x-%02x-%02x:%d",
+			   c >> 24, (c >> 16) & 0xff,
+			   (c >> 8) & 0xff, c & 0xff);
+		switch (c) {
+		case WLAN_CIPHER_SUITE_CCMP_256:
+			info->capa->enc |= WPA_DRIVER_CAPA_ENC_CCMP_256;
+			break;
+		case WLAN_CIPHER_SUITE_GCMP_256:
+			info->capa->enc |= WPA_DRIVER_CAPA_ENC_GCMP_256;
+			break;
+		case WLAN_CIPHER_SUITE_CCMP:
+			info->capa->enc |= WPA_DRIVER_CAPA_ENC_CCMP;
+			break;
+		case WLAN_CIPHER_SUITE_GCMP:
+			info->capa->enc |= WPA_DRIVER_CAPA_ENC_GCMP;
+			break;
+		case WLAN_CIPHER_SUITE_TKIP:
+			info->capa->enc |= WPA_DRIVER_CAPA_ENC_TKIP;
+			break;
+		case WLAN_CIPHER_SUITE_WEP104:
+			info->capa->enc |= WPA_DRIVER_CAPA_ENC_WEP104;
+			break;
+		case WLAN_CIPHER_SUITE_WEP40:
+			info->capa->enc |= WPA_DRIVER_CAPA_ENC_WEP40;
+			break;
+		case WLAN_CIPHER_SUITE_AES_CMAC:
+			info->capa->enc |= WPA_DRIVER_CAPA_ENC_BIP;
+			break;
+		case WLAN_CIPHER_SUITE_BIP_GMAC_128:
+			info->capa->enc |= WPA_DRIVER_CAPA_ENC_BIP_GMAC_128;
+			break;
+		case WLAN_CIPHER_SUITE_BIP_GMAC_256:
+			info->capa->enc |= WPA_DRIVER_CAPA_ENC_BIP_GMAC_256;
+			break;
+		case WLAN_CIPHER_SUITE_BIP_CMAC_256:
+			info->capa->enc |= WPA_DRIVER_CAPA_ENC_BIP_CMAC_256;
+			break;
+		case WLAN_CIPHER_SUITE_NO_GROUP_ADDR:
+			info->capa->enc |= WPA_DRIVER_CAPA_ENC_GTK_NOT_USED;
+			break;
+		}
+	}
+}
+
+
+static void wiphy_info_max_roc(struct wpa_driver_capa *capa,
+			       struct nlattr *tb)
+{
+	if (tb)
+		capa->max_remain_on_chan = nla_get_u32(tb);
+}
+
+
+static void wiphy_info_tdls(struct wpa_driver_capa *capa, struct nlattr *tdls,
+			    struct nlattr *ext_setup)
+{
+	if (tdls == NULL)
+		return;
+
+	wpa_printf(MSG_DEBUG, "nl80211: TDLS supported");
+	capa->flags |= WPA_DRIVER_FLAGS_TDLS_SUPPORT;
+
+	if (ext_setup) {
+		wpa_printf(MSG_DEBUG, "nl80211: TDLS external setup");
+		capa->flags |= WPA_DRIVER_FLAGS_TDLS_EXTERNAL_SETUP;
+	}
+}
+
+
+static int ext_feature_isset(const u8 *ext_features, int ext_features_len,
+			     enum nl80211_ext_feature_index ftidx)
+{
+	u8 ft_byte;
+
+	if ((int) ftidx / 8 >= ext_features_len)
+		return 0;
+
+	ft_byte = ext_features[ftidx / 8];
+	return (ft_byte & BIT(ftidx % 8)) != 0;
+}
+
+
+static void wiphy_info_ext_feature_flags(struct wiphy_info_data *info,
+					 struct nlattr *tb)
+{
+	struct wpa_driver_capa *capa = info->capa;
+
+	if (tb == NULL)
+		return;
+
+	if (ext_feature_isset(nla_data(tb), nla_len(tb),
+			      NL80211_EXT_FEATURE_VHT_IBSS))
+		capa->flags |= WPA_DRIVER_FLAGS_VHT_IBSS;
+}
+
+
+static void wiphy_info_feature_flags(struct wiphy_info_data *info,
+				     struct nlattr *tb)
+{
+	u32 flags;
+	struct wpa_driver_capa *capa = info->capa;
+
+	if (tb == NULL)
+		return;
+
+	flags = nla_get_u32(tb);
+
+	if (flags & NL80211_FEATURE_SK_TX_STATUS)
+		info->data_tx_status = 1;
+
+	if (flags & NL80211_FEATURE_INACTIVITY_TIMER)
+		capa->flags |= WPA_DRIVER_FLAGS_INACTIVITY_TIMER;
+
+	if (flags & NL80211_FEATURE_SAE)
+		capa->flags |= WPA_DRIVER_FLAGS_SAE;
+
+	if (flags & NL80211_FEATURE_NEED_OBSS_SCAN)
+		capa->flags |= WPA_DRIVER_FLAGS_OBSS_SCAN;
+
+	if (flags & NL80211_FEATURE_AP_MODE_CHAN_WIDTH_CHANGE)
+		capa->flags |= WPA_DRIVER_FLAGS_HT_2040_COEX;
+
+	if (flags & NL80211_FEATURE_TDLS_CHANNEL_SWITCH) {
+		wpa_printf(MSG_DEBUG, "nl80211: TDLS channel switch");
+		capa->flags |= WPA_DRIVER_FLAGS_TDLS_CHANNEL_SWITCH;
+	}
+
+	if (flags & NL80211_FEATURE_P2P_GO_CTWIN)
+		info->p2p_go_ctwindow_supported = 1;
+
+	if (flags & NL80211_FEATURE_LOW_PRIORITY_SCAN)
+		info->have_low_prio_scan = 1;
+
+	if (flags & NL80211_FEATURE_SCAN_RANDOM_MAC_ADDR)
+		info->mac_addr_rand_scan_supported = 1;
+
+	if (flags & NL80211_FEATURE_SCHED_SCAN_RANDOM_MAC_ADDR)
+		info->mac_addr_rand_sched_scan_supported = 1;
+
+	if (flags & NL80211_FEATURE_STATIC_SMPS)
+		capa->smps_modes |= WPA_DRIVER_SMPS_MODE_STATIC;
+
+	if (flags & NL80211_FEATURE_DYNAMIC_SMPS)
+		capa->smps_modes |= WPA_DRIVER_SMPS_MODE_DYNAMIC;
+
+	if (flags & NL80211_FEATURE_SUPPORTS_WMM_ADMISSION)
+		info->wmm_ac_supported = 1;
+
+	if (flags & NL80211_FEATURE_DS_PARAM_SET_IE_IN_PROBES)
+		capa->rrm_flags |= WPA_DRIVER_FLAGS_DS_PARAM_SET_IE_IN_PROBES;
+
+	if (flags & NL80211_FEATURE_WFA_TPC_IE_IN_PROBES)
+		capa->rrm_flags |= WPA_DRIVER_FLAGS_WFA_TPC_IE_IN_PROBES;
+
+	if (flags & NL80211_FEATURE_QUIET)
+		capa->rrm_flags |= WPA_DRIVER_FLAGS_QUIET;
+
+	if (flags & NL80211_FEATURE_TX_POWER_INSERTION)
+		capa->rrm_flags |= WPA_DRIVER_FLAGS_TX_POWER_INSERTION;
+
+	if (flags & NL80211_FEATURE_HT_IBSS)
+		capa->flags |= WPA_DRIVER_FLAGS_HT_IBSS;
+}
+
+
+static void wiphy_info_probe_resp_offload(struct wpa_driver_capa *capa,
+					  struct nlattr *tb)
+{
+	u32 protocols;
+
+	if (tb == NULL)
+		return;
+
+	protocols = nla_get_u32(tb);
+	wpa_printf(MSG_DEBUG, "nl80211: Supports Probe Response offload in AP "
+		   "mode");
+	capa->flags |= WPA_DRIVER_FLAGS_PROBE_RESP_OFFLOAD;
+	capa->probe_resp_offloads = probe_resp_offload_support(protocols);
+}
+
+
+static void wiphy_info_wowlan_triggers(struct wpa_driver_capa *capa,
+				       struct nlattr *tb)
+{
+	struct nlattr *triggers[MAX_NL80211_WOWLAN_TRIG + 1];
+
+	if (tb == NULL)
+		return;
+
+	if (nla_parse_nested(triggers, MAX_NL80211_WOWLAN_TRIG,
+			     tb, NULL))
+		return;
+
+	if (triggers[NL80211_WOWLAN_TRIG_ANY])
+		capa->wowlan_triggers.any = 1;
+	if (triggers[NL80211_WOWLAN_TRIG_DISCONNECT])
+		capa->wowlan_triggers.disconnect = 1;
+	if (triggers[NL80211_WOWLAN_TRIG_MAGIC_PKT])
+		capa->wowlan_triggers.magic_pkt = 1;
+	if (triggers[NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE])
+		capa->wowlan_triggers.gtk_rekey_failure = 1;
+	if (triggers[NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST])
+		capa->wowlan_triggers.eap_identity_req = 1;
+	if (triggers[NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE])
+		capa->wowlan_triggers.four_way_handshake = 1;
+	if (triggers[NL80211_WOWLAN_TRIG_RFKILL_RELEASE])
+		capa->wowlan_triggers.rfkill_release = 1;
+}
+
+
+static int wiphy_info_handler(struct nl_msg *msg, void *arg)
+{
+	struct nlattr *tb[NL80211_ATTR_MAX + 1];
+	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+	struct wiphy_info_data *info = arg;
+	struct wpa_driver_capa *capa = info->capa;
+	struct wpa_driver_nl80211_data *drv = info->drv;
+
+	nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+		  genlmsg_attrlen(gnlh, 0), NULL);
+
+	if (tb[NL80211_ATTR_WIPHY_NAME])
+		os_strlcpy(drv->phyname,
+			   nla_get_string(tb[NL80211_ATTR_WIPHY_NAME]),
+			   sizeof(drv->phyname));
+	if (tb[NL80211_ATTR_MAX_NUM_SCAN_SSIDS])
+		capa->max_scan_ssids =
+			nla_get_u8(tb[NL80211_ATTR_MAX_NUM_SCAN_SSIDS]);
+
+	if (tb[NL80211_ATTR_MAX_NUM_SCHED_SCAN_SSIDS])
+		capa->max_sched_scan_ssids =
+			nla_get_u8(tb[NL80211_ATTR_MAX_NUM_SCHED_SCAN_SSIDS]);
+
+	if (tb[NL80211_ATTR_MAX_MATCH_SETS])
+		capa->max_match_sets =
+			nla_get_u8(tb[NL80211_ATTR_MAX_MATCH_SETS]);
+
+	if (tb[NL80211_ATTR_MAC_ACL_MAX])
+		capa->max_acl_mac_addrs =
+			nla_get_u8(tb[NL80211_ATTR_MAC_ACL_MAX]);
+
+	wiphy_info_supported_iftypes(info, tb[NL80211_ATTR_SUPPORTED_IFTYPES]);
+	wiphy_info_iface_comb(info, tb[NL80211_ATTR_INTERFACE_COMBINATIONS]);
+	wiphy_info_supp_cmds(info, tb[NL80211_ATTR_SUPPORTED_COMMANDS]);
+	wiphy_info_cipher_suites(info, tb[NL80211_ATTR_CIPHER_SUITES]);
+
+	if (tb[NL80211_ATTR_OFFCHANNEL_TX_OK]) {
+		wpa_printf(MSG_DEBUG, "nl80211: Using driver-based "
+			   "off-channel TX");
+		capa->flags |= WPA_DRIVER_FLAGS_OFFCHANNEL_TX;
+	}
+
+	if (tb[NL80211_ATTR_ROAM_SUPPORT]) {
+		wpa_printf(MSG_DEBUG, "nl80211: Using driver-based roaming");
+		capa->flags |= WPA_DRIVER_FLAGS_BSS_SELECTION;
+	}
+
+	wiphy_info_max_roc(capa,
+			   tb[NL80211_ATTR_MAX_REMAIN_ON_CHANNEL_DURATION]);
+
+	if (tb[NL80211_ATTR_SUPPORT_AP_UAPSD])
+		capa->flags |= WPA_DRIVER_FLAGS_AP_UAPSD;
+
+	wiphy_info_tdls(capa, tb[NL80211_ATTR_TDLS_SUPPORT],
+			tb[NL80211_ATTR_TDLS_EXTERNAL_SETUP]);
+
+	if (tb[NL80211_ATTR_DEVICE_AP_SME])
+		info->device_ap_sme = 1;
+
+	wiphy_info_feature_flags(info, tb[NL80211_ATTR_FEATURE_FLAGS]);
+	wiphy_info_ext_feature_flags(info, tb[NL80211_ATTR_EXT_FEATURES]);
+	wiphy_info_probe_resp_offload(capa,
+				      tb[NL80211_ATTR_PROBE_RESP_OFFLOAD]);
+
+	if (tb[NL80211_ATTR_EXT_CAPA] && tb[NL80211_ATTR_EXT_CAPA_MASK] &&
+	    drv->extended_capa == NULL) {
+		drv->extended_capa =
+			os_malloc(nla_len(tb[NL80211_ATTR_EXT_CAPA]));
+		if (drv->extended_capa) {
+			os_memcpy(drv->extended_capa,
+				  nla_data(tb[NL80211_ATTR_EXT_CAPA]),
+				  nla_len(tb[NL80211_ATTR_EXT_CAPA]));
+			drv->extended_capa_len =
+				nla_len(tb[NL80211_ATTR_EXT_CAPA]);
+		}
+		drv->extended_capa_mask =
+			os_malloc(nla_len(tb[NL80211_ATTR_EXT_CAPA_MASK]));
+		if (drv->extended_capa_mask) {
+			os_memcpy(drv->extended_capa_mask,
+				  nla_data(tb[NL80211_ATTR_EXT_CAPA_MASK]),
+				  nla_len(tb[NL80211_ATTR_EXT_CAPA_MASK]));
+		} else {
+			os_free(drv->extended_capa);
+			drv->extended_capa = NULL;
+			drv->extended_capa_len = 0;
+		}
+	}
+
+	if (tb[NL80211_ATTR_VENDOR_DATA]) {
+		struct nlattr *nl;
+		int rem;
+
+		nla_for_each_nested(nl, tb[NL80211_ATTR_VENDOR_DATA], rem) {
+			struct nl80211_vendor_cmd_info *vinfo;
+			if (nla_len(nl) != sizeof(*vinfo)) {
+				wpa_printf(MSG_DEBUG, "nl80211: Unexpected vendor data info");
+				continue;
+			}
+			vinfo = nla_data(nl);
+			if (vinfo->vendor_id == OUI_QCA) {
+				switch (vinfo->subcmd) {
+				case QCA_NL80211_VENDOR_SUBCMD_TEST:
+					drv->vendor_cmd_test_avail = 1;
+					break;
+				case QCA_NL80211_VENDOR_SUBCMD_ROAMING:
+					drv->roaming_vendor_cmd_avail = 1;
+					break;
+				case QCA_NL80211_VENDOR_SUBCMD_DFS_CAPABILITY:
+					drv->dfs_vendor_cmd_avail = 1;
+					break;
+				case QCA_NL80211_VENDOR_SUBCMD_GET_FEATURES:
+					drv->get_features_vendor_cmd_avail = 1;
+					break;
+				case QCA_NL80211_VENDOR_SUBCMD_DO_ACS:
+					drv->capa.flags |=
+						WPA_DRIVER_FLAGS_ACS_OFFLOAD;
+					break;
+				}
+			}
+
+			wpa_printf(MSG_DEBUG, "nl80211: Supported vendor command: vendor_id=0x%x subcmd=%u",
+				   vinfo->vendor_id, vinfo->subcmd);
+		}
+	}
+
+	if (tb[NL80211_ATTR_VENDOR_EVENTS]) {
+		struct nlattr *nl;
+		int rem;
+
+		nla_for_each_nested(nl, tb[NL80211_ATTR_VENDOR_EVENTS], rem) {
+			struct nl80211_vendor_cmd_info *vinfo;
+			if (nla_len(nl) != sizeof(*vinfo)) {
+				wpa_printf(MSG_DEBUG, "nl80211: Unexpected vendor data info");
+				continue;
+			}
+			vinfo = nla_data(nl);
+			wpa_printf(MSG_DEBUG, "nl80211: Supported vendor event: vendor_id=0x%x subcmd=%u",
+				   vinfo->vendor_id, vinfo->subcmd);
+		}
+	}
+
+	wiphy_info_wowlan_triggers(capa,
+				   tb[NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED]);
+
+	if (tb[NL80211_ATTR_MAX_AP_ASSOC_STA])
+		capa->max_stations =
+			nla_get_u32(tb[NL80211_ATTR_MAX_AP_ASSOC_STA]);
+
+	return NL_SKIP;
+}
+
+
+static int wpa_driver_nl80211_get_info(struct wpa_driver_nl80211_data *drv,
+				       struct wiphy_info_data *info)
+{
+	u32 feat;
+	struct nl_msg *msg;
+	int flags = 0;
+
+	os_memset(info, 0, sizeof(*info));
+	info->capa = &drv->capa;
+	info->drv = drv;
+
+	feat = get_nl80211_protocol_features(drv);
+	if (feat & NL80211_PROTOCOL_FEATURE_SPLIT_WIPHY_DUMP)
+		flags = NLM_F_DUMP;
+	msg = nl80211_cmd_msg(drv->first_bss, flags, NL80211_CMD_GET_WIPHY);
+	if (!msg || nla_put_flag(msg, NL80211_ATTR_SPLIT_WIPHY_DUMP)) {
+		nlmsg_free(msg);
+		return -1;
+	}
+
+	if (send_and_recv_msgs(drv, msg, wiphy_info_handler, info))
+		return -1;
+
+	if (info->auth_supported)
+		drv->capa.flags |= WPA_DRIVER_FLAGS_SME;
+	else if (!info->connect_supported) {
+		wpa_printf(MSG_INFO, "nl80211: Driver does not support "
+			   "authentication/association or connect commands");
+		info->error = 1;
+	}
+
+	if (info->p2p_go_supported && info->p2p_client_supported)
+		drv->capa.flags |= WPA_DRIVER_FLAGS_P2P_CAPABLE;
+	if (info->p2p_concurrent) {
+		wpa_printf(MSG_DEBUG, "nl80211: Use separate P2P group "
+			   "interface (driver advertised support)");
+		drv->capa.flags |= WPA_DRIVER_FLAGS_P2P_CONCURRENT;
+		drv->capa.flags |= WPA_DRIVER_FLAGS_P2P_MGMT_AND_NON_P2P;
+	}
+	if (info->num_multichan_concurrent > 1) {
+		wpa_printf(MSG_DEBUG, "nl80211: Enable multi-channel "
+			   "concurrent (driver advertised support)");
+		drv->capa.num_multichan_concurrent =
+			info->num_multichan_concurrent;
+	}
+	if (drv->capa.flags & WPA_DRIVER_FLAGS_DEDICATED_P2P_DEVICE)
+		wpa_printf(MSG_DEBUG, "nl80211: use P2P_DEVICE support");
+
+	/* default to 5000 since early versions of mac80211 don't set it */
+	if (!drv->capa.max_remain_on_chan)
+		drv->capa.max_remain_on_chan = 5000;
+
+	if (info->channel_switch_supported)
+		drv->capa.flags |= WPA_DRIVER_FLAGS_AP_CSA;
+	drv->capa.wmm_ac_supported = info->wmm_ac_supported;
+
+	drv->capa.mac_addr_rand_sched_scan_supported =
+		info->mac_addr_rand_sched_scan_supported;
+	drv->capa.mac_addr_rand_scan_supported =
+		info->mac_addr_rand_scan_supported;
+
+	return 0;
+}
+
+
+static int dfs_info_handler(struct nl_msg *msg, void *arg)
+{
+	struct nlattr *tb[NL80211_ATTR_MAX + 1];
+	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+	int *dfs_capability_ptr = arg;
+
+	nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+		  genlmsg_attrlen(gnlh, 0), NULL);
+
+	if (tb[NL80211_ATTR_VENDOR_DATA]) {
+		struct nlattr *nl_vend = tb[NL80211_ATTR_VENDOR_DATA];
+		struct nlattr *tb_vendor[QCA_WLAN_VENDOR_ATTR_MAX + 1];
+
+		nla_parse(tb_vendor, QCA_WLAN_VENDOR_ATTR_MAX,
+			  nla_data(nl_vend), nla_len(nl_vend), NULL);
+
+		if (tb_vendor[QCA_WLAN_VENDOR_ATTR_DFS]) {
+			u32 val;
+			val = nla_get_u32(tb_vendor[QCA_WLAN_VENDOR_ATTR_DFS]);
+			wpa_printf(MSG_DEBUG, "nl80211: DFS offload capability: %u",
+				   val);
+			*dfs_capability_ptr = val;
+		}
+	}
+
+	return NL_SKIP;
+}
+
+
+static void qca_nl80211_check_dfs_capa(struct wpa_driver_nl80211_data *drv)
+{
+	struct nl_msg *msg;
+	int dfs_capability = 0;
+	int ret;
+
+	if (!drv->dfs_vendor_cmd_avail)
+		return;
+
+	if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
+	    nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
+	    nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
+			QCA_NL80211_VENDOR_SUBCMD_DFS_CAPABILITY)) {
+		nlmsg_free(msg);
+		return;
+	}
+
+	ret = send_and_recv_msgs(drv, msg, dfs_info_handler, &dfs_capability);
+	if (!ret && dfs_capability)
+		drv->capa.flags |= WPA_DRIVER_FLAGS_DFS_OFFLOAD;
+}
+
+
+struct features_info {
+	u8 *flags;
+	size_t flags_len;
+};
+
+
+static int features_info_handler(struct nl_msg *msg, void *arg)
+{
+	struct nlattr *tb[NL80211_ATTR_MAX + 1];
+	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+	struct features_info *info = arg;
+	struct nlattr *nl_vend, *attr;
+
+	nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+		  genlmsg_attrlen(gnlh, 0), NULL);
+
+	nl_vend = tb[NL80211_ATTR_VENDOR_DATA];
+	if (nl_vend) {
+		struct nlattr *tb_vendor[QCA_WLAN_VENDOR_ATTR_MAX + 1];
+
+		nla_parse(tb_vendor, QCA_WLAN_VENDOR_ATTR_MAX,
+			  nla_data(nl_vend), nla_len(nl_vend), NULL);
+
+		attr = tb_vendor[QCA_WLAN_VENDOR_ATTR_FEATURE_FLAGS];
+		if (attr) {
+			info->flags = nla_data(attr);
+			info->flags_len = nla_len(attr);
+		}
+	}
+
+	return NL_SKIP;
+}
+
+
+static int check_feature(enum qca_wlan_vendor_features feature,
+			 struct features_info *info)
+{
+	size_t idx = feature / 8;
+
+	return (idx < info->flags_len) &&
+		(info->flags[idx] & BIT(feature % 8));
+}
+
+
+static void qca_nl80211_get_features(struct wpa_driver_nl80211_data *drv)
+{
+	struct nl_msg *msg;
+	struct features_info info;
+	int ret;
+
+	if (!drv->get_features_vendor_cmd_avail)
+		return;
+
+	if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
+	    nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
+	    nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
+			QCA_NL80211_VENDOR_SUBCMD_GET_FEATURES)) {
+		nlmsg_free(msg);
+		return;
+	}
+
+	os_memset(&info, 0, sizeof(info));
+	ret = send_and_recv_msgs(drv, msg, features_info_handler, &info);
+	if (ret || !info.flags)
+		return;
+
+	if (check_feature(QCA_WLAN_VENDOR_FEATURE_KEY_MGMT_OFFLOAD, &info))
+		drv->capa.flags |= WPA_DRIVER_FLAGS_KEY_MGMT_OFFLOAD;
+
+	if (check_feature(QCA_WLAN_VENDOR_FEATURE_SUPPORT_HW_MODE_ANY, &info))
+		drv->capa.flags |= WPA_DRIVER_FLAGS_SUPPORT_HW_MODE_ANY;
+}
+
+
+int wpa_driver_nl80211_capa(struct wpa_driver_nl80211_data *drv)
+{
+	struct wiphy_info_data info;
+	if (wpa_driver_nl80211_get_info(drv, &info))
+		return -1;
+
+	if (info.error)
+		return -1;
+
+	drv->has_capability = 1;
+	drv->capa.key_mgmt = WPA_DRIVER_CAPA_KEY_MGMT_WPA |
+		WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK |
+		WPA_DRIVER_CAPA_KEY_MGMT_WPA2 |
+		WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK |
+		WPA_DRIVER_CAPA_KEY_MGMT_SUITE_B |
+		WPA_DRIVER_CAPA_KEY_MGMT_SUITE_B_192;
+	drv->capa.auth = WPA_DRIVER_AUTH_OPEN |
+		WPA_DRIVER_AUTH_SHARED |
+		WPA_DRIVER_AUTH_LEAP;
+
+	drv->capa.flags |= WPA_DRIVER_FLAGS_SANE_ERROR_CODES;
+	drv->capa.flags |= WPA_DRIVER_FLAGS_SET_KEYS_AFTER_ASSOC_DONE;
+	drv->capa.flags |= WPA_DRIVER_FLAGS_EAPOL_TX_STATUS;
+
+	/*
+	 * As all cfg80211 drivers must support cases where the AP interface is
+	 * removed without the knowledge of wpa_supplicant/hostapd, e.g., in
+	 * case that the user space daemon has crashed, they must be able to
+	 * cleanup all stations and key entries in the AP tear down flow. Thus,
+	 * this flag can/should always be set for cfg80211 drivers.
+	 */
+	drv->capa.flags |= WPA_DRIVER_FLAGS_AP_TEARDOWN_SUPPORT;
+
+	if (!info.device_ap_sme) {
+		drv->capa.flags |= WPA_DRIVER_FLAGS_DEAUTH_TX_STATUS;
+
+		/*
+		 * No AP SME is currently assumed to also indicate no AP MLME
+		 * in the driver/firmware.
+		 */
+		drv->capa.flags |= WPA_DRIVER_FLAGS_AP_MLME;
+	}
+
+	drv->device_ap_sme = info.device_ap_sme;
+	drv->poll_command_supported = info.poll_command_supported;
+	drv->data_tx_status = info.data_tx_status;
+	drv->p2p_go_ctwindow_supported = info.p2p_go_ctwindow_supported;
+	if (info.set_qos_map_supported)
+		drv->capa.flags |= WPA_DRIVER_FLAGS_QOS_MAPPING;
+	drv->have_low_prio_scan = info.have_low_prio_scan;
+
+	/*
+	 * If poll command and tx status are supported, mac80211 is new enough
+	 * to have everything we need to not need monitor interfaces.
+	 */
+	drv->use_monitor = !info.poll_command_supported || !info.data_tx_status;
+
+	if (drv->device_ap_sme && drv->use_monitor) {
+		/*
+		 * Non-mac80211 drivers may not support monitor interface.
+		 * Make sure we do not get stuck with incorrect capability here
+		 * by explicitly testing this.
+		 */
+		if (!info.monitor_supported) {
+			wpa_printf(MSG_DEBUG, "nl80211: Disable use_monitor "
+				   "with device_ap_sme since no monitor mode "
+				   "support detected");
+			drv->use_monitor = 0;
+		}
+	}
+
+	/*
+	 * If we aren't going to use monitor interfaces, but the
+	 * driver doesn't support data TX status, we won't get TX
+	 * status for EAPOL frames.
+	 */
+	if (!drv->use_monitor && !info.data_tx_status)
+		drv->capa.flags &= ~WPA_DRIVER_FLAGS_EAPOL_TX_STATUS;
+
+	qca_nl80211_check_dfs_capa(drv);
+	qca_nl80211_get_features(drv);
+
+	return 0;
+}
+
+
+struct phy_info_arg {
+	u16 *num_modes;
+	struct hostapd_hw_modes *modes;
+	int last_mode, last_chan_idx;
+};
+
+static void phy_info_ht_capa(struct hostapd_hw_modes *mode, struct nlattr *capa,
+			     struct nlattr *ampdu_factor,
+			     struct nlattr *ampdu_density,
+			     struct nlattr *mcs_set)
+{
+	if (capa)
+		mode->ht_capab = nla_get_u16(capa);
+
+	if (ampdu_factor)
+		mode->a_mpdu_params |= nla_get_u8(ampdu_factor) & 0x03;
+
+	if (ampdu_density)
+		mode->a_mpdu_params |= nla_get_u8(ampdu_density) << 2;
+
+	if (mcs_set && nla_len(mcs_set) >= 16) {
+		u8 *mcs;
+		mcs = nla_data(mcs_set);
+		os_memcpy(mode->mcs_set, mcs, 16);
+	}
+}
+
+
+static void phy_info_vht_capa(struct hostapd_hw_modes *mode,
+			      struct nlattr *capa,
+			      struct nlattr *mcs_set)
+{
+	if (capa)
+		mode->vht_capab = nla_get_u32(capa);
+
+	if (mcs_set && nla_len(mcs_set) >= 8) {
+		u8 *mcs;
+		mcs = nla_data(mcs_set);
+		os_memcpy(mode->vht_mcs_set, mcs, 8);
+	}
+}
+
+
+static void phy_info_freq(struct hostapd_hw_modes *mode,
+			  struct hostapd_channel_data *chan,
+			  struct nlattr *tb_freq[])
+{
+	u8 channel;
+	chan->freq = nla_get_u32(tb_freq[NL80211_FREQUENCY_ATTR_FREQ]);
+	chan->flag = 0;
+	chan->dfs_cac_ms = 0;
+	if (ieee80211_freq_to_chan(chan->freq, &channel) != NUM_HOSTAPD_MODES)
+		chan->chan = channel;
+
+	if (tb_freq[NL80211_FREQUENCY_ATTR_DISABLED])
+		chan->flag |= HOSTAPD_CHAN_DISABLED;
+	if (tb_freq[NL80211_FREQUENCY_ATTR_NO_IR])
+		chan->flag |= HOSTAPD_CHAN_NO_IR;
+	if (tb_freq[NL80211_FREQUENCY_ATTR_RADAR])
+		chan->flag |= HOSTAPD_CHAN_RADAR;
+	if (tb_freq[NL80211_FREQUENCY_ATTR_INDOOR_ONLY])
+		chan->flag |= HOSTAPD_CHAN_INDOOR_ONLY;
+	if (tb_freq[NL80211_FREQUENCY_ATTR_GO_CONCURRENT])
+		chan->flag |= HOSTAPD_CHAN_GO_CONCURRENT;
+
+	if (tb_freq[NL80211_FREQUENCY_ATTR_DFS_STATE]) {
+		enum nl80211_dfs_state state =
+			nla_get_u32(tb_freq[NL80211_FREQUENCY_ATTR_DFS_STATE]);
+
+		switch (state) {
+		case NL80211_DFS_USABLE:
+			chan->flag |= HOSTAPD_CHAN_DFS_USABLE;
+			break;
+		case NL80211_DFS_AVAILABLE:
+			chan->flag |= HOSTAPD_CHAN_DFS_AVAILABLE;
+			break;
+		case NL80211_DFS_UNAVAILABLE:
+			chan->flag |= HOSTAPD_CHAN_DFS_UNAVAILABLE;
+			break;
+		}
+	}
+
+	if (tb_freq[NL80211_FREQUENCY_ATTR_DFS_CAC_TIME]) {
+		chan->dfs_cac_ms = nla_get_u32(
+			tb_freq[NL80211_FREQUENCY_ATTR_DFS_CAC_TIME]);
+	}
+}
+
+
+static int phy_info_freqs(struct phy_info_arg *phy_info,
+			  struct hostapd_hw_modes *mode, struct nlattr *tb)
+{
+	static struct nla_policy freq_policy[NL80211_FREQUENCY_ATTR_MAX + 1] = {
+		[NL80211_FREQUENCY_ATTR_FREQ] = { .type = NLA_U32 },
+		[NL80211_FREQUENCY_ATTR_DISABLED] = { .type = NLA_FLAG },
+		[NL80211_FREQUENCY_ATTR_NO_IR] = { .type = NLA_FLAG },
+		[NL80211_FREQUENCY_ATTR_RADAR] = { .type = NLA_FLAG },
+		[NL80211_FREQUENCY_ATTR_MAX_TX_POWER] = { .type = NLA_U32 },
+		[NL80211_FREQUENCY_ATTR_DFS_STATE] = { .type = NLA_U32 },
+	};
+	int new_channels = 0;
+	struct hostapd_channel_data *channel;
+	struct nlattr *tb_freq[NL80211_FREQUENCY_ATTR_MAX + 1];
+	struct nlattr *nl_freq;
+	int rem_freq, idx;
+
+	if (tb == NULL)
+		return NL_OK;
+
+	nla_for_each_nested(nl_freq, tb, rem_freq) {
+		nla_parse(tb_freq, NL80211_FREQUENCY_ATTR_MAX,
+			  nla_data(nl_freq), nla_len(nl_freq), freq_policy);
+		if (!tb_freq[NL80211_FREQUENCY_ATTR_FREQ])
+			continue;
+		new_channels++;
+	}
+
+	channel = os_realloc_array(mode->channels,
+				   mode->num_channels + new_channels,
+				   sizeof(struct hostapd_channel_data));
+	if (!channel)
+		return NL_SKIP;
+
+	mode->channels = channel;
+	mode->num_channels += new_channels;
+
+	idx = phy_info->last_chan_idx;
+
+	nla_for_each_nested(nl_freq, tb, rem_freq) {
+		nla_parse(tb_freq, NL80211_FREQUENCY_ATTR_MAX,
+			  nla_data(nl_freq), nla_len(nl_freq), freq_policy);
+		if (!tb_freq[NL80211_FREQUENCY_ATTR_FREQ])
+			continue;
+		phy_info_freq(mode, &mode->channels[idx], tb_freq);
+		idx++;
+	}
+	phy_info->last_chan_idx = idx;
+
+	return NL_OK;
+}
+
+
+static int phy_info_rates(struct hostapd_hw_modes *mode, struct nlattr *tb)
+{
+	static struct nla_policy rate_policy[NL80211_BITRATE_ATTR_MAX + 1] = {
+		[NL80211_BITRATE_ATTR_RATE] = { .type = NLA_U32 },
+		[NL80211_BITRATE_ATTR_2GHZ_SHORTPREAMBLE] =
+		{ .type = NLA_FLAG },
+	};
+	struct nlattr *tb_rate[NL80211_BITRATE_ATTR_MAX + 1];
+	struct nlattr *nl_rate;
+	int rem_rate, idx;
+
+	if (tb == NULL)
+		return NL_OK;
+
+	nla_for_each_nested(nl_rate, tb, rem_rate) {
+		nla_parse(tb_rate, NL80211_BITRATE_ATTR_MAX,
+			  nla_data(nl_rate), nla_len(nl_rate),
+			  rate_policy);
+		if (!tb_rate[NL80211_BITRATE_ATTR_RATE])
+			continue;
+		mode->num_rates++;
+	}
+
+	mode->rates = os_calloc(mode->num_rates, sizeof(int));
+	if (!mode->rates)
+		return NL_SKIP;
+
+	idx = 0;
+
+	nla_for_each_nested(nl_rate, tb, rem_rate) {
+		nla_parse(tb_rate, NL80211_BITRATE_ATTR_MAX,
+			  nla_data(nl_rate), nla_len(nl_rate),
+			  rate_policy);
+		if (!tb_rate[NL80211_BITRATE_ATTR_RATE])
+			continue;
+		mode->rates[idx] = nla_get_u32(
+			tb_rate[NL80211_BITRATE_ATTR_RATE]);
+		idx++;
+	}
+
+	return NL_OK;
+}
+
+
+static int phy_info_band(struct phy_info_arg *phy_info, struct nlattr *nl_band)
+{
+	struct nlattr *tb_band[NL80211_BAND_ATTR_MAX + 1];
+	struct hostapd_hw_modes *mode;
+	int ret;
+
+	if (phy_info->last_mode != nl_band->nla_type) {
+		mode = os_realloc_array(phy_info->modes,
+					*phy_info->num_modes + 1,
+					sizeof(*mode));
+		if (!mode)
+			return NL_SKIP;
+		phy_info->modes = mode;
+
+		mode = &phy_info->modes[*(phy_info->num_modes)];
+		os_memset(mode, 0, sizeof(*mode));
+		mode->mode = NUM_HOSTAPD_MODES;
+		mode->flags = HOSTAPD_MODE_FLAG_HT_INFO_KNOWN |
+			HOSTAPD_MODE_FLAG_VHT_INFO_KNOWN;
+
+		/*
+		 * Unsupported VHT MCS stream is defined as value 3, so the VHT
+		 * MCS RX/TX map must be initialized with 0xffff to mark all 8
+		 * possible streams as unsupported. This will be overridden if
+		 * driver advertises VHT support.
+		 */
+		mode->vht_mcs_set[0] = 0xff;
+		mode->vht_mcs_set[1] = 0xff;
+		mode->vht_mcs_set[4] = 0xff;
+		mode->vht_mcs_set[5] = 0xff;
+
+		*(phy_info->num_modes) += 1;
+		phy_info->last_mode = nl_band->nla_type;
+		phy_info->last_chan_idx = 0;
+	} else
+		mode = &phy_info->modes[*(phy_info->num_modes) - 1];
+
+	nla_parse(tb_band, NL80211_BAND_ATTR_MAX, nla_data(nl_band),
+		  nla_len(nl_band), NULL);
+
+	phy_info_ht_capa(mode, tb_band[NL80211_BAND_ATTR_HT_CAPA],
+			 tb_band[NL80211_BAND_ATTR_HT_AMPDU_FACTOR],
+			 tb_band[NL80211_BAND_ATTR_HT_AMPDU_DENSITY],
+			 tb_band[NL80211_BAND_ATTR_HT_MCS_SET]);
+	phy_info_vht_capa(mode, tb_band[NL80211_BAND_ATTR_VHT_CAPA],
+			  tb_band[NL80211_BAND_ATTR_VHT_MCS_SET]);
+	ret = phy_info_freqs(phy_info, mode, tb_band[NL80211_BAND_ATTR_FREQS]);
+	if (ret != NL_OK)
+		return ret;
+	ret = phy_info_rates(mode, tb_band[NL80211_BAND_ATTR_RATES]);
+	if (ret != NL_OK)
+		return ret;
+
+	return NL_OK;
+}
+
+
+static int phy_info_handler(struct nl_msg *msg, void *arg)
+{
+	struct nlattr *tb_msg[NL80211_ATTR_MAX + 1];
+	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+	struct phy_info_arg *phy_info = arg;
+	struct nlattr *nl_band;
+	int rem_band;
+
+	nla_parse(tb_msg, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+		  genlmsg_attrlen(gnlh, 0), NULL);
+
+	if (!tb_msg[NL80211_ATTR_WIPHY_BANDS])
+		return NL_SKIP;
+
+	nla_for_each_nested(nl_band, tb_msg[NL80211_ATTR_WIPHY_BANDS], rem_band)
+	{
+		int res = phy_info_band(phy_info, nl_band);
+		if (res != NL_OK)
+			return res;
+	}
+
+	return NL_SKIP;
+}
+
+
+static struct hostapd_hw_modes *
+wpa_driver_nl80211_postprocess_modes(struct hostapd_hw_modes *modes,
+				     u16 *num_modes)
+{
+	u16 m;
+	struct hostapd_hw_modes *mode11g = NULL, *nmodes, *mode;
+	int i, mode11g_idx = -1;
+
+	/* heuristic to set up modes */
+	for (m = 0; m < *num_modes; m++) {
+		if (!modes[m].num_channels)
+			continue;
+		if (modes[m].channels[0].freq < 4000) {
+			modes[m].mode = HOSTAPD_MODE_IEEE80211B;
+			for (i = 0; i < modes[m].num_rates; i++) {
+				if (modes[m].rates[i] > 200) {
+					modes[m].mode = HOSTAPD_MODE_IEEE80211G;
+					break;
+				}
+			}
+		} else if (modes[m].channels[0].freq > 50000)
+			modes[m].mode = HOSTAPD_MODE_IEEE80211AD;
+		else
+			modes[m].mode = HOSTAPD_MODE_IEEE80211A;
+	}
+
+	/* If only 802.11g mode is included, use it to construct matching
+	 * 802.11b mode data. */
+
+	for (m = 0; m < *num_modes; m++) {
+		if (modes[m].mode == HOSTAPD_MODE_IEEE80211B)
+			return modes; /* 802.11b already included */
+		if (modes[m].mode == HOSTAPD_MODE_IEEE80211G)
+			mode11g_idx = m;
+	}
+
+	if (mode11g_idx < 0)
+		return modes; /* 2.4 GHz band not supported at all */
+
+	nmodes = os_realloc_array(modes, *num_modes + 1, sizeof(*nmodes));
+	if (nmodes == NULL)
+		return modes; /* Could not add 802.11b mode */
+
+	mode = &nmodes[*num_modes];
+	os_memset(mode, 0, sizeof(*mode));
+	(*num_modes)++;
+	modes = nmodes;
+
+	mode->mode = HOSTAPD_MODE_IEEE80211B;
+
+	mode11g = &modes[mode11g_idx];
+	mode->num_channels = mode11g->num_channels;
+	mode->channels = os_malloc(mode11g->num_channels *
+				   sizeof(struct hostapd_channel_data));
+	if (mode->channels == NULL) {
+		(*num_modes)--;
+		return modes; /* Could not add 802.11b mode */
+	}
+	os_memcpy(mode->channels, mode11g->channels,
+		  mode11g->num_channels * sizeof(struct hostapd_channel_data));
+
+	mode->num_rates = 0;
+	mode->rates = os_malloc(4 * sizeof(int));
+	if (mode->rates == NULL) {
+		os_free(mode->channels);
+		(*num_modes)--;
+		return modes; /* Could not add 802.11b mode */
+	}
+
+	for (i = 0; i < mode11g->num_rates; i++) {
+		if (mode11g->rates[i] != 10 && mode11g->rates[i] != 20 &&
+		    mode11g->rates[i] != 55 && mode11g->rates[i] != 110)
+			continue;
+		mode->rates[mode->num_rates] = mode11g->rates[i];
+		mode->num_rates++;
+		if (mode->num_rates == 4)
+			break;
+	}
+
+	if (mode->num_rates == 0) {
+		os_free(mode->channels);
+		os_free(mode->rates);
+		(*num_modes)--;
+		return modes; /* No 802.11b rates */
+	}
+
+	wpa_printf(MSG_DEBUG, "nl80211: Added 802.11b mode based on 802.11g "
+		   "information");
+
+	return modes;
+}
+
+
+static void nl80211_set_ht40_mode(struct hostapd_hw_modes *mode, int start,
+				  int end)
+{
+	int c;
+
+	for (c = 0; c < mode->num_channels; c++) {
+		struct hostapd_channel_data *chan = &mode->channels[c];
+		if (chan->freq - 10 >= start && chan->freq + 10 <= end)
+			chan->flag |= HOSTAPD_CHAN_HT40;
+	}
+}
+
+
+static void nl80211_set_ht40_mode_sec(struct hostapd_hw_modes *mode, int start,
+				      int end)
+{
+	int c;
+
+	for (c = 0; c < mode->num_channels; c++) {
+		struct hostapd_channel_data *chan = &mode->channels[c];
+		if (!(chan->flag & HOSTAPD_CHAN_HT40))
+			continue;
+		if (chan->freq - 30 >= start && chan->freq - 10 <= end)
+			chan->flag |= HOSTAPD_CHAN_HT40MINUS;
+		if (chan->freq + 10 >= start && chan->freq + 30 <= end)
+			chan->flag |= HOSTAPD_CHAN_HT40PLUS;
+	}
+}
+
+
+static void nl80211_reg_rule_max_eirp(u32 start, u32 end, u32 max_eirp,
+				      struct phy_info_arg *results)
+{
+	u16 m;
+
+	for (m = 0; m < *results->num_modes; m++) {
+		int c;
+		struct hostapd_hw_modes *mode = &results->modes[m];
+
+		for (c = 0; c < mode->num_channels; c++) {
+			struct hostapd_channel_data *chan = &mode->channels[c];
+			if ((u32) chan->freq - 10 >= start &&
+			    (u32) chan->freq + 10 <= end)
+				chan->max_tx_power = max_eirp;
+		}
+	}
+}
+
+
+static void nl80211_reg_rule_ht40(u32 start, u32 end,
+				  struct phy_info_arg *results)
+{
+	u16 m;
+
+	for (m = 0; m < *results->num_modes; m++) {
+		if (!(results->modes[m].ht_capab &
+		      HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET))
+			continue;
+		nl80211_set_ht40_mode(&results->modes[m], start, end);
+	}
+}
+
+
+static void nl80211_reg_rule_sec(struct nlattr *tb[],
+				 struct phy_info_arg *results)
+{
+	u32 start, end, max_bw;
+	u16 m;
+
+	if (tb[NL80211_ATTR_FREQ_RANGE_START] == NULL ||
+	    tb[NL80211_ATTR_FREQ_RANGE_END] == NULL ||
+	    tb[NL80211_ATTR_FREQ_RANGE_MAX_BW] == NULL)
+		return;
+
+	start = nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_START]) / 1000;
+	end = nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_END]) / 1000;
+	max_bw = nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_MAX_BW]) / 1000;
+
+	if (max_bw < 20)
+		return;
+
+	for (m = 0; m < *results->num_modes; m++) {
+		if (!(results->modes[m].ht_capab &
+		      HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET))
+			continue;
+		nl80211_set_ht40_mode_sec(&results->modes[m], start, end);
+	}
+}
+
+
+static void nl80211_set_vht_mode(struct hostapd_hw_modes *mode, int start,
+				 int end)
+{
+	int c;
+
+	for (c = 0; c < mode->num_channels; c++) {
+		struct hostapd_channel_data *chan = &mode->channels[c];
+		if (chan->freq - 10 >= start && chan->freq + 70 <= end)
+			chan->flag |= HOSTAPD_CHAN_VHT_10_70;
+
+		if (chan->freq - 30 >= start && chan->freq + 50 <= end)
+			chan->flag |= HOSTAPD_CHAN_VHT_30_50;
+
+		if (chan->freq - 50 >= start && chan->freq + 30 <= end)
+			chan->flag |= HOSTAPD_CHAN_VHT_50_30;
+
+		if (chan->freq - 70 >= start && chan->freq + 10 <= end)
+			chan->flag |= HOSTAPD_CHAN_VHT_70_10;
+	}
+}
+
+
+static void nl80211_reg_rule_vht(struct nlattr *tb[],
+				 struct phy_info_arg *results)
+{
+	u32 start, end, max_bw;
+	u16 m;
+
+	if (tb[NL80211_ATTR_FREQ_RANGE_START] == NULL ||
+	    tb[NL80211_ATTR_FREQ_RANGE_END] == NULL ||
+	    tb[NL80211_ATTR_FREQ_RANGE_MAX_BW] == NULL)
+		return;
+
+	start = nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_START]) / 1000;
+	end = nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_END]) / 1000;
+	max_bw = nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_MAX_BW]) / 1000;
+
+	if (max_bw < 80)
+		return;
+
+	for (m = 0; m < *results->num_modes; m++) {
+		if (!(results->modes[m].ht_capab &
+		      HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET))
+			continue;
+		/* TODO: use a real VHT support indication */
+		if (!results->modes[m].vht_capab)
+			continue;
+
+		nl80211_set_vht_mode(&results->modes[m], start, end);
+	}
+}
+
+
+static const char * dfs_domain_name(enum nl80211_dfs_regions region)
+{
+	switch (region) {
+	case NL80211_DFS_UNSET:
+		return "DFS-UNSET";
+	case NL80211_DFS_FCC:
+		return "DFS-FCC";
+	case NL80211_DFS_ETSI:
+		return "DFS-ETSI";
+	case NL80211_DFS_JP:
+		return "DFS-JP";
+	default:
+		return "DFS-invalid";
+	}
+}
+
+
+static int nl80211_get_reg(struct nl_msg *msg, void *arg)
+{
+	struct phy_info_arg *results = arg;
+	struct nlattr *tb_msg[NL80211_ATTR_MAX + 1];
+	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+	struct nlattr *nl_rule;
+	struct nlattr *tb_rule[NL80211_FREQUENCY_ATTR_MAX + 1];
+	int rem_rule;
+	static struct nla_policy reg_policy[NL80211_FREQUENCY_ATTR_MAX + 1] = {
+		[NL80211_ATTR_REG_RULE_FLAGS] = { .type = NLA_U32 },
+		[NL80211_ATTR_FREQ_RANGE_START] = { .type = NLA_U32 },
+		[NL80211_ATTR_FREQ_RANGE_END] = { .type = NLA_U32 },
+		[NL80211_ATTR_FREQ_RANGE_MAX_BW] = { .type = NLA_U32 },
+		[NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN] = { .type = NLA_U32 },
+		[NL80211_ATTR_POWER_RULE_MAX_EIRP] = { .type = NLA_U32 },
+	};
+
+	nla_parse(tb_msg, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+		  genlmsg_attrlen(gnlh, 0), NULL);
+	if (!tb_msg[NL80211_ATTR_REG_ALPHA2] ||
+	    !tb_msg[NL80211_ATTR_REG_RULES]) {
+		wpa_printf(MSG_DEBUG, "nl80211: No regulatory information "
+			   "available");
+		return NL_SKIP;
+	}
+
+	if (tb_msg[NL80211_ATTR_DFS_REGION]) {
+		enum nl80211_dfs_regions dfs_domain;
+		dfs_domain = nla_get_u8(tb_msg[NL80211_ATTR_DFS_REGION]);
+		wpa_printf(MSG_DEBUG, "nl80211: Regulatory information - country=%s (%s)",
+			   (char *) nla_data(tb_msg[NL80211_ATTR_REG_ALPHA2]),
+			   dfs_domain_name(dfs_domain));
+	} else {
+		wpa_printf(MSG_DEBUG, "nl80211: Regulatory information - country=%s",
+			   (char *) nla_data(tb_msg[NL80211_ATTR_REG_ALPHA2]));
+	}
+
+	nla_for_each_nested(nl_rule, tb_msg[NL80211_ATTR_REG_RULES], rem_rule)
+	{
+		u32 start, end, max_eirp = 0, max_bw = 0, flags = 0;
+		nla_parse(tb_rule, NL80211_FREQUENCY_ATTR_MAX,
+			  nla_data(nl_rule), nla_len(nl_rule), reg_policy);
+		if (tb_rule[NL80211_ATTR_FREQ_RANGE_START] == NULL ||
+		    tb_rule[NL80211_ATTR_FREQ_RANGE_END] == NULL)
+			continue;
+		start = nla_get_u32(tb_rule[NL80211_ATTR_FREQ_RANGE_START]) / 1000;
+		end = nla_get_u32(tb_rule[NL80211_ATTR_FREQ_RANGE_END]) / 1000;
+		if (tb_rule[NL80211_ATTR_POWER_RULE_MAX_EIRP])
+			max_eirp = nla_get_u32(tb_rule[NL80211_ATTR_POWER_RULE_MAX_EIRP]) / 100;
+		if (tb_rule[NL80211_ATTR_FREQ_RANGE_MAX_BW])
+			max_bw = nla_get_u32(tb_rule[NL80211_ATTR_FREQ_RANGE_MAX_BW]) / 1000;
+		if (tb_rule[NL80211_ATTR_REG_RULE_FLAGS])
+			flags = nla_get_u32(tb_rule[NL80211_ATTR_REG_RULE_FLAGS]);
+
+		wpa_printf(MSG_DEBUG, "nl80211: %u-%u @ %u MHz %u mBm%s%s%s%s%s%s%s%s",
+			   start, end, max_bw, max_eirp,
+			   flags & NL80211_RRF_NO_OFDM ? " (no OFDM)" : "",
+			   flags & NL80211_RRF_NO_CCK ? " (no CCK)" : "",
+			   flags & NL80211_RRF_NO_INDOOR ? " (no indoor)" : "",
+			   flags & NL80211_RRF_NO_OUTDOOR ? " (no outdoor)" :
+			   "",
+			   flags & NL80211_RRF_DFS ? " (DFS)" : "",
+			   flags & NL80211_RRF_PTP_ONLY ? " (PTP only)" : "",
+			   flags & NL80211_RRF_PTMP_ONLY ? " (PTMP only)" : "",
+			   flags & NL80211_RRF_NO_IR ? " (no IR)" : "");
+		if (max_bw >= 40)
+			nl80211_reg_rule_ht40(start, end, results);
+		if (tb_rule[NL80211_ATTR_POWER_RULE_MAX_EIRP])
+			nl80211_reg_rule_max_eirp(start, end, max_eirp,
+						  results);
+	}
+
+	nla_for_each_nested(nl_rule, tb_msg[NL80211_ATTR_REG_RULES], rem_rule)
+	{
+		nla_parse(tb_rule, NL80211_FREQUENCY_ATTR_MAX,
+			  nla_data(nl_rule), nla_len(nl_rule), reg_policy);
+		nl80211_reg_rule_sec(tb_rule, results);
+	}
+
+	nla_for_each_nested(nl_rule, tb_msg[NL80211_ATTR_REG_RULES], rem_rule)
+	{
+		nla_parse(tb_rule, NL80211_FREQUENCY_ATTR_MAX,
+			  nla_data(nl_rule), nla_len(nl_rule), reg_policy);
+		nl80211_reg_rule_vht(tb_rule, results);
+	}
+
+	return NL_SKIP;
+}
+
+
+static int nl80211_set_regulatory_flags(struct wpa_driver_nl80211_data *drv,
+					struct phy_info_arg *results)
+{
+	struct nl_msg *msg;
+
+	msg = nlmsg_alloc();
+	if (!msg)
+		return -ENOMEM;
+
+	nl80211_cmd(drv, msg, 0, NL80211_CMD_GET_REG);
+	return send_and_recv_msgs(drv, msg, nl80211_get_reg, results);
+}
+
+
+struct hostapd_hw_modes *
+nl80211_get_hw_feature_data(void *priv, u16 *num_modes, u16 *flags)
+{
+	u32 feat;
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	int nl_flags = 0;
+	struct nl_msg *msg;
+	struct phy_info_arg result = {
+		.num_modes = num_modes,
+		.modes = NULL,
+		.last_mode = -1,
+	};
+
+	*num_modes = 0;
+	*flags = 0;
+
+	feat = get_nl80211_protocol_features(drv);
+	if (feat & NL80211_PROTOCOL_FEATURE_SPLIT_WIPHY_DUMP)
+		nl_flags = NLM_F_DUMP;
+	if (!(msg = nl80211_cmd_msg(bss, nl_flags, NL80211_CMD_GET_WIPHY)) ||
+	    nla_put_flag(msg, NL80211_ATTR_SPLIT_WIPHY_DUMP)) {
+		nlmsg_free(msg);
+		return NULL;
+	}
+
+	if (send_and_recv_msgs(drv, msg, phy_info_handler, &result) == 0) {
+		nl80211_set_regulatory_flags(drv, &result);
+		return wpa_driver_nl80211_postprocess_modes(result.modes,
+							    num_modes);
+	}
+
+	return NULL;
+}
diff --git a/src/drivers/driver_nl80211_event.c b/src/drivers/driver_nl80211_event.c
new file mode 100644
index 0000000..7b0f721
--- /dev/null
+++ b/src/drivers/driver_nl80211_event.c
@@ -0,0 +1,2092 @@
+/*
+ * Driver interaction with Linux nl80211/cfg80211 - Event processing
+ * Copyright (c) 2002-2014, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2007, Johannes Berg <johannes@sipsolutions.net>
+ * Copyright (c) 2009-2010, Atheros Communications
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+#include <netlink/genl/genl.h>
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "common/qca-vendor.h"
+#include "common/qca-vendor-attr.h"
+#include "common/ieee802_11_defs.h"
+#include "common/ieee802_11_common.h"
+#include "driver_nl80211.h"
+
+
+static const char * nl80211_command_to_string(enum nl80211_commands cmd)
+{
+#define C2S(x) case x: return #x;
+	switch (cmd) {
+	C2S(NL80211_CMD_UNSPEC)
+	C2S(NL80211_CMD_GET_WIPHY)
+	C2S(NL80211_CMD_SET_WIPHY)
+	C2S(NL80211_CMD_NEW_WIPHY)
+	C2S(NL80211_CMD_DEL_WIPHY)
+	C2S(NL80211_CMD_GET_INTERFACE)
+	C2S(NL80211_CMD_SET_INTERFACE)
+	C2S(NL80211_CMD_NEW_INTERFACE)
+	C2S(NL80211_CMD_DEL_INTERFACE)
+	C2S(NL80211_CMD_GET_KEY)
+	C2S(NL80211_CMD_SET_KEY)
+	C2S(NL80211_CMD_NEW_KEY)
+	C2S(NL80211_CMD_DEL_KEY)
+	C2S(NL80211_CMD_GET_BEACON)
+	C2S(NL80211_CMD_SET_BEACON)
+	C2S(NL80211_CMD_START_AP)
+	C2S(NL80211_CMD_STOP_AP)
+	C2S(NL80211_CMD_GET_STATION)
+	C2S(NL80211_CMD_SET_STATION)
+	C2S(NL80211_CMD_NEW_STATION)
+	C2S(NL80211_CMD_DEL_STATION)
+	C2S(NL80211_CMD_GET_MPATH)
+	C2S(NL80211_CMD_SET_MPATH)
+	C2S(NL80211_CMD_NEW_MPATH)
+	C2S(NL80211_CMD_DEL_MPATH)
+	C2S(NL80211_CMD_SET_BSS)
+	C2S(NL80211_CMD_SET_REG)
+	C2S(NL80211_CMD_REQ_SET_REG)
+	C2S(NL80211_CMD_GET_MESH_CONFIG)
+	C2S(NL80211_CMD_SET_MESH_CONFIG)
+	C2S(NL80211_CMD_SET_MGMT_EXTRA_IE)
+	C2S(NL80211_CMD_GET_REG)
+	C2S(NL80211_CMD_GET_SCAN)
+	C2S(NL80211_CMD_TRIGGER_SCAN)
+	C2S(NL80211_CMD_NEW_SCAN_RESULTS)
+	C2S(NL80211_CMD_SCAN_ABORTED)
+	C2S(NL80211_CMD_REG_CHANGE)
+	C2S(NL80211_CMD_AUTHENTICATE)
+	C2S(NL80211_CMD_ASSOCIATE)
+	C2S(NL80211_CMD_DEAUTHENTICATE)
+	C2S(NL80211_CMD_DISASSOCIATE)
+	C2S(NL80211_CMD_MICHAEL_MIC_FAILURE)
+	C2S(NL80211_CMD_REG_BEACON_HINT)
+	C2S(NL80211_CMD_JOIN_IBSS)
+	C2S(NL80211_CMD_LEAVE_IBSS)
+	C2S(NL80211_CMD_TESTMODE)
+	C2S(NL80211_CMD_CONNECT)
+	C2S(NL80211_CMD_ROAM)
+	C2S(NL80211_CMD_DISCONNECT)
+	C2S(NL80211_CMD_SET_WIPHY_NETNS)
+	C2S(NL80211_CMD_GET_SURVEY)
+	C2S(NL80211_CMD_NEW_SURVEY_RESULTS)
+	C2S(NL80211_CMD_SET_PMKSA)
+	C2S(NL80211_CMD_DEL_PMKSA)
+	C2S(NL80211_CMD_FLUSH_PMKSA)
+	C2S(NL80211_CMD_REMAIN_ON_CHANNEL)
+	C2S(NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL)
+	C2S(NL80211_CMD_SET_TX_BITRATE_MASK)
+	C2S(NL80211_CMD_REGISTER_FRAME)
+	C2S(NL80211_CMD_FRAME)
+	C2S(NL80211_CMD_FRAME_TX_STATUS)
+	C2S(NL80211_CMD_SET_POWER_SAVE)
+	C2S(NL80211_CMD_GET_POWER_SAVE)
+	C2S(NL80211_CMD_SET_CQM)
+	C2S(NL80211_CMD_NOTIFY_CQM)
+	C2S(NL80211_CMD_SET_CHANNEL)
+	C2S(NL80211_CMD_SET_WDS_PEER)
+	C2S(NL80211_CMD_FRAME_WAIT_CANCEL)
+	C2S(NL80211_CMD_JOIN_MESH)
+	C2S(NL80211_CMD_LEAVE_MESH)
+	C2S(NL80211_CMD_UNPROT_DEAUTHENTICATE)
+	C2S(NL80211_CMD_UNPROT_DISASSOCIATE)
+	C2S(NL80211_CMD_NEW_PEER_CANDIDATE)
+	C2S(NL80211_CMD_GET_WOWLAN)
+	C2S(NL80211_CMD_SET_WOWLAN)
+	C2S(NL80211_CMD_START_SCHED_SCAN)
+	C2S(NL80211_CMD_STOP_SCHED_SCAN)
+	C2S(NL80211_CMD_SCHED_SCAN_RESULTS)
+	C2S(NL80211_CMD_SCHED_SCAN_STOPPED)
+	C2S(NL80211_CMD_SET_REKEY_OFFLOAD)
+	C2S(NL80211_CMD_PMKSA_CANDIDATE)
+	C2S(NL80211_CMD_TDLS_OPER)
+	C2S(NL80211_CMD_TDLS_MGMT)
+	C2S(NL80211_CMD_UNEXPECTED_FRAME)
+	C2S(NL80211_CMD_PROBE_CLIENT)
+	C2S(NL80211_CMD_REGISTER_BEACONS)
+	C2S(NL80211_CMD_UNEXPECTED_4ADDR_FRAME)
+	C2S(NL80211_CMD_SET_NOACK_MAP)
+	C2S(NL80211_CMD_CH_SWITCH_NOTIFY)
+	C2S(NL80211_CMD_START_P2P_DEVICE)
+	C2S(NL80211_CMD_STOP_P2P_DEVICE)
+	C2S(NL80211_CMD_CONN_FAILED)
+	C2S(NL80211_CMD_SET_MCAST_RATE)
+	C2S(NL80211_CMD_SET_MAC_ACL)
+	C2S(NL80211_CMD_RADAR_DETECT)
+	C2S(NL80211_CMD_GET_PROTOCOL_FEATURES)
+	C2S(NL80211_CMD_UPDATE_FT_IES)
+	C2S(NL80211_CMD_FT_EVENT)
+	C2S(NL80211_CMD_CRIT_PROTOCOL_START)
+	C2S(NL80211_CMD_CRIT_PROTOCOL_STOP)
+	C2S(NL80211_CMD_GET_COALESCE)
+	C2S(NL80211_CMD_SET_COALESCE)
+	C2S(NL80211_CMD_CHANNEL_SWITCH)
+	C2S(NL80211_CMD_VENDOR)
+	C2S(NL80211_CMD_SET_QOS_MAP)
+	C2S(NL80211_CMD_ADD_TX_TS)
+	C2S(NL80211_CMD_DEL_TX_TS)
+	default:
+		return "NL80211_CMD_UNKNOWN";
+	}
+#undef C2S
+}
+
+
+static void mlme_event_auth(struct wpa_driver_nl80211_data *drv,
+			    const u8 *frame, size_t len)
+{
+	const struct ieee80211_mgmt *mgmt;
+	union wpa_event_data event;
+
+	if (!(drv->capa.flags & WPA_DRIVER_FLAGS_SME) &&
+	    drv->force_connect_cmd) {
+		/*
+		 * Avoid reporting two association events that would confuse
+		 * the core code.
+		 */
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: Ignore auth event when using driver SME");
+		return;
+	}
+
+	wpa_printf(MSG_DEBUG, "nl80211: Authenticate event");
+	mgmt = (const struct ieee80211_mgmt *) frame;
+	if (len < 24 + sizeof(mgmt->u.auth)) {
+		wpa_printf(MSG_DEBUG, "nl80211: Too short association event "
+			   "frame");
+		return;
+	}
+
+	os_memcpy(drv->auth_bssid, mgmt->sa, ETH_ALEN);
+	os_memset(drv->auth_attempt_bssid, 0, ETH_ALEN);
+	os_memset(&event, 0, sizeof(event));
+	os_memcpy(event.auth.peer, mgmt->sa, ETH_ALEN);
+	event.auth.auth_type = le_to_host16(mgmt->u.auth.auth_alg);
+	event.auth.auth_transaction =
+		le_to_host16(mgmt->u.auth.auth_transaction);
+	event.auth.status_code = le_to_host16(mgmt->u.auth.status_code);
+	if (len > 24 + sizeof(mgmt->u.auth)) {
+		event.auth.ies = mgmt->u.auth.variable;
+		event.auth.ies_len = len - 24 - sizeof(mgmt->u.auth);
+	}
+
+	wpa_supplicant_event(drv->ctx, EVENT_AUTH, &event);
+}
+
+
+static void nl80211_parse_wmm_params(struct nlattr *wmm_attr,
+				     struct wmm_params *wmm_params)
+{
+	struct nlattr *wmm_info[NL80211_STA_WME_MAX + 1];
+	static struct nla_policy wme_policy[NL80211_STA_WME_MAX + 1] = {
+		[NL80211_STA_WME_UAPSD_QUEUES] = { .type = NLA_U8 },
+	};
+
+	if (!wmm_attr ||
+	    nla_parse_nested(wmm_info, NL80211_STA_WME_MAX, wmm_attr,
+			     wme_policy) ||
+	    !wmm_info[NL80211_STA_WME_UAPSD_QUEUES])
+		return;
+
+	wmm_params->uapsd_queues =
+		nla_get_u8(wmm_info[NL80211_STA_WME_UAPSD_QUEUES]);
+	wmm_params->info_bitmap |= WMM_PARAMS_UAPSD_QUEUES_INFO;
+}
+
+
+static void mlme_event_assoc(struct wpa_driver_nl80211_data *drv,
+			    const u8 *frame, size_t len, struct nlattr *wmm)
+{
+	const struct ieee80211_mgmt *mgmt;
+	union wpa_event_data event;
+	u16 status;
+
+	if (!(drv->capa.flags & WPA_DRIVER_FLAGS_SME) &&
+	    drv->force_connect_cmd) {
+		/*
+		 * Avoid reporting two association events that would confuse
+		 * the core code.
+		 */
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: Ignore assoc event when using driver SME");
+		return;
+	}
+
+	wpa_printf(MSG_DEBUG, "nl80211: Associate event");
+	mgmt = (const struct ieee80211_mgmt *) frame;
+	if (len < 24 + sizeof(mgmt->u.assoc_resp)) {
+		wpa_printf(MSG_DEBUG, "nl80211: Too short association event "
+			   "frame");
+		return;
+	}
+
+	status = le_to_host16(mgmt->u.assoc_resp.status_code);
+	if (status != WLAN_STATUS_SUCCESS) {
+		os_memset(&event, 0, sizeof(event));
+		event.assoc_reject.bssid = mgmt->bssid;
+		if (len > 24 + sizeof(mgmt->u.assoc_resp)) {
+			event.assoc_reject.resp_ies =
+				(u8 *) mgmt->u.assoc_resp.variable;
+			event.assoc_reject.resp_ies_len =
+				len - 24 - sizeof(mgmt->u.assoc_resp);
+		}
+		event.assoc_reject.status_code = status;
+
+		wpa_supplicant_event(drv->ctx, EVENT_ASSOC_REJECT, &event);
+		return;
+	}
+
+	drv->associated = 1;
+	os_memcpy(drv->bssid, mgmt->sa, ETH_ALEN);
+	os_memcpy(drv->prev_bssid, mgmt->sa, ETH_ALEN);
+
+	os_memset(&event, 0, sizeof(event));
+	if (len > 24 + sizeof(mgmt->u.assoc_resp)) {
+		event.assoc_info.resp_ies = (u8 *) mgmt->u.assoc_resp.variable;
+		event.assoc_info.resp_ies_len =
+			len - 24 - sizeof(mgmt->u.assoc_resp);
+	}
+
+	event.assoc_info.freq = drv->assoc_freq;
+
+	nl80211_parse_wmm_params(wmm, &event.assoc_info.wmm_params);
+
+	wpa_supplicant_event(drv->ctx, EVENT_ASSOC, &event);
+}
+
+
+static void mlme_event_connect(struct wpa_driver_nl80211_data *drv,
+			       enum nl80211_commands cmd, struct nlattr *status,
+			       struct nlattr *addr, struct nlattr *req_ie,
+			       struct nlattr *resp_ie,
+			       struct nlattr *authorized,
+			       struct nlattr *key_replay_ctr,
+			       struct nlattr *ptk_kck,
+			       struct nlattr *ptk_kek)
+{
+	union wpa_event_data event;
+	const u8 *ssid;
+	u16 status_code;
+
+	if (drv->capa.flags & WPA_DRIVER_FLAGS_SME) {
+		/*
+		 * Avoid reporting two association events that would confuse
+		 * the core code.
+		 */
+		wpa_printf(MSG_DEBUG, "nl80211: Ignore connect event (cmd=%d) "
+			   "when using userspace SME", cmd);
+		return;
+	}
+
+	status_code = status ? nla_get_u16(status) : WLAN_STATUS_SUCCESS;
+
+	if (cmd == NL80211_CMD_CONNECT) {
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: Connect event (status=%u ignore_next_local_disconnect=%d)",
+			   status_code, drv->ignore_next_local_disconnect);
+	} else if (cmd == NL80211_CMD_ROAM) {
+		wpa_printf(MSG_DEBUG, "nl80211: Roam event");
+	}
+
+	os_memset(&event, 0, sizeof(event));
+	if (cmd == NL80211_CMD_CONNECT && status_code != WLAN_STATUS_SUCCESS) {
+		if (addr)
+			event.assoc_reject.bssid = nla_data(addr);
+		if (drv->ignore_next_local_disconnect) {
+			drv->ignore_next_local_disconnect = 0;
+			if (!event.assoc_reject.bssid ||
+			    (os_memcmp(event.assoc_reject.bssid,
+				       drv->auth_attempt_bssid,
+				       ETH_ALEN) != 0)) {
+				/*
+				 * Ignore the event that came without a BSSID or
+				 * for the old connection since this is likely
+				 * not relevant to the new Connect command.
+				 */
+				wpa_printf(MSG_DEBUG,
+					   "nl80211: Ignore connection failure event triggered during reassociation");
+				return;
+			}
+		}
+		if (resp_ie) {
+			event.assoc_reject.resp_ies = nla_data(resp_ie);
+			event.assoc_reject.resp_ies_len = nla_len(resp_ie);
+		}
+		event.assoc_reject.status_code = status_code;
+		wpa_supplicant_event(drv->ctx, EVENT_ASSOC_REJECT, &event);
+		return;
+	}
+
+	drv->associated = 1;
+	if (addr) {
+		os_memcpy(drv->bssid, nla_data(addr), ETH_ALEN);
+		os_memcpy(drv->prev_bssid, drv->bssid, ETH_ALEN);
+	}
+
+	if (req_ie) {
+		event.assoc_info.req_ies = nla_data(req_ie);
+		event.assoc_info.req_ies_len = nla_len(req_ie);
+
+		if (cmd == NL80211_CMD_ROAM) {
+			ssid = nl80211_get_ie(event.assoc_info.req_ies,
+					      event.assoc_info.req_ies_len,
+					      WLAN_EID_SSID);
+			if (ssid && ssid[1] > 0 && ssid[1] <= 32) {
+				drv->ssid_len = ssid[1];
+				os_memcpy(drv->ssid, ssid + 2, ssid[1]);
+			}
+		}
+	}
+	if (resp_ie) {
+		event.assoc_info.resp_ies = nla_data(resp_ie);
+		event.assoc_info.resp_ies_len = nla_len(resp_ie);
+	}
+
+	event.assoc_info.freq = nl80211_get_assoc_freq(drv);
+
+	if (authorized && nla_get_u8(authorized)) {
+		event.assoc_info.authorized = 1;
+		wpa_printf(MSG_DEBUG, "nl80211: connection authorized");
+	}
+	if (key_replay_ctr) {
+		event.assoc_info.key_replay_ctr = nla_data(key_replay_ctr);
+		event.assoc_info.key_replay_ctr_len = nla_len(key_replay_ctr);
+	}
+	if (ptk_kck) {
+		event.assoc_info.ptk_kck = nla_data(ptk_kck);
+		event.assoc_info.ptk_kck_len = nla_len(ptk_kck);
+	}
+	if (ptk_kek) {
+		event.assoc_info.ptk_kek = nla_data(ptk_kek);
+		event.assoc_info.ptk_kek_len = nla_len(ptk_kek);
+	}
+
+	wpa_supplicant_event(drv->ctx, EVENT_ASSOC, &event);
+}
+
+
+static void mlme_event_disconnect(struct wpa_driver_nl80211_data *drv,
+				  struct nlattr *reason, struct nlattr *addr,
+				  struct nlattr *by_ap)
+{
+	union wpa_event_data data;
+	unsigned int locally_generated = by_ap == NULL;
+
+	if (drv->capa.flags & WPA_DRIVER_FLAGS_SME) {
+		/*
+		 * Avoid reporting two disassociation events that could
+		 * confuse the core code.
+		 */
+		wpa_printf(MSG_DEBUG, "nl80211: Ignore disconnect "
+			   "event when using userspace SME");
+		return;
+	}
+
+	if (drv->ignore_next_local_disconnect) {
+		drv->ignore_next_local_disconnect = 0;
+		if (locally_generated) {
+			wpa_printf(MSG_DEBUG, "nl80211: Ignore disconnect "
+				   "event triggered during reassociation");
+			return;
+		}
+		wpa_printf(MSG_WARNING, "nl80211: Was expecting local "
+			   "disconnect but got another disconnect "
+			   "event first");
+	}
+
+	wpa_printf(MSG_DEBUG, "nl80211: Disconnect event");
+	nl80211_mark_disconnected(drv);
+	os_memset(&data, 0, sizeof(data));
+	if (reason)
+		data.deauth_info.reason_code = nla_get_u16(reason);
+	data.deauth_info.locally_generated = by_ap == NULL;
+	wpa_supplicant_event(drv->ctx, EVENT_DEAUTH, &data);
+}
+
+
+static int calculate_chan_offset(int width, int freq, int cf1, int cf2)
+{
+	int freq1 = 0;
+
+	switch (convert2width(width)) {
+	case CHAN_WIDTH_20_NOHT:
+	case CHAN_WIDTH_20:
+		return 0;
+	case CHAN_WIDTH_40:
+		freq1 = cf1 - 10;
+		break;
+	case CHAN_WIDTH_80:
+		freq1 = cf1 - 30;
+		break;
+	case CHAN_WIDTH_160:
+		freq1 = cf1 - 70;
+		break;
+	case CHAN_WIDTH_UNKNOWN:
+	case CHAN_WIDTH_80P80:
+		/* FIXME: implement this */
+		return 0;
+	}
+
+	return (abs(freq - freq1) / 20) % 2 == 0 ? 1 : -1;
+}
+
+
+static void mlme_event_ch_switch(struct wpa_driver_nl80211_data *drv,
+				 struct nlattr *ifindex, struct nlattr *freq,
+				 struct nlattr *type, struct nlattr *bw,
+				 struct nlattr *cf1, struct nlattr *cf2)
+{
+	struct i802_bss *bss;
+	union wpa_event_data data;
+	int ht_enabled = 1;
+	int chan_offset = 0;
+	int ifidx;
+
+	wpa_printf(MSG_DEBUG, "nl80211: Channel switch event");
+
+	if (!freq)
+		return;
+
+	ifidx = nla_get_u32(ifindex);
+	bss = get_bss_ifindex(drv, ifidx);
+	if (bss == NULL) {
+		wpa_printf(MSG_WARNING, "nl80211: Unknown ifindex (%d) for channel switch, ignoring",
+			   ifidx);
+		return;
+	}
+
+	if (type) {
+		enum nl80211_channel_type ch_type = nla_get_u32(type);
+
+		wpa_printf(MSG_DEBUG, "nl80211: Channel type: %d", ch_type);
+		switch (ch_type) {
+		case NL80211_CHAN_NO_HT:
+			ht_enabled = 0;
+			break;
+		case NL80211_CHAN_HT20:
+			break;
+		case NL80211_CHAN_HT40PLUS:
+			chan_offset = 1;
+			break;
+		case NL80211_CHAN_HT40MINUS:
+			chan_offset = -1;
+			break;
+		}
+	} else if (bw && cf1) {
+		/* This can happen for example with VHT80 ch switch */
+		chan_offset = calculate_chan_offset(nla_get_u32(bw),
+						    nla_get_u32(freq),
+						    nla_get_u32(cf1),
+						    cf2 ? nla_get_u32(cf2) : 0);
+	} else {
+		wpa_printf(MSG_WARNING, "nl80211: Unknown secondary channel information - following channel definition calculations may fail");
+	}
+
+	os_memset(&data, 0, sizeof(data));
+	data.ch_switch.freq = nla_get_u32(freq);
+	data.ch_switch.ht_enabled = ht_enabled;
+	data.ch_switch.ch_offset = chan_offset;
+	if (bw)
+		data.ch_switch.ch_width = convert2width(nla_get_u32(bw));
+	if (cf1)
+		data.ch_switch.cf1 = nla_get_u32(cf1);
+	if (cf2)
+		data.ch_switch.cf2 = nla_get_u32(cf2);
+
+	bss->freq = data.ch_switch.freq;
+
+	wpa_supplicant_event(bss->ctx, EVENT_CH_SWITCH, &data);
+}
+
+
+static void mlme_timeout_event(struct wpa_driver_nl80211_data *drv,
+			       enum nl80211_commands cmd, struct nlattr *addr)
+{
+	union wpa_event_data event;
+	enum wpa_event_type ev;
+
+	if (nla_len(addr) != ETH_ALEN)
+		return;
+
+	wpa_printf(MSG_DEBUG, "nl80211: MLME event %d; timeout with " MACSTR,
+		   cmd, MAC2STR((u8 *) nla_data(addr)));
+
+	if (cmd == NL80211_CMD_AUTHENTICATE)
+		ev = EVENT_AUTH_TIMED_OUT;
+	else if (cmd == NL80211_CMD_ASSOCIATE)
+		ev = EVENT_ASSOC_TIMED_OUT;
+	else
+		return;
+
+	os_memset(&event, 0, sizeof(event));
+	os_memcpy(event.timeout_event.addr, nla_data(addr), ETH_ALEN);
+	wpa_supplicant_event(drv->ctx, ev, &event);
+}
+
+
+static void mlme_event_mgmt(struct i802_bss *bss,
+			    struct nlattr *freq, struct nlattr *sig,
+			    const u8 *frame, size_t len)
+{
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	const struct ieee80211_mgmt *mgmt;
+	union wpa_event_data event;
+	u16 fc, stype;
+	int ssi_signal = 0;
+	int rx_freq = 0;
+
+	wpa_printf(MSG_MSGDUMP, "nl80211: Frame event");
+	mgmt = (const struct ieee80211_mgmt *) frame;
+	if (len < 24) {
+		wpa_printf(MSG_DEBUG, "nl80211: Too short management frame");
+		return;
+	}
+
+	fc = le_to_host16(mgmt->frame_control);
+	stype = WLAN_FC_GET_STYPE(fc);
+
+	if (sig)
+		ssi_signal = (s32) nla_get_u32(sig);
+
+	os_memset(&event, 0, sizeof(event));
+	if (freq) {
+		event.rx_mgmt.freq = nla_get_u32(freq);
+		rx_freq = drv->last_mgmt_freq = event.rx_mgmt.freq;
+	}
+	wpa_printf(MSG_DEBUG,
+		   "nl80211: RX frame sa=" MACSTR
+		   " freq=%d ssi_signal=%d fc=0x%x seq_ctrl=0x%x stype=%u (%s) len=%u",
+		   MAC2STR(mgmt->sa), rx_freq, ssi_signal, fc,
+		   le_to_host16(mgmt->seq_ctrl), stype, fc2str(fc),
+		   (unsigned int) len);
+	event.rx_mgmt.frame = frame;
+	event.rx_mgmt.frame_len = len;
+	event.rx_mgmt.ssi_signal = ssi_signal;
+	event.rx_mgmt.drv_priv = bss;
+	wpa_supplicant_event(drv->ctx, EVENT_RX_MGMT, &event);
+}
+
+
+static void mlme_event_mgmt_tx_status(struct wpa_driver_nl80211_data *drv,
+				      struct nlattr *cookie, const u8 *frame,
+				      size_t len, struct nlattr *ack)
+{
+	union wpa_event_data event;
+	const struct ieee80211_hdr *hdr;
+	u16 fc;
+
+	wpa_printf(MSG_DEBUG, "nl80211: Frame TX status event");
+	if (!is_ap_interface(drv->nlmode)) {
+		u64 cookie_val;
+
+		if (!cookie)
+			return;
+
+		cookie_val = nla_get_u64(cookie);
+		wpa_printf(MSG_DEBUG, "nl80211: Action TX status:"
+			   " cookie=0%llx%s (ack=%d)",
+			   (long long unsigned int) cookie_val,
+			   cookie_val == drv->send_action_cookie ?
+			   " (match)" : " (unknown)", ack != NULL);
+		if (cookie_val != drv->send_action_cookie)
+			return;
+	}
+
+	hdr = (const struct ieee80211_hdr *) frame;
+	fc = le_to_host16(hdr->frame_control);
+
+	os_memset(&event, 0, sizeof(event));
+	event.tx_status.type = WLAN_FC_GET_TYPE(fc);
+	event.tx_status.stype = WLAN_FC_GET_STYPE(fc);
+	event.tx_status.dst = hdr->addr1;
+	event.tx_status.data = frame;
+	event.tx_status.data_len = len;
+	event.tx_status.ack = ack != NULL;
+	wpa_supplicant_event(drv->ctx, EVENT_TX_STATUS, &event);
+}
+
+
+static void mlme_event_deauth_disassoc(struct wpa_driver_nl80211_data *drv,
+				       enum wpa_event_type type,
+				       const u8 *frame, size_t len)
+{
+	const struct ieee80211_mgmt *mgmt;
+	union wpa_event_data event;
+	const u8 *bssid = NULL;
+	u16 reason_code = 0;
+
+	if (type == EVENT_DEAUTH)
+		wpa_printf(MSG_DEBUG, "nl80211: Deauthenticate event");
+	else
+		wpa_printf(MSG_DEBUG, "nl80211: Disassociate event");
+
+	mgmt = (const struct ieee80211_mgmt *) frame;
+	if (len >= 24) {
+		bssid = mgmt->bssid;
+
+		if ((drv->capa.flags & WPA_DRIVER_FLAGS_SME) &&
+		    !drv->associated &&
+		    os_memcmp(bssid, drv->auth_bssid, ETH_ALEN) != 0 &&
+		    os_memcmp(bssid, drv->auth_attempt_bssid, ETH_ALEN) != 0 &&
+		    os_memcmp(bssid, drv->prev_bssid, ETH_ALEN) == 0) {
+			/*
+			 * Avoid issues with some roaming cases where
+			 * disconnection event for the old AP may show up after
+			 * we have started connection with the new AP.
+			 */
+			wpa_printf(MSG_DEBUG, "nl80211: Ignore deauth/disassoc event from old AP " MACSTR " when already authenticating with " MACSTR,
+				   MAC2STR(bssid),
+				   MAC2STR(drv->auth_attempt_bssid));
+			return;
+		}
+
+		if (drv->associated != 0 &&
+		    os_memcmp(bssid, drv->bssid, ETH_ALEN) != 0 &&
+		    os_memcmp(bssid, drv->auth_bssid, ETH_ALEN) != 0) {
+			/*
+			 * We have presumably received this deauth as a
+			 * response to a clear_state_mismatch() outgoing
+			 * deauth.  Don't let it take us offline!
+			 */
+			wpa_printf(MSG_DEBUG, "nl80211: Deauth received "
+				   "from Unknown BSSID " MACSTR " -- ignoring",
+				   MAC2STR(bssid));
+			return;
+		}
+	}
+
+	nl80211_mark_disconnected(drv);
+	os_memset(&event, 0, sizeof(event));
+
+	/* Note: Same offset for Reason Code in both frame subtypes */
+	if (len >= 24 + sizeof(mgmt->u.deauth))
+		reason_code = le_to_host16(mgmt->u.deauth.reason_code);
+
+	if (type == EVENT_DISASSOC) {
+		event.disassoc_info.locally_generated =
+			!os_memcmp(mgmt->sa, drv->first_bss->addr, ETH_ALEN);
+		event.disassoc_info.addr = bssid;
+		event.disassoc_info.reason_code = reason_code;
+		if (frame + len > mgmt->u.disassoc.variable) {
+			event.disassoc_info.ie = mgmt->u.disassoc.variable;
+			event.disassoc_info.ie_len = frame + len -
+				mgmt->u.disassoc.variable;
+		}
+	} else {
+		if (drv->ignore_deauth_event) {
+			wpa_printf(MSG_DEBUG, "nl80211: Ignore deauth event due to previous forced deauth-during-auth");
+			drv->ignore_deauth_event = 0;
+			return;
+		}
+		event.deauth_info.locally_generated =
+			!os_memcmp(mgmt->sa, drv->first_bss->addr, ETH_ALEN);
+		if (drv->ignore_next_local_deauth) {
+			drv->ignore_next_local_deauth = 0;
+			if (event.deauth_info.locally_generated) {
+				wpa_printf(MSG_DEBUG, "nl80211: Ignore deauth event triggered due to own deauth request");
+				return;
+			}
+			wpa_printf(MSG_WARNING, "nl80211: Was expecting local deauth but got another disconnect event first");
+		}
+		event.deauth_info.addr = bssid;
+		event.deauth_info.reason_code = reason_code;
+		if (frame + len > mgmt->u.deauth.variable) {
+			event.deauth_info.ie = mgmt->u.deauth.variable;
+			event.deauth_info.ie_len = frame + len -
+				mgmt->u.deauth.variable;
+		}
+	}
+
+	wpa_supplicant_event(drv->ctx, type, &event);
+}
+
+
+static void mlme_event_unprot_disconnect(struct wpa_driver_nl80211_data *drv,
+					 enum wpa_event_type type,
+					 const u8 *frame, size_t len)
+{
+	const struct ieee80211_mgmt *mgmt;
+	union wpa_event_data event;
+	u16 reason_code = 0;
+
+	if (type == EVENT_UNPROT_DEAUTH)
+		wpa_printf(MSG_DEBUG, "nl80211: Unprot Deauthenticate event");
+	else
+		wpa_printf(MSG_DEBUG, "nl80211: Unprot Disassociate event");
+
+	if (len < 24)
+		return;
+
+	mgmt = (const struct ieee80211_mgmt *) frame;
+
+	os_memset(&event, 0, sizeof(event));
+	/* Note: Same offset for Reason Code in both frame subtypes */
+	if (len >= 24 + sizeof(mgmt->u.deauth))
+		reason_code = le_to_host16(mgmt->u.deauth.reason_code);
+
+	if (type == EVENT_UNPROT_DISASSOC) {
+		event.unprot_disassoc.sa = mgmt->sa;
+		event.unprot_disassoc.da = mgmt->da;
+		event.unprot_disassoc.reason_code = reason_code;
+	} else {
+		event.unprot_deauth.sa = mgmt->sa;
+		event.unprot_deauth.da = mgmt->da;
+		event.unprot_deauth.reason_code = reason_code;
+	}
+
+	wpa_supplicant_event(drv->ctx, type, &event);
+}
+
+
+static void mlme_event(struct i802_bss *bss,
+		       enum nl80211_commands cmd, struct nlattr *frame,
+		       struct nlattr *addr, struct nlattr *timed_out,
+		       struct nlattr *freq, struct nlattr *ack,
+		       struct nlattr *cookie, struct nlattr *sig,
+		       struct nlattr *wmm)
+{
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	const u8 *data;
+	size_t len;
+
+	if (timed_out && addr) {
+		mlme_timeout_event(drv, cmd, addr);
+		return;
+	}
+
+	if (frame == NULL) {
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: MLME event %d (%s) without frame data",
+			   cmd, nl80211_command_to_string(cmd));
+		return;
+	}
+
+	data = nla_data(frame);
+	len = nla_len(frame);
+	if (len < 4 + 2 * ETH_ALEN) {
+		wpa_printf(MSG_MSGDUMP, "nl80211: MLME event %d (%s) on %s("
+			   MACSTR ") - too short",
+			   cmd, nl80211_command_to_string(cmd), bss->ifname,
+			   MAC2STR(bss->addr));
+		return;
+	}
+	wpa_printf(MSG_MSGDUMP, "nl80211: MLME event %d (%s) on %s(" MACSTR
+		   ") A1=" MACSTR " A2=" MACSTR, cmd,
+		   nl80211_command_to_string(cmd), bss->ifname,
+		   MAC2STR(bss->addr), MAC2STR(data + 4),
+		   MAC2STR(data + 4 + ETH_ALEN));
+	if (cmd != NL80211_CMD_FRAME_TX_STATUS && !(data[4] & 0x01) &&
+	    os_memcmp(bss->addr, data + 4, ETH_ALEN) != 0 &&
+	    os_memcmp(bss->addr, data + 4 + ETH_ALEN, ETH_ALEN) != 0) {
+		wpa_printf(MSG_MSGDUMP, "nl80211: %s: Ignore MLME frame event "
+			   "for foreign address", bss->ifname);
+		return;
+	}
+	wpa_hexdump(MSG_MSGDUMP, "nl80211: MLME event frame",
+		    nla_data(frame), nla_len(frame));
+
+	switch (cmd) {
+	case NL80211_CMD_AUTHENTICATE:
+		mlme_event_auth(drv, nla_data(frame), nla_len(frame));
+		break;
+	case NL80211_CMD_ASSOCIATE:
+		mlme_event_assoc(drv, nla_data(frame), nla_len(frame), wmm);
+		break;
+	case NL80211_CMD_DEAUTHENTICATE:
+		mlme_event_deauth_disassoc(drv, EVENT_DEAUTH,
+					   nla_data(frame), nla_len(frame));
+		break;
+	case NL80211_CMD_DISASSOCIATE:
+		mlme_event_deauth_disassoc(drv, EVENT_DISASSOC,
+					   nla_data(frame), nla_len(frame));
+		break;
+	case NL80211_CMD_FRAME:
+		mlme_event_mgmt(bss, freq, sig, nla_data(frame),
+				nla_len(frame));
+		break;
+	case NL80211_CMD_FRAME_TX_STATUS:
+		mlme_event_mgmt_tx_status(drv, cookie, nla_data(frame),
+					  nla_len(frame), ack);
+		break;
+	case NL80211_CMD_UNPROT_DEAUTHENTICATE:
+		mlme_event_unprot_disconnect(drv, EVENT_UNPROT_DEAUTH,
+					     nla_data(frame), nla_len(frame));
+		break;
+	case NL80211_CMD_UNPROT_DISASSOCIATE:
+		mlme_event_unprot_disconnect(drv, EVENT_UNPROT_DISASSOC,
+					     nla_data(frame), nla_len(frame));
+		break;
+	default:
+		break;
+	}
+}
+
+
+static void mlme_event_michael_mic_failure(struct i802_bss *bss,
+					   struct nlattr *tb[])
+{
+	union wpa_event_data data;
+
+	wpa_printf(MSG_DEBUG, "nl80211: MLME event Michael MIC failure");
+	os_memset(&data, 0, sizeof(data));
+	if (tb[NL80211_ATTR_MAC]) {
+		wpa_hexdump(MSG_DEBUG, "nl80211: Source MAC address",
+			    nla_data(tb[NL80211_ATTR_MAC]),
+			    nla_len(tb[NL80211_ATTR_MAC]));
+		data.michael_mic_failure.src = nla_data(tb[NL80211_ATTR_MAC]);
+	}
+	if (tb[NL80211_ATTR_KEY_SEQ]) {
+		wpa_hexdump(MSG_DEBUG, "nl80211: TSC",
+			    nla_data(tb[NL80211_ATTR_KEY_SEQ]),
+			    nla_len(tb[NL80211_ATTR_KEY_SEQ]));
+	}
+	if (tb[NL80211_ATTR_KEY_TYPE]) {
+		enum nl80211_key_type key_type =
+			nla_get_u32(tb[NL80211_ATTR_KEY_TYPE]);
+		wpa_printf(MSG_DEBUG, "nl80211: Key Type %d", key_type);
+		if (key_type == NL80211_KEYTYPE_PAIRWISE)
+			data.michael_mic_failure.unicast = 1;
+	} else
+		data.michael_mic_failure.unicast = 1;
+
+	if (tb[NL80211_ATTR_KEY_IDX]) {
+		u8 key_id = nla_get_u8(tb[NL80211_ATTR_KEY_IDX]);
+		wpa_printf(MSG_DEBUG, "nl80211: Key Id %d", key_id);
+	}
+
+	wpa_supplicant_event(bss->ctx, EVENT_MICHAEL_MIC_FAILURE, &data);
+}
+
+
+static void mlme_event_join_ibss(struct wpa_driver_nl80211_data *drv,
+				 struct nlattr *tb[])
+{
+	unsigned int freq;
+
+	if (tb[NL80211_ATTR_MAC] == NULL) {
+		wpa_printf(MSG_DEBUG, "nl80211: No address in IBSS joined "
+			   "event");
+		return;
+	}
+	os_memcpy(drv->bssid, nla_data(tb[NL80211_ATTR_MAC]), ETH_ALEN);
+
+	drv->associated = 1;
+	wpa_printf(MSG_DEBUG, "nl80211: IBSS " MACSTR " joined",
+		   MAC2STR(drv->bssid));
+
+	freq = nl80211_get_assoc_freq(drv);
+	if (freq) {
+		wpa_printf(MSG_DEBUG, "nl80211: IBSS on frequency %u MHz",
+			   freq);
+		drv->first_bss->freq = freq;
+	}
+
+	wpa_supplicant_event(drv->ctx, EVENT_ASSOC, NULL);
+}
+
+
+static void mlme_event_remain_on_channel(struct wpa_driver_nl80211_data *drv,
+					 int cancel_event, struct nlattr *tb[])
+{
+	unsigned int freq, chan_type, duration;
+	union wpa_event_data data;
+	u64 cookie;
+
+	if (tb[NL80211_ATTR_WIPHY_FREQ])
+		freq = nla_get_u32(tb[NL80211_ATTR_WIPHY_FREQ]);
+	else
+		freq = 0;
+
+	if (tb[NL80211_ATTR_WIPHY_CHANNEL_TYPE])
+		chan_type = nla_get_u32(tb[NL80211_ATTR_WIPHY_CHANNEL_TYPE]);
+	else
+		chan_type = 0;
+
+	if (tb[NL80211_ATTR_DURATION])
+		duration = nla_get_u32(tb[NL80211_ATTR_DURATION]);
+	else
+		duration = 0;
+
+	if (tb[NL80211_ATTR_COOKIE])
+		cookie = nla_get_u64(tb[NL80211_ATTR_COOKIE]);
+	else
+		cookie = 0;
+
+	wpa_printf(MSG_DEBUG, "nl80211: Remain-on-channel event (cancel=%d "
+		   "freq=%u channel_type=%u duration=%u cookie=0x%llx (%s))",
+		   cancel_event, freq, chan_type, duration,
+		   (long long unsigned int) cookie,
+		   cookie == drv->remain_on_chan_cookie ? "match" : "unknown");
+
+	if (cookie != drv->remain_on_chan_cookie)
+		return; /* not for us */
+
+	if (cancel_event)
+		drv->pending_remain_on_chan = 0;
+
+	os_memset(&data, 0, sizeof(data));
+	data.remain_on_channel.freq = freq;
+	data.remain_on_channel.duration = duration;
+	wpa_supplicant_event(drv->ctx, cancel_event ?
+			     EVENT_CANCEL_REMAIN_ON_CHANNEL :
+			     EVENT_REMAIN_ON_CHANNEL, &data);
+}
+
+
+static void mlme_event_ft_event(struct wpa_driver_nl80211_data *drv,
+				struct nlattr *tb[])
+{
+	union wpa_event_data data;
+
+	os_memset(&data, 0, sizeof(data));
+
+	if (tb[NL80211_ATTR_IE]) {
+		data.ft_ies.ies = nla_data(tb[NL80211_ATTR_IE]);
+		data.ft_ies.ies_len = nla_len(tb[NL80211_ATTR_IE]);
+	}
+
+	if (tb[NL80211_ATTR_IE_RIC]) {
+		data.ft_ies.ric_ies = nla_data(tb[NL80211_ATTR_IE_RIC]);
+		data.ft_ies.ric_ies_len = nla_len(tb[NL80211_ATTR_IE_RIC]);
+	}
+
+	if (tb[NL80211_ATTR_MAC])
+		os_memcpy(data.ft_ies.target_ap,
+			  nla_data(tb[NL80211_ATTR_MAC]), ETH_ALEN);
+
+	wpa_printf(MSG_DEBUG, "nl80211: FT event target_ap " MACSTR,
+		   MAC2STR(data.ft_ies.target_ap));
+
+	wpa_supplicant_event(drv->ctx, EVENT_FT_RESPONSE, &data);
+}
+
+
+static void send_scan_event(struct wpa_driver_nl80211_data *drv, int aborted,
+			    struct nlattr *tb[])
+{
+	union wpa_event_data event;
+	struct nlattr *nl;
+	int rem;
+	struct scan_info *info;
+#define MAX_REPORT_FREQS 50
+	int freqs[MAX_REPORT_FREQS];
+	int num_freqs = 0;
+
+	if (drv->scan_for_auth) {
+		drv->scan_for_auth = 0;
+		wpa_printf(MSG_DEBUG, "nl80211: Scan results for missing "
+			   "cfg80211 BSS entry");
+		wpa_driver_nl80211_authenticate_retry(drv);
+		return;
+	}
+
+	os_memset(&event, 0, sizeof(event));
+	info = &event.scan_info;
+	info->aborted = aborted;
+
+	if (tb[NL80211_ATTR_SCAN_SSIDS]) {
+		nla_for_each_nested(nl, tb[NL80211_ATTR_SCAN_SSIDS], rem) {
+			struct wpa_driver_scan_ssid *s =
+				&info->ssids[info->num_ssids];
+			s->ssid = nla_data(nl);
+			s->ssid_len = nla_len(nl);
+			wpa_printf(MSG_DEBUG, "nl80211: Scan probed for SSID '%s'",
+				   wpa_ssid_txt(s->ssid, s->ssid_len));
+			info->num_ssids++;
+			if (info->num_ssids == WPAS_MAX_SCAN_SSIDS)
+				break;
+		}
+	}
+	if (tb[NL80211_ATTR_SCAN_FREQUENCIES]) {
+		char msg[200], *pos, *end;
+		int res;
+
+		pos = msg;
+		end = pos + sizeof(msg);
+		*pos = '\0';
+
+		nla_for_each_nested(nl, tb[NL80211_ATTR_SCAN_FREQUENCIES], rem)
+		{
+			freqs[num_freqs] = nla_get_u32(nl);
+			res = os_snprintf(pos, end - pos, " %d",
+					  freqs[num_freqs]);
+			if (!os_snprintf_error(end - pos, res))
+				pos += res;
+			num_freqs++;
+			if (num_freqs == MAX_REPORT_FREQS - 1)
+				break;
+		}
+		info->freqs = freqs;
+		info->num_freqs = num_freqs;
+		wpa_printf(MSG_DEBUG, "nl80211: Scan included frequencies:%s",
+			   msg);
+	}
+	wpa_supplicant_event(drv->ctx, EVENT_SCAN_RESULTS, &event);
+}
+
+
+static void nl80211_cqm_event(struct wpa_driver_nl80211_data *drv,
+			      struct nlattr *tb[])
+{
+	static struct nla_policy cqm_policy[NL80211_ATTR_CQM_MAX + 1] = {
+		[NL80211_ATTR_CQM_RSSI_THOLD] = { .type = NLA_U32 },
+		[NL80211_ATTR_CQM_RSSI_HYST] = { .type = NLA_U8 },
+		[NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT] = { .type = NLA_U32 },
+		[NL80211_ATTR_CQM_PKT_LOSS_EVENT] = { .type = NLA_U32 },
+	};
+	struct nlattr *cqm[NL80211_ATTR_CQM_MAX + 1];
+	enum nl80211_cqm_rssi_threshold_event event;
+	union wpa_event_data ed;
+	struct wpa_signal_info sig;
+	int res;
+
+	if (tb[NL80211_ATTR_CQM] == NULL ||
+	    nla_parse_nested(cqm, NL80211_ATTR_CQM_MAX, tb[NL80211_ATTR_CQM],
+			     cqm_policy)) {
+		wpa_printf(MSG_DEBUG, "nl80211: Ignore invalid CQM event");
+		return;
+	}
+
+	os_memset(&ed, 0, sizeof(ed));
+
+	if (cqm[NL80211_ATTR_CQM_PKT_LOSS_EVENT]) {
+		if (!tb[NL80211_ATTR_MAC])
+			return;
+		os_memcpy(ed.low_ack.addr, nla_data(tb[NL80211_ATTR_MAC]),
+			  ETH_ALEN);
+		wpa_supplicant_event(drv->ctx, EVENT_STATION_LOW_ACK, &ed);
+		return;
+	}
+
+	if (cqm[NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT] == NULL)
+		return;
+	event = nla_get_u32(cqm[NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT]);
+
+	if (event == NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH) {
+		wpa_printf(MSG_DEBUG, "nl80211: Connection quality monitor "
+			   "event: RSSI high");
+		ed.signal_change.above_threshold = 1;
+	} else if (event == NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW) {
+		wpa_printf(MSG_DEBUG, "nl80211: Connection quality monitor "
+			   "event: RSSI low");
+		ed.signal_change.above_threshold = 0;
+	} else
+		return;
+
+	res = nl80211_get_link_signal(drv, &sig);
+	if (res == 0) {
+		ed.signal_change.current_signal = sig.current_signal;
+		ed.signal_change.current_txrate = sig.current_txrate;
+		wpa_printf(MSG_DEBUG, "nl80211: Signal: %d dBm  txrate: %d",
+			   sig.current_signal, sig.current_txrate);
+	}
+
+	res = nl80211_get_link_noise(drv, &sig);
+	if (res == 0) {
+		ed.signal_change.current_noise = sig.current_noise;
+		wpa_printf(MSG_DEBUG, "nl80211: Noise: %d dBm",
+			   sig.current_noise);
+	}
+
+	wpa_supplicant_event(drv->ctx, EVENT_SIGNAL_CHANGE, &ed);
+}
+
+
+static void nl80211_new_peer_candidate(struct wpa_driver_nl80211_data *drv,
+				       struct nlattr **tb)
+{
+	const u8 *addr;
+	union wpa_event_data data;
+
+	if (drv->nlmode != NL80211_IFTYPE_MESH_POINT ||
+	    !tb[NL80211_ATTR_MAC] || !tb[NL80211_ATTR_IE])
+		return;
+
+	addr = nla_data(tb[NL80211_ATTR_MAC]);
+	wpa_printf(MSG_DEBUG, "nl80211: New peer candidate" MACSTR,
+		   MAC2STR(addr));
+
+	os_memset(&data, 0, sizeof(data));
+	data.mesh_peer.peer = addr;
+	data.mesh_peer.ies = nla_data(tb[NL80211_ATTR_IE]);
+	data.mesh_peer.ie_len = nla_len(tb[NL80211_ATTR_IE]);
+	wpa_supplicant_event(drv->ctx, EVENT_NEW_PEER_CANDIDATE, &data);
+}
+
+
+static void nl80211_new_station_event(struct wpa_driver_nl80211_data *drv,
+				      struct i802_bss *bss,
+				      struct nlattr **tb)
+{
+	u8 *addr;
+	union wpa_event_data data;
+
+	if (tb[NL80211_ATTR_MAC] == NULL)
+		return;
+	addr = nla_data(tb[NL80211_ATTR_MAC]);
+	wpa_printf(MSG_DEBUG, "nl80211: New station " MACSTR, MAC2STR(addr));
+
+	if (is_ap_interface(drv->nlmode) && drv->device_ap_sme) {
+		u8 *ies = NULL;
+		size_t ies_len = 0;
+		if (tb[NL80211_ATTR_IE]) {
+			ies = nla_data(tb[NL80211_ATTR_IE]);
+			ies_len = nla_len(tb[NL80211_ATTR_IE]);
+		}
+		wpa_hexdump(MSG_DEBUG, "nl80211: Assoc Req IEs", ies, ies_len);
+		drv_event_assoc(bss->ctx, addr, ies, ies_len, 0);
+		return;
+	}
+
+	if (drv->nlmode != NL80211_IFTYPE_ADHOC)
+		return;
+
+	os_memset(&data, 0, sizeof(data));
+	os_memcpy(data.ibss_rsn_start.peer, addr, ETH_ALEN);
+	wpa_supplicant_event(bss->ctx, EVENT_IBSS_RSN_START, &data);
+}
+
+
+static void nl80211_del_station_event(struct wpa_driver_nl80211_data *drv,
+				      struct nlattr **tb)
+{
+	u8 *addr;
+	union wpa_event_data data;
+
+	if (tb[NL80211_ATTR_MAC] == NULL)
+		return;
+	addr = nla_data(tb[NL80211_ATTR_MAC]);
+	wpa_printf(MSG_DEBUG, "nl80211: Delete station " MACSTR,
+		   MAC2STR(addr));
+
+	if (is_ap_interface(drv->nlmode) && drv->device_ap_sme) {
+		drv_event_disassoc(drv->ctx, addr);
+		return;
+	}
+
+	if (drv->nlmode != NL80211_IFTYPE_ADHOC)
+		return;
+
+	os_memset(&data, 0, sizeof(data));
+	os_memcpy(data.ibss_peer_lost.peer, addr, ETH_ALEN);
+	wpa_supplicant_event(drv->ctx, EVENT_IBSS_PEER_LOST, &data);
+}
+
+
+static void nl80211_rekey_offload_event(struct wpa_driver_nl80211_data *drv,
+					struct nlattr **tb)
+{
+	struct nlattr *rekey_info[NUM_NL80211_REKEY_DATA];
+	static struct nla_policy rekey_policy[NUM_NL80211_REKEY_DATA] = {
+		[NL80211_REKEY_DATA_KEK] = {
+			.minlen = NL80211_KEK_LEN,
+			.maxlen = NL80211_KEK_LEN,
+		},
+		[NL80211_REKEY_DATA_KCK] = {
+			.minlen = NL80211_KCK_LEN,
+			.maxlen = NL80211_KCK_LEN,
+		},
+		[NL80211_REKEY_DATA_REPLAY_CTR] = {
+			.minlen = NL80211_REPLAY_CTR_LEN,
+			.maxlen = NL80211_REPLAY_CTR_LEN,
+		},
+	};
+	union wpa_event_data data;
+
+	if (!tb[NL80211_ATTR_MAC] ||
+	    !tb[NL80211_ATTR_REKEY_DATA] ||
+	    nla_parse_nested(rekey_info, MAX_NL80211_REKEY_DATA,
+			     tb[NL80211_ATTR_REKEY_DATA], rekey_policy) ||
+	    !rekey_info[NL80211_REKEY_DATA_REPLAY_CTR])
+		return;
+
+	os_memset(&data, 0, sizeof(data));
+	data.driver_gtk_rekey.bssid = nla_data(tb[NL80211_ATTR_MAC]);
+	wpa_printf(MSG_DEBUG, "nl80211: Rekey offload event for BSSID " MACSTR,
+		   MAC2STR(data.driver_gtk_rekey.bssid));
+	data.driver_gtk_rekey.replay_ctr =
+		nla_data(rekey_info[NL80211_REKEY_DATA_REPLAY_CTR]);
+	wpa_hexdump(MSG_DEBUG, "nl80211: Rekey offload - Replay Counter",
+		    data.driver_gtk_rekey.replay_ctr, NL80211_REPLAY_CTR_LEN);
+	wpa_supplicant_event(drv->ctx, EVENT_DRIVER_GTK_REKEY, &data);
+}
+
+
+static void nl80211_pmksa_candidate_event(struct wpa_driver_nl80211_data *drv,
+					  struct nlattr **tb)
+{
+	struct nlattr *cand[NUM_NL80211_PMKSA_CANDIDATE];
+	static struct nla_policy cand_policy[NUM_NL80211_PMKSA_CANDIDATE] = {
+		[NL80211_PMKSA_CANDIDATE_INDEX] = { .type = NLA_U32 },
+		[NL80211_PMKSA_CANDIDATE_BSSID] = {
+			.minlen = ETH_ALEN,
+			.maxlen = ETH_ALEN,
+		},
+		[NL80211_PMKSA_CANDIDATE_PREAUTH] = { .type = NLA_FLAG },
+	};
+	union wpa_event_data data;
+
+	wpa_printf(MSG_DEBUG, "nl80211: PMKSA candidate event");
+
+	if (!tb[NL80211_ATTR_PMKSA_CANDIDATE] ||
+	    nla_parse_nested(cand, MAX_NL80211_PMKSA_CANDIDATE,
+			     tb[NL80211_ATTR_PMKSA_CANDIDATE], cand_policy) ||
+	    !cand[NL80211_PMKSA_CANDIDATE_INDEX] ||
+	    !cand[NL80211_PMKSA_CANDIDATE_BSSID])
+		return;
+
+	os_memset(&data, 0, sizeof(data));
+	os_memcpy(data.pmkid_candidate.bssid,
+		  nla_data(cand[NL80211_PMKSA_CANDIDATE_BSSID]), ETH_ALEN);
+	data.pmkid_candidate.index =
+		nla_get_u32(cand[NL80211_PMKSA_CANDIDATE_INDEX]);
+	data.pmkid_candidate.preauth =
+		cand[NL80211_PMKSA_CANDIDATE_PREAUTH] != NULL;
+	wpa_supplicant_event(drv->ctx, EVENT_PMKID_CANDIDATE, &data);
+}
+
+
+static void nl80211_client_probe_event(struct wpa_driver_nl80211_data *drv,
+				       struct nlattr **tb)
+{
+	union wpa_event_data data;
+
+	wpa_printf(MSG_DEBUG, "nl80211: Probe client event");
+
+	if (!tb[NL80211_ATTR_MAC] || !tb[NL80211_ATTR_ACK])
+		return;
+
+	os_memset(&data, 0, sizeof(data));
+	os_memcpy(data.client_poll.addr,
+		  nla_data(tb[NL80211_ATTR_MAC]), ETH_ALEN);
+
+	wpa_supplicant_event(drv->ctx, EVENT_DRIVER_CLIENT_POLL_OK, &data);
+}
+
+
+static void nl80211_tdls_oper_event(struct wpa_driver_nl80211_data *drv,
+				    struct nlattr **tb)
+{
+	union wpa_event_data data;
+
+	wpa_printf(MSG_DEBUG, "nl80211: TDLS operation event");
+
+	if (!tb[NL80211_ATTR_MAC] || !tb[NL80211_ATTR_TDLS_OPERATION])
+		return;
+
+	os_memset(&data, 0, sizeof(data));
+	os_memcpy(data.tdls.peer, nla_data(tb[NL80211_ATTR_MAC]), ETH_ALEN);
+	switch (nla_get_u8(tb[NL80211_ATTR_TDLS_OPERATION])) {
+	case NL80211_TDLS_SETUP:
+		wpa_printf(MSG_DEBUG, "nl80211: TDLS setup request for peer "
+			   MACSTR, MAC2STR(data.tdls.peer));
+		data.tdls.oper = TDLS_REQUEST_SETUP;
+		break;
+	case NL80211_TDLS_TEARDOWN:
+		wpa_printf(MSG_DEBUG, "nl80211: TDLS teardown request for peer "
+			   MACSTR, MAC2STR(data.tdls.peer));
+		data.tdls.oper = TDLS_REQUEST_TEARDOWN;
+		break;
+	case NL80211_TDLS_DISCOVERY_REQ:
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: TDLS discovery request for peer " MACSTR,
+			   MAC2STR(data.tdls.peer));
+		data.tdls.oper = TDLS_REQUEST_DISCOVER;
+		break;
+	default:
+		wpa_printf(MSG_DEBUG, "nl80211: Unsupported TDLS operatione "
+			   "event");
+		return;
+	}
+	if (tb[NL80211_ATTR_REASON_CODE]) {
+		data.tdls.reason_code =
+			nla_get_u16(tb[NL80211_ATTR_REASON_CODE]);
+	}
+
+	wpa_supplicant_event(drv->ctx, EVENT_TDLS, &data);
+}
+
+
+static void nl80211_stop_ap(struct wpa_driver_nl80211_data *drv,
+			    struct nlattr **tb)
+{
+	wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_UNAVAILABLE, NULL);
+}
+
+
+static void nl80211_connect_failed_event(struct wpa_driver_nl80211_data *drv,
+					 struct nlattr **tb)
+{
+	union wpa_event_data data;
+	u32 reason;
+
+	wpa_printf(MSG_DEBUG, "nl80211: Connect failed event");
+
+	if (!tb[NL80211_ATTR_MAC] || !tb[NL80211_ATTR_CONN_FAILED_REASON])
+		return;
+
+	os_memset(&data, 0, sizeof(data));
+	os_memcpy(data.connect_failed_reason.addr,
+		  nla_data(tb[NL80211_ATTR_MAC]), ETH_ALEN);
+
+	reason = nla_get_u32(tb[NL80211_ATTR_CONN_FAILED_REASON]);
+	switch (reason) {
+	case NL80211_CONN_FAIL_MAX_CLIENTS:
+		wpa_printf(MSG_DEBUG, "nl80211: Max client reached");
+		data.connect_failed_reason.code = MAX_CLIENT_REACHED;
+		break;
+	case NL80211_CONN_FAIL_BLOCKED_CLIENT:
+		wpa_printf(MSG_DEBUG, "nl80211: Blocked client " MACSTR
+			   " tried to connect",
+			   MAC2STR(data.connect_failed_reason.addr));
+		data.connect_failed_reason.code = BLOCKED_CLIENT;
+		break;
+	default:
+		wpa_printf(MSG_DEBUG, "nl8021l: Unknown connect failed reason "
+			   "%u", reason);
+		return;
+	}
+
+	wpa_supplicant_event(drv->ctx, EVENT_CONNECT_FAILED_REASON, &data);
+}
+
+
+static void nl80211_radar_event(struct wpa_driver_nl80211_data *drv,
+				struct nlattr **tb)
+{
+	union wpa_event_data data;
+	enum nl80211_radar_event event_type;
+
+	if (!tb[NL80211_ATTR_WIPHY_FREQ] || !tb[NL80211_ATTR_RADAR_EVENT])
+		return;
+
+	os_memset(&data, 0, sizeof(data));
+	data.dfs_event.freq = nla_get_u32(tb[NL80211_ATTR_WIPHY_FREQ]);
+	event_type = nla_get_u32(tb[NL80211_ATTR_RADAR_EVENT]);
+
+	/* Check HT params */
+	if (tb[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) {
+		data.dfs_event.ht_enabled = 1;
+		data.dfs_event.chan_offset = 0;
+
+		switch (nla_get_u32(tb[NL80211_ATTR_WIPHY_CHANNEL_TYPE])) {
+		case NL80211_CHAN_NO_HT:
+			data.dfs_event.ht_enabled = 0;
+			break;
+		case NL80211_CHAN_HT20:
+			break;
+		case NL80211_CHAN_HT40PLUS:
+			data.dfs_event.chan_offset = 1;
+			break;
+		case NL80211_CHAN_HT40MINUS:
+			data.dfs_event.chan_offset = -1;
+			break;
+		}
+	}
+
+	/* Get VHT params */
+	if (tb[NL80211_ATTR_CHANNEL_WIDTH])
+		data.dfs_event.chan_width =
+			convert2width(nla_get_u32(
+					      tb[NL80211_ATTR_CHANNEL_WIDTH]));
+	if (tb[NL80211_ATTR_CENTER_FREQ1])
+		data.dfs_event.cf1 = nla_get_u32(tb[NL80211_ATTR_CENTER_FREQ1]);
+	if (tb[NL80211_ATTR_CENTER_FREQ2])
+		data.dfs_event.cf2 = nla_get_u32(tb[NL80211_ATTR_CENTER_FREQ2]);
+
+	wpa_printf(MSG_DEBUG, "nl80211: DFS event on freq %d MHz, ht: %d, offset: %d, width: %d, cf1: %dMHz, cf2: %dMHz",
+		   data.dfs_event.freq, data.dfs_event.ht_enabled,
+		   data.dfs_event.chan_offset, data.dfs_event.chan_width,
+		   data.dfs_event.cf1, data.dfs_event.cf2);
+
+	switch (event_type) {
+	case NL80211_RADAR_DETECTED:
+		wpa_supplicant_event(drv->ctx, EVENT_DFS_RADAR_DETECTED, &data);
+		break;
+	case NL80211_RADAR_CAC_FINISHED:
+		wpa_supplicant_event(drv->ctx, EVENT_DFS_CAC_FINISHED, &data);
+		break;
+	case NL80211_RADAR_CAC_ABORTED:
+		wpa_supplicant_event(drv->ctx, EVENT_DFS_CAC_ABORTED, &data);
+		break;
+	case NL80211_RADAR_NOP_FINISHED:
+		wpa_supplicant_event(drv->ctx, EVENT_DFS_NOP_FINISHED, &data);
+		break;
+	default:
+		wpa_printf(MSG_DEBUG, "nl80211: Unknown radar event %d "
+			   "received", event_type);
+		break;
+	}
+}
+
+
+static void nl80211_spurious_frame(struct i802_bss *bss, struct nlattr **tb,
+				   int wds)
+{
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	union wpa_event_data event;
+
+	if (!tb[NL80211_ATTR_MAC])
+		return;
+
+	os_memset(&event, 0, sizeof(event));
+	event.rx_from_unknown.bssid = bss->addr;
+	event.rx_from_unknown.addr = nla_data(tb[NL80211_ATTR_MAC]);
+	event.rx_from_unknown.wds = wds;
+
+	wpa_supplicant_event(drv->ctx, EVENT_RX_FROM_UNKNOWN, &event);
+}
+
+
+static void qca_nl80211_avoid_freq(struct wpa_driver_nl80211_data *drv,
+				   const u8 *data, size_t len)
+{
+	u32 i, count;
+	union wpa_event_data event;
+	struct wpa_freq_range *range = NULL;
+	const struct qca_avoid_freq_list *freq_range;
+
+	freq_range = (const struct qca_avoid_freq_list *) data;
+	if (len < sizeof(freq_range->count))
+		return;
+
+	count = freq_range->count;
+	if (len < sizeof(freq_range->count) +
+	    count * sizeof(struct qca_avoid_freq_range)) {
+		wpa_printf(MSG_DEBUG, "nl80211: Ignored too short avoid frequency list (len=%u)",
+			   (unsigned int) len);
+		return;
+	}
+
+	if (count > 0) {
+		range = os_calloc(count, sizeof(struct wpa_freq_range));
+		if (range == NULL)
+			return;
+	}
+
+	os_memset(&event, 0, sizeof(event));
+	for (i = 0; i < count; i++) {
+		unsigned int idx = event.freq_range.num;
+		range[idx].min = freq_range->range[i].start_freq;
+		range[idx].max = freq_range->range[i].end_freq;
+		wpa_printf(MSG_DEBUG, "nl80211: Avoid frequency range: %u-%u",
+			   range[idx].min, range[idx].max);
+		if (range[idx].min > range[idx].max) {
+			wpa_printf(MSG_DEBUG, "nl80211: Ignore invalid frequency range");
+			continue;
+		}
+		event.freq_range.num++;
+	}
+	event.freq_range.range = range;
+
+	wpa_supplicant_event(drv->ctx, EVENT_AVOID_FREQUENCIES, &event);
+
+	os_free(range);
+}
+
+
+static enum hostapd_hw_mode get_qca_hw_mode(u8 hw_mode)
+{
+	switch (hw_mode) {
+	case QCA_ACS_MODE_IEEE80211B:
+		return HOSTAPD_MODE_IEEE80211B;
+	case QCA_ACS_MODE_IEEE80211G:
+		return HOSTAPD_MODE_IEEE80211G;
+	case QCA_ACS_MODE_IEEE80211A:
+		return HOSTAPD_MODE_IEEE80211A;
+	case QCA_ACS_MODE_IEEE80211AD:
+		return HOSTAPD_MODE_IEEE80211AD;
+	case QCA_ACS_MODE_IEEE80211ANY:
+		return HOSTAPD_MODE_IEEE80211ANY;
+	default:
+		return NUM_HOSTAPD_MODES;
+	}
+}
+
+
+static void qca_nl80211_acs_select_ch(struct wpa_driver_nl80211_data *drv,
+				   const u8 *data, size_t len)
+{
+	struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_ACS_MAX + 1];
+	union wpa_event_data event;
+
+	wpa_printf(MSG_DEBUG,
+		   "nl80211: ACS channel selection vendor event received");
+
+	if (nla_parse(tb, QCA_WLAN_VENDOR_ATTR_ACS_MAX,
+		      (struct nlattr *) data, len, NULL) ||
+	    !tb[QCA_WLAN_VENDOR_ATTR_ACS_PRIMARY_CHANNEL] ||
+	    !tb[QCA_WLAN_VENDOR_ATTR_ACS_SECONDARY_CHANNEL])
+		return;
+
+	os_memset(&event, 0, sizeof(event));
+	event.acs_selected_channels.pri_channel =
+		nla_get_u8(tb[QCA_WLAN_VENDOR_ATTR_ACS_PRIMARY_CHANNEL]);
+	event.acs_selected_channels.sec_channel =
+		nla_get_u8(tb[QCA_WLAN_VENDOR_ATTR_ACS_SECONDARY_CHANNEL]);
+	if (tb[QCA_WLAN_VENDOR_ATTR_ACS_VHT_SEG0_CENTER_CHANNEL])
+		event.acs_selected_channels.vht_seg0_center_ch =
+			nla_get_u8(tb[QCA_WLAN_VENDOR_ATTR_ACS_VHT_SEG0_CENTER_CHANNEL]);
+	if (tb[QCA_WLAN_VENDOR_ATTR_ACS_VHT_SEG0_CENTER_CHANNEL])
+		event.acs_selected_channels.vht_seg1_center_ch =
+			nla_get_u8(tb[QCA_WLAN_VENDOR_ATTR_ACS_VHT_SEG1_CENTER_CHANNEL]);
+	if (tb[QCA_WLAN_VENDOR_ATTR_ACS_CHWIDTH])
+		event.acs_selected_channels.ch_width =
+			nla_get_u16(tb[QCA_WLAN_VENDOR_ATTR_ACS_CHWIDTH]);
+	if (tb[QCA_WLAN_VENDOR_ATTR_ACS_HW_MODE]) {
+		u8 hw_mode = nla_get_u8(tb[QCA_WLAN_VENDOR_ATTR_ACS_HW_MODE]);
+
+		event.acs_selected_channels.hw_mode = get_qca_hw_mode(hw_mode);
+		if (event.acs_selected_channels.hw_mode == NUM_HOSTAPD_MODES ||
+		    event.acs_selected_channels.hw_mode ==
+		    HOSTAPD_MODE_IEEE80211ANY) {
+			wpa_printf(MSG_DEBUG,
+				   "nl80211: Invalid hw_mode %d in ACS selection event",
+				   hw_mode);
+			return;
+		}
+	}
+
+	wpa_printf(MSG_INFO,
+		   "nl80211: ACS Results: PCH: %d SCH: %d BW: %d VHT0: %d VHT1: %d HW_MODE: %d",
+		   event.acs_selected_channels.pri_channel,
+		   event.acs_selected_channels.sec_channel,
+		   event.acs_selected_channels.ch_width,
+		   event.acs_selected_channels.vht_seg0_center_ch,
+		   event.acs_selected_channels.vht_seg1_center_ch,
+		   event.acs_selected_channels.hw_mode);
+
+	/* Ignore ACS channel list check for backwards compatibility */
+
+	wpa_supplicant_event(drv->ctx, EVENT_ACS_CHANNEL_SELECTED, &event);
+}
+
+
+static void qca_nl80211_key_mgmt_auth(struct wpa_driver_nl80211_data *drv,
+				      const u8 *data, size_t len)
+{
+	struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_MAX + 1];
+	u8 *bssid;
+
+	wpa_printf(MSG_DEBUG,
+		   "nl80211: Key management roam+auth vendor event received");
+
+	if (nla_parse(tb, QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_MAX,
+		      (struct nlattr *) data, len, NULL) ||
+	    !tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_BSSID] ||
+	    nla_len(tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_BSSID]) != ETH_ALEN ||
+	    !tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_REQ_IE] ||
+	    !tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_RESP_IE] ||
+	    !tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_AUTHORIZED])
+		return;
+
+	bssid = nla_data(tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_BSSID]);
+	wpa_printf(MSG_DEBUG, "  * roam BSSID " MACSTR, MAC2STR(bssid));
+
+	mlme_event_connect(drv, NL80211_CMD_ROAM, NULL,
+			   tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_BSSID],
+			   tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_REQ_IE],
+			   tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_RESP_IE],
+			   tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_AUTHORIZED],
+			   tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_KEY_REPLAY_CTR],
+			   tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_PTK_KCK],
+			   tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_PTK_KEK]);
+}
+
+
+static void qca_nl80211_dfs_offload_radar_event(
+	struct wpa_driver_nl80211_data *drv, u32 subcmd, u8 *msg, int length)
+{
+	union wpa_event_data data;
+	struct nlattr *tb[NL80211_ATTR_MAX + 1];
+
+	wpa_printf(MSG_DEBUG,
+		   "nl80211: DFS offload radar vendor event received");
+
+	if (nla_parse(tb, NL80211_ATTR_MAX,
+		      (struct nlattr *) msg, length, NULL))
+		return;
+
+	if (!tb[NL80211_ATTR_WIPHY_FREQ]) {
+		wpa_printf(MSG_INFO,
+			   "nl80211: Error parsing WIPHY_FREQ in FS offload radar vendor event");
+		return;
+	}
+
+	os_memset(&data, 0, sizeof(data));
+	data.dfs_event.freq = nla_get_u32(tb[NL80211_ATTR_WIPHY_FREQ]);
+
+	wpa_printf(MSG_DEBUG, "nl80211: DFS event on freq %d MHz",
+		   data.dfs_event.freq);
+
+	/* Check HT params */
+	if (tb[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) {
+		data.dfs_event.ht_enabled = 1;
+		data.dfs_event.chan_offset = 0;
+
+		switch (nla_get_u32(tb[NL80211_ATTR_WIPHY_CHANNEL_TYPE])) {
+		case NL80211_CHAN_NO_HT:
+			data.dfs_event.ht_enabled = 0;
+			break;
+		case NL80211_CHAN_HT20:
+			break;
+		case NL80211_CHAN_HT40PLUS:
+			data.dfs_event.chan_offset = 1;
+			break;
+		case NL80211_CHAN_HT40MINUS:
+			data.dfs_event.chan_offset = -1;
+			break;
+		}
+	}
+
+	/* Get VHT params */
+	if (tb[NL80211_ATTR_CHANNEL_WIDTH])
+		data.dfs_event.chan_width =
+			convert2width(nla_get_u32(
+					      tb[NL80211_ATTR_CHANNEL_WIDTH]));
+	if (tb[NL80211_ATTR_CENTER_FREQ1])
+		data.dfs_event.cf1 = nla_get_u32(tb[NL80211_ATTR_CENTER_FREQ1]);
+	if (tb[NL80211_ATTR_CENTER_FREQ2])
+		data.dfs_event.cf2 = nla_get_u32(tb[NL80211_ATTR_CENTER_FREQ2]);
+
+	wpa_printf(MSG_DEBUG, "nl80211: DFS event on freq %d MHz, ht: %d, "
+		    "offset: %d, width: %d, cf1: %dMHz, cf2: %dMHz",
+		    data.dfs_event.freq, data.dfs_event.ht_enabled,
+		    data.dfs_event.chan_offset, data.dfs_event.chan_width,
+		    data.dfs_event.cf1, data.dfs_event.cf2);
+
+	switch (subcmd) {
+	case QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_RADAR_DETECTED:
+		wpa_supplicant_event(drv->ctx, EVENT_DFS_RADAR_DETECTED, &data);
+		break;
+	case QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_CAC_STARTED:
+		wpa_supplicant_event(drv->ctx, EVENT_DFS_CAC_STARTED, &data);
+		break;
+	case QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_CAC_FINISHED:
+		wpa_supplicant_event(drv->ctx, EVENT_DFS_CAC_FINISHED, &data);
+		break;
+	case QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_CAC_ABORTED:
+		wpa_supplicant_event(drv->ctx, EVENT_DFS_CAC_ABORTED, &data);
+		break;
+	case QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_CAC_NOP_FINISHED:
+		wpa_supplicant_event(drv->ctx, EVENT_DFS_NOP_FINISHED, &data);
+		break;
+	default:
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: Unknown DFS offload radar event %d received",
+			   subcmd);
+		break;
+	}
+}
+
+
+static void nl80211_vendor_event_qca(struct wpa_driver_nl80211_data *drv,
+				     u32 subcmd, u8 *data, size_t len)
+{
+	switch (subcmd) {
+	case QCA_NL80211_VENDOR_SUBCMD_TEST:
+		wpa_hexdump(MSG_DEBUG, "nl80211: QCA test event", data, len);
+		break;
+	case QCA_NL80211_VENDOR_SUBCMD_AVOID_FREQUENCY:
+		qca_nl80211_avoid_freq(drv, data, len);
+		break;
+	case QCA_NL80211_VENDOR_SUBCMD_KEY_MGMT_ROAM_AUTH:
+		qca_nl80211_key_mgmt_auth(drv, data, len);
+		break;
+	case QCA_NL80211_VENDOR_SUBCMD_DO_ACS:
+		qca_nl80211_acs_select_ch(drv, data, len);
+		break;
+	case QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_CAC_STARTED:
+	case QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_CAC_FINISHED:
+	case QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_CAC_ABORTED:
+	case QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_CAC_NOP_FINISHED:
+	case QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_RADAR_DETECTED:
+		qca_nl80211_dfs_offload_radar_event(drv, subcmd, data, len);
+		break;
+	default:
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: Ignore unsupported QCA vendor event %u",
+			   subcmd);
+		break;
+	}
+}
+
+
+static void nl80211_vendor_event(struct wpa_driver_nl80211_data *drv,
+				 struct nlattr **tb)
+{
+	u32 vendor_id, subcmd, wiphy = 0;
+	int wiphy_idx;
+	u8 *data = NULL;
+	size_t len = 0;
+
+	if (!tb[NL80211_ATTR_VENDOR_ID] ||
+	    !tb[NL80211_ATTR_VENDOR_SUBCMD])
+		return;
+
+	vendor_id = nla_get_u32(tb[NL80211_ATTR_VENDOR_ID]);
+	subcmd = nla_get_u32(tb[NL80211_ATTR_VENDOR_SUBCMD]);
+
+	if (tb[NL80211_ATTR_WIPHY])
+		wiphy = nla_get_u32(tb[NL80211_ATTR_WIPHY]);
+
+	wpa_printf(MSG_DEBUG, "nl80211: Vendor event: wiphy=%u vendor_id=0x%x subcmd=%u",
+		   wiphy, vendor_id, subcmd);
+
+	if (tb[NL80211_ATTR_VENDOR_DATA]) {
+		data = nla_data(tb[NL80211_ATTR_VENDOR_DATA]);
+		len = nla_len(tb[NL80211_ATTR_VENDOR_DATA]);
+		wpa_hexdump(MSG_MSGDUMP, "nl80211: Vendor data", data, len);
+	}
+
+	wiphy_idx = nl80211_get_wiphy_index(drv->first_bss);
+	if (wiphy_idx >= 0 && wiphy_idx != (int) wiphy) {
+		wpa_printf(MSG_DEBUG, "nl80211: Ignore vendor event for foreign wiphy %u (own: %d)",
+			   wiphy, wiphy_idx);
+		return;
+	}
+
+	switch (vendor_id) {
+	case OUI_QCA:
+		nl80211_vendor_event_qca(drv, subcmd, data, len);
+		break;
+	default:
+		wpa_printf(MSG_DEBUG, "nl80211: Ignore unsupported vendor event");
+		break;
+	}
+}
+
+
+static void nl80211_reg_change_event(struct wpa_driver_nl80211_data *drv,
+				     struct nlattr *tb[])
+{
+	union wpa_event_data data;
+	enum nl80211_reg_initiator init;
+
+	wpa_printf(MSG_DEBUG, "nl80211: Regulatory domain change");
+
+	if (tb[NL80211_ATTR_REG_INITIATOR] == NULL)
+		return;
+
+	os_memset(&data, 0, sizeof(data));
+	init = nla_get_u8(tb[NL80211_ATTR_REG_INITIATOR]);
+	wpa_printf(MSG_DEBUG, " * initiator=%d", init);
+	switch (init) {
+	case NL80211_REGDOM_SET_BY_CORE:
+		data.channel_list_changed.initiator = REGDOM_SET_BY_CORE;
+		break;
+	case NL80211_REGDOM_SET_BY_USER:
+		data.channel_list_changed.initiator = REGDOM_SET_BY_USER;
+		break;
+	case NL80211_REGDOM_SET_BY_DRIVER:
+		data.channel_list_changed.initiator = REGDOM_SET_BY_DRIVER;
+		break;
+	case NL80211_REGDOM_SET_BY_COUNTRY_IE:
+		data.channel_list_changed.initiator = REGDOM_SET_BY_COUNTRY_IE;
+		break;
+	}
+
+	if (tb[NL80211_ATTR_REG_TYPE]) {
+		enum nl80211_reg_type type;
+		type = nla_get_u8(tb[NL80211_ATTR_REG_TYPE]);
+		wpa_printf(MSG_DEBUG, " * type=%d", type);
+		switch (type) {
+		case NL80211_REGDOM_TYPE_COUNTRY:
+			data.channel_list_changed.type = REGDOM_TYPE_COUNTRY;
+			break;
+		case NL80211_REGDOM_TYPE_WORLD:
+			data.channel_list_changed.type = REGDOM_TYPE_WORLD;
+			break;
+		case NL80211_REGDOM_TYPE_CUSTOM_WORLD:
+			data.channel_list_changed.type =
+				REGDOM_TYPE_CUSTOM_WORLD;
+			break;
+		case NL80211_REGDOM_TYPE_INTERSECTION:
+			data.channel_list_changed.type =
+				REGDOM_TYPE_INTERSECTION;
+			break;
+		}
+	}
+
+	if (tb[NL80211_ATTR_REG_ALPHA2]) {
+		os_strlcpy(data.channel_list_changed.alpha2,
+			   nla_get_string(tb[NL80211_ATTR_REG_ALPHA2]),
+			   sizeof(data.channel_list_changed.alpha2));
+		wpa_printf(MSG_DEBUG, " * alpha2=%s",
+			   data.channel_list_changed.alpha2);
+	}
+
+	wpa_supplicant_event(drv->ctx, EVENT_CHANNEL_LIST_CHANGED, &data);
+}
+
+
+static void do_process_drv_event(struct i802_bss *bss, int cmd,
+				 struct nlattr **tb)
+{
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	union wpa_event_data data;
+
+	wpa_printf(MSG_DEBUG, "nl80211: Drv Event %d (%s) received for %s",
+		   cmd, nl80211_command_to_string(cmd), bss->ifname);
+
+	if (cmd == NL80211_CMD_ROAM &&
+	    (drv->capa.flags & WPA_DRIVER_FLAGS_KEY_MGMT_OFFLOAD)) {
+		/*
+		 * Device will use roam+auth vendor event to indicate
+		 * roaming, so ignore the regular roam event.
+		 */
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: Ignore roam event (cmd=%d), device will use vendor event roam+auth",
+			   cmd);
+		return;
+	}
+
+	if (drv->ap_scan_as_station != NL80211_IFTYPE_UNSPECIFIED &&
+	    (cmd == NL80211_CMD_NEW_SCAN_RESULTS ||
+	     cmd == NL80211_CMD_SCAN_ABORTED)) {
+		wpa_driver_nl80211_set_mode(drv->first_bss,
+					    drv->ap_scan_as_station);
+		drv->ap_scan_as_station = NL80211_IFTYPE_UNSPECIFIED;
+	}
+
+	switch (cmd) {
+	case NL80211_CMD_TRIGGER_SCAN:
+		wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: Scan trigger");
+		drv->scan_state = SCAN_STARTED;
+		if (drv->scan_for_auth) {
+			/*
+			 * Cannot indicate EVENT_SCAN_STARTED here since we skip
+			 * EVENT_SCAN_RESULTS in scan_for_auth case and the
+			 * upper layer implementation could get confused about
+			 * scanning state.
+			 */
+			wpa_printf(MSG_DEBUG, "nl80211: Do not indicate scan-start event due to internal scan_for_auth");
+			break;
+		}
+		wpa_supplicant_event(drv->ctx, EVENT_SCAN_STARTED, NULL);
+		break;
+	case NL80211_CMD_START_SCHED_SCAN:
+		wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: Sched scan started");
+		drv->scan_state = SCHED_SCAN_STARTED;
+		break;
+	case NL80211_CMD_SCHED_SCAN_STOPPED:
+		wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: Sched scan stopped");
+		drv->scan_state = SCHED_SCAN_STOPPED;
+		wpa_supplicant_event(drv->ctx, EVENT_SCHED_SCAN_STOPPED, NULL);
+		break;
+	case NL80211_CMD_NEW_SCAN_RESULTS:
+		wpa_dbg(drv->ctx, MSG_DEBUG,
+			"nl80211: New scan results available");
+		drv->scan_state = SCAN_COMPLETED;
+		drv->scan_complete_events = 1;
+		eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, drv,
+				     drv->ctx);
+		send_scan_event(drv, 0, tb);
+		break;
+	case NL80211_CMD_SCHED_SCAN_RESULTS:
+		wpa_dbg(drv->ctx, MSG_DEBUG,
+			"nl80211: New sched scan results available");
+		drv->scan_state = SCHED_SCAN_RESULTS;
+		send_scan_event(drv, 0, tb);
+		break;
+	case NL80211_CMD_SCAN_ABORTED:
+		wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: Scan aborted");
+		drv->scan_state = SCAN_ABORTED;
+		/*
+		 * Need to indicate that scan results are available in order
+		 * not to make wpa_supplicant stop its scanning.
+		 */
+		eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, drv,
+				     drv->ctx);
+		send_scan_event(drv, 1, tb);
+		break;
+	case NL80211_CMD_AUTHENTICATE:
+	case NL80211_CMD_ASSOCIATE:
+	case NL80211_CMD_DEAUTHENTICATE:
+	case NL80211_CMD_DISASSOCIATE:
+	case NL80211_CMD_FRAME_TX_STATUS:
+	case NL80211_CMD_UNPROT_DEAUTHENTICATE:
+	case NL80211_CMD_UNPROT_DISASSOCIATE:
+		mlme_event(bss, cmd, tb[NL80211_ATTR_FRAME],
+			   tb[NL80211_ATTR_MAC], tb[NL80211_ATTR_TIMED_OUT],
+			   tb[NL80211_ATTR_WIPHY_FREQ], tb[NL80211_ATTR_ACK],
+			   tb[NL80211_ATTR_COOKIE],
+			   tb[NL80211_ATTR_RX_SIGNAL_DBM],
+			   tb[NL80211_ATTR_STA_WME]);
+		break;
+	case NL80211_CMD_CONNECT:
+	case NL80211_CMD_ROAM:
+		mlme_event_connect(drv, cmd,
+				   tb[NL80211_ATTR_STATUS_CODE],
+				   tb[NL80211_ATTR_MAC],
+				   tb[NL80211_ATTR_REQ_IE],
+				   tb[NL80211_ATTR_RESP_IE],
+				   NULL, NULL, NULL, NULL);
+		break;
+	case NL80211_CMD_CH_SWITCH_NOTIFY:
+		mlme_event_ch_switch(drv,
+				     tb[NL80211_ATTR_IFINDEX],
+				     tb[NL80211_ATTR_WIPHY_FREQ],
+				     tb[NL80211_ATTR_WIPHY_CHANNEL_TYPE],
+				     tb[NL80211_ATTR_CHANNEL_WIDTH],
+				     tb[NL80211_ATTR_CENTER_FREQ1],
+				     tb[NL80211_ATTR_CENTER_FREQ2]);
+		break;
+	case NL80211_CMD_DISCONNECT:
+		mlme_event_disconnect(drv, tb[NL80211_ATTR_REASON_CODE],
+				      tb[NL80211_ATTR_MAC],
+				      tb[NL80211_ATTR_DISCONNECTED_BY_AP]);
+		break;
+	case NL80211_CMD_MICHAEL_MIC_FAILURE:
+		mlme_event_michael_mic_failure(bss, tb);
+		break;
+	case NL80211_CMD_JOIN_IBSS:
+		mlme_event_join_ibss(drv, tb);
+		break;
+	case NL80211_CMD_REMAIN_ON_CHANNEL:
+		mlme_event_remain_on_channel(drv, 0, tb);
+		break;
+	case NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL:
+		mlme_event_remain_on_channel(drv, 1, tb);
+		break;
+	case NL80211_CMD_NOTIFY_CQM:
+		nl80211_cqm_event(drv, tb);
+		break;
+	case NL80211_CMD_REG_CHANGE:
+		nl80211_reg_change_event(drv, tb);
+		break;
+	case NL80211_CMD_REG_BEACON_HINT:
+		wpa_printf(MSG_DEBUG, "nl80211: Regulatory beacon hint");
+		os_memset(&data, 0, sizeof(data));
+		data.channel_list_changed.initiator = REGDOM_BEACON_HINT;
+		wpa_supplicant_event(drv->ctx, EVENT_CHANNEL_LIST_CHANGED,
+				     &data);
+		break;
+	case NL80211_CMD_NEW_STATION:
+		nl80211_new_station_event(drv, bss, tb);
+		break;
+	case NL80211_CMD_DEL_STATION:
+		nl80211_del_station_event(drv, tb);
+		break;
+	case NL80211_CMD_SET_REKEY_OFFLOAD:
+		nl80211_rekey_offload_event(drv, tb);
+		break;
+	case NL80211_CMD_PMKSA_CANDIDATE:
+		nl80211_pmksa_candidate_event(drv, tb);
+		break;
+	case NL80211_CMD_PROBE_CLIENT:
+		nl80211_client_probe_event(drv, tb);
+		break;
+	case NL80211_CMD_TDLS_OPER:
+		nl80211_tdls_oper_event(drv, tb);
+		break;
+	case NL80211_CMD_CONN_FAILED:
+		nl80211_connect_failed_event(drv, tb);
+		break;
+	case NL80211_CMD_FT_EVENT:
+		mlme_event_ft_event(drv, tb);
+		break;
+	case NL80211_CMD_RADAR_DETECT:
+		nl80211_radar_event(drv, tb);
+		break;
+	case NL80211_CMD_STOP_AP:
+		nl80211_stop_ap(drv, tb);
+		break;
+	case NL80211_CMD_VENDOR:
+		nl80211_vendor_event(drv, tb);
+		break;
+	case NL80211_CMD_NEW_PEER_CANDIDATE:
+		nl80211_new_peer_candidate(drv, tb);
+		break;
+	default:
+		wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: Ignored unknown event "
+			"(cmd=%d)", cmd);
+		break;
+	}
+}
+
+
+int process_global_event(struct nl_msg *msg, void *arg)
+{
+	struct nl80211_global *global = arg;
+	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+	struct nlattr *tb[NL80211_ATTR_MAX + 1];
+	struct wpa_driver_nl80211_data *drv, *tmp;
+	int ifidx = -1;
+	struct i802_bss *bss;
+	u64 wdev_id = 0;
+	int wdev_id_set = 0;
+
+	nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+		  genlmsg_attrlen(gnlh, 0), NULL);
+
+	if (tb[NL80211_ATTR_IFINDEX])
+		ifidx = nla_get_u32(tb[NL80211_ATTR_IFINDEX]);
+	else if (tb[NL80211_ATTR_WDEV]) {
+		wdev_id = nla_get_u64(tb[NL80211_ATTR_WDEV]);
+		wdev_id_set = 1;
+	}
+
+	dl_list_for_each_safe(drv, tmp, &global->interfaces,
+			      struct wpa_driver_nl80211_data, list) {
+		for (bss = drv->first_bss; bss; bss = bss->next) {
+			if ((ifidx == -1 && !wdev_id_set) ||
+			    ifidx == bss->ifindex ||
+			    (wdev_id_set && bss->wdev_id_set &&
+			     wdev_id == bss->wdev_id)) {
+				do_process_drv_event(bss, gnlh->cmd, tb);
+				return NL_SKIP;
+			}
+		}
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: Ignored event (cmd=%d) for foreign interface (ifindex %d wdev 0x%llx)",
+			   gnlh->cmd, ifidx, (long long unsigned int) wdev_id);
+	}
+
+	return NL_SKIP;
+}
+
+
+int process_bss_event(struct nl_msg *msg, void *arg)
+{
+	struct i802_bss *bss = arg;
+	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+	struct nlattr *tb[NL80211_ATTR_MAX + 1];
+
+	nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+		  genlmsg_attrlen(gnlh, 0), NULL);
+
+	wpa_printf(MSG_DEBUG, "nl80211: BSS Event %d (%s) received for %s",
+		   gnlh->cmd, nl80211_command_to_string(gnlh->cmd),
+		   bss->ifname);
+
+	switch (gnlh->cmd) {
+	case NL80211_CMD_FRAME:
+	case NL80211_CMD_FRAME_TX_STATUS:
+		mlme_event(bss, gnlh->cmd, tb[NL80211_ATTR_FRAME],
+			   tb[NL80211_ATTR_MAC], tb[NL80211_ATTR_TIMED_OUT],
+			   tb[NL80211_ATTR_WIPHY_FREQ], tb[NL80211_ATTR_ACK],
+			   tb[NL80211_ATTR_COOKIE],
+			   tb[NL80211_ATTR_RX_SIGNAL_DBM],
+			   tb[NL80211_ATTR_STA_WME]);
+		break;
+	case NL80211_CMD_UNEXPECTED_FRAME:
+		nl80211_spurious_frame(bss, tb, 0);
+		break;
+	case NL80211_CMD_UNEXPECTED_4ADDR_FRAME:
+		nl80211_spurious_frame(bss, tb, 1);
+		break;
+	default:
+		wpa_printf(MSG_DEBUG, "nl80211: Ignored unknown event "
+			   "(cmd=%d)", gnlh->cmd);
+		break;
+	}
+
+	return NL_SKIP;
+}
diff --git a/src/drivers/driver_nl80211_monitor.c b/src/drivers/driver_nl80211_monitor.c
new file mode 100644
index 0000000..45385da
--- /dev/null
+++ b/src/drivers/driver_nl80211_monitor.c
@@ -0,0 +1,491 @@
+/*
+ * Driver interaction with Linux nl80211/cfg80211 - AP monitor interface
+ * Copyright (c) 2002-2014, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2003-2004, Instant802 Networks, Inc.
+ * Copyright (c) 2005-2006, Devicescape Software, Inc.
+ * Copyright (c) 2007, Johannes Berg <johannes@sipsolutions.net>
+ * Copyright (c) 2009-2010, Atheros Communications
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+#include <netpacket/packet.h>
+#include <linux/filter.h>
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "common/ieee802_11_defs.h"
+#include "common/ieee802_11_common.h"
+#include "linux_ioctl.h"
+#include "radiotap_iter.h"
+#include "driver_nl80211.h"
+
+
+static void handle_tx_callback(void *ctx, u8 *buf, size_t len, int ok)
+{
+	struct ieee80211_hdr *hdr;
+	u16 fc;
+	union wpa_event_data event;
+
+	hdr = (struct ieee80211_hdr *) buf;
+	fc = le_to_host16(hdr->frame_control);
+
+	os_memset(&event, 0, sizeof(event));
+	event.tx_status.type = WLAN_FC_GET_TYPE(fc);
+	event.tx_status.stype = WLAN_FC_GET_STYPE(fc);
+	event.tx_status.dst = hdr->addr1;
+	event.tx_status.data = buf;
+	event.tx_status.data_len = len;
+	event.tx_status.ack = ok;
+	wpa_supplicant_event(ctx, EVENT_TX_STATUS, &event);
+}
+
+
+static void from_unknown_sta(struct wpa_driver_nl80211_data *drv,
+			     u8 *buf, size_t len)
+{
+	struct ieee80211_hdr *hdr = (void *)buf;
+	u16 fc;
+	union wpa_event_data event;
+
+	if (len < sizeof(*hdr))
+		return;
+
+	fc = le_to_host16(hdr->frame_control);
+
+	os_memset(&event, 0, sizeof(event));
+	event.rx_from_unknown.bssid = get_hdr_bssid(hdr, len);
+	event.rx_from_unknown.addr = hdr->addr2;
+	event.rx_from_unknown.wds = (fc & (WLAN_FC_FROMDS | WLAN_FC_TODS)) ==
+		(WLAN_FC_FROMDS | WLAN_FC_TODS);
+	wpa_supplicant_event(drv->ctx, EVENT_RX_FROM_UNKNOWN, &event);
+}
+
+
+static void handle_frame(struct wpa_driver_nl80211_data *drv,
+			 u8 *buf, size_t len, int datarate, int ssi_signal)
+{
+	struct ieee80211_hdr *hdr;
+	u16 fc;
+	union wpa_event_data event;
+
+	hdr = (struct ieee80211_hdr *) buf;
+	fc = le_to_host16(hdr->frame_control);
+
+	switch (WLAN_FC_GET_TYPE(fc)) {
+	case WLAN_FC_TYPE_MGMT:
+		os_memset(&event, 0, sizeof(event));
+		event.rx_mgmt.frame = buf;
+		event.rx_mgmt.frame_len = len;
+		event.rx_mgmt.datarate = datarate;
+		event.rx_mgmt.ssi_signal = ssi_signal;
+		wpa_supplicant_event(drv->ctx, EVENT_RX_MGMT, &event);
+		break;
+	case WLAN_FC_TYPE_CTRL:
+		/* can only get here with PS-Poll frames */
+		wpa_printf(MSG_DEBUG, "CTRL");
+		from_unknown_sta(drv, buf, len);
+		break;
+	case WLAN_FC_TYPE_DATA:
+		from_unknown_sta(drv, buf, len);
+		break;
+	}
+}
+
+
+static void handle_monitor_read(int sock, void *eloop_ctx, void *sock_ctx)
+{
+	struct wpa_driver_nl80211_data *drv = eloop_ctx;
+	int len;
+	unsigned char buf[3000];
+	struct ieee80211_radiotap_iterator iter;
+	int ret;
+	int datarate = 0, ssi_signal = 0;
+	int injected = 0, failed = 0, rxflags = 0;
+
+	len = recv(sock, buf, sizeof(buf), 0);
+	if (len < 0) {
+		wpa_printf(MSG_ERROR, "nl80211: Monitor socket recv failed: %s",
+			   strerror(errno));
+		return;
+	}
+
+	if (ieee80211_radiotap_iterator_init(&iter, (void *) buf, len, NULL)) {
+		wpa_printf(MSG_INFO, "nl80211: received invalid radiotap frame");
+		return;
+	}
+
+	while (1) {
+		ret = ieee80211_radiotap_iterator_next(&iter);
+		if (ret == -ENOENT)
+			break;
+		if (ret) {
+			wpa_printf(MSG_INFO, "nl80211: received invalid radiotap frame (%d)",
+				   ret);
+			return;
+		}
+		switch (iter.this_arg_index) {
+		case IEEE80211_RADIOTAP_FLAGS:
+			if (*iter.this_arg & IEEE80211_RADIOTAP_F_FCS)
+				len -= 4;
+			break;
+		case IEEE80211_RADIOTAP_RX_FLAGS:
+			rxflags = 1;
+			break;
+		case IEEE80211_RADIOTAP_TX_FLAGS:
+			injected = 1;
+			failed = le_to_host16((*(uint16_t *) iter.this_arg)) &
+					IEEE80211_RADIOTAP_F_TX_FAIL;
+			break;
+		case IEEE80211_RADIOTAP_DATA_RETRIES:
+			break;
+		case IEEE80211_RADIOTAP_CHANNEL:
+			/* TODO: convert from freq/flags to channel number */
+			break;
+		case IEEE80211_RADIOTAP_RATE:
+			datarate = *iter.this_arg * 5;
+			break;
+		case IEEE80211_RADIOTAP_DBM_ANTSIGNAL:
+			ssi_signal = (s8) *iter.this_arg;
+			break;
+		}
+	}
+
+	if (rxflags && injected)
+		return;
+
+	if (!injected)
+		handle_frame(drv, buf + iter._max_length,
+			     len - iter._max_length, datarate, ssi_signal);
+	else
+		handle_tx_callback(drv->ctx, buf + iter._max_length,
+				   len - iter._max_length, !failed);
+}
+
+
+/*
+ * we post-process the filter code later and rewrite
+ * this to the offset to the last instruction
+ */
+#define PASS	0xFF
+#define FAIL	0xFE
+
+static struct sock_filter msock_filter_insns[] = {
+	/*
+	 * do a little-endian load of the radiotap length field
+	 */
+	/* load lower byte into A */
+	BPF_STMT(BPF_LD  | BPF_B | BPF_ABS, 2),
+	/* put it into X (== index register) */
+	BPF_STMT(BPF_MISC| BPF_TAX, 0),
+	/* load upper byte into A */
+	BPF_STMT(BPF_LD  | BPF_B | BPF_ABS, 3),
+	/* left-shift it by 8 */
+	BPF_STMT(BPF_ALU | BPF_LSH | BPF_K, 8),
+	/* or with X */
+	BPF_STMT(BPF_ALU | BPF_OR | BPF_X, 0),
+	/* put result into X */
+	BPF_STMT(BPF_MISC| BPF_TAX, 0),
+
+	/*
+	 * Allow management frames through, this also gives us those
+	 * management frames that we sent ourselves with status
+	 */
+	/* load the lower byte of the IEEE 802.11 frame control field */
+	BPF_STMT(BPF_LD  | BPF_B | BPF_IND, 0),
+	/* mask off frame type and version */
+	BPF_STMT(BPF_ALU | BPF_AND | BPF_K, 0xF),
+	/* accept frame if it's both 0, fall through otherwise */
+	BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0, PASS, 0),
+
+	/*
+	 * TODO: add a bit to radiotap RX flags that indicates
+	 * that the sending station is not associated, then
+	 * add a filter here that filters on our DA and that flag
+	 * to allow us to deauth frames to that bad station.
+	 *
+	 * For now allow all To DS data frames through.
+	 */
+	/* load the IEEE 802.11 frame control field */
+	BPF_STMT(BPF_LD  | BPF_H | BPF_IND, 0),
+	/* mask off frame type, version and DS status */
+	BPF_STMT(BPF_ALU | BPF_AND | BPF_K, 0x0F03),
+	/* accept frame if version 0, type 2 and To DS, fall through otherwise
+	 */
+	BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0x0801, PASS, 0),
+
+#if 0
+	/*
+	 * drop non-data frames
+	 */
+	/* load the lower byte of the frame control field */
+	BPF_STMT(BPF_LD   | BPF_B | BPF_IND, 0),
+	/* mask off QoS bit */
+	BPF_STMT(BPF_ALU  | BPF_AND | BPF_K, 0x0c),
+	/* drop non-data frames */
+	BPF_JUMP(BPF_JMP  | BPF_JEQ | BPF_K, 8, 0, FAIL),
+#endif
+	/* load the upper byte of the frame control field */
+	BPF_STMT(BPF_LD   | BPF_B | BPF_IND, 1),
+	/* mask off toDS/fromDS */
+	BPF_STMT(BPF_ALU  | BPF_AND | BPF_K, 0x03),
+	/* accept WDS frames */
+	BPF_JUMP(BPF_JMP  | BPF_JEQ | BPF_K, 3, PASS, 0),
+
+	/*
+	 * add header length to index
+	 */
+	/* load the lower byte of the frame control field */
+	BPF_STMT(BPF_LD   | BPF_B | BPF_IND, 0),
+	/* mask off QoS bit */
+	BPF_STMT(BPF_ALU  | BPF_AND | BPF_K, 0x80),
+	/* right shift it by 6 to give 0 or 2 */
+	BPF_STMT(BPF_ALU  | BPF_RSH | BPF_K, 6),
+	/* add data frame header length */
+	BPF_STMT(BPF_ALU  | BPF_ADD | BPF_K, 24),
+	/* add index, was start of 802.11 header */
+	BPF_STMT(BPF_ALU  | BPF_ADD | BPF_X, 0),
+	/* move to index, now start of LL header */
+	BPF_STMT(BPF_MISC | BPF_TAX, 0),
+
+	/*
+	 * Accept empty data frames, we use those for
+	 * polling activity.
+	 */
+	BPF_STMT(BPF_LD  | BPF_W | BPF_LEN, 0),
+	BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_X, 0, PASS, 0),
+
+	/*
+	 * Accept EAPOL frames
+	 */
+	BPF_STMT(BPF_LD  | BPF_W | BPF_IND, 0),
+	BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0xAAAA0300, 0, FAIL),
+	BPF_STMT(BPF_LD  | BPF_W | BPF_IND, 4),
+	BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0x0000888E, PASS, FAIL),
+
+	/* keep these last two statements or change the code below */
+	/* return 0 == "DROP" */
+	BPF_STMT(BPF_RET | BPF_K, 0),
+	/* return ~0 == "keep all" */
+	BPF_STMT(BPF_RET | BPF_K, ~0),
+};
+
+static struct sock_fprog msock_filter = {
+	.len = ARRAY_SIZE(msock_filter_insns),
+	.filter = msock_filter_insns,
+};
+
+
+static int add_monitor_filter(int s)
+{
+	int idx;
+
+	/* rewrite all PASS/FAIL jump offsets */
+	for (idx = 0; idx < msock_filter.len; idx++) {
+		struct sock_filter *insn = &msock_filter_insns[idx];
+
+		if (BPF_CLASS(insn->code) == BPF_JMP) {
+			if (insn->code == (BPF_JMP|BPF_JA)) {
+				if (insn->k == PASS)
+					insn->k = msock_filter.len - idx - 2;
+				else if (insn->k == FAIL)
+					insn->k = msock_filter.len - idx - 3;
+			}
+
+			if (insn->jt == PASS)
+				insn->jt = msock_filter.len - idx - 2;
+			else if (insn->jt == FAIL)
+				insn->jt = msock_filter.len - idx - 3;
+
+			if (insn->jf == PASS)
+				insn->jf = msock_filter.len - idx - 2;
+			else if (insn->jf == FAIL)
+				insn->jf = msock_filter.len - idx - 3;
+		}
+	}
+
+	if (setsockopt(s, SOL_SOCKET, SO_ATTACH_FILTER,
+		       &msock_filter, sizeof(msock_filter))) {
+		wpa_printf(MSG_ERROR, "nl80211: setsockopt(SO_ATTACH_FILTER) failed: %s",
+			   strerror(errno));
+		return -1;
+	}
+
+	return 0;
+}
+
+
+void nl80211_remove_monitor_interface(struct wpa_driver_nl80211_data *drv)
+{
+	if (drv->monitor_refcount > 0)
+		drv->monitor_refcount--;
+	wpa_printf(MSG_DEBUG, "nl80211: Remove monitor interface: refcount=%d",
+		   drv->monitor_refcount);
+	if (drv->monitor_refcount > 0)
+		return;
+
+	if (drv->monitor_ifidx >= 0) {
+		nl80211_remove_iface(drv, drv->monitor_ifidx);
+		drv->monitor_ifidx = -1;
+	}
+	if (drv->monitor_sock >= 0) {
+		eloop_unregister_read_sock(drv->monitor_sock);
+		close(drv->monitor_sock);
+		drv->monitor_sock = -1;
+	}
+}
+
+
+int nl80211_create_monitor_interface(struct wpa_driver_nl80211_data *drv)
+{
+	char buf[IFNAMSIZ];
+	struct sockaddr_ll ll;
+	int optval;
+	socklen_t optlen;
+
+	if (drv->monitor_ifidx >= 0) {
+		drv->monitor_refcount++;
+		wpa_printf(MSG_DEBUG, "nl80211: Re-use existing monitor interface: refcount=%d",
+			   drv->monitor_refcount);
+		return 0;
+	}
+
+	if (os_strncmp(drv->first_bss->ifname, "p2p-", 4) == 0) {
+		/*
+		 * P2P interface name is of the format p2p-%s-%d. For monitor
+		 * interface name corresponding to P2P GO, replace "p2p-" with
+		 * "mon-" to retain the same interface name length and to
+		 * indicate that it is a monitor interface.
+		 */
+		snprintf(buf, IFNAMSIZ, "mon-%s", drv->first_bss->ifname + 4);
+	} else {
+		/* Non-P2P interface with AP functionality. */
+		snprintf(buf, IFNAMSIZ, "mon.%s", drv->first_bss->ifname);
+	}
+
+	buf[IFNAMSIZ - 1] = '\0';
+
+	drv->monitor_ifidx =
+		nl80211_create_iface(drv, buf, NL80211_IFTYPE_MONITOR, NULL,
+				     0, NULL, NULL, 0);
+
+	if (drv->monitor_ifidx == -EOPNOTSUPP) {
+		/*
+		 * This is backward compatibility for a few versions of
+		 * the kernel only that didn't advertise the right
+		 * attributes for the only driver that then supported
+		 * AP mode w/o monitor -- ath6kl.
+		 */
+		wpa_printf(MSG_DEBUG, "nl80211: Driver does not support "
+			   "monitor interface type - try to run without it");
+		drv->device_ap_sme = 1;
+	}
+
+	if (drv->monitor_ifidx < 0)
+		return -1;
+
+	if (linux_set_iface_flags(drv->global->ioctl_sock, buf, 1))
+		goto error;
+
+	memset(&ll, 0, sizeof(ll));
+	ll.sll_family = AF_PACKET;
+	ll.sll_ifindex = drv->monitor_ifidx;
+	drv->monitor_sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
+	if (drv->monitor_sock < 0) {
+		wpa_printf(MSG_ERROR, "nl80211: socket[PF_PACKET,SOCK_RAW] failed: %s",
+			   strerror(errno));
+		goto error;
+	}
+
+	if (add_monitor_filter(drv->monitor_sock)) {
+		wpa_printf(MSG_INFO, "Failed to set socket filter for monitor "
+			   "interface; do filtering in user space");
+		/* This works, but will cost in performance. */
+	}
+
+	if (bind(drv->monitor_sock, (struct sockaddr *) &ll, sizeof(ll)) < 0) {
+		wpa_printf(MSG_ERROR, "nl80211: monitor socket bind failed: %s",
+			   strerror(errno));
+		goto error;
+	}
+
+	optlen = sizeof(optval);
+	optval = 20;
+	if (setsockopt
+	    (drv->monitor_sock, SOL_SOCKET, SO_PRIORITY, &optval, optlen)) {
+		wpa_printf(MSG_ERROR, "nl80211: Failed to set socket priority: %s",
+			   strerror(errno));
+		goto error;
+	}
+
+	if (eloop_register_read_sock(drv->monitor_sock, handle_monitor_read,
+				     drv, NULL)) {
+		wpa_printf(MSG_INFO, "nl80211: Could not register monitor read socket");
+		goto error;
+	}
+
+	drv->monitor_refcount++;
+	return 0;
+ error:
+	nl80211_remove_monitor_interface(drv);
+	return -1;
+}
+
+
+int nl80211_send_monitor(struct wpa_driver_nl80211_data *drv,
+			 const void *data, size_t len,
+			 int encrypt, int noack)
+{
+	__u8 rtap_hdr[] = {
+		0x00, 0x00, /* radiotap version */
+		0x0e, 0x00, /* radiotap length */
+		0x02, 0xc0, 0x00, 0x00, /* bmap: flags, tx and rx flags */
+		IEEE80211_RADIOTAP_F_FRAG, /* F_FRAG (fragment if required) */
+		0x00,       /* padding */
+		0x00, 0x00, /* RX and TX flags to indicate that */
+		0x00, 0x00, /* this is the injected frame directly */
+	};
+	struct iovec iov[2] = {
+		{
+			.iov_base = &rtap_hdr,
+			.iov_len = sizeof(rtap_hdr),
+		},
+		{
+			.iov_base = (void *) data,
+			.iov_len = len,
+		}
+	};
+	struct msghdr msg = {
+		.msg_name = NULL,
+		.msg_namelen = 0,
+		.msg_iov = iov,
+		.msg_iovlen = 2,
+		.msg_control = NULL,
+		.msg_controllen = 0,
+		.msg_flags = 0,
+	};
+	int res;
+	u16 txflags = 0;
+
+	if (encrypt)
+		rtap_hdr[8] |= IEEE80211_RADIOTAP_F_WEP;
+
+	if (drv->monitor_sock < 0) {
+		wpa_printf(MSG_DEBUG, "nl80211: No monitor socket available "
+			   "for %s", __func__);
+		return -1;
+	}
+
+	if (noack)
+		txflags |= IEEE80211_RADIOTAP_F_TX_NOACK;
+	WPA_PUT_LE16(&rtap_hdr[12], txflags);
+
+	res = sendmsg(drv->monitor_sock, &msg, 0);
+	if (res < 0) {
+		wpa_printf(MSG_INFO, "nl80211: sendmsg: %s", strerror(errno));
+		return -1;
+	}
+	return 0;
+}
diff --git a/src/drivers/driver_nl80211_scan.c b/src/drivers/driver_nl80211_scan.c
new file mode 100644
index 0000000..9cd3162
--- /dev/null
+++ b/src/drivers/driver_nl80211_scan.c
@@ -0,0 +1,775 @@
+/*
+ * Driver interaction with Linux nl80211/cfg80211 - Scanning
+ * Copyright (c) 2002-2014, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2007, Johannes Berg <johannes@sipsolutions.net>
+ * Copyright (c) 2009-2010, Atheros Communications
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+#include <netlink/genl/genl.h>
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "common/ieee802_11_defs.h"
+#include "driver_nl80211.h"
+
+
+static int get_noise_for_scan_results(struct nl_msg *msg, void *arg)
+{
+	struct nlattr *tb[NL80211_ATTR_MAX + 1];
+	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+	struct nlattr *sinfo[NL80211_SURVEY_INFO_MAX + 1];
+	static struct nla_policy survey_policy[NL80211_SURVEY_INFO_MAX + 1] = {
+		[NL80211_SURVEY_INFO_FREQUENCY] = { .type = NLA_U32 },
+		[NL80211_SURVEY_INFO_NOISE] = { .type = NLA_U8 },
+	};
+	struct wpa_scan_results *scan_results = arg;
+	struct wpa_scan_res *scan_res;
+	size_t i;
+
+	nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+		  genlmsg_attrlen(gnlh, 0), NULL);
+
+	if (!tb[NL80211_ATTR_SURVEY_INFO]) {
+		wpa_printf(MSG_DEBUG, "nl80211: Survey data missing");
+		return NL_SKIP;
+	}
+
+	if (nla_parse_nested(sinfo, NL80211_SURVEY_INFO_MAX,
+			     tb[NL80211_ATTR_SURVEY_INFO],
+			     survey_policy)) {
+		wpa_printf(MSG_DEBUG, "nl80211: Failed to parse nested "
+			   "attributes");
+		return NL_SKIP;
+	}
+
+	if (!sinfo[NL80211_SURVEY_INFO_NOISE])
+		return NL_SKIP;
+
+	if (!sinfo[NL80211_SURVEY_INFO_FREQUENCY])
+		return NL_SKIP;
+
+	for (i = 0; i < scan_results->num; ++i) {
+		scan_res = scan_results->res[i];
+		if (!scan_res)
+			continue;
+		if ((int) nla_get_u32(sinfo[NL80211_SURVEY_INFO_FREQUENCY]) !=
+		    scan_res->freq)
+			continue;
+		if (!(scan_res->flags & WPA_SCAN_NOISE_INVALID))
+			continue;
+		scan_res->noise = (s8)
+			nla_get_u8(sinfo[NL80211_SURVEY_INFO_NOISE]);
+		scan_res->flags &= ~WPA_SCAN_NOISE_INVALID;
+	}
+
+	return NL_SKIP;
+}
+
+
+static int nl80211_get_noise_for_scan_results(
+	struct wpa_driver_nl80211_data *drv,
+	struct wpa_scan_results *scan_res)
+{
+	struct nl_msg *msg;
+
+	msg = nl80211_drv_msg(drv, NLM_F_DUMP, NL80211_CMD_GET_SURVEY);
+	return send_and_recv_msgs(drv, msg, get_noise_for_scan_results,
+				  scan_res);
+}
+
+
+/**
+ * wpa_driver_nl80211_scan_timeout - Scan timeout to report scan completion
+ * @eloop_ctx: Driver private data
+ * @timeout_ctx: ctx argument given to wpa_driver_nl80211_init()
+ *
+ * This function can be used as registered timeout when starting a scan to
+ * generate a scan completed event if the driver does not report this.
+ */
+void wpa_driver_nl80211_scan_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+	struct wpa_driver_nl80211_data *drv = eloop_ctx;
+	if (drv->ap_scan_as_station != NL80211_IFTYPE_UNSPECIFIED) {
+		wpa_driver_nl80211_set_mode(drv->first_bss,
+					    drv->ap_scan_as_station);
+		drv->ap_scan_as_station = NL80211_IFTYPE_UNSPECIFIED;
+	}
+	wpa_printf(MSG_DEBUG, "Scan timeout - try to get results");
+	wpa_supplicant_event(timeout_ctx, EVENT_SCAN_RESULTS, NULL);
+}
+
+
+static struct nl_msg *
+nl80211_scan_common(struct i802_bss *bss, u8 cmd,
+		    struct wpa_driver_scan_params *params)
+{
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	struct nl_msg *msg;
+	size_t i;
+	u32 scan_flags = 0;
+
+	msg = nl80211_cmd_msg(bss, 0, cmd);
+	if (!msg)
+		return NULL;
+
+	if (params->num_ssids) {
+		struct nlattr *ssids;
+
+		ssids = nla_nest_start(msg, NL80211_ATTR_SCAN_SSIDS);
+		if (ssids == NULL)
+			goto fail;
+		for (i = 0; i < params->num_ssids; i++) {
+			wpa_hexdump_ascii(MSG_MSGDUMP, "nl80211: Scan SSID",
+					  params->ssids[i].ssid,
+					  params->ssids[i].ssid_len);
+			if (nla_put(msg, i + 1, params->ssids[i].ssid_len,
+				    params->ssids[i].ssid))
+				goto fail;
+		}
+		nla_nest_end(msg, ssids);
+	}
+
+	if (params->extra_ies) {
+		wpa_hexdump(MSG_MSGDUMP, "nl80211: Scan extra IEs",
+			    params->extra_ies, params->extra_ies_len);
+		if (nla_put(msg, NL80211_ATTR_IE, params->extra_ies_len,
+			    params->extra_ies))
+			goto fail;
+	}
+
+	if (params->freqs) {
+		struct nlattr *freqs;
+		freqs = nla_nest_start(msg, NL80211_ATTR_SCAN_FREQUENCIES);
+		if (freqs == NULL)
+			goto fail;
+		for (i = 0; params->freqs[i]; i++) {
+			wpa_printf(MSG_MSGDUMP, "nl80211: Scan frequency %u "
+				   "MHz", params->freqs[i]);
+			if (nla_put_u32(msg, i + 1, params->freqs[i]))
+				goto fail;
+		}
+		nla_nest_end(msg, freqs);
+	}
+
+	os_free(drv->filter_ssids);
+	drv->filter_ssids = params->filter_ssids;
+	params->filter_ssids = NULL;
+	drv->num_filter_ssids = params->num_filter_ssids;
+
+	if (params->only_new_results) {
+		wpa_printf(MSG_DEBUG, "nl80211: Add NL80211_SCAN_FLAG_FLUSH");
+		scan_flags |= NL80211_SCAN_FLAG_FLUSH;
+	}
+
+	if (params->low_priority && drv->have_low_prio_scan) {
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: Add NL80211_SCAN_FLAG_LOW_PRIORITY");
+		scan_flags |= NL80211_SCAN_FLAG_LOW_PRIORITY;
+	}
+
+	if (params->mac_addr_rand) {
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: Add NL80211_SCAN_FLAG_RANDOM_ADDR");
+		scan_flags |= NL80211_SCAN_FLAG_RANDOM_ADDR;
+
+		if (params->mac_addr) {
+			wpa_printf(MSG_DEBUG, "nl80211: MAC address: " MACSTR,
+				   MAC2STR(params->mac_addr));
+			if (nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN,
+				    params->mac_addr))
+				goto fail;
+		}
+
+		if (params->mac_addr_mask) {
+			wpa_printf(MSG_DEBUG, "nl80211: MAC address mask: "
+				   MACSTR, MAC2STR(params->mac_addr_mask));
+			if (nla_put(msg, NL80211_ATTR_MAC_MASK, ETH_ALEN,
+				    params->mac_addr_mask))
+				goto fail;
+		}
+	}
+
+	if (scan_flags &&
+	    nla_put_u32(msg, NL80211_ATTR_SCAN_FLAGS, scan_flags))
+		goto fail;
+
+	return msg;
+
+fail:
+	nlmsg_free(msg);
+	return NULL;
+}
+
+
+/**
+ * wpa_driver_nl80211_scan - Request the driver to initiate scan
+ * @bss: Pointer to private driver data from wpa_driver_nl80211_init()
+ * @params: Scan parameters
+ * Returns: 0 on success, -1 on failure
+ */
+int wpa_driver_nl80211_scan(struct i802_bss *bss,
+			    struct wpa_driver_scan_params *params)
+{
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	int ret = -1, timeout;
+	struct nl_msg *msg = NULL;
+
+	wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: scan request");
+	drv->scan_for_auth = 0;
+
+	msg = nl80211_scan_common(bss, NL80211_CMD_TRIGGER_SCAN, params);
+	if (!msg)
+		return -1;
+
+	if (params->p2p_probe) {
+		struct nlattr *rates;
+
+		wpa_printf(MSG_DEBUG, "nl80211: P2P probe - mask SuppRates");
+
+		rates = nla_nest_start(msg, NL80211_ATTR_SCAN_SUPP_RATES);
+		if (rates == NULL)
+			goto fail;
+
+		/*
+		 * Remove 2.4 GHz rates 1, 2, 5.5, 11 Mbps from supported rates
+		 * by masking out everything else apart from the OFDM rates 6,
+		 * 9, 12, 18, 24, 36, 48, 54 Mbps from non-MCS rates. All 5 GHz
+		 * rates are left enabled.
+		 */
+		if (nla_put(msg, NL80211_BAND_2GHZ, 8,
+			    "\x0c\x12\x18\x24\x30\x48\x60\x6c"))
+			goto fail;
+		nla_nest_end(msg, rates);
+
+		if (nla_put_flag(msg, NL80211_ATTR_TX_NO_CCK_RATE))
+			goto fail;
+	}
+
+	ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+	msg = NULL;
+	if (ret) {
+		wpa_printf(MSG_DEBUG, "nl80211: Scan trigger failed: ret=%d "
+			   "(%s)", ret, strerror(-ret));
+		if (drv->hostapd && is_ap_interface(drv->nlmode)) {
+			enum nl80211_iftype old_mode = drv->nlmode;
+
+			/*
+			 * mac80211 does not allow scan requests in AP mode, so
+			 * try to do this in station mode.
+			 */
+			if (wpa_driver_nl80211_set_mode(
+				    bss, NL80211_IFTYPE_STATION))
+				goto fail;
+
+			if (wpa_driver_nl80211_scan(bss, params)) {
+				wpa_driver_nl80211_set_mode(bss, old_mode);
+				goto fail;
+			}
+
+			/* Restore AP mode when processing scan results */
+			drv->ap_scan_as_station = old_mode;
+			ret = 0;
+		} else
+			goto fail;
+	}
+
+	drv->scan_state = SCAN_REQUESTED;
+	/* Not all drivers generate "scan completed" wireless event, so try to
+	 * read results after a timeout. */
+	timeout = 10;
+	if (drv->scan_complete_events) {
+		/*
+		 * The driver seems to deliver events to notify when scan is
+		 * complete, so use longer timeout to avoid race conditions
+		 * with scanning and following association request.
+		 */
+		timeout = 30;
+	}
+	wpa_printf(MSG_DEBUG, "Scan requested (ret=%d) - scan timeout %d "
+		   "seconds", ret, timeout);
+	eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, drv, drv->ctx);
+	eloop_register_timeout(timeout, 0, wpa_driver_nl80211_scan_timeout,
+			       drv, drv->ctx);
+
+fail:
+	nlmsg_free(msg);
+	return ret;
+}
+
+
+/**
+ * wpa_driver_nl80211_sched_scan - Initiate a scheduled scan
+ * @priv: Pointer to private driver data from wpa_driver_nl80211_init()
+ * @params: Scan parameters
+ * @interval: Interval between scan cycles in milliseconds
+ * Returns: 0 on success, -1 on failure or if not supported
+ */
+int wpa_driver_nl80211_sched_scan(void *priv,
+				  struct wpa_driver_scan_params *params,
+				  u32 interval)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	int ret = -1;
+	struct nl_msg *msg;
+	size_t i;
+
+	wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: sched_scan request");
+
+#ifdef ANDROID
+	if (!drv->capa.sched_scan_supported)
+		return android_pno_start(bss, params);
+#endif /* ANDROID */
+
+	msg = nl80211_scan_common(bss, NL80211_CMD_START_SCHED_SCAN, params);
+	if (!msg ||
+	    nla_put_u32(msg, NL80211_ATTR_SCHED_SCAN_INTERVAL, interval))
+		goto fail;
+
+	if ((drv->num_filter_ssids &&
+	    (int) drv->num_filter_ssids <= drv->capa.max_match_sets) ||
+	    params->filter_rssi) {
+		struct nlattr *match_sets;
+		match_sets = nla_nest_start(msg, NL80211_ATTR_SCHED_SCAN_MATCH);
+		if (match_sets == NULL)
+			goto fail;
+
+		for (i = 0; i < drv->num_filter_ssids; i++) {
+			struct nlattr *match_set_ssid;
+			wpa_hexdump_ascii(MSG_MSGDUMP,
+					  "nl80211: Sched scan filter SSID",
+					  drv->filter_ssids[i].ssid,
+					  drv->filter_ssids[i].ssid_len);
+
+			match_set_ssid = nla_nest_start(msg, i + 1);
+			if (match_set_ssid == NULL ||
+			    nla_put(msg, NL80211_ATTR_SCHED_SCAN_MATCH_SSID,
+				    drv->filter_ssids[i].ssid_len,
+				    drv->filter_ssids[i].ssid) ||
+			    (params->filter_rssi &&
+			     nla_put_u32(msg,
+					 NL80211_SCHED_SCAN_MATCH_ATTR_RSSI,
+					 params->filter_rssi)))
+				goto fail;
+
+			nla_nest_end(msg, match_set_ssid);
+		}
+
+		/*
+		 * Due to backward compatibility code, newer kernels treat this
+		 * matchset (with only an RSSI filter) as the default for all
+		 * other matchsets, unless it's the only one, in which case the
+		 * matchset will actually allow all SSIDs above the RSSI.
+		 */
+		if (params->filter_rssi) {
+			struct nlattr *match_set_rssi;
+			match_set_rssi = nla_nest_start(msg, 0);
+			if (match_set_rssi == NULL ||
+			    nla_put_u32(msg, NL80211_SCHED_SCAN_MATCH_ATTR_RSSI,
+					params->filter_rssi))
+				goto fail;
+			wpa_printf(MSG_MSGDUMP,
+				   "nl80211: Sched scan RSSI filter %d dBm",
+				   params->filter_rssi);
+			nla_nest_end(msg, match_set_rssi);
+		}
+
+		nla_nest_end(msg, match_sets);
+	}
+
+	ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+
+	/* TODO: if we get an error here, we should fall back to normal scan */
+
+	msg = NULL;
+	if (ret) {
+		wpa_printf(MSG_DEBUG, "nl80211: Sched scan start failed: "
+			   "ret=%d (%s)", ret, strerror(-ret));
+		goto fail;
+	}
+
+	wpa_printf(MSG_DEBUG, "nl80211: Sched scan requested (ret=%d) - "
+		   "scan interval %d msec", ret, interval);
+
+fail:
+	nlmsg_free(msg);
+	return ret;
+}
+
+
+/**
+ * wpa_driver_nl80211_stop_sched_scan - Stop a scheduled scan
+ * @priv: Pointer to private driver data from wpa_driver_nl80211_init()
+ * Returns: 0 on success, -1 on failure or if not supported
+ */
+int wpa_driver_nl80211_stop_sched_scan(void *priv)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	int ret;
+	struct nl_msg *msg;
+
+#ifdef ANDROID
+	if (!drv->capa.sched_scan_supported)
+		return android_pno_stop(bss);
+#endif /* ANDROID */
+
+	msg = nl80211_drv_msg(drv, 0, NL80211_CMD_STOP_SCHED_SCAN);
+	ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+	if (ret) {
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: Sched scan stop failed: ret=%d (%s)",
+			   ret, strerror(-ret));
+	} else {
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: Sched scan stop sent");
+	}
+
+	return ret;
+}
+
+
+const u8 * nl80211_get_ie(const u8 *ies, size_t ies_len, u8 ie)
+{
+	const u8 *end, *pos;
+
+	if (ies == NULL)
+		return NULL;
+
+	pos = ies;
+	end = ies + ies_len;
+
+	while (pos + 1 < end) {
+		if (pos + 2 + pos[1] > end)
+			break;
+		if (pos[0] == ie)
+			return pos;
+		pos += 2 + pos[1];
+	}
+
+	return NULL;
+}
+
+
+static int nl80211_scan_filtered(struct wpa_driver_nl80211_data *drv,
+				 const u8 *ie, size_t ie_len)
+{
+	const u8 *ssid;
+	size_t i;
+
+	if (drv->filter_ssids == NULL)
+		return 0;
+
+	ssid = nl80211_get_ie(ie, ie_len, WLAN_EID_SSID);
+	if (ssid == NULL)
+		return 1;
+
+	for (i = 0; i < drv->num_filter_ssids; i++) {
+		if (ssid[1] == drv->filter_ssids[i].ssid_len &&
+		    os_memcmp(ssid + 2, drv->filter_ssids[i].ssid, ssid[1]) ==
+		    0)
+			return 0;
+	}
+
+	return 1;
+}
+
+
+int bss_info_handler(struct nl_msg *msg, void *arg)
+{
+	struct nlattr *tb[NL80211_ATTR_MAX + 1];
+	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+	struct nlattr *bss[NL80211_BSS_MAX + 1];
+	static struct nla_policy bss_policy[NL80211_BSS_MAX + 1] = {
+		[NL80211_BSS_BSSID] = { .type = NLA_UNSPEC },
+		[NL80211_BSS_FREQUENCY] = { .type = NLA_U32 },
+		[NL80211_BSS_TSF] = { .type = NLA_U64 },
+		[NL80211_BSS_BEACON_INTERVAL] = { .type = NLA_U16 },
+		[NL80211_BSS_CAPABILITY] = { .type = NLA_U16 },
+		[NL80211_BSS_INFORMATION_ELEMENTS] = { .type = NLA_UNSPEC },
+		[NL80211_BSS_SIGNAL_MBM] = { .type = NLA_U32 },
+		[NL80211_BSS_SIGNAL_UNSPEC] = { .type = NLA_U8 },
+		[NL80211_BSS_STATUS] = { .type = NLA_U32 },
+		[NL80211_BSS_SEEN_MS_AGO] = { .type = NLA_U32 },
+		[NL80211_BSS_BEACON_IES] = { .type = NLA_UNSPEC },
+	};
+	struct nl80211_bss_info_arg *_arg = arg;
+	struct wpa_scan_results *res = _arg->res;
+	struct wpa_scan_res **tmp;
+	struct wpa_scan_res *r;
+	const u8 *ie, *beacon_ie;
+	size_t ie_len, beacon_ie_len;
+	u8 *pos;
+	size_t i;
+
+	nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+		  genlmsg_attrlen(gnlh, 0), NULL);
+	if (!tb[NL80211_ATTR_BSS])
+		return NL_SKIP;
+	if (nla_parse_nested(bss, NL80211_BSS_MAX, tb[NL80211_ATTR_BSS],
+			     bss_policy))
+		return NL_SKIP;
+	if (bss[NL80211_BSS_STATUS]) {
+		enum nl80211_bss_status status;
+		status = nla_get_u32(bss[NL80211_BSS_STATUS]);
+		if (status == NL80211_BSS_STATUS_ASSOCIATED &&
+		    bss[NL80211_BSS_FREQUENCY]) {
+			_arg->assoc_freq =
+				nla_get_u32(bss[NL80211_BSS_FREQUENCY]);
+			wpa_printf(MSG_DEBUG, "nl80211: Associated on %u MHz",
+				   _arg->assoc_freq);
+		}
+		if (status == NL80211_BSS_STATUS_IBSS_JOINED &&
+		    bss[NL80211_BSS_FREQUENCY]) {
+			_arg->ibss_freq =
+				nla_get_u32(bss[NL80211_BSS_FREQUENCY]);
+			wpa_printf(MSG_DEBUG, "nl80211: IBSS-joined on %u MHz",
+				   _arg->ibss_freq);
+		}
+		if (status == NL80211_BSS_STATUS_ASSOCIATED &&
+		    bss[NL80211_BSS_BSSID]) {
+			os_memcpy(_arg->assoc_bssid,
+				  nla_data(bss[NL80211_BSS_BSSID]), ETH_ALEN);
+			wpa_printf(MSG_DEBUG, "nl80211: Associated with "
+				   MACSTR, MAC2STR(_arg->assoc_bssid));
+		}
+	}
+	if (!res)
+		return NL_SKIP;
+	if (bss[NL80211_BSS_INFORMATION_ELEMENTS]) {
+		ie = nla_data(bss[NL80211_BSS_INFORMATION_ELEMENTS]);
+		ie_len = nla_len(bss[NL80211_BSS_INFORMATION_ELEMENTS]);
+	} else {
+		ie = NULL;
+		ie_len = 0;
+	}
+	if (bss[NL80211_BSS_BEACON_IES]) {
+		beacon_ie = nla_data(bss[NL80211_BSS_BEACON_IES]);
+		beacon_ie_len = nla_len(bss[NL80211_BSS_BEACON_IES]);
+	} else {
+		beacon_ie = NULL;
+		beacon_ie_len = 0;
+	}
+
+	if (nl80211_scan_filtered(_arg->drv, ie ? ie : beacon_ie,
+				  ie ? ie_len : beacon_ie_len))
+		return NL_SKIP;
+
+	r = os_zalloc(sizeof(*r) + ie_len + beacon_ie_len);
+	if (r == NULL)
+		return NL_SKIP;
+	if (bss[NL80211_BSS_BSSID])
+		os_memcpy(r->bssid, nla_data(bss[NL80211_BSS_BSSID]),
+			  ETH_ALEN);
+	if (bss[NL80211_BSS_FREQUENCY])
+		r->freq = nla_get_u32(bss[NL80211_BSS_FREQUENCY]);
+	if (bss[NL80211_BSS_BEACON_INTERVAL])
+		r->beacon_int = nla_get_u16(bss[NL80211_BSS_BEACON_INTERVAL]);
+	if (bss[NL80211_BSS_CAPABILITY])
+		r->caps = nla_get_u16(bss[NL80211_BSS_CAPABILITY]);
+	r->flags |= WPA_SCAN_NOISE_INVALID;
+	if (bss[NL80211_BSS_SIGNAL_MBM]) {
+		r->level = nla_get_u32(bss[NL80211_BSS_SIGNAL_MBM]);
+		r->level /= 100; /* mBm to dBm */
+		r->flags |= WPA_SCAN_LEVEL_DBM | WPA_SCAN_QUAL_INVALID;
+	} else if (bss[NL80211_BSS_SIGNAL_UNSPEC]) {
+		r->level = nla_get_u8(bss[NL80211_BSS_SIGNAL_UNSPEC]);
+		r->flags |= WPA_SCAN_QUAL_INVALID;
+	} else
+		r->flags |= WPA_SCAN_LEVEL_INVALID | WPA_SCAN_QUAL_INVALID;
+	if (bss[NL80211_BSS_TSF])
+		r->tsf = nla_get_u64(bss[NL80211_BSS_TSF]);
+	if (bss[NL80211_BSS_SEEN_MS_AGO])
+		r->age = nla_get_u32(bss[NL80211_BSS_SEEN_MS_AGO]);
+	r->ie_len = ie_len;
+	pos = (u8 *) (r + 1);
+	if (ie) {
+		os_memcpy(pos, ie, ie_len);
+		pos += ie_len;
+	}
+	r->beacon_ie_len = beacon_ie_len;
+	if (beacon_ie)
+		os_memcpy(pos, beacon_ie, beacon_ie_len);
+
+	if (bss[NL80211_BSS_STATUS]) {
+		enum nl80211_bss_status status;
+		status = nla_get_u32(bss[NL80211_BSS_STATUS]);
+		switch (status) {
+		case NL80211_BSS_STATUS_ASSOCIATED:
+			r->flags |= WPA_SCAN_ASSOCIATED;
+			break;
+		default:
+			break;
+		}
+	}
+
+	/*
+	 * cfg80211 maintains separate BSS table entries for APs if the same
+	 * BSSID,SSID pair is seen on multiple channels. wpa_supplicant does
+	 * not use frequency as a separate key in the BSS table, so filter out
+	 * duplicated entries. Prefer associated BSS entry in such a case in
+	 * order to get the correct frequency into the BSS table. Similarly,
+	 * prefer newer entries over older.
+	 */
+	for (i = 0; i < res->num; i++) {
+		const u8 *s1, *s2;
+		if (os_memcmp(res->res[i]->bssid, r->bssid, ETH_ALEN) != 0)
+			continue;
+
+		s1 = nl80211_get_ie((u8 *) (res->res[i] + 1),
+				    res->res[i]->ie_len, WLAN_EID_SSID);
+		s2 = nl80211_get_ie((u8 *) (r + 1), r->ie_len, WLAN_EID_SSID);
+		if (s1 == NULL || s2 == NULL || s1[1] != s2[1] ||
+		    os_memcmp(s1, s2, 2 + s1[1]) != 0)
+			continue;
+
+		/* Same BSSID,SSID was already included in scan results */
+		wpa_printf(MSG_DEBUG, "nl80211: Remove duplicated scan result "
+			   "for " MACSTR, MAC2STR(r->bssid));
+
+		if (((r->flags & WPA_SCAN_ASSOCIATED) &&
+		     !(res->res[i]->flags & WPA_SCAN_ASSOCIATED)) ||
+		    r->age < res->res[i]->age) {
+			os_free(res->res[i]);
+			res->res[i] = r;
+		} else
+			os_free(r);
+		return NL_SKIP;
+	}
+
+	tmp = os_realloc_array(res->res, res->num + 1,
+			       sizeof(struct wpa_scan_res *));
+	if (tmp == NULL) {
+		os_free(r);
+		return NL_SKIP;
+	}
+	tmp[res->num++] = r;
+	res->res = tmp;
+
+	return NL_SKIP;
+}
+
+
+static void clear_state_mismatch(struct wpa_driver_nl80211_data *drv,
+				 const u8 *addr)
+{
+	if (drv->capa.flags & WPA_DRIVER_FLAGS_SME) {
+		wpa_printf(MSG_DEBUG, "nl80211: Clear possible state "
+			   "mismatch (" MACSTR ")", MAC2STR(addr));
+		wpa_driver_nl80211_mlme(drv, addr,
+					NL80211_CMD_DEAUTHENTICATE,
+					WLAN_REASON_PREV_AUTH_NOT_VALID, 1);
+	}
+}
+
+
+static void wpa_driver_nl80211_check_bss_status(
+	struct wpa_driver_nl80211_data *drv, struct wpa_scan_results *res)
+{
+	size_t i;
+
+	for (i = 0; i < res->num; i++) {
+		struct wpa_scan_res *r = res->res[i];
+
+		if (r->flags & WPA_SCAN_ASSOCIATED) {
+			wpa_printf(MSG_DEBUG, "nl80211: Scan results "
+				   "indicate BSS status with " MACSTR
+				   " as associated",
+				   MAC2STR(r->bssid));
+			if (is_sta_interface(drv->nlmode) &&
+			    !drv->associated) {
+				wpa_printf(MSG_DEBUG, "nl80211: Local state "
+					   "(not associated) does not match "
+					   "with BSS state");
+				clear_state_mismatch(drv, r->bssid);
+			} else if (is_sta_interface(drv->nlmode) &&
+				   os_memcmp(drv->bssid, r->bssid, ETH_ALEN) !=
+				   0) {
+				wpa_printf(MSG_DEBUG, "nl80211: Local state "
+					   "(associated with " MACSTR ") does "
+					   "not match with BSS state",
+					   MAC2STR(drv->bssid));
+				clear_state_mismatch(drv, r->bssid);
+				clear_state_mismatch(drv, drv->bssid);
+			}
+		}
+	}
+}
+
+
+static struct wpa_scan_results *
+nl80211_get_scan_results(struct wpa_driver_nl80211_data *drv)
+{
+	struct nl_msg *msg;
+	struct wpa_scan_results *res;
+	int ret;
+	struct nl80211_bss_info_arg arg;
+
+	res = os_zalloc(sizeof(*res));
+	if (res == NULL)
+		return NULL;
+	if (!(msg = nl80211_cmd_msg(drv->first_bss, NLM_F_DUMP,
+				    NL80211_CMD_GET_SCAN))) {
+		wpa_scan_results_free(res);
+		return NULL;
+	}
+
+	arg.drv = drv;
+	arg.res = res;
+	ret = send_and_recv_msgs(drv, msg, bss_info_handler, &arg);
+	if (ret == 0) {
+		wpa_printf(MSG_DEBUG, "nl80211: Received scan results (%lu "
+			   "BSSes)", (unsigned long) res->num);
+		nl80211_get_noise_for_scan_results(drv, res);
+		return res;
+	}
+	wpa_printf(MSG_DEBUG, "nl80211: Scan result fetch failed: ret=%d "
+		   "(%s)", ret, strerror(-ret));
+	wpa_scan_results_free(res);
+	return NULL;
+}
+
+
+/**
+ * wpa_driver_nl80211_get_scan_results - Fetch the latest scan results
+ * @priv: Pointer to private wext data from wpa_driver_nl80211_init()
+ * Returns: Scan results on success, -1 on failure
+ */
+struct wpa_scan_results * wpa_driver_nl80211_get_scan_results(void *priv)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	struct wpa_scan_results *res;
+
+	res = nl80211_get_scan_results(drv);
+	if (res)
+		wpa_driver_nl80211_check_bss_status(drv, res);
+	return res;
+}
+
+
+void nl80211_dump_scan(struct wpa_driver_nl80211_data *drv)
+{
+	struct wpa_scan_results *res;
+	size_t i;
+
+	res = nl80211_get_scan_results(drv);
+	if (res == NULL) {
+		wpa_printf(MSG_DEBUG, "nl80211: Failed to get scan results");
+		return;
+	}
+
+	wpa_printf(MSG_DEBUG, "nl80211: Scan result dump");
+	for (i = 0; i < res->num; i++) {
+		struct wpa_scan_res *r = res->res[i];
+		wpa_printf(MSG_DEBUG, "nl80211: %d/%d " MACSTR "%s",
+			   (int) i, (int) res->num, MAC2STR(r->bssid),
+			   r->flags & WPA_SCAN_ASSOCIATED ? " [assoc]" : "");
+	}
+
+	wpa_scan_results_free(res);
+}
diff --git a/src/drivers/driver_none.c b/src/drivers/driver_none.c
index d75c14b..6ff3eae 100644
--- a/src/drivers/driver_none.c
+++ b/src/drivers/driver_none.c
@@ -74,13 +74,6 @@
 }
 
 
-static int none_driver_send_eapol(void *priv, const u8 *dest, u16 proto,
-				  const u8 *data, size_t data_len)
-{
-	return -1;
-}
-
-
 const struct wpa_driver_ops wpa_driver_none_ops = {
 	.name = "none",
 	.desc = "no driver (RADIUS server/WPS ER)",
@@ -89,5 +82,4 @@
 	.send_ether = none_driver_send_ether,
 	.init = none_driver_init,
 	.deinit = none_driver_deinit,
-	.send_eapol = none_driver_send_eapol,
 };
diff --git a/src/drivers/driver_privsep.c b/src/drivers/driver_privsep.c
index ed88e71..1cfc15d 100644
--- a/src/drivers/driver_privsep.c
+++ b/src/drivers/driver_privsep.c
@@ -35,7 +35,7 @@
 		     (struct sockaddr *) &drv->priv_addr,
 		     sizeof(drv->priv_addr));
 	if (res < 0)
-		perror("sendto");
+		wpa_printf(MSG_ERROR, "sendto: %s", strerror(errno));
 	return res < 0 ? -1 : 0;
 }
 
@@ -59,7 +59,8 @@
 	msg.msg_namelen = sizeof(drv->priv_addr);
 
 	if (sendmsg(drv->cmd_socket, &msg, 0) < 0) {
-		perror("sendmsg(cmd_socket)");
+		wpa_printf(MSG_ERROR, "sendmsg(cmd_socket): %s",
+			   strerror(errno));
 		return -1;
 	}
 
@@ -74,14 +75,15 @@
 		tv.tv_usec = 0;
 		res = select(drv->cmd_socket + 1, &rfds, NULL, NULL, &tv);
 		if (res < 0 && errno != EINTR) {
-			perror("select");
+			wpa_printf(MSG_ERROR, "select: %s", strerror(errno));
 			return -1;
 		}
 
 		if (FD_ISSET(drv->cmd_socket, &rfds)) {
 			res = recv(drv->cmd_socket, reply, *reply_len, 0);
 			if (res < 0) {
-				perror("recv");
+				wpa_printf(MSG_ERROR, "recv: %s",
+					   strerror(errno));
 				return -1;
 			}
 			*reply_len = res;
@@ -228,7 +230,7 @@
 
 	wpa_printf(MSG_DEBUG, "%s: priv=%p freq=%d pairwise_suite=%d "
 		   "group_suite=%d key_mgmt_suite=%d auth_alg=%d mode=%d",
-		   __func__, priv, params->freq, params->pairwise_suite,
+		   __func__, priv, params->freq.freq, params->pairwise_suite,
 		   params->group_suite, params->key_mgmt_suite,
 		   params->auth_alg, params->mode);
 
@@ -241,7 +243,9 @@
 		os_memcpy(data->bssid, params->bssid, ETH_ALEN);
 	os_memcpy(data->ssid, params->ssid, params->ssid_len);
 	data->ssid_len = params->ssid_len;
-	data->freq = params->freq;
+	data->hwmode = params->freq.mode;
+	data->freq = params->freq.freq;
+	data->channel = params->freq.channel;
 	data->pairwise_suite = params->pairwise_suite;
 	data->group_suite = params->group_suite;
 	data->key_mgmt_suite = params->key_mgmt_suite;
@@ -277,14 +281,15 @@
 {
 	struct wpa_driver_privsep_data *drv = priv;
 	int res, ssid_len;
-	u8 reply[sizeof(int) + 32];
+	u8 reply[sizeof(int) + SSID_MAX_LEN];
 	size_t len = sizeof(reply);
 
 	res = wpa_priv_cmd(drv, PRIVSEP_CMD_GET_SSID, NULL, 0, reply, &len);
 	if (res < 0 || len < sizeof(int))
 		return -1;
 	os_memcpy(&ssid_len, reply, sizeof(int));
-	if (ssid_len < 0 || ssid_len > 32 || sizeof(int) + ssid_len > len) {
+	if (ssid_len < 0 || ssid_len > SSID_MAX_LEN ||
+	    sizeof(int) + ssid_len > len) {
 		wpa_printf(MSG_DEBUG, "privsep: Invalid get SSID reply");
 		return -1;
 	}
@@ -439,7 +444,8 @@
 	res = recvfrom(sock, buf, buflen, 0,
 		       (struct sockaddr *) &from, &fromlen);
 	if (res < 0) {
-		perror("recvfrom(priv_socket)");
+		wpa_printf(MSG_ERROR, "recvfrom(priv_socket): %s",
+			   strerror(errno));
 		os_free(buf);
 		return;
 	}
@@ -629,7 +635,7 @@
 
 	drv->priv_socket = socket(PF_UNIX, SOCK_DGRAM, 0);
 	if (drv->priv_socket < 0) {
-		perror("socket(PF_UNIX)");
+		wpa_printf(MSG_ERROR, "socket(PF_UNIX): %s", strerror(errno));
 		os_free(drv->own_socket_path);
 		drv->own_socket_path = NULL;
 		return -1;
@@ -640,7 +646,9 @@
 	os_strlcpy(addr.sun_path, drv->own_socket_path, sizeof(addr.sun_path));
 	if (bind(drv->priv_socket, (struct sockaddr *) &addr, sizeof(addr)) <
 	    0) {
-		perror("privsep-set-params priv-sock: bind(PF_UNIX)");
+		wpa_printf(MSG_ERROR,
+			   "privsep-set-params priv-sock: bind(PF_UNIX): %s",
+			   strerror(errno));
 		close(drv->priv_socket);
 		drv->priv_socket = -1;
 		unlink(drv->own_socket_path);
@@ -654,7 +662,7 @@
 
 	drv->cmd_socket = socket(PF_UNIX, SOCK_DGRAM, 0);
 	if (drv->cmd_socket < 0) {
-		perror("socket(PF_UNIX)");
+		wpa_printf(MSG_ERROR, "socket(PF_UNIX): %s", strerror(errno));
 		os_free(drv->own_cmd_path);
 		drv->own_cmd_path = NULL;
 		return -1;
@@ -665,7 +673,9 @@
 	os_strlcpy(addr.sun_path, drv->own_cmd_path, sizeof(addr.sun_path));
 	if (bind(drv->cmd_socket, (struct sockaddr *) &addr, sizeof(addr)) < 0)
 	{
-		perror("privsep-set-params cmd-sock: bind(PF_UNIX)");
+		wpa_printf(MSG_ERROR,
+			   "privsep-set-params cmd-sock: bind(PF_UNIX): %s",
+			   strerror(errno));
 		close(drv->cmd_socket);
 		drv->cmd_socket = -1;
 		unlink(drv->own_cmd_path);
@@ -733,7 +743,7 @@
 };
 
 
-struct wpa_driver_ops *wpa_drivers[] =
+const struct wpa_driver_ops *const wpa_drivers[] =
 {
 	&wpa_driver_privsep_ops,
 	NULL
diff --git a/src/drivers/driver_roboswitch.c b/src/drivers/driver_roboswitch.c
index 9ce3fa2..d3e0595 100644
--- a/src/drivers/driver_roboswitch.c
+++ b/src/drivers/driver_roboswitch.c
@@ -91,7 +91,8 @@
 	mii->reg_num = reg;
 
 	if (ioctl(drv->fd, SIOCGMIIREG, &drv->ifr) < 0) {
-		perror("ioctl[SIOCGMIIREG]");
+		wpa_printf(MSG_ERROR, "ioctl[SIOCGMIIREG]: %s",
+			   strerror(errno));
 		return 0x00;
 	}
 	return mii->val_out;
@@ -108,7 +109,8 @@
 	mii->val_in = val;
 
 	if (ioctl(drv->fd, SIOCSMIIREG, &drv->ifr) < 0) {
-		perror("ioctl[SIOCSMIIREG");
+		wpa_printf(MSG_ERROR, "ioctl[SIOCSMIIREG]: %s",
+			   strerror(errno));
 	}
 }
 
@@ -394,7 +396,8 @@
 	os_memset(&drv->ifr, 0, sizeof(drv->ifr));
 	os_strlcpy(drv->ifr.ifr_name, drv->ifname, IFNAMSIZ);
 	if (ioctl(drv->fd, SIOCGMIIPHY, &drv->ifr) < 0) {
-		perror("ioctl[SIOCGMIIPHY]");
+		wpa_printf(MSG_ERROR, "ioctl[SIOCGMIIPHY]: %s",
+			   strerror(errno));
 		os_free(drv);
 		return NULL;
 	}
diff --git a/src/drivers/driver_test.c b/src/drivers/driver_test.c
deleted file mode 100644
index 66edfa7..0000000
--- a/src/drivers/driver_test.c
+++ /dev/null
@@ -1,2683 +0,0 @@
-/*
- * Testing driver interface for a simulated network driver
- * Copyright (c) 2004-2010, Jouni Malinen <j@w1.fi>
- *
- * This software may be distributed under the terms of the BSD license.
- * See README for more details.
- */
-
-/* Make sure we get winsock2.h for Windows build to get sockaddr_storage */
-#include "build_config.h"
-#ifdef CONFIG_NATIVE_WINDOWS
-#include <winsock2.h>
-#endif /* CONFIG_NATIVE_WINDOWS */
-
-#include "utils/includes.h"
-
-#ifndef CONFIG_NATIVE_WINDOWS
-#include <sys/un.h>
-#include <dirent.h>
-#include <sys/stat.h>
-#define DRIVER_TEST_UNIX
-#endif /* CONFIG_NATIVE_WINDOWS */
-
-#include "utils/common.h"
-#include "utils/eloop.h"
-#include "utils/list.h"
-#include "utils/trace.h"
-#include "common/ieee802_11_defs.h"
-#include "crypto/sha1.h"
-#include "l2_packet/l2_packet.h"
-#include "wps/wps.h"
-#include "driver.h"
-
-
-struct test_client_socket {
-	struct test_client_socket *next;
-	u8 addr[ETH_ALEN];
-	struct sockaddr_un un;
-	socklen_t unlen;
-	struct test_driver_bss *bss;
-};
-
-struct test_driver_bss {
-	struct wpa_driver_test_data *drv;
-	struct dl_list list;
-	void *bss_ctx;
-	char ifname[IFNAMSIZ];
-	u8 bssid[ETH_ALEN];
-	u8 *ie;
-	size_t ielen;
-	u8 *wps_beacon_ie;
-	size_t wps_beacon_ie_len;
-	u8 *wps_probe_resp_ie;
-	size_t wps_probe_resp_ie_len;
-	u8 ssid[32];
-	size_t ssid_len;
-	int privacy;
-};
-
-struct wpa_driver_test_global {
-	int bss_add_used;
-	u8 req_addr[ETH_ALEN];
-};
-
-struct wpa_driver_test_data {
-	struct wpa_driver_test_global *global;
-	void *ctx;
-	WPA_TRACE_REF(ctx);
-	u8 own_addr[ETH_ALEN];
-	int test_socket;
-#ifdef DRIVER_TEST_UNIX
-	struct sockaddr_un hostapd_addr;
-#endif /* DRIVER_TEST_UNIX */
-	int hostapd_addr_set;
-	struct sockaddr_in hostapd_addr_udp;
-	int hostapd_addr_udp_set;
-	char *own_socket_path;
-	char *test_dir;
-#define MAX_SCAN_RESULTS 30
-	struct wpa_scan_res *scanres[MAX_SCAN_RESULTS];
-	size_t num_scanres;
-	int use_associnfo;
-	u8 assoc_wpa_ie[80];
-	size_t assoc_wpa_ie_len;
-	int associated;
-	u8 *probe_req_ie;
-	size_t probe_req_ie_len;
-	u8 probe_req_ssid[32];
-	size_t probe_req_ssid_len;
-	int ibss;
-	int ap;
-
-	struct test_client_socket *cli;
-	struct dl_list bss;
-	int udp_port;
-
-	int alloc_iface_idx;
-
-	int probe_req_report;
-	unsigned int remain_on_channel_freq;
-	unsigned int remain_on_channel_duration;
-
-	int current_freq;
-};
-
-
-static void wpa_driver_test_deinit(void *priv);
-static int wpa_driver_test_attach(struct wpa_driver_test_data *drv,
-				  const char *dir, int ap);
-static void wpa_driver_test_close_test_socket(
-	struct wpa_driver_test_data *drv);
-static void test_remain_on_channel_timeout(void *eloop_ctx, void *timeout_ctx);
-
-
-static void test_driver_free_bss(struct test_driver_bss *bss)
-{
-	os_free(bss->ie);
-	os_free(bss->wps_beacon_ie);
-	os_free(bss->wps_probe_resp_ie);
-	os_free(bss);
-}
-
-
-static void test_driver_free_bsses(struct wpa_driver_test_data *drv)
-{
-	struct test_driver_bss *bss, *tmp;
-
-	dl_list_for_each_safe(bss, tmp, &drv->bss, struct test_driver_bss,
-			      list) {
-		dl_list_del(&bss->list);
-		test_driver_free_bss(bss);
-	}
-}
-
-
-static struct test_client_socket *
-test_driver_get_cli(struct wpa_driver_test_data *drv, struct sockaddr_un *from,
-		    socklen_t fromlen)
-{
-	struct test_client_socket *cli = drv->cli;
-
-	while (cli) {
-		if (cli->unlen == fromlen &&
-		    strncmp(cli->un.sun_path, from->sun_path,
-			    fromlen - sizeof(cli->un.sun_family)) == 0)
-			return cli;
-		cli = cli->next;
-	}
-
-	return NULL;
-}
-
-
-static int test_driver_send_eapol(void *priv, const u8 *addr, const u8 *data,
-				  size_t data_len, int encrypt,
-				  const u8 *own_addr, u32 flags)
-{
-	struct test_driver_bss *dbss = priv;
-	struct wpa_driver_test_data *drv = dbss->drv;
-	struct test_client_socket *cli;
-	struct msghdr msg;
-	struct iovec io[3];
-	struct l2_ethhdr eth;
-
-	if (drv->test_socket < 0)
-		return -1;
-
-	cli = drv->cli;
-	while (cli) {
-		if (memcmp(cli->addr, addr, ETH_ALEN) == 0)
-			break;
-		cli = cli->next;
-	}
-
-	if (!cli) {
-		wpa_printf(MSG_DEBUG, "%s: no destination client entry",
-			   __func__);
-		return -1;
-	}
-
-	memcpy(eth.h_dest, addr, ETH_ALEN);
-	memcpy(eth.h_source, own_addr, ETH_ALEN);
-	eth.h_proto = host_to_be16(ETH_P_EAPOL);
-
-	io[0].iov_base = "EAPOL ";
-	io[0].iov_len = 6;
-	io[1].iov_base = &eth;
-	io[1].iov_len = sizeof(eth);
-	io[2].iov_base = (u8 *) data;
-	io[2].iov_len = data_len;
-
-	memset(&msg, 0, sizeof(msg));
-	msg.msg_iov = io;
-	msg.msg_iovlen = 3;
-	msg.msg_name = &cli->un;
-	msg.msg_namelen = cli->unlen;
-	return sendmsg(drv->test_socket, &msg, 0);
-}
-
-
-static int test_driver_send_ether(void *priv, const u8 *dst, const u8 *src,
-				  u16 proto, const u8 *data, size_t data_len)
-{
-	struct test_driver_bss *dbss = priv;
-	struct wpa_driver_test_data *drv = dbss->drv;
-	struct msghdr msg;
-	struct iovec io[3];
-	struct l2_ethhdr eth;
-	char desttxt[30];
-	struct sockaddr_un addr;
-	struct dirent *dent;
-	DIR *dir;
-	int ret = 0, broadcast = 0, count = 0;
-
-	if (drv->test_socket < 0 || drv->test_dir == NULL) {
-		wpa_printf(MSG_DEBUG, "%s: invalid parameters (sock=%d "
-			   "test_dir=%p)",
-			   __func__, drv->test_socket, drv->test_dir);
-		return -1;
-	}
-
-	broadcast = memcmp(dst, "\xff\xff\xff\xff\xff\xff", ETH_ALEN) == 0;
-	snprintf(desttxt, sizeof(desttxt), MACSTR, MAC2STR(dst));
-
-	memcpy(eth.h_dest, dst, ETH_ALEN);
-	memcpy(eth.h_source, src, ETH_ALEN);
-	eth.h_proto = host_to_be16(proto);
-
-	io[0].iov_base = "ETHER ";
-	io[0].iov_len = 6;
-	io[1].iov_base = &eth;
-	io[1].iov_len = sizeof(eth);
-	io[2].iov_base = (u8 *) data;
-	io[2].iov_len = data_len;
-
-	memset(&msg, 0, sizeof(msg));
-	msg.msg_iov = io;
-	msg.msg_iovlen = 3;
-
-	dir = opendir(drv->test_dir);
-	if (dir == NULL) {
-		perror("test_driver: opendir");
-		return -1;
-	}
-	while ((dent = readdir(dir))) {
-#ifdef _DIRENT_HAVE_D_TYPE
-		/* Skip the file if it is not a socket. Also accept
-		 * DT_UNKNOWN (0) in case the C library or underlying file
-		 * system does not support d_type. */
-		if (dent->d_type != DT_SOCK && dent->d_type != DT_UNKNOWN)
-			continue;
-#endif /* _DIRENT_HAVE_D_TYPE */
-		if (strcmp(dent->d_name, ".") == 0 ||
-		    strcmp(dent->d_name, "..") == 0)
-			continue;
-
-		memset(&addr, 0, sizeof(addr));
-		addr.sun_family = AF_UNIX;
-		snprintf(addr.sun_path, sizeof(addr.sun_path), "%s/%s",
-			 drv->test_dir, dent->d_name);
-
-		if (strcmp(addr.sun_path, drv->own_socket_path) == 0)
-			continue;
-		if (!broadcast && strstr(dent->d_name, desttxt) == NULL)
-			continue;
-
-		wpa_printf(MSG_DEBUG, "%s: Send ether frame to %s",
-			   __func__, dent->d_name);
-
-		msg.msg_name = &addr;
-		msg.msg_namelen = sizeof(addr);
-		ret = sendmsg(drv->test_socket, &msg, 0);
-		if (ret < 0)
-			perror("driver_test: sendmsg");
-		count++;
-	}
-	closedir(dir);
-
-	if (!broadcast && count == 0) {
-		wpa_printf(MSG_DEBUG, "%s: Destination " MACSTR " not found",
-			   __func__, MAC2STR(dst));
-		return -1;
-	}
-
-	return ret;
-}
-
-
-static int wpa_driver_test_send_mlme(void *priv, const u8 *data,
-				     size_t data_len, int noack)
-{
-	struct test_driver_bss *dbss = priv;
-	struct wpa_driver_test_data *drv = dbss->drv;
-	struct msghdr msg;
-	struct iovec io[2];
-	const u8 *dest;
-	struct sockaddr_un addr;
-	struct dirent *dent;
-	DIR *dir;
-	int broadcast;
-	int ret = 0;
-	struct ieee80211_hdr *hdr;
-	u16 fc;
-	char cmd[50];
-	int freq;
-#ifdef HOSTAPD
-	char desttxt[30];
-#endif /* HOSTAPD */
-	union wpa_event_data event;
-
-	wpa_hexdump(MSG_MSGDUMP, "test_send_mlme", data, data_len);
-	if (drv->test_socket < 0 || data_len < 10) {
-		wpa_printf(MSG_DEBUG, "%s: invalid parameters (sock=%d len=%lu"
-			   " test_dir=%p)",
-			   __func__, drv->test_socket,
-			   (unsigned long) data_len,
-			   drv->test_dir);
-		return -1;
-	}
-
-	dest = data + 4;
-	broadcast = os_memcmp(dest, "\xff\xff\xff\xff\xff\xff", ETH_ALEN) == 0;
-
-#ifdef HOSTAPD
-	snprintf(desttxt, sizeof(desttxt), MACSTR, MAC2STR(dest));
-#endif /* HOSTAPD */
-
-	if (drv->remain_on_channel_freq)
-		freq = drv->remain_on_channel_freq;
-	else
-		freq = drv->current_freq;
-	wpa_printf(MSG_DEBUG, "test_driver(%s): MLME TX on freq %d MHz",
-		   dbss->ifname, freq);
-	os_snprintf(cmd, sizeof(cmd), "MLME freq=%d ", freq);
-	io[0].iov_base = cmd;
-	io[0].iov_len = os_strlen(cmd);
-	io[1].iov_base = (void *) data;
-	io[1].iov_len = data_len;
-
-	os_memset(&msg, 0, sizeof(msg));
-	msg.msg_iov = io;
-	msg.msg_iovlen = 2;
-
-#ifdef HOSTAPD
-	if (drv->test_dir == NULL) {
-		wpa_printf(MSG_DEBUG, "%s: test_dir == NULL", __func__);
-		return -1;
-	}
-
-	dir = opendir(drv->test_dir);
-	if (dir == NULL) {
-		perror("test_driver: opendir");
-		return -1;
-	}
-	while ((dent = readdir(dir))) {
-#ifdef _DIRENT_HAVE_D_TYPE
-		/* Skip the file if it is not a socket. Also accept
-		 * DT_UNKNOWN (0) in case the C library or underlying file
-		 * system does not support d_type. */
-		if (dent->d_type != DT_SOCK && dent->d_type != DT_UNKNOWN)
-			continue;
-#endif /* _DIRENT_HAVE_D_TYPE */
-		if (os_strcmp(dent->d_name, ".") == 0 ||
-		    os_strcmp(dent->d_name, "..") == 0)
-			continue;
-
-		os_memset(&addr, 0, sizeof(addr));
-		addr.sun_family = AF_UNIX;
-		os_snprintf(addr.sun_path, sizeof(addr.sun_path), "%s/%s",
-			    drv->test_dir, dent->d_name);
-
-		if (os_strcmp(addr.sun_path, drv->own_socket_path) == 0)
-			continue;
-		if (!broadcast && os_strstr(dent->d_name, desttxt) == NULL)
-			continue;
-
-		wpa_printf(MSG_DEBUG, "%s: Send management frame to %s",
-			   __func__, dent->d_name);
-
-		msg.msg_name = &addr;
-		msg.msg_namelen = sizeof(addr);
-		ret = sendmsg(drv->test_socket, &msg, 0);
-		if (ret < 0)
-			perror("driver_test: sendmsg(test_socket)");
-	}
-	closedir(dir);
-#else /* HOSTAPD */
-
-	if (os_memcmp(dest, dbss->bssid, ETH_ALEN) == 0 ||
-	    drv->test_dir == NULL) {
-		if (drv->hostapd_addr_udp_set) {
-			msg.msg_name = &drv->hostapd_addr_udp;
-			msg.msg_namelen = sizeof(drv->hostapd_addr_udp);
-		} else {
-#ifdef DRIVER_TEST_UNIX
-			msg.msg_name = &drv->hostapd_addr;
-			msg.msg_namelen = sizeof(drv->hostapd_addr);
-#endif /* DRIVER_TEST_UNIX */
-		}
-	} else if (broadcast) {
-		dir = opendir(drv->test_dir);
-		if (dir == NULL)
-			return -1;
-		while ((dent = readdir(dir))) {
-#ifdef _DIRENT_HAVE_D_TYPE
-			/* Skip the file if it is not a socket.
-			 * Also accept DT_UNKNOWN (0) in case
-			 * the C library or underlying file
-			 * system does not support d_type. */
-			if (dent->d_type != DT_SOCK &&
-			    dent->d_type != DT_UNKNOWN)
-				continue;
-#endif /* _DIRENT_HAVE_D_TYPE */
-			if (os_strcmp(dent->d_name, ".") == 0 ||
-			    os_strcmp(dent->d_name, "..") == 0)
-				continue;
-			wpa_printf(MSG_DEBUG, "%s: Send broadcast MLME to %s",
-				   __func__, dent->d_name);
-			os_memset(&addr, 0, sizeof(addr));
-			addr.sun_family = AF_UNIX;
-			os_snprintf(addr.sun_path, sizeof(addr.sun_path),
-				    "%s/%s", drv->test_dir, dent->d_name);
-
-			msg.msg_name = &addr;
-			msg.msg_namelen = sizeof(addr);
-
-			ret = sendmsg(drv->test_socket, &msg, 0);
-			if (ret < 0)
-				perror("driver_test: sendmsg(test_socket)");
-		}
-		closedir(dir);
-		return ret;
-	} else {
-		struct stat st;
-		os_memset(&addr, 0, sizeof(addr));
-		addr.sun_family = AF_UNIX;
-		os_snprintf(addr.sun_path, sizeof(addr.sun_path),
-			    "%s/AP-" MACSTR, drv->test_dir, MAC2STR(dest));
-		if (stat(addr.sun_path, &st) < 0) {
-			os_snprintf(addr.sun_path, sizeof(addr.sun_path),
-				    "%s/STA-" MACSTR,
-				    drv->test_dir, MAC2STR(dest));
-		}
-		msg.msg_name = &addr;
-		msg.msg_namelen = sizeof(addr);
-	}
-
-	if (sendmsg(drv->test_socket, &msg, 0) < 0) {
-		perror("sendmsg(test_socket)");
-		return -1;
-	}
-#endif /* HOSTAPD */
-
-	hdr = (struct ieee80211_hdr *) data;
-	fc = le_to_host16(hdr->frame_control);
-
-	os_memset(&event, 0, sizeof(event));
-	event.tx_status.type = WLAN_FC_GET_TYPE(fc);
-	event.tx_status.stype = WLAN_FC_GET_STYPE(fc);
-	event.tx_status.dst = hdr->addr1;
-	event.tx_status.data = data;
-	event.tx_status.data_len = data_len;
-	event.tx_status.ack = ret >= 0;
-	wpa_supplicant_event(drv->ctx, EVENT_TX_STATUS, &event);
-
-	return ret;
-}
-
-
-static void test_driver_scan(struct wpa_driver_test_data *drv,
-			     struct sockaddr_un *from, socklen_t fromlen,
-			     char *data)
-{
-	char buf[512], *pos, *end;
-	int ret;
-	struct test_driver_bss *bss;
-	u8 sa[ETH_ALEN];
-	u8 ie[512];
-	size_t ielen;
-	union wpa_event_data event;
-
-	/* data: optional [ ' ' | STA-addr | ' ' | IEs(hex) ] */
-
-	wpa_printf(MSG_DEBUG, "test_driver: SCAN");
-
-	if (*data) {
-		if (*data != ' ' ||
-		    hwaddr_aton(data + 1, sa)) {
-			wpa_printf(MSG_DEBUG, "test_driver: Unexpected SCAN "
-				   "command format");
-			return;
-		}
-
-		data += 18;
-		while (*data == ' ')
-			data++;
-		ielen = os_strlen(data) / 2;
-		if (ielen > sizeof(ie))
-			ielen = sizeof(ie);
-		if (hexstr2bin(data, ie, ielen) < 0)
-			ielen = 0;
-
-		wpa_printf(MSG_DEBUG, "test_driver: Scan from " MACSTR,
-			   MAC2STR(sa));
-		wpa_hexdump(MSG_MSGDUMP, "test_driver: scan IEs", ie, ielen);
-
-		os_memset(&event, 0, sizeof(event));
-		event.rx_probe_req.sa = sa;
-		event.rx_probe_req.ie = ie;
-		event.rx_probe_req.ie_len = ielen;
-		wpa_supplicant_event(drv->ctx, EVENT_RX_PROBE_REQ, &event);
-	}
-
-	dl_list_for_each(bss, &drv->bss, struct test_driver_bss, list) {
-		pos = buf;
-		end = buf + sizeof(buf);
-
-		/* reply: SCANRESP BSSID SSID IEs */
-		ret = snprintf(pos, end - pos, "SCANRESP " MACSTR " ",
-			       MAC2STR(bss->bssid));
-		if (ret < 0 || ret >= end - pos)
-			return;
-		pos += ret;
-		pos += wpa_snprintf_hex(pos, end - pos,
-					bss->ssid, bss->ssid_len);
-		ret = snprintf(pos, end - pos, " ");
-		if (ret < 0 || ret >= end - pos)
-			return;
-		pos += ret;
-		pos += wpa_snprintf_hex(pos, end - pos, bss->ie, bss->ielen);
-		pos += wpa_snprintf_hex(pos, end - pos, bss->wps_probe_resp_ie,
-					bss->wps_probe_resp_ie_len);
-
-		if (bss->privacy) {
-			ret = snprintf(pos, end - pos, " PRIVACY");
-			if (ret < 0 || ret >= end - pos)
-				return;
-			pos += ret;
-		}
-
-		sendto(drv->test_socket, buf, pos - buf, 0,
-		       (struct sockaddr *) from, fromlen);
-	}
-}
-
-
-static void test_driver_assoc(struct wpa_driver_test_data *drv,
-			      struct sockaddr_un *from, socklen_t fromlen,
-			      char *data)
-{
-	struct test_client_socket *cli;
-	u8 ie[256], ssid[32];
-	size_t ielen, ssid_len = 0;
-	char *pos, *pos2, cmd[50];
-	struct test_driver_bss *bss, *tmp;
-
-	/* data: STA-addr SSID(hex) IEs(hex) */
-
-	cli = os_zalloc(sizeof(*cli));
-	if (cli == NULL)
-		return;
-
-	if (hwaddr_aton(data, cli->addr)) {
-		printf("test_socket: Invalid MAC address '%s' in ASSOC\n",
-		       data);
-		os_free(cli);
-		return;
-	}
-	pos = data + 17;
-	while (*pos == ' ')
-		pos++;
-	pos2 = strchr(pos, ' ');
-	ielen = 0;
-	if (pos2) {
-		ssid_len = (pos2 - pos) / 2;
-		if (hexstr2bin(pos, ssid, ssid_len) < 0) {
-			wpa_printf(MSG_DEBUG, "%s: Invalid SSID", __func__);
-			os_free(cli);
-			return;
-		}
-		wpa_hexdump_ascii(MSG_DEBUG, "test_driver_assoc: SSID",
-				  ssid, ssid_len);
-
-		pos = pos2 + 1;
-		ielen = strlen(pos) / 2;
-		if (ielen > sizeof(ie))
-			ielen = sizeof(ie);
-		if (hexstr2bin(pos, ie, ielen) < 0)
-			ielen = 0;
-	}
-
-	bss = NULL;
-	dl_list_for_each(tmp, &drv->bss, struct test_driver_bss, list) {
-		if (tmp->ssid_len == ssid_len &&
-		    os_memcmp(tmp->ssid, ssid, ssid_len) == 0) {
-			bss = tmp;
-			break;
-		}
-	}
-	if (bss == NULL) {
-		wpa_printf(MSG_DEBUG, "%s: No matching SSID found from "
-			   "configured BSSes", __func__);
-		os_free(cli);
-		return;
-	}
-
-	cli->bss = bss;
-	memcpy(&cli->un, from, sizeof(cli->un));
-	cli->unlen = fromlen;
-	cli->next = drv->cli;
-	drv->cli = cli;
-	wpa_hexdump_ascii(MSG_DEBUG, "test_socket: ASSOC sun_path",
-			  (const u8 *) cli->un.sun_path,
-			  cli->unlen - sizeof(cli->un.sun_family));
-
-	snprintf(cmd, sizeof(cmd), "ASSOCRESP " MACSTR " 0",
-		 MAC2STR(bss->bssid));
-	sendto(drv->test_socket, cmd, strlen(cmd), 0,
-	       (struct sockaddr *) from, fromlen);
-
-	drv_event_assoc(bss->bss_ctx, cli->addr, ie, ielen, 0);
-}
-
-
-static void test_driver_disassoc(struct wpa_driver_test_data *drv,
-				 struct sockaddr_un *from, socklen_t fromlen)
-{
-	struct test_client_socket *cli;
-
-	cli = test_driver_get_cli(drv, from, fromlen);
-	if (!cli)
-		return;
-
-	drv_event_disassoc(drv->ctx, cli->addr);
-}
-
-
-static void test_driver_eapol(struct wpa_driver_test_data *drv,
-			      struct sockaddr_un *from, socklen_t fromlen,
-			      u8 *data, size_t datalen)
-{
-#ifdef HOSTAPD
-	struct test_client_socket *cli;
-#endif /* HOSTAPD */
-	const u8 *src = NULL;
-
-	if (datalen > 14) {
-		/* Skip Ethernet header */
-		src = data + ETH_ALEN;
-		wpa_printf(MSG_DEBUG, "test_driver: dst=" MACSTR " src="
-			   MACSTR " proto=%04x",
-			   MAC2STR(data), MAC2STR(src),
-			   WPA_GET_BE16(data + 2 * ETH_ALEN));
-		data += 14;
-		datalen -= 14;
-	}
-
-#ifdef HOSTAPD
-	cli = test_driver_get_cli(drv, from, fromlen);
-	if (cli) {
-		drv_event_eapol_rx(cli->bss->bss_ctx, cli->addr, data,
-				   datalen);
-	} else {
-		wpa_printf(MSG_DEBUG, "test_socket: EAPOL from unknown "
-			   "client");
-	}
-#else /* HOSTAPD */
-	if (src)
-		drv_event_eapol_rx(drv->ctx, src, data, datalen);
-#endif /* HOSTAPD */
-}
-
-
-static void test_driver_ether(struct wpa_driver_test_data *drv,
-			      struct sockaddr_un *from, socklen_t fromlen,
-			      u8 *data, size_t datalen)
-{
-	struct l2_ethhdr *eth;
-
-	if (datalen < sizeof(*eth))
-		return;
-
-	eth = (struct l2_ethhdr *) data;
-	wpa_printf(MSG_DEBUG, "test_driver: RX ETHER dst=" MACSTR " src="
-		   MACSTR " proto=%04x",
-		   MAC2STR(eth->h_dest), MAC2STR(eth->h_source),
-		   be_to_host16(eth->h_proto));
-
-#ifdef CONFIG_IEEE80211R
-	if (be_to_host16(eth->h_proto) == ETH_P_RRB) {
-		union wpa_event_data ev;
-		os_memset(&ev, 0, sizeof(ev));
-		ev.ft_rrb_rx.src = eth->h_source;
-		ev.ft_rrb_rx.data = data + sizeof(*eth);
-		ev.ft_rrb_rx.data_len = datalen - sizeof(*eth);
-	}
-#endif /* CONFIG_IEEE80211R */
-}
-
-
-static void test_driver_mlme(struct wpa_driver_test_data *drv,
-			     struct sockaddr_un *from, socklen_t fromlen,
-			     u8 *data, size_t datalen)
-{
-	struct ieee80211_hdr *hdr;
-	u16 fc;
-	union wpa_event_data event;
-	int freq = 0, own_freq;
-	struct test_driver_bss *bss;
-
-	bss = dl_list_first(&drv->bss, struct test_driver_bss, list);
-
-	if (datalen > 6 && os_memcmp(data, "freq=", 5) == 0) {
-		size_t pos;
-		for (pos = 5; pos < datalen; pos++) {
-			if (data[pos] == ' ')
-				break;
-		}
-		if (pos < datalen) {
-			freq = atoi((const char *) &data[5]);
-			wpa_printf(MSG_DEBUG, "test_driver(%s): MLME RX on "
-				   "freq %d MHz", bss->ifname, freq);
-			pos++;
-			data += pos;
-			datalen -= pos;
-		}
-	}
-
-	if (drv->remain_on_channel_freq)
-		own_freq = drv->remain_on_channel_freq;
-	else
-		own_freq = drv->current_freq;
-
-	if (freq && own_freq && freq != own_freq) {
-		wpa_printf(MSG_DEBUG, "test_driver(%s): Ignore MLME RX on "
-			   "another frequency %d MHz (own %d MHz)",
-			   bss->ifname, freq, own_freq);
-		return;
-	}
-
-	hdr = (struct ieee80211_hdr *) data;
-
-	if (test_driver_get_cli(drv, from, fromlen) == NULL && datalen >= 16) {
-		struct test_client_socket *cli;
-		cli = os_zalloc(sizeof(*cli));
-		if (cli == NULL)
-			return;
-		wpa_printf(MSG_DEBUG, "Adding client entry for " MACSTR,
-			   MAC2STR(hdr->addr2));
-		memcpy(cli->addr, hdr->addr2, ETH_ALEN);
-		memcpy(&cli->un, from, sizeof(cli->un));
-		cli->unlen = fromlen;
-		cli->next = drv->cli;
-		drv->cli = cli;
-	}
-
-	wpa_hexdump(MSG_MSGDUMP, "test_driver_mlme: received frame",
-		    data, datalen);
-	fc = le_to_host16(hdr->frame_control);
-	if (WLAN_FC_GET_TYPE(fc) != WLAN_FC_TYPE_MGMT) {
-		wpa_printf(MSG_ERROR, "%s: received non-mgmt frame",
-			   __func__);
-		return;
-	}
-
-	os_memset(&event, 0, sizeof(event));
-	event.rx_mgmt.frame = data;
-	event.rx_mgmt.frame_len = datalen;
-	wpa_supplicant_event(drv->ctx, EVENT_RX_MGMT, &event);
-}
-
-
-static void test_driver_receive_unix(int sock, void *eloop_ctx, void *sock_ctx)
-{
-	struct wpa_driver_test_data *drv = eloop_ctx;
-	char buf[2000];
-	int res;
-	struct sockaddr_un from;
-	socklen_t fromlen = sizeof(from);
-
-	res = recvfrom(sock, buf, sizeof(buf) - 1, 0,
-		       (struct sockaddr *) &from, &fromlen);
-	if (res < 0) {
-		perror("recvfrom(test_socket)");
-		return;
-	}
-	buf[res] = '\0';
-
-	wpa_printf(MSG_DEBUG, "test_driver: received %u bytes", res);
-
-	if (strncmp(buf, "SCAN", 4) == 0) {
-		test_driver_scan(drv, &from, fromlen, buf + 4);
-	} else if (strncmp(buf, "ASSOC ", 6) == 0) {
-		test_driver_assoc(drv, &from, fromlen, buf + 6);
-	} else if (strcmp(buf, "DISASSOC") == 0) {
-		test_driver_disassoc(drv, &from, fromlen);
-	} else if (strncmp(buf, "EAPOL ", 6) == 0) {
-		test_driver_eapol(drv, &from, fromlen, (u8 *) buf + 6,
-				  res - 6);
-	} else if (strncmp(buf, "ETHER ", 6) == 0) {
-		test_driver_ether(drv, &from, fromlen, (u8 *) buf + 6,
-				  res - 6);
-	} else if (strncmp(buf, "MLME ", 5) == 0) {
-		test_driver_mlme(drv, &from, fromlen, (u8 *) buf + 5, res - 5);
-	} else {
-		wpa_hexdump_ascii(MSG_DEBUG, "Unknown test_socket command",
-				  (u8 *) buf, res);
-	}
-}
-
-
-static int test_driver_set_generic_elem(void *priv,
-					const u8 *elem, size_t elem_len)
-{
-	struct test_driver_bss *bss = priv;
-
-	os_free(bss->ie);
-
-	if (elem == NULL) {
-		bss->ie = NULL;
-		bss->ielen = 0;
-		return 0;
-	}
-
-	bss->ie = os_malloc(elem_len);
-	if (bss->ie == NULL) {
-		bss->ielen = 0;
-		return -1;
-	}
-
-	memcpy(bss->ie, elem, elem_len);
-	bss->ielen = elem_len;
-	return 0;
-}
-
-
-static int test_driver_set_ap_wps_ie(void *priv, const struct wpabuf *beacon,
-				     const struct wpabuf *proberesp,
-				     const struct wpabuf *assocresp)
-{
-	struct test_driver_bss *bss = priv;
-
-	if (beacon == NULL)
-		wpa_printf(MSG_DEBUG, "test_driver: Clear Beacon WPS IE");
-	else
-		wpa_hexdump_buf(MSG_DEBUG, "test_driver: Beacon WPS IE",
-				beacon);
-
-	os_free(bss->wps_beacon_ie);
-
-	if (beacon == NULL) {
-		bss->wps_beacon_ie = NULL;
-		bss->wps_beacon_ie_len = 0;
-	} else {
-		bss->wps_beacon_ie = os_malloc(wpabuf_len(beacon));
-		if (bss->wps_beacon_ie == NULL) {
-			bss->wps_beacon_ie_len = 0;
-			return -1;
-		}
-
-		os_memcpy(bss->wps_beacon_ie, wpabuf_head(beacon),
-			  wpabuf_len(beacon));
-		bss->wps_beacon_ie_len = wpabuf_len(beacon);
-	}
-
-	if (proberesp == NULL)
-		wpa_printf(MSG_DEBUG, "test_driver: Clear Probe Response WPS "
-			   "IE");
-	else
-		wpa_hexdump_buf(MSG_DEBUG, "test_driver: Probe Response WPS "
-				"IE", proberesp);
-
-	os_free(bss->wps_probe_resp_ie);
-
-	if (proberesp == NULL) {
-		bss->wps_probe_resp_ie = NULL;
-		bss->wps_probe_resp_ie_len = 0;
-	} else {
-		bss->wps_probe_resp_ie = os_malloc(wpabuf_len(proberesp));
-		if (bss->wps_probe_resp_ie == NULL) {
-			bss->wps_probe_resp_ie_len = 0;
-			return -1;
-		}
-
-		os_memcpy(bss->wps_probe_resp_ie, wpabuf_head(proberesp),
-			  wpabuf_len(proberesp));
-		bss->wps_probe_resp_ie_len = wpabuf_len(proberesp);
-	}
-
-	return 0;
-}
-
-
-static int test_driver_sta_deauth(void *priv, const u8 *own_addr,
-				  const u8 *addr, int reason)
-{
-	struct test_driver_bss *dbss = priv;
-	struct wpa_driver_test_data *drv = dbss->drv;
-	struct test_client_socket *cli;
-
-	if (drv->test_socket < 0)
-		return -1;
-
-	cli = drv->cli;
-	while (cli) {
-		if (memcmp(cli->addr, addr, ETH_ALEN) == 0)
-			break;
-		cli = cli->next;
-	}
-
-	if (!cli)
-		return -1;
-
-	return sendto(drv->test_socket, "DEAUTH", 6, 0,
-		      (struct sockaddr *) &cli->un, cli->unlen);
-}
-
-
-static int test_driver_sta_disassoc(void *priv, const u8 *own_addr,
-				    const u8 *addr, int reason)
-{
-	struct test_driver_bss *dbss = priv;
-	struct wpa_driver_test_data *drv = dbss->drv;
-	struct test_client_socket *cli;
-
-	if (drv->test_socket < 0)
-		return -1;
-
-	cli = drv->cli;
-	while (cli) {
-		if (memcmp(cli->addr, addr, ETH_ALEN) == 0)
-			break;
-		cli = cli->next;
-	}
-
-	if (!cli)
-		return -1;
-
-	return sendto(drv->test_socket, "DISASSOC", 8, 0,
-		      (struct sockaddr *) &cli->un, cli->unlen);
-}
-
-
-static int test_driver_bss_add(void *priv, const char *ifname, const u8 *bssid,
-			       void *bss_ctx, void **drv_priv)
-{
-	struct test_driver_bss *dbss = priv;
-	struct wpa_driver_test_data *drv = dbss->drv;
-	struct test_driver_bss *bss;
-
-	wpa_printf(MSG_DEBUG, "%s(ifname=%s bssid=" MACSTR ")",
-		   __func__, ifname, MAC2STR(bssid));
-
-	bss = os_zalloc(sizeof(*bss));
-	if (bss == NULL)
-		return -1;
-
-	bss->bss_ctx = bss_ctx;
-	bss->drv = drv;
-	os_strlcpy(bss->ifname, ifname, IFNAMSIZ);
-	os_memcpy(bss->bssid, bssid, ETH_ALEN);
-
-	dl_list_add(&drv->bss, &bss->list);
-	if (drv->global) {
-		drv->global->bss_add_used = 1;
-		os_memcpy(drv->global->req_addr, bssid, ETH_ALEN);
-	}
-
-	if (drv_priv)
-		*drv_priv = bss;
-
-	return 0;
-}
-
-
-static int test_driver_bss_remove(void *priv, const char *ifname)
-{
-	struct test_driver_bss *dbss = priv;
-	struct wpa_driver_test_data *drv = dbss->drv;
-	struct test_driver_bss *bss;
-	struct test_client_socket *cli, *prev_c;
-
-	wpa_printf(MSG_DEBUG, "%s(ifname=%s)", __func__, ifname);
-
-	dl_list_for_each(bss, &drv->bss, struct test_driver_bss, list) {
-		if (strcmp(bss->ifname, ifname) != 0)
-			continue;
-
-		for (prev_c = NULL, cli = drv->cli; cli;
-		     prev_c = cli, cli = cli->next) {
-			if (cli->bss != bss)
-				continue;
-			if (prev_c)
-				prev_c->next = cli->next;
-			else
-				drv->cli = cli->next;
-			os_free(cli);
-			break;
-		}
-
-		dl_list_del(&bss->list);
-		test_driver_free_bss(bss);
-		return 0;
-	}
-
-	return -1;
-}
-
-
-static int test_driver_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)
-{
-	struct test_driver_bss *dbss = priv;
-	struct wpa_driver_test_data *drv = dbss->drv;
-
-	wpa_printf(MSG_DEBUG, "%s(type=%d ifname=%s bss_ctx=%p)",
-		   __func__, type, ifname, bss_ctx);
-	if (addr)
-		os_memcpy(if_addr, addr, ETH_ALEN);
-	else {
-		drv->alloc_iface_idx++;
-		if_addr[0] = 0x02; /* locally administered */
-		sha1_prf(drv->own_addr, ETH_ALEN,
-			 "hostapd test addr generation",
-			 (const u8 *) &drv->alloc_iface_idx,
-			 sizeof(drv->alloc_iface_idx),
-			 if_addr + 1, ETH_ALEN - 1);
-	}
-	if (type == WPA_IF_AP_BSS || type == WPA_IF_P2P_GO ||
-	    type == WPA_IF_P2P_CLIENT || type == WPA_IF_P2P_GROUP)
-		return test_driver_bss_add(priv, ifname, if_addr, bss_ctx,
-					   drv_priv);
-	return 0;
-}
-
-
-static int test_driver_if_remove(void *priv, enum wpa_driver_if_type type,
-				 const char *ifname)
-{
-	wpa_printf(MSG_DEBUG, "%s(type=%d ifname=%s)", __func__, type, ifname);
-	if (type == WPA_IF_AP_BSS || type == WPA_IF_P2P_GO ||
-	    type == WPA_IF_P2P_CLIENT || type == WPA_IF_P2P_GROUP)
-		return test_driver_bss_remove(priv, ifname);
-	return 0;
-}
-
-
-static int test_driver_set_ssid(void *priv, const u8 *buf, int len)
-{
-	struct test_driver_bss *bss = priv;
-
-	wpa_printf(MSG_DEBUG, "%s(ifname=%s)", __func__, bss->ifname);
-	if (len < 0)
-		return -1;
-	wpa_hexdump_ascii(MSG_DEBUG, "test_driver_set_ssid: SSID", buf, len);
-
-	if ((size_t) len > sizeof(bss->ssid))
-		return -1;
-
-	os_memcpy(bss->ssid, buf, len);
-	bss->ssid_len = len;
-
-	return 0;
-}
-
-
-static int test_driver_set_privacy(void *priv, int enabled)
-{
-	struct test_driver_bss *dbss = priv;
-
-	wpa_printf(MSG_DEBUG, "%s(enabled=%d)",  __func__, enabled);
-	dbss->privacy = enabled;
-
-	return 0;
-}
-
-
-static int test_driver_set_sta_vlan(void *priv, const u8 *addr,
-				    const char *ifname, int vlan_id)
-{
-	wpa_printf(MSG_DEBUG, "%s(addr=" MACSTR " ifname=%s vlan_id=%d)",
-		   __func__, MAC2STR(addr), ifname, vlan_id);
-	return 0;
-}
-
-
-static int test_driver_sta_add(void *priv,
-			       struct hostapd_sta_add_params *params)
-{
-	struct test_driver_bss *bss = priv;
-	struct wpa_driver_test_data *drv = bss->drv;
-	struct test_client_socket *cli;
-
-	wpa_printf(MSG_DEBUG, "%s(ifname=%s addr=" MACSTR " aid=%d "
-		   "capability=0x%x listen_interval=%d)",
-		   __func__, bss->ifname, MAC2STR(params->addr), params->aid,
-		   params->capability, params->listen_interval);
-	wpa_hexdump(MSG_DEBUG, "test_driver_sta_add - supp_rates",
-		    params->supp_rates, params->supp_rates_len);
-
-	cli = drv->cli;
-	while (cli) {
-		if (os_memcmp(cli->addr, params->addr, ETH_ALEN) == 0)
-			break;
-		cli = cli->next;
-	}
-	if (!cli) {
-		wpa_printf(MSG_DEBUG, "%s: no matching client entry",
-			   __func__);
-		return -1;
-	}
-
-	cli->bss = bss;
-
-	return 0;
-}
-
-
-static struct wpa_driver_test_data * test_alloc_data(void *ctx,
-						     const char *ifname)
-{
-	struct wpa_driver_test_data *drv;
-	struct test_driver_bss *bss;
-
-	drv = os_zalloc(sizeof(struct wpa_driver_test_data));
-	if (drv == NULL) {
-		wpa_printf(MSG_ERROR, "Could not allocate memory for test "
-			   "driver data");
-		return NULL;
-	}
-
-	bss = os_zalloc(sizeof(struct test_driver_bss));
-	if (bss == NULL) {
-		os_free(drv);
-		return NULL;
-	}
-
-	drv->ctx = ctx;
-	wpa_trace_add_ref(drv, ctx, ctx);
-	dl_list_init(&drv->bss);
-	dl_list_add(&drv->bss, &bss->list);
-	os_strlcpy(bss->ifname, ifname, IFNAMSIZ);
-	bss->bss_ctx = ctx;
-	bss->drv = drv;
-
-	/* Generate a MAC address to help testing with multiple STAs */
-	drv->own_addr[0] = 0x02; /* locally administered */
-	sha1_prf((const u8 *) ifname, os_strlen(ifname),
-		 "test mac addr generation",
-		 NULL, 0, drv->own_addr + 1, ETH_ALEN - 1);
-
-	return drv;
-}
-
-
-static void * test_driver_init(struct hostapd_data *hapd,
-			       struct wpa_init_params *params)
-{
-	struct wpa_driver_test_data *drv;
-	struct sockaddr_un addr_un;
-	struct sockaddr_in addr_in;
-	struct sockaddr *addr;
-	socklen_t alen;
-	struct test_driver_bss *bss;
-
-	drv = test_alloc_data(hapd, params->ifname);
-	if (drv == NULL)
-		return NULL;
-	drv->ap = 1;
-	bss = dl_list_first(&drv->bss, struct test_driver_bss, list);
-	drv->global = params->global_priv;
-
-	bss->bss_ctx = hapd;
-	os_memcpy(bss->bssid, drv->own_addr, ETH_ALEN);
-	os_memcpy(params->own_addr, drv->own_addr, ETH_ALEN);
-
-	if (params->test_socket) {
-		if (os_strlen(params->test_socket) >=
-		    sizeof(addr_un.sun_path)) {
-			printf("Too long test_socket path\n");
-			wpa_driver_test_deinit(bss);
-			return NULL;
-		}
-		if (strncmp(params->test_socket, "DIR:", 4) == 0) {
-			size_t len = strlen(params->test_socket) + 30;
-			drv->test_dir = os_strdup(params->test_socket + 4);
-			drv->own_socket_path = os_malloc(len);
-			if (drv->own_socket_path) {
-				snprintf(drv->own_socket_path, len,
-					 "%s/AP-" MACSTR,
-					 params->test_socket + 4,
-					 MAC2STR(params->own_addr));
-			}
-		} else if (strncmp(params->test_socket, "UDP:", 4) == 0) {
-			drv->udp_port = atoi(params->test_socket + 4);
-		} else {
-			drv->own_socket_path = os_strdup(params->test_socket);
-		}
-		if (drv->own_socket_path == NULL && drv->udp_port == 0) {
-			wpa_driver_test_deinit(bss);
-			return NULL;
-		}
-
-		drv->test_socket = socket(drv->udp_port ? PF_INET : PF_UNIX,
-					  SOCK_DGRAM, 0);
-		if (drv->test_socket < 0) {
-			perror("socket");
-			wpa_driver_test_deinit(bss);
-			return NULL;
-		}
-
-		if (drv->udp_port) {
-			os_memset(&addr_in, 0, sizeof(addr_in));
-			addr_in.sin_family = AF_INET;
-			addr_in.sin_port = htons(drv->udp_port);
-			addr = (struct sockaddr *) &addr_in;
-			alen = sizeof(addr_in);
-		} else {
-			os_memset(&addr_un, 0, sizeof(addr_un));
-			addr_un.sun_family = AF_UNIX;
-			os_strlcpy(addr_un.sun_path, drv->own_socket_path,
-				   sizeof(addr_un.sun_path));
-			addr = (struct sockaddr *) &addr_un;
-			alen = sizeof(addr_un);
-		}
-		if (bind(drv->test_socket, addr, alen) < 0) {
-			perror("test-driver-init: bind(PF_UNIX)");
-			close(drv->test_socket);
-			if (drv->own_socket_path)
-				unlink(drv->own_socket_path);
-			wpa_driver_test_deinit(bss);
-			return NULL;
-		}
-		eloop_register_read_sock(drv->test_socket,
-					 test_driver_receive_unix, drv, NULL);
-	} else
-		drv->test_socket = -1;
-
-	return bss;
-}
-
-
-static void wpa_driver_test_poll(void *eloop_ctx, void *timeout_ctx)
-{
-	struct wpa_driver_test_data *drv = eloop_ctx;
-
-#ifdef DRIVER_TEST_UNIX
-	if (drv->associated && drv->hostapd_addr_set) {
-		struct stat st;
-		if (stat(drv->hostapd_addr.sun_path, &st) < 0) {
-			wpa_printf(MSG_DEBUG, "%s: lost connection to AP: %s",
-				   __func__, strerror(errno));
-			drv->associated = 0;
-			wpa_supplicant_event(drv->ctx, EVENT_DISASSOC, NULL);
-		}
-	}
-#endif /* DRIVER_TEST_UNIX */
-
-	eloop_register_timeout(1, 0, wpa_driver_test_poll, drv, NULL);
-}
-
-
-static void wpa_driver_test_scan_timeout(void *eloop_ctx, void *timeout_ctx)
-{
-	wpa_printf(MSG_DEBUG, "Scan timeout - try to get results");
-	wpa_supplicant_event(timeout_ctx, EVENT_SCAN_RESULTS, NULL);
-}
-
-
-#ifdef DRIVER_TEST_UNIX
-static void wpa_driver_scan_dir(struct wpa_driver_test_data *drv,
-				const char *path)
-{
-	struct dirent *dent;
-	DIR *dir;
-	struct sockaddr_un addr;
-	char cmd[512], *pos, *end;
-	int ret;
-
-	dir = opendir(path);
-	if (dir == NULL)
-		return;
-
-	end = cmd + sizeof(cmd);
-	pos = cmd;
-	ret = os_snprintf(pos, end - pos, "SCAN " MACSTR,
-			  MAC2STR(drv->own_addr));
-	if (ret >= 0 && ret < end - pos)
-		pos += ret;
-	if (drv->probe_req_ie) {
-		ret = os_snprintf(pos, end - pos, " ");
-		if (ret >= 0 && ret < end - pos)
-			pos += ret;
-		pos += wpa_snprintf_hex(pos, end - pos, drv->probe_req_ie,
-					drv->probe_req_ie_len);
-	}
-	if (drv->probe_req_ssid_len) {
-		/* Add SSID IE */
-		ret = os_snprintf(pos, end - pos, "%02x%02x",
-				  WLAN_EID_SSID,
-				  (unsigned int) drv->probe_req_ssid_len);
-		if (ret >= 0 && ret < end - pos)
-			pos += ret;
-		pos += wpa_snprintf_hex(pos, end - pos, drv->probe_req_ssid,
-					drv->probe_req_ssid_len);
-	}
-	end[-1] = '\0';
-
-	while ((dent = readdir(dir))) {
-		if (os_strncmp(dent->d_name, "AP-", 3) != 0 &&
-		    os_strncmp(dent->d_name, "STA-", 4) != 0)
-			continue;
-		if (drv->own_socket_path) {
-			size_t olen, dlen;
-			olen = os_strlen(drv->own_socket_path);
-			dlen = os_strlen(dent->d_name);
-			if (olen >= dlen &&
-			    os_strcmp(dent->d_name,
-				      drv->own_socket_path + olen - dlen) == 0)
-				continue;
-		}
-		wpa_printf(MSG_DEBUG, "%s: SCAN %s", __func__, dent->d_name);
-
-		os_memset(&addr, 0, sizeof(addr));
-		addr.sun_family = AF_UNIX;
-		os_snprintf(addr.sun_path, sizeof(addr.sun_path), "%s/%s",
-			    path, dent->d_name);
-
-		if (sendto(drv->test_socket, cmd, os_strlen(cmd), 0,
-			   (struct sockaddr *) &addr, sizeof(addr)) < 0) {
-			perror("sendto(test_socket)");
-		}
-	}
-	closedir(dir);
-}
-#endif /* DRIVER_TEST_UNIX */
-
-
-static int wpa_driver_test_scan(void *priv,
-				struct wpa_driver_scan_params *params)
-{
-	struct test_driver_bss *dbss = priv;
-	struct wpa_driver_test_data *drv = dbss->drv;
-	size_t i;
-
-	wpa_printf(MSG_DEBUG, "%s: priv=%p", __func__, priv);
-
-	os_free(drv->probe_req_ie);
-	if (params->extra_ies) {
-		drv->probe_req_ie = os_malloc(params->extra_ies_len);
-		if (drv->probe_req_ie == NULL) {
-			drv->probe_req_ie_len = 0;
-			return -1;
-		}
-		os_memcpy(drv->probe_req_ie, params->extra_ies,
-			  params->extra_ies_len);
-		drv->probe_req_ie_len = params->extra_ies_len;
-	} else {
-		drv->probe_req_ie = NULL;
-		drv->probe_req_ie_len = 0;
-	}
-
-	for (i = 0; i < params->num_ssids; i++)
-		wpa_hexdump(MSG_DEBUG, "Scan SSID",
-			    params->ssids[i].ssid, params->ssids[i].ssid_len);
-	drv->probe_req_ssid_len = 0;
-	if (params->num_ssids) {
-		os_memcpy(drv->probe_req_ssid, params->ssids[0].ssid,
-			  params->ssids[0].ssid_len);
-		drv->probe_req_ssid_len = params->ssids[0].ssid_len;
-	}
-	wpa_hexdump(MSG_DEBUG, "Scan extra IE(s)",
-		    params->extra_ies, params->extra_ies_len);
-
-	drv->num_scanres = 0;
-
-#ifdef DRIVER_TEST_UNIX
-	if (drv->test_socket >= 0 && drv->test_dir)
-		wpa_driver_scan_dir(drv, drv->test_dir);
-
-	if (drv->test_socket >= 0 && drv->hostapd_addr_set &&
-	    sendto(drv->test_socket, "SCAN", 4, 0,
-		   (struct sockaddr *) &drv->hostapd_addr,
-		   sizeof(drv->hostapd_addr)) < 0) {
-		perror("sendto(test_socket)");
-	}
-#endif /* DRIVER_TEST_UNIX */
-
-	if (drv->test_socket >= 0 && drv->hostapd_addr_udp_set &&
-	    sendto(drv->test_socket, "SCAN", 4, 0,
-		   (struct sockaddr *) &drv->hostapd_addr_udp,
-		   sizeof(drv->hostapd_addr_udp)) < 0) {
-		perror("sendto(test_socket)");
-	}
-
-	eloop_cancel_timeout(wpa_driver_test_scan_timeout, drv, drv->ctx);
-	eloop_register_timeout(1, 0, wpa_driver_test_scan_timeout, drv,
-			       drv->ctx);
-	return 0;
-}
-
-
-static struct wpa_scan_results * wpa_driver_test_get_scan_results2(void *priv)
-{
-	struct test_driver_bss *dbss = priv;
-	struct wpa_driver_test_data *drv = dbss->drv;
-	struct wpa_scan_results *res;
-	size_t i;
-
-	res = os_zalloc(sizeof(*res));
-	if (res == NULL)
-		return NULL;
-
-	res->res = os_calloc(drv->num_scanres, sizeof(struct wpa_scan_res *));
-	if (res->res == NULL) {
-		os_free(res);
-		return NULL;
-	}
-
-	for (i = 0; i < drv->num_scanres; i++) {
-		struct wpa_scan_res *r;
-		if (drv->scanres[i] == NULL)
-			continue;
-		r = os_malloc(sizeof(*r) + drv->scanres[i]->ie_len);
-		if (r == NULL)
-			break;
-		os_memcpy(r, drv->scanres[i],
-			  sizeof(*r) + drv->scanres[i]->ie_len);
-		res->res[res->num++] = r;
-	}
-
-	return res;
-}
-
-
-static int wpa_driver_test_set_key(const char *ifname, void *priv,
-				   enum wpa_alg alg, const u8 *addr,
-				   int key_idx, int set_tx,
-				   const u8 *seq, size_t seq_len,
-				   const u8 *key, size_t key_len)
-{
-	wpa_printf(MSG_DEBUG, "%s: ifname=%s priv=%p alg=%d key_idx=%d "
-		   "set_tx=%d",
-		   __func__, ifname, priv, alg, key_idx, set_tx);
-	if (addr)
-		wpa_printf(MSG_DEBUG, "   addr=" MACSTR, MAC2STR(addr));
-	if (seq)
-		wpa_hexdump(MSG_DEBUG, "   seq", seq, seq_len);
-	if (key)
-		wpa_hexdump_key(MSG_DEBUG, "   key", key, key_len);
-	return 0;
-}
-
-
-static int wpa_driver_update_mode(struct wpa_driver_test_data *drv, int ap)
-{
-	if (ap && !drv->ap) {
-		wpa_driver_test_close_test_socket(drv);
-		wpa_driver_test_attach(drv, drv->test_dir, 1);
-		drv->ap = 1;
-	} else if (!ap && drv->ap) {
-		wpa_driver_test_close_test_socket(drv);
-		wpa_driver_test_attach(drv, drv->test_dir, 0);
-		drv->ap = 0;
-	}
-
-	return 0;
-}
-
-
-static int wpa_driver_test_associate(
-	void *priv, struct wpa_driver_associate_params *params)
-{
-	struct test_driver_bss *dbss = priv;
-	struct wpa_driver_test_data *drv = dbss->drv;
-	wpa_printf(MSG_DEBUG, "%s: priv=%p freq=%d pairwise_suite=%d "
-		   "group_suite=%d key_mgmt_suite=%d auth_alg=%d mode=%d",
-		   __func__, priv, params->freq.freq, params->pairwise_suite,
-		   params->group_suite, params->key_mgmt_suite,
-		   params->auth_alg, params->mode);
-	wpa_driver_update_mode(drv, params->mode == IEEE80211_MODE_AP);
-	if (params->bssid) {
-		wpa_printf(MSG_DEBUG, "   bssid=" MACSTR,
-			   MAC2STR(params->bssid));
-	}
-	if (params->ssid) {
-		wpa_hexdump_ascii(MSG_DEBUG, "   ssid",
-				  params->ssid, params->ssid_len);
-	}
-	if (params->wpa_ie) {
-		wpa_hexdump(MSG_DEBUG, "   wpa_ie",
-			    params->wpa_ie, params->wpa_ie_len);
-		drv->assoc_wpa_ie_len = params->wpa_ie_len;
-		if (drv->assoc_wpa_ie_len > sizeof(drv->assoc_wpa_ie))
-			drv->assoc_wpa_ie_len = sizeof(drv->assoc_wpa_ie);
-		os_memcpy(drv->assoc_wpa_ie, params->wpa_ie,
-			  drv->assoc_wpa_ie_len);
-	} else
-		drv->assoc_wpa_ie_len = 0;
-
-	wpa_driver_update_mode(drv, params->mode == IEEE80211_MODE_AP);
-
-	drv->ibss = params->mode == IEEE80211_MODE_IBSS;
-	dbss->privacy = params->key_mgmt_suite &
-		(WPA_KEY_MGMT_IEEE8021X |
-		 WPA_KEY_MGMT_PSK |
-		 WPA_KEY_MGMT_WPA_NONE |
-		 WPA_KEY_MGMT_FT_IEEE8021X |
-		 WPA_KEY_MGMT_FT_PSK |
-		 WPA_KEY_MGMT_IEEE8021X_SHA256 |
-		 WPA_KEY_MGMT_PSK_SHA256);
-	if (params->wep_key_len[params->wep_tx_keyidx])
-		dbss->privacy = 1;
-
-#ifdef DRIVER_TEST_UNIX
-	if (drv->test_dir && params->bssid &&
-	    params->mode != IEEE80211_MODE_IBSS) {
-		os_memset(&drv->hostapd_addr, 0, sizeof(drv->hostapd_addr));
-		drv->hostapd_addr.sun_family = AF_UNIX;
-		os_snprintf(drv->hostapd_addr.sun_path,
-			    sizeof(drv->hostapd_addr.sun_path),
-			    "%s/AP-" MACSTR,
-			    drv->test_dir, MAC2STR(params->bssid));
-		drv->hostapd_addr_set = 1;
-	}
-#endif /* DRIVER_TEST_UNIX */
-
-	if (params->mode == IEEE80211_MODE_AP) {
-		if (params->ssid)
-			os_memcpy(dbss->ssid, params->ssid, params->ssid_len);
-		dbss->ssid_len = params->ssid_len;
-		os_memcpy(dbss->bssid, drv->own_addr, ETH_ALEN);
-		if (params->wpa_ie && params->wpa_ie_len) {
-			dbss->ie = os_malloc(params->wpa_ie_len);
-			if (dbss->ie) {
-				os_memcpy(dbss->ie, params->wpa_ie,
-					  params->wpa_ie_len);
-				dbss->ielen = params->wpa_ie_len;
-			}
-		}
-	} else if (drv->test_socket >= 0 &&
-		   (drv->hostapd_addr_set || drv->hostapd_addr_udp_set)) {
-		char cmd[200], *pos, *end;
-		int ret;
-		end = cmd + sizeof(cmd);
-		pos = cmd;
-		ret = os_snprintf(pos, end - pos, "ASSOC " MACSTR " ",
-				  MAC2STR(drv->own_addr));
-		if (ret >= 0 && ret < end - pos)
-			pos += ret;
-		if (params->ssid)
-			pos += wpa_snprintf_hex(pos, end - pos, params->ssid,
-						params->ssid_len);
-		ret = os_snprintf(pos, end - pos, " ");
-		if (ret >= 0 && ret < end - pos)
-			pos += ret;
-		pos += wpa_snprintf_hex(pos, end - pos, params->wpa_ie,
-					params->wpa_ie_len);
-		end[-1] = '\0';
-#ifdef DRIVER_TEST_UNIX
-		if (drv->hostapd_addr_set &&
-		    sendto(drv->test_socket, cmd, os_strlen(cmd), 0,
-			   (struct sockaddr *) &drv->hostapd_addr,
-			   sizeof(drv->hostapd_addr)) < 0) {
-			perror("sendto(test_socket)");
-			return -1;
-		}
-#endif /* DRIVER_TEST_UNIX */
-		if (drv->hostapd_addr_udp_set &&
-		    sendto(drv->test_socket, cmd, os_strlen(cmd), 0,
-			   (struct sockaddr *) &drv->hostapd_addr_udp,
-			   sizeof(drv->hostapd_addr_udp)) < 0) {
-			perror("sendto(test_socket)");
-			return -1;
-		}
-
-		if (params->ssid)
-			os_memcpy(dbss->ssid, params->ssid, params->ssid_len);
-		dbss->ssid_len = params->ssid_len;
-	} else {
-		drv->associated = 1;
-		if (params->mode == IEEE80211_MODE_IBSS) {
-			if (params->ssid)
-				os_memcpy(dbss->ssid, params->ssid,
-					  params->ssid_len);
-			dbss->ssid_len = params->ssid_len;
-			if (params->bssid)
-				os_memcpy(dbss->bssid, params->bssid,
-					  ETH_ALEN);
-			else {
-				os_get_random(dbss->bssid, ETH_ALEN);
-				dbss->bssid[0] &= ~0x01;
-				dbss->bssid[0] |= 0x02;
-			}
-		}
-		wpa_supplicant_event(drv->ctx, EVENT_ASSOC, NULL);
-	}
-
-	return 0;
-}
-
-
-static int wpa_driver_test_get_bssid(void *priv, u8 *bssid)
-{
-	struct test_driver_bss *dbss = priv;
-	os_memcpy(bssid, dbss->bssid, ETH_ALEN);
-	return 0;
-}
-
-
-static int wpa_driver_test_get_ssid(void *priv, u8 *ssid)
-{
-	struct test_driver_bss *dbss = priv;
-	os_memcpy(ssid, dbss->ssid, 32);
-	return dbss->ssid_len;
-}
-
-
-static int wpa_driver_test_send_disassoc(struct wpa_driver_test_data *drv)
-{
-#ifdef DRIVER_TEST_UNIX
-	if (drv->test_socket >= 0 &&
-	    sendto(drv->test_socket, "DISASSOC", 8, 0,
-		   (struct sockaddr *) &drv->hostapd_addr,
-		   sizeof(drv->hostapd_addr)) < 0) {
-		perror("sendto(test_socket)");
-		return -1;
-	}
-#endif /* DRIVER_TEST_UNIX */
-	if (drv->test_socket >= 0 && drv->hostapd_addr_udp_set &&
-	    sendto(drv->test_socket, "DISASSOC", 8, 0,
-		   (struct sockaddr *) &drv->hostapd_addr_udp,
-		   sizeof(drv->hostapd_addr_udp)) < 0) {
-		perror("sendto(test_socket)");
-		return -1;
-	}
-	return 0;
-}
-
-
-static int wpa_driver_test_deauthenticate(void *priv, const u8 *addr,
-					  int reason_code)
-{
-	struct test_driver_bss *dbss = priv;
-	struct wpa_driver_test_data *drv = dbss->drv;
-	wpa_printf(MSG_DEBUG, "%s addr=" MACSTR " reason_code=%d",
-		   __func__, MAC2STR(addr), reason_code);
-	os_memset(dbss->bssid, 0, ETH_ALEN);
-	drv->associated = 0;
-	wpa_supplicant_event(drv->ctx, EVENT_DISASSOC, NULL);
-	return wpa_driver_test_send_disassoc(drv);
-}
-
-
-static const u8 * wpa_scan_get_ie(const struct wpa_scan_res *res, u8 ie)
-{
-	const u8 *end, *pos;
-
-	pos = (const u8 *) (res + 1);
-	end = pos + res->ie_len;
-
-	while (pos + 1 < end) {
-		if (pos + 2 + pos[1] > end)
-			break;
-		if (pos[0] == ie)
-			return pos;
-		pos += 2 + pos[1];
-	}
-
-	return NULL;
-}
-
-
-static void wpa_driver_test_scanresp(struct wpa_driver_test_data *drv,
-				     struct sockaddr *from,
-				     socklen_t fromlen,
-				     const char *data)
-{
-	struct wpa_scan_res *res;
-	const char *pos, *pos2;
-	size_t len;
-	u8 *ie_pos, *ie_start, *ie_end;
-#define MAX_IE_LEN 1000
-	const u8 *ds_params;
-
-	wpa_printf(MSG_DEBUG, "test_driver: SCANRESP %s", data);
-	if (drv->num_scanres >= MAX_SCAN_RESULTS) {
-		wpa_printf(MSG_DEBUG, "test_driver: No room for the new scan "
-			   "result");
-		return;
-	}
-
-	/* SCANRESP BSSID SSID IEs */
-
-	res = os_zalloc(sizeof(*res) + MAX_IE_LEN);
-	if (res == NULL)
-		return;
-	ie_start = ie_pos = (u8 *) (res + 1);
-	ie_end = ie_pos + MAX_IE_LEN;
-
-	if (hwaddr_aton(data, res->bssid)) {
-		wpa_printf(MSG_DEBUG, "test_driver: invalid BSSID in scanres");
-		os_free(res);
-		return;
-	}
-
-	pos = data + 17;
-	while (*pos == ' ')
-		pos++;
-	pos2 = os_strchr(pos, ' ');
-	if (pos2 == NULL) {
-		wpa_printf(MSG_DEBUG, "test_driver: invalid SSID termination "
-			   "in scanres");
-		os_free(res);
-		return;
-	}
-	len = (pos2 - pos) / 2;
-	if (len > 32)
-		len = 32;
-	/*
-	 * Generate SSID IE from the SSID field since this IE is not included
-	 * in the main IE field.
-	 */
-	*ie_pos++ = WLAN_EID_SSID;
-	*ie_pos++ = len;
-	if (hexstr2bin(pos, ie_pos, len) < 0) {
-		wpa_printf(MSG_DEBUG, "test_driver: invalid SSID in scanres");
-		os_free(res);
-		return;
-	}
-	ie_pos += len;
-
-	pos = pos2 + 1;
-	pos2 = os_strchr(pos, ' ');
-	if (pos2 == NULL)
-		len = os_strlen(pos) / 2;
-	else
-		len = (pos2 - pos) / 2;
-	if ((int) len > ie_end - ie_pos)
-		len = ie_end - ie_pos;
-	if (hexstr2bin(pos, ie_pos, len) < 0) {
-		wpa_printf(MSG_DEBUG, "test_driver: invalid IEs in scanres");
-		os_free(res);
-		return;
-	}
-	ie_pos += len;
-	res->ie_len = ie_pos - ie_start;
-
-	if (pos2) {
-		pos = pos2 + 1;
-		while (*pos == ' ')
-			pos++;
-		if (os_strstr(pos, "PRIVACY"))
-			res->caps |= IEEE80211_CAP_PRIVACY;
-		if (os_strstr(pos, "IBSS"))
-			res->caps |= IEEE80211_CAP_IBSS;
-	}
-
-	ds_params = wpa_scan_get_ie(res, WLAN_EID_DS_PARAMS);
-	if (ds_params && ds_params[1] > 0) {
-		if (ds_params[2] >= 1 && ds_params[2] <= 13)
-			res->freq = 2407 + ds_params[2] * 5;
-	}
-
-	os_free(drv->scanres[drv->num_scanres]);
-	drv->scanres[drv->num_scanres++] = res;
-}
-
-
-static void wpa_driver_test_assocresp(struct wpa_driver_test_data *drv,
-				      struct sockaddr *from,
-				      socklen_t fromlen,
-				      const char *data)
-{
-	struct test_driver_bss *bss;
-
-	bss = dl_list_first(&drv->bss, struct test_driver_bss, list);
-
-	/* ASSOCRESP BSSID <res> */
-	if (hwaddr_aton(data, bss->bssid)) {
-		wpa_printf(MSG_DEBUG, "test_driver: invalid BSSID in "
-			   "assocresp");
-	}
-	if (drv->use_associnfo) {
-		union wpa_event_data event;
-		os_memset(&event, 0, sizeof(event));
-		event.assoc_info.req_ies = drv->assoc_wpa_ie;
-		event.assoc_info.req_ies_len = drv->assoc_wpa_ie_len;
-		wpa_supplicant_event(drv->ctx, EVENT_ASSOCINFO, &event);
-	}
-	drv->associated = 1;
-	wpa_supplicant_event(drv->ctx, EVENT_ASSOC, NULL);
-}
-
-
-static void wpa_driver_test_disassoc(struct wpa_driver_test_data *drv,
-				     struct sockaddr *from,
-				     socklen_t fromlen)
-{
-	drv->associated = 0;
-	wpa_supplicant_event(drv->ctx, EVENT_DISASSOC, NULL);
-}
-
-
-static void wpa_driver_test_eapol(struct wpa_driver_test_data *drv,
-				  struct sockaddr *from,
-				  socklen_t fromlen,
-				  const u8 *data, size_t data_len)
-{
-	const u8 *src;
-	struct test_driver_bss *bss;
-
-	bss = dl_list_first(&drv->bss, struct test_driver_bss, list);
-
-	if (data_len > 14) {
-		/* Skip Ethernet header */
-		src = data + ETH_ALEN;
-		data += 14;
-		data_len -= 14;
-	} else
-		src = bss->bssid;
-
-	drv_event_eapol_rx(drv->ctx, src, data, data_len);
-}
-
-
-static void wpa_driver_test_mlme(struct wpa_driver_test_data *drv,
-				 struct sockaddr *from,
-				 socklen_t fromlen,
-				 const u8 *data, size_t data_len)
-{
-	int freq = 0, own_freq;
-	union wpa_event_data event;
-	const struct ieee80211_mgmt *mgmt;
-	u16 fc;
-	struct test_driver_bss *bss;
-
-	bss = dl_list_first(&drv->bss, struct test_driver_bss, list);
-	if (data_len > 6 && os_memcmp(data, "freq=", 5) == 0) {
-		size_t pos;
-		for (pos = 5; pos < data_len; pos++) {
-			if (data[pos] == ' ')
-				break;
-		}
-		if (pos < data_len) {
-			freq = atoi((const char *) &data[5]);
-			wpa_printf(MSG_DEBUG, "test_driver(%s): MLME RX on "
-				   "freq %d MHz", bss->ifname, freq);
-			pos++;
-			data += pos;
-			data_len -= pos;
-		}
-	}
-
-	if (drv->remain_on_channel_freq)
-		own_freq = drv->remain_on_channel_freq;
-	else
-		own_freq = drv->current_freq;
-
-	if (freq && own_freq && freq != own_freq) {
-		wpa_printf(MSG_DEBUG, "test_driver(%s): Ignore MLME RX on "
-			   "another frequency %d MHz (own %d MHz)",
-			   bss->ifname, freq, own_freq);
-		return;
-	}
-
-	os_memset(&event, 0, sizeof(event));
-	event.mlme_rx.buf = data;
-	event.mlme_rx.len = data_len;
-	event.mlme_rx.freq = freq;
-	wpa_supplicant_event(drv->ctx, EVENT_MLME_RX, &event);
-
-	mgmt = (const struct ieee80211_mgmt *) data;
-	fc = le_to_host16(mgmt->frame_control);
-
-	if (drv->probe_req_report && data_len >= 24) {
-		if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT &&
-		    WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_PROBE_REQ) {
-			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 =
-				data_len - (mgmt->u.probe_req.variable - data);
-			wpa_supplicant_event(drv->ctx, EVENT_RX_PROBE_REQ,
-					     &event);
-		}
-	}
-}
-
-
-static void wpa_driver_test_scan_cmd(struct wpa_driver_test_data *drv,
-				     struct sockaddr *from,
-				     socklen_t fromlen,
-				     const u8 *data, size_t data_len)
-{
-	char buf[512], *pos, *end;
-	int ret;
-	struct test_driver_bss *bss;
-
-	bss = dl_list_first(&drv->bss, struct test_driver_bss, list);
-
-	/* data: optional [ STA-addr | ' ' | IEs(hex) ] */
-
-	if (bss == NULL || !drv->ibss)
-		return;
-
-	pos = buf;
-	end = buf + sizeof(buf);
-
-	/* reply: SCANRESP BSSID SSID IEs */
-	ret = snprintf(pos, end - pos, "SCANRESP " MACSTR " ",
-		       MAC2STR(bss->bssid));
-	if (ret < 0 || ret >= end - pos)
-		return;
-	pos += ret;
-	pos += wpa_snprintf_hex(pos, end - pos,
-				bss->ssid, bss->ssid_len);
-	ret = snprintf(pos, end - pos, " ");
-	if (ret < 0 || ret >= end - pos)
-		return;
-	pos += ret;
-	pos += wpa_snprintf_hex(pos, end - pos, drv->assoc_wpa_ie,
-				drv->assoc_wpa_ie_len);
-
-	if (bss->privacy) {
-		ret = snprintf(pos, end - pos, " PRIVACY");
-		if (ret < 0 || ret >= end - pos)
-			return;
-		pos += ret;
-	}
-
-	ret = snprintf(pos, end - pos, " IBSS");
-	if (ret < 0 || ret >= end - pos)
-		return;
-	pos += ret;
-
-	sendto(drv->test_socket, buf, pos - buf, 0,
-	       (struct sockaddr *) from, fromlen);
-}
-
-
-static void wpa_driver_test_receive_unix(int sock, void *eloop_ctx,
-					 void *sock_ctx)
-{
-	struct wpa_driver_test_data *drv = eloop_ctx;
-	char *buf;
-	int res;
-	struct sockaddr_storage from;
-	socklen_t fromlen = sizeof(from);
-	const size_t buflen = 2000;
-
-	if (drv->ap) {
-		test_driver_receive_unix(sock, eloop_ctx, sock_ctx);
-		return;
-	}
-
-	buf = os_malloc(buflen);
-	if (buf == NULL)
-		return;
-	res = recvfrom(sock, buf, buflen - 1, 0,
-		       (struct sockaddr *) &from, &fromlen);
-	if (res < 0) {
-		perror("recvfrom(test_socket)");
-		os_free(buf);
-		return;
-	}
-	buf[res] = '\0';
-
-	wpa_printf(MSG_DEBUG, "test_driver: received %u bytes", res);
-
-	if (os_strncmp(buf, "SCANRESP ", 9) == 0) {
-		wpa_driver_test_scanresp(drv, (struct sockaddr *) &from,
-					 fromlen, buf + 9);
-	} else if (os_strncmp(buf, "ASSOCRESP ", 10) == 0) {
-		wpa_driver_test_assocresp(drv, (struct sockaddr *) &from,
-					  fromlen, buf + 10);
-	} else if (os_strcmp(buf, "DISASSOC") == 0) {
-		wpa_driver_test_disassoc(drv, (struct sockaddr *) &from,
-					 fromlen);
-	} else if (os_strcmp(buf, "DEAUTH") == 0) {
-		wpa_driver_test_disassoc(drv, (struct sockaddr *) &from,
-					 fromlen);
-	} else if (os_strncmp(buf, "EAPOL ", 6) == 0) {
-		wpa_driver_test_eapol(drv, (struct sockaddr *) &from, fromlen,
-				      (const u8 *) buf + 6, res - 6);
-	} else if (os_strncmp(buf, "MLME ", 5) == 0) {
-		wpa_driver_test_mlme(drv, (struct sockaddr *) &from, fromlen,
-				     (const u8 *) buf + 5, res - 5);
-	} else if (os_strncmp(buf, "SCAN ", 5) == 0) {
-		wpa_driver_test_scan_cmd(drv, (struct sockaddr *) &from,
-					 fromlen,
-					 (const u8 *) buf + 5, res - 5);
-	} else {
-		wpa_hexdump_ascii(MSG_DEBUG, "Unknown test_socket command",
-				  (u8 *) buf, res);
-	}
-	os_free(buf);
-}
-
-
-static void * wpa_driver_test_init2(void *ctx, const char *ifname,
-				    void *global_priv)
-{
-	struct wpa_driver_test_data *drv;
-	struct wpa_driver_test_global *global = global_priv;
-	struct test_driver_bss *bss;
-
-	drv = test_alloc_data(ctx, ifname);
-	if (drv == NULL)
-		return NULL;
-	bss = dl_list_first(&drv->bss, struct test_driver_bss, list);
-	drv->global = global_priv;
-	drv->test_socket = -1;
-
-	/* Set dummy BSSID and SSID for testing. */
-	bss->bssid[0] = 0x02;
-	bss->bssid[1] = 0x00;
-	bss->bssid[2] = 0x00;
-	bss->bssid[3] = 0x00;
-	bss->bssid[4] = 0x00;
-	bss->bssid[5] = 0x01;
-	os_memcpy(bss->ssid, "test", 5);
-	bss->ssid_len = 4;
-
-	if (global->bss_add_used) {
-		os_memcpy(drv->own_addr, global->req_addr, ETH_ALEN);
-		global->bss_add_used = 0;
-	}
-
-	eloop_register_timeout(1, 0, wpa_driver_test_poll, drv, NULL);
-
-	return bss;
-}
-
-
-static void wpa_driver_test_close_test_socket(struct wpa_driver_test_data *drv)
-{
-	if (drv->test_socket >= 0) {
-		eloop_unregister_read_sock(drv->test_socket);
-		close(drv->test_socket);
-		drv->test_socket = -1;
-	}
-
-	if (drv->own_socket_path) {
-		unlink(drv->own_socket_path);
-		os_free(drv->own_socket_path);
-		drv->own_socket_path = NULL;
-	}
-}
-
-
-static void wpa_driver_test_deinit(void *priv)
-{
-	struct test_driver_bss *dbss = priv;
-	struct wpa_driver_test_data *drv = dbss->drv;
-	struct test_client_socket *cli, *prev;
-	int i;
-
-	cli = drv->cli;
-	while (cli) {
-		prev = cli;
-		cli = cli->next;
-		os_free(prev);
-	}
-
-#ifdef HOSTAPD
-	/* There should be only one BSS remaining at this point. */
-	if (dl_list_len(&drv->bss) != 1)
-		wpa_printf(MSG_ERROR, "%s: %u remaining BSS entries",
-			   __func__, dl_list_len(&drv->bss));
-#endif /* HOSTAPD */
-
-	test_driver_free_bsses(drv);
-
-	wpa_driver_test_close_test_socket(drv);
-	eloop_cancel_timeout(wpa_driver_test_scan_timeout, drv, drv->ctx);
-	eloop_cancel_timeout(wpa_driver_test_poll, drv, NULL);
-	eloop_cancel_timeout(test_remain_on_channel_timeout, drv, NULL);
-	os_free(drv->test_dir);
-	for (i = 0; i < MAX_SCAN_RESULTS; i++)
-		os_free(drv->scanres[i]);
-	os_free(drv->probe_req_ie);
-	wpa_trace_remove_ref(drv, ctx, drv->ctx);
-	os_free(drv);
-}
-
-
-static int wpa_driver_test_attach(struct wpa_driver_test_data *drv,
-				  const char *dir, int ap)
-{
-#ifdef DRIVER_TEST_UNIX
-	static unsigned int counter = 0;
-	struct sockaddr_un addr;
-	size_t len;
-
-	os_free(drv->own_socket_path);
-	if (dir) {
-		len = os_strlen(dir) + 30;
-		drv->own_socket_path = os_malloc(len);
-		if (drv->own_socket_path == NULL)
-			return -1;
-		os_snprintf(drv->own_socket_path, len, "%s/%s-" MACSTR,
-			    dir, ap ? "AP" : "STA", MAC2STR(drv->own_addr));
-	} else {
-		drv->own_socket_path = os_malloc(100);
-		if (drv->own_socket_path == NULL)
-			return -1;
-		os_snprintf(drv->own_socket_path, 100,
-			    "/tmp/wpa_supplicant_test-%d-%d",
-			    getpid(), counter++);
-	}
-
-	drv->test_socket = socket(PF_UNIX, SOCK_DGRAM, 0);
-	if (drv->test_socket < 0) {
-		perror("socket(PF_UNIX)");
-		os_free(drv->own_socket_path);
-		drv->own_socket_path = NULL;
-		return -1;
-	}
-
-	os_memset(&addr, 0, sizeof(addr));
-	addr.sun_family = AF_UNIX;
-	os_strlcpy(addr.sun_path, drv->own_socket_path, sizeof(addr.sun_path));
-	if (bind(drv->test_socket, (struct sockaddr *) &addr,
-		 sizeof(addr)) < 0) {
-		perror("test-driver-attach: bind(PF_UNIX)");
-		close(drv->test_socket);
-		unlink(drv->own_socket_path);
-		os_free(drv->own_socket_path);
-		drv->own_socket_path = NULL;
-		return -1;
-	}
-
-	eloop_register_read_sock(drv->test_socket,
-				 wpa_driver_test_receive_unix, drv, NULL);
-
-	return 0;
-#else /* DRIVER_TEST_UNIX */
-	return -1;
-#endif /* DRIVER_TEST_UNIX */
-}
-
-
-static int wpa_driver_test_attach_udp(struct wpa_driver_test_data *drv,
-				      char *dst)
-{
-	char *pos;
-
-	pos = os_strchr(dst, ':');
-	if (pos == NULL)
-		return -1;
-	*pos++ = '\0';
-	wpa_printf(MSG_DEBUG, "%s: addr=%s port=%s", __func__, dst, pos);
-
-	drv->test_socket = socket(PF_INET, SOCK_DGRAM, 0);
-	if (drv->test_socket < 0) {
-		perror("socket(PF_INET)");
-		return -1;
-	}
-
-	os_memset(&drv->hostapd_addr_udp, 0, sizeof(drv->hostapd_addr_udp));
-	drv->hostapd_addr_udp.sin_family = AF_INET;
-#if defined(CONFIG_NATIVE_WINDOWS) || defined(CONFIG_ANSI_C_EXTRA)
-	{
-		int a[4];
-		u8 *pos;
-		sscanf(dst, "%d.%d.%d.%d", &a[0], &a[1], &a[2], &a[3]);
-		pos = (u8 *) &drv->hostapd_addr_udp.sin_addr;
-		*pos++ = a[0];
-		*pos++ = a[1];
-		*pos++ = a[2];
-		*pos++ = a[3];
-	}
-#else /* CONFIG_NATIVE_WINDOWS or CONFIG_ANSI_C_EXTRA */
-	inet_aton(dst, &drv->hostapd_addr_udp.sin_addr);
-#endif /* CONFIG_NATIVE_WINDOWS or CONFIG_ANSI_C_EXTRA */
-	drv->hostapd_addr_udp.sin_port = htons(atoi(pos));
-
-	drv->hostapd_addr_udp_set = 1;
-
-	eloop_register_read_sock(drv->test_socket,
-				 wpa_driver_test_receive_unix, drv, NULL);
-
-	return 0;
-}
-
-
-static int wpa_driver_test_set_param(void *priv, const char *param)
-{
-	struct test_driver_bss *dbss = priv;
-	struct wpa_driver_test_data *drv = dbss->drv;
-	const char *pos;
-
-	wpa_printf(MSG_DEBUG, "%s: param='%s'", __func__, param);
-	if (param == NULL)
-		return 0;
-
-	wpa_driver_test_close_test_socket(drv);
-
-#ifdef DRIVER_TEST_UNIX
-	pos = os_strstr(param, "test_socket=");
-	if (pos) {
-		const char *pos2;
-		size_t len;
-
-		pos += 12;
-		pos2 = os_strchr(pos, ' ');
-		if (pos2)
-			len = pos2 - pos;
-		else
-			len = os_strlen(pos);
-		if (len > sizeof(drv->hostapd_addr.sun_path))
-			return -1;
-		os_memset(&drv->hostapd_addr, 0, sizeof(drv->hostapd_addr));
-		drv->hostapd_addr.sun_family = AF_UNIX;
-		os_memcpy(drv->hostapd_addr.sun_path, pos, len);
-		drv->hostapd_addr_set = 1;
-	}
-#endif /* DRIVER_TEST_UNIX */
-
-	pos = os_strstr(param, "test_dir=");
-	if (pos) {
-		char *end;
-		os_free(drv->test_dir);
-		drv->test_dir = os_strdup(pos + 9);
-		if (drv->test_dir == NULL)
-			return -1;
-		end = os_strchr(drv->test_dir, ' ');
-		if (end)
-			*end = '\0';
-		if (wpa_driver_test_attach(drv, drv->test_dir, 0))
-			return -1;
-	} else {
-		pos = os_strstr(param, "test_udp=");
-		if (pos) {
-			char *dst, *epos;
-			dst = os_strdup(pos + 9);
-			if (dst == NULL)
-				return -1;
-			epos = os_strchr(dst, ' ');
-			if (epos)
-				*epos = '\0';
-			if (wpa_driver_test_attach_udp(drv, dst))
-				return -1;
-			os_free(dst);
-		} else if (wpa_driver_test_attach(drv, NULL, 0))
-			return -1;
-	}
-
-	if (os_strstr(param, "use_associnfo=1")) {
-		wpa_printf(MSG_DEBUG, "test_driver: Use AssocInfo events");
-		drv->use_associnfo = 1;
-	}
-
-	return 0;
-}
-
-
-static const u8 * wpa_driver_test_get_mac_addr(void *priv)
-{
-	struct test_driver_bss *dbss = priv;
-	struct wpa_driver_test_data *drv = dbss->drv;
-	wpa_printf(MSG_DEBUG, "%s", __func__);
-	return drv->own_addr;
-}
-
-
-static int wpa_driver_test_send_eapol(void *priv, const u8 *dest, u16 proto,
-				      const u8 *data, size_t data_len)
-{
-	struct test_driver_bss *dbss = priv;
-	struct wpa_driver_test_data *drv = dbss->drv;
-	char *msg;
-	size_t msg_len;
-	struct l2_ethhdr eth;
-	struct sockaddr *addr;
-	socklen_t alen;
-#ifdef DRIVER_TEST_UNIX
-	struct sockaddr_un addr_un;
-#endif /* DRIVER_TEST_UNIX */
-
-	wpa_hexdump(MSG_MSGDUMP, "test_send_eapol TX frame", data, data_len);
-
-	os_memset(&eth, 0, sizeof(eth));
-	os_memcpy(eth.h_dest, dest, ETH_ALEN);
-	os_memcpy(eth.h_source, drv->own_addr, ETH_ALEN);
-	eth.h_proto = host_to_be16(proto);
-
-	msg_len = 6 + sizeof(eth) + data_len;
-	msg = os_malloc(msg_len);
-	if (msg == NULL)
-		return -1;
-	os_memcpy(msg, "EAPOL ", 6);
-	os_memcpy(msg + 6, &eth, sizeof(eth));
-	os_memcpy(msg + 6 + sizeof(eth), data, data_len);
-
-	if (os_memcmp(dest, dbss->bssid, ETH_ALEN) == 0 ||
-	    drv->test_dir == NULL) {
-		if (drv->hostapd_addr_udp_set) {
-			addr = (struct sockaddr *) &drv->hostapd_addr_udp;
-			alen = sizeof(drv->hostapd_addr_udp);
-		} else {
-#ifdef DRIVER_TEST_UNIX
-			addr = (struct sockaddr *) &drv->hostapd_addr;
-			alen = sizeof(drv->hostapd_addr);
-#else /* DRIVER_TEST_UNIX */
-			os_free(msg);
-			return -1;
-#endif /* DRIVER_TEST_UNIX */
-		}
-	} else {
-#ifdef DRIVER_TEST_UNIX
-		struct stat st;
-		os_memset(&addr_un, 0, sizeof(addr_un));
-		addr_un.sun_family = AF_UNIX;
-		os_snprintf(addr_un.sun_path, sizeof(addr_un.sun_path),
-			    "%s/STA-" MACSTR, drv->test_dir, MAC2STR(dest));
-		if (stat(addr_un.sun_path, &st) < 0) {
-			os_snprintf(addr_un.sun_path, sizeof(addr_un.sun_path),
-				    "%s/AP-" MACSTR,
-				    drv->test_dir, MAC2STR(dest));
-		}
-		addr = (struct sockaddr *) &addr_un;
-		alen = sizeof(addr_un);
-#else /* DRIVER_TEST_UNIX */
-		os_free(msg);
-		return -1;
-#endif /* DRIVER_TEST_UNIX */
-	}
-
-	if (sendto(drv->test_socket, msg, msg_len, 0, addr, alen) < 0) {
-		perror("sendmsg(test_socket)");
-		os_free(msg);
-		return -1;
-	}
-
-	os_free(msg);
-	return 0;
-}
-
-
-static int wpa_driver_test_get_capa(void *priv, struct wpa_driver_capa *capa)
-{
-	os_memset(capa, 0, sizeof(*capa));
-	capa->key_mgmt = WPA_DRIVER_CAPA_KEY_MGMT_WPA |
-		WPA_DRIVER_CAPA_KEY_MGMT_WPA2 |
-		WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK |
-		WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK |
-		WPA_DRIVER_CAPA_KEY_MGMT_WPA_NONE |
-		WPA_DRIVER_CAPA_KEY_MGMT_FT |
-		WPA_DRIVER_CAPA_KEY_MGMT_FT_PSK;
-	capa->enc = WPA_DRIVER_CAPA_ENC_WEP40 |
-		WPA_DRIVER_CAPA_ENC_WEP104 |
-		WPA_DRIVER_CAPA_ENC_TKIP |
-		WPA_DRIVER_CAPA_ENC_CCMP;
-	capa->auth = WPA_DRIVER_AUTH_OPEN |
-		WPA_DRIVER_AUTH_SHARED |
-		WPA_DRIVER_AUTH_LEAP;
-	capa->flags |= WPA_DRIVER_FLAGS_AP;
-	capa->flags |= WPA_DRIVER_FLAGS_P2P_CONCURRENT;
-	capa->flags |= WPA_DRIVER_FLAGS_P2P_DEDICATED_INTERFACE;
-	capa->flags |= WPA_DRIVER_FLAGS_P2P_CAPABLE;
-	capa->max_scan_ssids = 2;
-	capa->max_remain_on_chan = 60000;
-
-	return 0;
-}
-
-
-static int wpa_driver_test_mlme_setprotection(void *priv, const u8 *addr,
-					      int protect_type,
-					      int key_type)
-{
-	wpa_printf(MSG_DEBUG, "%s: protect_type=%d key_type=%d",
-		   __func__, protect_type, key_type);
-
-	if (addr) {
-		wpa_printf(MSG_DEBUG, "%s: addr=" MACSTR,
-			   __func__, MAC2STR(addr));
-	}
-
-	return 0;
-}
-
-
-static void * wpa_driver_test_global_init(void)
-{
-	struct wpa_driver_test_global *global;
-
-	global = os_zalloc(sizeof(*global));
-	return global;
-}
-
-
-static void wpa_driver_test_global_deinit(void *priv)
-{
-	struct wpa_driver_test_global *global = priv;
-	os_free(global);
-}
-
-
-static struct wpa_interface_info *
-wpa_driver_test_get_interfaces(void *global_priv)
-{
-	/* struct wpa_driver_test_global *global = priv; */
-	struct wpa_interface_info *iface;
-
-	iface = os_zalloc(sizeof(*iface));
-	if (iface == NULL)
-		return iface;
-	iface->ifname = os_strdup("sta0");
-	iface->desc = os_strdup("test interface 0");
-	iface->drv_name = "test";
-	iface->next = os_zalloc(sizeof(*iface));
-	if (iface->next) {
-		iface->next->ifname = os_strdup("sta1");
-		iface->next->desc = os_strdup("test interface 1");
-		iface->next->drv_name = "test";
-	}
-
-	return iface;
-}
-
-
-static struct hostapd_hw_modes *
-wpa_driver_test_get_hw_feature_data(void *priv, u16 *num_modes, u16 *flags)
-{
-	struct hostapd_hw_modes *modes;
-	size_t i;
-
-	*num_modes = 3;
-	*flags = 0;
-	modes = os_calloc(*num_modes, sizeof(struct hostapd_hw_modes));
-	if (modes == NULL)
-		return NULL;
-	modes[0].mode = HOSTAPD_MODE_IEEE80211G;
-	modes[0].num_channels = 11;
-	modes[0].num_rates = 12;
-	modes[0].channels = os_calloc(11, sizeof(struct hostapd_channel_data));
-	modes[0].rates = os_calloc(modes[0].num_rates, sizeof(int));
-	if (modes[0].channels == NULL || modes[0].rates == NULL)
-		goto fail;
-	for (i = 0; i < 11; i++) {
-		modes[0].channels[i].chan = i + 1;
-		modes[0].channels[i].freq = 2412 + 5 * i;
-		modes[0].channels[i].flag = 0;
-	}
-	modes[0].rates[0] = 10;
-	modes[0].rates[1] = 20;
-	modes[0].rates[2] = 55;
-	modes[0].rates[3] = 110;
-	modes[0].rates[4] = 60;
-	modes[0].rates[5] = 90;
-	modes[0].rates[6] = 120;
-	modes[0].rates[7] = 180;
-	modes[0].rates[8] = 240;
-	modes[0].rates[9] = 360;
-	modes[0].rates[10] = 480;
-	modes[0].rates[11] = 540;
-
-	modes[1].mode = HOSTAPD_MODE_IEEE80211B;
-	modes[1].num_channels = 11;
-	modes[1].num_rates = 4;
-	modes[1].channels = os_calloc(11, sizeof(struct hostapd_channel_data));
-	modes[1].rates = os_calloc(modes[1].num_rates, sizeof(int));
-	if (modes[1].channels == NULL || modes[1].rates == NULL)
-		goto fail;
-	for (i = 0; i < 11; i++) {
-		modes[1].channels[i].chan = i + 1;
-		modes[1].channels[i].freq = 2412 + 5 * i;
-		modes[1].channels[i].flag = 0;
-	}
-	modes[1].rates[0] = 10;
-	modes[1].rates[1] = 20;
-	modes[1].rates[2] = 55;
-	modes[1].rates[3] = 110;
-
-	modes[2].mode = HOSTAPD_MODE_IEEE80211A;
-	modes[2].num_channels = 1;
-	modes[2].num_rates = 8;
-	modes[2].channels = os_calloc(1, sizeof(struct hostapd_channel_data));
-	modes[2].rates = os_calloc(modes[2].num_rates, sizeof(int));
-	if (modes[2].channels == NULL || modes[2].rates == NULL)
-		goto fail;
-	modes[2].channels[0].chan = 60;
-	modes[2].channels[0].freq = 5300;
-	modes[2].channels[0].flag = 0;
-	modes[2].rates[0] = 60;
-	modes[2].rates[1] = 90;
-	modes[2].rates[2] = 120;
-	modes[2].rates[3] = 180;
-	modes[2].rates[4] = 240;
-	modes[2].rates[5] = 360;
-	modes[2].rates[6] = 480;
-	modes[2].rates[7] = 540;
-
-	return modes;
-
-fail:
-	if (modes) {
-		for (i = 0; i < *num_modes; i++) {
-			os_free(modes[i].channels);
-			os_free(modes[i].rates);
-		}
-		os_free(modes);
-	}
-	return NULL;
-}
-
-
-static int wpa_driver_test_set_freq(void *priv,
-				    struct hostapd_freq_params *freq)
-{
-	struct test_driver_bss *dbss = priv;
-	struct wpa_driver_test_data *drv = dbss->drv;
-	wpa_printf(MSG_DEBUG, "test: set_freq %u MHz", freq->freq);
-	drv->current_freq = freq->freq;
-	return 0;
-}
-
-
-static int wpa_driver_test_send_action(void *priv, unsigned int freq,
-				       unsigned int wait,
-				       const u8 *dst, const u8 *src,
-				       const u8 *bssid,
-				       const u8 *data, size_t data_len,
-				       int no_cck)
-{
-	struct test_driver_bss *dbss = priv;
-	struct wpa_driver_test_data *drv = dbss->drv;
-	int ret = -1;
-	u8 *buf;
-	struct ieee80211_hdr *hdr;
-
-	wpa_printf(MSG_DEBUG, "test: Send Action frame");
-
-	if ((drv->remain_on_channel_freq &&
-	     freq != drv->remain_on_channel_freq) ||
-	    (drv->remain_on_channel_freq == 0 &&
-	     freq != (unsigned int) drv->current_freq)) {
-		wpa_printf(MSG_DEBUG, "test: Reject Action frame TX on "
-			   "unexpected channel: freq=%u MHz (current_freq=%u "
-			   "MHz, remain-on-channel freq=%u MHz)",
-			   freq, drv->current_freq,
-			   drv->remain_on_channel_freq);
-		return -1;
-	}
-
-	buf = os_zalloc(24 + data_len);
-	if (buf == NULL)
-		return ret;
-	os_memcpy(buf + 24, data, data_len);
-	hdr = (struct ieee80211_hdr *) buf;
-	hdr->frame_control =
-		IEEE80211_FC(WLAN_FC_TYPE_MGMT, WLAN_FC_STYPE_ACTION);
-	os_memcpy(hdr->addr1, dst, ETH_ALEN);
-	os_memcpy(hdr->addr2, src, ETH_ALEN);
-	os_memcpy(hdr->addr3, bssid, ETH_ALEN);
-
-	ret = wpa_driver_test_send_mlme(priv, buf, 24 + data_len, 0);
-	os_free(buf);
-	return ret;
-}
-
-
-static void test_remain_on_channel_timeout(void *eloop_ctx, void *timeout_ctx)
-{
-	struct wpa_driver_test_data *drv = eloop_ctx;
-	union wpa_event_data data;
-
-	wpa_printf(MSG_DEBUG, "test: Remain-on-channel timeout");
-
-	os_memset(&data, 0, sizeof(data));
-	data.remain_on_channel.freq = drv->remain_on_channel_freq;
-	data.remain_on_channel.duration = drv->remain_on_channel_duration;
-
-	drv->remain_on_channel_freq = 0;
-
-	wpa_supplicant_event(drv->ctx, EVENT_CANCEL_REMAIN_ON_CHANNEL, &data);
-}
-
-
-static int wpa_driver_test_remain_on_channel(void *priv, unsigned int freq,
-					     unsigned int duration)
-{
-	struct test_driver_bss *dbss = priv;
-	struct wpa_driver_test_data *drv = dbss->drv;
-	union wpa_event_data data;
-
-	wpa_printf(MSG_DEBUG, "%s(freq=%u, duration=%u)",
-		   __func__, freq, duration);
-	if (drv->remain_on_channel_freq &&
-	    drv->remain_on_channel_freq != freq) {
-		wpa_printf(MSG_DEBUG, "test: Refuse concurrent "
-			   "remain_on_channel request");
-		return -1;
-	}
-
-	drv->remain_on_channel_freq = freq;
-	drv->remain_on_channel_duration = duration;
-	eloop_cancel_timeout(test_remain_on_channel_timeout, drv, NULL);
-	eloop_register_timeout(duration / 1000, (duration % 1000) * 1000,
-			       test_remain_on_channel_timeout, drv, NULL);
-
-	os_memset(&data, 0, sizeof(data));
-	data.remain_on_channel.freq = freq;
-	data.remain_on_channel.duration = duration;
-	wpa_supplicant_event(drv->ctx, EVENT_REMAIN_ON_CHANNEL, &data);
-
-	return 0;
-}
-
-
-static int wpa_driver_test_cancel_remain_on_channel(void *priv)
-{
-	struct test_driver_bss *dbss = priv;
-	struct wpa_driver_test_data *drv = dbss->drv;
-	wpa_printf(MSG_DEBUG, "%s", __func__);
-	if (!drv->remain_on_channel_freq)
-		return -1;
-	drv->remain_on_channel_freq = 0;
-	eloop_cancel_timeout(test_remain_on_channel_timeout, drv, NULL);
-	return 0;
-}
-
-
-static int wpa_driver_test_probe_req_report(void *priv, int report)
-{
-	struct test_driver_bss *dbss = priv;
-	struct wpa_driver_test_data *drv = dbss->drv;
-	wpa_printf(MSG_DEBUG, "%s(report=%d)", __func__, report);
-	drv->probe_req_report = report;
-	return 0;
-}
-
-
-const struct wpa_driver_ops wpa_driver_test_ops = {
-	"test",
-	"wpa_supplicant test driver",
-	.hapd_init = test_driver_init,
-	.hapd_deinit = wpa_driver_test_deinit,
-	.hapd_send_eapol = test_driver_send_eapol,
-	.send_mlme = wpa_driver_test_send_mlme,
-	.set_generic_elem = test_driver_set_generic_elem,
-	.sta_deauth = test_driver_sta_deauth,
-	.sta_disassoc = test_driver_sta_disassoc,
-	.get_hw_feature_data = wpa_driver_test_get_hw_feature_data,
-	.if_add = test_driver_if_add,
-	.if_remove = test_driver_if_remove,
-	.hapd_set_ssid = test_driver_set_ssid,
-	.set_privacy = test_driver_set_privacy,
-	.set_sta_vlan = test_driver_set_sta_vlan,
-	.sta_add = test_driver_sta_add,
-	.send_ether = test_driver_send_ether,
-	.set_ap_wps_ie = test_driver_set_ap_wps_ie,
-	.get_bssid = wpa_driver_test_get_bssid,
-	.get_ssid = wpa_driver_test_get_ssid,
-	.set_key = wpa_driver_test_set_key,
-	.deinit = wpa_driver_test_deinit,
-	.set_param = wpa_driver_test_set_param,
-	.deauthenticate = wpa_driver_test_deauthenticate,
-	.associate = wpa_driver_test_associate,
-	.get_capa = wpa_driver_test_get_capa,
-	.get_mac_addr = wpa_driver_test_get_mac_addr,
-	.send_eapol = wpa_driver_test_send_eapol,
-	.mlme_setprotection = wpa_driver_test_mlme_setprotection,
-	.get_scan_results2 = wpa_driver_test_get_scan_results2,
-	.global_init = wpa_driver_test_global_init,
-	.global_deinit = wpa_driver_test_global_deinit,
-	.init2 = wpa_driver_test_init2,
-	.get_interfaces = wpa_driver_test_get_interfaces,
-	.scan2 = wpa_driver_test_scan,
-	.set_freq = wpa_driver_test_set_freq,
-	.send_action = wpa_driver_test_send_action,
-	.remain_on_channel = wpa_driver_test_remain_on_channel,
-	.cancel_remain_on_channel = wpa_driver_test_cancel_remain_on_channel,
-	.probe_req_report = wpa_driver_test_probe_req_report,
-};
diff --git a/src/drivers/driver_wext.c b/src/drivers/driver_wext.c
index 1b3a757..01defdf 100644
--- a/src/drivers/driver_wext.c
+++ b/src/drivers/driver_wext.c
@@ -1,6 +1,6 @@
 /*
  * Driver interaction with generic Linux Wireless Extensions
- * Copyright (c) 2003-2010, 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.
@@ -18,6 +18,7 @@
 #include <sys/stat.h>
 #include <fcntl.h>
 #include <net/if_arp.h>
+#include <dirent.h>
 
 #include "linux_wext.h"
 #include "common.h"
@@ -78,7 +79,7 @@
 	os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
 
 	if (ioctl(drv->ioctl_sock, SIOCGIWAP, &iwr) < 0) {
-		perror("ioctl[SIOCGIWAP]");
+		wpa_printf(MSG_ERROR, "ioctl[SIOCGIWAP]: %s", strerror(errno));
 		ret = -1;
 	}
 	os_memcpy(bssid, iwr.u.ap_addr.sa_data, ETH_ALEN);
@@ -108,7 +109,7 @@
 		os_memset(iwr.u.ap_addr.sa_data, 0, ETH_ALEN);
 
 	if (ioctl(drv->ioctl_sock, SIOCSIWAP, &iwr) < 0) {
-		perror("ioctl[SIOCSIWAP]");
+		wpa_printf(MSG_ERROR, "ioctl[SIOCSIWAP]: %s", strerror(errno));
 		ret = -1;
 	}
 
@@ -131,15 +132,16 @@
 	os_memset(&iwr, 0, sizeof(iwr));
 	os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
 	iwr.u.essid.pointer = (caddr_t) ssid;
-	iwr.u.essid.length = 32;
+	iwr.u.essid.length = SSID_MAX_LEN;
 
 	if (ioctl(drv->ioctl_sock, SIOCGIWESSID, &iwr) < 0) {
-		perror("ioctl[SIOCGIWESSID]");
+		wpa_printf(MSG_ERROR, "ioctl[SIOCGIWESSID]: %s",
+			   strerror(errno));
 		ret = -1;
 	} else {
 		ret = iwr.u.essid.length;
-		if (ret > 32)
-			ret = 32;
+		if (ret > SSID_MAX_LEN)
+			ret = SSID_MAX_LEN;
 		/* Some drivers include nul termination in the SSID, so let's
 		 * remove it here before further processing. WE-21 changes this
 		 * to explicitly require the length _not_ to include nul
@@ -167,7 +169,7 @@
 	int ret = 0;
 	char buf[33];
 
-	if (ssid_len > 32)
+	if (ssid_len > SSID_MAX_LEN)
 		return -1;
 
 	os_memset(&iwr, 0, sizeof(iwr));
@@ -192,7 +194,8 @@
 	iwr.u.essid.length = ssid_len;
 
 	if (ioctl(drv->ioctl_sock, SIOCSIWESSID, &iwr) < 0) {
-		perror("ioctl[SIOCSIWESSID]");
+		wpa_printf(MSG_ERROR, "ioctl[SIOCSIWESSID]: %s",
+			   strerror(errno));
 		ret = -1;
 	}
 
@@ -218,7 +221,8 @@
 	iwr.u.freq.e = 1;
 
 	if (ioctl(drv->ioctl_sock, SIOCSIWFREQ, &iwr) < 0) {
-		perror("ioctl[SIOCSIWFREQ]");
+		wpa_printf(MSG_ERROR, "ioctl[SIOCSIWFREQ]: %s",
+			   strerror(errno));
 		ret = -1;
 	}
 
@@ -815,7 +819,8 @@
 
 	drv->ioctl_sock = socket(PF_INET, SOCK_DGRAM, 0);
 	if (drv->ioctl_sock < 0) {
-		perror("socket(PF_INET,SOCK_DGRAM)");
+		wpa_printf(MSG_ERROR, "socket(PF_INET,SOCK_DGRAM): %s",
+			   strerror(errno));
 		goto err1;
 	}
 
@@ -870,6 +875,105 @@
 }
 
 
+static int wext_hostap_ifname(struct wpa_driver_wext_data *drv,
+			      const char *ifname)
+{
+	char buf[200], *res;
+	int type;
+	FILE *f;
+
+	if (strcmp(ifname, ".") == 0 || strcmp(ifname, "..") == 0)
+		return -1;
+
+	snprintf(buf, sizeof(buf), "/sys/class/net/%s/device/net/%s/type",
+		 drv->ifname, ifname);
+
+	f = fopen(buf, "r");
+	if (!f)
+		return -1;
+	res = fgets(buf, sizeof(buf), f);
+	fclose(f);
+
+	type = res ? atoi(res) : -1;
+	wpa_printf(MSG_DEBUG, "WEXT: hostap ifname %s type %d", ifname, type);
+
+	if (type == ARPHRD_IEEE80211) {
+		wpa_printf(MSG_DEBUG,
+			   "WEXT: Found hostap driver wifi# interface (%s)",
+			   ifname);
+		wpa_driver_wext_alternative_ifindex(drv, ifname);
+		return 0;
+	}
+	return -1;
+}
+
+
+static int wext_add_hostap(struct wpa_driver_wext_data *drv)
+{
+	char buf[200];
+	int n;
+	struct dirent **names;
+	int ret = -1;
+
+	snprintf(buf, sizeof(buf), "/sys/class/net/%s/device/net", drv->ifname);
+	n = scandir(buf, &names, NULL, alphasort);
+	if (n < 0)
+		return -1;
+
+	while (n--) {
+		if (ret < 0 && wext_hostap_ifname(drv, names[n]->d_name) == 0)
+			ret = 0;
+		free(names[n]);
+	}
+	free(names);
+
+	return ret;
+}
+
+
+static void wext_check_hostap(struct wpa_driver_wext_data *drv)
+{
+	char buf[200], *pos;
+	ssize_t res;
+
+	/*
+	 * Host AP driver may use both wlan# and wifi# interface in wireless
+	 * events. Since some of the versions included WE-18 support, let's add
+	 * the alternative ifindex also from driver_wext.c for the time being.
+	 * This may be removed at some point once it is believed that old
+	 * versions of the driver are not in use anymore. However, it looks like
+	 * the wifi# interface is still used in the current kernel tree, so it
+	 * may not really be possible to remove this before the Host AP driver
+	 * gets removed from the kernel.
+	 */
+
+	/* First, try to see if driver information is available from sysfs */
+	snprintf(buf, sizeof(buf), "/sys/class/net/%s/device/driver",
+		 drv->ifname);
+	res = readlink(buf, buf, sizeof(buf) - 1);
+	if (res > 0) {
+		buf[res] = '\0';
+		pos = strrchr(buf, '/');
+		if (pos)
+			pos++;
+		else
+			pos = buf;
+		wpa_printf(MSG_DEBUG, "WEXT: Driver: %s", pos);
+		if (os_strncmp(pos, "hostap", 6) == 0 &&
+		    wext_add_hostap(drv) == 0)
+			return;
+	}
+
+	/* Second, use the old design with hardcoded ifname */
+	if (os_strncmp(drv->ifname, "wlan", 4) == 0) {
+		char ifname2[IFNAMSIZ + 1];
+		os_strlcpy(ifname2, drv->ifname, sizeof(ifname2));
+		os_memcpy(ifname2, "wifi", 4);
+		wpa_driver_wext_alternative_ifindex(drv, ifname2);
+	}
+}
+
+
 static int wpa_driver_wext_finish_drv_init(struct wpa_driver_wext_data *drv)
 {
 	int send_rfkill_event = 0;
@@ -910,20 +1014,7 @@
 
 	drv->ifindex = if_nametoindex(drv->ifname);
 
-	if (os_strncmp(drv->ifname, "wlan", 4) == 0) {
-		/*
-		 * Host AP driver may use both wlan# and wifi# interface in
-		 * wireless events. Since some of the versions included WE-18
-		 * support, let's add the alternative ifindex also from
-		 * driver_wext.c for the time being. This may be removed at
-		 * some point once it is believed that old versions of the
-		 * driver are not in use anymore.
-		 */
-		char ifname2[IFNAMSIZ + 1];
-		os_strlcpy(ifname2, drv->ifname, sizeof(ifname2));
-		os_memcpy(ifname2, "wifi", 4);
-		wpa_driver_wext_alternative_ifindex(drv, ifname2);
-	}
+	wext_check_hostap(drv);
 
 	netlink_send_oper_ifla(drv->netlink, drv->ifindex,
 			       1, IF_OPER_DORMANT);
@@ -1027,7 +1118,8 @@
 	}
 
 	if (ioctl(drv->ioctl_sock, SIOCSIWSCAN, &iwr) < 0) {
-		perror("ioctl[SIOCSIWSCAN]");
+		wpa_printf(MSG_ERROR, "ioctl[SIOCSIWSCAN]: %s",
+			   strerror(errno));
 		ret = -1;
 	}
 
@@ -1082,7 +1174,8 @@
 				   "trying larger buffer (%lu bytes)",
 				   (unsigned long) res_buf_len);
 		} else {
-			perror("ioctl[SIOCGIWSCAN]");
+			wpa_printf(MSG_ERROR, "ioctl[SIOCGIWSCAN]: %s",
+				   strerror(errno));
 			os_free(res_buf);
 			return NULL;
 		}
@@ -1106,7 +1199,7 @@
 	struct wpa_scan_res res;
 	u8 *ie;
 	size_t ie_len;
-	u8 ssid[32];
+	u8 ssid[SSID_MAX_LEN];
 	size_t ssid_len;
 	int maxrate;
 };
@@ -1533,7 +1626,8 @@
 		sizeof(range->enc_capa);
 
 	if (ioctl(drv->ioctl_sock, SIOCGIWRANGE, &iwr) < 0) {
-		perror("ioctl[SIOCGIWRANGE]");
+		wpa_printf(MSG_ERROR, "ioctl[SIOCGIWRANGE]: %s",
+			   strerror(errno));
 		os_free(range);
 		return -1;
 	} else if (iwr.u.data.length >= minlen &&
@@ -1568,8 +1662,9 @@
 		drv->capa.max_scan_ssids = 1;
 
 		wpa_printf(MSG_DEBUG, "  capabilities: key_mgmt 0x%x enc 0x%x "
-			   "flags 0x%x",
-			   drv->capa.key_mgmt, drv->capa.enc, drv->capa.flags);
+			   "flags 0x%llx",
+			   drv->capa.key_mgmt, drv->capa.enc,
+			   (unsigned long long) drv->capa.flags);
 	} else {
 		wpa_printf(MSG_DEBUG, "SIOCGIWRANGE: too old (short) data - "
 			   "assuming WPA is not supported");
@@ -1612,7 +1707,8 @@
 
 	ret = ioctl(drv->ioctl_sock, SIOCSIWENCODEEXT, &iwr);
 	if (ret < 0)
-		perror("ioctl[SIOCSIWENCODEEXT] PMK");
+		wpa_printf(MSG_ERROR, "ioctl[SIOCSIWENCODEEXT] PMK: %s",
+			   strerror(errno));
 	os_free(ext);
 
 	return ret;
@@ -1704,7 +1800,8 @@
 			ret = -2;
 		}
 
-		perror("ioctl[SIOCSIWENCODEEXT]");
+		wpa_printf(MSG_ERROR, "ioctl[SIOCSIWENCODEEXT]: %s",
+			   strerror(errno));
 	}
 
 	os_free(ext);
@@ -1778,7 +1875,8 @@
 	iwr.u.encoding.length = key_len;
 
 	if (ioctl(drv->ioctl_sock, SIOCSIWENCODE, &iwr) < 0) {
-		perror("ioctl[SIOCSIWENCODE]");
+		wpa_printf(MSG_ERROR, "ioctl[SIOCSIWENCODE]: %s",
+			   strerror(errno));
 		ret = -1;
 	}
 
@@ -1790,7 +1888,9 @@
 		iwr.u.encoding.pointer = (caddr_t) NULL;
 		iwr.u.encoding.length = 0;
 		if (ioctl(drv->ioctl_sock, SIOCSIWENCODE, &iwr) < 0) {
-			perror("ioctl[SIOCSIWENCODE] (set_tx)");
+			wpa_printf(MSG_ERROR,
+				   "ioctl[SIOCSIWENCODE] (set_tx): %s",
+				   strerror(errno));
 			ret = -1;
 		}
 	}
@@ -1839,7 +1939,8 @@
 	iwr.u.data.length = sizeof(mlme);
 
 	if (ioctl(drv->ioctl_sock, SIOCSIWMLME, &iwr) < 0) {
-		perror("ioctl[SIOCSIWMLME]");
+		wpa_printf(MSG_ERROR, "ioctl[SIOCSIWMLME]: %s",
+			   strerror(errno));
 		ret = -1;
 	}
 
@@ -1851,7 +1952,7 @@
 {
 	struct iwreq iwr;
 	const u8 null_bssid[ETH_ALEN] = { 0, 0, 0, 0, 0, 0 };
-	u8 ssid[32];
+	u8 ssid[SSID_MAX_LEN];
 	int i;
 
 	/*
@@ -1862,7 +1963,8 @@
 	os_memset(&iwr, 0, sizeof(iwr));
 	os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
 	if (ioctl(drv->ioctl_sock, SIOCGIWMODE, &iwr) < 0) {
-		perror("ioctl[SIOCGIWMODE]");
+		wpa_printf(MSG_ERROR, "ioctl[SIOCGIWMODE]: %s",
+			   strerror(errno));
 		iwr.u.mode = IW_MODE_INFRA;
 	}
 
@@ -1892,9 +1994,9 @@
 		 * SIOCSIWMLME commands (or tries to associate automatically
 		 * after deauth/disassoc).
 		 */
-		for (i = 0; i < 32; i++)
+		for (i = 0; i < SSID_MAX_LEN; i++)
 			ssid[i] = rand() & 0xFF;
-		if (wpa_driver_wext_set_ssid(drv, ssid, 32) < 0) {
+		if (wpa_driver_wext_set_ssid(drv, ssid, SSID_MAX_LEN) < 0) {
 			wpa_printf(MSG_DEBUG, "WEXT: Failed to set bogus "
 				   "SSID to disconnect");
 		}
@@ -1927,7 +2029,8 @@
 	iwr.u.data.length = ie_len;
 
 	if (ioctl(drv->ioctl_sock, SIOCSIWGENIE, &iwr) < 0) {
-		perror("ioctl[SIOCSIWGENIE]");
+		wpa_printf(MSG_ERROR, "ioctl[SIOCSIWGENIE]: %s",
+			   strerror(errno));
 		ret = -1;
 	}
 
@@ -2004,7 +2107,8 @@
 	}
 
 	if (ioctl(drv->ioctl_sock, SIOCSIWENCODE, &iwr) < 0) {
-		perror("ioctl[SIOCSIWENCODE]");
+		wpa_printf(MSG_ERROR, "ioctl[SIOCSIWENCODE]: %s",
+			   strerror(errno));
 		ret = -1;
 	}
 
@@ -2060,12 +2164,12 @@
 	if (wpa_driver_wext_set_gen_ie(drv, params->wpa_ie, params->wpa_ie_len)
 	    < 0)
 		ret = -1;
-	if (params->wpa_ie == NULL || params->wpa_ie_len == 0)
-		value = IW_AUTH_WPA_VERSION_DISABLED;
-	else if (params->wpa_ie[0] == WLAN_EID_RSN)
+	if (params->wpa_proto & WPA_PROTO_RSN)
 		value = IW_AUTH_WPA_VERSION_WPA2;
-	else
+	else if (params->wpa_proto & WPA_PROTO_WPA)
 		value = IW_AUTH_WPA_VERSION_WPA;
+	else
+		value = IW_AUTH_WPA_VERSION_DISABLED;
 	if (wpa_driver_wext_set_auth_param(drv,
 					   IW_AUTH_WPA_VERSION, value) < 0)
 		ret = -1;
@@ -2084,7 +2188,7 @@
 	value = params->key_mgmt_suite != WPA_KEY_MGMT_NONE ||
 		params->pairwise_suite != WPA_CIPHER_NONE ||
 		params->group_suite != WPA_CIPHER_NONE ||
-		params->wpa_ie_len;
+		(params->wpa_proto & (WPA_PROTO_RSN | WPA_PROTO_WPA));
 	if (wpa_driver_wext_set_auth_param(drv,
 					   IW_AUTH_PRIVACY_INVOKED, value) < 0)
 		ret = -1;
@@ -2181,7 +2285,8 @@
 	}
 
 	if (errno != EBUSY) {
-		perror("ioctl[SIOCSIWMODE]");
+		wpa_printf(MSG_ERROR, "ioctl[SIOCSIWMODE]: %s",
+			   strerror(errno));
 		goto done;
 	}
 
@@ -2190,7 +2295,8 @@
 	 * down, try to set the mode again, and bring it back up.
 	 */
 	if (ioctl(drv->ioctl_sock, SIOCGIWMODE, &iwr) < 0) {
-		perror("ioctl[SIOCGIWMODE]");
+		wpa_printf(MSG_ERROR, "ioctl[SIOCGIWMODE]: %s",
+			   strerror(errno));
 		goto done;
 	}
 
@@ -2203,7 +2309,8 @@
 		/* Try to set the mode again while the interface is down */
 		iwr.u.mode = new_mode;
 		if (ioctl(drv->ioctl_sock, SIOCSIWMODE, &iwr) < 0)
-			perror("ioctl[SIOCSIWMODE]");
+			wpa_printf(MSG_ERROR, "ioctl[SIOCSIWMODE]: %s",
+				   strerror(errno));
 		else
 			ret = 0;
 
@@ -2236,7 +2343,8 @@
 
 	if (ioctl(drv->ioctl_sock, SIOCSIWPMKSA, &iwr) < 0) {
 		if (errno != EOPNOTSUPP)
-			perror("ioctl[SIOCSIWPMKSA]");
+			wpa_printf(MSG_ERROR, "ioctl[SIOCSIWPMKSA]: %s",
+				   strerror(errno));
 		ret = -1;
 	}
 
@@ -2352,6 +2460,33 @@
 }
 
 
+static int wpa_driver_wext_status(void *priv, char *buf, size_t buflen)
+{
+	struct wpa_driver_wext_data *drv = priv;
+	int res;
+	char *pos, *end;
+	unsigned char addr[ETH_ALEN];
+
+	pos = buf;
+	end = buf + buflen;
+
+	if (linux_get_ifhwaddr(drv->ioctl_sock, drv->ifname, addr))
+		return -1;
+
+	res = os_snprintf(pos, end - pos,
+			  "ifindex=%d\n"
+			  "ifname=%s\n"
+			  "addr=" MACSTR "\n",
+			  drv->ifindex,
+			  drv->ifname,
+			  MAC2STR(addr));
+	if (os_snprintf_error(end - pos, res))
+		return pos - buf;
+	pos += res;
+
+	return pos - buf;
+}
+
 const struct wpa_driver_ops wpa_driver_wext_ops = {
 	.name = "wext",
 	.desc = "Linux wireless extensions (generic)",
@@ -2372,4 +2507,5 @@
 	.set_operstate = wpa_driver_wext_set_operstate,
 	.get_radio_name = wext_get_radio_name,
 	.signal_poll = wpa_driver_wext_signal_poll,
+	.status = wpa_driver_wext_status,
 };
diff --git a/src/drivers/driver_wired.c b/src/drivers/driver_wired.c
index 21f5e42..f95f3cc 100644
--- a/src/drivers/driver_wired.c
+++ b/src/drivers/driver_wired.c
@@ -100,7 +100,7 @@
 	if (setsockopt(sock, SOL_PACKET,
 		       add ? PACKET_ADD_MEMBERSHIP : PACKET_DROP_MEMBERSHIP,
 		       &mreq, sizeof(mreq)) < 0) {
-		perror("setsockopt");
+		wpa_printf(MSG_ERROR, "setsockopt: %s", strerror(errno));
 		return -1;
 	}
 	return 0;
@@ -158,7 +158,7 @@
 
 	len = recv(sock, buf, sizeof(buf), 0);
 	if (len < 0) {
-		perror("recv");
+		wpa_printf(MSG_ERROR, "recv: %s", strerror(errno));
 		return;
 	}
 
@@ -176,7 +176,7 @@
 
 	len = recv(sock, buf, sizeof(buf), 0);
 	if (len < 0) {
-		perror("recv");
+		wpa_printf(MSG_ERROR, "recv: %s", strerror(errno));
 		return;
 	}
 
@@ -209,19 +209,21 @@
 
 	drv->sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_PAE));
 	if (drv->sock < 0) {
-		perror("socket[PF_PACKET,SOCK_RAW]");
+		wpa_printf(MSG_ERROR, "socket[PF_PACKET,SOCK_RAW]: %s",
+			   strerror(errno));
 		return -1;
 	}
 
 	if (eloop_register_read_sock(drv->sock, handle_read, drv->ctx, NULL)) {
-		printf("Could not register read socket\n");
+		wpa_printf(MSG_INFO, "Could not register read socket");
 		return -1;
 	}
 
 	os_memset(&ifr, 0, sizeof(ifr));
 	os_strlcpy(ifr.ifr_name, drv->ifname, sizeof(ifr.ifr_name));
 	if (ioctl(drv->sock, SIOCGIFINDEX, &ifr) != 0) {
-		perror("ioctl(SIOCGIFINDEX)");
+		wpa_printf(MSG_ERROR, "ioctl(SIOCGIFINDEX): %s",
+			   strerror(errno));
 		return -1;
 	}
 
@@ -232,7 +234,7 @@
 		   addr.sll_ifindex);
 
 	if (bind(drv->sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
-		perror("bind");
+		wpa_printf(MSG_ERROR, "bind: %s", strerror(errno));
 		return -1;
 	}
 
@@ -247,26 +249,28 @@
 	os_memset(&ifr, 0, sizeof(ifr));
 	os_strlcpy(ifr.ifr_name, drv->ifname, sizeof(ifr.ifr_name));
 	if (ioctl(drv->sock, SIOCGIFHWADDR, &ifr) != 0) {
-		perror("ioctl(SIOCGIFHWADDR)");
+		wpa_printf(MSG_ERROR, "ioctl(SIOCGIFHWADDR): %s",
+			   strerror(errno));
 		return -1;
 	}
 
 	if (ifr.ifr_hwaddr.sa_family != ARPHRD_ETHER) {
-		printf("Invalid HW-addr family 0x%04x\n",
-		       ifr.ifr_hwaddr.sa_family);
+		wpa_printf(MSG_INFO, "Invalid HW-addr family 0x%04x",
+			   ifr.ifr_hwaddr.sa_family);
 		return -1;
 	}
 	os_memcpy(own_addr, ifr.ifr_hwaddr.sa_data, ETH_ALEN);
 
 	/* setup dhcp listen socket for sta detection */
 	if ((drv->dhcp_sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) {
-		perror("socket call failed for dhcp");
+		wpa_printf(MSG_ERROR, "socket call failed for dhcp: %s",
+			   strerror(errno));
 		return -1;
 	}
 
 	if (eloop_register_read_sock(drv->dhcp_sock, handle_dhcp, drv->ctx,
 				     NULL)) {
-		printf("Could not register read socket\n");
+		wpa_printf(MSG_INFO, "Could not register read socket");
 		return -1;
 	}
 
@@ -277,12 +281,14 @@
 
 	if (setsockopt(drv->dhcp_sock, SOL_SOCKET, SO_REUSEADDR, (char *) &n,
 		       sizeof(n)) == -1) {
-		perror("setsockopt[SOL_SOCKET,SO_REUSEADDR]");
+		wpa_printf(MSG_ERROR, "setsockopt[SOL_SOCKET,SO_REUSEADDR]: %s",
+			   strerror(errno));
 		return -1;
 	}
 	if (setsockopt(drv->dhcp_sock, SOL_SOCKET, SO_BROADCAST, (char *) &n,
 		       sizeof(n)) == -1) {
-		perror("setsockopt[SOL_SOCKET,SO_BROADCAST]");
+		wpa_printf(MSG_ERROR, "setsockopt[SOL_SOCKET,SO_BROADCAST]: %s",
+			   strerror(errno));
 		return -1;
 	}
 
@@ -290,13 +296,15 @@
 	os_strlcpy(ifr.ifr_ifrn.ifrn_name, drv->ifname, IFNAMSIZ);
 	if (setsockopt(drv->dhcp_sock, SOL_SOCKET, SO_BINDTODEVICE,
 		       (char *) &ifr, sizeof(ifr)) < 0) {
-		perror("setsockopt[SOL_SOCKET,SO_BINDTODEVICE]");
+		wpa_printf(MSG_ERROR,
+			   "setsockopt[SOL_SOCKET,SO_BINDTODEVICE]: %s",
+			   strerror(errno));
 		return -1;
 	}
 
 	if (bind(drv->dhcp_sock, (struct sockaddr *) &addr2,
 		 sizeof(struct sockaddr)) == -1) {
-		perror("bind");
+		wpa_printf(MSG_ERROR, "bind: %s", strerror(errno));
 		return -1;
 	}
 
@@ -320,8 +328,9 @@
 	len = sizeof(*hdr) + data_len;
 	hdr = os_zalloc(len);
 	if (hdr == NULL) {
-		printf("malloc() failed for wired_send_eapol(len=%lu)\n",
-		       (unsigned long) len);
+		wpa_printf(MSG_INFO,
+			   "malloc() failed for wired_send_eapol(len=%lu)",
+			   (unsigned long) len);
 		return -1;
 	}
 
@@ -337,9 +346,9 @@
 	os_free(hdr);
 
 	if (res < 0) {
-		perror("wired_send_eapol: send");
-		printf("wired_send_eapol - packet len: %lu - failed\n",
-		       (unsigned long) len);
+		wpa_printf(MSG_ERROR,
+			   "wired_send_eapol - packet len: %lu - failed: send: %s",
+			   (unsigned long) len, strerror(errno));
 	}
 
 	return res;
@@ -353,7 +362,8 @@
 
 	drv = os_zalloc(sizeof(struct wpa_driver_wired_data));
 	if (drv == NULL) {
-		printf("Could not allocate memory for wired driver data\n");
+		wpa_printf(MSG_INFO,
+			   "Could not allocate memory for wired driver data");
 		return NULL;
 	}
 
@@ -374,11 +384,15 @@
 {
 	struct wpa_driver_wired_data *drv = priv;
 
-	if (drv->sock >= 0)
+	if (drv->sock >= 0) {
+		eloop_unregister_read_sock(drv->sock);
 		close(drv->sock);
+	}
 
-	if (drv->dhcp_sock >= 0)
+	if (drv->dhcp_sock >= 0) {
+		eloop_unregister_read_sock(drv->dhcp_sock);
 		close(drv->dhcp_sock);
+	}
 
 	os_free(drv);
 }
@@ -414,14 +428,15 @@
 
 	s = socket(PF_INET, SOCK_DGRAM, 0);
 	if (s < 0) {
-		perror("socket");
+		wpa_printf(MSG_ERROR, "socket: %s", strerror(errno));
 		return -1;
 	}
 
 	os_memset(&ifr, 0, sizeof(ifr));
 	os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ);
 	if (ioctl(s, SIOCGIFFLAGS, (caddr_t) &ifr) < 0) {
-		perror("ioctl[SIOCGIFFLAGS]");
+		wpa_printf(MSG_ERROR, "ioctl[SIOCGIFFLAGS]: %s",
+			   strerror(errno));
 		close(s);
 		return -1;
 	}
@@ -438,7 +453,7 @@
 
 	s = socket(PF_INET, SOCK_DGRAM, 0);
 	if (s < 0) {
-		perror("socket");
+		wpa_printf(MSG_ERROR, "socket: %s", strerror(errno));
 		return -1;
 	}
 
@@ -446,7 +461,8 @@
 	os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ);
 	ifr.ifr_flags = flags & 0xffff;
 	if (ioctl(s, SIOCSIFFLAGS, (caddr_t) &ifr) < 0) {
-		perror("ioctl[SIOCSIFFLAGS]");
+		wpa_printf(MSG_ERROR, "ioctl[SIOCSIFFLAGS]: %s",
+			   strerror(errno));
 		close(s);
 		return -1;
 	}
@@ -463,14 +479,15 @@
 
 	s = socket(PF_INET, SOCK_DGRAM, 0);
 	if (s < 0) {
-		perror("socket");
+		wpa_printf(MSG_ERROR, "socket: %s", strerror(errno));
 		return -1;
 	}
 
 	os_memset(&ifmr, 0, sizeof(ifmr));
 	os_strlcpy(ifmr.ifm_name, ifname, IFNAMSIZ);
 	if (ioctl(s, SIOCGIFMEDIA, (caddr_t) &ifmr) < 0) {
-		perror("ioctl[SIOCGIFMEDIA]");
+		wpa_printf(MSG_ERROR, "ioctl[SIOCGIFMEDIA]: %s",
+			   strerror(errno));
 		close(s);
 		return -1;
 	}
@@ -494,7 +511,7 @@
 
 	s = socket(PF_INET, SOCK_DGRAM, 0);
 	if (s < 0) {
-		perror("socket");
+		wpa_printf(MSG_ERROR, "socket: %s", strerror(errno));
 		return -1;
 	}
 
@@ -528,7 +545,8 @@
 #endif /* defined(__NetBSD__) || defined(__OpenBSD__) || defined(__APPLE__) */
 
 	if (ioctl(s, add ? SIOCADDMULTI : SIOCDELMULTI, (caddr_t) &ifr) < 0) {
-		perror("ioctl[SIOC{ADD/DEL}MULTI]");
+		wpa_printf(MSG_ERROR, "ioctl[SIOC{ADD/DEL}MULTI]: %s",
+			   strerror(errno));
 		close(s);
 		return -1;
 	}
@@ -551,7 +569,7 @@
 #ifdef __linux__
 	drv->pf_sock = socket(PF_PACKET, SOCK_DGRAM, 0);
 	if (drv->pf_sock < 0)
-		perror("socket(PF_PACKET)");
+		wpa_printf(MSG_ERROR, "socket(PF_PACKET): %s", strerror(errno));
 #else /* __linux__ */
 	drv->pf_sock = -1;
 #endif /* __linux__ */
diff --git a/src/drivers/drivers.c b/src/drivers/drivers.c
index d0e42ec..a98af9a 100644
--- a/src/drivers/drivers.c
+++ b/src/drivers/drivers.c
@@ -19,9 +19,6 @@
 #ifdef CONFIG_DRIVER_HOSTAP
 extern struct wpa_driver_ops wpa_driver_hostap_ops; /* driver_hostap.c */
 #endif /* CONFIG_DRIVER_HOSTAP */
-#ifdef CONFIG_DRIVER_MADWIFI
-extern struct wpa_driver_ops wpa_driver_madwifi_ops; /* driver_madwifi.c */
-#endif /* CONFIG_DRIVER_MADWIFI */
 #ifdef CONFIG_DRIVER_BSD
 extern struct wpa_driver_ops wpa_driver_bsd_ops; /* driver_bsd.c */
 #endif /* CONFIG_DRIVER_BSD */
@@ -38,9 +35,6 @@
  /* driver_macsec_qca.c */
 extern struct wpa_driver_ops wpa_driver_macsec_qca_ops;
 #endif /* CONFIG_DRIVER_MACSEC_QCA */
-#ifdef CONFIG_DRIVER_TEST
-extern struct wpa_driver_ops wpa_driver_test_ops; /* driver_test.c */
-#endif /* CONFIG_DRIVER_TEST */
 #ifdef CONFIG_DRIVER_ROBOSWITCH
 /* driver_roboswitch.c */
 extern struct wpa_driver_ops wpa_driver_roboswitch_ops;
@@ -53,7 +47,7 @@
 #endif /* CONFIG_DRIVER_NONE */
 
 
-struct wpa_driver_ops *wpa_drivers[] =
+const struct wpa_driver_ops *const wpa_drivers[] =
 {
 #ifdef CONFIG_DRIVER_NL80211
 	&wpa_driver_nl80211_ops,
@@ -64,9 +58,6 @@
 #ifdef CONFIG_DRIVER_HOSTAP
 	&wpa_driver_hostap_ops,
 #endif /* CONFIG_DRIVER_HOSTAP */
-#ifdef CONFIG_DRIVER_MADWIFI
-	&wpa_driver_madwifi_ops,
-#endif /* CONFIG_DRIVER_MADWIFI */
 #ifdef CONFIG_DRIVER_BSD
 	&wpa_driver_bsd_ops,
 #endif /* CONFIG_DRIVER_BSD */
@@ -82,9 +73,6 @@
 #ifdef CONFIG_DRIVER_MACSEC_QCA
 	&wpa_driver_macsec_qca_ops,
 #endif /* CONFIG_DRIVER_MACSEC_QCA */
-#ifdef CONFIG_DRIVER_TEST
-	&wpa_driver_test_ops,
-#endif /* CONFIG_DRIVER_TEST */
 #ifdef CONFIG_DRIVER_ROBOSWITCH
 	&wpa_driver_roboswitch_ops,
 #endif /* CONFIG_DRIVER_ROBOSWITCH */
diff --git a/src/drivers/drivers.mak b/src/drivers/drivers.mak
index cdb913e..9434078 100644
--- a/src/drivers/drivers.mak
+++ b/src/drivers/drivers.mak
@@ -25,6 +25,10 @@
 ifdef CONFIG_DRIVER_NL80211
 DRV_CFLAGS += -DCONFIG_DRIVER_NL80211
 DRV_OBJS += ../src/drivers/driver_nl80211.o
+DRV_OBJS += ../src/drivers/driver_nl80211_capa.o
+DRV_OBJS += ../src/drivers/driver_nl80211_event.o
+DRV_OBJS += ../src/drivers/driver_nl80211_monitor.o
+DRV_OBJS += ../src/drivers/driver_nl80211_scan.o
 DRV_OBJS += ../src/utils/radiotap.o
 NEED_SME=y
 NEED_AP_MLME=y
@@ -35,7 +39,13 @@
 ifdef CONFIG_LIBNL32
   DRV_LIBS += -lnl-3
   DRV_LIBS += -lnl-genl-3
-  DRV_CFLAGS += -DCONFIG_LIBNL20 -I/usr/include/libnl3
+  DRV_CFLAGS += -DCONFIG_LIBNL20
+  ifdef LIBNL_INC
+    DRV_CFLAGS += -I$(LIBNL_INC)
+  else
+    PKG_CONFIG ?= pkg-config
+    DRV_CFLAGS += $(shell $(PKG_CONFIG) --cflags libnl-3.0)
+  endif
 ifdef CONFIG_LIBNL3_ROUTE
   DRV_LIBS += -lnl-route-3
   DRV_CFLAGS += -DCONFIG_LIBNL3_ROUTE
@@ -72,12 +82,6 @@
 DRV_OBJS += ../src/drivers/driver_openbsd.o
 endif
 
-ifdef CONFIG_DRIVER_TEST
-DRV_CFLAGS += -DCONFIG_DRIVER_TEST
-DRV_OBJS += ../src/drivers/driver_test.o
-NEED_AP_MLME=y
-endif
-
 ifdef CONFIG_DRIVER_NONE
 DRV_CFLAGS += -DCONFIG_DRIVER_NONE
 DRV_OBJS += ../src/drivers/driver_none.o
@@ -94,15 +98,6 @@
 NEED_LINUX_IOCTL=y
 endif
 
-ifdef CONFIG_DRIVER_MADWIFI
-DRV_AP_CFLAGS += -DCONFIG_DRIVER_MADWIFI
-DRV_AP_OBJS += ../src/drivers/driver_madwifi.o
-CONFIG_WIRELESS_EXTENSION=y
-CONFIG_L2_PACKET=linux
-NEED_NETLINK=y
-NEED_LINUX_IOCTL=y
-endif
-
 ifdef CONFIG_DRIVER_ATHEROS
 DRV_AP_CFLAGS += -DCONFIG_DRIVER_ATHEROS
 DRV_AP_OBJS += ../src/drivers/driver_atheros.o
diff --git a/src/drivers/drivers.mk b/src/drivers/drivers.mk
index 9fa70d9..8da4c53 100644
--- a/src/drivers/drivers.mk
+++ b/src/drivers/drivers.mk
@@ -20,6 +20,11 @@
 ifdef CONFIG_DRIVER_NL80211
 DRV_CFLAGS += -DCONFIG_DRIVER_NL80211
 DRV_OBJS += src/drivers/driver_nl80211.c
+DRV_OBJS += src/drivers/driver_nl80211_android.c
+DRV_OBJS += src/drivers/driver_nl80211_capa.c
+DRV_OBJS += src/drivers/driver_nl80211_event.c
+DRV_OBJS += src/drivers/driver_nl80211_monitor.c
+DRV_OBJS += src/drivers/driver_nl80211_scan.c
 DRV_OBJS += src/utils/radiotap.c
 NEED_SME=y
 NEED_AP_MLME=y
@@ -67,12 +72,6 @@
 DRV_OBJS += src/drivers/driver_openbsd.c
 endif
 
-ifdef CONFIG_DRIVER_TEST
-DRV_CFLAGS += -DCONFIG_DRIVER_TEST
-DRV_OBJS += src/drivers/driver_test.c
-NEED_AP_MLME=y
-endif
-
 ifdef CONFIG_DRIVER_NONE
 DRV_CFLAGS += -DCONFIG_DRIVER_NONE
 DRV_OBJS += src/drivers/driver_none.c
@@ -89,15 +88,6 @@
 NEED_LINUX_IOCTL=y
 endif
 
-ifdef CONFIG_DRIVER_MADWIFI
-DRV_AP_CFLAGS += -DCONFIG_DRIVER_MADWIFI
-DRV_AP_OBJS += src/drivers/driver_madwifi.c
-CONFIG_WIRELESS_EXTENSION=y
-CONFIG_L2_PACKET=linux
-NEED_NETLINK=y
-NEED_LINUX_IOCTL=y
-endif
-
 ifdef CONFIG_DRIVER_ATHEROS
 DRV_AP_CFLAGS += -DCONFIG_DRIVER_ATHEROS
 DRV_AP_OBJS += src/drivers/driver_atheros.c
diff --git a/src/drivers/linux_defines.h b/src/drivers/linux_defines.h
new file mode 100644
index 0000000..a107479
--- /dev/null
+++ b/src/drivers/linux_defines.h
@@ -0,0 +1,46 @@
+/*
+ * Linux defines for values that are not yet included in common C libraries
+ * Copyright (c) 2014, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef LINUX_DEFINES_H
+#define LINUX_DEFINES_H
+
+#ifndef SO_WIFI_STATUS
+# if defined(__sparc__)
+#  define SO_WIFI_STATUS	0x0025
+# elif defined(__parisc__)
+#  define SO_WIFI_STATUS	0x4022
+# else
+#  define SO_WIFI_STATUS	41
+# endif
+
+# define SCM_WIFI_STATUS	SO_WIFI_STATUS
+#endif
+
+#ifndef SO_EE_ORIGIN_TXSTATUS
+#define SO_EE_ORIGIN_TXSTATUS	4
+#endif
+
+#ifndef PACKET_TX_TIMESTAMP
+#define PACKET_TX_TIMESTAMP	16
+#endif
+
+#ifndef IFF_LOWER_UP
+#define IFF_LOWER_UP   0x10000         /* driver signals L1 up         */
+#endif
+#ifndef IFF_DORMANT
+#define IFF_DORMANT    0x20000         /* driver signals dormant       */
+#endif
+
+#ifndef IF_OPER_DORMANT
+#define IF_OPER_DORMANT 5
+#endif
+#ifndef IF_OPER_UP
+#define IF_OPER_UP 6
+#endif
+
+#endif /* LINUX_DEFINES_H */
diff --git a/src/drivers/linux_wext.h b/src/drivers/linux_wext.h
index 55cf955..e7c7001 100644
--- a/src/drivers/linux_wext.h
+++ b/src/drivers/linux_wext.h
@@ -19,13 +19,13 @@
 #define _LINUX_SOCKET_H
 #define _LINUX_IF_H
 
-#include <sys/types.h>
+#include <stdint.h>
 #include <net/if.h>
-typedef __uint32_t __u32;
-typedef __int32_t __s32;
-typedef __uint16_t __u16;
-typedef __int16_t __s16;
-typedef __uint8_t __u8;
+typedef uint32_t __u32;
+typedef int32_t __s32;
+typedef uint16_t __u16;
+typedef int16_t __s16;
+typedef uint8_t __u8;
 #ifndef __user
 #define __user
 #endif /* __user */
diff --git a/src/drivers/netlink.c b/src/drivers/netlink.c
index 2fa20b1..0e960f4 100644
--- a/src/drivers/netlink.c
+++ b/src/drivers/netlink.c
@@ -199,8 +199,7 @@
 		rta->rta_type = IFLA_LINKMODE;
 		rta->rta_len = RTA_LENGTH(sizeof(char));
 		*((char *) RTA_DATA(rta)) = linkmode;
-		req.hdr.nlmsg_len = NLMSG_ALIGN(req.hdr.nlmsg_len) +
-			RTA_LENGTH(sizeof(char));
+		req.hdr.nlmsg_len += RTA_SPACE(sizeof(char));
 	}
 	if (operstate != -1) {
 		rta = aliasing_hide_typecast(
@@ -209,8 +208,7 @@
 		rta->rta_type = IFLA_OPERSTATE;
 		rta->rta_len = RTA_LENGTH(sizeof(char));
 		*((char *) RTA_DATA(rta)) = operstate;
-		req.hdr.nlmsg_len = NLMSG_ALIGN(req.hdr.nlmsg_len) +
-			RTA_LENGTH(sizeof(char));
+		req.hdr.nlmsg_len += RTA_SPACE(sizeof(char));
 	}
 
 	wpa_printf(MSG_DEBUG, "netlink: Operstate: ifindex=%d linkmode=%d (%s), operstate=%d (%s)",
diff --git a/src/drivers/nl80211_copy.h b/src/drivers/nl80211_copy.h
index 4b28dc0..ae16ba9 100644
--- a/src/drivers/nl80211_copy.h
+++ b/src/drivers/nl80211_copy.h
@@ -25,10 +25,30 @@
  *
  */
 
+/*
+ * This header file defines the userspace API to the wireless stack. Please
+ * be careful not to break things - i.e. don't move anything around or so
+ * unless you can demonstrate that it breaks neither API nor ABI.
+ *
+ * Additions to the API should be accompanied by actual implementations in
+ * an upstream driver, so that example implementations exist in case there
+ * are ever concerns about the precise semantics of the API or changes are
+ * needed, and to ensure that code for dead (no longer implemented) API
+ * can actually be identified and removed.
+ * Nonetheless, semantics should also be documented carefully in this file.
+ */
+
 #include <linux/types.h>
 
 #define NL80211_GENL_NAME "nl80211"
 
+#define NL80211_MULTICAST_GROUP_CONFIG		"config"
+#define NL80211_MULTICAST_GROUP_SCAN		"scan"
+#define NL80211_MULTICAST_GROUP_REG		"regulatory"
+#define NL80211_MULTICAST_GROUP_MLME		"mlme"
+#define NL80211_MULTICAST_GROUP_VENDOR		"vendor"
+#define NL80211_MULTICAST_GROUP_TESTMODE	"testmode"
+
 /**
  * DOC: Station handling
  *
@@ -173,8 +193,8 @@
  *	%NL80211_ATTR_WIPHY and %NL80211_ATTR_WIPHY_NAME.
  *
  * @NL80211_CMD_GET_INTERFACE: Request an interface's configuration;
- *	either a dump request on a %NL80211_ATTR_WIPHY or a specific get
- *	on an %NL80211_ATTR_IFINDEX is supported.
+ *	either a dump request for all interfaces or a specific get with a
+ *	single %NL80211_ATTR_IFINDEX is supported.
  * @NL80211_CMD_SET_INTERFACE: Set type of a virtual interface, requires
  *	%NL80211_ATTR_IFINDEX and %NL80211_ATTR_IFTYPE.
  * @NL80211_CMD_NEW_INTERFACE: Newly created virtual interface or response
@@ -227,7 +247,11 @@
  *	the interface identified by %NL80211_ATTR_IFINDEX.
  * @NL80211_CMD_DEL_STATION: Remove a station identified by %NL80211_ATTR_MAC
  *	or, if no MAC address given, all stations, on the interface identified
- *	by %NL80211_ATTR_IFINDEX.
+ *	by %NL80211_ATTR_IFINDEX. %NL80211_ATTR_MGMT_SUBTYPE and
+ *	%NL80211_ATTR_REASON_CODE can optionally be used to specify which type
+ *	of disconnection indication should be sent to the station
+ *	(Deauthentication or Disassociation frame and reason code for that
+ *	frame).
  *
  * @NL80211_CMD_GET_MPATH: Get mesh path attributes for mesh path to
  * 	destination %NL80211_ATTR_MAC on the interface identified by
@@ -248,7 +272,18 @@
  *	%NL80211_ATTR_IFINDEX.
  *
  * @NL80211_CMD_GET_REG: ask the wireless core to send us its currently set
- * 	regulatory domain.
+ *	regulatory domain. If %NL80211_ATTR_WIPHY is specified and the device
+ *	has a private regulatory domain, it will be returned. Otherwise, the
+ *	global regdomain will be returned.
+ *	A device will have a private regulatory domain if it uses the
+ *	regulatory_hint() API. Even when a private regdomain is used the channel
+ *	information will still be mended according to further hints from
+ *	the regulatory core to help with compliance. A dump version of this API
+ *	is now available which will returns the global regdomain as well as
+ *	all private regdomains of present wiphys (for those that have it).
+ *	If a wiphy is self-managed (%NL80211_ATTR_WIPHY_SELF_MANAGED_REG), then
+ *	its private regdomain is the only valid one for it. The regulatory
+ *	core is not used to help with compliance in this case.
  * @NL80211_CMD_SET_REG: Set current regulatory domain. CRDA sends this command
  *	after being queried by the kernel. CRDA replies by sending a regulatory
  *	domain structure which consists of %NL80211_ATTR_REG_ALPHA set to our
@@ -302,7 +337,9 @@
  *	if passed, define which channels should be scanned; if not
  *	passed, all channels allowed for the current regulatory domain
  *	are used.  Extra IEs can also be passed from the userspace by
- *	using the %NL80211_ATTR_IE attribute.
+ *	using the %NL80211_ATTR_IE attribute.  The first cycle of the
+ *	scheduled scan can be delayed by %NL80211_ATTR_SCHED_SCAN_DELAY
+ *	is supplied.
  * @NL80211_CMD_STOP_SCHED_SCAN: stop a scheduled scan. Returns -ENOENT if
  *	scheduled scan is not running. The caller may assume that as soon
  *	as the call returns, it is safe to start a new scheduled scan again.
@@ -639,7 +676,18 @@
  * @NL80211_CMD_CH_SWITCH_NOTIFY: An AP or GO may decide to switch channels
  *	independently of the userspace SME, send this event indicating
  *	%NL80211_ATTR_IFINDEX is now on %NL80211_ATTR_WIPHY_FREQ and the
- *	attributes determining channel width.
+ *	attributes determining channel width.  This indication may also be
+ *	sent when a remotely-initiated switch (e.g., when a STA receives a CSA
+ *	from the remote AP) is completed;
+ *
+ * @NL80211_CMD_CH_SWITCH_STARTED_NOTIFY: Notify that a channel switch
+ *	has been started on an interface, regardless of the initiator
+ *	(ie. whether it was requested from a remote device or
+ *	initiated on our own).  It indicates that
+ *	%NL80211_ATTR_IFINDEX will be on %NL80211_ATTR_WIPHY_FREQ
+ *	after %NL80211_ATTR_CH_SWITCH_COUNT TBTT's.  The userspace may
+ *	decide to react to this indication by requesting other
+ *	interfaces to change channel as well.
  *
  * @NL80211_CMD_START_P2P_DEVICE: Start the given P2P Device, identified by
  *	its %NL80211_ATTR_WDEV identifier. It must have been created with
@@ -738,6 +786,31 @@
  *	before removing a station entry entirely, or before disassociating
  *	or similar, cleanup will happen in the driver/device in this case.
  *
+ * @NL80211_CMD_GET_MPP: Get mesh path attributes for mesh proxy path to
+ *	destination %NL80211_ATTR_MAC on the interface identified by
+ *	%NL80211_ATTR_IFINDEX.
+ *
+ * @NL80211_CMD_JOIN_OCB: Join the OCB network. The center frequency and
+ *	bandwidth of a channel must be given.
+ * @NL80211_CMD_LEAVE_OCB: Leave the OCB network -- no special arguments, the
+ *	network is determined by the network interface.
+ *
+ * @NL80211_CMD_TDLS_CHANNEL_SWITCH: Start channel-switching with a TDLS peer,
+ *	identified by the %NL80211_ATTR_MAC parameter. A target channel is
+ *	provided via %NL80211_ATTR_WIPHY_FREQ and other attributes determining
+ *	channel width/type. The target operating class is given via
+ *	%NL80211_ATTR_OPER_CLASS.
+ *	The driver is responsible for continually initiating channel-switching
+ *	operations and returning to the base channel for communication with the
+ *	AP.
+ * @NL80211_CMD_TDLS_CANCEL_CHANNEL_SWITCH: Stop channel-switching with a TDLS
+ *	peer given by %NL80211_ATTR_MAC. Both peers must be on the base channel
+ *	when this command completes.
+ *
+ * @NL80211_CMD_WIPHY_REG_CHANGE: Similar to %NL80211_CMD_REG_CHANGE, but used
+ *	as an event to indicate changes for devices with wiphy-specific regdom
+ *	management.
+ *
  * @NL80211_CMD_MAX: highest used command number
  * @__NL80211_CMD_AFTER_LAST: internal use
  */
@@ -912,6 +985,18 @@
 	NL80211_CMD_ADD_TX_TS,
 	NL80211_CMD_DEL_TX_TS,
 
+	NL80211_CMD_GET_MPP,
+
+	NL80211_CMD_JOIN_OCB,
+	NL80211_CMD_LEAVE_OCB,
+
+	NL80211_CMD_CH_SWITCH_STARTED_NOTIFY,
+
+	NL80211_CMD_TDLS_CHANNEL_SWITCH,
+	NL80211_CMD_TDLS_CANCEL_CHANNEL_SWITCH,
+
+	NL80211_CMD_WIPHY_REG_CHANGE,
+
 	/* add new commands above here */
 
 	/* used to define NL80211_CMD_MAX below */
@@ -1606,9 +1691,16 @@
  * @NL80211_ATTR_TDLS_PEER_CAPABILITY: flags for TDLS peer capabilities, u32.
  *	As specified in the &enum nl80211_tdls_peer_capability.
  *
- * @NL80211_ATTR_IFACE_SOCKET_OWNER: flag attribute, if set during interface
+ * @NL80211_ATTR_SOCKET_OWNER: Flag attribute, if set during interface
  *	creation then the new interface will be owned by the netlink socket
- *	that created it and will be destroyed when the socket is closed
+ *	that created it and will be destroyed when the socket is closed.
+ *	If set during scheduled scan start then the new scan req will be
+ *	owned by the netlink socket that created it and the scheduled scan will
+ *	be stopped when the socket is closed.
+ *	If set during configuration of regulatory indoor operation then the
+ *	regulatory indoor configuration would be owned by the netlink socket
+ *	that configured the indoor setting, and the indoor operation would be
+ *	cleared when the socket is closed.
  *
  * @NL80211_ATTR_TDLS_INITIATOR: flag attribute indicating the current end is
  *	the TDLS link initiator.
@@ -1638,6 +1730,37 @@
  * @NL80211_ATTR_SMPS_MODE: SMPS mode to use (ap mode). see
  *	&enum nl80211_smps_mode.
  *
+ * @NL80211_ATTR_OPER_CLASS: operating class
+ *
+ * @NL80211_ATTR_MAC_MASK: MAC address mask
+ *
+ * @NL80211_ATTR_WIPHY_SELF_MANAGED_REG: flag attribute indicating this device
+ *	is self-managing its regulatory information and any regulatory domain
+ *	obtained from it is coming from the device's wiphy and not the global
+ *	cfg80211 regdomain.
+ *
+ * @NL80211_ATTR_EXT_FEATURES: extended feature flags contained in a byte
+ *	array. The feature flags are identified by their bit index (see &enum
+ *	nl80211_ext_feature_index). The bit index is ordered starting at the
+ *	least-significant bit of the first byte in the array, ie. bit index 0
+ *	is located at bit 0 of byte 0. bit index 25 would be located at bit 1
+ *	of byte 3 (u8 array).
+ *
+ * @NL80211_ATTR_SURVEY_RADIO_STATS: Request overall radio statistics to be
+ *	returned along with other survey data. If set, @NL80211_CMD_GET_SURVEY
+ *	may return a survey entry without a channel indicating global radio
+ *	statistics (only some values are valid and make sense.)
+ *	For devices that don't return such an entry even then, the information
+ *	should be contained in the result as the sum of the respective counters
+ *	over all channels.
+ *
+ * @NL80211_ATTR_SCHED_SCAN_DELAY: delay before a scheduled scan (or a
+ *	WoWLAN net-detect scan) is started, u32 in seconds.
+
+ * @NL80211_ATTR_REG_INDOOR: flag attribute, if set indicates that the device
+ *      is operating in an indoor environment.
+ *
+ * @NUM_NL80211_ATTR: total number of nl80211_attrs available
  * @NL80211_ATTR_MAX: highest attribute number currently defined
  * @__NL80211_ATTR_AFTER_LAST: internal use
  */
@@ -1973,7 +2096,7 @@
 
 	NL80211_ATTR_TDLS_PEER_CAPABILITY,
 
-	NL80211_ATTR_IFACE_SOCKET_OWNER,
+	NL80211_ATTR_SOCKET_OWNER,
 
 	NL80211_ATTR_CSA_C_OFFSETS_TX,
 	NL80211_ATTR_MAX_CSA_COUNTERS,
@@ -1990,15 +2113,33 @@
 
 	NL80211_ATTR_SMPS_MODE,
 
+	NL80211_ATTR_OPER_CLASS,
+
+	NL80211_ATTR_MAC_MASK,
+
+	NL80211_ATTR_WIPHY_SELF_MANAGED_REG,
+
+	NL80211_ATTR_EXT_FEATURES,
+
+	NL80211_ATTR_SURVEY_RADIO_STATS,
+
+	NL80211_ATTR_NETNS_FD,
+
+	NL80211_ATTR_SCHED_SCAN_DELAY,
+
+	NL80211_ATTR_REG_INDOOR,
+
 	/* add attributes here, update the policy in nl80211.c */
 
 	__NL80211_ATTR_AFTER_LAST,
+	NUM_NL80211_ATTR = __NL80211_ATTR_AFTER_LAST,
 	NL80211_ATTR_MAX = __NL80211_ATTR_AFTER_LAST - 1
 };
 
 /* source-level API compatibility */
 #define NL80211_ATTR_SCAN_GENERATION NL80211_ATTR_GENERATION
 #define	NL80211_ATTR_MESH_PARAMS NL80211_ATTR_MESH_CONFIG
+#define NL80211_ATTR_IFACE_SOCKET_OWNER NL80211_ATTR_SOCKET_OWNER
 
 /*
  * Allow user space programs to use #ifdef on new attributes by defining them
@@ -2028,7 +2169,7 @@
 
 #define NL80211_MAX_SUPP_RATES			32
 #define NL80211_MAX_SUPP_HT_RATES		77
-#define NL80211_MAX_SUPP_REG_RULES		32
+#define NL80211_MAX_SUPP_REG_RULES		64
 #define NL80211_TKIP_DATA_OFFSET_ENCR_KEY	0
 #define NL80211_TKIP_DATA_OFFSET_TX_MIC_KEY	16
 #define NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY	24
@@ -2064,6 +2205,8 @@
  *	and therefore can't be created in the normal ways, use the
  *	%NL80211_CMD_START_P2P_DEVICE and %NL80211_CMD_STOP_P2P_DEVICE
  *	commands to create and destroy one
+ * @NL80211_IF_TYPE_OCB: Outside Context of a BSS
+ *	This mode corresponds to the MIB variable dot11OCBActivated=true
  * @NL80211_IFTYPE_MAX: highest interface type number currently defined
  * @NUM_NL80211_IFTYPES: number of defined interface types
  *
@@ -2083,6 +2226,7 @@
 	NL80211_IFTYPE_P2P_CLIENT,
 	NL80211_IFTYPE_P2P_GO,
 	NL80211_IFTYPE_P2P_DEVICE,
+	NL80211_IFTYPE_OCB,
 
 	/* keep last */
 	NUM_NL80211_IFTYPES,
@@ -2165,8 +2309,15 @@
  * @NL80211_RATE_INFO_VHT_MCS: MCS index for VHT (u8)
  * @NL80211_RATE_INFO_VHT_NSS: number of streams in VHT (u8)
  * @NL80211_RATE_INFO_80_MHZ_WIDTH: 80 MHz VHT rate
- * @NL80211_RATE_INFO_80P80_MHZ_WIDTH: 80+80 MHz VHT rate
+ * @NL80211_RATE_INFO_80P80_MHZ_WIDTH: unused - 80+80 is treated the
+ *	same as 160 for purposes of the bitrates
  * @NL80211_RATE_INFO_160_MHZ_WIDTH: 160 MHz VHT rate
+ * @NL80211_RATE_INFO_10_MHZ_WIDTH: 10 MHz width - note that this is
+ *	a legacy rate and will be reported as the actual bitrate, i.e.
+ *	half the base (20 MHz) rate
+ * @NL80211_RATE_INFO_5_MHZ_WIDTH: 5 MHz width - note that this is
+ *	a legacy rate and will be reported as the actual bitrate, i.e.
+ *	a quarter of the base (20 MHz) rate
  * @__NL80211_RATE_INFO_AFTER_LAST: internal use
  */
 enum nl80211_rate_info {
@@ -2181,6 +2332,8 @@
 	NL80211_RATE_INFO_80_MHZ_WIDTH,
 	NL80211_RATE_INFO_80P80_MHZ_WIDTH,
 	NL80211_RATE_INFO_160_MHZ_WIDTH,
+	NL80211_RATE_INFO_10_MHZ_WIDTH,
+	NL80211_RATE_INFO_5_MHZ_WIDTH,
 
 	/* keep last */
 	__NL80211_RATE_INFO_AFTER_LAST,
@@ -2225,18 +2378,24 @@
  *
  * @__NL80211_STA_INFO_INVALID: attribute number 0 is reserved
  * @NL80211_STA_INFO_INACTIVE_TIME: time since last activity (u32, msecs)
- * @NL80211_STA_INFO_RX_BYTES: total received bytes (u32, from this station)
- * @NL80211_STA_INFO_TX_BYTES: total transmitted bytes (u32, to this station)
- * @NL80211_STA_INFO_RX_BYTES64: total received bytes (u64, from this station)
- * @NL80211_STA_INFO_TX_BYTES64: total transmitted bytes (u64, to this station)
+ * @NL80211_STA_INFO_RX_BYTES: total received bytes (MPDU length)
+ *	(u32, from this station)
+ * @NL80211_STA_INFO_TX_BYTES: total transmitted bytes (MPDU length)
+ *	(u32, to this station)
+ * @NL80211_STA_INFO_RX_BYTES64: total received bytes (MPDU length)
+ *	(u64, from this station)
+ * @NL80211_STA_INFO_TX_BYTES64: total transmitted bytes (MPDU length)
+ *	(u64, to this station)
  * @NL80211_STA_INFO_SIGNAL: signal strength of last received PPDU (u8, dBm)
  * @NL80211_STA_INFO_TX_BITRATE: current unicast tx rate, nested attribute
  * 	containing info as possible, see &enum nl80211_rate_info
- * @NL80211_STA_INFO_RX_PACKETS: total received packet (u32, from this station)
- * @NL80211_STA_INFO_TX_PACKETS: total transmitted packets (u32, to this
- *	station)
- * @NL80211_STA_INFO_TX_RETRIES: total retries (u32, to this station)
- * @NL80211_STA_INFO_TX_FAILED: total failed packets (u32, to this station)
+ * @NL80211_STA_INFO_RX_PACKETS: total received packet (MSDUs and MMPDUs)
+ *	(u32, from this station)
+ * @NL80211_STA_INFO_TX_PACKETS: total transmitted packets (MSDUs and MMPDUs)
+ *	(u32, to this station)
+ * @NL80211_STA_INFO_TX_RETRIES: total retries (MPDUs) (u32, to this station)
+ * @NL80211_STA_INFO_TX_FAILED: total failed packets (MPDUs)
+ *	(u32, to this station)
  * @NL80211_STA_INFO_SIGNAL_AVG: signal strength average (u8, dBm)
  * @NL80211_STA_INFO_LLID: the station's mesh LLID
  * @NL80211_STA_INFO_PLID: the station's mesh PLID
@@ -2260,6 +2419,16 @@
  *	Same format as NL80211_STA_INFO_CHAIN_SIGNAL.
  * @NL80211_STA_EXPECTED_THROUGHPUT: expected throughput considering also the
  *	802.11 header (u32, kbps)
+ * @NL80211_STA_INFO_RX_DROP_MISC: RX packets dropped for unspecified reasons
+ *	(u64)
+ * @NL80211_STA_INFO_BEACON_RX: number of beacons received from this peer (u64)
+ * @NL80211_STA_INFO_BEACON_SIGNAL_AVG: signal strength average
+ *	for beacons only (u8, dBm)
+ * @NL80211_STA_INFO_TID_STATS: per-TID statistics (see &enum nl80211_tid_stats)
+ *	This is a nested attribute where each the inner attribute number is the
+ *	TID+1 and the special TID 16 (i.e. value 17) is used for non-QoS frames;
+ *	each one of those is again nested with &enum nl80211_tid_stats
+ *	attributes carrying the actual values.
  * @__NL80211_STA_INFO_AFTER_LAST: internal
  * @NL80211_STA_INFO_MAX: highest possible station info attribute
  */
@@ -2292,6 +2461,10 @@
 	NL80211_STA_INFO_CHAIN_SIGNAL,
 	NL80211_STA_INFO_CHAIN_SIGNAL_AVG,
 	NL80211_STA_INFO_EXPECTED_THROUGHPUT,
+	NL80211_STA_INFO_RX_DROP_MISC,
+	NL80211_STA_INFO_BEACON_RX,
+	NL80211_STA_INFO_BEACON_SIGNAL_AVG,
+	NL80211_STA_INFO_TID_STATS,
 
 	/* keep last */
 	__NL80211_STA_INFO_AFTER_LAST,
@@ -2299,6 +2472,31 @@
 };
 
 /**
+ * enum nl80211_tid_stats - per TID statistics attributes
+ * @__NL80211_TID_STATS_INVALID: attribute number 0 is reserved
+ * @NL80211_TID_STATS_RX_MSDU: number of MSDUs received (u64)
+ * @NL80211_TID_STATS_TX_MSDU: number of MSDUs transmitted (or
+ *	attempted to transmit; u64)
+ * @NL80211_TID_STATS_TX_MSDU_RETRIES: number of retries for
+ *	transmitted MSDUs (not counting the first attempt; u64)
+ * @NL80211_TID_STATS_TX_MSDU_FAILED: number of failed transmitted
+ *	MSDUs (u64)
+ * @NUM_NL80211_TID_STATS: number of attributes here
+ * @NL80211_TID_STATS_MAX: highest numbered attribute here
+ */
+enum nl80211_tid_stats {
+	__NL80211_TID_STATS_INVALID,
+	NL80211_TID_STATS_RX_MSDU,
+	NL80211_TID_STATS_TX_MSDU,
+	NL80211_TID_STATS_TX_MSDU_RETRIES,
+	NL80211_TID_STATS_TX_MSDU_FAILED,
+
+	/* keep last */
+	NUM_NL80211_TID_STATS,
+	NL80211_TID_STATS_MAX = NUM_NL80211_TID_STATS - 1
+};
+
+/**
  * enum nl80211_mpath_flags - nl80211 mesh path flags
  *
  * @NL80211_MPATH_FLAG_ACTIVE: the mesh path is active
@@ -2631,6 +2829,11 @@
  * @NL80211_RRF_AUTO_BW: maximum available bandwidth should be calculated
  *	base on contiguous rules and wider channels will be allowed to cross
  *	multiple contiguous/overlapping frequency ranges.
+ * @NL80211_RRF_GO_CONCURRENT: See &NL80211_FREQUENCY_ATTR_GO_CONCURRENT
+ * @NL80211_RRF_NO_HT40MINUS: channels can't be used in HT40- operation
+ * @NL80211_RRF_NO_HT40PLUS: channels can't be used in HT40+ operation
+ * @NL80211_RRF_NO_80MHZ: 80MHz operation not allowed
+ * @NL80211_RRF_NO_160MHZ: 160MHz operation not allowed
  */
 enum nl80211_reg_rule_flags {
 	NL80211_RRF_NO_OFDM		= 1<<0,
@@ -2643,11 +2846,18 @@
 	NL80211_RRF_NO_IR		= 1<<7,
 	__NL80211_RRF_NO_IBSS		= 1<<8,
 	NL80211_RRF_AUTO_BW		= 1<<11,
+	NL80211_RRF_GO_CONCURRENT	= 1<<12,
+	NL80211_RRF_NO_HT40MINUS	= 1<<13,
+	NL80211_RRF_NO_HT40PLUS		= 1<<14,
+	NL80211_RRF_NO_80MHZ		= 1<<15,
+	NL80211_RRF_NO_160MHZ		= 1<<16,
 };
 
 #define NL80211_RRF_PASSIVE_SCAN	NL80211_RRF_NO_IR
 #define NL80211_RRF_NO_IBSS		NL80211_RRF_NO_IR
 #define NL80211_RRF_NO_IR		NL80211_RRF_NO_IR
+#define NL80211_RRF_NO_HT40		(NL80211_RRF_NO_HT40MINUS |\
+					 NL80211_RRF_NO_HT40PLUS)
 
 /* For backport compatibility with older userspace */
 #define NL80211_RRF_NO_IR_ALL		(NL80211_RRF_NO_IR | __NL80211_RRF_NO_IBSS)
@@ -2700,16 +2910,18 @@
  * @NL80211_SURVEY_INFO_FREQUENCY: center frequency of channel
  * @NL80211_SURVEY_INFO_NOISE: noise level of channel (u8, dBm)
  * @NL80211_SURVEY_INFO_IN_USE: channel is currently being used
- * @NL80211_SURVEY_INFO_CHANNEL_TIME: amount of time (in ms) that the radio
- *	spent on this channel
- * @NL80211_SURVEY_INFO_CHANNEL_TIME_BUSY: amount of the time the primary
+ * @NL80211_SURVEY_INFO_TIME: amount of time (in ms) that the radio
+ *	was turned on (on channel or globally)
+ * @NL80211_SURVEY_INFO_TIME_BUSY: amount of the time the primary
  *	channel was sensed busy (either due to activity or energy detect)
- * @NL80211_SURVEY_INFO_CHANNEL_TIME_EXT_BUSY: amount of time the extension
+ * @NL80211_SURVEY_INFO_TIME_EXT_BUSY: amount of time the extension
  *	channel was sensed busy
- * @NL80211_SURVEY_INFO_CHANNEL_TIME_RX: amount of time the radio spent
- *	receiving data
- * @NL80211_SURVEY_INFO_CHANNEL_TIME_TX: amount of time the radio spent
- *	transmitting data
+ * @NL80211_SURVEY_INFO_TIME_RX: amount of time the radio spent
+ *	receiving data (on channel or globally)
+ * @NL80211_SURVEY_INFO_TIME_TX: amount of time the radio spent
+ *	transmitting data (on channel or globally)
+ * @NL80211_SURVEY_INFO_TIME_SCAN: time the radio spent for scan
+ *	(on this channel or globally)
  * @NL80211_SURVEY_INFO_MAX: highest survey info attribute number
  *	currently defined
  * @__NL80211_SURVEY_INFO_AFTER_LAST: internal use
@@ -2719,17 +2931,25 @@
 	NL80211_SURVEY_INFO_FREQUENCY,
 	NL80211_SURVEY_INFO_NOISE,
 	NL80211_SURVEY_INFO_IN_USE,
-	NL80211_SURVEY_INFO_CHANNEL_TIME,
-	NL80211_SURVEY_INFO_CHANNEL_TIME_BUSY,
-	NL80211_SURVEY_INFO_CHANNEL_TIME_EXT_BUSY,
-	NL80211_SURVEY_INFO_CHANNEL_TIME_RX,
-	NL80211_SURVEY_INFO_CHANNEL_TIME_TX,
+	NL80211_SURVEY_INFO_TIME,
+	NL80211_SURVEY_INFO_TIME_BUSY,
+	NL80211_SURVEY_INFO_TIME_EXT_BUSY,
+	NL80211_SURVEY_INFO_TIME_RX,
+	NL80211_SURVEY_INFO_TIME_TX,
+	NL80211_SURVEY_INFO_TIME_SCAN,
 
 	/* keep last */
 	__NL80211_SURVEY_INFO_AFTER_LAST,
 	NL80211_SURVEY_INFO_MAX = __NL80211_SURVEY_INFO_AFTER_LAST - 1
 };
 
+/* keep old names for compatibility */
+#define NL80211_SURVEY_INFO_CHANNEL_TIME		NL80211_SURVEY_INFO_TIME
+#define NL80211_SURVEY_INFO_CHANNEL_TIME_BUSY		NL80211_SURVEY_INFO_TIME_BUSY
+#define NL80211_SURVEY_INFO_CHANNEL_TIME_EXT_BUSY	NL80211_SURVEY_INFO_TIME_EXT_BUSY
+#define NL80211_SURVEY_INFO_CHANNEL_TIME_RX		NL80211_SURVEY_INFO_TIME_RX
+#define NL80211_SURVEY_INFO_CHANNEL_TIME_TX		NL80211_SURVEY_INFO_TIME_TX
+
 /**
  * enum nl80211_mntr_flags - monitor configuration flags
  *
@@ -2894,7 +3114,8 @@
  *
  * @NL80211_MESHCONF_PLINK_TIMEOUT: If no tx activity is seen from a STA we've
  *	established peering with for longer than this time (in seconds), then
- *	remove it from the STA's list of peers.  Default is 30 minutes.
+ *	remove it from the STA's list of peers. You may set this to 0 to disable
+ *	the removal of the STA. Default is 30 minutes.
  *
  * @__NL80211_MESHCONF_ATTR_AFTER_LAST: internal use
  */
@@ -3166,6 +3387,9 @@
 /**
  * enum nl80211_bss_status - BSS "status"
  * @NL80211_BSS_STATUS_AUTHENTICATED: Authenticated with this BSS.
+ *	Note that this is no longer used since cfg80211 no longer
+ *	keeps track of whether or not authentication was done with
+ *	a given BSS.
  * @NL80211_BSS_STATUS_ASSOCIATED: Associated with this BSS.
  * @NL80211_BSS_STATUS_IBSS_JOINED: Joined to this IBSS.
  *
@@ -3379,6 +3603,8 @@
  *	interval in which %NL80211_ATTR_CQM_TXE_PKTS and
  *	%NL80211_ATTR_CQM_TXE_RATE must be satisfied before generating an
  *	%NL80211_CMD_NOTIFY_CQM. Set to 0 to turn off TX error reporting.
+ * @NL80211_ATTR_CQM_BEACON_LOSS_EVENT: flag attribute that's set in a beacon
+ *	loss event
  * @__NL80211_ATTR_CQM_AFTER_LAST: internal
  * @NL80211_ATTR_CQM_MAX: highest key attribute
  */
@@ -3391,6 +3617,7 @@
 	NL80211_ATTR_CQM_TXE_RATE,
 	NL80211_ATTR_CQM_TXE_PKTS,
 	NL80211_ATTR_CQM_TXE_INTVL,
+	NL80211_ATTR_CQM_BEACON_LOSS_EVENT,
 
 	/* keep last */
 	__NL80211_ATTR_CQM_AFTER_LAST,
@@ -3403,9 +3630,7 @@
  *      configured threshold
  * @NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH: The RSSI is higher than the
  *      configured threshold
- * @NL80211_CQM_RSSI_BEACON_LOSS_EVENT: The device experienced beacon loss.
- *	(Note that deauth/disassoc will still follow if the AP is not
- *	available. This event might get used as roaming event, etc.)
+ * @NL80211_CQM_RSSI_BEACON_LOSS_EVENT: (reserved, never sent)
  */
 enum nl80211_cqm_rssi_threshold_event {
 	NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW,
@@ -3492,6 +3717,8 @@
  * @NL80211_WOWLAN_TRIG_ANY: wake up on any activity, do not really put
  *	the chip into a special state -- works best with chips that have
  *	support for low-power operation already (flag)
+ *	Note that this mode is incompatible with all of the others, if
+ *	any others are even supported by the device.
  * @NL80211_WOWLAN_TRIG_DISCONNECT: wake up on disconnect, the way disconnect
  *	is detected is implementation-specific (flag)
  * @NL80211_WOWLAN_TRIG_MAGIC_PKT: wake up on magic packet (6x 0xff, followed
@@ -3545,6 +3772,28 @@
  * @NL80211_WOWLAN_TRIG_WAKEUP_TCP_NOMORETOKENS: For wakeup reporting only,
  *	the TCP connection ran out of tokens to use for data to send to the
  *	service
+ * @NL80211_WOWLAN_TRIG_NET_DETECT: wake up when a configured network
+ *	is detected.  This is a nested attribute that contains the
+ *	same attributes used with @NL80211_CMD_START_SCHED_SCAN.  It
+ *	specifies how the scan is performed (e.g. the interval, the
+ *	channels to scan and the initial delay) as well as the scan
+ *	results that will trigger a wake (i.e. the matchsets).  This
+ *	attribute is also sent in a response to
+ *	@NL80211_CMD_GET_WIPHY, indicating the number of match sets
+ *	supported by the driver (u32).
+ * @NL80211_WOWLAN_TRIG_NET_DETECT_RESULTS: nested attribute
+ *	containing an array with information about what triggered the
+ *	wake up.  If no elements are present in the array, it means
+ *	that the information is not available.  If more than one
+ *	element is present, it means that more than one match
+ *	occurred.
+ *	Each element in the array is a nested attribute that contains
+ *	one optional %NL80211_ATTR_SSID attribute and one optional
+ *	%NL80211_ATTR_SCAN_FREQUENCIES attribute.  At least one of
+ *	these attributes must be present.  If
+ *	%NL80211_ATTR_SCAN_FREQUENCIES contains more than one
+ *	frequency, it means that the match occurred in more than one
+ *	channel.
  * @NUM_NL80211_WOWLAN_TRIG: number of wake on wireless triggers
  * @MAX_NL80211_WOWLAN_TRIG: highest wowlan trigger attribute number
  *
@@ -3570,6 +3819,8 @@
 	NL80211_WOWLAN_TRIG_WAKEUP_TCP_MATCH,
 	NL80211_WOWLAN_TRIG_WAKEUP_TCP_CONNLOST,
 	NL80211_WOWLAN_TRIG_WAKEUP_TCP_NOMORETOKENS,
+	NL80211_WOWLAN_TRIG_NET_DETECT,
+	NL80211_WOWLAN_TRIG_NET_DETECT_RESULTS,
 
 	/* keep last */
 	NUM_NL80211_WOWLAN_TRIG,
@@ -4042,6 +4293,27 @@
  *	multiplexing powersave, ie. can turn off all but one chain
  *	and then wake the rest up as required after, for example,
  *	rts/cts handshake.
+ * @NL80211_FEATURE_SUPPORTS_WMM_ADMISSION: the device supports setting up WMM
+ *	TSPEC sessions (TID aka TSID 0-7) with the %NL80211_CMD_ADD_TX_TS
+ *	command. Standard IEEE 802.11 TSPEC setup is not yet supported, it
+ *	needs to be able to handle Block-Ack agreements and other things.
+ * @NL80211_FEATURE_MAC_ON_CREATE: Device supports configuring
+ *	the vif's MAC address upon creation.
+ *	See 'macaddr' field in the vif_params (cfg80211.h).
+ * @NL80211_FEATURE_TDLS_CHANNEL_SWITCH: Driver supports channel switching when
+ *	operating as a TDLS peer.
+ * @NL80211_FEATURE_SCAN_RANDOM_MAC_ADDR: This device/driver supports using a
+ *	random MAC address during scan (if the device is unassociated); the
+ *	%NL80211_SCAN_FLAG_RANDOM_ADDR flag may be set for scans and the MAC
+ *	address mask/value will be used.
+ * @NL80211_FEATURE_SCHED_SCAN_RANDOM_MAC_ADDR: This device/driver supports
+ *	using a random MAC address for every scan iteration during scheduled
+ *	scan (while not associated), the %NL80211_SCAN_FLAG_RANDOM_ADDR may
+ *	be set for scheduled scan and the MAC address mask/value will be used.
+ * @NL80211_FEATURE_ND_RANDOM_MAC_ADDR: This device/driver supports using a
+ *	random MAC address for every scan iteration during "net detect", i.e.
+ *	scan in unassociated WoWLAN, the %NL80211_SCAN_FLAG_RANDOM_ADDR may
+ *	be set for scheduled scan and the MAC address mask/value will be used.
  */
 enum nl80211_feature_flags {
 	NL80211_FEATURE_SK_TX_STATUS			= 1 << 0,
@@ -4070,6 +4342,27 @@
 	NL80211_FEATURE_ACKTO_ESTIMATION		= 1 << 23,
 	NL80211_FEATURE_STATIC_SMPS			= 1 << 24,
 	NL80211_FEATURE_DYNAMIC_SMPS			= 1 << 25,
+	NL80211_FEATURE_SUPPORTS_WMM_ADMISSION		= 1 << 26,
+	NL80211_FEATURE_MAC_ON_CREATE			= 1 << 27,
+	NL80211_FEATURE_TDLS_CHANNEL_SWITCH		= 1 << 28,
+	NL80211_FEATURE_SCAN_RANDOM_MAC_ADDR		= 1 << 29,
+	NL80211_FEATURE_SCHED_SCAN_RANDOM_MAC_ADDR	= 1 << 30,
+	NL80211_FEATURE_ND_RANDOM_MAC_ADDR		= 1 << 31,
+};
+
+/**
+ * enum nl80211_ext_feature_index - bit index of extended features.
+ * @NL80211_EXT_FEATURE_VHT_IBSS: This driver supports IBSS with VHT datarates.
+ *
+ * @NUM_NL80211_EXT_FEATURES: number of extended features.
+ * @MAX_NL80211_EXT_FEATURES: highest extended feature index.
+ */
+enum nl80211_ext_feature_index {
+	NL80211_EXT_FEATURE_VHT_IBSS,
+
+	/* add new features before the definition below */
+	NUM_NL80211_EXT_FEATURES,
+	MAX_NL80211_EXT_FEATURES = NUM_NL80211_EXT_FEATURES - 1
 };
 
 /**
@@ -4118,11 +4411,21 @@
  *	dangerous because will destroy stations performance as a lot of frames
  *	will be lost while scanning off-channel, therefore it must be used only
  *	when really needed
+ * @NL80211_SCAN_FLAG_RANDOM_ADDR: use a random MAC address for this scan (or
+ *	for scheduled scan: a different one for every scan iteration). When the
+ *	flag is set, depending on device capabilities the @NL80211_ATTR_MAC and
+ *	@NL80211_ATTR_MAC_MASK attributes may also be given in which case only
+ *	the masked bits will be preserved from the MAC address and the remainder
+ *	randomised. If the attributes are not given full randomisation (46 bits,
+ *	locally administered 1, multicast 0) is assumed.
+ *	This flag must not be requested when the feature isn't supported, check
+ *	the nl80211 feature flags for the device.
  */
 enum nl80211_scan_flags {
 	NL80211_SCAN_FLAG_LOW_PRIORITY			= 1<<0,
 	NL80211_SCAN_FLAG_FLUSH				= 1<<1,
 	NL80211_SCAN_FLAG_AP				= 1<<2,
+	NL80211_SCAN_FLAG_RANDOM_ADDR			= 1<<3,
 };
 
 /**
diff --git a/src/drivers/priv_netlink.h b/src/drivers/priv_netlink.h
index 6232088..d3f091c 100644
--- a/src/drivers/priv_netlink.h
+++ b/src/drivers/priv_netlink.h
@@ -68,6 +68,7 @@
 ((attrlen) -= RTA_ALIGN((rta)->rta_len), \
 (struct rtattr *) (((char *)(rta)) + RTA_ALIGN((rta)->rta_len)))
 #define RTA_LENGTH(len) (RTA_ALIGN(sizeof(struct rtattr)) + (len))
+#define RTA_SPACE(len) RTA_ALIGN(RTA_LENGTH(len))
 #define RTA_DATA(rta) ((void *) (((char *) (rta)) + RTA_LENGTH(0)))
 #define RTA_PAYLOAD(rta) ((int) ((rta)->rta_len) - RTA_LENGTH(0))
 
diff --git a/src/eap_common/Makefile b/src/eap_common/Makefile
index adfd3df..f00b438 100644
--- a/src/eap_common/Makefile
+++ b/src/eap_common/Makefile
@@ -1,8 +1,31 @@
-all:
-	@echo Nothing to be made.
+all: libeap_common.a
 
 clean:
-	rm -f *~ *.o *.d *.gcno *.gcda *.gcov
+	rm -f *~ *.o *.d *.gcno *.gcda *.gcov libeap_common.a
 
 install:
 	@echo Nothing to be made.
+
+include ../lib.rules
+
+LIB_OBJS= \
+	chap.o \
+	eap_common.o \
+	eap_eke_common.o \
+	eap_eke_common.o \
+	eap_fast_common.o \
+	eap_gpsk_common.o \
+	eap_ikev2_common.o \
+	eap_pax_common.o \
+	eap_peap_common.o \
+	eap_psk_common.o \
+	eap_pwd_common.o \
+	eap_sake_common.o \
+	eap_sim_common.o \
+	eap_wsc_common.o \
+	ikev2_common.o
+
+libeap_common.a: $(LIB_OBJS)
+	$(AR) crT $@ $?
+
+-include $(OBJS:%.o=%.d)
diff --git a/src/eap_common/eap_common.c b/src/eap_common/eap_common.c
index 7b077cb..51a15d7 100644
--- a/src/eap_common/eap_common.c
+++ b/src/eap_common/eap_common.c
@@ -1,6 +1,6 @@
 /*
  * EAP common peer/server definitions
- * Copyright (c) 2004-2012, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2014, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -192,7 +192,7 @@
 
 
 /**
- * eap_get_id - Get EAP Type from wpabuf
+ * eap_get_type - Get EAP Type from wpabuf
  * @msg: Buffer starting with an EAP header
  * Returns: The EAP Type after the EAP header
  */
@@ -203,3 +203,86 @@
 
 	return ((const u8 *) wpabuf_head(msg))[sizeof(struct eap_hdr)];
 }
+
+
+#ifdef CONFIG_ERP
+int erp_parse_tlvs(const u8 *pos, const u8 *end, struct erp_tlvs *tlvs,
+		   int stop_at_keyname)
+{
+	os_memset(tlvs, 0, sizeof(*tlvs));
+
+	while (pos < end) {
+		u8 tlv_type, tlv_len;
+
+		tlv_type = *pos++;
+		switch (tlv_type) {
+		case EAP_ERP_TV_RRK_LIFETIME:
+		case EAP_ERP_TV_RMSK_LIFETIME:
+			/* 4-octet TV */
+			if (pos + 4 > end) {
+				wpa_printf(MSG_DEBUG, "EAP: Too short TV");
+				return -1;
+			}
+			pos += 4;
+			break;
+		case EAP_ERP_TLV_DOMAIN_NAME:
+		case EAP_ERP_TLV_KEYNAME_NAI:
+		case EAP_ERP_TLV_CRYPTOSUITES:
+		case EAP_ERP_TLV_AUTHORIZATION_INDICATION:
+		case EAP_ERP_TLV_CALLED_STATION_ID:
+		case EAP_ERP_TLV_CALLING_STATION_ID:
+		case EAP_ERP_TLV_NAS_IDENTIFIER:
+		case EAP_ERP_TLV_NAS_IP_ADDRESS:
+		case EAP_ERP_TLV_NAS_IPV6_ADDRESS:
+			if (pos >= end) {
+				wpa_printf(MSG_DEBUG, "EAP: Too short TLV");
+				return -1;
+			}
+			tlv_len = *pos++;
+			if (tlv_len > (unsigned) (end - pos)) {
+				wpa_printf(MSG_DEBUG, "EAP: Truncated TLV");
+				return -1;
+			}
+			if (tlv_type == EAP_ERP_TLV_KEYNAME_NAI) {
+				if (tlvs->keyname) {
+					wpa_printf(MSG_DEBUG,
+						   "EAP: More than one keyName-NAI");
+					return -1;
+				}
+				tlvs->keyname = pos;
+				tlvs->keyname_len = tlv_len;
+				if (stop_at_keyname)
+					return 0;
+			} else if (tlv_type == EAP_ERP_TLV_DOMAIN_NAME) {
+				tlvs->domain = pos;
+				tlvs->domain_len = tlv_len;
+			}
+			pos += tlv_len;
+			break;
+		default:
+			if (tlv_type >= 128 && tlv_type <= 191) {
+				/* Undefined TLV */
+				if (pos >= end) {
+					wpa_printf(MSG_DEBUG,
+						   "EAP: Too short TLV");
+					return -1;
+				}
+				tlv_len = *pos++;
+				if (tlv_len > (unsigned) (end - pos)) {
+					wpa_printf(MSG_DEBUG,
+						   "EAP: Truncated TLV");
+					return -1;
+				}
+				pos += tlv_len;
+				break;
+			}
+			wpa_printf(MSG_DEBUG, "EAP: Unknown TV/TLV type %u",
+				   tlv_type);
+			pos = end;
+			break;
+		}
+	}
+
+	return 0;
+}
+#endif /* CONFIG_ERP */
diff --git a/src/eap_common/eap_common.h b/src/eap_common/eap_common.h
index 8850c1f..e62f167 100644
--- a/src/eap_common/eap_common.h
+++ b/src/eap_common/eap_common.h
@@ -1,6 +1,6 @@
 /*
  * EAP common peer/server definitions
- * Copyright (c) 2004-2012, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2014, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -11,6 +11,14 @@
 
 #include "wpabuf.h"
 
+struct erp_tlvs {
+	const u8 *keyname;
+	const u8 *domain;
+
+	u8 keyname_len;
+	u8 domain_len;
+};
+
 int eap_hdr_len_valid(const struct wpabuf *msg, size_t min_payload);
 const u8 * eap_hdr_validate(int vendor, EapType eap_type,
 			    const struct wpabuf *msg, size_t *plen);
@@ -19,5 +27,7 @@
 void eap_update_len(struct wpabuf *msg);
 u8 eap_get_id(const struct wpabuf *msg);
 EapType eap_get_type(const struct wpabuf *msg);
+int erp_parse_tlvs(const u8 *pos, const u8 *end, struct erp_tlvs *tlvs,
+		   int stop_at_keyname);
 
 #endif /* EAP_COMMON_H */
diff --git a/src/eap_common/eap_defs.h b/src/eap_common/eap_defs.h
index 4f14a01..54f26ca 100644
--- a/src/eap_common/eap_defs.h
+++ b/src/eap_common/eap_defs.h
@@ -1,6 +1,6 @@
 /*
  * EAP server/peer: Shared EAP definitions
- * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2014, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -27,11 +27,39 @@
 #endif /* _MSC_VER */
 
 enum { EAP_CODE_REQUEST = 1, EAP_CODE_RESPONSE = 2, EAP_CODE_SUCCESS = 3,
-       EAP_CODE_FAILURE = 4 };
+       EAP_CODE_FAILURE = 4, EAP_CODE_INITIATE = 5, EAP_CODE_FINISH = 6 };
 
 /* EAP Request and Response data begins with one octet Type. Success and
  * Failure do not have additional data. */
 
+/* Type field in EAP-Initiate and EAP-Finish messages */
+enum eap_erp_type {
+	EAP_ERP_TYPE_REAUTH_START = 1,
+	EAP_ERP_TYPE_REAUTH = 2,
+};
+
+/* ERP TV/TLV types */
+enum eap_erp_tlv_type {
+	EAP_ERP_TLV_KEYNAME_NAI = 1,
+	EAP_ERP_TV_RRK_LIFETIME = 2,
+	EAP_ERP_TV_RMSK_LIFETIME = 3,
+	EAP_ERP_TLV_DOMAIN_NAME = 4,
+	EAP_ERP_TLV_CRYPTOSUITES = 5,
+	EAP_ERP_TLV_AUTHORIZATION_INDICATION = 6,
+	EAP_ERP_TLV_CALLED_STATION_ID = 128,
+	EAP_ERP_TLV_CALLING_STATION_ID = 129,
+	EAP_ERP_TLV_NAS_IDENTIFIER = 130,
+	EAP_ERP_TLV_NAS_IP_ADDRESS = 131,
+	EAP_ERP_TLV_NAS_IPV6_ADDRESS = 132,
+};
+
+/* ERP Cryptosuite */
+enum eap_erp_cryptosuite {
+	EAP_ERP_CS_HMAC_SHA256_64 = 1,
+	EAP_ERP_CS_HMAC_SHA256_128 = 2,
+	EAP_ERP_CS_HMAC_SHA256_256 = 3,
+};
+
 /*
  * EAP Method Types as allocated by IANA:
  * http://www.iana.org/assignments/eap-numbers
@@ -84,5 +112,7 @@
 
 #define EAP_MSK_LEN 64
 #define EAP_EMSK_LEN 64
+#define EAP_EMSK_NAME_LEN 8
+#define ERP_MAX_KEY_LEN 64
 
 #endif /* EAP_DEFS_H */
diff --git a/src/eap_common/eap_fast_common.c b/src/eap_common/eap_fast_common.c
index fceb1b0..151cc78 100644
--- a/src/eap_common/eap_fast_common.c
+++ b/src/eap_common/eap_fast_common.c
@@ -96,49 +96,18 @@
 u8 * eap_fast_derive_key(void *ssl_ctx, struct tls_connection *conn,
 			 const char *label, size_t len)
 {
-	struct tls_keys keys;
-	u8 *rnd = NULL, *out;
-	int block_size;
+	u8 *out;
 
-	block_size = tls_connection_get_keyblock_size(ssl_ctx, conn);
-	if (block_size < 0)
-		return NULL;
-
-	out = os_malloc(block_size + len);
+	out = os_malloc(len);
 	if (out == NULL)
 		return NULL;
 
-	if (tls_connection_prf(ssl_ctx, conn, label, 1, out, block_size + len)
-	    == 0) {
-		os_memmove(out, out + block_size, len);
-		return out;
+	if (tls_connection_prf(ssl_ctx, conn, label, 1, 1, out, len)) {
+		os_free(out);
+		return NULL;
 	}
 
-	if (tls_connection_get_keys(ssl_ctx, conn, &keys))
-		goto fail;
-
-	rnd = os_malloc(keys.client_random_len + keys.server_random_len);
-	if (rnd == NULL)
-		goto fail;
-
-	os_memcpy(rnd, keys.server_random, keys.server_random_len);
-	os_memcpy(rnd + keys.server_random_len, keys.client_random,
-		  keys.client_random_len);
-
-	wpa_hexdump_key(MSG_MSGDUMP, "EAP-FAST: master_secret for key "
-			"expansion", keys.master_key, keys.master_key_len);
-	if (tls_prf_sha1_md5(keys.master_key, keys.master_key_len,
-			     label, rnd, keys.client_random_len +
-			     keys.server_random_len, out, block_size + len))
-		goto fail;
-	os_free(rnd);
-	os_memmove(out, out + block_size, len);
 	return out;
-
-fail:
-	os_free(rnd);
-	os_free(out);
-	return NULL;
 }
 
 
diff --git a/src/eap_common/eap_pax_common.c b/src/eap_common/eap_pax_common.c
index b3bbacc..0e80ef5 100644
--- a/src/eap_common/eap_pax_common.c
+++ b/src/eap_common/eap_pax_common.c
@@ -121,10 +121,11 @@
  * @mk: Buffer for the derived Master Key
  * @ck: Buffer for the derived Confirmation Key
  * @ick: Buffer for the derived Integrity Check Key
+ * @mid: Buffer for the derived Method ID
  * Returns: 0 on success, -1 on failure
  */
 int eap_pax_initial_key_derivation(u8 mac_id, const u8 *ak, const u8 *e,
-				   u8 *mk, u8 *ck, u8 *ick)
+				   u8 *mk, u8 *ck, u8 *ick, u8 *mid)
 {
 	wpa_printf(MSG_DEBUG, "EAP-PAX: initial key derivation");
 	if (eap_pax_kdf(mac_id, ak, EAP_PAX_AK_LEN, "Master Key",
@@ -132,13 +133,16 @@
 	    eap_pax_kdf(mac_id, mk, EAP_PAX_MK_LEN, "Confirmation Key",
 			e, 2 * EAP_PAX_RAND_LEN, EAP_PAX_CK_LEN, ck) ||
 	    eap_pax_kdf(mac_id, mk, EAP_PAX_MK_LEN, "Integrity Check Key",
-			e, 2 * EAP_PAX_RAND_LEN, EAP_PAX_ICK_LEN, ick))
+			e, 2 * EAP_PAX_RAND_LEN, EAP_PAX_ICK_LEN, ick) ||
+	    eap_pax_kdf(mac_id, mk, EAP_PAX_MK_LEN, "Method ID",
+			e, 2 * EAP_PAX_RAND_LEN, EAP_PAX_MID_LEN, mid))
 		return -1;
 
 	wpa_hexdump_key(MSG_MSGDUMP, "EAP-PAX: AK", ak, EAP_PAX_AK_LEN);
 	wpa_hexdump_key(MSG_MSGDUMP, "EAP-PAX: MK", mk, EAP_PAX_MK_LEN);
 	wpa_hexdump_key(MSG_MSGDUMP, "EAP-PAX: CK", ck, EAP_PAX_CK_LEN);
 	wpa_hexdump_key(MSG_MSGDUMP, "EAP-PAX: ICK", ick, EAP_PAX_ICK_LEN);
+	wpa_hexdump_key(MSG_MSGDUMP, "EAP-PAX: MID", mid, EAP_PAX_MID_LEN);
 
 	return 0;
 }
diff --git a/src/eap_common/eap_pax_common.h b/src/eap_common/eap_pax_common.h
index fb03df2..e6cdf4d 100644
--- a/src/eap_common/eap_pax_common.h
+++ b/src/eap_common/eap_pax_common.h
@@ -74,6 +74,7 @@
 #define EAP_PAX_MK_LEN 16
 #define EAP_PAX_CK_LEN 16
 #define EAP_PAX_ICK_LEN 16
+#define EAP_PAX_MID_LEN 16
 
 
 int eap_pax_kdf(u8 mac_id, const u8 *key, size_t key_len,
@@ -86,6 +87,6 @@
 		const u8 *data3, size_t data3_len,
 		u8 *mac);
 int eap_pax_initial_key_derivation(u8 mac_id, const u8 *ak, const u8 *e,
-				   u8 *mk, u8 *ck, u8 *ick);
+				   u8 *mk, u8 *ck, u8 *ick, u8 *mid);
 
 #endif /* EAP_PAX_COMMON_H */
diff --git a/src/eap_common/eap_pwd_common.c b/src/eap_common/eap_pwd_common.c
index 631c363..4d27623 100644
--- a/src/eap_common/eap_pwd_common.c
+++ b/src/eap_common/eap_pwd_common.c
@@ -86,9 +86,10 @@
  * on the password and identities.
  */
 int compute_password_element(EAP_PWD_group *grp, u16 num,
-			     u8 *password, int password_len,
-			     u8 *id_server, int id_server_len,
-			     u8 *id_peer, int id_peer_len, u8 *token)
+			     const u8 *password, size_t password_len,
+			     const u8 *id_server, size_t id_server_len,
+			     const u8 *id_peer, size_t id_peer_len,
+			     const u8 *token)
 {
 	BIGNUM *x_candidate = NULL, *rnd = NULL, *cofactor = NULL;
 	struct crypto_hash *hash;
@@ -283,10 +284,10 @@
 }
 
 
-int compute_keys(EAP_PWD_group *grp, BN_CTX *bnctx, BIGNUM *k,
-		 BIGNUM *peer_scalar, BIGNUM *server_scalar,
-		 u8 *confirm_peer, u8 *confirm_server,
-		 u32 *ciphersuite, u8 *msk, u8 *emsk, u8 *session_id)
+int compute_keys(EAP_PWD_group *grp, BN_CTX *bnctx, const BIGNUM *k,
+		 const BIGNUM *peer_scalar, const BIGNUM *server_scalar,
+		 const u8 *confirm_peer, const u8 *confirm_server,
+		 const u32 *ciphersuite, u8 *msk, u8 *emsk, u8 *session_id)
 {
 	struct crypto_hash *hash;
 	u8 mk[SHA256_MAC_LEN], *cruft;
@@ -306,7 +307,7 @@
 		os_free(cruft);
 		return -1;
 	}
-	eap_pwd_h_update(hash, (u8 *) ciphersuite, sizeof(u32));
+	eap_pwd_h_update(hash, (const u8 *) ciphersuite, sizeof(u32));
 	offset = BN_num_bytes(grp->order) - BN_num_bytes(peer_scalar);
 	os_memset(cruft, 0, BN_num_bytes(grp->prime));
 	BN_bn2bin(peer_scalar, cruft + offset);
diff --git a/src/eap_common/eap_pwd_common.h b/src/eap_common/eap_pwd_common.h
index c54c441..a0d717e 100644
--- a/src/eap_common/eap_pwd_common.h
+++ b/src/eap_common/eap_pwd_common.h
@@ -56,10 +56,15 @@
 } STRUCT_PACKED;
 
 /* common routines */
-int compute_password_element(EAP_PWD_group *, u16, u8 *, int, u8 *, int, u8 *,
-			     int, u8 *);
-int compute_keys(EAP_PWD_group *, BN_CTX *, BIGNUM *, BIGNUM *, BIGNUM *,
-		 u8 *, u8 *, u32 *, u8 *, u8 *, u8 *);
+int compute_password_element(EAP_PWD_group *grp, u16 num,
+			     const u8 *password, size_t password_len,
+			     const u8 *id_server, size_t id_server_len,
+			     const u8 *id_peer, size_t id_peer_len,
+			     const u8 *token);
+int compute_keys(EAP_PWD_group *grp, BN_CTX *bnctx, const BIGNUM *k,
+		 const BIGNUM *peer_scalar, const BIGNUM *server_scalar,
+		 const u8 *confirm_peer, const u8 *confirm_server,
+		 const u32 *ciphersuite, u8 *msk, u8 *emsk, u8 *session_id);
 struct crypto_hash * eap_pwd_h_init(void);
 void eap_pwd_h_update(struct crypto_hash *hash, const u8 *data, size_t len);
 void eap_pwd_h_final(struct crypto_hash *hash, u8 *digest);
diff --git a/src/eap_common/eap_sake_common.c b/src/eap_common/eap_sake_common.c
index a76253d..c22e43e 100644
--- a/src/eap_common/eap_sake_common.c
+++ b/src/eap_common/eap_sake_common.c
@@ -16,99 +16,99 @@
 
 
 static int eap_sake_parse_add_attr(struct eap_sake_parse_attr *attr,
-				   const u8 *pos)
+				   u8 attr_id, u8 len, const u8 *data)
 {
 	size_t i;
 
-	switch (pos[0]) {
+	switch (attr_id) {
 	case EAP_SAKE_AT_RAND_S:
 		wpa_printf(MSG_DEBUG, "EAP-SAKE: Parse: AT_RAND_S");
-		if (pos[1] != 2 + EAP_SAKE_RAND_LEN) {
+		if (len != EAP_SAKE_RAND_LEN) {
 			wpa_printf(MSG_DEBUG, "EAP-SAKE: AT_RAND_S with "
-				   "invalid length %d", pos[1]);
+				   "invalid payload length %d", len);
 			return -1;
 		}
-		attr->rand_s = pos + 2;
+		attr->rand_s = data;
 		break;
 	case EAP_SAKE_AT_RAND_P:
 		wpa_printf(MSG_DEBUG, "EAP-SAKE: Parse: AT_RAND_P");
-		if (pos[1] != 2 + EAP_SAKE_RAND_LEN) {
+		if (len != EAP_SAKE_RAND_LEN) {
 			wpa_printf(MSG_DEBUG, "EAP-SAKE: AT_RAND_P with "
-				   "invalid length %d", pos[1]);
+				   "invalid payload length %d", len);
 			return -1;
 		}
-		attr->rand_p = pos + 2;
+		attr->rand_p = data;
 		break;
 	case EAP_SAKE_AT_MIC_S:
 		wpa_printf(MSG_DEBUG, "EAP-SAKE: Parse: AT_MIC_S");
-		if (pos[1] != 2 + EAP_SAKE_MIC_LEN) {
+		if (len != EAP_SAKE_MIC_LEN) {
 			wpa_printf(MSG_DEBUG, "EAP-SAKE: AT_MIC_S with "
-				   "invalid length %d", pos[1]);
+				   "invalid payload length %d", len);
 			return -1;
 		}
-		attr->mic_s = pos + 2;
+		attr->mic_s = data;
 		break;
 	case EAP_SAKE_AT_MIC_P:
 		wpa_printf(MSG_DEBUG, "EAP-SAKE: Parse: AT_MIC_P");
-		if (pos[1] != 2 + EAP_SAKE_MIC_LEN) {
+		if (len != EAP_SAKE_MIC_LEN) {
 			wpa_printf(MSG_DEBUG, "EAP-SAKE: AT_MIC_P with "
-				   "invalid length %d", pos[1]);
+				   "invalid payload length %d", len);
 			return -1;
 		}
-		attr->mic_p = pos + 2;
+		attr->mic_p = data;
 		break;
 	case EAP_SAKE_AT_SERVERID:
 		wpa_printf(MSG_DEBUG, "EAP-SAKE: Parse: AT_SERVERID");
-		attr->serverid = pos + 2;
-		attr->serverid_len = pos[1] - 2;
+		attr->serverid = data;
+		attr->serverid_len = len;
 		break;
 	case EAP_SAKE_AT_PEERID:
 		wpa_printf(MSG_DEBUG, "EAP-SAKE: Parse: AT_PEERID");
-		attr->peerid = pos + 2;
-		attr->peerid_len = pos[1] - 2;
+		attr->peerid = data;
+		attr->peerid_len = len;
 		break;
 	case EAP_SAKE_AT_SPI_S:
 		wpa_printf(MSG_DEBUG, "EAP-SAKE: Parse: AT_SPI_S");
-		attr->spi_s = pos + 2;
-		attr->spi_s_len = pos[1] - 2;
+		attr->spi_s = data;
+		attr->spi_s_len = len;
 		break;
 	case EAP_SAKE_AT_SPI_P:
 		wpa_printf(MSG_DEBUG, "EAP-SAKE: Parse: AT_SPI_P");
-		attr->spi_p = pos + 2;
-		attr->spi_p_len = pos[1] - 2;
+		attr->spi_p = data;
+		attr->spi_p_len = len;
 		break;
 	case EAP_SAKE_AT_ANY_ID_REQ:
 		wpa_printf(MSG_DEBUG, "EAP-SAKE: Parse: AT_ANY_ID_REQ");
-		if (pos[1] != 4) {
+		if (len != 2) {
 			wpa_printf(MSG_DEBUG, "EAP-SAKE: Invalid AT_ANY_ID_REQ"
-				   " length %d", pos[1]);
+				   " payload length %d", len);
 			return -1;
 		}
-		attr->any_id_req = pos + 2;
+		attr->any_id_req = data;
 		break;
 	case EAP_SAKE_AT_PERM_ID_REQ:
 		wpa_printf(MSG_DEBUG, "EAP-SAKE: Parse: AT_PERM_ID_REQ");
-		if (pos[1] != 4) {
+		if (len != 2) {
 			wpa_printf(MSG_DEBUG, "EAP-SAKE: Invalid "
-				   "AT_PERM_ID_REQ length %d", pos[1]);
+				   "AT_PERM_ID_REQ payload length %d", len);
 			return -1;
 		}
-		attr->perm_id_req = pos + 2;
+		attr->perm_id_req = data;
 		break;
 	case EAP_SAKE_AT_ENCR_DATA:
 		wpa_printf(MSG_DEBUG, "EAP-SAKE: Parse: AT_ENCR_DATA");
-		attr->encr_data = pos + 2;
-		attr->encr_data_len = pos[1] - 2;
+		attr->encr_data = data;
+		attr->encr_data_len = len;
 		break;
 	case EAP_SAKE_AT_IV:
 		wpa_printf(MSG_DEBUG, "EAP-SAKE: Parse: AT_IV");
-		attr->iv = pos + 2;
-		attr->iv_len = pos[1] - 2;
+		attr->iv = data;
+		attr->iv_len = len;
 		break;
 	case EAP_SAKE_AT_PADDING:
 		wpa_printf(MSG_DEBUG, "EAP-SAKE: Parse: AT_PADDING");
-		for (i = 2; i < pos[1]; i++) {
-			if (pos[i]) {
+		for (i = 0; i < len; i++) {
+			if (data[i]) {
 				wpa_printf(MSG_DEBUG, "EAP-SAKE: AT_PADDING "
 					   "with non-zero pad byte");
 				return -1;
@@ -117,26 +117,26 @@
 		break;
 	case EAP_SAKE_AT_NEXT_TMPID:
 		wpa_printf(MSG_DEBUG, "EAP-SAKE: Parse: AT_NEXT_TMPID");
-		attr->next_tmpid = pos + 2;
-		attr->next_tmpid_len = pos[1] - 2;
+		attr->next_tmpid = data;
+		attr->next_tmpid_len = len;
 		break;
 	case EAP_SAKE_AT_MSK_LIFE:
 		wpa_printf(MSG_DEBUG, "EAP-SAKE: Parse: AT_IV");
-		if (pos[1] != 6) {
+		if (len != 4) {
 			wpa_printf(MSG_DEBUG, "EAP-SAKE: Invalid "
-				   "AT_MSK_LIFE length %d", pos[1]);
+				   "AT_MSK_LIFE payload length %d", len);
 			return -1;
 		}
-		attr->msk_life = pos + 2;
+		attr->msk_life = data;
 		break;
 	default:
-		if (pos[0] < 128) {
+		if (attr_id < 128) {
 			wpa_printf(MSG_DEBUG, "EAP-SAKE: Unknown non-skippable"
-				   " attribute %d", pos[0]);
+				   " attribute %d", attr_id);
 			return -1;
 		}
 		wpa_printf(MSG_DEBUG, "EAP-SAKE: Ignoring unknown skippable "
-			   "attribute %d", pos[0]);
+			   "attribute %d", attr_id);
 		break;
 	}
 
@@ -180,7 +180,7 @@
 			return -1;
 		}
 
-		if (eap_sake_parse_add_attr(attr, pos))
+		if (eap_sake_parse_add_attr(attr, pos[0], pos[1] - 2, pos + 2))
 			return -1;
 
 		pos += pos[1];
diff --git a/src/eap_common/ikev2_common.c b/src/eap_common/ikev2_common.c
index 3d4fb6f..d60358c 100644
--- a/src/eap_common/ikev2_common.c
+++ b/src/eap_common/ikev2_common.c
@@ -16,7 +16,7 @@
 #include "ikev2_common.h"
 
 
-static struct ikev2_integ_alg ikev2_integ_algs[] = {
+static const struct ikev2_integ_alg ikev2_integ_algs[] = {
 	{ AUTH_HMAC_SHA1_96, 20, 12 },
 	{ AUTH_HMAC_MD5_96, 16, 12 }
 };
@@ -24,7 +24,7 @@
 #define NUM_INTEG_ALGS ARRAY_SIZE(ikev2_integ_algs)
 
 
-static struct ikev2_prf_alg ikev2_prf_algs[] = {
+static const struct ikev2_prf_alg ikev2_prf_algs[] = {
 	{ PRF_HMAC_SHA1, 20, 20 },
 	{ PRF_HMAC_MD5, 16, 16 }
 };
@@ -32,7 +32,7 @@
 #define NUM_PRF_ALGS ARRAY_SIZE(ikev2_prf_algs)
 
 
-static struct ikev2_encr_alg ikev2_encr_algs[] = {
+static const struct ikev2_encr_alg ikev2_encr_algs[] = {
 	{ ENCR_AES_CBC, 16, 16 }, /* only 128-bit keys supported for now */
 	{ ENCR_3DES, 24, 8 }
 };
@@ -251,25 +251,29 @@
 	os_memset(payloads, 0, sizeof(*payloads));
 
 	while (next_payload != IKEV2_PAYLOAD_NO_NEXT_PAYLOAD) {
-		int plen, pdatalen;
+		unsigned int plen, pdatalen, left;
 		const u8 *pdata;
 		wpa_printf(MSG_DEBUG, "IKEV2: Processing payload %u",
 			   next_payload);
-		if (end - pos < (int) sizeof(*phdr)) {
+		if (end < pos)
+			return -1;
+		left = end - pos;
+		if (left < sizeof(*phdr)) {
 			wpa_printf(MSG_INFO, "IKEV2:   Too short message for "
 				   "payload header (left=%ld)",
 				   (long) (end - pos));
+			return -1;
 		}
 		phdr = (const struct ikev2_payload_hdr *) pos;
 		plen = WPA_GET_BE16(phdr->payload_length);
-		if (plen < (int) sizeof(*phdr) || pos + plen > end) {
+		if (plen < sizeof(*phdr) || plen > left) {
 			wpa_printf(MSG_INFO, "IKEV2:   Invalid payload header "
 				   "length %d", plen);
 			return -1;
 		}
 
 		wpa_printf(MSG_DEBUG, "IKEV2:   Next Payload: %u  Flags: 0x%x"
-			   "  Payload Length: %d",
+			   "  Payload Length: %u",
 			   phdr->next_payload, phdr->flags, plen);
 
 		pdata = (const u8 *) (phdr + 1);
diff --git a/src/eap_peer/Makefile b/src/eap_peer/Makefile
index f79519b..6531ccd 100644
--- a/src/eap_peer/Makefile
+++ b/src/eap_peer/Makefile
@@ -1,11 +1,23 @@
-all:
-	@echo Nothing to be made.
+all: libeap_peer.a
 
 clean:
-	rm -f *~ *.o *.so *.d *.gcno *.gcda *.gcov
+	rm -f *~ *.o *.so *.d *.gcno *.gcda *.gcov libeap_peer.a
 
 install:
 	if ls *.so >/dev/null 2>&1; then \
 		install -d $(DESTDIR)$(LIBDIR)/wpa_supplicant && \
 		cp *.so $(DESTDIR)$(LIBDIR)/wpa_supplicant \
 	; fi
+
+include ../lib.rules
+
+CFLAGS += -DIEEE8021X_EAPOL
+
+LIB_OBJS= \
+	eap.o \
+	eap_methods.o
+
+libeap_peer.a: $(LIB_OBJS)
+	$(AR) crT $@ $?
+
+-include $(OBJS:%.o=%.d)
diff --git a/src/eap_peer/eap.c b/src/eap_peer/eap.c
index 9880d3b..1dbe003 100644
--- a/src/eap_peer/eap.c
+++ b/src/eap_peer/eap.c
@@ -23,6 +23,7 @@
 #include "ext_password.h"
 #include "crypto/crypto.h"
 #include "crypto/tls.h"
+#include "crypto/sha256.h"
 #include "common/wpa_ctrl.h"
 #include "eap_common/eap_wsc_common.h"
 #include "eap_i.h"
@@ -190,6 +191,8 @@
 	sm->num_rounds = 0;
 	sm->prev_failure = 0;
 	sm->expected_failure = 0;
+	sm->reauthInit = FALSE;
+	sm->erp_seq = (u32) -1;
 }
 
 
@@ -353,6 +356,267 @@
 }
 
 
+#ifdef CONFIG_ERP
+
+static char * eap_home_realm(struct eap_sm *sm)
+{
+	struct eap_peer_config *config = eap_get_config(sm);
+	char *realm;
+	size_t i, realm_len;
+
+	if (!config)
+		return NULL;
+
+	if (config->identity) {
+		for (i = 0; i < config->identity_len; i++) {
+			if (config->identity[i] == '@')
+				break;
+		}
+		if (i < config->identity_len) {
+			realm_len = config->identity_len - i - 1;
+			realm = os_malloc(realm_len + 1);
+			if (realm == NULL)
+				return NULL;
+			os_memcpy(realm, &config->identity[i + 1], realm_len);
+			realm[realm_len] = '\0';
+			return realm;
+		}
+	}
+
+	if (config->anonymous_identity) {
+		for (i = 0; i < config->anonymous_identity_len; i++) {
+			if (config->anonymous_identity[i] == '@')
+				break;
+		}
+		if (i < config->anonymous_identity_len) {
+			realm_len = config->anonymous_identity_len - i - 1;
+			realm = os_malloc(realm_len + 1);
+			if (realm == NULL)
+				return NULL;
+			os_memcpy(realm, &config->anonymous_identity[i + 1],
+				  realm_len);
+			realm[realm_len] = '\0';
+			return realm;
+		}
+	}
+
+	return os_strdup("");
+}
+
+
+static struct eap_erp_key *
+eap_erp_get_key(struct eap_sm *sm, const char *realm)
+{
+	struct eap_erp_key *erp;
+
+	dl_list_for_each(erp, &sm->erp_keys, struct eap_erp_key, list) {
+		char *pos;
+
+		pos = os_strchr(erp->keyname_nai, '@');
+		if (!pos)
+			continue;
+		pos++;
+		if (os_strcmp(pos, realm) == 0)
+			return erp;
+	}
+
+	return NULL;
+}
+
+
+static struct eap_erp_key *
+eap_erp_get_key_nai(struct eap_sm *sm, const char *nai)
+{
+	struct eap_erp_key *erp;
+
+	dl_list_for_each(erp, &sm->erp_keys, struct eap_erp_key, list) {
+		if (os_strcmp(erp->keyname_nai, nai) == 0)
+			return erp;
+	}
+
+	return NULL;
+}
+
+
+static void eap_peer_erp_free_key(struct eap_erp_key *erp)
+{
+	dl_list_del(&erp->list);
+	bin_clear_free(erp, sizeof(*erp));
+}
+
+
+static void eap_erp_remove_keys_realm(struct eap_sm *sm, const char *realm)
+{
+	struct eap_erp_key *erp;
+
+	while ((erp = eap_erp_get_key(sm, realm)) != NULL) {
+		wpa_printf(MSG_DEBUG, "EAP: Delete old ERP key %s",
+			   erp->keyname_nai);
+		eap_peer_erp_free_key(erp);
+	}
+}
+
+#endif /* CONFIG_ERP */
+
+
+void eap_peer_erp_free_keys(struct eap_sm *sm)
+{
+#ifdef CONFIG_ERP
+	struct eap_erp_key *erp, *tmp;
+
+	dl_list_for_each_safe(erp, tmp, &sm->erp_keys, struct eap_erp_key, list)
+		eap_peer_erp_free_key(erp);
+#endif /* CONFIG_ERP */
+}
+
+
+static void eap_peer_erp_init(struct eap_sm *sm)
+{
+#ifdef CONFIG_ERP
+	u8 *emsk = NULL;
+	size_t emsk_len = 0;
+	u8 EMSKname[EAP_EMSK_NAME_LEN];
+	u8 len[2];
+	char *realm;
+	size_t realm_len, nai_buf_len;
+	struct eap_erp_key *erp = NULL;
+	int pos;
+
+	realm = eap_home_realm(sm);
+	if (!realm)
+		return;
+	realm_len = os_strlen(realm);
+	wpa_printf(MSG_DEBUG, "EAP: Realm for ERP keyName-NAI: %s", realm);
+	eap_erp_remove_keys_realm(sm, realm);
+
+	nai_buf_len = 2 * EAP_EMSK_NAME_LEN + 1 + realm_len;
+	if (nai_buf_len > 253) {
+		/*
+		 * keyName-NAI has a maximum length of 253 octet to fit in
+		 * RADIUS attributes.
+		 */
+		wpa_printf(MSG_DEBUG,
+			   "EAP: Too long realm for ERP keyName-NAI maximum length");
+		goto fail;
+	}
+	nai_buf_len++; /* null termination */
+	erp = os_zalloc(sizeof(*erp) + nai_buf_len);
+	if (erp == NULL)
+		goto fail;
+
+	emsk = sm->m->get_emsk(sm, sm->eap_method_priv, &emsk_len);
+	if (!emsk || emsk_len == 0 || emsk_len > ERP_MAX_KEY_LEN) {
+		wpa_printf(MSG_DEBUG,
+			   "EAP: No suitable EMSK available for ERP");
+		goto fail;
+	}
+
+	wpa_hexdump_key(MSG_DEBUG, "EAP: EMSK", emsk, emsk_len);
+
+	WPA_PUT_BE16(len, 8);
+	if (hmac_sha256_kdf(sm->eapSessionId, sm->eapSessionIdLen, "EMSK",
+			    len, sizeof(len),
+			    EMSKname, EAP_EMSK_NAME_LEN) < 0) {
+		wpa_printf(MSG_DEBUG, "EAP: Could not derive EMSKname");
+		goto fail;
+	}
+	wpa_hexdump(MSG_DEBUG, "EAP: EMSKname", EMSKname, EAP_EMSK_NAME_LEN);
+
+	pos = wpa_snprintf_hex(erp->keyname_nai, nai_buf_len,
+			       EMSKname, EAP_EMSK_NAME_LEN);
+	erp->keyname_nai[pos] = '@';
+	os_memcpy(&erp->keyname_nai[pos + 1], realm, realm_len);
+
+	WPA_PUT_BE16(len, emsk_len);
+	if (hmac_sha256_kdf(emsk, emsk_len,
+			    "EAP Re-authentication Root Key@ietf.org",
+			    len, sizeof(len), erp->rRK, emsk_len) < 0) {
+		wpa_printf(MSG_DEBUG, "EAP: Could not derive rRK for ERP");
+		goto fail;
+	}
+	erp->rRK_len = emsk_len;
+	wpa_hexdump_key(MSG_DEBUG, "EAP: ERP rRK", erp->rRK, erp->rRK_len);
+
+	if (hmac_sha256_kdf(erp->rRK, erp->rRK_len,
+			    "EAP Re-authentication Integrity Key@ietf.org",
+			    len, sizeof(len), erp->rIK, erp->rRK_len) < 0) {
+		wpa_printf(MSG_DEBUG, "EAP: Could not derive rIK for ERP");
+		goto fail;
+	}
+	erp->rIK_len = erp->rRK_len;
+	wpa_hexdump_key(MSG_DEBUG, "EAP: ERP rIK", erp->rIK, erp->rIK_len);
+
+	wpa_printf(MSG_DEBUG, "EAP: Stored ERP keys %s", erp->keyname_nai);
+	dl_list_add(&sm->erp_keys, &erp->list);
+	erp = NULL;
+fail:
+	bin_clear_free(emsk, emsk_len);
+	bin_clear_free(erp, sizeof(*erp));
+	os_free(realm);
+#endif /* CONFIG_ERP */
+}
+
+
+#ifdef CONFIG_ERP
+static int eap_peer_erp_reauth_start(struct eap_sm *sm,
+				     const struct eap_hdr *hdr, size_t len)
+{
+	char *realm;
+	struct eap_erp_key *erp;
+	struct wpabuf *msg;
+	u8 hash[SHA256_MAC_LEN];
+
+	realm = eap_home_realm(sm);
+	if (!realm)
+		return -1;
+
+	erp = eap_erp_get_key(sm, realm);
+	os_free(realm);
+	realm = NULL;
+	if (!erp)
+		return -1;
+
+	if (erp->next_seq >= 65536)
+		return -1; /* SEQ has range of 0..65535 */
+
+	/* TODO: check rRK lifetime expiration */
+
+	wpa_printf(MSG_DEBUG, "EAP: Valid ERP key found %s (SEQ=%u)",
+		   erp->keyname_nai, erp->next_seq);
+
+	msg = eap_msg_alloc(EAP_VENDOR_IETF, EAP_ERP_TYPE_REAUTH,
+			    1 + 2 + 2 + os_strlen(erp->keyname_nai) + 1 + 16,
+			    EAP_CODE_INITIATE, hdr->identifier);
+	if (msg == NULL)
+		return -1;
+
+	wpabuf_put_u8(msg, 0x20); /* Flags: R=0 B=0 L=1 */
+	wpabuf_put_be16(msg, erp->next_seq);
+
+	wpabuf_put_u8(msg, EAP_ERP_TLV_KEYNAME_NAI);
+	wpabuf_put_u8(msg, os_strlen(erp->keyname_nai));
+	wpabuf_put_str(msg, erp->keyname_nai);
+
+	wpabuf_put_u8(msg, EAP_ERP_CS_HMAC_SHA256_128); /* Cryptosuite */
+
+	if (hmac_sha256(erp->rIK, erp->rIK_len,
+			wpabuf_head(msg), wpabuf_len(msg), hash) < 0) {
+		wpabuf_free(msg);
+		return -1;
+	}
+	wpabuf_put_data(msg, hash, 16);
+
+	wpa_printf(MSG_DEBUG, "EAP: Sending EAP-Initiate/Re-auth");
+	sm->erp_seq = erp->next_seq;
+	erp->next_seq++;
+	wpabuf_free(sm->eapRespData);
+	sm->eapRespData = msg;
+	sm->reauthInit = TRUE;
+	return 0;
+}
+#endif /* CONFIG_ERP */
+
+
 /*
  * The method processing happens here. The request from the authenticator is
  * processed, and an appropriate response packet is built.
@@ -414,6 +678,8 @@
 
 	if (sm->m->isKeyAvailable && sm->m->getKey &&
 	    sm->m->isKeyAvailable(sm, sm->eap_method_priv)) {
+		struct eap_peer_config *config = eap_get_config(sm);
+
 		eap_sm_free_key(sm);
 		sm->eapKeyData = sm->m->getKey(sm, sm->eap_method_priv,
 					       &sm->eapKeyDataLen);
@@ -426,6 +692,8 @@
 			wpa_hexdump(MSG_DEBUG, "EAP: Session-Id",
 				    sm->eapSessionId, sm->eapSessionIdLen);
 		}
+		if (config->erp && sm->m->get_emsk && sm->eapSessionId)
+			eap_peer_erp_init(sm);
 	}
 }
 
@@ -450,6 +718,7 @@
 	}
 	eapol_set_bool(sm, EAPOL_eapReq, FALSE);
 	eapol_set_int(sm, EAPOL_idleWhile, sm->ClientTimeout);
+	sm->reauthInit = FALSE;
 }
 
 
@@ -665,6 +934,15 @@
 }
 
 
+static int eap_peer_sm_allow_canned(struct eap_sm *sm)
+{
+	struct eap_peer_config *config = eap_get_config(sm);
+
+	return config && config->phase1 &&
+		os_strstr(config->phase1, "allow_canned_success=1");
+}
+
+
 static void eap_peer_sm_step_received(struct eap_sm *sm)
 {
 	int duplicate = eap_peer_req_is_duplicate(sm);
@@ -678,6 +956,17 @@
 	    (sm->reqId == sm->lastId ||
 	     eap_success_workaround(sm, sm->reqId, sm->lastId)))
 		SM_ENTER(EAP, SUCCESS);
+	else if (sm->workaround && sm->lastId == -1 && sm->rxSuccess &&
+		 !sm->rxFailure && !sm->rxReq && eap_peer_sm_allow_canned(sm))
+		SM_ENTER(EAP, SUCCESS); /* EAP-Success prior any EAP method */
+	else if (sm->workaround && sm->lastId == -1 && sm->rxFailure &&
+		 !sm->rxReq && sm->methodState != METHOD_CONT &&
+		 eap_peer_sm_allow_canned(sm))
+		SM_ENTER(EAP, FAILURE); /* EAP-Failure prior any EAP method */
+	else if (sm->workaround && sm->rxSuccess && !sm->rxFailure &&
+		 !sm->rxReq && sm->methodState != METHOD_CONT &&
+		 eap_peer_sm_allow_canned(sm))
+		SM_ENTER(EAP, SUCCESS); /* EAP-Success after Identity */
 	else if (sm->methodState != METHOD_CONT &&
 		 ((sm->rxFailure &&
 		   sm->decision != DECISION_UNCOND_SUCC) ||
@@ -709,6 +998,8 @@
 	else if (sm->selectedMethod == EAP_TYPE_LEAP &&
 		 (sm->rxSuccess || sm->rxResp))
 		SM_ENTER(EAP, METHOD);
+	else if (sm->reauthInit)
+		SM_ENTER(EAP, SEND_RESPONSE);
 	else
 		SM_ENTER(EAP, DISCARD);
 }
@@ -1231,6 +1522,219 @@
 }
 
 
+static void eap_peer_initiate(struct eap_sm *sm, const struct eap_hdr *hdr,
+			      size_t len)
+{
+#ifdef CONFIG_ERP
+	const u8 *pos = (const u8 *) (hdr + 1);
+	const u8 *end = ((const u8 *) hdr) + len;
+	struct erp_tlvs parse;
+
+	if (len < sizeof(*hdr) + 1) {
+		wpa_printf(MSG_DEBUG, "EAP: Ignored too short EAP-Initiate");
+		return;
+	}
+
+	if (*pos != EAP_ERP_TYPE_REAUTH_START) {
+		wpa_printf(MSG_DEBUG,
+			   "EAP: Ignored unexpected EAP-Initiate Type=%u",
+			   *pos);
+		return;
+	}
+
+	pos++;
+	if (pos >= end) {
+		wpa_printf(MSG_DEBUG,
+			   "EAP: Too short EAP-Initiate/Re-auth-Start");
+		return;
+	}
+	pos++; /* Reserved */
+	wpa_hexdump(MSG_DEBUG, "EAP: EAP-Initiate/Re-auth-Start TVs/TLVs",
+		    pos, end - pos);
+
+	if (erp_parse_tlvs(pos, end, &parse, 0) < 0)
+		goto invalid;
+
+	if (parse.domain) {
+		wpa_hexdump_ascii(MSG_DEBUG,
+				  "EAP: EAP-Initiate/Re-auth-Start - Domain name",
+				  parse.domain, parse.domain_len);
+		/* TODO: Derivation of domain specific keys for local ER */
+	}
+
+	if (eap_peer_erp_reauth_start(sm, hdr, len) == 0)
+		return;
+
+invalid:
+#endif /* CONFIG_ERP */
+	wpa_printf(MSG_DEBUG,
+		   "EAP: EAP-Initiate/Re-auth-Start - No suitable ERP keys available - try to start full EAP authentication");
+	eapol_set_bool(sm, EAPOL_eapTriggerStart, TRUE);
+}
+
+
+static void eap_peer_finish(struct eap_sm *sm, const struct eap_hdr *hdr,
+			    size_t len)
+{
+#ifdef CONFIG_ERP
+	const u8 *pos = (const u8 *) (hdr + 1);
+	const u8 *end = ((const u8 *) hdr) + len;
+	const u8 *start;
+	struct erp_tlvs parse;
+	u8 flags;
+	u16 seq;
+	u8 hash[SHA256_MAC_LEN];
+	size_t hash_len;
+	struct eap_erp_key *erp;
+	int max_len;
+	char nai[254];
+	u8 seed[4];
+	int auth_tag_ok = 0;
+
+	if (len < sizeof(*hdr) + 1) {
+		wpa_printf(MSG_DEBUG, "EAP: Ignored too short EAP-Finish");
+		return;
+	}
+
+	if (*pos != EAP_ERP_TYPE_REAUTH) {
+		wpa_printf(MSG_DEBUG,
+			   "EAP: Ignored unexpected EAP-Finish Type=%u", *pos);
+		return;
+	}
+
+	if (len < sizeof(*hdr) + 4) {
+		wpa_printf(MSG_DEBUG,
+			   "EAP: Ignored too short EAP-Finish/Re-auth");
+		return;
+	}
+
+	pos++;
+	flags = *pos++;
+	seq = WPA_GET_BE16(pos);
+	pos += 2;
+	wpa_printf(MSG_DEBUG, "EAP: Flags=0x%x SEQ=%u", flags, seq);
+
+	if (seq != sm->erp_seq) {
+		wpa_printf(MSG_DEBUG,
+			   "EAP: Unexpected EAP-Finish/Re-auth SEQ=%u", seq);
+		return;
+	}
+
+	/*
+	 * Parse TVs/TLVs. Since we do not yet know the length of the
+	 * Authentication Tag, stop parsing if an unknown TV/TLV is seen and
+	 * just try to find the keyName-NAI first so that we can check the
+	 * Authentication Tag.
+	 */
+	if (erp_parse_tlvs(pos, end, &parse, 1) < 0)
+		return;
+
+	if (!parse.keyname) {
+		wpa_printf(MSG_DEBUG,
+			   "EAP: No keyName-NAI in EAP-Finish/Re-auth Packet");
+		return;
+	}
+
+	wpa_hexdump_ascii(MSG_DEBUG, "EAP: EAP-Finish/Re-auth - keyName-NAI",
+			  parse.keyname, parse.keyname_len);
+	if (parse.keyname_len > 253) {
+		wpa_printf(MSG_DEBUG,
+			   "EAP: Too long keyName-NAI in EAP-Finish/Re-auth");
+		return;
+	}
+	os_memcpy(nai, parse.keyname, parse.keyname_len);
+	nai[parse.keyname_len] = '\0';
+
+	erp = eap_erp_get_key_nai(sm, nai);
+	if (!erp) {
+		wpa_printf(MSG_DEBUG, "EAP: No matching ERP key found for %s",
+			   nai);
+		return;
+	}
+
+	/* Is there enough room for Cryptosuite and Authentication Tag? */
+	start = parse.keyname + parse.keyname_len;
+	max_len = end - start;
+	hash_len = 16;
+	if (max_len < 1 + (int) hash_len) {
+		wpa_printf(MSG_DEBUG,
+			   "EAP: Not enough room for Authentication Tag");
+		if (flags & 0x80)
+			goto no_auth_tag;
+		return;
+	}
+	if (end[-17] != EAP_ERP_CS_HMAC_SHA256_128) {
+		wpa_printf(MSG_DEBUG, "EAP: Different Cryptosuite used");
+		if (flags & 0x80)
+			goto no_auth_tag;
+		return;
+	}
+
+	if (hmac_sha256(erp->rIK, erp->rIK_len, (const u8 *) hdr,
+			end - ((const u8 *) hdr) - hash_len, hash) < 0)
+		return;
+	if (os_memcmp(end - hash_len, hash, hash_len) != 0) {
+		wpa_printf(MSG_DEBUG,
+			   "EAP: Authentication Tag mismatch");
+		return;
+	}
+	auth_tag_ok = 1;
+	end -= 1 + hash_len;
+
+no_auth_tag:
+	/*
+	 * Parse TVs/TLVs again now that we know the exact part of the buffer
+	 * that contains them.
+	 */
+	wpa_hexdump(MSG_DEBUG, "EAP: EAP-Finish/Re-Auth TVs/TLVs",
+		    pos, end - pos);
+	if (erp_parse_tlvs(pos, end, &parse, 0) < 0)
+		return;
+
+	if (flags & 0x80 || !auth_tag_ok) {
+		wpa_printf(MSG_DEBUG,
+			   "EAP: EAP-Finish/Re-auth indicated failure");
+		eapol_set_bool(sm, EAPOL_eapFail, TRUE);
+		eapol_set_bool(sm, EAPOL_eapReq, FALSE);
+		eapol_set_bool(sm, EAPOL_eapNoResp, TRUE);
+		wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_FAILURE
+			"EAP authentication failed");
+		sm->prev_failure = 1;
+		wpa_printf(MSG_DEBUG,
+			   "EAP: Drop ERP key to try full authentication on next attempt");
+		eap_peer_erp_free_key(erp);
+		return;
+	}
+
+	eap_sm_free_key(sm);
+	sm->eapKeyDataLen = 0;
+	sm->eapKeyData = os_malloc(erp->rRK_len);
+	if (!sm->eapKeyData)
+		return;
+	sm->eapKeyDataLen = erp->rRK_len;
+
+	WPA_PUT_BE16(seed, seq);
+	WPA_PUT_BE16(&seed[2], erp->rRK_len);
+	if (hmac_sha256_kdf(erp->rRK, erp->rRK_len,
+			    "Re-authentication Master Session Key@ietf.org",
+			    seed, sizeof(seed),
+			    sm->eapKeyData, erp->rRK_len) < 0) {
+		wpa_printf(MSG_DEBUG, "EAP: Could not derive rMSK for ERP");
+		eap_sm_free_key(sm);
+		return;
+	}
+	wpa_hexdump_key(MSG_DEBUG, "EAP: ERP rMSK",
+			sm->eapKeyData, sm->eapKeyDataLen);
+	sm->eapKeyAvailable = TRUE;
+	eapol_set_bool(sm, EAPOL_eapSuccess, TRUE);
+	eapol_set_bool(sm, EAPOL_eapReq, FALSE);
+	eapol_set_bool(sm, EAPOL_eapNoResp, TRUE);
+	wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_SUCCESS
+		"EAP re-authentication completed successfully");
+#endif /* CONFIG_ERP */
+}
+
+
 static void eap_sm_parseEapReq(struct eap_sm *sm, const struct wpabuf *req)
 {
 	const struct eap_hdr *hdr;
@@ -1322,6 +1826,12 @@
 		eap_notify_status(sm, "completion", "failure");
 		sm->rxFailure = TRUE;
 		break;
+	case EAP_CODE_INITIATE:
+		eap_peer_initiate(sm, hdr, plen);
+		break;
+	case EAP_CODE_FINISH:
+		eap_peer_finish(sm, hdr, plen);
+		break;
 	default:
 		wpa_printf(MSG_DEBUG, "EAP: Ignored EAP-Packet with unknown "
 			   "code %d", hdr->code);
@@ -1368,6 +1878,8 @@
 		sm->eapol_cb->notify_cert(sm->eapol_ctx,
 					  data->peer_cert.depth,
 					  data->peer_cert.subject,
+					  data->peer_cert.altsubject,
+					  data->peer_cert.num_altsubject,
 					  hash_hex, data->peer_cert.cert);
 		break;
 	case TLS_ALERT:
@@ -1399,7 +1911,7 @@
  * structure remains alive while the EAP state machine is active.
  */
 struct eap_sm * eap_peer_sm_init(void *eapol_ctx,
-				 struct eapol_callbacks *eapol_cb,
+				 const struct eapol_callbacks *eapol_cb,
 				 void *msg_ctx, struct eap_config *conf)
 {
 	struct eap_sm *sm;
@@ -1413,11 +1925,13 @@
 	sm->msg_ctx = msg_ctx;
 	sm->ClientTimeout = EAP_CLIENT_TIMEOUT_DEFAULT;
 	sm->wps = conf->wps;
+	dl_list_init(&sm->erp_keys);
 
 	os_memset(&tlsconf, 0, sizeof(tlsconf));
 	tlsconf.opensc_engine_path = conf->opensc_engine_path;
 	tlsconf.pkcs11_engine_path = conf->pkcs11_engine_path;
 	tlsconf.pkcs11_module_path = conf->pkcs11_module_path;
+	tlsconf.openssl_ciphers = conf->openssl_ciphers;
 #ifdef CONFIG_FIPS
 	tlsconf.fips_mode = 1;
 #endif /* CONFIG_FIPS */
@@ -1459,6 +1973,7 @@
 	if (sm->ssl_ctx2)
 		tls_deinit(sm->ssl_ctx2);
 	tls_deinit(sm->ssl_ctx);
+	eap_peer_erp_free_keys(sm);
 	os_free(sm);
 }
 
@@ -1607,7 +2122,7 @@
 	len = os_snprintf(buf, buflen,
 			  "EAP state=%s\n",
 			  eap_sm_state_txt(sm->EAP_state));
-	if (len < 0 || (size_t) len >= buflen)
+	if (os_snprintf_error(buflen, len))
 		return 0;
 
 	if (sm->selectedMethod != EAP_TYPE_NONE) {
@@ -1626,7 +2141,7 @@
 		ret = os_snprintf(buf + len, buflen - len,
 				  "selectedMethod=%d (EAP-%s)\n",
 				  sm->selectedMethod, name);
-		if (ret < 0 || (size_t) ret >= buflen - len)
+		if (os_snprintf_error(buflen - len, ret))
 			return len;
 		len += ret;
 
@@ -1647,7 +2162,7 @@
 				  eap_sm_method_state_txt(sm->methodState),
 				  eap_sm_decision_txt(sm->decision),
 				  sm->ClientTimeout);
-		if (ret < 0 || (size_t) ret >= buflen - len)
+		if (os_snprintf_error(buflen - len, ret))
 			return len;
 		len += ret;
 	}
@@ -1885,7 +2400,7 @@
 u32 eap_get_phase2_type(const char *name, int *vendor)
 {
 	int v;
-	u8 type = eap_peer_get_type(name, &v);
+	u32 type = eap_peer_get_type(name, &v);
 	if (eap_allowed_phase2_type(v, type)) {
 		*vendor = v;
 		return type;
diff --git a/src/eap_peer/eap.h b/src/eap_peer/eap.h
index 712e929..1a645af 100644
--- a/src/eap_peer/eap.h
+++ b/src/eap_peer/eap.h
@@ -94,7 +94,14 @@
 	 *
 	 * EAP state machines reads this value.
 	 */
-	EAPOL_altReject
+	EAPOL_altReject,
+
+	/**
+	 * EAPOL_eapTriggerStart - EAP-based trigger to send EAPOL-Start
+	 *
+	 * EAP state machine writes this value.
+	 */
+	EAPOL_eapTriggerStart
 };
 
 /**
@@ -221,10 +228,13 @@
 	 * @ctx: eapol_ctx from eap_peer_sm_init() call
 	 * @depth: Depth in certificate chain (0 = server)
 	 * @subject: Subject of the peer certificate
+	 * @altsubject: Select fields from AltSubject of the peer certificate
+	 * @num_altsubject: Number of altsubject values
 	 * @cert_hash: SHA-256 hash of the certificate
 	 * @cert: Peer certificate
 	 */
 	void (*notify_cert)(void *ctx, int depth, const char *subject,
+			    const char *altsubject[], int num_altsubject,
 			    const char *cert_hash, const struct wpabuf *cert);
 
 	/**
@@ -236,6 +246,14 @@
 	void (*notify_status)(void *ctx, const char *status,
 			      const char *parameter);
 
+#ifdef CONFIG_EAP_PROXY
+	/**
+	 * eap_proxy_cb - Callback signifying any updates from eap_proxy
+	 * @ctx: eapol_ctx from eap_peer_sm_init() call
+	 */
+	void (*eap_proxy_cb)(void *ctx);
+#endif /* CONFIG_EAP_PROXY */
+
 	/**
 	 * set_anon_id - Set or add anonymous identity
 	 * @ctx: eapol_ctx from eap_peer_sm_init() call
@@ -268,6 +286,14 @@
 	 */
 	const char *pkcs11_module_path;
 	/**
+	 * openssl_ciphers - OpenSSL cipher string
+	 *
+	 * This is an OpenSSL specific configuration option for configuring the
+	 * default ciphers. If not set, "DEFAULT:!EXP:!LOW" is used as the
+	 * default.
+	 */
+	const char *openssl_ciphers;
+	/**
 	 * wps - WPS context data
 	 *
 	 * This is only used by EAP-WSC and can be left %NULL if not available.
@@ -281,7 +307,7 @@
 };
 
 struct eap_sm * eap_peer_sm_init(void *eapol_ctx,
-				 struct eapol_callbacks *eapol_cb,
+				 const struct eapol_callbacks *eapol_cb,
 				 void *msg_ctx, struct eap_config *conf);
 void eap_peer_sm_deinit(struct eap_sm *sm);
 int eap_peer_sm_step(struct eap_sm *sm);
@@ -321,6 +347,7 @@
 void eap_sm_set_ext_pw_ctx(struct eap_sm *sm, struct ext_password_data *ext);
 void eap_set_anon_id(struct eap_sm *sm, const u8 *id, size_t len);
 int eap_peer_was_failure_expected(struct eap_sm *sm);
+void eap_peer_erp_free_keys(struct eap_sm *sm);
 
 #endif /* IEEE8021X_EAPOL */
 
diff --git a/src/eap_peer/eap_aka.c b/src/eap_peer/eap_aka.c
index 0662ae7..dc9e8cc 100644
--- a/src/eap_peer/eap_aka.c
+++ b/src/eap_peer/eap_aka.c
@@ -1296,7 +1296,7 @@
 
 	pos = eap_hdr_validate(EAP_VENDOR_IETF, data->eap_method, reqData,
 			       &len);
-	if (pos == NULL || len < 1) {
+	if (pos == NULL || len < 3) {
 		ret->ignore = TRUE;
 		return NULL;
 	}
diff --git a/src/eap_peer/eap_config.h b/src/eap_peer/eap_config.h
index 2591e11..2b1a1d5 100644
--- a/src/eap_peer/eap_config.h
+++ b/src/eap_peer/eap_config.h
@@ -186,6 +186,10 @@
 	 * string is in following format:
 	 *
 	 * /C=US/ST=CA/L=San Francisco/CN=Test AS/emailAddress=as@n.example.com
+	 *
+	 * Note: Since this is a substring match, this cannot be used securily
+	 * to do a suffix match against a possible domain name in the CN entry.
+	 * For such a use case, domain_suffix_match should be used instead.
 	 */
 	u8 *subject_match;
 
@@ -213,7 +217,7 @@
 	 * If set, this FQDN is used as a suffix match requirement for the
 	 * server certificate in SubjectAltName dNSName element(s). If a
 	 * matching dNSName is found, this constraint is met. If no dNSName
-	 * values are present, this constraint is matched against SubjetName CN
+	 * values are present, this constraint is matched against SubjectName CN
 	 * using same suffix match comparison. Suffix match here means that the
 	 * host/domain name is compared one label at a time starting from the
 	 * top-level domain and all the labels in domain_suffix_match shall be
@@ -226,6 +230,21 @@
 	char *domain_suffix_match;
 
 	/**
+	 * domain_match - Constraint for server domain name
+	 *
+	 * If set, this FQDN is used as a full match requirement for the
+	 * server certificate in SubjectAltName dNSName element(s). If a
+	 * matching dNSName is found, this constraint is met. If no dNSName
+	 * values are present, this constraint is matched against SubjectName CN
+	 * using same full match comparison. This behavior is similar to
+	 * domain_suffix_match, but has the requirement of a full match, i.e.,
+	 * no subdomains or wildcard matches are allowed. Case-insensitive
+	 * comparison is used, so "Example.com" matches "example.com", but would
+	 * not match "test.Example.com".
+	 */
+	char *domain_match;
+
+	/**
 	 * ca_cert2 - File path to CA certificate file (PEM/DER) (Phase 2)
 	 *
 	 * This file can have one or more trusted CA certificates. If ca_cert2
@@ -329,6 +348,14 @@
 	char *domain_suffix_match2;
 
 	/**
+	 * domain_match2 - Constraint for server domain name
+	 *
+	 * This field is like domain_match, but used for phase 2 (inside
+	 * EAP-TTLS/PEAP/FAST tunnel) authentication.
+	 */
+	char *domain_match2;
+
+	/**
 	 * eap_methods - Allowed EAP methods
 	 *
 	 * (vendor=EAP_VENDOR_IETF,method=EAP_TYPE_NONE) terminated list of
@@ -391,6 +418,16 @@
 	 *
 	 * EAP-WSC (WPS) uses following options: pin=Device_Password and
 	 * uuid=Device_UUID
+	 *
+	 * For wired IEEE 802.1X authentication, "allow_canned_success=1" can be
+	 * used to configure a mode that allows EAP-Success (and EAP-Failure)
+	 * without going through authentication step. Some switches use such
+	 * sequence when forcing the port to be authorized/unauthorized or as a
+	 * fallback option if the authentication server is unreachable. By
+	 * default, wpa_supplicant discards such frames to protect against
+	 * potential attacks by rogue devices, but this option can be used to
+	 * disable that protection for cases where the server/authenticator does
+	 * not need to be authenticated.
 	 */
 	char *phase1;
 
@@ -398,7 +435,9 @@
 	 * phase2 - Phase2 (inner authentication with TLS tunnel) parameters
 	 *
 	 * String with field-value pairs, e.g., "auth=MSCHAPV2" for EAP-PEAP or
-	 * "autheap=MSCHAPV2 autheap=MD5" for EAP-TTLS.
+	 * "autheap=MSCHAPV2 autheap=MD5" for EAP-TTLS. "mschapv2_retry=0" can
+	 * be used to disable MSCHAPv2 password retry in authentication failure
+	 * cases.
 	 */
 	char *phase2;
 
@@ -686,6 +725,20 @@
 	 * has more than one.
 	 */
 	int sim_num;
+
+	/**
+	 * openssl_ciphers - OpenSSL cipher string
+	 *
+	 * This is an OpenSSL specific configuration option for configuring the
+	 * ciphers for this connection. If not set, the default cipher suite
+	 * list is used.
+	 */
+	char *openssl_ciphers;
+
+	/**
+	 * erp - Whether EAP Re-authentication Protocol (ERP) is enabled
+	 */
+	int erp;
 };
 
 
diff --git a/src/eap_peer/eap_eke.c b/src/eap_peer/eap_eke.c
index 9fec66c..dfbda56 100644
--- a/src/eap_peer/eap_eke.c
+++ b/src/eap_peer/eap_eke.c
@@ -195,15 +195,14 @@
 
 static struct wpabuf * eap_eke_build_fail(struct eap_eke_data *data,
 					  struct eap_method_ret *ret,
-					  const struct wpabuf *reqData,
-					  u32 failure_code)
+					  u8 id, u32 failure_code)
 {
 	struct wpabuf *resp;
 
 	wpa_printf(MSG_DEBUG, "EAP-EKE: Sending EAP-EKE-Failure/Response - code=0x%x",
 		   failure_code);
 
-	resp = eap_eke_build_msg(data, eap_get_id(reqData), 4, EAP_EKE_FAILURE);
+	resp = eap_eke_build_msg(data, id, 4, EAP_EKE_FAILURE);
 	if (resp)
 		wpabuf_put_be32(resp, failure_code);
 
@@ -230,9 +229,10 @@
 	const u8 *pos, *end;
 	const u8 *prop = NULL;
 	u8 idtype;
+	u8 id = eap_get_id(reqData);
 
 	if (data->state != IDENTITY) {
-		return eap_eke_build_fail(data, ret, reqData,
+		return eap_eke_build_fail(data, ret, id,
 					  EAP_EKE_FAIL_PROTO_ERROR);
 	}
 
@@ -240,7 +240,7 @@
 
 	if (payload_len < 2 + 4) {
 		wpa_printf(MSG_DEBUG, "EAP-EKE: Too short ID/Request Data");
-		return eap_eke_build_fail(data, ret, reqData,
+		return eap_eke_build_fail(data, ret, id,
 					  EAP_EKE_FAIL_PROTO_ERROR);
 	}
 
@@ -253,7 +253,7 @@
 	if (pos + num_prop * 4 > end) {
 		wpa_printf(MSG_DEBUG, "EAP-EKE: Too short ID/Request Data (num_prop=%u)",
 			   num_prop);
-		return eap_eke_build_fail(data, ret, reqData,
+		return eap_eke_build_fail(data, ret, id,
 					  EAP_EKE_FAIL_PROTO_ERROR);
 	}
 
@@ -293,7 +293,7 @@
 
 	if (prop == NULL) {
 		wpa_printf(MSG_DEBUG, "EAP-EKE: No acceptable proposal found");
-		return eap_eke_build_fail(data, ret, reqData,
+		return eap_eke_build_fail(data, ret, id,
 					  EAP_EKE_FAIL_NO_PROPOSAL_CHOSEN);
 	}
 
@@ -301,7 +301,7 @@
 
 	if (pos == end) {
 		wpa_printf(MSG_DEBUG, "EAP-EKE: Too short ID/Request Data to include IDType/Identity");
-		return eap_eke_build_fail(data, ret, reqData,
+		return eap_eke_build_fail(data, ret, id,
 					  EAP_EKE_FAIL_PROTO_ERROR);
 	}
 
@@ -312,7 +312,7 @@
 	os_free(data->serverid);
 	data->serverid = os_malloc(end - pos);
 	if (data->serverid == NULL) {
-		return eap_eke_build_fail(data, ret, reqData,
+		return eap_eke_build_fail(data, ret, id,
 					  EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR);
 	}
 	os_memcpy(data->serverid, pos, end - pos);
@@ -320,11 +320,11 @@
 
 	wpa_printf(MSG_DEBUG, "EAP-EKE: Sending EAP-EKE-ID/Response");
 
-	resp = eap_eke_build_msg(data, eap_get_id(reqData),
+	resp = eap_eke_build_msg(data, id,
 				 2 + 4 + 1 + data->peerid_len,
 				 EAP_EKE_ID);
 	if (resp == NULL) {
-		return eap_eke_build_fail(data, ret, reqData,
+		return eap_eke_build_fail(data, ret, id,
 					  EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR);
 	}
 
@@ -339,7 +339,7 @@
 	data->msgs = wpabuf_alloc(wpabuf_len(reqData) + wpabuf_len(resp));
 	if (data->msgs == NULL) {
 		wpabuf_free(resp);
-		return eap_eke_build_fail(data, ret, reqData,
+		return eap_eke_build_fail(data, ret, id,
 					  EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR);
 	}
 	wpabuf_put_buf(data->msgs, reqData);
@@ -366,10 +366,11 @@
 	u8 pub[EAP_EKE_MAX_DH_LEN];
 	const u8 *password;
 	size_t password_len;
+	u8 id = eap_get_id(reqData);
 
 	if (data->state != COMMIT) {
 		wpa_printf(MSG_DEBUG, "EAP-EKE: EAP-EKE-Commit/Request received in unexpected state (%d)", data->state);
-		return eap_eke_build_fail(data, ret, reqData,
+		return eap_eke_build_fail(data, ret, id,
 					  EAP_EKE_FAIL_PROTO_ERROR);
 	}
 
@@ -378,7 +379,7 @@
 	password = eap_get_config_password(sm, &password_len);
 	if (password == NULL) {
 		wpa_printf(MSG_INFO, "EAP-EKE: No password configured!");
-		return eap_eke_build_fail(data, ret, reqData,
+		return eap_eke_build_fail(data, ret, id,
 					  EAP_EKE_FAIL_PASSWD_NOT_FOUND);
 	}
 
@@ -387,7 +388,7 @@
 
 	if (pos + data->sess.dhcomp_len > end) {
 		wpa_printf(MSG_DEBUG, "EAP-EKE: Too short EAP-EKE-Commit");
-		return eap_eke_build_fail(data, ret, reqData,
+		return eap_eke_build_fail(data, ret, id,
 					  EAP_EKE_FAIL_PROTO_ERROR);
 	}
 
@@ -405,7 +406,7 @@
 			       data->serverid, data->serverid_len,
 			       data->peerid, data->peerid_len, key) < 0) {
 		wpa_printf(MSG_INFO, "EAP-EKE: Failed to derive key");
-		return eap_eke_build_fail(data, ret, reqData,
+		return eap_eke_build_fail(data, ret, id,
 					  EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR);
 	}
 
@@ -416,7 +417,7 @@
 	if (eap_eke_dh_init(data->sess.dhgroup, data->dh_priv, pub) < 0) {
 		wpa_printf(MSG_INFO, "EAP-EKE: Failed to initialize DH");
 		os_memset(key, 0, sizeof(key));
-		return eap_eke_build_fail(data, ret, reqData,
+		return eap_eke_build_fail(data, ret, id,
 					  EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR);
 	}
 
@@ -424,7 +425,7 @@
 	{
 		wpa_printf(MSG_INFO, "EAP-EKE: Failed to derive shared secret");
 		os_memset(key, 0, sizeof(key));
-		return eap_eke_build_fail(data, ret, reqData,
+		return eap_eke_build_fail(data, ret, id,
 					  EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR);
 	}
 
@@ -433,18 +434,18 @@
 				 data->peerid, data->peerid_len) < 0) {
 		wpa_printf(MSG_INFO, "EAP-EKE: Failed to derive Ke/Ki");
 		os_memset(key, 0, sizeof(key));
-		return eap_eke_build_fail(data, ret, reqData,
+		return eap_eke_build_fail(data, ret, id,
 					  EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR);
 	}
 
 	wpa_printf(MSG_DEBUG, "EAP-EKE: Sending EAP-EKE-Commit/Response");
 
-	resp = eap_eke_build_msg(data, eap_get_id(reqData),
+	resp = eap_eke_build_msg(data, id,
 				 data->sess.dhcomp_len + data->sess.pnonce_len,
 				 EAP_EKE_COMMIT);
 	if (resp == NULL) {
 		os_memset(key, 0, sizeof(key));
-		return eap_eke_build_fail(data, ret, reqData,
+		return eap_eke_build_fail(data, ret, id,
 					  EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR);
 	}
 
@@ -453,7 +454,7 @@
 	if (eap_eke_dhcomp(&data->sess, key, pub, rpos) < 0) {
 		wpa_printf(MSG_INFO, "EAP-EKE: Failed to build DHComponent_P");
 		os_memset(key, 0, sizeof(key));
-		return eap_eke_build_fail(data, ret, reqData,
+		return eap_eke_build_fail(data, ret, id,
 					  EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR);
 	}
 	os_memset(key, 0, sizeof(key));
@@ -463,7 +464,7 @@
 
 	if (random_get_bytes(data->nonce_p, data->sess.nonce_len)) {
 		wpabuf_free(resp);
-		return eap_eke_build_fail(data, ret, reqData,
+		return eap_eke_build_fail(data, ret, id,
 					  EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR);
 	}
 	wpa_hexdump_key(MSG_DEBUG, "EAP-EKE: Nonce_P",
@@ -472,7 +473,7 @@
 	if (eap_eke_prot(&data->sess, data->nonce_p, data->sess.nonce_len,
 			 wpabuf_put(resp, 0), &prot_len) < 0) {
 		wpabuf_free(resp);
-		return eap_eke_build_fail(data, ret, reqData,
+		return eap_eke_build_fail(data, ret, id,
 					  EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR);
 	}
 	wpa_hexdump(MSG_DEBUG, "EAP-EKE: PNonce_P",
@@ -484,7 +485,7 @@
 	if (wpabuf_resize(&data->msgs, wpabuf_len(reqData) + wpabuf_len(resp))
 	    < 0) {
 		wpabuf_free(resp);
-		return eap_eke_build_fail(data, ret, reqData,
+		return eap_eke_build_fail(data, ret, id,
 					  EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR);
 	}
 	wpabuf_put_buf(data->msgs, reqData);
@@ -509,11 +510,12 @@
 	u8 auth_s[EAP_EKE_MAX_HASH_LEN];
 	size_t decrypt_len;
 	u8 *auth;
+	u8 id = eap_get_id(reqData);
 
 	if (data->state != CONFIRM) {
 		wpa_printf(MSG_DEBUG, "EAP-EKE: EAP-EKE-Confirm/Request received in unexpected state (%d)",
 			   data->state);
-		return eap_eke_build_fail(data, ret, reqData,
+		return eap_eke_build_fail(data, ret, id,
 					  EAP_EKE_FAIL_PROTO_ERROR);
 	}
 
@@ -524,7 +526,7 @@
 
 	if (pos + data->sess.pnonce_ps_len + data->sess.prf_len > end) {
 		wpa_printf(MSG_DEBUG, "EAP-EKE: Too short EAP-EKE-Confirm");
-		return eap_eke_build_fail(data, ret, reqData,
+		return eap_eke_build_fail(data, ret, id,
 					  EAP_EKE_FAIL_PROTO_ERROR);
 	}
 
@@ -532,19 +534,19 @@
 	if (eap_eke_decrypt_prot(&data->sess, pos, data->sess.pnonce_ps_len,
 				 nonces, &decrypt_len) < 0) {
 		wpa_printf(MSG_INFO, "EAP-EKE: Failed to decrypt PNonce_PS");
-		return eap_eke_build_fail(data, ret, reqData,
+		return eap_eke_build_fail(data, ret, id,
 					  EAP_EKE_FAIL_AUTHENTICATION_FAIL);
 	}
 	if (decrypt_len != (size_t) 2 * data->sess.nonce_len) {
 		wpa_printf(MSG_INFO, "EAP-EKE: PNonce_PS protected data length does not match length of Nonce_P and Nonce_S");
-		return eap_eke_build_fail(data, ret, reqData,
+		return eap_eke_build_fail(data, ret, id,
 					  EAP_EKE_FAIL_AUTHENTICATION_FAIL);
 	}
 	wpa_hexdump_key(MSG_DEBUG, "EAP-EKE: Received Nonce_P | Nonce_S",
 			nonces, 2 * data->sess.nonce_len);
 	if (os_memcmp(data->nonce_p, nonces, data->sess.nonce_len) != 0) {
 		wpa_printf(MSG_INFO, "EAP-EKE: Received Nonce_P does not match transmitted Nonce_P");
-		return eap_eke_build_fail(data, ret, reqData,
+		return eap_eke_build_fail(data, ret, id,
 					  EAP_EKE_FAIL_AUTHENTICATION_FAIL);
 	}
 
@@ -556,30 +558,30 @@
 	if (eap_eke_derive_ka(&data->sess, data->serverid, data->serverid_len,
 			      data->peerid, data->peerid_len,
 			      data->nonce_p, data->nonce_s) < 0) {
-		return eap_eke_build_fail(data, ret, reqData,
+		return eap_eke_build_fail(data, ret, id,
 					  EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR);
 	}
 
 	if (eap_eke_auth(&data->sess, "EAP-EKE server", data->msgs, auth_s) < 0)
 	{
-		return eap_eke_build_fail(data, ret, reqData,
+		return eap_eke_build_fail(data, ret, id,
 					  EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR);
 	}
 	wpa_hexdump(MSG_DEBUG, "EAP-EKE: Auth_S", auth_s, data->sess.prf_len);
 	if (os_memcmp_const(auth_s, pos + data->sess.pnonce_ps_len,
 			    data->sess.prf_len) != 0) {
 		wpa_printf(MSG_INFO, "EAP-EKE: Auth_S does not match");
-		return eap_eke_build_fail(data, ret, reqData,
+		return eap_eke_build_fail(data, ret, id,
 					  EAP_EKE_FAIL_AUTHENTICATION_FAIL);
 	}
 
 	wpa_printf(MSG_DEBUG, "EAP-EKE: Sending EAP-EKE-Confirm/Response");
 
-	resp = eap_eke_build_msg(data, eap_get_id(reqData),
+	resp = eap_eke_build_msg(data, id,
 				 data->sess.pnonce_len + data->sess.prf_len,
 				 EAP_EKE_CONFIRM);
 	if (resp == NULL) {
-		return eap_eke_build_fail(data, ret, reqData,
+		return eap_eke_build_fail(data, ret, id,
 					  EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR);
 	}
 
@@ -587,7 +589,7 @@
 	if (eap_eke_prot(&data->sess, data->nonce_s, data->sess.nonce_len,
 			 wpabuf_put(resp, 0), &prot_len) < 0) {
 		wpabuf_free(resp);
-		return eap_eke_build_fail(data, ret, reqData,
+		return eap_eke_build_fail(data, ret, id,
 					  EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR);
 	}
 	wpabuf_put(resp, prot_len);
@@ -595,7 +597,7 @@
 	auth = wpabuf_put(resp, data->sess.prf_len);
 	if (eap_eke_auth(&data->sess, "EAP-EKE peer", data->msgs, auth) < 0) {
 		wpabuf_free(resp);
-		return eap_eke_build_fail(data, ret, reqData,
+		return eap_eke_build_fail(data, ret, id,
 					  EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR);
 	}
 	wpa_hexdump(MSG_DEBUG, "EAP-EKE: Auth_P", auth, data->sess.prf_len);
@@ -606,7 +608,7 @@
 			       data->msk, data->emsk) < 0) {
 		wpa_printf(MSG_INFO, "EAP-EKE: Failed to derive MSK/EMSK");
 		wpabuf_free(resp);
-		return eap_eke_build_fail(data, ret, reqData,
+		return eap_eke_build_fail(data, ret, id,
 					  EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR);
 	}
 
@@ -638,7 +640,8 @@
 		wpa_printf(MSG_INFO, "EAP-EKE: Failure-Code 0x%x", code);
 	}
 
-	return eap_eke_build_fail(data, ret, reqData, EAP_EKE_FAIL_NO_ERROR);
+	return eap_eke_build_fail(data, ret, eap_get_id(reqData),
+				  EAP_EKE_FAIL_NO_ERROR);
 }
 
 
@@ -741,6 +744,29 @@
 }
 
 
+static u8 * eap_eke_get_session_id(struct eap_sm *sm, void *priv, size_t *len)
+{
+	struct eap_eke_data *data = priv;
+	u8 *sid;
+	size_t sid_len;
+
+	if (data->state != SUCCESS)
+		return NULL;
+
+	sid_len = 1 + 2 * data->sess.nonce_len;
+	sid = os_malloc(sid_len);
+	if (sid == NULL)
+		return NULL;
+	sid[0] = EAP_TYPE_EKE;
+	os_memcpy(sid + 1, data->nonce_p, data->sess.nonce_len);
+	os_memcpy(sid + 1 + data->sess.nonce_len, data->nonce_s,
+		  data->sess.nonce_len);
+	*len = sid_len;
+
+	return sid;
+}
+
+
 int eap_peer_eke_register(void)
 {
 	struct eap_method *eap;
@@ -757,6 +783,7 @@
 	eap->isKeyAvailable = eap_eke_isKeyAvailable;
 	eap->getKey = eap_eke_getKey;
 	eap->get_emsk = eap_eke_get_emsk;
+	eap->getSessionId = eap_eke_get_session_id;
 
 	ret = eap_peer_method_register(eap);
 	if (ret)
diff --git a/src/eap_peer/eap_fast.c b/src/eap_peer/eap_fast.c
index 0739187..f636e74 100644
--- a/src/eap_peer/eap_fast.c
+++ b/src/eap_peer/eap_fast.c
@@ -267,8 +267,8 @@
 }
 
 
-static void eap_fast_derive_key_auth(struct eap_sm *sm,
-				     struct eap_fast_data *data)
+static int eap_fast_derive_key_auth(struct eap_sm *sm,
+				    struct eap_fast_data *data)
 {
 	u8 *sks;
 
@@ -281,7 +281,7 @@
 	if (sks == NULL) {
 		wpa_printf(MSG_DEBUG, "EAP-FAST: Failed to derive "
 			   "session_key_seed");
-		return;
+		return -1;
 	}
 
 	/*
@@ -294,11 +294,12 @@
 	data->simck_idx = 0;
 	os_memcpy(data->simck, sks, EAP_FAST_SIMCK_LEN);
 	os_free(sks);
+	return 0;
 }
 
 
-static void eap_fast_derive_key_provisioning(struct eap_sm *sm,
-					     struct eap_fast_data *data)
+static int eap_fast_derive_key_provisioning(struct eap_sm *sm,
+					    struct eap_fast_data *data)
 {
 	os_free(data->key_block_p);
 	data->key_block_p = (struct eap_fast_key_block_provisioning *)
@@ -307,7 +308,7 @@
 				    sizeof(*data->key_block_p));
 	if (data->key_block_p == NULL) {
 		wpa_printf(MSG_DEBUG, "EAP-FAST: Failed to derive key block");
-		return;
+		return -1;
 	}
 	/*
 	 * RFC 4851, Section 5.2:
@@ -326,15 +327,19 @@
 	wpa_hexdump_key(MSG_DEBUG, "EAP-FAST: client_challenge",
 			data->key_block_p->client_challenge,
 			sizeof(data->key_block_p->client_challenge));
+	return 0;
 }
 
 
-static void eap_fast_derive_keys(struct eap_sm *sm, struct eap_fast_data *data)
+static int eap_fast_derive_keys(struct eap_sm *sm, struct eap_fast_data *data)
 {
+	int res;
+
 	if (data->anon_provisioning)
-		eap_fast_derive_key_provisioning(sm, data);
+		res = eap_fast_derive_key_provisioning(sm, data);
 	else
-		eap_fast_derive_key_auth(sm, data);
+		res = eap_fast_derive_key_auth(sm, data);
+	return res;
 }
 
 
@@ -1172,7 +1177,7 @@
 static int eap_fast_process_decrypted(struct eap_sm *sm,
 				      struct eap_fast_data *data,
 				      struct eap_method_ret *ret,
-				      const struct eap_hdr *req,
+				      u8 identifier,
 				      struct wpabuf *decrypted,
 				      struct wpabuf **out_data)
 {
@@ -1184,18 +1189,18 @@
 		return 0;
 	if (resp)
 		return eap_fast_encrypt_response(sm, data, resp,
-						 req->identifier, out_data);
+						 identifier, out_data);
 
 	if (tlv.result == EAP_TLV_RESULT_FAILURE) {
 		resp = eap_fast_tlv_result(EAP_TLV_RESULT_FAILURE, 0);
 		return eap_fast_encrypt_response(sm, data, resp,
-						 req->identifier, out_data);
+						 identifier, out_data);
 	}
 
 	if (tlv.iresult == EAP_TLV_RESULT_FAILURE) {
 		resp = eap_fast_tlv_result(EAP_TLV_RESULT_FAILURE, 1);
 		return eap_fast_encrypt_response(sm, data, resp,
-						 req->identifier, out_data);
+						 identifier, out_data);
 	}
 
 	if (tlv.crypto_binding) {
@@ -1277,14 +1282,13 @@
 		resp = wpabuf_alloc(1);
 	}
 
-	return eap_fast_encrypt_response(sm, data, resp, req->identifier,
+	return eap_fast_encrypt_response(sm, data, resp, identifier,
 					 out_data);
 }
 
 
 static int eap_fast_decrypt(struct eap_sm *sm, struct eap_fast_data *data,
-			    struct eap_method_ret *ret,
-			    const struct eap_hdr *req,
+			    struct eap_method_ret *ret, u8 identifier,
 			    const struct wpabuf *in_data,
 			    struct wpabuf **out_data)
 {
@@ -1309,7 +1313,7 @@
 		/* Received TLS ACK - requesting more fragments */
 		return eap_peer_tls_encrypt(sm, &data->ssl, EAP_TYPE_FAST,
 					    data->fast_version,
-					    req->identifier, NULL, out_data);
+					    identifier, NULL, out_data);
 	}
 
 	res = eap_peer_tls_decrypt(sm, &data->ssl, in_data, &in_decrypted);
@@ -1328,7 +1332,7 @@
 		return -1;
 	}
 
-	res = eap_fast_process_decrypted(sm, data, ret, req,
+	res = eap_fast_process_decrypted(sm, data, ret, identifier,
 					 in_decrypted, out_data);
 
 	wpabuf_free(in_decrypted);
@@ -1340,7 +1344,7 @@
 static const u8 * eap_fast_get_a_id(const u8 *buf, size_t len, size_t *id_len)
 {
 	const u8 *a_id;
-	struct pac_tlv_hdr *hdr;
+	const struct pac_tlv_hdr *hdr;
 
 	/*
 	 * Parse authority identity (A-ID) from the EAP-FAST/Start. This
@@ -1350,13 +1354,13 @@
 	*id_len = len;
 	if (len > sizeof(*hdr)) {
 		int tlen;
-		hdr = (struct pac_tlv_hdr *) buf;
+		hdr = (const struct pac_tlv_hdr *) buf;
 		tlen = be_to_host16(hdr->len);
 		if (be_to_host16(hdr->type) == PAC_TYPE_A_ID &&
 		    sizeof(*hdr) + tlen <= len) {
 			wpa_printf(MSG_DEBUG, "EAP-FAST: A-ID was in TLV "
 				   "(Start)");
-			a_id = (u8 *) (hdr + 1);
+			a_id = (const u8 *) (hdr + 1);
 			*id_len = tlen;
 		}
 	}
@@ -1529,6 +1533,7 @@
 	struct wpabuf *resp;
 	const u8 *pos;
 	struct eap_fast_data *data = priv;
+	struct wpabuf msg;
 
 	pos = eap_peer_tls_process_init(sm, &data->ssl, EAP_TYPE_FAST, ret,
 					reqData, &left, &flags);
@@ -1545,13 +1550,13 @@
 		left = 0; /* A-ID is not used in further packet processing */
 	}
 
+	wpabuf_set(&msg, pos, left);
+
 	resp = NULL;
 	if (tls_connection_established(sm->ssl_ctx, data->ssl.conn) &&
 	    !data->resuming) {
 		/* Process tunneled (encrypted) phase 2 data. */
-		struct wpabuf msg;
-		wpabuf_set(&msg, pos, left);
-		res = eap_fast_decrypt(sm, data, ret, req, &msg, &resp);
+		res = eap_fast_decrypt(sm, data, ret, id, &msg, &resp);
 		if (res < 0) {
 			ret->methodState = METHOD_DONE;
 			ret->decision = DECISION_FAIL;
@@ -1565,8 +1570,8 @@
 		/* Continue processing TLS handshake (phase 1). */
 		res = eap_peer_tls_process_helper(sm, &data->ssl,
 						  EAP_TYPE_FAST,
-						  data->fast_version, id, pos,
-						  left, &resp);
+						  data->fast_version, id, &msg,
+						  &resp);
 
 		if (tls_connection_established(sm->ssl_ctx, data->ssl.conn)) {
 			char cipher[80];
@@ -1586,20 +1591,24 @@
 			} else
 				data->anon_provisioning = 0;
 			data->resuming = 0;
-			eap_fast_derive_keys(sm, data);
+			if (eap_fast_derive_keys(sm, data) < 0) {
+				wpa_printf(MSG_DEBUG,
+					   "EAP-FAST: Could not derive keys");
+				ret->methodState = METHOD_DONE;
+				ret->decision = DECISION_FAIL;
+				wpabuf_free(resp);
+				return NULL;
+			}
 		}
 
 		if (res == 2) {
-			struct wpabuf msg;
 			/*
 			 * Application data included in the handshake message.
 			 */
 			wpabuf_free(data->pending_phase2_req);
 			data->pending_phase2_req = resp;
 			resp = NULL;
-			wpabuf_set(&msg, pos, left);
-			res = eap_fast_decrypt(sm, data, ret, req, &msg,
-					       &resp);
+			res = eap_fast_decrypt(sm, data, ret, id, &msg, &resp);
 		}
 	}
 
@@ -1666,7 +1675,7 @@
 		ret = os_snprintf(buf + len, buflen - len,
 				  "EAP-FAST Phase2 method=%s\n",
 				  data->phase2_method->name);
-		if (ret < 0 || (size_t) ret >= buflen - len)
+		if (os_snprintf_error(buflen - len, ret))
 			return len;
 		len += ret;
 	}
diff --git a/src/eap_peer/eap_fast_pac.c b/src/eap_peer/eap_fast_pac.c
index 21d6098..89e604e 100644
--- a/src/eap_peer/eap_fast_pac.c
+++ b/src/eap_peer/eap_fast_pac.c
@@ -504,28 +504,28 @@
 	end = *buf + *buf_len;
 
 	ret = os_snprintf(*pos, end - *pos, "%s=", field);
-	if (ret < 0 || ret >= end - *pos)
+	if (os_snprintf_error(end - *pos, ret))
 		return;
 	*pos += ret;
 	*pos += wpa_snprintf_hex(*pos, end - *pos, data, len);
 	ret = os_snprintf(*pos, end - *pos, "\n");
-	if (ret < 0 || ret >= end - *pos)
+	if (os_snprintf_error(end - *pos, ret))
 		return;
 	*pos += ret;
 
 	if (txt) {
 		ret = os_snprintf(*pos, end - *pos, "%s-txt=", field);
-		if (ret < 0 || ret >= end - *pos)
+		if (os_snprintf_error(end - *pos, ret))
 			return;
 		*pos += ret;
 		for (i = 0; i < len; i++) {
 			ret = os_snprintf(*pos, end - *pos, "%c", data[i]);
-			if (ret < 0 || ret >= end - *pos)
+			if (os_snprintf_error(end - *pos, ret))
 				return;
 			*pos += ret;
 		}
 		ret = os_snprintf(*pos, end - *pos, "\n");
-		if (ret < 0 || ret >= end - *pos)
+		if (os_snprintf_error(end - *pos, ret))
 			return;
 		*pos += ret;
 	}
@@ -578,7 +578,7 @@
 
 	ret = os_snprintf(*pos, *buf + *buf_len - *pos,
 			  "START\nPAC-Type=%d\n", pac->pac_type);
-	if (ret < 0 || ret >= *buf + *buf_len - *pos)
+	if (os_snprintf_error(*buf + *buf_len - *pos, ret))
 		return -1;
 
 	*pos += ret;
@@ -600,7 +600,7 @@
 		return -1;
 	}
 	ret = os_snprintf(*pos, *buf + *buf_len - *pos, "END\n");
-	if (ret < 0 || ret >= *buf + *buf_len - *pos)
+	if (os_snprintf_error(*buf + *buf_len - *pos, ret))
 		return -1;
 	*pos += ret;
 
@@ -632,7 +632,7 @@
 		return -1;
 
 	ret = os_snprintf(pos, buf + buf_len - pos, "%s\n", pac_file_hdr);
-	if (ret < 0 || ret >= buf + buf_len - pos) {
+	if (os_snprintf_error(buf + buf_len - pos, ret)) {
 		os_free(buf);
 		return -1;
 	}
@@ -714,7 +714,7 @@
 		pos += 2;
 		len = WPA_GET_BE16(pos);
 		pos += 2;
-		if (pos + len > end)
+		if (len > (unsigned int) (end - pos))
 			break;
 
 		if (type == PAC_TYPE_A_ID) {
@@ -799,7 +799,9 @@
 	pos = buf + 6;
 	end = buf + len;
 	while (pos < end) {
-		if (end - pos < 2 + 32 + 2 + 2)
+		u16 val;
+
+		if (end - pos < 2 + EAP_FAST_PAC_KEY_LEN + 2 + 2)
 			goto parse_fail;
 
 		pac = os_zalloc(sizeof(*pac));
@@ -810,19 +812,23 @@
 		pos += 2;
 		os_memcpy(pac->pac_key, pos, EAP_FAST_PAC_KEY_LEN);
 		pos += EAP_FAST_PAC_KEY_LEN;
-		pac->pac_opaque_len = WPA_GET_BE16(pos);
+		val = WPA_GET_BE16(pos);
 		pos += 2;
-		if (pos + pac->pac_opaque_len + 2 > end)
+		if (val > end - pos)
 			goto parse_fail;
+		pac->pac_opaque_len = val;
 		pac->pac_opaque = os_malloc(pac->pac_opaque_len);
 		if (pac->pac_opaque == NULL)
 			goto parse_fail;
 		os_memcpy(pac->pac_opaque, pos, pac->pac_opaque_len);
 		pos += pac->pac_opaque_len;
-		pac->pac_info_len = WPA_GET_BE16(pos);
-		pos += 2;
-		if (pos + pac->pac_info_len > end)
+		if (2 > end - pos)
 			goto parse_fail;
+		val = WPA_GET_BE16(pos);
+		pos += 2;
+		if (val > end - pos)
+			goto parse_fail;
+		pac->pac_info_len = val;
 		pac->pac_info = os_malloc(pac->pac_info_len);
 		if (pac->pac_info == NULL)
 			goto parse_fail;
diff --git a/src/eap_peer/eap_gpsk.c b/src/eap_peer/eap_gpsk.c
index c54bf11..902b4ba 100644
--- a/src/eap_peer/eap_gpsk.c
+++ b/src/eap_peer/eap_gpsk.c
@@ -274,7 +274,7 @@
 static struct wpabuf * eap_gpsk_process_gpsk_1(struct eap_sm *sm,
 					       struct eap_gpsk_data *data,
 					       struct eap_method_ret *ret,
-					       const struct wpabuf *reqData,
+					       u8 identifier,
 					       const u8 *payload,
 					       size_t payload_len)
 {
@@ -301,7 +301,7 @@
 		return NULL;
 	}
 
-	resp = eap_gpsk_send_gpsk_2(data, eap_get_id(reqData),
+	resp = eap_gpsk_send_gpsk_2(data, identifier,
 				    csuite_list, csuite_list_len);
 	if (resp == NULL)
 		return NULL;
@@ -583,7 +583,7 @@
 static struct wpabuf * eap_gpsk_process_gpsk_3(struct eap_sm *sm,
 					       struct eap_gpsk_data *data,
 					       struct eap_method_ret *ret,
-					       const struct wpabuf *reqData,
+					       u8 identifier,
 					       const u8 *payload,
 					       size_t payload_len)
 {
@@ -615,7 +615,7 @@
 			   (unsigned long) (end - pos));
 	}
 
-	resp = eap_gpsk_send_gpsk_4(data, eap_get_id(reqData));
+	resp = eap_gpsk_send_gpsk_4(data, identifier);
 	if (resp == NULL)
 		return NULL;
 
@@ -670,6 +670,7 @@
 	struct wpabuf *resp;
 	const u8 *pos;
 	size_t len;
+	u8 opcode, id;
 
 	pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_GPSK, reqData, &len);
 	if (pos == NULL || len < 1) {
@@ -677,25 +678,27 @@
 		return NULL;
 	}
 
-	wpa_printf(MSG_DEBUG, "EAP-GPSK: Received frame: opcode %d", *pos);
+	id = eap_get_id(reqData);
+	opcode = *pos++;
+	len--;
+	wpa_printf(MSG_DEBUG, "EAP-GPSK: Received frame: opcode %d", opcode);
 
 	ret->ignore = FALSE;
 	ret->methodState = METHOD_MAY_CONT;
 	ret->decision = DECISION_FAIL;
 	ret->allowNotifications = FALSE;
 
-	switch (*pos) {
+	switch (opcode) {
 	case EAP_GPSK_OPCODE_GPSK_1:
-		resp = eap_gpsk_process_gpsk_1(sm, data, ret, reqData,
-					       pos + 1, len - 1);
+		resp = eap_gpsk_process_gpsk_1(sm, data, ret, id, pos, len);
 		break;
 	case EAP_GPSK_OPCODE_GPSK_3:
-		resp = eap_gpsk_process_gpsk_3(sm, data, ret, reqData,
-					       pos + 1, len - 1);
+		resp = eap_gpsk_process_gpsk_3(sm, data, ret, id, pos, len);
 		break;
 	default:
-		wpa_printf(MSG_DEBUG, "EAP-GPSK: Ignoring message with "
-			   "unknown opcode %d", *pos);
+		wpa_printf(MSG_DEBUG,
+			   "EAP-GPSK: Ignoring message with unknown opcode %d",
+			   opcode);
 		ret->ignore = TRUE;
 		return NULL;
 	}
diff --git a/src/eap_peer/eap_i.h b/src/eap_peer/eap_i.h
index fde809c..5f8b5fa 100644
--- a/src/eap_peer/eap_i.h
+++ b/src/eap_peer/eap_i.h
@@ -1,6 +1,6 @@
 /*
  * EAP peer state machines internal structures (RFC 4137)
- * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2014, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -10,6 +10,7 @@
 #define EAP_I_H
 
 #include "wpabuf.h"
+#include "utils/list.h"
 #include "eap_peer/eap.h"
 #include "eap_common/eap_common.h"
 
@@ -277,6 +278,16 @@
 };
 
 
+struct eap_erp_key {
+	struct dl_list list;
+	size_t rRK_len;
+	size_t rIK_len;
+	u8 rRK[ERP_MAX_KEY_LEN];
+	u8 rIK[ERP_MAX_KEY_LEN];
+	u32 next_seq;
+	char keyname_nai[];
+};
+
 /**
  * struct eap_sm - EAP state machine data
  */
@@ -317,10 +328,12 @@
 	/* not defined in RFC 4137 */
 	Boolean changed;
 	void *eapol_ctx;
-	struct eapol_callbacks *eapol_cb;
+	const struct eapol_callbacks *eapol_cb;
 	void *eap_method_priv;
 	int init_phase2;
 	int fast_reauth;
+	Boolean reauthInit; /* send EAP-Identity/Re-auth */
+	u32 erp_seq;
 
 	Boolean rxResp /* LEAP only */;
 	Boolean leap_done;
@@ -353,6 +366,8 @@
 	int external_sim;
 
 	unsigned int expected_failure:1;
+
+	struct dl_list erp_keys; /* struct eap_erp_key */
 };
 
 const u8 * eap_get_config_identity(struct eap_sm *sm, size_t *len);
diff --git a/src/eap_peer/eap_ikev2.c b/src/eap_peer/eap_ikev2.c
index c12b519..b5ef71b 100644
--- a/src/eap_peer/eap_ikev2.c
+++ b/src/eap_peer/eap_ikev2.c
@@ -301,6 +301,13 @@
 
 	if (data->in_buf == NULL) {
 		/* First fragment of the message */
+		if (message_length > 50000) {
+			/* Limit maximum memory allocation */
+			wpa_printf(MSG_DEBUG,
+				   "EAP-IKEV2: Ignore too long message");
+			ret->ignore = TRUE;
+			return NULL;
+		}
 		data->in_buf = wpabuf_alloc(message_length);
 		if (data->in_buf == NULL) {
 			wpa_printf(MSG_DEBUG, "EAP-IKEV2: No memory for "
@@ -315,6 +322,7 @@
 			   (unsigned long) wpabuf_tailroom(data->in_buf));
 	}
 
+	ret->ignore = FALSE;
 	return eap_ikev2_build_frag_ack(id, EAP_CODE_RESPONSE);
 }
 
diff --git a/src/eap_peer/eap_methods.c b/src/eap_peer/eap_methods.c
index 83a1457..1bdd81e 100644
--- a/src/eap_peer/eap_methods.c
+++ b/src/eap_peer/eap_methods.c
@@ -103,7 +103,7 @@
 	for (m = eap_methods; m; m = m->next) {
 		ret = os_snprintf(pos, end - pos, "%s%s",
 				  m == eap_methods ? "" : " ", m->name);
-		if (ret < 0 || ret >= end - pos)
+		if (os_snprintf_error(end - pos, ret))
 			break;
 		pos += ret;
 	}
@@ -133,7 +133,7 @@
 	for (m = eap_methods; m; m = m->next)
 		array_len++;
 
-	array = os_zalloc(sizeof(char *) * (array_len + 1));
+	array = os_calloc(array_len + 1, sizeof(char *));
 	if (array == NULL)
 		return NULL;
 
diff --git a/src/eap_peer/eap_mschapv2.c b/src/eap_peer/eap_mschapv2.c
index 430c501..9e486e7 100644
--- a/src/eap_peer/eap_mschapv2.c
+++ b/src/eap_peer/eap_mschapv2.c
@@ -472,6 +472,13 @@
 		pos += 2;
 		msg = pos;
 	}
+	if (data->prev_error == ERROR_AUTHENTICATION_FAILURE && retry &&
+	    config && config->phase2 &&
+	    os_strstr(config->phase2, "mschapv2_retry=0")) {
+		wpa_printf(MSG_DEBUG,
+			   "EAP-MSCHAPV2: mark password retry disabled based on local configuration");
+		retry = 0;
+	}
 	wpa_msg(sm->msg_ctx, MSG_WARNING,
 		"EAP-MSCHAPV2: failure message: '%s' (retry %sallowed, error "
 		"%d)",
diff --git a/src/eap_peer/eap_pax.c b/src/eap_peer/eap_pax.c
index 1c111c2..c920bcd 100644
--- a/src/eap_peer/eap_pax.c
+++ b/src/eap_peer/eap_pax.c
@@ -38,6 +38,7 @@
 	u8 mk[EAP_PAX_MK_LEN];
 	u8 ck[EAP_PAX_CK_LEN];
 	u8 ick[EAP_PAX_ICK_LEN];
+	u8 mid[EAP_PAX_MID_LEN];
 };
 
 
@@ -178,8 +179,8 @@
 		    data->rand.r.y, EAP_PAX_RAND_LEN);
 
 	if (eap_pax_initial_key_derivation(req->mac_id, data->ak, data->rand.e,
-					   data->mk, data->ck, data->ick) < 0)
-	{
+					   data->mk, data->ck, data->ick,
+					   data->mid) < 0) {
 		ret->ignore = TRUE;
 		return NULL;
 	}
@@ -332,7 +333,7 @@
 	u16 flen, mlen;
 
 	pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_PAX, reqData, &len);
-	if (pos == NULL || len < EAP_PAX_ICV_LEN) {
+	if (pos == NULL || len < sizeof(*req) + EAP_PAX_ICV_LEN) {
 		ret->ignore = TRUE;
 		return NULL;
 	}
@@ -501,6 +502,26 @@
 }
 
 
+static u8 * eap_pax_get_session_id(struct eap_sm *sm, void *priv, size_t *len)
+{
+	struct eap_pax_data *data = priv;
+	u8 *sid;
+
+	if (data->state != PAX_DONE)
+		return NULL;
+
+	sid = os_malloc(1 + EAP_PAX_MID_LEN);
+	if (sid == NULL)
+		return NULL;
+
+	*len = 1 + EAP_PAX_MID_LEN;
+	sid[0] = EAP_TYPE_PAX;
+	os_memcpy(sid + 1, data->mid, EAP_PAX_MID_LEN);
+
+	return sid;
+}
+
+
 int eap_peer_pax_register(void)
 {
 	struct eap_method *eap;
@@ -517,6 +538,7 @@
 	eap->isKeyAvailable = eap_pax_isKeyAvailable;
 	eap->getKey = eap_pax_getKey;
 	eap->get_emsk = eap_pax_get_emsk;
+	eap->getSessionId = eap_pax_get_session_id;
 
 	ret = eap_peer_method_register(eap);
 	if (ret)
diff --git a/src/eap_peer/eap_peap.c b/src/eap_peer/eap_peap.c
index 472e861..4f68fce 100644
--- a/src/eap_peer/eap_peap.c
+++ b/src/eap_peer/eap_peap.c
@@ -968,6 +968,7 @@
 	struct wpabuf *resp;
 	const u8 *pos;
 	struct eap_peap_data *data = priv;
+	struct wpabuf msg;
 
 	pos = eap_peer_tls_process_init(sm, &data->ssl, EAP_TYPE_PEAP, ret,
 					reqData, &left, &flags);
@@ -998,17 +999,17 @@
 			   * should always be, anyway */
 	}
 
+	wpabuf_set(&msg, pos, left);
+
 	resp = NULL;
 	if (tls_connection_established(sm->ssl_ctx, data->ssl.conn) &&
 	    !data->resuming) {
-		struct wpabuf msg;
-		wpabuf_set(&msg, pos, left);
 		res = eap_peap_decrypt(sm, data, ret, req, &msg, &resp);
 	} else {
 		res = eap_peer_tls_process_helper(sm, &data->ssl,
 						  EAP_TYPE_PEAP,
-						  data->peap_version, id, pos,
-						  left, &resp);
+						  data->peap_version, id, &msg,
+						  &resp);
 
 		if (tls_connection_established(sm->ssl_ctx, data->ssl.conn)) {
 			char *label;
@@ -1077,14 +1078,12 @@
 		}
 
 		if (res == 2) {
-			struct wpabuf msg;
 			/*
 			 * Application data included in the handshake message.
 			 */
 			wpabuf_free(data->pending_phase2_req);
 			data->pending_phase2_req = resp;
 			resp = NULL;
-			wpabuf_set(&msg, pos, left);
 			res = eap_peap_decrypt(sm, data, ret, req, &msg,
 					       &resp);
 		}
@@ -1156,7 +1155,7 @@
 				  "EAP-PEAPv%d Phase2 method=%s\n",
 				  data->peap_version,
 				  data->phase2_method->name);
-		if (ret < 0 || (size_t) ret >= buflen - len)
+		if (os_snprintf_error(buflen - len, ret))
 			return len;
 		len += ret;
 	}
diff --git a/src/eap_peer/eap_pwd.c b/src/eap_peer/eap_pwd.c
index 6a0e45b..cd7a2ab 100644
--- a/src/eap_peer/eap_pwd.c
+++ b/src/eap_peer/eap_pwd.c
@@ -10,6 +10,7 @@
 
 #include "common.h"
 #include "crypto/sha256.h"
+#include "crypto/ms_funcs.h"
 #include "eap_peer/eap_i.h"
 #include "eap_common/eap_pwd_common.h"
 
@@ -25,6 +26,7 @@
 	size_t id_server_len;
 	u8 *password;
 	size_t password_len;
+	int password_hash;
 	u16 group_num;
 	EAP_PWD_group *grp;
 
@@ -86,8 +88,9 @@
 	const u8 *identity, *password;
 	size_t identity_len, password_len;
 	int fragment_size;
+	int pwhash;
 
-	password = eap_get_config_password(sm, &password_len);
+	password = eap_get_config_password2(sm, &password_len, &pwhash);
 	if (password == NULL) {
 		wpa_printf(MSG_INFO, "EAP-PWD: No password configured!");
 		return NULL;
@@ -129,6 +132,7 @@
 	}
 	os_memcpy(data->password, password, password_len);
 	data->password_len = password_len;
+	data->password_hash = pwhash;
 
 	data->out_frag_pos = data->in_frag_pos = 0;
 	data->inbuf = data->outbuf = NULL;
@@ -216,6 +220,10 @@
 			    const u8 *payload, size_t payload_len)
 {
 	struct eap_pwd_id *id;
+	const u8 *password;
+	size_t password_len;
+	u8 pwhashhash[16];
+	int res;
 
 	if (data->state != PWD_ID_Req) {
 		ret->ignore = TRUE;
@@ -231,6 +239,9 @@
 
 	id = (struct eap_pwd_id *) payload;
 	data->group_num = be_to_host16(id->group_num);
+	wpa_printf(MSG_DEBUG,
+		   "EAP-PWD: Server EAP-pwd-ID proposal: group=%u random=%u prf=%u prep=%u",
+		   data->group_num, id->random_function, id->prf, id->prep);
 	if ((id->random_function != EAP_PWD_DEFAULT_RAND_FUNC) ||
 	    (id->prf != EAP_PWD_DEFAULT_PRF)) {
 		ret->ignore = TRUE;
@@ -238,6 +249,22 @@
 		return;
 	}
 
+	if (id->prep != EAP_PWD_PREP_NONE &&
+	    id->prep != EAP_PWD_PREP_MS) {
+		wpa_printf(MSG_DEBUG,
+			   "EAP-PWD: Unsupported password pre-processing technique (Prep=%u)",
+			   id->prep);
+		eap_pwd_state(data, FAILURE);
+		return;
+	}
+
+	if (id->prep == EAP_PWD_PREP_NONE && data->password_hash) {
+		wpa_printf(MSG_DEBUG,
+			   "EAP-PWD: Unhashed password not available");
+		eap_pwd_state(data, FAILURE);
+		return;
+	}
+
 	wpa_printf(MSG_DEBUG, "EAP-PWD (peer): using group %d",
 		   data->group_num);
 
@@ -260,12 +287,39 @@
 		return;
 	}
 
+	if (id->prep == EAP_PWD_PREP_MS) {
+		if (data->password_hash) {
+			res = hash_nt_password_hash(data->password, pwhashhash);
+		} else {
+			u8 pwhash[16];
+
+			res = nt_password_hash(data->password,
+					       data->password_len, pwhash);
+			if (res == 0)
+				res = hash_nt_password_hash(pwhash, pwhashhash);
+			os_memset(pwhash, 0, sizeof(pwhash));
+		}
+
+		if (res) {
+			eap_pwd_state(data, FAILURE);
+			return;
+		}
+
+		password = pwhashhash;
+		password_len = sizeof(pwhashhash);
+	} else {
+		password = data->password;
+		password_len = data->password_len;
+	}
+
 	/* compute PWE */
-	if (compute_password_element(data->grp, data->group_num,
-				     data->password, data->password_len,
-				     data->id_server, data->id_server_len,
-				     data->id_peer, data->id_peer_len,
-				     id->token)) {
+	res = compute_password_element(data->grp, data->group_num,
+				       password, password_len,
+				       data->id_server, data->id_server_len,
+				       data->id_peer, data->id_peer_len,
+				       id->token);
+	os_memset(pwhashhash, 0, sizeof(pwhashhash));
+	if (res) {
 		wpa_printf(MSG_INFO, "EAP-PWD (peer): unable to compute PWE");
 		eap_pwd_state(data, FAILURE);
 		return;
@@ -301,6 +355,23 @@
 	BIGNUM *mask = NULL, *x = NULL, *y = NULL, *cofactor = NULL;
 	u16 offset;
 	u8 *ptr, *scalar = NULL, *element = NULL;
+	size_t prime_len, order_len;
+
+	if (data->state != PWD_Commit_Req) {
+		ret->ignore = TRUE;
+		goto fin;
+	}
+
+	prime_len = BN_num_bytes(data->grp->prime);
+	order_len = BN_num_bytes(data->grp->order);
+
+	if (payload_len != 2 * prime_len + order_len) {
+		wpa_printf(MSG_INFO,
+			   "EAP-pwd: Unexpected Commit payload length %u (expected %u)",
+			   (unsigned int) payload_len,
+			   (unsigned int) (2 * prime_len + order_len));
+		goto fin;
+	}
 
 	if (((data->private_value = BN_new()) == NULL) ||
 	    ((data->my_element = EC_POINT_new(data->grp->group)) == NULL) ||
@@ -500,6 +571,18 @@
 	u8 conf[SHA256_MAC_LEN], *cruft = NULL, *ptr;
 	int offset;
 
+	if (data->state != PWD_Confirm_Req) {
+		ret->ignore = TRUE;
+		goto fin;
+	}
+
+	if (payload_len != SHA256_MAC_LEN) {
+		wpa_printf(MSG_INFO,
+			   "EAP-pwd: Unexpected Confirm payload length %u (expected %u)",
+			   (unsigned int) payload_len, SHA256_MAC_LEN);
+		goto fin;
+	}
+
 	/*
 	 * first build up the ciphersuite which is group | random_function |
 	 *	prf
@@ -783,17 +866,30 @@
 	 * if it's the first fragment there'll be a length field
 	 */
 	if (EAP_PWD_GET_LENGTH_BIT(lm_exch)) {
+		if (len < 2) {
+			wpa_printf(MSG_DEBUG,
+				   "EAP-pwd: Frame too short to contain Total-Length field");
+			ret->ignore = TRUE;
+			return NULL;
+		}
 		tot_len = WPA_GET_BE16(pos);
 		wpa_printf(MSG_DEBUG, "EAP-pwd: Incoming fragments whose "
 			   "total length = %d", tot_len);
 		if (tot_len > 15000)
 			return NULL;
+		if (data->inbuf) {
+			wpa_printf(MSG_DEBUG,
+				   "EAP-pwd: Unexpected new fragment start when previous fragment is still in use");
+			ret->ignore = TRUE;
+			return NULL;
+		}
 		data->inbuf = wpabuf_alloc(tot_len);
 		if (data->inbuf == NULL) {
 			wpa_printf(MSG_INFO, "Out of memory to buffer "
 				   "fragments!");
 			return NULL;
 		}
+		data->in_frag_pos = 0;
 		pos += sizeof(u16);
 		len -= sizeof(u16);
 	}
@@ -872,6 +968,7 @@
 	/*
 	 * we have output! Do we need to fragment it?
 	 */
+	lm_exch = EAP_PWD_GET_EXCHANGE(lm_exch);
 	len = wpabuf_len(data->outbuf);
 	if ((len + EAP_PWD_HDR_SIZE) > data->mtu) {
 		resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PWD, data->mtu,
@@ -951,7 +1048,6 @@
 	struct eap_method *eap;
 	int ret;
 
-	EVP_add_digest(EVP_sha256());
 	eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION,
 				    EAP_VENDOR_IETF, EAP_TYPE_PWD, "PWD");
 	if (eap == NULL)
diff --git a/src/eap_peer/eap_sake.c b/src/eap_peer/eap_sake.c
index 7d14907..c4f9843 100644
--- a/src/eap_peer/eap_sake.c
+++ b/src/eap_peer/eap_sake.c
@@ -141,7 +141,7 @@
 static struct wpabuf * eap_sake_process_identity(struct eap_sm *sm,
 						 struct eap_sake_data *data,
 						 struct eap_method_ret *ret,
-						 const struct wpabuf *reqData,
+						 u8 id,
 						 const u8 *payload,
 						 size_t payload_len)
 {
@@ -166,8 +166,7 @@
 
 	wpa_printf(MSG_DEBUG, "EAP-SAKE: Sending Response/Identity");
 
-	resp = eap_sake_build_msg(data, eap_get_id(reqData),
-				  2 + data->peerid_len,
+	resp = eap_sake_build_msg(data, id, 2 + data->peerid_len,
 				  EAP_SAKE_SUBTYPE_IDENTITY);
 	if (resp == NULL)
 		return NULL;
@@ -185,7 +184,7 @@
 static struct wpabuf * eap_sake_process_challenge(struct eap_sm *sm,
 						  struct eap_sake_data *data,
 						  struct eap_method_ret *ret,
-						  const struct wpabuf *reqData,
+						  u8 id,
 						  const u8 *payload,
 						  size_t payload_len)
 {
@@ -247,8 +246,7 @@
 	rlen = 2 + EAP_SAKE_RAND_LEN + 2 + EAP_SAKE_MIC_LEN;
 	if (data->peerid)
 		rlen += 2 + data->peerid_len;
-	resp = eap_sake_build_msg(data, eap_get_id(reqData), rlen,
-				  EAP_SAKE_SUBTYPE_CHALLENGE);
+	resp = eap_sake_build_msg(data, id, rlen, EAP_SAKE_SUBTYPE_CHALLENGE);
 	if (resp == NULL)
 		return NULL;
 
@@ -285,6 +283,7 @@
 static struct wpabuf * eap_sake_process_confirm(struct eap_sm *sm,
 						struct eap_sake_data *data,
 						struct eap_method_ret *ret,
+						u8 id,
 						const struct wpabuf *reqData,
 						const u8 *payload,
 						size_t payload_len)
@@ -323,14 +322,13 @@
 		ret->allowNotifications = FALSE;
 		wpa_printf(MSG_DEBUG, "EAP-SAKE: Sending "
 			   "Response/Auth-Reject");
-		return eap_sake_build_msg(data, eap_get_id(reqData), 0,
+		return eap_sake_build_msg(data, id, 0,
 					  EAP_SAKE_SUBTYPE_AUTH_REJECT);
 	}
 
 	wpa_printf(MSG_DEBUG, "EAP-SAKE: Sending Response/Confirm");
 
-	resp = eap_sake_build_msg(data, eap_get_id(reqData),
-				  2 + EAP_SAKE_MIC_LEN,
+	resp = eap_sake_build_msg(data, id, 2 + EAP_SAKE_MIC_LEN,
 				  EAP_SAKE_SUBTYPE_CONFIRM);
 	if (resp == NULL)
 		return NULL;
@@ -367,7 +365,7 @@
 	struct wpabuf *resp;
 	const u8 *pos, *end;
 	size_t len;
-	u8 subtype, session_id;
+	u8 subtype, session_id, id;
 
 	pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_SAKE, reqData, &len);
 	if (pos == NULL || len < sizeof(struct eap_sake_hdr)) {
@@ -377,6 +375,7 @@
 
 	req = (const struct eap_sake_hdr *) pos;
 	end = pos + len;
+	id = eap_get_id(reqData);
 	subtype = req->subtype;
 	session_id = req->session_id;
 	pos = (const u8 *) (req + 1);
@@ -402,15 +401,15 @@
 
 	switch (subtype) {
 	case EAP_SAKE_SUBTYPE_IDENTITY:
-		resp = eap_sake_process_identity(sm, data, ret, reqData,
+		resp = eap_sake_process_identity(sm, data, ret, id,
 						 pos, end - pos);
 		break;
 	case EAP_SAKE_SUBTYPE_CHALLENGE:
-		resp = eap_sake_process_challenge(sm, data, ret, reqData,
+		resp = eap_sake_process_challenge(sm, data, ret, id,
 						  pos, end - pos);
 		break;
 	case EAP_SAKE_SUBTYPE_CONFIRM:
-		resp = eap_sake_process_confirm(sm, data, ret, reqData,
+		resp = eap_sake_process_confirm(sm, data, ret, id, reqData,
 						pos, end - pos);
 		break;
 	default:
diff --git a/src/eap_peer/eap_sim.c b/src/eap_peer/eap_sim.c
index bd06df7..99a2816 100644
--- a/src/eap_peer/eap_sim.c
+++ b/src/eap_peer/eap_sim.c
@@ -1042,7 +1042,7 @@
 	}
 
 	pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_SIM, reqData, &len);
-	if (pos == NULL || len < 1) {
+	if (pos == NULL || len < 3) {
 		ret->ignore = TRUE;
 		return NULL;
 	}
diff --git a/src/eap_peer/eap_tls.c b/src/eap_peer/eap_tls.c
index 5aa3fd5..d81b1cf 100644
--- a/src/eap_peer/eap_tls.c
+++ b/src/eap_peer/eap_tls.c
@@ -228,6 +228,7 @@
 	u8 flags, id;
 	const u8 *pos;
 	struct eap_tls_data *data = priv;
+	struct wpabuf msg;
 
 	pos = eap_peer_tls_process_init(sm, &data->ssl, data->eap_type, ret,
 					reqData, &left, &flags);
@@ -242,8 +243,9 @@
 	}
 
 	resp = NULL;
+	wpabuf_set(&msg, pos, left);
 	res = eap_peer_tls_process_helper(sm, &data->ssl, data->eap_type, 0,
-					  id, pos, left, &resp);
+					  id, &msg, &resp);
 
 	if (res < 0) {
 		return eap_tls_failure(sm, data, ret, res, resp, id);
diff --git a/src/eap_peer/eap_tls_common.c b/src/eap_peer/eap_tls_common.c
index fe9bfe0..fef7fdb 100644
--- a/src/eap_peer/eap_tls_common.c
+++ b/src/eap_peer/eap_tls_common.c
@@ -91,6 +91,7 @@
 	params->subject_match = (char *) config->subject_match;
 	params->altsubject_match = (char *) config->altsubject_match;
 	params->suffix_match = config->domain_suffix_match;
+	params->domain_match = config->domain_match;
 	params->engine = config->engine;
 	params->engine_id = config->engine_id;
 	params->pin = config->pin;
@@ -113,6 +114,7 @@
 	params->subject_match = (char *) config->subject_match2;
 	params->altsubject_match = (char *) config->altsubject_match2;
 	params->suffix_match = config->domain_suffix_match2;
+	params->domain_match = config->domain_match2;
 	params->engine = config->engine2;
 	params->engine_id = config->engine2_id;
 	params->pin = config->pin2;
@@ -147,6 +149,8 @@
 	} else {
 		wpa_printf(MSG_DEBUG, "TLS: using phase1 config options");
 		eap_tls_params_from_conf1(params, config);
+		if (data->eap_type == EAP_TYPE_FAST)
+			params->flags |= TLS_CONN_EAP_FAST;
 	}
 
 	/*
@@ -167,6 +171,8 @@
 		return -1;
 	}
 
+	params->openssl_ciphers = config->openssl_ciphers;
+
 	return 0;
 }
 
@@ -190,28 +196,25 @@
 	}
 
 	res = tls_connection_set_params(data->ssl_ctx, data->conn, params);
-	if (res == TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED) {
+	if (res == TLS_SET_PARAMS_ENGINE_PRV_BAD_PIN) {
 		/*
-		 * At this point with the pkcs11 engine the PIN might be wrong.
-		 * We reset the PIN in the configuration to be sure to not use
-		 * it again and the calling function must request a new one.
+		 * At this point with the pkcs11 engine the PIN is wrong. We
+		 * reset the PIN in the configuration to be sure to not use it
+		 * again and the calling function must request a new one.
 		 */
-		os_free(config->pin);
-		config->pin = NULL;
-	} else if (res == TLS_SET_PARAMS_ENGINE_PRV_VERIFY_FAILED) {
-		wpa_printf(MSG_INFO, "TLS: Failed to load private key");
-		/*
-		 * We do not know exactly but maybe the PIN was wrong,
-		 * so ask for a new one.
-		 */
+		wpa_printf(MSG_INFO,
+			   "TLS: Bad PIN provided, requesting a new one");
 		os_free(config->pin);
 		config->pin = NULL;
 		eap_sm_request_pin(sm);
 		sm->ignore = TRUE;
-		tls_connection_deinit(data->ssl_ctx, data->conn);
-		data->conn = NULL;
-		return -1;
-	} else if (res) {
+	} else if (res == TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED) {
+		wpa_printf(MSG_INFO, "TLS: Failed to initialize engine");
+	} else if (res == TLS_SET_PARAMS_ENGINE_PRV_VERIFY_FAILED) {
+		wpa_printf(MSG_INFO, "TLS: Failed to load private key");
+		sm->ignore = TRUE;
+	}
+	if (res) {
 		wpa_printf(MSG_INFO, "TLS: Failed to set TLS connection "
 			   "parameters");
 		tls_connection_deinit(data->ssl_ctx, data->conn);
@@ -307,53 +310,19 @@
 u8 * eap_peer_tls_derive_key(struct eap_sm *sm, struct eap_ssl_data *data,
 			     const char *label, size_t len)
 {
-#ifndef CONFIG_FIPS
-	struct tls_keys keys;
-#endif /* CONFIG_FIPS */
-	u8 *rnd = NULL, *out;
+	u8 *out;
 
 	out = os_malloc(len);
 	if (out == NULL)
 		return NULL;
 
-	/* First, try to use TLS library function for PRF, if available. */
-	if (tls_connection_prf(data->ssl_ctx, data->conn, label, 0, out, len)
-	    == 0)
-		return out;
+	if (tls_connection_prf(data->ssl_ctx, data->conn, label, 0, 0,
+			       out, len)) {
+		os_free(out);
+		return NULL;
+	}
 
-#ifndef CONFIG_FIPS
-	/*
-	 * TLS library did not support key generation, so get the needed TLS
-	 * session parameters and use an internal implementation of TLS PRF to
-	 * derive the key.
-	 */
-	if (tls_connection_get_keys(data->ssl_ctx, data->conn, &keys))
-		goto fail;
-
-	if (keys.client_random == NULL || keys.server_random == NULL ||
-	    keys.master_key == NULL)
-		goto fail;
-
-	rnd = os_malloc(keys.client_random_len + keys.server_random_len);
-	if (rnd == NULL)
-		goto fail;
-	os_memcpy(rnd, keys.client_random, keys.client_random_len);
-	os_memcpy(rnd + keys.client_random_len, keys.server_random,
-		  keys.server_random_len);
-
-	if (tls_prf_sha1_md5(keys.master_key, keys.master_key_len,
-			     label, rnd, keys.client_random_len +
-			     keys.server_random_len, out, len))
-		goto fail;
-
-	os_free(rnd);
 	return out;
-
-fail:
-#endif /* CONFIG_FIPS */
-	os_free(out);
-	os_free(rnd);
-	return NULL;
 }
 
 
@@ -377,15 +346,10 @@
 	struct tls_keys keys;
 	u8 *out;
 
-	/*
-	 * TLS library did not support session ID generation,
-	 * so get the needed TLS session parameters
-	 */
 	if (tls_connection_get_keys(sm->ssl_ctx, data->conn, &keys))
 		return NULL;
 
-	if (keys.client_random == NULL || keys.server_random == NULL ||
-	    keys.master_key == NULL)
+	if (keys.client_random == NULL || keys.server_random == NULL)
 		return NULL;
 
 	*len = 1 + keys.client_random_len + keys.server_random_len;
@@ -397,7 +361,7 @@
 	out[0] = eap_type;
 	os_memcpy(out + 1, keys.client_random, keys.client_random_len);
 	os_memcpy(out + 1 + keys.client_random_len, keys.server_random,
-	          keys.server_random_len);
+		  keys.server_random_len);
 
 	return out;
 }
@@ -513,22 +477,19 @@
  * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
  * @data: Data for TLS processing
  * @in_data: Message received from the server
- * @in_len: Length of in_data
  * @out_data: Buffer for returning a pointer to application data (if available)
  * Returns: 0 on success, 1 if more input data is needed, 2 if application data
  * is available, -1 on failure
  */
 static int eap_tls_process_input(struct eap_sm *sm, struct eap_ssl_data *data,
-				 const u8 *in_data, size_t in_len,
+				 const struct wpabuf *in_data,
 				 struct wpabuf **out_data)
 {
 	const struct wpabuf *msg;
 	int need_more_input;
 	struct wpabuf *appl_data;
-	struct wpabuf buf;
 
-	wpabuf_set(&buf, in_data, in_len);
-	msg = eap_peer_tls_data_reassemble(data, &buf, &need_more_input);
+	msg = eap_peer_tls_data_reassemble(data, in_data, &need_more_input);
 	if (msg == NULL)
 		return need_more_input ? 1 : -1;
 
@@ -648,7 +609,6 @@
  * @peap_version: Version number for EAP-PEAP/TTLS
  * @id: EAP identifier for the response
  * @in_data: Message received from the server
- * @in_len: Length of in_data
  * @out_data: Buffer for returning a pointer to the response message
  * Returns: 0 on success, 1 if more input data is needed, 2 if application data
  * is available, or -1 on failure
@@ -671,14 +631,15 @@
  */
 int eap_peer_tls_process_helper(struct eap_sm *sm, struct eap_ssl_data *data,
 				EapType eap_type, int peap_version,
-				u8 id, const u8 *in_data, size_t in_len,
+				u8 id, const struct wpabuf *in_data,
 				struct wpabuf **out_data)
 {
 	int ret = 0;
 
 	*out_data = NULL;
 
-	if (data->tls_out && wpabuf_len(data->tls_out) > 0 && in_len > 0) {
+	if (data->tls_out && wpabuf_len(data->tls_out) > 0 &&
+	    wpabuf_len(in_data) > 0) {
 		wpa_printf(MSG_DEBUG, "SSL: Received non-ACK when output "
 			   "fragments are waiting to be sent out");
 		return -1;
@@ -689,8 +650,7 @@
 		 * No more data to send out - expect to receive more data from
 		 * the AS.
 		 */
-		int res = eap_tls_process_input(sm, data, in_data, in_len,
-						out_data);
+		int res = eap_tls_process_input(sm, data, in_data, out_data);
 		if (res) {
 			/*
 			 * Input processing failed (res = -1) or more data is
@@ -795,8 +755,11 @@
 	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", name);
-		if (ret < 0 || (size_t) ret >= 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;
 	}
@@ -1028,7 +991,7 @@
 {
 	char *start, *pos, *buf;
 	struct eap_method_type *methods = NULL, *_methods;
-	u8 method;
+	u32 method;
 	size_t num_methods = 0, prefix_len;
 
 	if (config == NULL || config->phase2 == NULL)
diff --git a/src/eap_peer/eap_tls_common.h b/src/eap_peer/eap_tls_common.h
index 390c216..acd2b78 100644
--- a/src/eap_peer/eap_tls_common.h
+++ b/src/eap_peer/eap_tls_common.h
@@ -100,7 +100,7 @@
 				    size_t *len);
 int eap_peer_tls_process_helper(struct eap_sm *sm, struct eap_ssl_data *data,
 				EapType eap_type, int peap_version,
-				u8 id, const u8 *in_data, size_t in_len,
+				u8 id, const struct wpabuf *in_data,
 				struct wpabuf **out_data);
 struct wpabuf * eap_peer_tls_build_ack(u8 id, EapType eap_type,
 				       int peap_version);
diff --git a/src/eap_peer/eap_ttls.c b/src/eap_peer/eap_ttls.c
index 771da58..25e3cba 100644
--- a/src/eap_peer/eap_ttls.c
+++ b/src/eap_peer/eap_ttls.c
@@ -136,7 +136,7 @@
 static void eap_ttls_free_key(struct eap_ttls_data *data)
 {
 	if (data->key_data) {
-		bin_clear_free(data->key_data, EAP_TLS_KEY_LEN);
+		bin_clear_free(data->key_data, EAP_TLS_KEY_LEN + EAP_EMSK_LEN);
 		data->key_data = NULL;
 	}
 }
@@ -225,7 +225,8 @@
 	eap_ttls_free_key(data);
 	data->key_data = eap_peer_tls_derive_key(sm, &data->ssl,
 						 "ttls keying material",
-						 EAP_TLS_KEY_LEN);
+						 EAP_TLS_KEY_LEN +
+						 EAP_EMSK_LEN);
 	if (!data->key_data) {
 		wpa_printf(MSG_INFO, "EAP-TTLS: Failed to derive key");
 		return -1;
@@ -233,6 +234,9 @@
 
 	wpa_hexdump_key(MSG_DEBUG, "EAP-TTLS: Derived key",
 			data->key_data, EAP_TLS_KEY_LEN);
+	wpa_hexdump_key(MSG_DEBUG, "EAP-TTLS: Derived EMSK",
+			data->key_data + EAP_TLS_KEY_LEN,
+			EAP_EMSK_LEN);
 
 	os_free(data->session_id);
 	data->session_id = eap_peer_tls_derive_session_id(sm, &data->ssl,
@@ -991,6 +995,7 @@
 				 resp, out_data)) {
 		wpa_printf(MSG_INFO, "EAP-TTLS: Failed to encrypt a Phase 2 "
 			   "frame");
+		wpabuf_free(resp);
 		return -1;
 	}
 	wpabuf_free(resp);
@@ -1380,14 +1385,14 @@
 				      struct eap_ttls_data *data,
 				      struct eap_method_ret *ret,
 				      u8 identifier,
-				      const u8 *in_data, size_t in_len,
+				      const struct wpabuf *in_data,
 				      struct wpabuf **out_data)
 {
 	int res;
 
 	res = eap_peer_tls_process_helper(sm, &data->ssl, EAP_TYPE_TTLS,
 					  data->ttls_version, identifier,
-					  in_data, in_len, out_data);
+					  in_data, out_data);
 
 	if (tls_connection_established(sm->ssl_ctx, data->ssl.conn)) {
 		wpa_printf(MSG_DEBUG, "EAP-TTLS: TLS done, proceed to "
@@ -1414,15 +1419,13 @@
 	}
 
 	if (res == 2) {
-		struct wpabuf msg;
 		/*
 		 * Application data included in the handshake message.
 		 */
 		wpabuf_free(data->pending_phase2_req);
 		data->pending_phase2_req = *out_data;
 		*out_data = NULL;
-		wpabuf_set(&msg, in_data, in_len);
-		res = eap_ttls_decrypt(sm, data, ret, identifier, &msg,
+		res = eap_ttls_decrypt(sm, data, ret, identifier, in_data,
 				       out_data);
 	}
 
@@ -1472,6 +1475,7 @@
 	struct wpabuf *resp;
 	const u8 *pos;
 	struct eap_ttls_data *data = priv;
+	struct wpabuf msg;
 
 	pos = eap_peer_tls_process_init(sm, &data->ssl, EAP_TYPE_TTLS, ret,
 					reqData, &left, &flags);
@@ -1492,15 +1496,15 @@
 		left = 0;
 	}
 
+	wpabuf_set(&msg, pos, left);
+
 	resp = NULL;
 	if (tls_connection_established(sm->ssl_ctx, data->ssl.conn) &&
 	    !data->resuming) {
-		struct wpabuf msg;
-		wpabuf_set(&msg, pos, left);
 		res = eap_ttls_decrypt(sm, data, ret, id, &msg, &resp);
 	} else {
 		res = eap_ttls_process_handshake(sm, data, ret, id,
-						 pos, left, &resp);
+						 &msg, &resp);
 	}
 
 	eap_ttls_check_auth_status(sm, data, ret);
@@ -1567,7 +1571,7 @@
 	ret = os_snprintf(buf + len, buflen - len,
 			  "EAP-TTLSv%d Phase2 method=",
 			  data->ttls_version);
-	if (ret < 0 || (size_t) ret >= buflen - len)
+	if (os_snprintf_error(buflen - len, ret))
 		return len;
 	len += ret;
 	switch (data->phase2_type) {
@@ -1592,7 +1596,7 @@
 		ret = 0;
 		break;
 	}
-	if (ret < 0 || (size_t) ret >= buflen - len)
+	if (os_snprintf_error(buflen - len, ret))
 		return len;
 	len += ret;
 
@@ -1645,6 +1649,25 @@
 }
 
 
+static u8 * eap_ttls_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
+{
+	struct eap_ttls_data *data = priv;
+	u8 *key;
+
+	if (data->key_data == NULL)
+		return NULL;
+
+	key = os_malloc(EAP_EMSK_LEN);
+	if (key == NULL)
+		return NULL;
+
+	*len = EAP_EMSK_LEN;
+	os_memcpy(key, data->key_data + EAP_TLS_KEY_LEN, EAP_EMSK_LEN);
+
+	return key;
+}
+
+
 int eap_peer_ttls_register(void)
 {
 	struct eap_method *eap;
@@ -1665,6 +1688,7 @@
 	eap->has_reauth_data = eap_ttls_has_reauth_data;
 	eap->deinit_for_reauth = eap_ttls_deinit_for_reauth;
 	eap->init_for_reauth = eap_ttls_init_for_reauth;
+	eap->get_emsk = eap_ttls_get_emsk;
 
 	ret = eap_peer_method_register(eap);
 	if (ret)
diff --git a/src/eap_peer/eap_vendor_test.c b/src/eap_peer/eap_vendor_test.c
index 040d1e7..b61057e 100644
--- a/src/eap_peer/eap_vendor_test.c
+++ b/src/eap_peer/eap_vendor_test.c
@@ -1,6 +1,6 @@
 /*
  * EAP peer method: Test method for vendor specific (expanded) EAP type
- * Copyright (c) 2005-2006, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2005-2015, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -14,31 +14,36 @@
 
 #include "common.h"
 #include "eap_i.h"
-#ifdef TEST_PENDING_REQUEST
 #include "eloop.h"
-#endif /* TEST_PENDING_REQUEST */
 
 
 #define EAP_VENDOR_ID EAP_VENDOR_HOSTAP
 #define EAP_VENDOR_TYPE 0xfcfbfaf9
 
 
-/* #define TEST_PENDING_REQUEST */
-
 struct eap_vendor_test_data {
 	enum { INIT, CONFIRM, SUCCESS } state;
 	int first_try;
+	int test_pending_req;
 };
 
 
 static void * eap_vendor_test_init(struct eap_sm *sm)
 {
 	struct eap_vendor_test_data *data;
+	const u8 *password;
+	size_t password_len;
+
 	data = os_zalloc(sizeof(*data));
 	if (data == NULL)
 		return NULL;
 	data->state = INIT;
 	data->first_try = 1;
+
+	password = eap_get_config_password(sm, &password_len);
+	data->test_pending_req = password && password_len == 7 &&
+		os_memcmp(password, "pending", 7) == 0;
+
 	return data;
 }
 
@@ -50,7 +55,6 @@
 }
 
 
-#ifdef TEST_PENDING_REQUEST
 static void eap_vendor_ready(void *eloop_ctx, void *timeout_ctx)
 {
 	struct eap_sm *sm = eloop_ctx;
@@ -58,7 +62,6 @@
 		   "request");
 	eap_notify_pending(sm);
 }
-#endif /* TEST_PENDING_REQUEST */
 
 
 static struct wpabuf * eap_vendor_test_process(struct eap_sm *sm, void *priv,
@@ -98,8 +101,7 @@
 	}
 
 	if (data->state == CONFIRM) {
-#ifdef TEST_PENDING_REQUEST
-		if (data->first_try) {
+		if (data->test_pending_req && data->first_try) {
 			data->first_try = 0;
 			wpa_printf(MSG_DEBUG, "EAP-VENDOR-TEST: Testing "
 				   "pending request");
@@ -108,7 +110,6 @@
 					       NULL);
 			return NULL;
 		}
-#endif /* TEST_PENDING_REQUEST */
 	}
 
 	ret->ignore = FALSE;
diff --git a/src/eap_peer/eap_wsc.c b/src/eap_peer/eap_wsc.c
index 23e9823..7ce0a53 100644
--- a/src/eap_peer/eap_wsc.c
+++ b/src/eap_peer/eap_wsc.c
@@ -462,7 +462,7 @@
 		message_length = WPA_GET_BE16(pos);
 		pos += 2;
 
-		if (message_length < end - pos) {
+		if (message_length < end - pos || message_length > 50000) {
 			wpa_printf(MSG_DEBUG, "EAP-WSC: Invalid Message "
 				   "Length");
 			ret->ignore = TRUE;
diff --git a/src/eap_peer/ikev2.c b/src/eap_peer/ikev2.c
index 8186afb..55ab72a 100644
--- a/src/eap_peer/ikev2.c
+++ b/src/eap_peer/ikev2.c
@@ -213,7 +213,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;
@@ -369,7 +369,7 @@
 	}
 
 	if (kei_len < 4 + 96) {
-		wpa_printf(MSG_INFO, "IKEV2: Too show Key Exchange Payload");
+		wpa_printf(MSG_INFO, "IKEV2: Too short Key Exchange Payload");
 		return -1;
 	}
 
diff --git a/src/eap_server/Makefile b/src/eap_server/Makefile
index adfd3df..1172b72 100644
--- a/src/eap_server/Makefile
+++ b/src/eap_server/Makefile
@@ -1,8 +1,21 @@
-all:
-	@echo Nothing to be made.
+all: libeap_server.a
 
 clean:
-	rm -f *~ *.o *.d *.gcno *.gcda *.gcov
+	rm -f *~ *.o *.d *.gcno *.gcda *.gcov libeap_server.a
 
 install:
 	@echo Nothing to be made.
+
+include ../lib.rules
+
+CFLAGS += -DCONFIG_HS20
+
+LIB_OBJS= \
+	eap_server.o \
+	eap_server_identity.o \
+	eap_server_methods.o
+
+libeap_server.a: $(LIB_OBJS)
+	$(AR) crT $@ $?
+
+-include $(OBJS:%.o=%.d)
diff --git a/src/eap_server/eap.h b/src/eap_server/eap.h
index 1253bd6..09be581 100644
--- a/src/eap_server/eap.h
+++ b/src/eap_server/eap.h
@@ -1,6 +1,6 @@
 /*
  * hostapd / EAP Full Authenticator state machine (RFC 4137)
- * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2014, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -10,6 +10,7 @@
 #define EAP_H
 
 #include "common/defs.h"
+#include "utils/list.h"
 #include "eap_common/eap_defs.h"
 #include "eap_server/eap_methods.h"
 #include "wpabuf.h"
@@ -58,6 +59,8 @@
 	struct wpabuf *eapReqData;
 	u8 *eapKeyData;
 	size_t eapKeyDataLen;
+	u8 *eapSessionId;
+	size_t eapSessionIdLen;
 	Boolean eapKeyAvailable; /* called keyAvailable in IEEE 802.1X-2004 */
 
 	/* AAA interface to full authenticator variables */
@@ -78,11 +81,27 @@
 	Boolean aaaTimeout;
 };
 
+struct eap_server_erp_key {
+	struct dl_list list;
+	size_t rRK_len;
+	size_t rIK_len;
+	u8 rRK[ERP_MAX_KEY_LEN];
+	u8 rIK[ERP_MAX_KEY_LEN];
+	u32 recv_seq;
+	u8 cryptosuite;
+	char keyname_nai[];
+};
+
 struct eapol_callbacks {
 	int (*get_eap_user)(void *ctx, const u8 *identity, size_t identity_len,
 			    int phase2, struct eap_user *user);
 	const char * (*get_eap_req_id_text)(void *ctx, size_t *len);
 	void (*log_msg)(void *ctx, const char *msg);
+	int (*get_erp_send_reauth_start)(void *ctx);
+	const char * (*get_erp_domain)(void *ctx);
+	struct eap_server_erp_key * (*erp_get_key)(void *ctx,
+						   const char *keyname);
+	int (*erp_add_key)(void *ctx, struct eap_server_erp_key *erp);
 };
 
 struct eap_config {
@@ -111,6 +130,7 @@
 
 	const u8 *server_id;
 	size_t server_id_len;
+	int erp;
 
 #ifdef CONFIG_TESTING_OPTIONS
 	u32 tls_test_flags;
@@ -119,7 +139,7 @@
 
 
 struct eap_sm * eap_server_sm_init(void *eapol_ctx,
-				   struct eapol_callbacks *eapol_cb,
+				   const struct eapol_callbacks *eapol_cb,
 				   struct eap_config *eap_conf);
 void eap_server_sm_deinit(struct eap_sm *sm);
 int eap_server_sm_step(struct eap_sm *sm);
@@ -129,5 +149,8 @@
 const u8 * eap_get_identity(struct eap_sm *sm, size_t *len);
 struct eap_eapol_interface * eap_get_interface(struct eap_sm *sm);
 void eap_server_clear_identity(struct eap_sm *sm);
+void eap_server_mschap_rx_callback(struct eap_sm *sm, const char *source,
+				   const u8 *username, size_t username_len,
+				   const u8 *challenge, const u8 *response);
 
 #endif /* EAP_H */
diff --git a/src/eap_server/eap_i.h b/src/eap_server/eap_i.h
index 3a6802b..978c879 100644
--- a/src/eap_server/eap_i.h
+++ b/src/eap_server/eap_i.h
@@ -88,6 +88,19 @@
 	 * private data or this function may derive the key.
 	 */
 	u8 * (*get_emsk)(struct eap_sm *sm, void *priv, size_t *len);
+
+	/**
+	 * getSessionId - Get EAP method specific Session-Id
+	 * @sm: Pointer to EAP state machine allocated with eap_server_sm_init()
+	 * @priv: Pointer to private EAP method data from eap_method::init()
+	 * @len: Pointer to a variable to store Session-Id length
+	 * Returns: Session-Id or %NULL if not available
+	 *
+	 * This function can be used to get the Session-Id from the EAP method.
+	 * The Session-Id may already be stored in the method-specific private
+	 * data or this function may derive the Session-Id.
+	 */
+	u8 * (*getSessionId)(struct eap_sm *sm, void *priv, size_t *len);
 };
 
 /**
@@ -103,7 +116,8 @@
 		EAP_INITIALIZE_PASSTHROUGH, EAP_IDLE2, EAP_RETRANSMIT2,
 		EAP_RECEIVED2, EAP_DISCARD2, EAP_SEND_REQUEST2,
 		EAP_AAA_REQUEST, EAP_AAA_RESPONSE, EAP_AAA_IDLE,
-		EAP_TIMEOUT_FAILURE2, EAP_FAILURE2, EAP_SUCCESS2
+		EAP_TIMEOUT_FAILURE2, EAP_FAILURE2, EAP_SUCCESS2,
+		EAP_INITIATE_REAUTH_START, EAP_INITIATE_RECEIVED
 	} EAP_state;
 
 	/* Constants */
@@ -125,6 +139,7 @@
 
 	/* Short-term (not maintained between packets) */
 	Boolean rxResp;
+	Boolean rxInitiate;
 	int respId;
 	EapType respMethod;
 	int respVendor;
@@ -132,7 +147,7 @@
 	Boolean ignore;
 	enum {
 		DECISION_SUCCESS, DECISION_FAILURE, DECISION_CONTINUE,
-		DECISION_PASSTHROUGH
+		DECISION_PASSTHROUGH, DECISION_INITIATE_REAUTH_START
 	} decision;
 
 	/* Miscellaneous variables */
@@ -140,7 +155,7 @@
 	/* not defined in RFC 4137 */
 	Boolean changed;
 	void *eapol_ctx, *msg_ctx;
-	struct eapol_callbacks *eapol_cb;
+	const struct eapol_callbacks *eapol_cb;
 	void *eap_method_priv;
 	u8 *identity;
 	size_t identity_len;
@@ -192,6 +207,10 @@
 	const u8 *server_id;
 	size_t server_id_len;
 
+	Boolean initiate_reauth_start_sent;
+	Boolean try_initiate_reauth;
+	int erp;
+
 #ifdef CONFIG_TESTING_OPTIONS
 	u32 tls_test_flags;
 #endif /* CONFIG_TESTING_OPTIONS */
diff --git a/src/eap_server/eap_server.c b/src/eap_server/eap_server.c
index c1bb6b8..6651229 100644
--- a/src/eap_server/eap_server.c
+++ b/src/eap_server/eap_server.c
@@ -1,6 +1,6 @@
 /*
  * hostapd / EAP Full Authenticator state machine (RFC 4137)
- * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2014, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -15,6 +15,7 @@
 #include "includes.h"
 
 #include "common.h"
+#include "crypto/sha256.h"
 #include "eap_i.h"
 #include "state_machine.h"
 #include "common/wpa_ctrl.h"
@@ -44,6 +45,73 @@
 static Boolean eap_sm_Policy_doPickUp(struct eap_sm *sm, EapType method);
 
 
+static int eap_get_erp_send_reauth_start(struct eap_sm *sm)
+{
+	if (sm->eapol_cb->get_erp_send_reauth_start)
+		return sm->eapol_cb->get_erp_send_reauth_start(sm->eapol_ctx);
+	return 0;
+}
+
+
+static const char * eap_get_erp_domain(struct eap_sm *sm)
+{
+	if (sm->eapol_cb->get_erp_domain)
+		return sm->eapol_cb->get_erp_domain(sm->eapol_ctx);
+	return NULL;
+}
+
+
+#ifdef CONFIG_ERP
+
+static struct eap_server_erp_key * eap_erp_get_key(struct eap_sm *sm,
+						   const char *keyname)
+{
+	if (sm->eapol_cb->erp_get_key)
+		return sm->eapol_cb->erp_get_key(sm->eapol_ctx, keyname);
+	return NULL;
+}
+
+
+static int eap_erp_add_key(struct eap_sm *sm, struct eap_server_erp_key *erp)
+{
+	if (sm->eapol_cb->erp_add_key)
+		return sm->eapol_cb->erp_add_key(sm->eapol_ctx, erp);
+	return -1;
+}
+
+#endif /* CONFIG_ERP */
+
+
+static struct wpabuf * eap_sm_buildInitiateReauthStart(struct eap_sm *sm,
+						       u8 id)
+{
+	const char *domain;
+	size_t plen = 1;
+	struct wpabuf *msg;
+	size_t domain_len = 0;
+
+	domain = eap_get_erp_domain(sm);
+	if (domain) {
+		domain_len = os_strlen(domain);
+		plen += 2 + domain_len;
+	}
+
+	msg = eap_msg_alloc(EAP_VENDOR_IETF, EAP_ERP_TYPE_REAUTH_START, plen,
+			    EAP_CODE_INITIATE, id);
+	if (msg == NULL)
+		return NULL;
+	wpabuf_put_u8(msg, 0); /* Reserved */
+	if (domain) {
+		/* Domain name TLV */
+		wpabuf_put_u8(msg, EAP_ERP_TLV_DOMAIN_NAME);
+		wpabuf_put_u8(msg, domain_len);
+		wpabuf_put_data(msg, domain, domain_len);
+	}
+
+	return msg;
+}
+
+
 static int eap_copy_buf(struct wpabuf **dst, const struct wpabuf *src)
 {
 	if (src == NULL)
@@ -164,6 +232,7 @@
 		eap_server_clear_identity(sm);
 	}
 
+	sm->try_initiate_reauth = FALSE;
 	sm->currentId = -1;
 	sm->eap_if.eapSuccess = FALSE;
 	sm->eap_if.eapFail = FALSE;
@@ -171,6 +240,9 @@
 	bin_clear_free(sm->eap_if.eapKeyData, sm->eap_if.eapKeyDataLen);
 	sm->eap_if.eapKeyData = NULL;
 	sm->eap_if.eapKeyDataLen = 0;
+	os_free(sm->eap_if.eapSessionId);
+	sm->eap_if.eapSessionId = NULL;
+	sm->eap_if.eapSessionIdLen = 0;
 	sm->eap_if.eapKeyAvailable = FALSE;
 	sm->eap_if.eapRestart = FALSE;
 
@@ -336,6 +408,95 @@
 }
 
 
+static void eap_server_erp_init(struct eap_sm *sm)
+{
+#ifdef CONFIG_ERP
+	u8 *emsk = NULL;
+	size_t emsk_len = 0;
+	u8 EMSKname[EAP_EMSK_NAME_LEN];
+	u8 len[2];
+	const char *domain;
+	size_t domain_len, nai_buf_len;
+	struct eap_server_erp_key *erp = NULL;
+	int pos;
+
+	domain = eap_get_erp_domain(sm);
+	if (!domain)
+		return;
+
+	domain_len = os_strlen(domain);
+
+	nai_buf_len = 2 * EAP_EMSK_NAME_LEN + 1 + domain_len;
+	if (nai_buf_len > 253) {
+		/*
+		 * keyName-NAI has a maximum length of 253 octet to fit in
+		 * RADIUS attributes.
+		 */
+		wpa_printf(MSG_DEBUG,
+			   "EAP: Too long realm for ERP keyName-NAI maximum length");
+		return;
+	}
+	nai_buf_len++; /* null termination */
+	erp = os_zalloc(sizeof(*erp) + nai_buf_len);
+	if (erp == NULL)
+		goto fail;
+	erp->recv_seq = (u32) -1;
+
+	emsk = sm->m->get_emsk(sm, sm->eap_method_priv, &emsk_len);
+	if (!emsk || emsk_len == 0 || emsk_len > ERP_MAX_KEY_LEN) {
+		wpa_printf(MSG_DEBUG,
+			   "EAP: No suitable EMSK available for ERP");
+		goto fail;
+	}
+
+	wpa_hexdump_key(MSG_DEBUG, "EAP: EMSK", emsk, emsk_len);
+
+	WPA_PUT_BE16(len, 8);
+	if (hmac_sha256_kdf(sm->eap_if.eapSessionId, sm->eap_if.eapSessionIdLen,
+			    "EMSK", len, sizeof(len),
+			    EMSKname, EAP_EMSK_NAME_LEN) < 0) {
+		wpa_printf(MSG_DEBUG, "EAP: Could not derive EMSKname");
+		goto fail;
+	}
+	wpa_hexdump(MSG_DEBUG, "EAP: EMSKname", EMSKname, EAP_EMSK_NAME_LEN);
+
+	pos = wpa_snprintf_hex(erp->keyname_nai, nai_buf_len,
+			       EMSKname, EAP_EMSK_NAME_LEN);
+	erp->keyname_nai[pos] = '@';
+	os_memcpy(&erp->keyname_nai[pos + 1], domain, domain_len);
+
+	WPA_PUT_BE16(len, emsk_len);
+	if (hmac_sha256_kdf(emsk, emsk_len,
+			    "EAP Re-authentication Root Key@ietf.org",
+			    len, sizeof(len), erp->rRK, emsk_len) < 0) {
+		wpa_printf(MSG_DEBUG, "EAP: Could not derive rRK for ERP");
+		goto fail;
+	}
+	erp->rRK_len = emsk_len;
+	wpa_hexdump_key(MSG_DEBUG, "EAP: ERP rRK", erp->rRK, erp->rRK_len);
+
+	if (hmac_sha256_kdf(erp->rRK, erp->rRK_len,
+			    "EAP Re-authentication Integrity Key@ietf.org",
+			    len, sizeof(len), erp->rIK, erp->rRK_len) < 0) {
+		wpa_printf(MSG_DEBUG, "EAP: Could not derive rIK for ERP");
+		goto fail;
+	}
+	erp->rIK_len = erp->rRK_len;
+	wpa_hexdump_key(MSG_DEBUG, "EAP: ERP rIK", erp->rIK, erp->rIK_len);
+
+	if (eap_erp_add_key(sm, erp) == 0) {
+		wpa_printf(MSG_DEBUG, "EAP: Stored ERP keys %s",
+			   erp->keyname_nai);
+		erp = NULL;
+	}
+
+fail:
+	bin_clear_free(emsk, emsk_len);
+	bin_clear_free(erp, sizeof(*erp));
+#endif /* CONFIG_ERP */
+}
+
+
 SM_STATE(EAP, METHOD_RESPONSE)
 {
 	SM_ENTRY(EAP, METHOD_RESPONSE);
@@ -355,6 +516,18 @@
 			sm->eap_if.eapKeyData = NULL;
 			sm->eap_if.eapKeyDataLen = 0;
 		}
+		os_free(sm->eap_if.eapSessionId);
+		sm->eap_if.eapSessionId = NULL;
+		if (sm->m->getSessionId) {
+			sm->eap_if.eapSessionId = sm->m->getSessionId(
+				sm, sm->eap_method_priv,
+				&sm->eap_if.eapSessionIdLen);
+			wpa_hexdump(MSG_DEBUG, "EAP: Session-Id",
+				    sm->eap_if.eapSessionId,
+				    sm->eap_if.eapSessionIdLen);
+		}
+		if (sm->erp && sm->m->get_emsk && sm->eap_if.eapSessionId)
+			eap_server_erp_init(sm);
 		sm->methodState = METHOD_END;
 	} else {
 		sm->methodState = METHOD_CONTINUE;
@@ -369,6 +542,7 @@
 
 	SM_ENTRY(EAP, PROPOSE_METHOD);
 
+	sm->try_initiate_reauth = FALSE;
 try_another_method:
 	type = eap_sm_Policy_getNextMethod(sm, &vendor);
 	if (vendor == EAP_VENDOR_IETF)
@@ -492,12 +666,326 @@
 }
 
 
+SM_STATE(EAP, INITIATE_REAUTH_START)
+{
+	SM_ENTRY(EAP, INITIATE_REAUTH_START);
+
+	sm->initiate_reauth_start_sent = TRUE;
+	sm->try_initiate_reauth = TRUE;
+	sm->currentId = eap_sm_nextId(sm, sm->currentId);
+	wpa_printf(MSG_DEBUG,
+		   "EAP: building EAP-Initiate-Re-auth-Start: Identifier %d",
+		   sm->currentId);
+	sm->lastId = sm->currentId;
+	wpabuf_free(sm->eap_if.eapReqData);
+	sm->eap_if.eapReqData = eap_sm_buildInitiateReauthStart(sm,
+								sm->currentId);
+	wpabuf_free(sm->lastReqData);
+	sm->lastReqData = NULL;
+}
+
+
+#ifdef CONFIG_ERP
+
+static void erp_send_finish_reauth(struct eap_sm *sm,
+				   struct eap_server_erp_key *erp, u8 id,
+				   u8 flags, u16 seq, const char *nai)
+{
+	size_t plen;
+	struct wpabuf *msg;
+	u8 hash[SHA256_MAC_LEN];
+	size_t hash_len;
+	u8 seed[4];
+
+	if (erp) {
+		switch (erp->cryptosuite) {
+		case EAP_ERP_CS_HMAC_SHA256_256:
+			hash_len = 32;
+			break;
+		case EAP_ERP_CS_HMAC_SHA256_128:
+			hash_len = 16;
+			break;
+		default:
+			return;
+		}
+	} else
+		hash_len = 0;
+
+	plen = 1 + 2 + 2 + os_strlen(nai);
+	if (hash_len)
+		plen += 1 + hash_len;
+	msg = eap_msg_alloc(EAP_VENDOR_IETF, EAP_ERP_TYPE_REAUTH, plen,
+			    EAP_CODE_FINISH, id);
+	if (msg == NULL)
+		return;
+	wpabuf_put_u8(msg, flags);
+	wpabuf_put_be16(msg, seq);
+
+	wpabuf_put_u8(msg, EAP_ERP_TLV_KEYNAME_NAI);
+	wpabuf_put_u8(msg, os_strlen(nai));
+	wpabuf_put_str(msg, nai);
+
+	if (erp) {
+		wpabuf_put_u8(msg, erp->cryptosuite);
+		if (hmac_sha256(erp->rIK, erp->rIK_len,
+				wpabuf_head(msg), wpabuf_len(msg), hash) < 0) {
+			wpabuf_free(msg);
+			return;
+		}
+		wpabuf_put_data(msg, hash, hash_len);
+	}
+
+	wpa_printf(MSG_DEBUG, "EAP: Send EAP-Finish/Re-auth (%s)",
+		   flags & 0x80 ? "failure" : "success");
+
+	sm->lastId = sm->currentId;
+	sm->currentId = id;
+	wpabuf_free(sm->eap_if.eapReqData);
+	sm->eap_if.eapReqData = msg;
+	wpabuf_free(sm->lastReqData);
+	sm->lastReqData = NULL;
+
+	if ((flags & 0x80) || !erp) {
+		sm->eap_if.eapFail = TRUE;
+		wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_FAILURE
+			MACSTR, MAC2STR(sm->peer_addr));
+		return;
+	}
+
+	bin_clear_free(sm->eap_if.eapKeyData, sm->eap_if.eapKeyDataLen);
+	sm->eap_if.eapKeyDataLen = 0;
+	sm->eap_if.eapKeyData = os_malloc(erp->rRK_len);
+	if (!sm->eap_if.eapKeyData)
+		return;
+
+	WPA_PUT_BE16(seed, seq);
+	WPA_PUT_BE16(&seed[2], erp->rRK_len);
+	if (hmac_sha256_kdf(erp->rRK, erp->rRK_len,
+			    "Re-authentication Master Session Key@ietf.org",
+			    seed, sizeof(seed),
+			    sm->eap_if.eapKeyData, erp->rRK_len) < 0) {
+		wpa_printf(MSG_DEBUG, "EAP: Could not derive rMSK for ERP");
+		bin_clear_free(sm->eap_if.eapKeyData, erp->rRK_len);
+		sm->eap_if.eapKeyData = NULL;
+		return;
+	}
+	sm->eap_if.eapKeyDataLen = erp->rRK_len;
+	sm->eap_if.eapKeyAvailable = TRUE;
+	wpa_hexdump_key(MSG_DEBUG, "EAP: ERP rMSK",
+			sm->eap_if.eapKeyData, sm->eap_if.eapKeyDataLen);
+	sm->eap_if.eapSuccess = TRUE;
+
+	wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_SUCCESS
+		MACSTR, MAC2STR(sm->peer_addr));
+}
+
+
+SM_STATE(EAP, INITIATE_RECEIVED)
+{
+	const u8 *pos, *end, *start, *tlvs, *hdr;
+	const struct eap_hdr *ehdr;
+	size_t len;
+	u8 flags;
+	u16 seq;
+	char nai[254];
+	struct eap_server_erp_key *erp;
+	int max_len;
+	u8 hash[SHA256_MAC_LEN];
+	size_t hash_len;
+	struct erp_tlvs parse;
+	u8 resp_flags = 0x80; /* default to failure; cleared on success */
+
+	SM_ENTRY(EAP, INITIATE_RECEIVED);
+
+	sm->rxInitiate = FALSE;
+
+	pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_ERP_TYPE_REAUTH,
+			       sm->eap_if.eapRespData, &len);
+	if (pos == NULL) {
+		wpa_printf(MSG_INFO, "EAP-Initiate: Invalid frame");
+		goto fail;
+	}
+	hdr = wpabuf_head(sm->eap_if.eapRespData);
+	ehdr = wpabuf_head(sm->eap_if.eapRespData);
+
+	wpa_hexdump(MSG_DEBUG, "EAP: EAP-Initiate/Re-Auth", pos, len);
+	if (len < 4) {
+		wpa_printf(MSG_INFO, "EAP: Too short EAP-Initiate/Re-auth");
+		goto fail;
+	}
+	end = pos + len;
+
+	flags = *pos++;
+	seq = WPA_GET_BE16(pos);
+	pos += 2;
+	wpa_printf(MSG_DEBUG, "EAP: Flags=0x%x SEQ=%u", flags, seq);
+	tlvs = pos;
+
+	/*
+	 * Parse TVs/TLVs. Since we do not yet know the length of the
+	 * Authentication Tag, stop parsing if an unknown TV/TLV is seen and
+	 * just try to find the keyName-NAI first so that we can check the
+	 * Authentication Tag.
+	 */
+	if (erp_parse_tlvs(tlvs, end, &parse, 1) < 0)
+		goto fail;
+
+	if (!parse.keyname) {
+		wpa_printf(MSG_DEBUG,
+			   "EAP: No keyName-NAI in EAP-Initiate/Re-auth Packet");
+		goto fail;
+	}
+
+	wpa_hexdump_ascii(MSG_DEBUG, "EAP: EAP-Initiate/Re-auth - keyName-NAI",
+			  parse.keyname, parse.keyname_len);
+	if (parse.keyname_len > 253) {
+		wpa_printf(MSG_DEBUG,
+			   "EAP: Too long keyName-NAI in EAP-Initiate/Re-auth");
+		goto fail;
+	}
+	os_memcpy(nai, parse.keyname, parse.keyname_len);
+	nai[parse.keyname_len] = '\0';
+
+	if (!sm->eap_server) {
+		/*
+		 * In passthrough case, EAP-Initiate/Re-auth replaces
+		 * EAP Identity exchange. Use keyName-NAI as the user identity
+		 * and forward EAP-Initiate/Re-auth to the backend
+		 * authentication server.
+		 */
+		wpa_printf(MSG_DEBUG,
+			   "EAP: Use keyName-NAI as user identity for backend authentication");
+		eap_server_clear_identity(sm);
+		sm->identity = (u8 *) dup_binstr(parse.keyname,
+						 parse.keyname_len);
+		if (!sm->identity)
+			goto fail;
+		sm->identity_len = parse.keyname_len;
+		return;
+	}
+
+	erp = eap_erp_get_key(sm, nai);
+	if (!erp) {
+		wpa_printf(MSG_DEBUG, "EAP: No matching ERP key found for %s",
+			   nai);
+		goto report_error;
+	}
+
+	if (erp->recv_seq != (u32) -1 && erp->recv_seq >= seq) {
+		wpa_printf(MSG_DEBUG,
+			   "EAP: SEQ=%u replayed (already received SEQ=%u)",
+			   seq, erp->recv_seq);
+		goto fail;
+	}
+
+	/* Is there enough room for Cryptosuite and Authentication Tag? */
+	start = parse.keyname + parse.keyname_len;
+	max_len = end - start;
+	if (max_len <
+	    1 + (erp->cryptosuite == EAP_ERP_CS_HMAC_SHA256_256 ? 32 : 16)) {
+		wpa_printf(MSG_DEBUG,
+			   "EAP: Not enough room for Authentication Tag");
+		goto fail;
+	}
+
+	switch (erp->cryptosuite) {
+	case EAP_ERP_CS_HMAC_SHA256_256:
+		if (end[-33] != erp->cryptosuite) {
+			wpa_printf(MSG_DEBUG,
+				   "EAP: Different Cryptosuite used");
+			goto fail;
+		}
+		hash_len = 32;
+		break;
+	case EAP_ERP_CS_HMAC_SHA256_128:
+		if (end[-17] != erp->cryptosuite) {
+			wpa_printf(MSG_DEBUG,
+				   "EAP: Different Cryptosuite used");
+			goto fail;
+		}
+		hash_len = 16;
+		break;
+	default:
+		hash_len = 0;
+		break;
+	}
+
+	if (hash_len) {
+		if (hmac_sha256(erp->rIK, erp->rIK_len, hdr,
+				end - hdr - hash_len, hash) < 0)
+			goto fail;
+		if (os_memcmp(end - hash_len, hash, hash_len) != 0) {
+			wpa_printf(MSG_DEBUG,
+				   "EAP: Authentication Tag mismatch");
+			goto fail;
+		}
+	}
+
+	/* Check if any supported CS results in matching tag */
+	if (!hash_len && max_len >= 1 + 32 &&
+	    end[-33] == EAP_ERP_CS_HMAC_SHA256_256) {
+		if (hmac_sha256(erp->rIK, erp->rIK_len, hdr,
+				end - hdr - 32, hash) < 0)
+			goto fail;
+		if (os_memcmp(end - 32, hash, 32) == 0) {
+			wpa_printf(MSG_DEBUG,
+				   "EAP: Authentication Tag match using HMAC-SHA256-256");
+			hash_len = 32;
+			erp->cryptosuite = EAP_ERP_CS_HMAC_SHA256_256;
+		}
+	}
+
+	if (!hash_len && end[-17] == EAP_ERP_CS_HMAC_SHA256_128) {
+		if (hmac_sha256(erp->rIK, erp->rIK_len, hdr,
+				end - hdr - 16, hash) < 0)
+			goto fail;
+		if (os_memcmp(end - 16, hash, 16) == 0) {
+			wpa_printf(MSG_DEBUG,
+				   "EAP: Authentication Tag match using HMAC-SHA256-128");
+			hash_len = 16;
+			erp->cryptosuite = EAP_ERP_CS_HMAC_SHA256_128;
+		}
+	}
+
+	if (!hash_len) {
+		wpa_printf(MSG_DEBUG,
+			   "EAP: No supported cryptosuite matched Authentication Tag");
+		goto fail;
+	}
+	end -= 1 + hash_len;
+
+	/*
+	 * Parse TVs/TLVs again now that we know the exact part of the buffer
+	 * that contains them.
+	 */
+	wpa_hexdump(MSG_DEBUG, "EAP: EAP-Initiate/Re-Auth TVs/TLVs",
+		    tlvs, end - tlvs);
+	if (erp_parse_tlvs(tlvs, end, &parse, 0) < 0)
+		goto fail;
+
+	wpa_printf(MSG_DEBUG, "EAP: ERP key %s SEQ updated to %u",
+		   erp->keyname_nai, seq);
+	erp->recv_seq = seq;
+	resp_flags &= ~0x80; /* R=0 - success */
+
+report_error:
+	erp_send_finish_reauth(sm, erp, ehdr->identifier, resp_flags, seq, nai);
+	return;
+
+fail:
+	sm->ignore = TRUE;
+}
+
+#endif /* CONFIG_ERP */
+
+
 SM_STATE(EAP, INITIALIZE_PASSTHROUGH)
 {
 	SM_ENTRY(EAP, INITIALIZE_PASSTHROUGH);
 
 	wpabuf_free(sm->eap_if.aaaEapRespData);
 	sm->eap_if.aaaEapRespData = NULL;
+	sm->try_initiate_reauth = FALSE;
 }
 
 
@@ -691,9 +1179,14 @@
 			SM_ENTER(EAP, INITIALIZE);
 		break;
 	case EAP_IDLE:
-		if (sm->eap_if.retransWhile == 0)
-			SM_ENTER(EAP, RETRANSMIT);
-		else if (sm->eap_if.eapResp)
+		if (sm->eap_if.retransWhile == 0) {
+			if (sm->try_initiate_reauth) {
+				sm->try_initiate_reauth = FALSE;
+				SM_ENTER(EAP, SELECT_ACTION);
+			} else {
+				SM_ENTER(EAP, RETRANSMIT);
+			}
+		} else if (sm->eap_if.eapResp)
 			SM_ENTER(EAP, RECEIVED);
 		break;
 	case EAP_RETRANSMIT:
@@ -716,6 +1209,10 @@
 			   sm->respVendor == EAP_VENDOR_IETF &&
 			   sm->respVendorMethod == sm->currentMethod)))
 			SM_ENTER(EAP, INTEGRITY_CHECK);
+#ifdef CONFIG_ERP
+		else if (sm->rxInitiate)
+			SM_ENTER(EAP, INITIATE_RECEIVED);
+#endif /* CONFIG_ERP */
 		else {
 			wpa_printf(MSG_DEBUG, "EAP: RECEIVED->DISCARD: "
 				   "rxResp=%d respId=%d currentId=%d "
@@ -804,9 +1301,22 @@
 			SM_ENTER(EAP, SUCCESS);
 		else if (sm->decision == DECISION_PASSTHROUGH)
 			SM_ENTER(EAP, INITIALIZE_PASSTHROUGH);
+		else if (sm->decision == DECISION_INITIATE_REAUTH_START)
+			SM_ENTER(EAP, INITIATE_REAUTH_START);
+#ifdef CONFIG_ERP
+		else if (sm->eap_server && sm->erp && sm->rxInitiate)
+			SM_ENTER(EAP, INITIATE_RECEIVED);
+#endif /* CONFIG_ERP */
 		else
 			SM_ENTER(EAP, PROPOSE_METHOD);
 		break;
+	case EAP_INITIATE_REAUTH_START:
+		SM_ENTER(EAP, SEND_REQUEST);
+		break;
+	case EAP_INITIATE_RECEIVED:
+		if (!sm->eap_server)
+			SM_ENTER(EAP, SELECT_ACTION);
+		break;
 	case EAP_TIMEOUT_FAILURE:
 		break;
 	case EAP_FAILURE:
@@ -876,6 +1386,12 @@
 {
 	int rto, i;
 
+	if (sm->try_initiate_reauth) {
+		wpa_printf(MSG_DEBUG,
+			   "EAP: retransmit timeout 1 second for EAP-Initiate-Re-auth-Start");
+		return 1;
+	}
+
 	if (methodTimeout) {
 		/*
 		 * EAP method (either internal or through AAA server, provided
@@ -929,6 +1445,7 @@
 
 	/* parse rxResp, respId, respMethod */
 	sm->rxResp = FALSE;
+	sm->rxInitiate = FALSE;
 	sm->respId = -1;
 	sm->respMethod = EAP_TYPE_NONE;
 	sm->respVendor = EAP_VENDOR_IETF;
@@ -955,6 +1472,8 @@
 
 	if (hdr->code == EAP_CODE_RESPONSE)
 		sm->rxResp = TRUE;
+	else if (hdr->code == EAP_CODE_INITIATE)
+		sm->rxInitiate = TRUE;
 
 	if (plen > sizeof(*hdr)) {
 		u8 *pos = (u8 *) (hdr + 1);
@@ -972,10 +1491,10 @@
 		}
 	}
 
-	wpa_printf(MSG_DEBUG, "EAP: parseEapResp: rxResp=%d respId=%d "
-		   "respMethod=%u respVendor=%u respVendorMethod=%u",
-		   sm->rxResp, sm->respId, sm->respMethod, sm->respVendor,
-		   sm->respVendorMethod);
+	wpa_printf(MSG_DEBUG,
+		   "EAP: parseEapResp: rxResp=%d rxInitiate=%d respId=%d respMethod=%u respVendor=%u respVendorMethod=%u",
+		   sm->rxResp, sm->rxInitiate, sm->respId, sm->respMethod,
+		   sm->respVendor, sm->respVendorMethod);
 }
 
 
@@ -1216,6 +1735,13 @@
 		return DECISION_CONTINUE;
 	}
 
+	if (!sm->identity && eap_get_erp_send_reauth_start(sm) &&
+	    !sm->initiate_reauth_start_sent) {
+		wpa_printf(MSG_DEBUG,
+			   "EAP: getDecision: send EAP-Initiate/Re-auth-Start");
+		return DECISION_INITIATE_REAUTH_START;
+	}
+
 	if (sm->identity == NULL || sm->currentId == -1) {
 		wpa_printf(MSG_DEBUG, "EAP: getDecision: no identity known "
 			   "yet -> CONTINUE");
@@ -1276,7 +1802,7 @@
  * This function allocates and initializes an EAP state machine.
  */
 struct eap_sm * eap_server_sm_init(void *eapol_ctx,
-				   struct eapol_callbacks *eapol_cb,
+				   const struct eapol_callbacks *eapol_cb,
 				   struct eap_config *conf)
 {
 	struct eap_sm *sm;
@@ -1326,6 +1852,7 @@
 	sm->pbc_in_m1 = conf->pbc_in_m1;
 	sm->server_id = conf->server_id;
 	sm->server_id_len = conf->server_id_len;
+	sm->erp = conf->erp;
 
 #ifdef CONFIG_TESTING_OPTIONS
 	sm->tls_test_flags = conf->tls_test_flags;
@@ -1353,6 +1880,7 @@
 		sm->m->reset(sm, sm->eap_method_priv);
 	wpabuf_free(sm->eap_if.eapReqData);
 	bin_clear_free(sm->eap_if.eapKeyData, sm->eap_if.eapKeyDataLen);
+	os_free(sm->eap_if.eapSessionId);
 	wpabuf_free(sm->lastReqData);
 	wpabuf_free(sm->eap_if.eapRespData);
 	os_free(sm->identity);
@@ -1451,3 +1979,25 @@
 	os_free(sm->identity);
 	sm->identity = NULL;
 }
+
+
+#ifdef CONFIG_TESTING_OPTIONS
+void eap_server_mschap_rx_callback(struct eap_sm *sm, const char *source,
+				   const u8 *username, size_t username_len,
+				   const u8 *challenge, const u8 *response)
+{
+	char hex_challenge[30], hex_response[90], user[100];
+
+	/* Print out Challenge and Response in format supported by asleap. */
+	if (username)
+		printf_encode(user, sizeof(user), username, username_len);
+	else
+		user[0] = '\0';
+	wpa_snprintf_hex_sep(hex_challenge, sizeof(hex_challenge),
+			     challenge, sizeof(challenge), ':');
+	wpa_snprintf_hex_sep(hex_response, sizeof(hex_response), response, 24,
+			     ':');
+	wpa_printf(MSG_DEBUG, "[%s/user=%s] asleap -C %s -R %s",
+		   source, user, hex_challenge, hex_response);
+}
+#endif /* CONFIG_TESTING_OPTIONS */
diff --git a/src/eap_server/eap_server_aka.c b/src/eap_server/eap_server_aka.c
index 09b976e..db9b6aa 100644
--- a/src/eap_server/eap_server_aka.c
+++ b/src/eap_server/eap_server_aka.c
@@ -1294,6 +1294,28 @@
 }
 
 
+static u8 * eap_aka_get_session_id(struct eap_sm *sm, void *priv, size_t *len)
+{
+	struct eap_aka_data *data = priv;
+	u8 *id;
+
+	if (data->state != SUCCESS)
+		return NULL;
+
+	*len = 1 + EAP_AKA_RAND_LEN + EAP_AKA_AUTN_LEN;
+	id = os_malloc(*len);
+	if (id == NULL)
+		return NULL;
+
+	id[0] = data->eap_method;
+	os_memcpy(id + 1, data->rand, EAP_AKA_RAND_LEN);
+	os_memcpy(id + 1 + EAP_AKA_RAND_LEN, data->autn, EAP_AKA_AUTN_LEN);
+	wpa_hexdump(MSG_DEBUG, "EAP-AKA: Derived Session-Id", id, *len);
+
+	return id;
+}
+
+
 int eap_server_aka_register(void)
 {
 	struct eap_method *eap;
@@ -1313,6 +1335,7 @@
 	eap->getKey = eap_aka_getKey;
 	eap->isSuccess = eap_aka_isSuccess;
 	eap->get_emsk = eap_aka_get_emsk;
+	eap->getSessionId = eap_aka_get_session_id;
 
 	ret = eap_server_method_register(eap);
 	if (ret)
@@ -1342,6 +1365,7 @@
 	eap->getKey = eap_aka_getKey;
 	eap->isSuccess = eap_aka_isSuccess;
 	eap->get_emsk = eap_aka_get_emsk;
+	eap->getSessionId = eap_aka_get_session_id;
 
 	ret = eap_server_method_register(eap);
 	if (ret)
diff --git a/src/eap_server/eap_server_eke.c b/src/eap_server/eap_server_eke.c
index 966f511..ba82be9 100644
--- a/src/eap_server/eap_server_eke.c
+++ b/src/eap_server/eap_server_eke.c
@@ -766,6 +766,29 @@
 }
 
 
+static u8 * eap_eke_get_session_id(struct eap_sm *sm, void *priv, size_t *len)
+{
+	struct eap_eke_data *data = priv;
+	u8 *sid;
+	size_t sid_len;
+
+	if (data->state != SUCCESS)
+		return NULL;
+
+	sid_len = 1 + 2 * data->sess.nonce_len;
+	sid = os_malloc(sid_len);
+	if (sid == NULL)
+		return NULL;
+	sid[0] = EAP_TYPE_EKE;
+	os_memcpy(sid + 1, data->nonce_p, data->sess.nonce_len);
+	os_memcpy(sid + 1 + data->sess.nonce_len, data->nonce_s,
+		  data->sess.nonce_len);
+	*len = sid_len;
+
+	return sid;
+}
+
+
 int eap_server_eke_register(void)
 {
 	struct eap_method *eap;
@@ -785,6 +808,7 @@
 	eap->getKey = eap_eke_getKey;
 	eap->isSuccess = eap_eke_isSuccess;
 	eap->get_emsk = eap_eke_get_emsk;
+	eap->getSessionId = eap_eke_get_session_id;
 
 	ret = eap_server_method_register(eap);
 	if (ret)
diff --git a/src/eap_server/eap_server_fast.c b/src/eap_server/eap_server_fast.c
index 2692bce..6745100 100644
--- a/src/eap_server/eap_server_fast.c
+++ b/src/eap_server/eap_server_fast.c
@@ -186,7 +186,6 @@
 
 		switch (*pos) {
 		case PAC_OPAQUE_TYPE_PAD:
-			pos = end;
 			goto done;
 		case PAC_OPAQUE_TYPE_KEY:
 			if (pos[1] != EAP_FAST_PAC_KEY_LEN) {
@@ -820,6 +819,9 @@
 	encr = eap_server_tls_encrypt(sm, &data->ssl, plain);
 	wpabuf_free(plain);
 
+	if (!encr)
+		return -1;
+
 	if (data->ssl.tls_out && piggyback) {
 		wpa_printf(MSG_DEBUG, "EAP-FAST: Piggyback Phase 2 data "
 			   "(len=%d) with last Phase 1 Message (len=%d "
@@ -1017,7 +1019,7 @@
 	if (m->check(sm, priv, &buf)) {
 		wpa_printf(MSG_DEBUG, "EAP-FAST: Phase2 check() asked to "
 			   "ignore the packet");
-		next_type = eap_fast_req_failure(sm, data);
+		eap_fast_req_failure(sm, data);
 		return;
 	}
 
@@ -1590,6 +1592,18 @@
 }
 
 
+static u8 * eap_fast_get_session_id(struct eap_sm *sm, void *priv, size_t *len)
+{
+	struct eap_fast_data *data = priv;
+
+	if (data->state != SUCCESS)
+		return NULL;
+
+	return eap_server_tls_derive_session_id(sm, &data->ssl, EAP_TYPE_FAST,
+						len);
+}
+
+
 int eap_server_fast_register(void)
 {
 	struct eap_method *eap;
@@ -1609,6 +1623,7 @@
 	eap->getKey = eap_fast_getKey;
 	eap->get_emsk = eap_fast_get_emsk;
 	eap->isSuccess = eap_fast_isSuccess;
+	eap->getSessionId = eap_fast_get_session_id;
 
 	ret = eap_server_method_register(eap);
 	if (ret)
diff --git a/src/eap_server/eap_server_gpsk.c b/src/eap_server/eap_server_gpsk.c
index cb369e4..50f15c3 100644
--- a/src/eap_server/eap_server_gpsk.c
+++ b/src/eap_server/eap_server_gpsk.c
@@ -24,6 +24,8 @@
 	size_t sk_len;
 	u8 pk[EAP_GPSK_MAX_PK_LEN];
 	size_t pk_len;
+	u8 session_id[128];
+	size_t id_len;
 	u8 *id_peer;
 	size_t id_peer_len;
 #define MAX_NUM_CSUITES 2
@@ -417,6 +419,21 @@
 		return;
 	}
 
+	if (eap_gpsk_derive_session_id(sm->user->password,
+				       sm->user->password_len,
+				       data->vendor, data->specifier,
+				       data->rand_peer, data->rand_server,
+				       data->id_peer, data->id_peer_len,
+				       sm->server_id, sm->server_id_len,
+				       EAP_TYPE_GPSK,
+				       data->session_id, &data->id_len) < 0) {
+		wpa_printf(MSG_DEBUG, "EAP-GPSK: Failed to derive Session-Id");
+		eap_gpsk_state(data, FAILURE);
+		return;
+	}
+	wpa_hexdump(MSG_DEBUG, "EAP-GPSK: Derived Session-Id",
+		    data->session_id, data->id_len);
+
 	miclen = eap_gpsk_mic_len(data->vendor, data->specifier);
 	if (end - pos < (int) miclen) {
 		wpa_printf(MSG_DEBUG, "EAP-GPSK: Message too short for MIC "
@@ -593,6 +610,24 @@
 }
 
 
+static u8 * eap_gpsk_get_session_id(struct eap_sm *sm, void *priv, size_t *len)
+{
+	struct eap_gpsk_data *data = priv;
+	u8 *sid;
+
+	if (data->state != SUCCESS)
+		return NULL;
+
+	sid = os_malloc(data->id_len);
+	if (sid == NULL)
+		return NULL;
+	os_memcpy(sid, data->session_id, data->id_len);
+	*len = data->id_len;
+
+	return sid;
+}
+
+
 int eap_server_gpsk_register(void)
 {
 	struct eap_method *eap;
@@ -612,6 +647,7 @@
 	eap->getKey = eap_gpsk_getKey;
 	eap->isSuccess = eap_gpsk_isSuccess;
 	eap->get_emsk = eap_gpsk_get_emsk;
+	eap->getSessionId = eap_gpsk_get_session_id;
 
 	ret = eap_server_method_register(eap);
 	if (ret)
diff --git a/src/eap_server/eap_server_ikev2.c b/src/eap_server/eap_server_ikev2.c
index 65b2ef6..16e6276 100644
--- a/src/eap_server/eap_server_ikev2.c
+++ b/src/eap_server/eap_server_ikev2.c
@@ -309,6 +309,12 @@
 
 	if (data->in_buf == NULL) {
 		/* First fragment of the message */
+		if (message_length > 50000) {
+			/* Limit maximum memory allocation */
+			wpa_printf(MSG_DEBUG,
+				   "EAP-IKEV2: Ignore too long message");
+			return -1;
+		}
 		data->in_buf = wpabuf_alloc(message_length);
 		if (data->in_buf == NULL) {
 			wpa_printf(MSG_DEBUG, "EAP-IKEV2: No memory for "
@@ -511,6 +517,36 @@
 }
 
 
+static u8 * eap_ikev2_get_session_id(struct eap_sm *sm, void *priv, size_t *len)
+{
+	struct eap_ikev2_data *data = priv;
+	u8 *sid;
+	size_t sid_len;
+	size_t offset;
+
+	if (data->state != DONE || !data->keymat_ok)
+		return NULL;
+
+	sid_len = 1 + data->ikev2.i_nonce_len + data->ikev2.r_nonce_len;
+	sid = os_malloc(sid_len);
+	if (sid) {
+		offset = 0;
+		sid[offset] = EAP_TYPE_IKEV2;
+		offset++;
+		os_memcpy(sid + offset, data->ikev2.i_nonce,
+			  data->ikev2.i_nonce_len);
+		offset += data->ikev2.i_nonce_len;
+		os_memcpy(sid + offset, data->ikev2.r_nonce,
+			  data->ikev2.r_nonce_len);
+		*len = sid_len;
+		wpa_hexdump(MSG_DEBUG, "EAP-IKEV2: Derived Session-Id",
+			    sid, sid_len);
+	}
+
+	return sid;
+}
+
+
 int eap_server_ikev2_register(void)
 {
 	struct eap_method *eap;
@@ -531,6 +567,7 @@
 	eap->getKey = eap_ikev2_getKey;
 	eap->isSuccess = eap_ikev2_isSuccess;
 	eap->get_emsk = eap_ikev2_get_emsk;
+	eap->getSessionId = eap_ikev2_get_session_id;
 
 	ret = eap_server_method_register(eap);
 	if (ret)
diff --git a/src/eap_server/eap_server_methods.c b/src/eap_server/eap_server_methods.c
index 0209fad..9e9dc93 100644
--- a/src/eap_server/eap_server_methods.c
+++ b/src/eap_server/eap_server_methods.c
@@ -153,7 +153,7 @@
  * eap_server_get_name - Get EAP method name for the given EAP type
  * @vendor: EAP Vendor-Id (0 = IETF)
  * @type: EAP method type
- * Returns: EAP method name, e.g., TLS, or %NULL if not found
+ * Returns: EAP method name, e.g., TLS, or "unknown" if not found
  *
  * This function maps EAP type numbers into EAP type names based on the list of
  * EAP methods included in the build.
@@ -167,5 +167,5 @@
 		if (m->vendor == vendor && m->method == type)
 			return m->name;
 	}
-	return NULL;
+	return "unknown";
 }
diff --git a/src/eap_server/eap_server_mschapv2.c b/src/eap_server/eap_server_mschapv2.c
index f7a753d..98d74e0 100644
--- a/src/eap_server/eap_server_mschapv2.c
+++ b/src/eap_server/eap_server_mschapv2.c
@@ -360,6 +360,19 @@
 		}
 	}
 
+#ifdef CONFIG_TESTING_OPTIONS
+	{
+		u8 challenge[8];
+
+		if (challenge_hash(peer_challenge, data->auth_challenge,
+				   username, username_len, challenge) == 0) {
+			eap_server_mschap_rx_callback(sm, "EAP-MSCHAPV2",
+						      username, username_len,
+						      challenge, nt_response);
+		}
+	}
+#endif /* CONFIG_TESTING_OPTIONS */
+
 	if (username_len != user_len ||
 	    os_memcmp(username, user, username_len) != 0) {
 		wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Mismatch in user names");
@@ -414,13 +427,16 @@
 			}
 			pw_hash = pw_hash_buf;
 		}
-		generate_authenticator_response_pwhash(
-			pw_hash, peer_challenge, data->auth_challenge,
-			username, username_len, nt_response,
-			data->auth_response);
-
-		hash_nt_password_hash(pw_hash, pw_hash_hash);
-		get_master_key(pw_hash_hash, nt_response, data->master_key);
+		if (generate_authenticator_response_pwhash(
+			    pw_hash, peer_challenge, data->auth_challenge,
+			    username, username_len, nt_response,
+			    data->auth_response) < 0 ||
+		    hash_nt_password_hash(pw_hash, pw_hash_hash) < 0 ||
+		    get_master_key(pw_hash_hash, nt_response,
+				   data->master_key)) {
+			data->state = FAILURE;
+			return;
+		}
 		data->master_key_valid = 1;
 		wpa_hexdump_key(MSG_DEBUG, "EAP-MSCHAPV2: Derived Master Key",
 				data->master_key, MSCHAPV2_KEY_LEN);
diff --git a/src/eap_server/eap_server_pax.c b/src/eap_server/eap_server_pax.c
index d9d4375..0e6b4a0 100644
--- a/src/eap_server/eap_server_pax.c
+++ b/src/eap_server/eap_server_pax.c
@@ -36,6 +36,7 @@
 	u8 mk[EAP_PAX_MK_LEN];
 	u8 ck[EAP_PAX_CK_LEN];
 	u8 ick[EAP_PAX_ICK_LEN];
+	u8 mid[EAP_PAX_MID_LEN];
 	int keys_set;
 	char *cid;
 	size_t cid_len;
@@ -148,7 +149,6 @@
 		    (u8 *) data->cid, data->cid_len, NULL, 0, pos);
 	wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: MAC_CK(B, CID)",
 		    pos, EAP_PAX_MAC_LEN);
-	pos += EAP_PAX_MAC_LEN;
 
 	/* Optional ADE could be added here, if needed */
 
@@ -388,7 +388,7 @@
 
 	if (eap_pax_initial_key_derivation(data->mac_id, data->ak,
 					   data->rand.e, data->mk, data->ck,
-					   data->ick) < 0) {
+					   data->ick, data->mid) < 0) {
 		wpa_printf(MSG_INFO, "EAP-PAX: Failed to complete initial "
 			   "key derivation");
 		data->state = FAILURE;
@@ -542,6 +542,26 @@
 }
 
 
+static u8 * eap_pax_get_session_id(struct eap_sm *sm, void *priv, size_t *len)
+{
+	struct eap_pax_data *data = priv;
+	u8 *sid;
+
+	if (data->state != SUCCESS)
+		return NULL;
+
+	sid = os_malloc(1 + EAP_PAX_MID_LEN);
+	if (sid == NULL)
+		return NULL;
+
+	*len = 1 + EAP_PAX_MID_LEN;
+	sid[0] = EAP_TYPE_PAX;
+	os_memcpy(sid + 1, data->mid, EAP_PAX_MID_LEN);
+
+	return sid;
+}
+
+
 int eap_server_pax_register(void)
 {
 	struct eap_method *eap;
@@ -561,6 +581,7 @@
 	eap->getKey = eap_pax_getKey;
 	eap->isSuccess = eap_pax_isSuccess;
 	eap->get_emsk = eap_pax_get_emsk;
+	eap->getSessionId = eap_pax_get_session_id;
 
 	ret = eap_server_method_register(eap);
 	if (ret)
diff --git a/src/eap_server/eap_server_peap.c b/src/eap_server/eap_server_peap.c
index 594e02d..3848f30 100644
--- a/src/eap_server/eap_server_peap.c
+++ b/src/eap_server/eap_server_peap.c
@@ -344,12 +344,14 @@
 	size_t mlen;
 
 	mlen = 6; /* Result TLV */
-	if (data->crypto_binding != NO_BINDING)
+	if (data->peap_version == 0 && data->tlv_request == TLV_REQ_SUCCESS &&
+	    data->crypto_binding != NO_BINDING) {
 		mlen += 60; /* Cryptobinding TLV */
 #ifdef EAP_SERVER_TNC
-	if (data->soh_response)
-		mlen += wpabuf_len(data->soh_response);
+		if (data->soh_response)
+			mlen += wpabuf_len(data->soh_response);
 #endif /* EAP_SERVER_TNC */
+	}
 
 	buf = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TLV, mlen,
 			    EAP_CODE_REQUEST, id);
@@ -537,15 +539,14 @@
 
 
 static int eap_peap_phase2_init(struct eap_sm *sm, struct eap_peap_data *data,
-				EapType eap_type)
+				int vendor, EapType eap_type)
 {
 	if (data->phase2_priv && data->phase2_method) {
 		data->phase2_method->reset(sm, data->phase2_priv);
 		data->phase2_method = NULL;
 		data->phase2_priv = NULL;
 	}
-	data->phase2_method = eap_server_get_eap_method(EAP_VENDOR_IETF,
-							eap_type);
+	data->phase2_method = eap_server_get_eap_method(vendor, eap_type);
 	if (!data->phase2_method)
 		return -1;
 
@@ -735,7 +736,7 @@
 	const u8 *soh_tlv = NULL;
 	size_t soh_tlv_len = 0;
 	int tlv_type, mandatory, tlv_len, vtlv_len;
-	u8 next_type;
+	u32 next_type;
 	u32 vendor_id;
 
 	pos = eap_hdr_validate(EAP_VENDOR_MICROSOFT, 0x21, in_data, &left);
@@ -850,8 +851,9 @@
 	eap_peap_state(data, PHASE2_METHOD);
 	next_type = sm->user->methods[0].method;
 	sm->user_eap_method_index = 1;
-	wpa_printf(MSG_DEBUG, "EAP-PEAP: try EAP type %d", next_type);
-	eap_peap_phase2_init(sm, data, next_type);
+	wpa_printf(MSG_DEBUG, "EAP-PEAP: try EAP vendor %d type %d",
+		   sm->user->methods[0].vendor, next_type);
+	eap_peap_phase2_init(sm, data, sm->user->methods[0].vendor, next_type);
 }
 #endif /* EAP_SERVER_TNC */
 
@@ -860,7 +862,8 @@
 					     struct eap_peap_data *data,
 					     struct wpabuf *in_data)
 {
-	u8 next_type = EAP_TYPE_NONE;
+	int next_vendor = EAP_VENDOR_IETF;
+	u32 next_type = EAP_TYPE_NONE;
 	const struct eap_hdr *hdr;
 	const u8 *pos;
 	size_t left;
@@ -892,17 +895,23 @@
 			    "allowed types", pos + 1, left - 1);
 		eap_sm_process_nak(sm, pos + 1, left - 1);
 		if (sm->user && sm->user_eap_method_index < EAP_MAX_METHODS &&
-		    sm->user->methods[sm->user_eap_method_index].method !=
-		    EAP_TYPE_NONE) {
+		    (sm->user->methods[sm->user_eap_method_index].vendor !=
+		     EAP_VENDOR_IETF ||
+		     sm->user->methods[sm->user_eap_method_index].method !=
+		     EAP_TYPE_NONE)) {
+			next_vendor = sm->user->methods[
+				sm->user_eap_method_index].vendor;
 			next_type = sm->user->methods[
 				sm->user_eap_method_index++].method;
-			wpa_printf(MSG_DEBUG, "EAP-PEAP: try EAP type %d",
-				   next_type);
+			wpa_printf(MSG_DEBUG,
+				   "EAP-PEAP: try EAP vendor %d type 0x%x",
+				   next_vendor, next_type);
 		} else {
 			eap_peap_req_failure(sm, data);
+			next_vendor = EAP_VENDOR_IETF;
 			next_type = EAP_TYPE_NONE;
 		}
-		eap_peap_phase2_init(sm, data, next_type);
+		eap_peap_phase2_init(sm, data, next_vendor, next_type);
 		return;
 	}
 
@@ -927,8 +936,9 @@
 	if (!data->phase2_method->isSuccess(sm, data->phase2_priv)) {
 		wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase2 method failed");
 		eap_peap_req_failure(sm, data);
+		next_vendor = EAP_VENDOR_IETF;
 		next_type = EAP_TYPE_NONE;
-		eap_peap_phase2_init(sm, data, next_type);
+		eap_peap_phase2_init(sm, data, next_vendor, next_type);
 		return;
 	}
 
@@ -940,7 +950,8 @@
 			wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase2 getKey "
 				   "failed");
 			eap_peap_req_failure(sm, data);
-			eap_peap_phase2_init(sm, data, EAP_TYPE_NONE);
+			eap_peap_phase2_init(sm, data, EAP_VENDOR_IETF,
+					     EAP_TYPE_NONE);
 			return;
 		}
 	}
@@ -955,6 +966,7 @@
 					  "database",
 					  sm->identity, sm->identity_len);
 			eap_peap_req_failure(sm, data);
+			next_vendor = EAP_VENDOR_IETF;
 			next_type = EAP_TYPE_NONE;
 			break;
 		}
@@ -965,18 +977,22 @@
 			eap_peap_state(data, PHASE2_SOH);
 			wpa_printf(MSG_DEBUG, "EAP-PEAP: Try to initialize "
 				   "TNC (NAP SOH)");
+			next_vendor = EAP_VENDOR_IETF;
 			next_type = EAP_TYPE_NONE;
 			break;
 		}
 #endif /* EAP_SERVER_TNC */
 
 		eap_peap_state(data, PHASE2_METHOD);
+		next_vendor = sm->user->methods[0].vendor;
 		next_type = sm->user->methods[0].method;
 		sm->user_eap_method_index = 1;
-		wpa_printf(MSG_DEBUG, "EAP-PEAP: try EAP type %d", next_type);
+		wpa_printf(MSG_DEBUG, "EAP-PEAP: try EAP vendor %d type 0x%x",
+			   next_vendor, next_type);
 		break;
 	case PHASE2_METHOD:
 		eap_peap_req_success(sm, data);
+		next_vendor = EAP_VENDOR_IETF;
 		next_type = EAP_TYPE_NONE;
 		break;
 	case FAILURE:
@@ -987,7 +1003,7 @@
 		break;
 	}
 
-	eap_peap_phase2_init(sm, data, next_type);
+	eap_peap_phase2_init(sm, data, next_vendor, next_type);
 }
 
 
@@ -1131,7 +1147,8 @@
 		break;
 	case PHASE2_START:
 		eap_peap_state(data, PHASE2_ID);
-		eap_peap_phase2_init(sm, data, EAP_TYPE_IDENTITY);
+		eap_peap_phase2_init(sm, data, EAP_VENDOR_IETF,
+				     EAP_TYPE_IDENTITY);
 		break;
 	case PHASE1_ID2:
 	case PHASE2_ID:
@@ -1229,6 +1246,18 @@
 }
 
 
+static u8 * eap_peap_get_session_id(struct eap_sm *sm, void *priv, size_t *len)
+{
+	struct eap_peap_data *data = priv;
+
+	if (data->state != SUCCESS)
+		return NULL;
+
+	return eap_server_tls_derive_session_id(sm, &data->ssl, EAP_TYPE_PEAP,
+						len);
+}
+
+
 int eap_server_peap_register(void)
 {
 	struct eap_method *eap;
@@ -1247,6 +1276,7 @@
 	eap->isDone = eap_peap_isDone;
 	eap->getKey = eap_peap_getKey;
 	eap->isSuccess = eap_peap_isSuccess;
+	eap->getSessionId = eap_peap_get_session_id;
 
 	ret = eap_server_method_register(eap);
 	if (ret)
diff --git a/src/eap_server/eap_server_psk.c b/src/eap_server/eap_server_psk.c
index db394e9..12b5d25 100644
--- a/src/eap_server/eap_server_psk.c
+++ b/src/eap_server/eap_server_psk.c
@@ -485,6 +485,28 @@
 }
 
 
+static u8 * eap_psk_get_session_id(struct eap_sm *sm, void *priv, size_t *len)
+{
+	struct eap_psk_data *data = priv;
+	u8 *id;
+
+	if (data->state != SUCCESS)
+		return NULL;
+
+	*len = 1 + 2 * EAP_PSK_RAND_LEN;
+	id = os_malloc(*len);
+	if (id == NULL)
+		return NULL;
+
+	id[0] = EAP_TYPE_PSK;
+	os_memcpy(id + 1, data->rand_p, EAP_PSK_RAND_LEN);
+	os_memcpy(id + 1 + EAP_PSK_RAND_LEN, data->rand_s, EAP_PSK_RAND_LEN);
+	wpa_hexdump(MSG_DEBUG, "EAP-PSK: Derived Session-Id", id, *len);
+
+	return id;
+}
+
+
 int eap_server_psk_register(void)
 {
 	struct eap_method *eap;
@@ -504,6 +526,7 @@
 	eap->getKey = eap_psk_getKey;
 	eap->isSuccess = eap_psk_isSuccess;
 	eap->get_emsk = eap_psk_get_emsk;
+	eap->getSessionId = eap_psk_get_session_id;
 
 	ret = eap_server_method_register(eap);
 	if (ret)
diff --git a/src/eap_server/eap_server_pwd.c b/src/eap_server/eap_server_pwd.c
index b48c331..9f787ab 100644
--- a/src/eap_server/eap_server_pwd.c
+++ b/src/eap_server/eap_server_pwd.c
@@ -10,6 +10,7 @@
 
 #include "common.h"
 #include "crypto/sha256.h"
+#include "crypto/ms_funcs.h"
 #include "eap_server/eap_i.h"
 #include "eap_common/eap_pwd_common.h"
 
@@ -24,6 +25,7 @@
 	size_t id_server_len;
 	u8 *password;
 	size_t password_len;
+	int password_hash;
 	u32 token;
 	u16 group_num;
 	EAP_PWD_group *grp;
@@ -112,6 +114,7 @@
 	}
 	data->password_len = sm->user->password_len;
 	os_memcpy(data->password, sm->user->password, data->password_len);
+	data->password_hash = sm->user->password_hash;
 
 	data->bnctx = BN_CTX_new();
 	if (data->bnctx == NULL) {
@@ -181,7 +184,8 @@
 	wpabuf_put_u8(data->outbuf, EAP_PWD_DEFAULT_RAND_FUNC);
 	wpabuf_put_u8(data->outbuf, EAP_PWD_DEFAULT_PRF);
 	wpabuf_put_data(data->outbuf, &data->token, sizeof(data->token));
-	wpabuf_put_u8(data->outbuf, EAP_PWD_PREP_NONE);
+	wpabuf_put_u8(data->outbuf, data->password_hash ? EAP_PWD_PREP_MS :
+		      EAP_PWD_PREP_NONE);
 	wpabuf_put_data(data->outbuf, data->id_server, data->id_server_len);
 }
 
@@ -579,6 +583,10 @@
 				    const u8 *payload, size_t payload_len)
 {
 	struct eap_pwd_id *id;
+	const u8 *password;
+	size_t password_len;
+	u8 pwhashhash[16];
+	int res;
 
 	if (payload_len < sizeof(struct eap_pwd_id)) {
 		wpa_printf(MSG_INFO, "EAP-pwd: Invalid ID response");
@@ -610,11 +618,25 @@
 			   "group");
 		return;
 	}
-	if (compute_password_element(data->grp, data->group_num,
-				     data->password, data->password_len,
-				     data->id_server, data->id_server_len,
-				     data->id_peer, data->id_peer_len,
-				     (u8 *) &data->token)) {
+
+	if (data->password_hash) {
+		res = hash_nt_password_hash(data->password, pwhashhash);
+		if (res)
+			return;
+		password = pwhashhash;
+		password_len = sizeof(pwhashhash);
+	} else {
+		password = data->password;
+		password_len = data->password_len;
+	}
+
+	res = compute_password_element(data->grp, data->group_num,
+				       password, password_len,
+				       data->id_server, data->id_server_len,
+				       data->id_peer, data->id_peer_len,
+				       (u8 *) &data->token);
+	os_memset(pwhashhash, 0, sizeof(pwhashhash));
+	if (res) {
 		wpa_printf(MSG_INFO, "EAP-PWD (server): unable to compute "
 			   "PWE");
 		return;
@@ -634,9 +656,21 @@
 	BIGNUM *x = NULL, *y = NULL, *cofactor = NULL;
 	EC_POINT *K = NULL, *point = NULL;
 	int res = 0;
+	size_t prime_len, order_len;
 
 	wpa_printf(MSG_DEBUG, "EAP-pwd: Received commit response");
 
+	prime_len = BN_num_bytes(data->grp->prime);
+	order_len = BN_num_bytes(data->grp->order);
+
+	if (payload_len != 2 * prime_len + order_len) {
+		wpa_printf(MSG_INFO,
+			   "EAP-pwd: Unexpected Commit payload length %u (expected %u)",
+			   (unsigned int) payload_len,
+			   (unsigned int) (2 * prime_len + order_len));
+		goto fin;
+	}
+
 	if (((data->peer_scalar = BN_new()) == NULL) ||
 	    ((data->k = BN_new()) == NULL) ||
 	    ((cofactor = BN_new()) == NULL) ||
@@ -752,6 +786,13 @@
 	u8 conf[SHA256_MAC_LEN], *cruft = NULL, *ptr;
 	int offset;
 
+	if (payload_len != SHA256_MAC_LEN) {
+		wpa_printf(MSG_INFO,
+			   "EAP-pwd: Unexpected Confirm payload length %u (expected %u)",
+			   (unsigned int) payload_len, SHA256_MAC_LEN);
+		goto fin;
+	}
+
 	/* build up the ciphersuite: group | random_function | prf */
 	grp = htons(data->group_num);
 	ptr = (u8 *) &cs;
@@ -901,17 +942,28 @@
 	 * the first fragment has a total length
 	 */
 	if (EAP_PWD_GET_LENGTH_BIT(lm_exch)) {
+		if (len < 2) {
+			wpa_printf(MSG_DEBUG,
+				   "EAP-pwd: Frame too short to contain Total-Length field");
+			return;
+		}
 		tot_len = WPA_GET_BE16(pos);
 		wpa_printf(MSG_DEBUG, "EAP-pwd: Incoming fragments, total "
 			   "length = %d", tot_len);
 		if (tot_len > 15000)
 			return;
+		if (data->inbuf) {
+			wpa_printf(MSG_DEBUG,
+				   "EAP-pwd: Unexpected new fragment start when previous fragment is still in use");
+			return;
+		}
 		data->inbuf = wpabuf_alloc(tot_len);
 		if (data->inbuf == NULL) {
 			wpa_printf(MSG_INFO, "EAP-pwd: Out of memory to "
 				   "buffer fragments!");
 			return;
 		}
+		data->in_frag_pos = 0;
 		pos += sizeof(u16);
 		len -= sizeof(u16);
 	}
@@ -1020,6 +1072,25 @@
 }
 
 
+static u8 * eap_pwd_get_session_id(struct eap_sm *sm, void *priv, size_t *len)
+{
+	struct eap_pwd_data *data = priv;
+	u8 *id;
+
+	if (data->state != SUCCESS)
+		return NULL;
+
+	id = os_malloc(1 + SHA256_MAC_LEN);
+	if (id == NULL)
+		return NULL;
+
+	os_memcpy(id, data->session_id, 1 + SHA256_MAC_LEN);
+	*len = 1 + SHA256_MAC_LEN;
+
+	return id;
+}
+
+
 int eap_server_pwd_register(void)
 {
 	struct eap_method *eap;
@@ -1028,8 +1099,6 @@
 	struct timezone tz;
 	u32 sr;
 
-	EVP_add_digest(EVP_sha256());
-
 	sr = 0xdeaddada;
 	(void) gettimeofday(&tp, &tz);
 	sr ^= (tp.tv_sec ^ tp.tv_usec);
@@ -1050,6 +1119,7 @@
 	eap->getKey = eap_pwd_getkey;
 	eap->get_emsk = eap_pwd_get_emsk;
 	eap->isSuccess = eap_pwd_is_success;
+	eap->getSessionId = eap_pwd_get_session_id;
 
 	ret = eap_server_method_register(eap);
 	if (ret)
diff --git a/src/eap_server/eap_server_sake.c b/src/eap_server/eap_server_sake.c
index 1937621..de70777 100644
--- a/src/eap_server/eap_server_sake.c
+++ b/src/eap_server/eap_server_sake.c
@@ -495,6 +495,28 @@
 }
 
 
+static u8 * eap_sake_get_session_id(struct eap_sm *sm, void *priv, size_t *len)
+{
+	struct eap_sake_data *data = priv;
+	u8 *id;
+
+	if (data->state != SUCCESS)
+		return NULL;
+
+	*len = 1 + 2 * EAP_SAKE_RAND_LEN;
+	id = os_malloc(*len);
+	if (id == NULL)
+		return NULL;
+
+	id[0] = EAP_TYPE_SAKE;
+	os_memcpy(id + 1, data->rand_s, EAP_SAKE_RAND_LEN);
+	os_memcpy(id + 1 + EAP_SAKE_RAND_LEN, data->rand_s, EAP_SAKE_RAND_LEN);
+	wpa_hexdump(MSG_DEBUG, "EAP-SAKE: Derived Session-Id", id, *len);
+
+	return id;
+}
+
+
 int eap_server_sake_register(void)
 {
 	struct eap_method *eap;
@@ -514,6 +536,7 @@
 	eap->getKey = eap_sake_getKey;
 	eap->isSuccess = eap_sake_isSuccess;
 	eap->get_emsk = eap_sake_get_emsk;
+	eap->getSessionId = eap_sake_get_session_id;
 
 	ret = eap_server_method_register(eap);
 	if (ret)
diff --git a/src/eap_server/eap_server_sim.c b/src/eap_server/eap_server_sim.c
index 23ee2b6..ddfb71c 100644
--- a/src/eap_server/eap_server_sim.c
+++ b/src/eap_server/eap_server_sim.c
@@ -820,6 +820,29 @@
 }
 
 
+static u8 * eap_sim_get_session_id(struct eap_sm *sm, void *priv, size_t *len)
+{
+	struct eap_sim_data *data = priv;
+	u8 *id;
+
+	if (data->state != SUCCESS)
+		return NULL;
+
+	*len = 1 + data->num_chal * GSM_RAND_LEN + EAP_SIM_NONCE_MT_LEN;
+	id = os_malloc(*len);
+	if (id == NULL)
+		return NULL;
+
+	id[0] = EAP_TYPE_SIM;
+	os_memcpy(id + 1, data->rand, data->num_chal * GSM_RAND_LEN);
+	os_memcpy(id + 1 + data->num_chal * GSM_RAND_LEN, data->nonce_mt,
+		  EAP_SIM_NONCE_MT_LEN);
+	wpa_hexdump(MSG_DEBUG, "EAP-SIM: Derived Session-Id", id, *len);
+
+	return id;
+}
+
+
 int eap_server_sim_register(void)
 {
 	struct eap_method *eap;
@@ -839,6 +862,7 @@
 	eap->getKey = eap_sim_getKey;
 	eap->isSuccess = eap_sim_isSuccess;
 	eap->get_emsk = eap_sim_get_emsk;
+	eap->getSessionId = eap_sim_get_session_id;
 
 	ret = eap_server_method_register(eap);
 	if (ret)
diff --git a/src/eap_server/eap_server_tls.c b/src/eap_server/eap_server_tls.c
index 6bed62f..58cfe8a 100644
--- a/src/eap_server/eap_server_tls.c
+++ b/src/eap_server/eap_server_tls.c
@@ -287,7 +287,7 @@
 		if (emsk)
 			os_memcpy(emsk, eapKeyData + EAP_TLS_KEY_LEN,
 				  EAP_EMSK_LEN);
-		os_free(eapKeyData);
+		bin_clear_free(eapKeyData, EAP_TLS_KEY_LEN + EAP_EMSK_LEN);
 	} else
 		emsk = NULL;
 
@@ -310,6 +310,18 @@
 }
 
 
+static u8 * eap_tls_get_session_id(struct eap_sm *sm, void *priv, size_t *len)
+{
+	struct eap_tls_data *data = priv;
+
+	if (data->state != SUCCESS)
+		return NULL;
+
+	return eap_server_tls_derive_session_id(sm, &data->ssl, EAP_TYPE_TLS,
+						len);
+}
+
+
 int eap_server_tls_register(void)
 {
 	struct eap_method *eap;
@@ -329,6 +341,7 @@
 	eap->getKey = eap_tls_getKey;
 	eap->isSuccess = eap_tls_isSuccess;
 	eap->get_emsk = eap_tls_get_emsk;
+	eap->getSessionId = eap_tls_get_session_id;
 
 	ret = eap_server_method_register(eap);
 	if (ret)
diff --git a/src/eap_server/eap_server_tls_common.c b/src/eap_server/eap_server_tls_common.c
index 01853e6..23498c9 100644
--- a/src/eap_server/eap_server_tls_common.c
+++ b/src/eap_server/eap_server_tls_common.c
@@ -100,43 +100,60 @@
 u8 * eap_server_tls_derive_key(struct eap_sm *sm, struct eap_ssl_data *data,
 			       char *label, size_t len)
 {
-	struct tls_keys keys;
-	u8 *rnd = NULL, *out;
+	u8 *out;
 
 	out = os_malloc(len);
 	if (out == NULL)
 		return NULL;
 
-	if (tls_connection_prf(sm->ssl_ctx, data->conn, label, 0, out, len) ==
-	    0)
-		return out;
+	if (tls_connection_prf(sm->ssl_ctx, data->conn, label, 0, 0,
+			       out, len)) {
+		os_free(out);
+		return NULL;
+	}
+
+	return out;
+}
+
+
+/**
+ * eap_server_tls_derive_session_id - Derive a Session-Id based on TLS data
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @data: Data for TLS processing
+ * @eap_type: EAP method used in Phase 1 (EAP_TYPE_TLS/PEAP/TTLS/FAST)
+ * @len: Pointer to length of the session ID generated
+ * Returns: Pointer to allocated Session-Id on success or %NULL on failure
+ *
+ * This function derive the Session-Id based on the TLS session data
+ * (client/server random and method type).
+ *
+ * The caller is responsible for freeing the returned buffer.
+ */
+u8 * eap_server_tls_derive_session_id(struct eap_sm *sm,
+				      struct eap_ssl_data *data, u8 eap_type,
+				      size_t *len)
+{
+	struct tls_keys keys;
+	u8 *out;
 
 	if (tls_connection_get_keys(sm->ssl_ctx, data->conn, &keys))
-		goto fail;
+		return NULL;
 
-	if (keys.client_random == NULL || keys.server_random == NULL ||
-	    keys.master_key == NULL)
-		goto fail;
+	if (keys.client_random == NULL || keys.server_random == NULL)
+		return NULL;
 
-	rnd = os_malloc(keys.client_random_len + keys.server_random_len);
-	if (rnd == NULL)
-		goto fail;
-	os_memcpy(rnd, keys.client_random, keys.client_random_len);
-	os_memcpy(rnd + keys.client_random_len, keys.server_random,
+	*len = 1 + keys.client_random_len + keys.server_random_len;
+	out = os_malloc(*len);
+	if (out == NULL)
+		return NULL;
+
+	/* Session-Id = EAP type || client.random || server.random */
+	out[0] = eap_type;
+	os_memcpy(out + 1, keys.client_random, keys.client_random_len);
+	os_memcpy(out + 1 + keys.client_random_len, keys.server_random,
 		  keys.server_random_len);
 
-	if (tls_prf_sha1_md5(keys.master_key, keys.master_key_len,
-			     label, rnd, keys.client_random_len +
-			     keys.server_random_len, out, len))
-		goto fail;
-
-	os_free(rnd);
 	return out;
-
-fail:
-	os_free(out);
-	os_free(rnd);
-	return NULL;
 }
 
 
diff --git a/src/eap_server/eap_server_ttls.c b/src/eap_server/eap_server_ttls.c
index 31e3871..31c67e8 100644
--- a/src/eap_server/eap_server_ttls.c
+++ b/src/eap_server/eap_server_ttls.c
@@ -409,7 +409,7 @@
 				       RADIUS_VENDOR_ID_MICROSOFT, 1, 43);
 		*pos++ = data->mschapv2_ident;
 		ret = os_snprintf((char *) pos, end - pos, "S=");
-		if (ret >= 0 && ret < end - pos)
+		if (!os_snprintf_error(end - pos, ret))
 			pos += ret;
 		pos += wpa_snprintf_hex_uppercase(
 			(char *) pos, end - pos, data->mschapv2_auth_response,
@@ -618,6 +618,12 @@
 		return;
 	}
 
+#ifdef CONFIG_TESTING_OPTIONS
+	eap_server_mschap_rx_callback(sm, "TTLS-MSCHAP",
+				      sm->identity, sm->identity_len,
+				      challenge, response + 2 + 24);
+#endif /* CONFIG_TESTING_OPTIONS */
+
 	if (os_memcmp_const(challenge, chal, EAP_TTLS_MSCHAP_CHALLENGE_LEN)
 	    != 0 ||
 	    response[0] != chal[EAP_TTLS_MSCHAP_CHALLENGE_LEN]) {
@@ -740,6 +746,18 @@
 	}
 
 	rx_resp = response + 2 + EAP_TTLS_MSCHAPV2_CHALLENGE_LEN + 8;
+#ifdef CONFIG_TESTING_OPTIONS
+	{
+		u8 challenge2[8];
+
+		if (challenge_hash(peer_challenge, auth_challenge,
+				   username, username_len, challenge2) == 0) {
+			eap_server_mschap_rx_callback(sm, "TTLS-MSCHAPV2",
+						      username, username_len,
+						      challenge2, rx_resp);
+		}
+	}
+#endif /* CONFIG_TESTING_OPTIONS */
 	if (os_memcmp_const(nt_response, rx_resp, 24) == 0) {
 		wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: Correct "
 			   "NT-Response");
@@ -1181,6 +1199,50 @@
 }
 
 
+static u8 * eap_ttls_get_session_id(struct eap_sm *sm, void *priv, size_t *len)
+{
+	struct eap_ttls_data *data = priv;
+
+	if (data->state != SUCCESS)
+		return NULL;
+
+	return eap_server_tls_derive_session_id(sm, &data->ssl, EAP_TYPE_TTLS,
+						len);
+}
+
+
+static u8 * eap_ttls_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
+{
+	struct eap_ttls_data *data = priv;
+	u8 *eapKeyData, *emsk;
+
+	if (data->state != SUCCESS)
+		return NULL;
+
+	eapKeyData = eap_server_tls_derive_key(sm, &data->ssl,
+					       "ttls keying material",
+					       EAP_TLS_KEY_LEN + EAP_EMSK_LEN);
+	if (eapKeyData) {
+		emsk = os_malloc(EAP_EMSK_LEN);
+		if (emsk)
+			os_memcpy(emsk, eapKeyData + EAP_TLS_KEY_LEN,
+				  EAP_EMSK_LEN);
+		bin_clear_free(eapKeyData, EAP_TLS_KEY_LEN + EAP_EMSK_LEN);
+	} else
+		emsk = NULL;
+
+	if (emsk) {
+		*len = EAP_EMSK_LEN;
+		wpa_hexdump(MSG_DEBUG, "EAP-TTLS: Derived EMSK",
+			    emsk, EAP_EMSK_LEN);
+	} else {
+		wpa_printf(MSG_DEBUG, "EAP-TTLS: Failed to derive EMSK");
+	}
+
+	return emsk;
+}
+
+
 int eap_server_ttls_register(void)
 {
 	struct eap_method *eap;
@@ -1199,6 +1261,8 @@
 	eap->isDone = eap_ttls_isDone;
 	eap->getKey = eap_ttls_getKey;
 	eap->isSuccess = eap_ttls_isSuccess;
+	eap->getSessionId = eap_ttls_get_session_id;
+	eap->get_emsk = eap_ttls_get_emsk;
 
 	ret = eap_server_method_register(eap);
 	if (ret)
diff --git a/src/eap_server/eap_server_wsc.c b/src/eap_server/eap_server_wsc.c
index 97ec0c0..9d9c28d 100644
--- a/src/eap_server/eap_server_wsc.c
+++ b/src/eap_server/eap_server_wsc.c
@@ -380,7 +380,7 @@
 		message_length = WPA_GET_BE16(pos);
 		pos += 2;
 
-		if (message_length < end - pos) {
+		if (message_length < end - pos || message_length > 50000) {
 			wpa_printf(MSG_DEBUG, "EAP-WSC: Invalid Message "
 				   "Length");
 			return;
diff --git a/src/eap_server/eap_sim_db.c b/src/eap_server/eap_sim_db.c
index bc2cbe5..acf5435 100644
--- a/src/eap_server/eap_sim_db.c
+++ b/src/eap_server/eap_sim_db.c
@@ -573,16 +573,14 @@
 	char buf[1000], *pos, *cmd, *imsi;
 	int res;
 
-	res = recv(sock, buf, sizeof(buf), 0);
+	res = recv(sock, buf, sizeof(buf) - 1, 0);
 	if (res < 0)
 		return;
+	buf[res] = '\0';
 	wpa_hexdump_ascii_key(MSG_MSGDUMP, "EAP-SIM DB: Received from an "
 			      "external source", (u8 *) buf, res);
 	if (res == 0)
 		return;
-	if (res >= (int) sizeof(buf))
-		res = sizeof(buf) - 1;
-	buf[res] = '\0';
 
 	if (data->get_complete_cb == NULL) {
 		wpa_printf(MSG_DEBUG, "EAP-SIM DB: No get_complete_cb "
@@ -924,12 +922,13 @@
 
 	imsi_len = os_strlen(imsi);
 	len = os_snprintf(msg, sizeof(msg), "SIM-REQ-AUTH ");
-	if (len < 0 || len + imsi_len >= sizeof(msg))
+	if (os_snprintf_error(sizeof(msg), len) ||
+	    len + imsi_len >= sizeof(msg))
 		return EAP_SIM_DB_FAILURE;
 	os_memcpy(msg + len, imsi, imsi_len);
 	len += imsi_len;
 	ret = os_snprintf(msg + len, sizeof(msg) - len, " %d", max_chal);
-	if (ret < 0 || (size_t) ret >= sizeof(msg) - len)
+	if (os_snprintf_error(sizeof(msg) - len, ret))
 		return EAP_SIM_DB_FAILURE;
 	len += ret;
 
@@ -966,7 +965,7 @@
 	pos = id;
 	end = id + sizeof(buf) * 2 + 2;
 	*pos++ = prefix;
-	pos += wpa_snprintf_hex(pos, end - pos, buf, sizeof(buf));
+	wpa_snprintf_hex(pos, end - pos, buf, sizeof(buf));
 	
 	return id;
 }
@@ -1387,7 +1386,8 @@
 
 	imsi_len = os_strlen(imsi);
 	len = os_snprintf(msg, sizeof(msg), "AKA-REQ-AUTH ");
-	if (len < 0 || len + imsi_len >= sizeof(msg))
+	if (os_snprintf_error(sizeof(msg), len) ||
+	    len + imsi_len >= sizeof(msg))
 		return EAP_SIM_DB_FAILURE;
 	os_memcpy(msg + len, imsi, imsi_len);
 	len += imsi_len;
@@ -1451,19 +1451,20 @@
 
 		imsi_len = os_strlen(imsi);
 		len = os_snprintf(msg, sizeof(msg), "AKA-AUTS ");
-		if (len < 0 || len + imsi_len >= sizeof(msg))
+		if (os_snprintf_error(sizeof(msg), len) ||
+		    len + imsi_len >= sizeof(msg))
 			return -1;
 		os_memcpy(msg + len, imsi, imsi_len);
 		len += imsi_len;
 
 		ret = os_snprintf(msg + len, sizeof(msg) - len, " ");
-		if (ret < 0 || (size_t) ret >= sizeof(msg) - len)
+		if (os_snprintf_error(sizeof(msg) - len, ret))
 			return -1;
 		len += ret;
 		len += wpa_snprintf_hex(msg + len, sizeof(msg) - len,
 					auts, EAP_AKA_AUTS_LEN);
 		ret = os_snprintf(msg + len, sizeof(msg) - len, " ");
-		if (ret < 0 || (size_t) ret >= sizeof(msg) - len)
+		if (os_snprintf_error(sizeof(msg) - len, ret))
 			return -1;
 		len += ret;
 		len += wpa_snprintf_hex(msg + len, sizeof(msg) - len,
diff --git a/src/eap_server/eap_tls_common.h b/src/eap_server/eap_tls_common.h
index 91449af..ddf90b8 100644
--- a/src/eap_server/eap_tls_common.h
+++ b/src/eap_server/eap_tls_common.h
@@ -74,6 +74,9 @@
 void eap_server_tls_ssl_deinit(struct eap_sm *sm, struct eap_ssl_data *data);
 u8 * eap_server_tls_derive_key(struct eap_sm *sm, struct eap_ssl_data *data,
 			       char *label, size_t len);
+u8 * eap_server_tls_derive_session_id(struct eap_sm *sm,
+				      struct eap_ssl_data *data, u8 eap_type,
+				      size_t *len);
 struct wpabuf * eap_server_tls_build_msg(struct eap_ssl_data *data,
 					 int eap_type, int version, u8 id);
 struct wpabuf * eap_server_tls_build_ack(u8 id, int eap_type, int version);
diff --git a/src/eapol_auth/Makefile b/src/eapol_auth/Makefile
index adfd3df..7b927a1 100644
--- a/src/eapol_auth/Makefile
+++ b/src/eapol_auth/Makefile
@@ -1,8 +1,16 @@
-all:
-	@echo Nothing to be made.
+all: libeapol_auth.a
 
 clean:
-	rm -f *~ *.o *.d *.gcno *.gcda *.gcov
+	rm -f *~ *.o *.d *.gcno *.gcda *.gcov libeapol_auth.a
 
 install:
 	@echo Nothing to be made.
+
+include ../lib.rules
+
+LIB_OBJS = eapol_auth_sm.o eapol_auth_dump.o
+
+libeapol_auth.a: $(LIB_OBJS)
+	$(AR) crT $@ $?
+
+-include $(OBJS:%.o=%.d)
diff --git a/src/eapol_auth/eapol_auth_dump.c b/src/eapol_auth/eapol_auth_dump.c
index 6c6969b..5579582 100644
--- a/src/eapol_auth/eapol_auth_dump.c
+++ b/src/eapol_auth/eapol_auth_dump.c
@@ -130,7 +130,7 @@
 	ret = os_snprintf(pos, end - pos, "aWhile=%d\nquietWhile=%d\n"
 			  "reAuthWhen=%d\n",
 			  sm->aWhile, sm->quietWhile, sm->reAuthWhen);
-	if (ret < 0 || ret >= end - pos)
+	if (os_snprintf_error(end - pos, ret))
 		return pos - buf;
 	pos += ret;
 
@@ -173,7 +173,7 @@
 			  _SB(sm->eap_if->portEnabled),
 			  _SB(sm->portValid),
 			  _SB(sm->reAuthenticate));
-	if (ret < 0 || ret >= end - pos)
+	if (os_snprintf_error(end - pos, ret))
 		return pos - buf;
 	pos += ret;
 
@@ -215,7 +215,7 @@
 			  sm->authAuthReauthsWhileAuthenticated,
 			  sm->authAuthEapStartsWhileAuthenticated,
 			  sm->authAuthEapLogoffWhileAuthenticated);
-	if (ret < 0 || ret >= end - pos)
+	if (os_snprintf_error(end - pos, ret))
 		return pos - buf;
 	pos += ret;
 
@@ -240,7 +240,7 @@
 			  sm->backendOtherRequestsToSupplicant,
 			  sm->backendAuthSuccesses,
 			  sm->backendAuthFails);
-	if (ret < 0 || ret >= end - pos)
+	if (os_snprintf_error(end - pos, ret))
 		return pos - buf;
 	pos += ret;
 
@@ -251,14 +251,14 @@
 			  reauth_timer_state_txt(sm->reauth_timer_state),
 			  sm->reAuthPeriod,
 			  _SB(sm->reAuthEnabled));
-	if (ret < 0 || ret >= end - pos)
+	if (os_snprintf_error(end - pos, ret))
 		return pos - buf;
 	pos += ret;
 
 	ret = os_snprintf(pos, end - pos,
 			  "auth_key_tx_state=%s\n",
 			  auth_key_tx_state_txt(sm->auth_key_tx_state));
-	if (ret < 0 || ret >= end - pos)
+	if (os_snprintf_error(end - pos, ret))
 		return pos - buf;
 	pos += ret;
 
@@ -267,7 +267,7 @@
 			  "rxKey=%s\n",
 			  key_rx_state_txt(sm->key_rx_state),
 			  _SB(sm->rxKey));
-	if (ret < 0 || ret >= end - pos)
+	if (os_snprintf_error(end - pos, ret))
 		return pos - buf;
 	pos += ret;
 
@@ -280,7 +280,7 @@
 			  ctrl_dir_txt(sm->adminControlledDirections),
 			  ctrl_dir_txt(sm->operControlledDirections),
 			  _SB(sm->operEdge));
-	if (ret < 0 || ret >= end - pos)
+	if (os_snprintf_error(end - pos, ret))
 		return pos - buf;
 	pos += ret;
 #undef _SB
diff --git a/src/eapol_auth/eapol_auth_sm.c b/src/eapol_auth/eapol_auth_sm.c
index a76fa13..3b0c2e4 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-2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2002-2014, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -22,7 +22,7 @@
 #define STATE_MACHINE_DEBUG_PREFIX "IEEE 802.1X"
 #define STATE_MACHINE_ADDR sm->addr
 
-static struct eapol_callbacks eapol_cb;
+static const struct eapol_callbacks eapol_cb;
 
 /* EAPOL state machines are described in IEEE Std 802.1X-2004, Chap. 8.2 */
 
@@ -43,6 +43,7 @@
 static void eapol_sm_step_run(struct eapol_state_machine *sm);
 static void eapol_sm_step_cb(void *eloop_ctx, void *timeout_ctx);
 static void eapol_auth_initialize(struct eapol_state_machine *sm);
+static void eapol_auth_conf_free(struct eapol_auth_config *conf);
 
 
 static void eapol_auth_logger(struct eapol_authenticator *eapol,
@@ -833,6 +834,7 @@
 	eap_conf.pbc_in_m1 = eapol->conf.pbc_in_m1;
 	eap_conf.server_id = eapol->conf.server_id;
 	eap_conf.server_id_len = eapol->conf.server_id_len;
+	eap_conf.erp = eapol->conf.erp;
 	sm->eap = eap_server_sm_init(sm, &eapol_cb, &eap_conf);
 	if (sm->eap == NULL) {
 		eapol_auth_free(sm);
@@ -851,6 +853,11 @@
 		sm->radius_cui = wpabuf_alloc_copy(radius_cui,
 						   os_strlen(radius_cui));
 
+	sm->acct_multi_session_id_lo = eapol->acct_multi_session_id_lo++;
+	if (eapol->acct_multi_session_id_lo == 0)
+		eapol->acct_multi_session_id_hi++;
+	sm->acct_multi_session_id_hi = eapol->acct_multi_session_id_hi;
+
 	return sm;
 }
 
@@ -1020,11 +1027,44 @@
 }
 
 
-static struct eapol_callbacks eapol_cb =
+static int eapol_sm_get_erp_send_reauth_start(void *ctx)
+{
+	struct eapol_state_machine *sm = ctx;
+	return sm->eapol->conf.erp_send_reauth_start;
+}
+
+
+static const char * eapol_sm_get_erp_domain(void *ctx)
+{
+	struct eapol_state_machine *sm = ctx;
+	return sm->eapol->conf.erp_domain;
+}
+
+
+static struct eap_server_erp_key * eapol_sm_erp_get_key(void *ctx,
+							const char *keyname)
+{
+	struct eapol_state_machine *sm = ctx;
+	return sm->eapol->cb.erp_get_key(sm->eapol->conf.ctx, keyname);
+}
+
+
+static int eapol_sm_erp_add_key(void *ctx, struct eap_server_erp_key *erp)
+{
+	struct eapol_state_machine *sm = ctx;
+	return sm->eapol->cb.erp_add_key(sm->eapol->conf.ctx, erp);
+}
+
+
+static const struct eapol_callbacks eapol_cb =
 {
 	eapol_sm_get_eap_user,
 	eapol_sm_get_eap_req_id_text,
-	NULL
+	NULL,
+	eapol_sm_get_erp_send_reauth_start,
+	eapol_sm_get_erp_domain,
+	eapol_sm_erp_get_key,
+	eapol_sm_erp_add_key,
 };
 
 
@@ -1069,21 +1109,16 @@
 	}
 	if (src->pac_opaque_encr_key) {
 		dst->pac_opaque_encr_key = os_malloc(16);
-		if (dst->pac_opaque_encr_key == NULL) {
-			os_free(dst->eap_req_id_text);
-			return -1;
-		}
+		if (dst->pac_opaque_encr_key == NULL)
+			goto fail;
 		os_memcpy(dst->pac_opaque_encr_key, src->pac_opaque_encr_key,
 			  16);
 	} else
 		dst->pac_opaque_encr_key = NULL;
 	if (src->eap_fast_a_id) {
 		dst->eap_fast_a_id = os_malloc(src->eap_fast_a_id_len);
-		if (dst->eap_fast_a_id == NULL) {
-			os_free(dst->eap_req_id_text);
-			os_free(dst->pac_opaque_encr_key);
-			return -1;
-		}
+		if (dst->eap_fast_a_id == NULL)
+			goto fail;
 		os_memcpy(dst->eap_fast_a_id, src->eap_fast_a_id,
 			  src->eap_fast_a_id_len);
 		dst->eap_fast_a_id_len = src->eap_fast_a_id_len;
@@ -1091,12 +1126,8 @@
 		dst->eap_fast_a_id = NULL;
 	if (src->eap_fast_a_id_info) {
 		dst->eap_fast_a_id_info = os_strdup(src->eap_fast_a_id_info);
-		if (dst->eap_fast_a_id_info == NULL) {
-			os_free(dst->eap_req_id_text);
-			os_free(dst->pac_opaque_encr_key);
-			os_free(dst->eap_fast_a_id);
-			return -1;
-		}
+		if (dst->eap_fast_a_id_info == NULL)
+			goto fail;
 	} else
 		dst->eap_fast_a_id_info = NULL;
 	dst->eap_fast_prov = src->eap_fast_prov;
@@ -1106,7 +1137,23 @@
 	dst->tnc = src->tnc;
 	dst->wps = src->wps;
 	dst->fragment_size = src->fragment_size;
+
+	os_free(dst->erp_domain);
+	if (src->erp_domain) {
+		dst->erp_domain = os_strdup(src->erp_domain);
+		if (dst->erp_domain == NULL)
+			goto fail;
+	} else {
+		dst->erp_domain = NULL;
+	}
+	dst->erp_send_reauth_start = src->erp_send_reauth_start;
+	dst->erp = src->erp;
+
 	return 0;
+
+fail:
+	eapol_auth_conf_free(dst);
+	return -1;
 }
 
 
@@ -1120,6 +1167,8 @@
 	conf->eap_fast_a_id = NULL;
 	os_free(conf->eap_fast_a_id_info);
 	conf->eap_fast_a_id_info = NULL;
+	os_free(conf->erp_domain);
+	conf->erp_domain = NULL;
 }
 
 
@@ -1127,6 +1176,7 @@
 					     struct eapol_auth_cb *cb)
 {
 	struct eapol_authenticator *eapol;
+	struct os_time now;
 
 	eapol = os_zalloc(sizeof(*eapol));
 	if (eapol == NULL)
@@ -1152,6 +1202,14 @@
 	eapol->cb.abort_auth = cb->abort_auth;
 	eapol->cb.tx_key = cb->tx_key;
 	eapol->cb.eapol_event = cb->eapol_event;
+	eapol->cb.erp_get_key = cb->erp_get_key;
+	eapol->cb.erp_add_key = cb->erp_add_key;
+
+	/* Acct-Multi-Session-Id should be unique over reboots. If reliable
+	 * clock is not available, this could be replaced with reboot counter,
+	 * etc. */
+	os_get_time(&now);
+	eapol->acct_multi_session_id_hi = now.sec;
 
 	return eapol;
 }
diff --git a/src/eapol_auth/eapol_auth_sm.h b/src/eapol_auth/eapol_auth_sm.h
index 320a0ad..ebed19a 100644
--- a/src/eapol_auth/eapol_auth_sm.h
+++ b/src/eapol_auth/eapol_auth_sm.h
@@ -24,6 +24,9 @@
 	void *eap_sim_db_priv;
 	char *eap_req_id_text; /* a copy of this will be allocated */
 	size_t eap_req_id_text_len;
+	int erp_send_reauth_start;
+	char *erp_domain; /* a copy of this will be allocated */
+	int erp; /* Whether ERP is enabled on authentication server */
 	u8 *pac_opaque_encr_key;
 	u8 *eap_fast_a_id;
 	size_t eap_fast_a_id_len;
@@ -45,6 +48,7 @@
 };
 
 struct eap_user;
+struct eap_server_erp_key;
 
 typedef enum {
 	EAPOL_LOGGER_DEBUG, EAPOL_LOGGER_INFO, EAPOL_LOGGER_WARNING
@@ -71,6 +75,9 @@
 	void (*abort_auth)(void *ctx, void *sta_ctx);
 	void (*tx_key)(void *ctx, void *sta_ctx);
 	void (*eapol_event)(void *ctx, void *sta_ctx, enum eapol_event type);
+	struct eap_server_erp_key * (*erp_get_key)(void *ctx,
+						   const char *keyname);
+	int (*erp_add_key)(void *ctx, struct eap_server_erp_key *erp);
 };
 
 
diff --git a/src/eapol_auth/eapol_auth_sm_i.h b/src/eapol_auth/eapol_auth_sm_i.h
index 25baddb..a29b49c 100644
--- a/src/eapol_auth/eapol_auth_sm_i.h
+++ b/src/eapol_auth/eapol_auth_sm_i.h
@@ -30,6 +30,9 @@
 
 	u8 *default_wep_key;
 	u8 default_wep_key_idx;
+
+	u32 acct_multi_session_id_hi;
+	u32 acct_multi_session_id_lo;
 };
 
 
@@ -175,6 +178,9 @@
 	void *sta; /* station context pointer to use in callbacks */
 
 	int remediation;
+
+	u32 acct_multi_session_id_hi;
+	u32 acct_multi_session_id_lo;
 };
 
 #endif /* EAPOL_AUTH_SM_I_H */
diff --git a/src/eapol_supp/Makefile b/src/eapol_supp/Makefile
index adfd3df..80db9d4 100644
--- a/src/eapol_supp/Makefile
+++ b/src/eapol_supp/Makefile
@@ -1,8 +1,18 @@
-all:
-	@echo Nothing to be made.
+all: libeapol_supp.a
 
 clean:
-	rm -f *~ *.o *.d *.gcno *.gcda *.gcov
+	rm -f *~ *.o *.d *.gcno *.gcda *.gcov libeapol_supp.a
 
 install:
 	@echo Nothing to be made.
+
+include ../lib.rules
+
+CFLAGS += -DIEEE8021X_EAPOL
+
+LIB_OBJS = eapol_supp_sm.o
+
+libeapol_supp.a: $(LIB_OBJS)
+	$(AR) crT $@ $?
+
+-include $(OBJS:%.o=%.d)
diff --git a/src/eapol_supp/eapol_supp_sm.c b/src/eapol_supp/eapol_supp_sm.c
index 70258be..eb8c5bb 100644
--- a/src/eapol_supp/eapol_supp_sm.c
+++ b/src/eapol_supp/eapol_supp_sm.c
@@ -128,6 +128,7 @@
 	struct wpabuf *eapReqData; /* for EAP */
 	Boolean altAccept; /* for EAP */
 	Boolean altReject; /* for EAP */
+	Boolean eapTriggerStart;
 	Boolean replay_counter_valid;
 	u8 last_replay_counter[16];
 	struct eapol_config conf;
@@ -222,6 +223,7 @@
 	SM_ENTRY(SUPP_PAE, DISCONNECTED);
 	sm->sPortMode = Auto;
 	sm->startCount = 0;
+	sm->eapTriggerStart = FALSE;
 	sm->logoffSent = FALSE;
 	eapol_sm_set_port_unauthorized(sm);
 	sm->suppAbort = TRUE;
@@ -244,6 +246,11 @@
 {
 	int send_start = sm->SUPP_PAE_state == SUPP_PAE_CONNECTING;
 	SM_ENTRY(SUPP_PAE, CONNECTING);
+
+	if (sm->eapTriggerStart)
+		send_start = 1;
+	sm->eapTriggerStart = FALSE;
+
 	if (send_start) {
 		sm->startWhen = sm->startPeriod;
 		sm->startCount++;
@@ -255,7 +262,7 @@
 		 * delay authentication. Use a short timeout to send the first
 		 * EAPOL-Start if Authenticator does not start authentication.
 		 */
-		if (sm->conf.wps) {
+		if (sm->conf.wps && !(sm->conf.wps & EAPOL_PEER_IS_WPS20_AP)) {
 			/* Reduce latency on starting WPS negotiation. */
 			wpa_printf(MSG_DEBUG,
 				   "EAPOL: Using shorter startWhen for WPS");
@@ -386,6 +393,8 @@
 			SM_ENTER(SUPP_PAE, HELD);
 		else if (sm->suppTimeout)
 			SM_ENTER(SUPP_PAE, CONNECTING);
+		else if (sm->eapTriggerStart)
+			SM_ENTER(SUPP_PAE, CONNECTING);
 		break;
 	case SUPP_PAE_HELD:
 		if (sm->heldWhile == 0)
@@ -1099,7 +1108,7 @@
 			  "suppPortStatus=%s\n",
 			  eapol_supp_pae_state(sm->SUPP_PAE_state),
 			  eapol_port_status(sm->suppPortStatus));
-	if (len < 0 || (size_t) len >= buflen)
+	if (os_snprintf_error(buflen, len))
 		return 0;
 
 	if (verbose) {
@@ -1116,7 +1125,7 @@
 				  sm->maxStart,
 				  eapol_port_control(sm->portControl),
 				  eapol_supp_be_state(sm->SUPP_BE_state));
-		if (ret < 0 || (size_t) ret >= buflen - len)
+		if (os_snprintf_error(buflen - len, ret))
 			return len;
 		len += ret;
 	}
@@ -1170,7 +1179,7 @@
 			  "Authorized" : "Unauthorized",
 			  sm->SUPP_BE_state);
 
-	if (ret < 0 || (size_t) ret >= buflen)
+	if (os_snprintf_error(buflen, ret))
 		return 0;
 	len = ret;
 
@@ -1198,7 +1207,7 @@
 			  sm->dot1xSuppLastEapolFrameVersion,
 			  MAC2STR(sm->dot1xSuppLastEapolFrameSource));
 
-	if (ret < 0 || (size_t) ret >= buflen - len)
+	if (os_snprintf_error(buflen - len, ret))
 		return len;
 	len += ret;
 
@@ -1625,21 +1634,15 @@
 /**
  * eapol_sm_notify_pmkid_attempt - Notification of PMKSA caching
  * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
- * @attempt: Whether PMKSA caching is tried
  *
- * Notify EAPOL state machines whether PMKSA caching is used.
+ * Notify EAPOL state machines if PMKSA caching is used.
  */
-void eapol_sm_notify_pmkid_attempt(struct eapol_sm *sm, int attempt)
+void eapol_sm_notify_pmkid_attempt(struct eapol_sm *sm)
 {
 	if (sm == NULL)
 		return;
-	if (attempt) {
-		wpa_printf(MSG_DEBUG, "RSN: Trying to use cached PMKSA");
-		sm->cached_pmk = TRUE;
-	} else {
-		wpa_printf(MSG_DEBUG, "RSN: Do not try to use cached PMKSA");
-		sm->cached_pmk = FALSE;
-	}
+	wpa_printf(MSG_DEBUG, "RSN: Trying to use cached PMKSA");
+	sm->cached_pmk = TRUE;
 }
 
 
@@ -1822,6 +1825,8 @@
 		return sm->altAccept;
 	case EAPOL_altReject:
 		return sm->altReject;
+	case EAPOL_eapTriggerStart:
+		return sm->eapTriggerStart;
 	}
 	return FALSE;
 }
@@ -1861,6 +1866,9 @@
 	case EAPOL_altReject:
 		sm->altReject = value;
 		break;
+	case EAPOL_eapTriggerStart:
+		sm->eapTriggerStart = value;
+		break;
 	}
 }
 
@@ -1948,13 +1956,14 @@
 #endif /* CONFIG_CTRL_IFACE || !CONFIG_NO_STDOUT_DEBUG */
 
 static void eapol_sm_notify_cert(void *ctx, int depth, const char *subject,
-				 const char *cert_hash,
+				 const char *altsubject[],
+				 int num_altsubject, const char *cert_hash,
 				 const struct wpabuf *cert)
 {
 	struct eapol_sm *sm = ctx;
 	if (sm->ctx->cert_cb)
-		sm->ctx->cert_cb(sm->ctx->ctx, depth, subject,
-				 cert_hash, cert);
+		sm->ctx->cert_cb(sm->ctx->ctx, depth, subject, altsubject,
+				 num_altsubject, cert_hash, cert);
 }
 
 
@@ -1968,6 +1977,17 @@
 }
 
 
+#ifdef CONFIG_EAP_PROXY
+static void eapol_sm_eap_proxy_cb(void *ctx)
+{
+	struct eapol_sm *sm = ctx;
+
+	if (sm->ctx->eap_proxy_cb)
+		sm->ctx->eap_proxy_cb(sm->ctx->ctx);
+}
+#endif /* CONFIG_EAP_PROXY */
+
+
 static void eapol_sm_set_anon_id(void *ctx, const u8 *id, size_t len)
 {
 	struct eapol_sm *sm = ctx;
@@ -1977,7 +1997,7 @@
 }
 
 
-static struct eapol_callbacks eapol_cb =
+static const struct eapol_callbacks eapol_cb =
 {
 	eapol_sm_get_config,
 	eapol_sm_get_bool,
@@ -1991,6 +2011,9 @@
 	eapol_sm_eap_param_needed,
 	eapol_sm_notify_cert,
 	eapol_sm_notify_status,
+#ifdef CONFIG_EAP_PROXY
+	eapol_sm_eap_proxy_cb,
+#endif /* CONFIG_EAP_PROXY */
 	eapol_sm_set_anon_id
 };
 
@@ -2026,6 +2049,7 @@
 	conf.opensc_engine_path = ctx->opensc_engine_path;
 	conf.pkcs11_engine_path = ctx->pkcs11_engine_path;
 	conf.pkcs11_module_path = ctx->pkcs11_module_path;
+	conf.openssl_ciphers = ctx->openssl_ciphers;
 	conf.wps = ctx->wps;
 	conf.cert_in_cb = ctx->cert_in_cb;
 
@@ -2106,3 +2130,10 @@
 	return -1;
 #endif /* CONFIG_EAP_PROXY */
 }
+
+
+void eapol_sm_erp_flush(struct eapol_sm *sm)
+{
+	if (sm)
+		eap_peer_erp_free_keys(sm->eap);
+}
diff --git a/src/eapol_supp/eapol_supp_sm.h b/src/eapol_supp/eapol_supp_sm.h
index 5b37314..1309ff7 100644
--- a/src/eapol_supp/eapol_supp_sm.h
+++ b/src/eapol_supp/eapol_supp_sm.h
@@ -59,6 +59,8 @@
 	 */
 	int external_sim;
 
+#define EAPOL_LOCAL_WPS_IN_USE BIT(0)
+#define EAPOL_PEER_IS_WPS20_AP BIT(1)
 	/**
 	 * wps - Whether this connection is used for WPS
 	 */
@@ -210,6 +212,15 @@
 	const char *pkcs11_module_path;
 
 	/**
+	 * openssl_ciphers - OpenSSL cipher string
+	 *
+	 * This is an OpenSSL specific configuration option for configuring the
+	 * default ciphers. If not set, "DEFAULT:!EXP:!LOW" is used as the
+	 * default.
+	 */
+	const char *openssl_ciphers;
+
+	/**
 	 * wps - WPS context data
 	 *
 	 * This is only used by EAP-WSC and can be left %NULL if not available.
@@ -237,10 +248,13 @@
 	 * @ctx: Callback context (ctx)
 	 * @depth: Depth in certificate chain (0 = server)
 	 * @subject: Subject of the peer certificate
+	 * @altsubject: Select fields from AltSubject of the peer certificate
+	 * @num_altsubject: Number of altsubject values
 	 * @cert_hash: SHA-256 hash of the certificate
 	 * @cert: Peer certificate
 	 */
 	void (*cert_cb)(void *ctx, int depth, const char *subject,
+			const char *altsubject[], int num_altsubject,
 			const char *cert_hash, const struct wpabuf *cert);
 
 	/**
@@ -257,6 +271,14 @@
 	void (*status_cb)(void *ctx, const char *status,
 			  const char *parameter);
 
+#ifdef CONFIG_EAP_PROXY
+	/**
+	 * eap_proxy_cb - Callback signifying any updates from eap_proxy
+	 * @ctx: eapol_ctx from eap_peer_sm_init() call
+	 */
+	void (*eap_proxy_cb)(void *ctx);
+#endif /* CONFIG_EAP_PROXY */
+
 	/**
 	 * set_anon_id - Set or add anonymous identity
 	 * @ctx: eapol_ctx from eap_peer_sm_init() call
@@ -293,7 +315,7 @@
 const u8 * eapol_sm_get_session_id(struct eapol_sm *sm, size_t *len);
 void eapol_sm_notify_logoff(struct eapol_sm *sm, Boolean logoff);
 void eapol_sm_notify_cached(struct eapol_sm *sm);
-void eapol_sm_notify_pmkid_attempt(struct eapol_sm *sm, int attempt);
+void eapol_sm_notify_pmkid_attempt(struct eapol_sm *sm);
 void eapol_sm_register_scard_ctx(struct eapol_sm *sm, void *ctx);
 void eapol_sm_notify_portControl(struct eapol_sm *sm, PortControl portControl);
 void eapol_sm_notify_ctrl_attached(struct eapol_sm *sm);
@@ -305,6 +327,7 @@
 void eapol_sm_set_ext_pw_ctx(struct eapol_sm *sm,
 			     struct ext_password_data *ext);
 int eapol_sm_failed(struct eapol_sm *sm);
+void eapol_sm_erp_flush(struct eapol_sm *sm);
 int eapol_sm_get_eap_proxy_imsi(struct eapol_sm *sm, char *imsi, size_t *len);
 #else /* IEEE8021X_EAPOL */
 static inline struct eapol_sm *eapol_sm_init(struct eapol_ctx *ctx)
@@ -365,13 +388,20 @@
 {
 	return -1;
 }
+static inline const u8 *
+eapol_sm_get_session_id(struct eapol_sm *sm, size_t *len)
+{
+	return NULL;
+}
 static inline void eapol_sm_notify_logoff(struct eapol_sm *sm, Boolean logoff)
 {
 }
 static inline void eapol_sm_notify_cached(struct eapol_sm *sm)
 {
 }
-#define eapol_sm_notify_pmkid_attempt(sm, attempt) do { } while (0)
+static inline void eapol_sm_notify_pmkid_attempt(struct eapol_sm *sm)
+{
+}
 #define eapol_sm_register_scard_ctx(sm, ctx) do { } while (0)
 static inline void eapol_sm_notify_portControl(struct eapol_sm *sm,
 					       PortControl portControl)
@@ -405,6 +435,9 @@
 {
 	return 0;
 }
+static inline void eapol_sm_erp_flush(struct eapol_sm *sm)
+{
+}
 #endif /* IEEE8021X_EAPOL */
 
 #endif /* EAPOL_SUPP_SM_H */
diff --git a/src/l2_packet/Makefile b/src/l2_packet/Makefile
index adfd3df..47925b7 100644
--- a/src/l2_packet/Makefile
+++ b/src/l2_packet/Makefile
@@ -1,8 +1,16 @@
-all:
-	@echo Nothing to be made.
+all: libl2_packet.a
 
 clean:
-	rm -f *~ *.o *.d *.gcno *.gcda *.gcov
+	rm -f *~ *.o *.d *.gcno *.gcda *.gcov libl2_packet.a
 
 install:
 	@echo Nothing to be made.
+
+include ../lib.rules
+
+LIB_OBJS = l2_packet_linux.o
+
+libl2_packet.a: $(LIB_OBJS)
+	$(AR) crT $@ $?
+
+-include $(OBJS:%.o=%.d)
diff --git a/src/l2_packet/l2_packet.h b/src/l2_packet/l2_packet.h
index dd825b5..2a45245 100644
--- a/src/l2_packet/l2_packet.h
+++ b/src/l2_packet/l2_packet.h
@@ -39,6 +39,11 @@
 #pragma pack(pop)
 #endif /* _MSC_VER */
 
+enum l2_packet_filter_type {
+	L2_PACKET_FILTER_DHCP,
+	L2_PACKET_FILTER_NDISC,
+};
+
 /**
  * l2_packet_init - Initialize l2_packet interface
  * @ifname: Interface name
@@ -63,6 +68,19 @@
 	void *rx_callback_ctx, int l2_hdr);
 
 /**
+ * l2_packet_init_bridge - Like l2_packet_init() but with bridge workaround
+ *
+ * This version of l2_packet_init() can be used to enable a workaround for Linux
+ * packet socket in case of a station interface in a bridge.
+ */
+struct l2_packet_data * l2_packet_init_bridge(
+	const char *br_ifname, const char *ifname, const u8 *own_addr,
+	unsigned short protocol,
+	void (*rx_callback)(void *ctx, const u8 *src_addr,
+			    const u8 *buf, size_t len),
+	void *rx_callback_ctx, int l2_hdr);
+
+/**
  * l2_packet_deinit - Deinitialize l2_packet interface
  * @l2: Pointer to internal l2_packet data from l2_packet_init()
  */
@@ -121,4 +139,16 @@
  */
 void l2_packet_notify_auth_start(struct l2_packet_data *l2);
 
+/**
+ * l2_packet_set_packet_filter - Set socket filter for l2_packet
+ * @l2: Pointer to internal l2_packet data from l2_packet_init()
+ * @type: enum l2_packet_filter_type, type of filter
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is used to set the socket filter for l2_packet socket.
+ *
+ */
+int l2_packet_set_packet_filter(struct l2_packet_data *l2,
+				enum l2_packet_filter_type type);
+
 #endif /* L2_PACKET_H */
diff --git a/src/l2_packet/l2_packet_freebsd.c b/src/l2_packet/l2_packet_freebsd.c
index 2e9a04c..aa83648 100644
--- a/src/l2_packet/l2_packet_freebsd.c
+++ b/src/l2_packet/l2_packet_freebsd.c
@@ -256,6 +256,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) {
@@ -308,3 +320,10 @@
 void l2_packet_notify_auth_start(struct l2_packet_data *l2)
 {
 }
+
+
+int l2_packet_set_packet_filter(struct l2_packet_data *l2,
+				enum l2_packet_filter_type type)
+{
+	return -1;
+}
diff --git a/src/l2_packet/l2_packet_linux.c b/src/l2_packet/l2_packet_linux.c
index 1419830..41de2f8 100644
--- a/src/l2_packet/l2_packet_linux.c
+++ b/src/l2_packet/l2_packet_linux.c
@@ -1,6 +1,6 @@
 /*
  * WPA Supplicant - Layer2 packet handling with Linux packet sockets
- * Copyright (c) 2003-2005, 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,9 +10,12 @@
 #include <sys/ioctl.h>
 #include <netpacket/packet.h>
 #include <net/if.h>
+#include <linux/filter.h>
 
 #include "common.h"
 #include "eloop.h"
+#include "crypto/sha1.h"
+#include "crypto/crypto.h"
 #include "l2_packet.h"
 
 
@@ -26,6 +29,56 @@
 	void *rx_callback_ctx;
 	int l2_hdr; /* whether to include layer 2 (Ethernet) header data
 		     * buffers */
+
+	/* For working around Linux packet socket behavior and regression. */
+	int fd_br_rx;
+	int last_from_br;
+	u8 last_hash[SHA1_MAC_LEN];
+	unsigned int num_rx, num_rx_br;
+};
+
+/* Generated by 'sudo tcpdump -s 3000 -dd greater 278 and ip and udp and
+ * src port bootps and dst port bootpc'
+ */
+static struct sock_filter dhcp_sock_filter_insns[] = {
+	{ 0x80, 0, 0, 0x00000000 },
+	{ 0x35, 0, 12, 0x00000116 },
+	{ 0x28, 0, 0, 0x0000000c },
+	{ 0x15, 0, 10, 0x00000800 },
+	{ 0x30, 0, 0, 0x00000017 },
+	{ 0x15, 0, 8, 0x00000011 },
+	{ 0x28, 0, 0, 0x00000014 },
+	{ 0x45, 6, 0, 0x00001fff },
+	{ 0xb1, 0, 0, 0x0000000e },
+	{ 0x48, 0, 0, 0x0000000e },
+	{ 0x15, 0, 3, 0x00000043 },
+	{ 0x48, 0, 0, 0x00000010 },
+	{ 0x15, 0, 1, 0x00000044 },
+	{ 0x6, 0, 0, 0x00000bb8 },
+	{ 0x6, 0, 0, 0x00000000 },
+};
+
+static const struct sock_fprog dhcp_sock_filter = {
+	.len = ARRAY_SIZE(dhcp_sock_filter_insns),
+	.filter = dhcp_sock_filter_insns,
+};
+
+
+/* Generated by 'sudo tcpdump -dd -s 1500 multicast and ip6[6]=58' */
+static struct sock_filter ndisc_sock_filter_insns[] = {
+	{ 0x30, 0, 0, 0x00000000 },
+	{ 0x45, 0, 5, 0x00000001 },
+	{ 0x28, 0, 0, 0x0000000c },
+	{ 0x15, 0, 3, 0x000086dd },
+	{ 0x30, 0, 0, 0x00000014 },
+	{ 0x15, 0, 1, 0x0000003a },
+	{ 0x6, 0, 0, 0x000005dc },
+	{ 0x6, 0, 0, 0x00000000 },
+};
+
+static const struct sock_fprog ndisc_sock_filter = {
+	.len = ARRAY_SIZE(ndisc_sock_filter_insns),
+	.filter = ndisc_sock_filter_insns,
 };
 
 
@@ -74,6 +127,7 @@
 	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,
@@ -84,6 +138,80 @@
 		return;
 	}
 
+	wpa_printf(MSG_DEBUG, "%s: src=" MACSTR " len=%d",
+		   __func__, MAC2STR(ll.sll_addr), (int) res);
+
+	if (l2->fd_br_rx >= 0) {
+		u8 hash[SHA1_MAC_LEN];
+		const u8 *addr[1];
+		size_t len[1];
+
+		/*
+		 * Close the workaround socket if the kernel version seems to be
+		 * able to deliver packets through the packet socket before
+		 * authorization has been completed (in dormant state).
+		 */
+		if (l2->num_rx_br <= 1) {
+			wpa_printf(MSG_DEBUG,
+				   "l2_packet_receive: Main packet socket for %s seems to have working RX - close workaround bridge socket",
+				   l2->ifname);
+			eloop_unregister_read_sock(l2->fd_br_rx);
+			close(l2->fd_br_rx);
+			l2->fd_br_rx = -1;
+		}
+
+		addr[0] = buf;
+		len[0] = res;
+		sha1_vector(1, addr, len, hash);
+		if (l2->last_from_br &&
+		    os_memcmp(hash, l2->last_hash, SHA1_MAC_LEN) == 0) {
+			wpa_printf(MSG_DEBUG, "%s: Drop duplicate RX",
+				   __func__);
+			return;
+		}
+		os_memcpy(l2->last_hash, hash, SHA1_MAC_LEN);
+	}
+
+	l2->last_from_br = 0;
+	l2->rx_callback(l2->rx_callback_ctx, ll.sll_addr, buf, res);
+}
+
+
+static void l2_packet_receive_br(int sock, void *eloop_ctx, void *sock_ctx)
+{
+	struct l2_packet_data *l2 = eloop_ctx;
+	u8 buf[2300];
+	int res;
+	struct sockaddr_ll ll;
+	socklen_t fromlen;
+	u8 hash[SHA1_MAC_LEN];
+	const u8 *addr[1];
+	size_t len[1];
+
+	l2->num_rx_br++;
+	os_memset(&ll, 0, sizeof(ll));
+	fromlen = sizeof(ll);
+	res = recvfrom(sock, buf, sizeof(buf), 0, (struct sockaddr *) &ll,
+		       &fromlen);
+	if (res < 0) {
+		wpa_printf(MSG_DEBUG, "l2_packet_receive_br - recvfrom: %s",
+			   strerror(errno));
+		return;
+	}
+
+	wpa_printf(MSG_DEBUG, "%s: src=" MACSTR " len=%d",
+		   __func__, MAC2STR(ll.sll_addr), (int) res);
+
+	addr[0] = buf;
+	len[0] = res;
+	sha1_vector(1, addr, len, hash);
+	if (!l2->last_from_br &&
+	    os_memcmp(hash, l2->last_hash, SHA1_MAC_LEN) == 0) {
+		wpa_printf(MSG_DEBUG, "%s: Drop duplicate RX", __func__);
+		return;
+	}
+	l2->last_from_br = 1;
+	os_memcpy(l2->last_hash, hash, SHA1_MAC_LEN);
 	l2->rx_callback(l2->rx_callback_ctx, ll.sll_addr, buf, res);
 }
 
@@ -105,6 +233,7 @@
 	l2->rx_callback = rx_callback;
 	l2->rx_callback_ctx = rx_callback_ctx;
 	l2->l2_hdr = l2_hdr;
+	l2->fd_br_rx = -1;
 
 	l2->fd = socket(PF_PACKET, l2_hdr ? SOCK_RAW : SOCK_DGRAM,
 			htons(protocol));
@@ -152,6 +281,87 @@
 }
 
 
+struct l2_packet_data * l2_packet_init_bridge(
+	const char *br_ifname, const char *ifname, const u8 *own_addr,
+	unsigned short protocol,
+	void (*rx_callback)(void *ctx, const u8 *src_addr,
+			    const u8 *buf, size_t len),
+	void *rx_callback_ctx, int l2_hdr)
+{
+	struct l2_packet_data *l2;
+	struct sock_filter ethertype_sock_filter_insns[] = {
+		/* Load ethertype */
+		BPF_STMT(BPF_LD | BPF_H | BPF_ABS, 2 * ETH_ALEN),
+		/* Jump over next statement if ethertype does not match */
+		BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, protocol, 0, 1),
+		/* Ethertype match - return all */
+		BPF_STMT(BPF_RET | BPF_K, ~0),
+		/* No match - drop */
+		BPF_STMT(BPF_RET | BPF_K, 0)
+	};
+	const struct sock_fprog ethertype_sock_filter = {
+		.len = ARRAY_SIZE(ethertype_sock_filter_insns),
+		.filter = ethertype_sock_filter_insns,
+	};
+	struct sockaddr_ll ll;
+
+	l2 = l2_packet_init(br_ifname, own_addr, protocol, rx_callback,
+			    rx_callback_ctx, l2_hdr);
+	if (!l2)
+		return NULL;
+
+	/*
+	 * The Linux packet socket behavior has changed over the years and there
+	 * is an inconvenient regression in it that breaks RX for a specific
+	 * protocol from interfaces in a bridge when that interface is not in
+	 * fully operation state (i.e., when in station mode and not completed
+	 * authorization). To work around this, register ETH_P_ALL version of
+	 * the packet socket bound to the real netdev and use socket filter to
+	 * match the ethertype value. This version is less efficient, but
+	 * required for functionality with many kernel version. If the main
+	 * packet socket is found to be working, this less efficient version
+	 * gets closed automatically.
+	 */
+
+	l2->fd_br_rx = socket(PF_PACKET, l2_hdr ? SOCK_RAW : SOCK_DGRAM,
+			      htons(ETH_P_ALL));
+	if (l2->fd_br_rx < 0) {
+		wpa_printf(MSG_DEBUG, "%s: socket(PF_PACKET-fd_br_rx): %s",
+			   __func__, strerror(errno));
+		/* try to continue without the workaround RX socket */
+		return l2;
+	}
+
+	os_memset(&ll, 0, sizeof(ll));
+	ll.sll_family = PF_PACKET;
+	ll.sll_ifindex = if_nametoindex(ifname);
+	ll.sll_protocol = htons(ETH_P_ALL);
+	if (bind(l2->fd_br_rx, (struct sockaddr *) &ll, sizeof(ll)) < 0) {
+		wpa_printf(MSG_DEBUG, "%s: bind[PF_PACKET-fd_br_rx]: %s",
+			   __func__, strerror(errno));
+		/* try to continue without the workaround RX socket */
+		close(l2->fd_br_rx);
+		l2->fd_br_rx = -1;
+		return l2;
+	}
+
+	if (setsockopt(l2->fd_br_rx, SOL_SOCKET, SO_ATTACH_FILTER,
+		       &ethertype_sock_filter, sizeof(struct sock_fprog))) {
+		wpa_printf(MSG_DEBUG,
+			   "l2_packet_linux: setsockopt(SO_ATTACH_FILTER) failed: %s",
+			   strerror(errno));
+		/* try to continue without the workaround RX socket */
+		close(l2->fd_br_rx);
+		l2->fd_br_rx = -1;
+		return l2;
+	}
+
+	eloop_register_read_sock(l2->fd_br_rx, l2_packet_receive_br, l2, NULL);
+
+	return l2;
+}
+
+
 void l2_packet_deinit(struct l2_packet_data *l2)
 {
 	if (l2 == NULL)
@@ -161,7 +371,12 @@
 		eloop_unregister_read_sock(l2->fd);
 		close(l2->fd);
 	}
-		
+
+	if (l2->fd_br_rx >= 0) {
+		eloop_unregister_read_sock(l2->fd_br_rx);
+		close(l2->fd_br_rx);
+	}
+
 	os_free(l2);
 }
 
@@ -202,3 +417,31 @@
 void l2_packet_notify_auth_start(struct l2_packet_data *l2)
 {
 }
+
+
+int l2_packet_set_packet_filter(struct l2_packet_data *l2,
+				enum l2_packet_filter_type type)
+{
+	const struct sock_fprog *sock_filter;
+
+	switch (type) {
+	case L2_PACKET_FILTER_DHCP:
+		sock_filter = &dhcp_sock_filter;
+		break;
+	case L2_PACKET_FILTER_NDISC:
+		sock_filter = &ndisc_sock_filter;
+		break;
+	default:
+		return -1;
+	}
+
+	if (setsockopt(l2->fd, SOL_SOCKET, SO_ATTACH_FILTER,
+		       sock_filter, sizeof(struct sock_fprog))) {
+		wpa_printf(MSG_ERROR,
+			   "l2_packet_linux: setsockopt(SO_ATTACH_FILTER) failed: %s",
+			   strerror(errno));
+		return -1;
+	}
+
+	return 0;
+}
diff --git a/src/l2_packet/l2_packet_ndis.c b/src/l2_packet/l2_packet_ndis.c
index 23b8ddc..7167781 100644
--- a/src/l2_packet/l2_packet_ndis.c
+++ b/src/l2_packet/l2_packet_ndis.c
@@ -450,6 +450,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)
@@ -514,3 +526,10 @@
 void l2_packet_notify_auth_start(struct l2_packet_data *l2)
 {
 }
+
+
+int l2_packet_set_packet_filter(struct l2_packet_data *l2,
+				enum l2_packet_filter_type type)
+{
+	return -1;
+}
diff --git a/src/l2_packet/l2_packet_none.c b/src/l2_packet/l2_packet_none.c
index 6896c4e..307fc6d 100644
--- a/src/l2_packet/l2_packet_none.c
+++ b/src/l2_packet/l2_packet_none.c
@@ -91,6 +91,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)
@@ -116,3 +128,10 @@
 {
 	/* This function can be left empty */
 }
+
+
+int l2_packet_set_packet_filter(struct l2_packet_data *l2,
+				enum l2_packet_filter_type type)
+{
+	return -1;
+}
diff --git a/src/l2_packet/l2_packet_pcap.c b/src/l2_packet/l2_packet_pcap.c
index 45aef56..bb4f4a3 100644
--- a/src/l2_packet/l2_packet_pcap.c
+++ b/src/l2_packet/l2_packet_pcap.c
@@ -54,15 +54,16 @@
 
 	l2->eth = eth_open(l2->ifname);
 	if (!l2->eth) {
-		printf("Failed to open interface '%s'.\n", l2->ifname);
-		perror("eth_open");
+		wpa_printf(MSG_ERROR,
+			   "Failed to open interface '%s' - eth_open: %s",
+			   l2->ifname, strerror(errno));
 		return -1;
 	}
 
 	if (eth_get(l2->eth, &own_addr) < 0) {
-		printf("Failed to get own hw address from interface '%s'.\n",
-		       l2->ifname);
-		perror("eth_get");
+		wpa_printf(MSG_ERROR,
+			   "Failed to get own hw address from interface '%s' - eth_get: %s",
+			   l2->ifname, strerror(errno));
 		eth_close(l2->eth);
 		l2->eth = NULL;
 		return -1;
@@ -378,3 +379,10 @@
 			       l2, l2->pcap);
 #endif /* CONFIG_WINPCAP */
 }
+
+
+int l2_packet_set_packet_filter(struct l2_packet_data *l2,
+				enum l2_packet_filter_type type)
+{
+	return -1;
+}
diff --git a/src/l2_packet/l2_packet_privsep.c b/src/l2_packet/l2_packet_privsep.c
index 6b117ca..e26ca20 100644
--- a/src/l2_packet/l2_packet_privsep.c
+++ b/src/l2_packet/l2_packet_privsep.c
@@ -44,7 +44,7 @@
 	msg.msg_namelen = sizeof(l2->priv_addr);
 
 	if (sendmsg(l2->fd, &msg, 0) < 0) {
-		perror("L2: sendmsg(cmd)");
+		wpa_printf(MSG_ERROR, "L2: sendmsg(cmd): %s", strerror(errno));
 		return -1;
 	}
 
@@ -82,7 +82,8 @@
 	msg.msg_namelen = sizeof(l2->priv_addr);
 
 	if (sendmsg(l2->fd, &msg, 0) < 0) {
-		perror("L2: sendmsg(packet_send)");
+		wpa_printf(MSG_ERROR, "L2: sendmsg(packet_send): %s",
+			   strerror(errno));
 		return -1;
 	}
 
@@ -102,7 +103,8 @@
 	res = recvfrom(sock, buf, sizeof(buf), 0, (struct sockaddr *) &from,
 		       &fromlen);
 	if (res < 0) {
-		perror("l2_packet_receive - recvfrom");
+		wpa_printf(MSG_ERROR, "l2_packet_receive - recvfrom: %s",
+			   strerror(errno));
 		return;
 	}
 	if (res < ETH_ALEN) {
@@ -162,7 +164,7 @@
 
 	l2->fd = socket(PF_UNIX, SOCK_DGRAM, 0);
 	if (l2->fd < 0) {
-		perror("socket(PF_UNIX)");
+		wpa_printf(MSG_ERROR, "socket(PF_UNIX): %s", strerror(errno));
 		os_free(l2->own_socket_path);
 		l2->own_socket_path = NULL;
 		os_free(l2);
@@ -173,7 +175,8 @@
 	addr.sun_family = AF_UNIX;
 	os_strlcpy(addr.sun_path, l2->own_socket_path, sizeof(addr.sun_path));
 	if (bind(l2->fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
-		perror("l2-pkt-privsep: bind(PF_UNIX)");
+		wpa_printf(MSG_ERROR, "l2-pkt-privsep: bind(PF_UNIX): %s",
+			   strerror(errno));
 		goto fail;
 	}
 
@@ -191,14 +194,14 @@
 	tv.tv_usec = 0;
 	res = select(l2->fd + 1, &rfds, NULL, NULL, &tv);
 	if (res < 0 && errno != EINTR) {
-		perror("select");
+		wpa_printf(MSG_ERROR, "select: %s", strerror(errno));
 		goto fail;
 	}
 
 	if (FD_ISSET(l2->fd, &rfds)) {
 		res = recv(l2->fd, reply, sizeof(reply), 0);
 		if (res < 0) {
-			perror("recv");
+			wpa_printf(MSG_ERROR, "recv: %s", strerror(errno));
 			goto fail;
 		}
 	} else {
@@ -228,6 +231,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)
@@ -259,3 +274,10 @@
 {
 	wpa_priv_cmd(l2, PRIVSEP_CMD_L2_NOTIFY_AUTH_START, NULL, 0);
 }
+
+
+int l2_packet_set_packet_filter(struct l2_packet_data *l2,
+				enum l2_packet_filter_type type)
+{
+	return -1;
+}
diff --git a/src/l2_packet/l2_packet_winpcap.c b/src/l2_packet/l2_packet_winpcap.c
index b6e5088..74085a3 100644
--- a/src/l2_packet/l2_packet_winpcap.c
+++ b/src/l2_packet/l2_packet_winpcap.c
@@ -248,6 +248,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);
+}
+
+
 static void l2_packet_deinit_timeout(void *eloop_ctx, void *timeout_ctx)
 {
 	struct l2_packet_data *l2 = eloop_ctx;
diff --git a/src/lib.rules b/src/lib.rules
index b260d25..0c79d99 100644
--- a/src/lib.rules
+++ b/src/lib.rules
@@ -15,6 +15,10 @@
 Q=
 E=true
 endif
+ifeq ($(QUIET), 1)
+Q=@
+E=true
+endif
 
 %.o: %.c
 	$(Q)$(CC) -c -o $@ $(CFLAGS) $<
diff --git a/src/p2p/Makefile b/src/p2p/Makefile
index adfd3df..5587fcf 100644
--- a/src/p2p/Makefile
+++ b/src/p2p/Makefile
@@ -1,8 +1,29 @@
-all:
-	@echo Nothing to be made.
+all: libp2p.a
 
 clean:
-	rm -f *~ *.o *.d *.gcno *.gcda *.gcov
+	rm -f *~ *.o *.d *.gcno *.gcda *.gcov libp2p.a
 
 install:
 	@echo Nothing to be made.
+
+include ../lib.rules
+
+CFLAGS += -DCONFIG_WIFI_DISPLAY
+CFLAGS += -DCONFIG_WPS_NFC
+
+LIB_OBJS= \
+	p2p_build.o \
+	p2p.o \
+	p2p_dev_disc.o \
+	p2p_go_neg.o \
+	p2p_group.o \
+	p2p_invitation.o \
+	p2p_parse.o \
+	p2p_pd.o \
+	p2p_sd.o \
+	p2p_utils.o
+
+libp2p.a: $(LIB_OBJS)
+	$(AR) crT $@ $?
+
+-include $(OBJS:%.o=%.d)
diff --git a/src/p2p/p2p.c b/src/p2p/p2p.c
index 28c3aa0..b87ff96 100644
--- a/src/p2p/p2p.c
+++ b/src/p2p/p2p.c
@@ -13,6 +13,8 @@
 #include "common/ieee802_11_defs.h"
 #include "common/ieee802_11_common.h"
 #include "common/wpa_ctrl.h"
+#include "crypto/sha256.h"
+#include "crypto/crypto.h"
 #include "wps/wps_i.h"
 #include "p2p_i.h"
 #include "p2p.h"
@@ -151,6 +153,19 @@
 }
 
 
+struct p2ps_advertisement * p2p_get_p2ps_adv_list(struct p2p_data *p2p)
+{
+	return p2p ? p2p->p2ps_adv_list : NULL;
+}
+
+
+void p2p_set_intended_addr(struct p2p_data *p2p, const u8 *intended_addr)
+{
+	if (p2p && intended_addr)
+		os_memcpy(p2p->intended_addr, intended_addr, ETH_ALEN);
+}
+
+
 u16 p2p_get_provisioning_info(struct p2p_data *p2p, const u8 *addr)
 {
 	struct p2p_device *dev = NULL;
@@ -211,29 +226,35 @@
 }
 
 
-void p2p_go_neg_failed(struct p2p_data *p2p, struct p2p_device *peer,
-		       int status)
+void p2p_go_neg_failed(struct p2p_data *p2p, int status)
 {
 	struct p2p_go_neg_results res;
-	p2p_clear_timeout(p2p);
-	p2p_set_state(p2p, P2P_IDLE);
-	if (p2p->go_neg_peer) {
-		p2p->go_neg_peer->flags &= ~P2P_DEV_PEER_WAITING_RESPONSE;
-		p2p->go_neg_peer->wps_method = WPS_NOT_READY;
-		p2p->go_neg_peer->oob_pw_id = 0;
+	struct p2p_device *peer = p2p->go_neg_peer;
+
+	if (!peer)
+		return;
+
+	eloop_cancel_timeout(p2p_go_neg_wait_timeout, p2p, NULL);
+	if (p2p->state != P2P_SEARCH) {
+		/*
+		 * Clear timeouts related to GO Negotiation if no new p2p_find
+		 * has been started.
+		 */
+		p2p_clear_timeout(p2p);
+		p2p_set_state(p2p, P2P_IDLE);
 	}
+
+	peer->flags &= ~P2P_DEV_PEER_WAITING_RESPONSE;
+	peer->wps_method = WPS_NOT_READY;
+	peer->oob_pw_id = 0;
+	wpabuf_free(peer->go_neg_conf);
+	peer->go_neg_conf = NULL;
 	p2p->go_neg_peer = NULL;
 
 	os_memset(&res, 0, sizeof(res));
 	res.status = status;
-	if (peer) {
-		wpabuf_free(peer->go_neg_conf);
-		peer->go_neg_conf = NULL;
-		os_memcpy(res.peer_device_addr, peer->info.p2p_device_addr,
-			  ETH_ALEN);
-		os_memcpy(res.peer_interface_addr, peer->intended_addr,
-			  ETH_ALEN);
-	}
+	os_memcpy(res.peer_device_addr, peer->info.p2p_device_addr, ETH_ALEN);
+	os_memcpy(res.peer_interface_addr, peer->intended_addr, ETH_ALEN);
 	p2p->cfg->go_neg_completed(p2p->cfg->cb_ctx, &res);
 }
 
@@ -276,7 +297,7 @@
 		return;
 	}
 
-	ies = p2p_build_probe_resp_ies(p2p);
+	ies = p2p_build_probe_resp_ies(p2p, NULL, 0);
 	if (ies == NULL)
 		return;
 
@@ -325,7 +346,7 @@
 		return 0;
 	}
 
-	ies = p2p_build_probe_resp_ies(p2p);
+	ies = p2p_build_probe_resp_ies(p2p, NULL, 0);
 	if (ies == NULL)
 		return -1;
 
@@ -348,8 +369,10 @@
 static void p2p_device_clear_reported(struct p2p_data *p2p)
 {
 	struct p2p_device *dev;
-	dl_list_for_each(dev, &p2p->devices, struct p2p_device, list)
+	dl_list_for_each(dev, &p2p->devices, struct p2p_device, list) {
 		dev->flags &= ~P2P_DEV_REPORTED;
+		dev->sd_reqs = 0;
+	}
 }
 
 
@@ -445,7 +468,8 @@
 
 static int p2p_add_group_clients(struct p2p_data *p2p, const u8 *go_dev_addr,
 				 const u8 *go_interface_addr, int freq,
-				 const u8 *gi, size_t gi_len)
+				 const u8 *gi, size_t gi_len,
+				 struct os_reltime *rx_time)
 {
 	struct p2p_group_info info;
 	size_t c;
@@ -513,10 +537,11 @@
 
 		os_memcpy(dev->interface_addr, cli->p2p_interface_addr,
 			  ETH_ALEN);
-		os_get_reltime(&dev->last_seen);
+		os_memcpy(&dev->last_seen, rx_time, sizeof(struct os_reltime));
 		os_memcpy(dev->member_in_go_dev, go_dev_addr, ETH_ALEN);
 		os_memcpy(dev->member_in_go_iface, go_interface_addr,
 			  ETH_ALEN);
+		dev->flags |= P2P_DEV_LAST_SEEN_AS_GROUP_CLIENT;
 	}
 
 	return 0;
@@ -650,6 +675,24 @@
 }
 
 
+static int p2p_compare_wfd_info(struct p2p_device *dev,
+			      const struct p2p_message *msg)
+{
+	if (dev->info.wfd_subelems && msg->wfd_subelems) {
+		if (dev->info.wfd_subelems->used != msg->wfd_subelems->used)
+			return 1;
+
+		return os_memcmp(dev->info.wfd_subelems->buf,
+				 msg->wfd_subelems->buf,
+				 dev->info.wfd_subelems->used);
+	}
+	if (dev->info.wfd_subelems || msg->wfd_subelems)
+		return 1;
+
+	return 0;
+}
+
+
 /**
  * p2p_add_device - Add peer entries based on scan results or P2P frames
  * @p2p: P2P module context from p2p_init()
@@ -675,6 +718,7 @@
 	struct p2p_device *dev;
 	struct p2p_message msg;
 	const u8 *p2p_dev_addr;
+	int wfd_changed;
 	int i;
 	struct os_reltime time_now;
 
@@ -716,22 +760,30 @@
 
 	/*
 	 * Update the device entry only if the new peer
-	 * entry is newer than the one previously stored.
+	 * entry is newer than the one previously stored, or if
+	 * the device was previously seen as a P2P Client in a group
+	 * and the new entry isn't older than a threshold.
 	 */
 	if (dev->last_seen.sec > 0 &&
-	    os_reltime_before(rx_time, &dev->last_seen)) {
-		p2p_dbg(p2p, "Do not update peer entry based on old frame (rx_time=%u.%06u last_seen=%u.%06u)",
+	    os_reltime_before(rx_time, &dev->last_seen) &&
+	    (!(dev->flags & P2P_DEV_LAST_SEEN_AS_GROUP_CLIENT) ||
+	     os_reltime_expired(&dev->last_seen, rx_time,
+				P2P_DEV_GROUP_CLIENT_RESP_THRESHOLD))) {
+		p2p_dbg(p2p,
+			"Do not update peer entry based on old frame (rx_time=%u.%06u last_seen=%u.%06u flags=0x%x)",
 			(unsigned int) rx_time->sec,
 			(unsigned int) rx_time->usec,
 			(unsigned int) dev->last_seen.sec,
-			(unsigned int) dev->last_seen.usec);
+			(unsigned int) dev->last_seen.usec,
+			dev->flags);
 		p2p_parse_free(&msg);
 		return -1;
 	}
 
 	os_memcpy(&dev->last_seen, rx_time, sizeof(struct os_reltime));
 
-	dev->flags &= ~(P2P_DEV_PROBE_REQ_ONLY | P2P_DEV_GROUP_CLIENT_ONLY);
+	dev->flags &= ~(P2P_DEV_PROBE_REQ_ONLY | P2P_DEV_GROUP_CLIENT_ONLY |
+			P2P_DEV_LAST_SEEN_AS_GROUP_CLIENT);
 
 	if (os_memcmp(addr, p2p_dev_addr, ETH_ALEN) != 0)
 		os_memcpy(dev->interface_addr, addr, ETH_ALEN);
@@ -744,6 +796,12 @@
 		dev->oper_ssid_len = msg.ssid[1];
 	}
 
+	if (msg.adv_service_instance && msg.adv_service_instance_len) {
+		wpabuf_free(dev->info.p2ps_instance);
+		dev->info.p2ps_instance = wpabuf_alloc_copy(
+			msg.adv_service_instance, msg.adv_service_instance_len);
+	}
+
 	if (freq >= 2412 && freq <= 2484 && msg.ds_params &&
 	    *msg.ds_params >= 1 && *msg.ds_params <= 14) {
 		int ds_freq;
@@ -787,6 +845,8 @@
 			break;
 	}
 
+	wfd_changed = p2p_compare_wfd_info(dev, &msg);
+
 	if (msg.wfd_subelems) {
 		wpabuf_free(dev->info.wfd_subelems);
 		dev->info.wfd_subelems = wpabuf_dup(msg.wfd_subelems);
@@ -794,14 +854,17 @@
 
 	if (scan_res) {
 		p2p_add_group_clients(p2p, p2p_dev_addr, addr, freq,
-				      msg.group_info, msg.group_info_len);
+				      msg.group_info, msg.group_info_len,
+				      rx_time);
 	}
 
 	p2p_parse_free(&msg);
 
 	p2p_update_peer_vendor_elems(dev, ies, ies_len);
 
-	if (dev->flags & P2P_DEV_REPORTED)
+	if (dev->flags & P2P_DEV_REPORTED && !wfd_changed &&
+	    (!msg.adv_service_instance ||
+	     (dev->flags & P2P_DEV_P2PS_REPORTED)))
 		return 0;
 
 	p2p_dbg(p2p, "Peer found with Listen frequency %d MHz (rx_time=%u.%06u)",
@@ -837,6 +900,9 @@
 			    !(dev->flags & P2P_DEV_REPORTED_ONCE));
 	dev->flags |= P2P_DEV_REPORTED | P2P_DEV_REPORTED_ONCE;
 
+	if (msg.adv_service_instance)
+		dev->flags |= P2P_DEV_P2PS_REPORTED;
+
 	return 0;
 }
 
@@ -849,8 +915,7 @@
 		/*
 		 * If GO Negotiation is in progress, report that it has failed.
 		 */
-		p2p_go_neg_failed(p2p, dev, -1);
-		p2p->go_neg_peer = NULL;
+		p2p_go_neg_failed(p2p, -1);
 	}
 	if (p2p->invite_peer == dev)
 		p2p->invite_peer = NULL;
@@ -872,6 +937,7 @@
 	wpabuf_free(dev->info.wfd_subelems);
 	wpabuf_free(dev->info.vendor_elems);
 	wpabuf_free(dev->go_neg_conf);
+	wpabuf_free(dev->info.p2ps_instance);
 
 	os_free(dev);
 }
@@ -957,14 +1023,8 @@
 				 p2p->num_req_dev_types, p2p->req_dev_types,
 				 p2p->find_dev_id, pw_id);
 	if (res < 0) {
-		p2p_dbg(p2p, "Scan request failed");
+		p2p_dbg(p2p, "Scan request schedule failed");
 		p2p_continue_find(p2p);
-	} else {
-		p2p_dbg(p2p, "Running p2p_scan");
-		p2p->p2p_scan_running = 1;
-		eloop_cancel_timeout(p2p_scan_timeout, p2p, NULL);
-		eloop_register_timeout(P2P_SCAN_TIMEOUT, 0, p2p_scan_timeout,
-				       p2p, NULL);
 	}
 }
 
@@ -977,6 +1037,22 @@
 }
 
 
+void p2p_notify_scan_trigger_status(struct p2p_data *p2p, int status)
+{
+	if (status != 0) {
+		p2p_dbg(p2p, "Scan request failed");
+		/* Do continue find even for the first p2p_find_scan */
+		p2p_continue_find(p2p);
+	} else {
+		p2p_dbg(p2p, "Running p2p_scan");
+		p2p->p2p_scan_running = 1;
+		eloop_cancel_timeout(p2p_scan_timeout, p2p, NULL);
+		eloop_register_timeout(P2P_SCAN_TIMEOUT, 0, p2p_scan_timeout,
+				       p2p, NULL);
+	}
+}
+
+
 static int p2p_run_after_scan(struct p2p_data *p2p)
 {
 	struct p2p_device *dev;
@@ -1046,10 +1122,46 @@
 }
 
 
+static int p2ps_gen_hash(struct p2p_data *p2p, const char *str, u8 *hash)
+{
+	u8 buf[SHA256_MAC_LEN];
+	char str_buf[256];
+	const u8 *adv_array;
+	size_t i, adv_len;
+
+	if (!str || !hash)
+		return 0;
+
+	if (!str[0]) {
+		os_memcpy(hash, p2p->wild_card_hash, P2PS_HASH_LEN);
+		return 1;
+	}
+
+	adv_array = (u8 *) str_buf;
+	adv_len = os_strlen(str);
+	if (adv_len >= sizeof(str_buf))
+		return 0;
+
+	for (i = 0; str[i] && i < adv_len; i++) {
+		if (str[i] >= 'A' && str[i] <= 'Z')
+			str_buf[i] = str[i] - 'A' + 'a';
+		else
+			str_buf[i] = str[i];
+	}
+
+	if (sha256_vector(1, &adv_array, &adv_len, buf))
+		return 0;
+
+	os_memcpy(hash, buf, P2PS_HASH_LEN);
+	return 1;
+}
+
+
 int p2p_find(struct p2p_data *p2p, unsigned int timeout,
 	     enum p2p_discovery_type type,
 	     unsigned int num_req_dev_types, const u8 *req_dev_types,
-	     const u8 *dev_id, unsigned int search_delay)
+	     const u8 *dev_id, unsigned int search_delay,
+	     u8 seek_count, const char **seek, int freq)
 {
 	int res;
 
@@ -1076,6 +1188,46 @@
 	} else
 		p2p->find_dev_id = NULL;
 
+	if (seek_count == 0 || !seek) {
+		/* Not an ASP search */
+		p2p->p2ps_seek = 0;
+	} else if (seek_count == 1 && seek && (!seek[0] || !seek[0][0])) {
+		/*
+		 * An empty seek string means no hash values, but still an ASP
+		 * search.
+		 */
+		p2p_dbg(p2p, "ASP search");
+		p2p->p2ps_seek_count = 0;
+		p2p->p2ps_seek = 1;
+	} else if (seek && seek_count <= P2P_MAX_QUERY_HASH) {
+		u8 buf[P2PS_HASH_LEN];
+		int i, count = 0;
+
+		for (i = 0; i < seek_count; i++) {
+			if (!p2ps_gen_hash(p2p, seek[i], buf))
+				continue;
+
+			p2p_dbg(p2p, "Seek service %s hash " MACSTR,
+				seek[i], MAC2STR(buf));
+			os_memcpy(&p2p->p2ps_seek_hash[count * P2PS_HASH_LEN],
+				  buf, P2PS_HASH_LEN);
+			count++;
+		}
+
+		p2p->p2ps_seek_count = count;
+		p2p->p2ps_seek = 1;
+	} else {
+		p2p->p2ps_seek_count = 0;
+		p2p->p2ps_seek = 1;
+	}
+
+	/* Special case to perform wildcard search */
+	if (p2p->p2ps_seek_count == 0 && p2p->p2ps_seek) {
+		p2p->p2ps_seek_count = 1;
+		os_memcpy(&p2p->p2ps_seek_hash, p2p->wild_card_hash,
+			  P2PS_HASH_LEN);
+	}
+
 	p2p->start_after_scan = P2P_AFTER_SCAN_NOTHING;
 	p2p_clear_timeout(p2p);
 	p2p->cfg->stop_listen(p2p->cfg->cb_ctx);
@@ -1091,6 +1243,19 @@
 				       p2p, NULL);
 	switch (type) {
 	case P2P_FIND_START_WITH_FULL:
+		if (freq > 0) {
+			/*
+			 * Start with the specified channel and then move to
+			 * social channels only scans.
+			 */
+			res = p2p->cfg->p2p_scan(p2p->cfg->cb_ctx,
+						 P2P_SCAN_SPECIFIC, freq,
+						 p2p->num_req_dev_types,
+						 p2p->req_dev_types, dev_id,
+						 DEV_PW_DEFAULT);
+			break;
+		}
+		/* fall through */
 	case P2P_FIND_PROGRESSIVE:
 		res = p2p->cfg->p2p_scan(p2p->cfg->cb_ctx, P2P_SCAN_FULL, 0,
 					 p2p->num_req_dev_types,
@@ -1107,17 +1272,11 @@
 		return -1;
 	}
 
-	if (res == 0) {
-		p2p_dbg(p2p, "Running p2p_scan");
-		p2p->p2p_scan_running = 1;
-		eloop_cancel_timeout(p2p_scan_timeout, p2p, NULL);
-		eloop_register_timeout(P2P_SCAN_TIMEOUT, 0, p2p_scan_timeout,
-				       p2p, NULL);
-	} else if (p2p->p2p_scan_running) {
+	if (res != 0 && p2p->p2p_scan_running) {
 		p2p_dbg(p2p, "Failed to start p2p_scan - another p2p_scan was already running");
 		/* wait for the previous p2p_scan to complete */
 		res = 0; /* do not report failure */
-	} else {
+	} else if (res != 0) {
 		p2p_dbg(p2p, "Failed to start p2p_scan");
 		p2p_set_state(p2p, P2P_IDLE);
 		eloop_cancel_timeout(p2p_find_timeout, p2p, NULL);
@@ -1132,8 +1291,11 @@
 	p2p_dbg(p2p, "Stopping find");
 	eloop_cancel_timeout(p2p_find_timeout, p2p, NULL);
 	p2p_clear_timeout(p2p);
-	if (p2p->state == P2P_SEARCH)
+	if (p2p->state == P2P_SEARCH || p2p->state == P2P_SD_DURING_FIND)
 		p2p->cfg->find_stopped(p2p->cfg->cb_ctx);
+
+	p2p->p2ps_seek_count = 0;
+
 	p2p_set_state(p2p, P2P_IDLE);
 	p2p_free_req_dev_types(p2p);
 	p2p->start_after_scan = P2P_AFTER_SCAN_NOTHING;
@@ -1143,6 +1305,7 @@
 	p2p->sd_peer = NULL;
 	p2p->invite_peer = NULL;
 	p2p_stop_listen_for_freq(p2p, freq);
+	p2p->send_action_in_progress = 0;
 }
 
 
@@ -1230,7 +1393,7 @@
 static void p2p_prepare_channel_best(struct p2p_data *p2p)
 {
 	u8 op_class, op_channel;
-	const int op_classes_5ghz[] = { 124, 115, 0 };
+	const int op_classes_5ghz[] = { 124, 125, 115, 0 };
 	const int op_classes_ht40[] = { 126, 127, 116, 117, 0 };
 	const int op_classes_vht[] = { 128, 0 };
 
@@ -1335,8 +1498,8 @@
 	if (go)
 		p2p_channels_remove_freqs(&p2p->channels, &p2p->no_go_freq);
 	else if (!force_freq)
-		p2p_channels_union(&p2p->channels, &p2p->cfg->cli_channels,
-				   &p2p->channels);
+		p2p_channels_union_inplace(&p2p->channels,
+					   &p2p->cfg->cli_channels);
 	p2p_channels_dump(p2p, "after go/cli filter/add", &p2p->channels);
 
 	p2p_dbg(p2p, "Own preference for operation channel: Operating Class %u Channel %u%s",
@@ -1606,7 +1769,14 @@
 
 int p2p_go_params(struct p2p_data *p2p, struct p2p_go_neg_results *params)
 {
-	p2p_build_ssid(p2p, params->ssid, &params->ssid_len);
+	if (p2p->ssid_set) {
+		os_memcpy(params->ssid, p2p->ssid, p2p->ssid_len);
+		params->ssid_len = p2p->ssid_len;
+	} else {
+		p2p_build_ssid(p2p, params->ssid, &params->ssid_len);
+	}
+	p2p->ssid_set = 0;
+
 	p2p_random(params->passphrase, p2p->cfg->passphrase_len);
 	return 0;
 }
@@ -1617,8 +1787,6 @@
 	struct p2p_go_neg_results res;
 	int go = peer->go_state == LOCAL_GO;
 	struct p2p_channels intersection;
-	int freqs;
-	size_t i, j;
 
 	p2p_dbg(p2p, "GO Negotiation with " MACSTR " completed (%s will be GO)",
 		MAC2STR(peer->info.p2p_device_addr), go ? "local end" : "peer");
@@ -1659,21 +1827,9 @@
 		p2p_channels_dump(p2p, "intersection after no-GO removal",
 				  &intersection);
 	}
-	freqs = 0;
-	for (i = 0; i < intersection.reg_classes; i++) {
-		struct p2p_reg_class *c = &intersection.reg_class[i];
-		if (freqs + 1 == P2P_MAX_CHANNELS)
-			break;
-		for (j = 0; j < c->channels; j++) {
-			int freq;
-			if (freqs + 1 == P2P_MAX_CHANNELS)
-				break;
-			freq = p2p_channel_to_freq(c->reg_class, c->channel[j]);
-			if (freq < 0)
-				continue;
-			res.freq_list[freqs++] = freq;
-		}
-	}
+
+	p2p_channels_to_freqs(&intersection, res.freq_list,
+			      P2P_MAX_CHANNELS);
 
 	res.peer_config_timeout = go ? peer->client_timeout : peer->go_timeout;
 
@@ -1714,7 +1870,6 @@
 					   rx_freq);
 		break;
 	case P2P_INVITATION_RESP:
-		p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
 		p2p_process_invitation_resp(p2p, sa, data + 1, len - 1);
 		break;
 	case P2P_PROV_DISC_REQ:
@@ -1993,18 +2148,21 @@
 				attr.num_req_dev_type))
 		return 1; /* Own Primary Device Type matches */
 
-	for (i = 0; i < p2p->cfg->num_sec_dev_types; i++)
+	for (i = 0; i < p2p->cfg->num_sec_dev_types; i++) {
 		if (dev_type_list_match(p2p->cfg->sec_dev_type[i],
 					attr.req_dev_type,
 					attr.num_req_dev_type))
-		return 1; /* Own Secondary Device Type matches */
+			return 1; /* Own Secondary Device Type matches */
+	}
 
 	/* No matching device type found */
 	return 0;
 }
 
 
-struct wpabuf * p2p_build_probe_resp_ies(struct p2p_data *p2p)
+struct wpabuf * p2p_build_probe_resp_ies(struct p2p_data *p2p,
+					 const u8 *query_hash,
+					 u8 query_count)
 {
 	struct wpabuf *buf;
 	u8 *len;
@@ -2019,6 +2177,9 @@
 	if (p2p->vendor_elem && p2p->vendor_elem[VENDOR_ELEM_PROBE_RESP_P2P])
 		extra += wpabuf_len(p2p->vendor_elem[VENDOR_ELEM_PROBE_RESP_P2P]);
 
+	if (query_count)
+		extra += MAX_SVC_ADV_IE_LEN;
+
 	buf = wpabuf_alloc(1000 + extra);
 	if (buf == NULL)
 		return NULL;
@@ -2053,26 +2214,51 @@
 	p2p_buf_add_device_info(buf, p2p, NULL);
 	p2p_buf_update_ie_hdr(buf, len);
 
+	if (query_count) {
+		p2p_buf_add_service_instance(buf, p2p, query_count, query_hash,
+					     p2p->p2ps_adv_list);
+	}
+
 	return buf;
 }
 
 
+static int p2p_service_find_asp(struct p2p_data *p2p, const u8 *hash)
+{
+	struct p2ps_advertisement *adv_data;
+	int any_wfa;
+
+	p2p_dbg(p2p, "ASP find - ASP list: %p", p2p->p2ps_adv_list);
+
+	/* Wildcard org.wi-fi.wfds matches any WFA spec defined service */
+	any_wfa = os_memcmp(hash, p2p->wild_card_hash, P2PS_HASH_LEN) == 0;
+
+	adv_data = p2p->p2ps_adv_list;
+	while (adv_data) {
+		if (os_memcmp(hash, adv_data->hash, P2PS_HASH_LEN) == 0)
+			return 1; /* exact hash match */
+		if (any_wfa &&
+		    os_strncmp(adv_data->svc_name, P2PS_WILD_HASH_STR,
+			       os_strlen(P2PS_WILD_HASH_STR)) == 0)
+			return 1; /* WFA service match */
+		adv_data = adv_data->next;
+	}
+
+	return 0;
+}
+
+
 static enum p2p_probe_req_status
 p2p_reply_probe(struct p2p_data *p2p, const u8 *addr, const u8 *dst,
-		const u8 *bssid, const u8 *ie, size_t ie_len)
+		const u8 *bssid, const u8 *ie, size_t ie_len,
+		unsigned int rx_freq)
 {
 	struct ieee802_11_elems elems;
 	struct wpabuf *buf;
 	struct ieee80211_mgmt *resp;
 	struct p2p_message msg;
 	struct wpabuf *ies;
-
-	if (!p2p->in_listen || !p2p->drv_in_listen) {
-		/* not in Listen state - ignore Probe Request */
-		p2p_dbg(p2p, "Not in Listen state (in_listen=%d drv_in_listen=%d) - ignore Probe Request",
-			p2p->in_listen, p2p->drv_in_listen);
-		return P2P_PREQ_NOT_LISTEN;
-	}
+	u8 channel, op_class;
 
 	if (ieee802_11_parse_elems((u8 *) ie, ie_len, &elems, 0) ==
 	    ParseFailed) {
@@ -2124,6 +2310,53 @@
 		return P2P_PREQ_NOT_P2P;
 	}
 
+	if (msg.service_hash && msg.service_hash_count) {
+		const u8 *hash = msg.service_hash;
+		u8 i;
+		int p2ps_svc_found = 0;
+
+		p2p_dbg(p2p, "in_listen=%d drv_in_listen=%d when received P2PS Probe Request at %u MHz; own Listen channel %u, pending listen freq %u MHz",
+			p2p->in_listen, p2p->drv_in_listen, rx_freq,
+			p2p->cfg->channel, p2p->pending_listen_freq);
+
+		if (!p2p->in_listen && !p2p->drv_in_listen &&
+		    p2p->pending_listen_freq && rx_freq &&
+		    rx_freq != p2p->pending_listen_freq) {
+			p2p_dbg(p2p, "Do not reply to Probe Request frame that was received on %u MHz while waiting to start Listen state on %u MHz",
+				rx_freq, p2p->pending_listen_freq);
+			p2p_parse_free(&msg);
+			return P2P_PREQ_NOT_LISTEN;
+		}
+
+		for (i = 0; i < msg.service_hash_count; i++) {
+			if (p2p_service_find_asp(p2p, hash)) {
+				p2p_dbg(p2p, "Service Hash match found: "
+					MACSTR, MAC2STR(hash));
+				p2ps_svc_found = 1;
+				break;
+			}
+			hash += P2PS_HASH_LEN;
+		}
+
+		/* Probed hash unknown */
+		if (!p2ps_svc_found) {
+			p2p_dbg(p2p, "No Service Hash match found");
+			p2p_parse_free(&msg);
+			return P2P_PREQ_NOT_PROCESSED;
+		}
+	} else {
+		/* This is not a P2PS Probe Request */
+		p2p_dbg(p2p, "No P2PS Hash in Probe Request");
+
+		if (!p2p->in_listen || !p2p->drv_in_listen) {
+			/* not in Listen state - ignore Probe Request */
+			p2p_dbg(p2p, "Not in Listen state (in_listen=%d drv_in_listen=%d) - ignore Probe Request",
+				p2p->in_listen, p2p->drv_in_listen);
+			p2p_parse_free(&msg);
+			return P2P_PREQ_NOT_LISTEN;
+		}
+	}
+
 	if (msg.device_id &&
 	    os_memcmp(msg.device_id, p2p->cfg->dev_addr, ETH_ALEN) != 0) {
 		/* Device ID did not match */
@@ -2141,11 +2374,11 @@
 		p2p_parse_free(&msg);
 		return P2P_PREQ_NOT_PROCESSED;
 	}
-	p2p_parse_free(&msg);
 
 	if (!p2p->cfg->send_probe_resp) {
 		/* Response generated elsewhere */
 		p2p_dbg(p2p, "Probe Resp generated elsewhere - do not generate additional response");
+		p2p_parse_free(&msg);
 		return P2P_PREQ_NOT_PROCESSED;
 	}
 
@@ -2157,7 +2390,9 @@
 	 * really only used for discovery purposes, not to learn exact BSS
 	 * parameters.
 	 */
-	ies = p2p_build_probe_resp_ies(p2p);
+	ies = p2p_build_probe_resp_ies(p2p, msg.service_hash,
+				       msg.service_hash_count);
+	p2p_parse_free(&msg);
 	if (ies == NULL)
 		return P2P_PREQ_NOT_PROCESSED;
 
@@ -2167,8 +2402,8 @@
 		return P2P_PREQ_NOT_PROCESSED;
 	}
 
-	resp = NULL;
-	resp = wpabuf_put(buf, resp->u.probe_resp.variable - (u8 *) resp);
+	resp = wpabuf_put(buf, offsetof(struct ieee80211_mgmt,
+					u.probe_resp.variable));
 
 	resp->frame_control = host_to_le16((WLAN_FC_TYPE_MGMT << 2) |
 					   (WLAN_FC_STYPE_PROBE_RESP << 4));
@@ -2197,31 +2432,50 @@
 	wpabuf_put_u8(buf, 480 / 5);
 	wpabuf_put_u8(buf, 540 / 5);
 
+	if (!rx_freq) {
+		channel = p2p->cfg->channel;
+	} else if (p2p_freq_to_channel(rx_freq, &op_class, &channel)) {
+		wpabuf_free(ies);
+		wpabuf_free(buf);
+		return P2P_PREQ_NOT_PROCESSED;
+	}
+
 	wpabuf_put_u8(buf, WLAN_EID_DS_PARAMS);
 	wpabuf_put_u8(buf, 1);
-	wpabuf_put_u8(buf, p2p->cfg->channel);
+	wpabuf_put_u8(buf, channel);
 
 	wpabuf_put_buf(buf, ies);
 	wpabuf_free(ies);
 
-	p2p->cfg->send_probe_resp(p2p->cfg->cb_ctx, buf);
+	p2p->cfg->send_probe_resp(p2p->cfg->cb_ctx, buf, rx_freq);
 
 	wpabuf_free(buf);
 
-	return P2P_PREQ_NOT_PROCESSED;
+	return P2P_PREQ_PROCESSED;
 }
 
 
 enum p2p_probe_req_status
 p2p_probe_req_rx(struct p2p_data *p2p, const u8 *addr, const u8 *dst,
-		 const u8 *bssid, const u8 *ie, size_t ie_len)
+		 const u8 *bssid, const u8 *ie, size_t ie_len,
+		 unsigned int rx_freq)
 {
 	enum p2p_probe_req_status res;
 
 	p2p_add_dev_from_probe_req(p2p, addr, ie, ie_len);
 
-	res = p2p_reply_probe(p2p, addr, dst, bssid, ie, ie_len);
+	res = p2p_reply_probe(p2p, addr, dst, bssid, ie, ie_len, rx_freq);
+	if (res != P2P_PREQ_PROCESSED && res != P2P_PREQ_NOT_PROCESSED)
+		return res;
 
+	/*
+	 * Activate a pending GO Negotiation/Invite flow if a received Probe
+	 * Request frame is from an expected peer. Some devices may share the
+	 * same address for P2P and non-P2P STA running simultaneously. The
+	 * P2P_PREQ_PROCESSED and P2P_PREQ_NOT_PROCESSED p2p_reply_probe()
+	 * return values verified above ensure we are handling a Probe Request
+	 * frame from a P2P peer.
+	 */
 	if ((p2p->state == P2P_CONNECT || p2p->state == P2P_CONNECT_LISTEN) &&
 	    p2p->go_neg_peer &&
 	    os_memcmp(addr, p2p->go_neg_peer->info.p2p_device_addr, ETH_ALEN)
@@ -2231,7 +2485,7 @@
 		p2p_dbg(p2p, "Found GO Negotiation peer - try to start GO negotiation from timeout");
 		eloop_cancel_timeout(p2p_go_neg_start, p2p, NULL);
 		eloop_register_timeout(0, 0, p2p_go_neg_start, p2p, NULL);
-		return P2P_PREQ_PROCESSED;
+		return res;
 	}
 
 	if ((p2p->state == P2P_INVITE || p2p->state == P2P_INVITE_LISTEN) &&
@@ -2243,7 +2497,7 @@
 		p2p_dbg(p2p, "Found Invite peer - try to start Invite from timeout");
 		eloop_cancel_timeout(p2p_invite_start, p2p, NULL);
 		eloop_register_timeout(0, 0, p2p_invite_start, p2p, NULL);
-		return P2P_PREQ_PROCESSED;
+		return res;
 	}
 
 	return res;
@@ -2258,10 +2512,21 @@
 	size_t tmplen;
 	int res;
 	u8 group_capab;
+	struct p2p_message msg;
 
 	if (p2p_ie == NULL)
 		return 0; /* WLAN AP is not a P2P manager */
 
+	os_memset(&msg, 0, sizeof(msg));
+	if (p2p_parse_p2p_ie(p2p_ie, &msg) < 0)
+		return 0;
+
+	p2p_dbg(p2p, "BSS P2P manageability %s",
+		msg.manageability ? "enabled" : "disabled");
+
+	if (!msg.manageability)
+		return 0;
+
 	/*
 	 * (Re)Association Request - P2P IE
 	 * P2P Capability attribute (shall be present)
@@ -2378,6 +2643,151 @@
 }
 
 
+struct p2ps_advertisement *
+p2p_service_p2ps_id(struct p2p_data *p2p, u32 adv_id)
+{
+	struct p2ps_advertisement *adv_data;
+
+	if (!p2p)
+		return NULL;
+
+	adv_data = p2p->p2ps_adv_list;
+	while (adv_data) {
+		if (adv_data->id == adv_id)
+			return adv_data;
+		adv_data = adv_data->next;
+	}
+
+	return NULL;
+}
+
+
+int p2p_service_del_asp(struct p2p_data *p2p, u32 adv_id)
+{
+	struct p2ps_advertisement *adv_data;
+	struct p2ps_advertisement **prior;
+
+	if (!p2p)
+		return -1;
+
+	adv_data = p2p->p2ps_adv_list;
+	prior = &p2p->p2ps_adv_list;
+	while (adv_data) {
+		if (adv_data->id == adv_id) {
+			p2p_dbg(p2p, "Delete ASP adv_id=0x%x", adv_id);
+			*prior = adv_data->next;
+			os_free(adv_data);
+			return 0;
+		}
+		prior = &adv_data->next;
+		adv_data = adv_data->next;
+	}
+
+	return -1;
+}
+
+
+int p2p_service_add_asp(struct p2p_data *p2p, int auto_accept, u32 adv_id,
+			const char *adv_str, u8 svc_state, u16 config_methods,
+			const char *svc_info)
+{
+	struct p2ps_advertisement *adv_data, *tmp, **prev;
+	u8 buf[P2PS_HASH_LEN];
+	size_t adv_data_len, adv_len, info_len = 0;
+
+	if (!p2p || !adv_str || !adv_str[0])
+		return -1;
+
+	if (!(config_methods & p2p->cfg->config_methods)) {
+		p2p_dbg(p2p, "Config methods not supported svc: 0x%x dev: 0x%x",
+			config_methods, p2p->cfg->config_methods);
+		return -1;
+	}
+
+	if (!p2ps_gen_hash(p2p, adv_str, buf))
+		return -1;
+
+	if (svc_info)
+		info_len = os_strlen(svc_info);
+	adv_len = os_strlen(adv_str);
+	adv_data_len = sizeof(struct p2ps_advertisement) + adv_len + 1 +
+		info_len + 1;
+
+	adv_data = os_zalloc(adv_data_len);
+	if (!adv_data)
+		return -1;
+
+	os_memcpy(adv_data->hash, buf, P2PS_HASH_LEN);
+	adv_data->id = adv_id;
+	adv_data->state = svc_state;
+	adv_data->config_methods = config_methods & p2p->cfg->config_methods;
+	adv_data->auto_accept = (u8) auto_accept;
+	os_memcpy(adv_data->svc_name, adv_str, adv_len);
+
+	if (svc_info && info_len) {
+		adv_data->svc_info = &adv_data->svc_name[adv_len + 1];
+		os_memcpy(adv_data->svc_info, svc_info, info_len);
+	}
+
+	/*
+	 * Group Advertisements by service string. They do not need to be
+	 * sorted, but groups allow easier Probe Response instance grouping
+	 */
+	tmp = p2p->p2ps_adv_list;
+	prev = &p2p->p2ps_adv_list;
+	while (tmp) {
+		if (tmp->id == adv_data->id) {
+			if (os_strcmp(tmp->svc_name, adv_data->svc_name) != 0) {
+				os_free(adv_data);
+				return -1;
+			}
+			adv_data->next = tmp->next;
+			*prev = adv_data;
+			os_free(tmp);
+			goto inserted;
+		} else {
+			if (os_strcmp(tmp->svc_name, adv_data->svc_name) == 0) {
+				adv_data->next = tmp->next;
+				tmp->next = adv_data;
+				goto inserted;
+			}
+		}
+		prev = &tmp->next;
+		tmp = tmp->next;
+	}
+
+	/* No svc_name match found */
+	adv_data->next = p2p->p2ps_adv_list;
+	p2p->p2ps_adv_list = adv_data;
+
+inserted:
+	p2p_dbg(p2p,
+		"Added ASP advertisement adv_id=0x%x config_methods=0x%x svc_state=0x%x adv_str='%s'",
+		adv_id, adv_data->config_methods, svc_state, adv_str);
+
+	return 0;
+}
+
+
+void p2p_service_flush_asp(struct p2p_data *p2p)
+{
+	struct p2ps_advertisement *adv, *prev;
+
+	if (!p2p)
+		return;
+
+	adv = p2p->p2ps_adv_list;
+	while (adv) {
+		prev = adv;
+		adv = adv->next;
+		os_free(prev);
+	}
+
+	p2p->p2ps_adv_list = NULL;
+	p2p_dbg(p2p, "All ASP advertisements flushed");
+}
+
+
 int p2p_parse_dev_addr_in_p2p_ie(struct wpabuf *p2p_ie, u8 *dev_addr)
 {
 	struct p2p_message msg;
@@ -2491,6 +2901,8 @@
 			p2p->cfg->num_pref_chan = 0;
 	}
 
+	p2ps_gen_hash(p2p, P2PS_WILD_HASH_STR, p2p->wild_card_hash);
+
 	p2p->min_disc_int = 1;
 	p2p->max_disc_int = 3;
 	p2p->max_disc_tu = -1;
@@ -2542,6 +2954,7 @@
 	eloop_cancel_timeout(p2p_ext_listen_timeout, p2p, NULL);
 	eloop_cancel_timeout(p2p_scan_timeout, p2p, NULL);
 	eloop_cancel_timeout(p2p_go_neg_start, p2p, NULL);
+	eloop_cancel_timeout(p2p_go_neg_wait_timeout, p2p, NULL);
 	p2p_flush(p2p);
 	p2p_free_req_dev_types(p2p);
 	os_free(p2p->cfg->dev_name);
@@ -2551,10 +2964,13 @@
 	os_free(p2p->cfg->serial_number);
 	os_free(p2p->cfg->pref_chan);
 	os_free(p2p->groups);
+	p2ps_prov_free(p2p);
 	wpabuf_free(p2p->sd_resp);
 	os_free(p2p->after_scan_tx);
 	p2p_remove_wps_vendor_extensions(p2p);
 	os_free(p2p->no_go_freq.range);
+	p2p_service_flush_asp(p2p);
+
 	os_free(p2p);
 }
 
@@ -2584,8 +3000,10 @@
 
 	p2p_dbg(p2p, "Unauthorizing " MACSTR, MAC2STR(addr));
 
-	if (p2p->go_neg_peer == dev)
+	if (p2p->go_neg_peer == dev) {
+		eloop_cancel_timeout(p2p_go_neg_wait_timeout, p2p, NULL);
 		p2p->go_neg_peer = NULL;
+	}
 
 	dev->wps_method = WPS_NOT_READY;
 	dev->oob_pw_id = 0;
@@ -2743,28 +3161,64 @@
 }
 
 
+static int p2p_pre_find_operation(struct p2p_data *p2p, struct p2p_device *dev)
+{
+	if (dev->sd_pending_bcast_queries == 0) {
+		/* Initialize with total number of registered broadcast
+		 * SD queries. */
+		dev->sd_pending_bcast_queries = p2p->num_p2p_sd_queries;
+	}
+
+	if (p2p_start_sd(p2p, dev) == 0)
+		return 1;
+
+	if (dev->req_config_methods &&
+	    !(dev->flags & P2P_DEV_PD_FOR_JOIN)) {
+		p2p_dbg(p2p, "Send pending Provision Discovery Request to "
+			MACSTR " (config methods 0x%x)",
+			MAC2STR(dev->info.p2p_device_addr),
+			dev->req_config_methods);
+		if (p2p_send_prov_disc_req(p2p, dev, 0, 0) == 0)
+			return 1;
+	}
+
+	return 0;
+}
+
+
 void p2p_continue_find(struct p2p_data *p2p)
 {
 	struct p2p_device *dev;
-	p2p_set_state(p2p, P2P_SEARCH);
-	dl_list_for_each(dev, &p2p->devices, struct p2p_device, list) {
-		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;
-		}
+	int found;
 
-		if (p2p_start_sd(p2p, dev) == 0)
-			return;
-		if (dev->req_config_methods &&
-		    !(dev->flags & P2P_DEV_PD_FOR_JOIN)) {
-			p2p_dbg(p2p, "Send pending Provision Discovery Request to "
-				MACSTR " (config methods 0x%x)",
-				MAC2STR(dev->info.p2p_device_addr),
-				dev->req_config_methods);
-			if (p2p_send_prov_disc_req(p2p, dev, 0, 0) == 0)
-				return;
+	p2p_set_state(p2p, P2P_SEARCH);
+
+	/* Continue from the device following the last iteration */
+	found = 0;
+	dl_list_for_each(dev, &p2p->devices, struct p2p_device, list) {
+		if (dev == p2p->last_p2p_find_oper) {
+			found = 1;
+			continue;
 		}
+		if (!found)
+			continue;
+		if (p2p_pre_find_operation(p2p, dev) > 0) {
+			p2p->last_p2p_find_oper = dev;
+			return;
+		}
+	}
+
+	/*
+	 * Wrap around to the beginning of the list and continue until the last
+	 * iteration device.
+	 */
+	dl_list_for_each(dev, &p2p->devices, struct p2p_device, list) {
+		if (p2p_pre_find_operation(p2p, dev) > 0) {
+			p2p->last_p2p_find_oper = dev;
+			return;
+		}
+		if (dev == p2p->last_p2p_find_oper)
+			break;
 	}
 
 	p2p_listen_in_find(p2p, 1);
@@ -2778,18 +3232,22 @@
 	p2p->pending_action_state = P2P_NO_PENDING_ACTION;
 
 	if (!success) {
+		if (p2p->sd_peer)
+			p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
 		p2p->sd_peer = NULL;
-		p2p_continue_find(p2p);
+		if (p2p->state != P2P_IDLE)
+			p2p_continue_find(p2p);
 		return;
 	}
 
 	if (p2p->sd_peer == NULL) {
 		p2p_dbg(p2p, "No SD peer entry known");
-		p2p_continue_find(p2p);
+		if (p2p->state != P2P_IDLE)
+			p2p_continue_find(p2p);
 		return;
 	}
 
-	if (p2p->sd_query->for_all_peers) {
+	if (p2p->sd_query && p2p->sd_query->for_all_peers) {
 		/* Update the pending broadcast SD query count for this device
 		 */
 		p2p->sd_peer->sd_pending_bcast_queries--;
@@ -2816,9 +3274,6 @@
 {
 	struct p2p_device *dev;
 
-	if (p2p->state != P2P_IDLE)
-		return;
-
 	/*
 	 * Retry the prov disc req attempt only for the peer that the user had
 	 * requested.
@@ -2892,6 +3347,51 @@
 }
 
 
+static int p2p_check_after_scan_tx_continuation(struct p2p_data *p2p)
+{
+	if (p2p->after_scan_tx_in_progress) {
+		p2p->after_scan_tx_in_progress = 0;
+		if (p2p->start_after_scan != P2P_AFTER_SCAN_NOTHING &&
+		    p2p_run_after_scan(p2p))
+			return 1;
+		if (p2p->state == P2P_SEARCH) {
+			p2p_dbg(p2p, "Continue find after after_scan_tx completion");
+			p2p_continue_find(p2p);
+		}
+	}
+
+	return 0;
+}
+
+
+static void p2p_prov_disc_resp_cb(struct p2p_data *p2p, int success)
+{
+	p2p_dbg(p2p, "Provision Discovery Response TX callback: success=%d",
+		success);
+
+	if (p2p->send_action_in_progress) {
+		p2p->send_action_in_progress = 0;
+		p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
+	}
+
+	p2p->pending_action_state = P2P_NO_PENDING_ACTION;
+
+	if (!success)
+		goto continue_search;
+
+	if (!p2p->cfg->prov_disc_resp_cb ||
+	    p2p->cfg->prov_disc_resp_cb(p2p->cfg->cb_ctx) < 1)
+		goto continue_search;
+
+	p2p_dbg(p2p,
+		"Post-Provision Discovery operations started - do not try to continue other P2P operations");
+	return;
+
+continue_search:
+	p2p_check_after_scan_tx_continuation(p2p);
+}
+
+
 int p2p_scan_res_handler(struct p2p_data *p2p, const u8 *bssid, int freq,
 			 struct os_reltime *rx_time, int level, const u8 *ies,
 			 size_t ies_len)
@@ -2934,6 +3434,7 @@
 
 void p2p_scan_ie(struct p2p_data *p2p, struct wpabuf *ies, const u8 *dev_id)
 {
+	u8 dev_capab;
 	u8 *len;
 
 #ifdef CONFIG_WIFI_DISPLAY
@@ -2946,8 +3447,15 @@
 			       p2p->vendor_elem[VENDOR_ELEM_PROBE_REQ_P2P]);
 
 	len = p2p_buf_add_ie_hdr(ies);
-	p2p_buf_add_capability(ies, p2p->dev_capab &
-			       ~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY, 0);
+
+	dev_capab = p2p->dev_capab & ~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY;
+
+	/* P2PS requires Probe Request frames to include SD bit */
+	if (p2p->p2ps_seek && p2p->p2ps_seek_count)
+		dev_capab |= P2P_DEV_CAPAB_SERVICE_DISCOVERY;
+
+	p2p_buf_add_capability(ies, dev_capab, 0);
+
 	if (dev_id)
 		p2p_buf_add_device_id(ies, dev_id);
 	if (p2p->cfg->reg_class && p2p->cfg->channel)
@@ -2957,6 +3465,10 @@
 	if (p2p->ext_listen_interval)
 		p2p_buf_add_ext_listen_timing(ies, p2p->ext_listen_period,
 					      p2p->ext_listen_interval);
+
+	if (p2p->p2ps_seek && p2p->p2ps_seek_count)
+		p2p_buf_add_service_hash(ies, p2p);
+
 	/* TODO: p2p_buf_add_operating_channel() if GO */
 	p2p_buf_update_ie_hdr(ies, len);
 }
@@ -3058,24 +3570,20 @@
 {
 	p2p_dbg(p2p, "GO Negotiation Response (failure) TX callback: success=%d", success);
 	if (p2p->go_neg_peer && p2p->go_neg_peer->status != P2P_SC_SUCCESS) {
-		p2p_go_neg_failed(p2p, p2p->go_neg_peer,
-				  p2p->go_neg_peer->status);
-	} else if (success) {
+		p2p_go_neg_failed(p2p, p2p->go_neg_peer->status);
+		return;
+	}
+
+	if (success) {
 		struct p2p_device *dev;
 		dev = p2p_get_device(p2p, addr);
 		if (dev &&
-		    dev->status == P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE) {
+		    dev->status == P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE)
 			dev->flags |= P2P_DEV_PEER_WAITING_RESPONSE;
-			if ((p2p->state == P2P_SEARCH) ||
-			    (p2p->state == P2P_LISTEN_ONLY)) {
-				/* Clear our search state or Listen state since
-				 * now peer is awaiting response from our side.
-				 */
-				p2p_dbg(p2p, "Clear the P2P discovery state");
-				p2p_stop_find(p2p);
-			}
-		}
 	}
+
+	if (p2p->state == P2P_SEARCH || p2p->state == P2P_SD_DURING_FIND)
+		p2p_continue_find(p2p);
 }
 
 
@@ -3087,7 +3595,7 @@
 	p2p_dbg(p2p, "GO Negotiation Confirm TX callback: result=%d", result);
 	if (result == P2P_SEND_ACTION_FAILED) {
 		p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
-		p2p_go_neg_failed(p2p, p2p->go_neg_peer, -1);
+		p2p_go_neg_failed(p2p, -1);
 		return;
 	}
 
@@ -3152,9 +3660,9 @@
 	int success;
 
 	p2p_dbg(p2p, "Action frame TX callback (state=%d freq=%u dst=" MACSTR
-		" src=" MACSTR " bssid=" MACSTR " result=%d",
+		" src=" MACSTR " bssid=" MACSTR " result=%d p2p_state=%s)",
 		p2p->pending_action_state, freq, MAC2STR(dst), MAC2STR(src),
-		MAC2STR(bssid), result);
+		MAC2STR(bssid), result, p2p_state_txt(p2p->state));
 	success = result == P2P_SEND_ACTION_SUCCESS;
 	state = p2p->pending_action_state;
 	p2p->pending_action_state = P2P_NO_PENDING_ACTION;
@@ -3164,16 +3672,7 @@
 			p2p->send_action_in_progress = 0;
 			p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
 		}
-		if (p2p->after_scan_tx_in_progress) {
-			p2p->after_scan_tx_in_progress = 0;
-			if (p2p->start_after_scan != P2P_AFTER_SCAN_NOTHING &&
-			    p2p_run_after_scan(p2p))
-				break;
-			if (p2p->state == P2P_SEARCH) {
-				p2p_dbg(p2p, "Continue find after after_scan_tx completion");
-				p2p_continue_find(p2p);
-			}
-		}
+		p2p_check_after_scan_tx_continuation(p2p);
 		break;
 	case P2P_PENDING_GO_NEG_REQUEST:
 		p2p_go_neg_req_cb(p2p, success);
@@ -3193,6 +3692,9 @@
 	case P2P_PENDING_PD:
 		p2p_prov_disc_cb(p2p, success);
 		break;
+	case P2P_PENDING_PD_RESPONSE:
+		p2p_prov_disc_resp_cb(p2p, success);
+		break;
 	case P2P_PENDING_INVITATION_REQUEST:
 		p2p_invitation_req_cb(p2p, success);
 		break;
@@ -3258,7 +3760,7 @@
 	if (p2p->state == P2P_CONNECT_LISTEN && p2p->go_neg_peer) {
 		if (p2p->go_neg_peer->connect_reqs >= 120) {
 			p2p_dbg(p2p, "Timeout on sending GO Negotiation Request without getting response");
-			p2p_go_neg_failed(p2p, p2p->go_neg_peer, -1);
+			p2p_go_neg_failed(p2p, -1);
 			return 0;
 		}
 
@@ -3309,7 +3811,7 @@
 	if (p2p->go_neg_peer &&
 	    (p2p->go_neg_peer->flags & P2P_DEV_WAIT_GO_NEG_CONFIRM)) {
 		p2p_dbg(p2p, "Wait for GO Negotiation Confirm timed out - assume GO Negotiation failed");
-		p2p_go_neg_failed(p2p, p2p->go_neg_peer, -1);
+		p2p_go_neg_failed(p2p, -1);
 		return;
 	}
 	if (p2p->go_neg_peer &&
@@ -3340,7 +3842,7 @@
 
 		if (p2p->go_neg_peer->connect_reqs >= 120) {
 			p2p_dbg(p2p, "Timeout on sending GO Negotiation Request without getting response");
-			p2p_go_neg_failed(p2p, p2p->go_neg_peer, -1);
+			p2p_go_neg_failed(p2p, -1);
 			return;
 		}
 
@@ -3366,20 +3868,12 @@
 static void p2p_timeout_wait_peer_idle(struct p2p_data *p2p)
 {
 	struct p2p_device *dev = p2p->go_neg_peer;
-	struct os_reltime now;
 
 	if (dev == NULL) {
 		p2p_dbg(p2p, "Unknown GO Neg peer - stop GO Neg wait");
 		return;
 	}
 
-	os_get_reltime(&now);
-	if (os_reltime_expired(&now, &dev->go_neg_wait_started, 120)) {
-		p2p_dbg(p2p, "Timeout on waiting peer to become ready for GO Negotiation");
-		p2p_go_neg_failed(p2p, dev, -1);
-		return;
-	}
-
 	p2p_dbg(p2p, "Go to Listen state while waiting for the peer to become ready for GO Negotiation");
 	p2p_set_state(p2p, P2P_WAIT_PEER_CONNECT);
 	p2p_listen_in_find(p2p, 0);
@@ -3407,6 +3901,9 @@
 
 static void p2p_timeout_prov_disc_req(struct p2p_data *p2p)
 {
+	u32 adv_id = 0;
+	u8 *adv_mac = NULL;
+
 	p2p->pending_action_state = P2P_NO_PENDING_ACTION;
 
 	/*
@@ -3435,12 +3932,18 @@
 				for_join = 1;
 		}
 
+		if (p2p->p2ps_prov) {
+			adv_id = p2p->p2ps_prov->adv_id;
+			adv_mac = p2p->p2ps_prov->adv_mac;
+		}
+
 		if (p2p->cfg->prov_disc_fail)
 			p2p->cfg->prov_disc_fail(p2p->cfg->cb_ctx,
 						 p2p->pending_pd_devaddr,
 						 for_join ?
 						 P2P_PROV_DISC_TIMEOUT_JOIN :
-						 P2P_PROV_DISC_TIMEOUT);
+						 P2P_PROV_DISC_TIMEOUT,
+						 adv_id, adv_mac, NULL);
 		p2p_reset_pending_pd(p2p);
 	}
 }
@@ -3490,6 +3993,10 @@
 	p2p_dbg(p2p, "Timeout (state=%s)", p2p_state_txt(p2p->state));
 
 	p2p->in_listen = 0;
+	if (p2p->drv_in_listen) {
+		p2p_dbg(p2p, "Driver is still in listen state - stop it");
+		p2p->cfg->stop_listen(p2p->cfg->cb_ctx);
+	}
 
 	switch (p2p->state) {
 	case P2P_IDLE:
@@ -3585,6 +4092,8 @@
 		return "PBC";
 	case WPS_NFC:
 		return "NFC";
+	case WPS_P2PS:
+		return "P2PS";
 	}
 
 	return "??";
@@ -3661,7 +4170,7 @@
 			  "country=%c%c\n"
 			  "oper_freq=%d\n"
 			  "req_config_methods=0x%x\n"
-			  "flags=%s%s%s%s%s%s%s%s%s%s%s%s%s\n"
+			  "flags=%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s\n"
 			  "status=%d\n"
 			  "invitation_reqs=%u\n",
 			  (int) (now.sec - dev->last_seen.sec),
@@ -3687,6 +4196,8 @@
 			  "[PD_PEER_DISPLAY]" : "",
 			  dev->flags & P2P_DEV_PD_PEER_KEYPAD ?
 			  "[PD_PEER_KEYPAD]" : "",
+			  dev->flags & P2P_DEV_PD_PEER_P2PS ?
+			  "[PD_PEER_P2PS]" : "",
 			  dev->flags & P2P_DEV_USER_REJECTED ?
 			  "[USER_REJECTED]" : "",
 			  dev->flags & P2P_DEV_PEER_WAITING_RESPONSE ?
@@ -3703,9 +4214,11 @@
 			  "[FORCE_FREQ]" : "",
 			  dev->flags & P2P_DEV_PD_FOR_JOIN ?
 			  "[PD_FOR_JOIN]" : "",
+			  dev->flags & P2P_DEV_LAST_SEEN_AS_GROUP_CLIENT ?
+			  "[LAST_SEEN_AS_GROUP_CLIENT]" : "",
 			  dev->status,
 			  dev->invitation_reqs);
-	if (res < 0 || res >= end - pos)
+	if (os_snprintf_error(end - pos, res))
 		return pos - buf;
 	pos += res;
 
@@ -3715,7 +4228,7 @@
 				  "ext_listen_interval=%u\n",
 				  dev->ext_listen_period,
 				  dev->ext_listen_interval);
-		if (res < 0 || res >= end - pos)
+		if (os_snprintf_error(end - pos, res))
 			return pos - buf;
 		pos += res;
 	}
@@ -3725,7 +4238,7 @@
 				  "oper_ssid=%s\n",
 				  wpa_ssid_txt(dev->oper_ssid,
 					       dev->oper_ssid_len));
-		if (res < 0 || res >= end - pos)
+		if (os_snprintf_error(end - pos, res))
 			return pos - buf;
 		pos += res;
 	}
@@ -3733,7 +4246,7 @@
 #ifdef CONFIG_WIFI_DISPLAY
 	if (dev->info.wfd_subelems) {
 		res = os_snprintf(pos, end - pos, "wfd_subelems=");
-		if (res < 0 || res >= end - pos)
+		if (os_snprintf_error(end - pos, res))
 			return pos - buf;
 		pos += res;
 
@@ -3742,7 +4255,7 @@
 					wpabuf_len(dev->info.wfd_subelems));
 
 		res = os_snprintf(pos, end - pos, "\n");
-		if (res < 0 || res >= end - pos)
+		if (os_snprintf_error(end - pos, res))
 			return pos - buf;
 		pos += res;
 	}
@@ -4118,15 +4631,18 @@
 	if (p2p_channel_to_freq(reg_class, channel) < 0)
 		return -1;
 
-	p2p_dbg(p2p, "Set Listen channel: reg_class %u channel %u",
-		reg_class, channel);
-
 	/*
 	 * Listen channel was set in configuration or set by control interface;
 	 * cannot override it.
 	 */
-	if (p2p->cfg->channel_forced && forced == 0)
+	if (p2p->cfg->channel_forced && forced == 0) {
+		p2p_dbg(p2p,
+			"Listen channel was previously configured - do not override based on optimization");
 		return -1;
+	}
+
+	p2p_dbg(p2p, "Set Listen channel: reg_class %u channel %u",
+		reg_class, channel);
 
 	if (p2p->state == P2P_IDLE) {
 		p2p->cfg->reg_class = reg_class;
@@ -4751,6 +5267,7 @@
 
 	if (!msg.oob_go_neg_channel) {
 		p2p_dbg(p2p, "OOB GO Negotiation Channel attribute not included");
+		p2p_parse_free(&msg);
 		return -1;
 	}
 
@@ -4762,6 +5279,7 @@
 					   msg.oob_go_neg_channel[4]);
 	if (freq < 0) {
 		p2p_dbg(p2p, "Unknown peer OOB GO Neg channel");
+		p2p_parse_free(&msg);
 		return -1;
 	}
 	role = msg.oob_go_neg_channel[5];
@@ -4782,6 +5300,7 @@
 					   p2p->cfg->channel);
 		if (freq < 0) {
 			p2p_dbg(p2p, "Own listen channel not known");
+			p2p_parse_free(&msg);
 			return -1;
 		}
 		p2p_dbg(p2p, "Use own Listen channel as OOB GO Neg channel: %u MHz", freq);
@@ -4860,3 +5379,13 @@
 {
 	p2p->vendor_elem = vendor_elem;
 }
+
+
+void p2p_go_neg_wait_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+	struct p2p_data *p2p = eloop_ctx;
+
+	p2p_dbg(p2p,
+		"Timeout on waiting peer to become ready for GO Negotiation");
+	p2p_go_neg_failed(p2p, -1);
+}
diff --git a/src/p2p/p2p.h b/src/p2p/p2p.h
index 076a2ac..67b8bdb 100644
--- a/src/p2p/p2p.h
+++ b/src/p2p/p2p.h
@@ -9,7 +9,18 @@
 #ifndef P2P_H
 #define P2P_H
 
-#include "wps/wps_defs.h"
+#include "common/ieee802_11_defs.h"
+#include "wps/wps.h"
+
+/* P2P ASP Setup Capability */
+#define P2PS_SETUP_NONE 0
+#define P2PS_SETUP_NEW BIT(0)
+#define P2PS_SETUP_CLIENT BIT(1)
+#define P2PS_SETUP_GROUP_OWNER BIT(2)
+
+#define P2PS_WILD_HASH_STR "org.wi-fi.wfds"
+#define P2PS_HASH_LEN 6
+#define P2P_MAX_QUERY_HASH 6
 
 /**
  * P2P_MAX_REG_CLASSES - Maximum number of regulatory classes
@@ -52,7 +63,8 @@
 };
 
 enum p2p_wps_method {
-	WPS_NOT_READY, WPS_PIN_DISPLAY, WPS_PIN_KEYPAD, WPS_PBC, WPS_NFC
+	WPS_NOT_READY, WPS_PIN_DISPLAY, WPS_PIN_KEYPAD, WPS_PBC, WPS_NFC,
+	WPS_P2PS
 };
 
 /**
@@ -84,7 +96,7 @@
 	/**
 	 * ssid - SSID of the group
 	 */
-	u8 ssid[32];
+	u8 ssid[SSID_MAX_LEN];
 
 	/**
 	 * ssid_len - Length of SSID in octets
@@ -142,11 +154,99 @@
 	unsigned int peer_config_timeout;
 };
 
+struct p2ps_provision {
+	/**
+	 * status - Remote returned provisioning status code
+	 */
+	int status;
+
+	/**
+	 * adv_id - P2PS Advertisement ID
+	 */
+	u32 adv_id;
+
+	/**
+	 * session_id - P2PS Session ID
+	 */
+	u32 session_id;
+
+	/**
+	 * method - WPS Method (to be) used to establish session
+	 */
+	u16 method;
+
+	/**
+	 * conncap - Connection Capabilities negotiated between P2P peers
+	 */
+	u8 conncap;
+
+	/**
+	 * role - Info about the roles to be used for this connection
+	 */
+	u8 role;
+
+	/**
+	 * session_mac - MAC address of the peer that started the session
+	 */
+	u8 session_mac[ETH_ALEN];
+
+	/**
+	 * adv_mac - MAC address of the peer advertised the service
+	 */
+	u8 adv_mac[ETH_ALEN];
+
+	/**
+	 * info - Vendor defined extra Provisioning information
+	 */
+	char info[0];
+};
+
+struct p2ps_advertisement {
+	struct p2ps_advertisement *next;
+
+	/**
+	 * svc_info - Pointer to (internal) Service defined information
+	 */
+	char *svc_info;
+
+	/**
+	 * id - P2PS Advertisement ID
+	 */
+	u32 id;
+
+	/**
+	 * config_methods - WPS Methods which are allowed for this service
+	 */
+	u16 config_methods;
+
+	/**
+	 * state - Current state of the service: 0 - Out Of Service, 1-255 Vendor defined
+	 */
+	u8 state;
+
+	/**
+	 * auto_accept - Automatically Accept provisioning request if possible.
+	 */
+	u8 auto_accept;
+
+	/**
+	 * hash - 6 octet Service Name has to match against incoming Probe Requests
+	 */
+	u8 hash[P2PS_HASH_LEN];
+
+	/**
+	 * svc_name - NULL Terminated UTF-8 Service Name, and svc_info storage
+	 */
+	char svc_name[0];
+};
+
+
 struct p2p_data;
 
 enum p2p_scan_type {
 	P2P_SCAN_SOCIAL,
 	P2P_SCAN_FULL,
+	P2P_SCAN_SPECIFIC,
 	P2P_SCAN_SOCIAL_PLUS_ONE
 };
 
@@ -169,27 +269,27 @@
 	/**
 	 * device_name - Device Name (0..32 octets encoded in UTF-8)
 	 */
-	char device_name[33];
+	char device_name[WPS_DEV_NAME_MAX_LEN + 1];
 
 	/**
 	 * manufacturer - Manufacturer (0..64 octets encoded in UTF-8)
 	 */
-	char manufacturer[65];
+	char manufacturer[WPS_MANUFACTURER_MAX_LEN + 1];
 
 	/**
 	 * model_name - Model Name (0..32 octets encoded in UTF-8)
 	 */
-	char model_name[33];
+	char model_name[WPS_MODEL_NAME_MAX_LEN + 1];
 
 	/**
 	 * model_number - Model Number (0..32 octets encoded in UTF-8)
 	 */
-	char model_number[33];
+	char model_number[WPS_MODEL_NUMBER_MAX_LEN + 1];
 
 	/**
 	 * serial_number - Serial Number (0..32 octets encoded in UTF-8)
 	 */
-	char serial_number[33];
+	char serial_number[WPS_SERIAL_NUMBER_MAX_LEN + 1];
 
 	/**
 	 * level - Signal level
@@ -217,7 +317,7 @@
 	 * This list includes from 0 to 16 Secondary Device Types as indicated
 	 * by wps_sec_dev_type_list_len (8 * number of types).
 	 */
-	u8 wps_sec_dev_type_list[128];
+	u8 wps_sec_dev_type_list[WPS_SEC_DEV_TYPE_MAX_LEN];
 
 	/**
 	 * wps_sec_dev_type_list_len - Length of secondary device type list
@@ -238,6 +338,11 @@
 	 * IE(s) from the frame that was used to discover the peer.
 	 */
 	struct wpabuf *vendor_elems;
+
+	/**
+	 * p2ps_instance - P2PS Application Service Info
+	 */
+	struct wpabuf *p2ps_instance;
 };
 
 enum p2p_prov_disc_status {
@@ -245,6 +350,7 @@
 	P2P_PROV_DISC_TIMEOUT,
 	P2P_PROV_DISC_REJECTED,
 	P2P_PROV_DISC_TIMEOUT_JOIN,
+	P2P_PROV_DISC_INFO_UNAVAILABLE,
 };
 
 struct p2p_channel {
@@ -390,7 +496,7 @@
 	 * This data will be added to the end of the SSID after the
 	 * DIRECT-<random two octets> prefix.
 	 */
-	u8 ssid_postfix[32 - 9];
+	u8 ssid_postfix[SSID_MAX_LEN - 9];
 
 	/**
 	 * ssid_postfix_len - Length of the ssid_postfix data
@@ -441,7 +547,8 @@
 	 * operation to be completed. Type type argument specifies which type
 	 * of scan is to be done. @P2P_SCAN_SOCIAL indicates that only the
 	 * social channels (1, 6, 11) should be scanned. @P2P_SCAN_FULL
-	 * indicates that all channels are to be scanned.
+	 * indicates that all channels are to be scanned. @P2P_SCAN_SPECIFIC
+	 * request a scan of a single channel specified by freq.
 	 * @P2P_SCAN_SOCIAL_PLUS_ONE request scan of all the social channels
 	 * plus one extra channel specified by freq.
 	 *
@@ -463,12 +570,14 @@
 	 * send_probe_resp - Transmit a Probe Response frame
 	 * @ctx: Callback context from cb_ctx
 	 * @buf: Probe Response frame (including the header and body)
+	 * @freq: Forced frequency (in MHz) to use or 0.
 	 * Returns: 0 on success, -1 on failure
 	 *
 	 * This function is used to reply to Probe Request frames that were
 	 * indicated with a call to p2p_probe_req_rx(). The response is to be
-	 * sent on the same channel or to be dropped if the driver is not
-	 * anymore listening to Probe Request frames.
+	 * sent on the same channel, unless otherwise specified, or to be
+	 * dropped if the driver is not listening to Probe Request frames
+	 * anymore.
 	 *
 	 * Alternatively, the responsibility for building the Probe Response
 	 * frames in Listen state may be in another system component in which
@@ -479,7 +588,8 @@
 	 * Request frames must be indicated by calling p2p_probe_req_rx() even
 	 * if this send_probe_resp() is not used.
 	 */
-	int (*send_probe_resp)(void *ctx, const struct wpabuf *buf);
+	int (*send_probe_resp)(void *ctx, const struct wpabuf *buf,
+			       unsigned int freq);
 
 	/**
 	 * send_action - Transmit an Action frame
@@ -598,6 +708,7 @@
 	 * @ctx: Callback context from cb_ctx
 	 * @src: Source address of the message triggering this notification
 	 * @dev_passwd_id: WPS Device Password ID
+	 * @go_intent: Peer's GO Intent
 	 *
 	 * This callback is used to notify that a P2P Device is requesting
 	 * group owner negotiation with us, but we do not have all the
@@ -606,7 +717,8 @@
 	 * PIN or PBC button press. This information can be provided with a
 	 * call to p2p_connect().
 	 */
-	void (*go_neg_req_rx)(void *ctx, const u8 *src, u16 dev_passwd_id);
+	void (*go_neg_req_rx)(void *ctx, const u8 *src, u16 dev_passwd_id,
+			      u8 go_intent);
 
 	/**
 	 * go_neg_completed - Notification of GO Negotiation results
@@ -705,6 +817,9 @@
 	 * @ctx: Callback context from cb_ctx
 	 * @peer: Source address of the response
 	 * @status: Cause of failure, will not be %P2P_PROV_DISC_SUCCESS
+	 * @adv_id: If non-zero, then the adv_id of the PD Request
+	 * @adv_mac: P2P Device Address of the advertizer
+	 * @deferred_session_resp: Deferred session response sent by advertizer
 	 *
 	 * This callback is used to indicate either a failure or no response
 	 * to an earlier provision discovery request.
@@ -713,7 +828,9 @@
 	 * is not used or failures do not need to be indicated.
 	 */
 	void (*prov_disc_fail)(void *ctx, const u8 *peer,
-			       enum p2p_prov_disc_status status);
+			       enum p2p_prov_disc_status status,
+			       u32 adv_id, const u8 *adv_mac,
+			       const char *deferred_session_resp);
 
 	/**
 	 * invitation_process - Optional callback for processing Invitations
@@ -835,6 +952,83 @@
 	 * or 0 if not.
 	 */
 	int (*is_p2p_in_progress)(void *ctx);
+
+	/**
+	 * Determine if we have a persistent group we share with remote peer
+	 * @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
+	 * @ret_ssid: Buffer for returning group SSID
+	 * @ret_ssid_len: Buffer for returning length of @ssid
+	 * 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);
+
+	/**
+	 * Get information about a possible local GO role
+	 * @ctx: Callback context from cb_ctx
+	 * @intended_addr: Buffer for returning intended GO interface address
+	 * @ssid: Buffer for returning group SSID
+	 * @ssid_len: Buffer for returning length of @ssid
+	 * @group_iface: Buffer for returning whether a separate group interface
+	 *	would be used
+	 * Returns: 1 if GO info found, 0 otherwise
+	 *
+	 * This is used to compose New Group settings (SSID, and intended
+	 * address) during P2PS provisioning if results of provisioning *might*
+	 * result in our being an autonomous GO.
+	 */
+	int (*get_go_info)(void *ctx, u8 *intended_addr,
+			   u8 *ssid, size_t *ssid_len, int *group_iface);
+
+	/**
+	 * remove_stale_groups - Remove stale P2PS groups
+	 *
+	 * Because P2PS stages *potential* GOs, and remote devices can remove
+	 * credentials unilaterally, we need to make sure we don't let stale
+	 * unusable groups build up.
+	 */
+	int (*remove_stale_groups)(void *ctx, const u8 *peer, const u8 *go,
+				   const u8 *ssid, size_t ssid_len);
+
+	/**
+	 * p2ps_prov_complete - P2PS provisioning complete
+	 *
+	 * When P2PS provisioning completes (successfully or not) we must
+	 * transmit all of the results to the upper layers.
+	 */
+	void (*p2ps_prov_complete)(void *ctx, u8 status, const u8 *dev,
+				   const u8 *adv_mac, const u8 *ses_mac,
+				   const u8 *grp_mac, u32 adv_id, u32 ses_id,
+				   u8 conncap, int passwd_id,
+				   const u8 *persist_ssid,
+				   size_t persist_ssid_size, int response_done,
+				   int prov_start, const char *session_info);
+
+	/**
+	 * prov_disc_resp_cb - Callback for indicating completion of PD Response
+	 * @ctx: Callback context from cb_ctx
+	 * Returns: 1 if operation was started, 0 otherwise
+	 *
+	 * This callback can be used to perform any pending actions after
+	 * provisioning. It is mainly used for P2PS pending group creation.
+	 */
+	int (*prov_disc_resp_cb)(void *ctx);
+
+	/**
+	 * p2ps_group_capability - Determine group capability
+	 *
+	 * This function can be used to determine group capability based on
+	 * information from P2PS PD exchange and the current state of ongoing
+	 * groups and driver capabilities.
+	 *
+	 * P2PS_SETUP_* bitmap is used as the parameters and return value.
+	 */
+	u8 (*p2ps_group_capability)(void *ctx, u8 incoming, u8 role);
 };
 
 
@@ -941,12 +1135,26 @@
  *	requested device types.
  * @dev_id: Device ID to search for or %NULL to find all devices
  * @search_delay: Extra delay in milliseconds between search iterations
+ * @seek_count: Number of ASP Service Strings in the seek_string array
+ * @seek_string: ASP Service Strings to query for in Probe Requests
+ * @freq: Requested first scan frequency (in MHz) to modify type ==
+ *	P2P_FIND_START_WITH_FULL behavior. 0 = Use normal full scan.
+ *	If p2p_find is already in progress, this parameter is ignored and full
+ *	scan will be executed.
  * Returns: 0 on success, -1 on failure
  */
 int p2p_find(struct p2p_data *p2p, unsigned int timeout,
 	     enum p2p_discovery_type type,
 	     unsigned int num_req_dev_types, const u8 *req_dev_types,
-	     const u8 *dev_id, unsigned int search_delay);
+	     const u8 *dev_id, unsigned int search_delay,
+	     u8 seek_count, const char **seek_string, int freq);
+
+/**
+ * p2p_notify_scan_trigger_status - Indicate scan trigger status
+ * @p2p: P2P module context from p2p_init()
+ * @status: 0 on success, -1 on failure
+ */
+void p2p_notify_scan_trigger_status(struct p2p_data *p2p, int status);
 
 /**
  * p2p_stop_find - Stop P2P Find (Device Discovery)
@@ -1051,6 +1259,7 @@
  * p2p_prov_disc_req - Send Provision Discovery Request
  * @p2p: P2P module context from p2p_init()
  * @peer_addr: MAC address of the peer P2P client
+ * @p2ps_prov: Provisioning info for P2PS
  * @config_methods: WPS Config Methods value (only one bit set)
  * @join: Whether this is used by a client joining an active group
  * @force_freq: Forced TX frequency for the frame (mainly for the join case)
@@ -1066,7 +1275,8 @@
  * indicated with the p2p_config::prov_disc_resp() callback.
  */
 int p2p_prov_disc_req(struct p2p_data *p2p, const u8 *peer_addr,
-		      u16 config_methods, int join, int force_freq,
+		      struct p2ps_provision *p2ps_prov, u16 config_methods,
+		      int join, int force_freq,
 		      int user_initiated_pd);
 
 /**
@@ -1256,11 +1466,13 @@
  * @bssid: BSSID if available or %NULL
  * @ie: Information elements from the Probe Request frame body
  * @ie_len: Length of ie buffer in octets
+ * @rx_freq: Probe Request frame RX frequency
  * Returns: value indicating the type and status of the probe request
  */
 enum p2p_probe_req_status
 p2p_probe_req_rx(struct p2p_data *p2p, const u8 *addr, const u8 *dst,
-		 const u8 *bssid, const u8 *ie, size_t ie_len);
+		 const u8 *bssid, const u8 *ie, size_t ie_len,
+		 unsigned int rx_freq);
 
 /**
  * p2p_rx_action - Report received Action frame
@@ -1403,7 +1615,7 @@
 	/**
 	 * ssid - Group SSID
 	 */
-	u8 ssid[32];
+	u8 ssid[SSID_MAX_LEN];
 
 	/**
 	 * ssid_len - Length of SSID
@@ -1738,6 +1950,9 @@
 int p2p_channels_includes_freq(const struct p2p_channels *channels,
 			       unsigned int freq);
 
+int p2p_channels_to_freqs(const struct p2p_channels *channels,
+			  int *freq_list, unsigned int max_len);
+
 /**
  * p2p_supported_freq - Check whether channel is supported for P2P
  * @p2p: P2P module context from p2p_init()
@@ -1806,6 +2021,13 @@
 unsigned int p2p_get_group_num_members(struct p2p_group *group);
 
 /**
+ * p2p_client_limit_reached - Check if client limit is reached
+ * @group: P2P group context from p2p_group_init()
+ * Returns: 1 if no of clients limit reached
+ */
+int p2p_client_limit_reached(struct p2p_group *group);
+
+/**
  * p2p_iterate_group_members - Iterate group members
  * @group: P2P group context from p2p_group_init()
  * @next: iteration pointer, must be a pointer to a void * that is set to %NULL
@@ -1912,7 +2134,8 @@
 /**
  * p2p_in_progress - Check whether a P2P operation is progress
  * @p2p: P2P module context from p2p_init()
- * Returns: 0 if P2P module is idle or 1 if an operation is in progress
+ * Returns: 0 if P2P module is idle, 1 if an operation is in progress but not
+ * in search state, or 2 if search state operation is in progress
  */
 int p2p_in_progress(struct p2p_data *p2p);
 
@@ -1999,7 +2222,7 @@
 	size_t oob_dev_pw_len;
 	int go_freq;
 	u8 go_dev_addr[ETH_ALEN];
-	u8 go_ssid[32];
+	u8 go_ssid[SSID_MAX_LEN];
 	size_t go_ssid_len;
 };
 
@@ -2019,4 +2242,15 @@
 
 void p2p_set_vendor_elems(struct p2p_data *p2p, struct wpabuf **vendor_elem);
 
+void p2p_set_intended_addr(struct p2p_data *p2p, const u8 *intended_addr);
+
+struct p2ps_advertisement *
+p2p_service_p2ps_id(struct p2p_data *p2p, u32 adv_id);
+int p2p_service_add_asp(struct p2p_data *p2p, int auto_accept, u32 adv_id,
+			const char *adv_str, u8 svc_state,
+			u16 config_methods, const char *svc_info);
+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);
+
 #endif /* P2P_H */
diff --git a/src/p2p/p2p_build.c b/src/p2p/p2p_build.c
index e9b683d..c733543 100644
--- a/src/p2p/p2p_build.c
+++ b/src/p2p/p2p_build.c
@@ -164,15 +164,18 @@
 		if (peer->wps_method == WPS_PBC)
 			methods |= WPS_CONFIG_PUSHBUTTON;
 		else if (peer->wps_method == WPS_PIN_DISPLAY ||
-			 peer->wps_method == WPS_PIN_KEYPAD)
+			 peer->wps_method == WPS_PIN_KEYPAD) {
 			methods |= WPS_CONFIG_DISPLAY | WPS_CONFIG_KEYPAD;
+			methods |= WPS_CONFIG_P2PS;
+		}
 	} else if (p2p->cfg->config_methods) {
 		methods |= p2p->cfg->config_methods &
 			(WPS_CONFIG_PUSHBUTTON | WPS_CONFIG_DISPLAY |
-			 WPS_CONFIG_KEYPAD);
+			 WPS_CONFIG_KEYPAD | WPS_CONFIG_P2PS);
 	} else {
 		methods |= WPS_CONFIG_PUSHBUTTON;
 		methods |= WPS_CONFIG_DISPLAY | WPS_CONFIG_KEYPAD;
+		methods |= WPS_CONFIG_P2PS;
 	}
 	wpabuf_put_be16(buf, methods);
 
@@ -342,6 +345,325 @@
 }
 
 
+void p2p_buf_add_service_hash(struct wpabuf *buf, struct p2p_data *p2p)
+{
+	if (!p2p)
+		return;
+
+	/* Service Hash */
+	wpabuf_put_u8(buf, P2P_ATTR_SERVICE_HASH);
+	wpabuf_put_le16(buf, p2p->p2ps_seek_count * P2PS_HASH_LEN);
+	wpabuf_put_data(buf, p2p->p2ps_seek_hash,
+			p2p->p2ps_seek_count * P2PS_HASH_LEN);
+	wpa_hexdump(MSG_DEBUG, "P2P: * Service Hash",
+		    p2p->p2ps_seek_hash, p2p->p2ps_seek_count * P2PS_HASH_LEN);
+}
+
+
+void p2p_buf_add_session_info(struct wpabuf *buf, const char *info)
+{
+	size_t info_len = 0;
+
+	if (info && info[0])
+		info_len = os_strlen(info);
+
+	/* Session Information Data Info */
+	wpabuf_put_u8(buf, P2P_ATTR_SESSION_INFORMATION_DATA);
+	wpabuf_put_le16(buf, (u16) info_len);
+
+	if (info) {
+		wpabuf_put_data(buf, info, info_len);
+		wpa_printf(MSG_DEBUG, "P2P: * Session Info Data (%s)", info);
+	}
+}
+
+
+void p2p_buf_add_connection_capability(struct wpabuf *buf, u8 connection_cap)
+{
+	/* Connection Capability Info */
+	wpabuf_put_u8(buf, P2P_ATTR_CONNECTION_CAPABILITY);
+	wpabuf_put_le16(buf, 1);
+	wpabuf_put_u8(buf, connection_cap);
+	wpa_printf(MSG_DEBUG, "P2P: * Connection Capability: 0x%x",
+		   connection_cap);
+}
+
+
+void p2p_buf_add_advertisement_id(struct wpabuf *buf, u32 id, const u8 *mac)
+{
+	if (!buf || !mac)
+		return;
+
+	/* Advertisement ID Info */
+	wpabuf_put_u8(buf, P2P_ATTR_ADVERTISEMENT_ID);
+	wpabuf_put_le16(buf, (u16) (sizeof(u32) + ETH_ALEN));
+	wpabuf_put_le32(buf, id);
+	wpabuf_put_data(buf, mac, ETH_ALEN);
+	wpa_printf(MSG_DEBUG, "P2P: * Advertisement ID (%x) " MACSTR,
+		   id, MAC2STR(mac));
+}
+
+
+static int p2ps_wildcard_hash(struct p2p_data *p2p,
+			      const u8 *hash, u8 hash_count)
+{
+	u8 i;
+	const u8 *test = hash;
+
+	for (i = 0; i < hash_count; i++) {
+		if (os_memcmp(test, p2p->wild_card_hash, P2PS_HASH_LEN) == 0)
+			return 1;
+		test += P2PS_HASH_LEN;
+	}
+
+	return 0;
+}
+
+
+static int p2p_wfa_service_adv(struct p2p_data *p2p)
+{
+	struct p2ps_advertisement *adv;
+
+	for (adv = p2p->p2ps_adv_list; adv; adv = adv->next) {
+		if (os_strncmp(adv->svc_name, P2PS_WILD_HASH_STR,
+			       os_strlen(P2PS_WILD_HASH_STR)) == 0)
+			return 1;
+	}
+
+	return 0;
+}
+
+
+static int p2p_buf_add_service_info(struct wpabuf *buf, struct p2p_data *p2p,
+				    u32 adv_id, u16 config_methods,
+				    const char *svc_name, u8 **ie_len, u8 **pos,
+				    size_t *total_len, u8 *attr_len)
+{
+	size_t svc_len;
+	size_t remaining;
+	size_t info_len;
+
+	p2p_dbg(p2p, "Add service info for %s (adv_id=%u)", svc_name, adv_id);
+	svc_len = os_strlen(svc_name);
+	info_len = sizeof(adv_id) + sizeof(config_methods) + sizeof(u8) +
+		svc_len;
+
+	if (info_len + *total_len > MAX_SVC_ADV_LEN) {
+		p2p_dbg(p2p,
+			"Unsufficient buffer, failed to add advertised service info");
+		return -1;
+	}
+
+	if (svc_len > 255) {
+		p2p_dbg(p2p,
+			"Invalid service name length (%u bytes), failed to add advertised service info",
+			(unsigned int) svc_len);
+		return -1;
+	}
+
+	if (*ie_len) {
+		int ie_data_len = (*pos - *ie_len) - 1;
+
+		if (ie_data_len < 0 || ie_data_len > 255) {
+			p2p_dbg(p2p,
+				"Invalid IE length, failed to add advertised service info");
+			return -1;
+		}
+		remaining = 255 - ie_data_len;
+	} else {
+		/*
+		 * Adding new P2P IE header takes 6 extra bytes:
+		 * - 2 byte IE header (1 byte IE id and 1 byte length)
+		 * - 4 bytes of IE_VENDOR_TYPE are reduced from 255 below
+		 */
+		*ie_len = p2p_buf_add_ie_hdr(buf);
+		remaining = 255 - 4;
+	}
+
+	if (remaining < sizeof(u32) + sizeof(u16) + sizeof(u8)) {
+		/*
+		 * Split adv_id, config_methods, and svc_name_len between two
+		 * IEs.
+		 */
+		size_t front = remaining;
+		size_t back = sizeof(u32) + sizeof(u16) + sizeof(u8) - front;
+		u8 holder[sizeof(u32) + sizeof(u16) + sizeof(u8)];
+
+		WPA_PUT_LE32(holder, adv_id);
+		WPA_PUT_BE16(&holder[sizeof(u32)], config_methods);
+		holder[sizeof(u32) + sizeof(u16)] = svc_len;
+
+		if (front)
+			wpabuf_put_data(buf, holder, front);
+
+		p2p_buf_update_ie_hdr(buf, *ie_len);
+		*ie_len = p2p_buf_add_ie_hdr(buf);
+
+		wpabuf_put_data(buf, &holder[front], back);
+		remaining = 255 - 4 - (sizeof(u32) + sizeof(u16) + sizeof(u8)) -
+			back;
+	} else {
+		wpabuf_put_le32(buf, adv_id);
+		wpabuf_put_be16(buf, config_methods);
+		wpabuf_put_u8(buf, svc_len);
+		remaining -= sizeof(adv_id) + sizeof(config_methods) +
+			sizeof(u8);
+	}
+
+	if (remaining < svc_len) {
+		/* split svc_name between two or three IEs */
+		size_t front = remaining;
+		size_t back = svc_len - front;
+
+		if (front)
+			wpabuf_put_data(buf, svc_name, front);
+
+		p2p_buf_update_ie_hdr(buf, *ie_len);
+		*ie_len = p2p_buf_add_ie_hdr(buf);
+
+		/* In rare cases, we must split across 3 attributes */
+		if (back > 255 - 4) {
+			wpabuf_put_data(buf, &svc_name[front], 255 - 4);
+			back -= 255 - 4;
+			front += 255 - 4;
+			p2p_buf_update_ie_hdr(buf, *ie_len);
+			*ie_len = p2p_buf_add_ie_hdr(buf);
+		}
+
+		wpabuf_put_data(buf, &svc_name[front], back);
+		remaining = 255 - 4 - back;
+	} else {
+		wpabuf_put_data(buf, svc_name, svc_len);
+		remaining -= svc_len;
+	}
+
+	p2p_buf_update_ie_hdr(buf, *ie_len);
+
+	/* set *ie_len to NULL if a new IE has to be added on the next call */
+	if (!remaining)
+		*ie_len = NULL;
+
+	/* set *pos to point to the next byte to update */
+	*pos = wpabuf_put(buf, 0);
+
+	*total_len += info_len;
+	WPA_PUT_LE16(attr_len, (u16) *total_len);
+	return 0;
+}
+
+
+void p2p_buf_add_service_instance(struct wpabuf *buf, struct p2p_data *p2p,
+				  u8 hash_count, const u8 *hash,
+				  struct p2ps_advertisement *adv_list)
+{
+	struct p2ps_advertisement *adv;
+	int p2ps_wildcard;
+	size_t total_len;
+	struct wpabuf *tmp_buf = NULL;
+	u8 *pos, *attr_len, *ie_len = NULL;
+
+	if (!adv_list || !hash || !hash_count)
+		return;
+
+	wpa_hexdump(MSG_DEBUG, "P2PS: Probe Request service hash values",
+		    hash, hash_count * P2PS_HASH_LEN);
+	p2ps_wildcard = p2ps_wildcard_hash(p2p, hash, hash_count) &&
+		p2p_wfa_service_adv(p2p);
+
+	/* Allocate temp buffer, allowing for overflow of 1 instance */
+	tmp_buf = wpabuf_alloc(MAX_SVC_ADV_IE_LEN + 256 + P2PS_HASH_LEN);
+	if (!tmp_buf)
+		return;
+
+	/*
+	 * Attribute data can be split into a number of IEs. Start with the
+	 * first IE and the attribute headers here.
+	 */
+	ie_len = p2p_buf_add_ie_hdr(tmp_buf);
+
+	total_len = 0;
+
+	wpabuf_put_u8(tmp_buf, P2P_ATTR_ADVERTISED_SERVICE);
+	attr_len = wpabuf_put(tmp_buf, sizeof(u16));
+	WPA_PUT_LE16(attr_len, (u16) total_len);
+	p2p_buf_update_ie_hdr(tmp_buf, ie_len);
+	pos = wpabuf_put(tmp_buf, 0);
+
+	if (p2ps_wildcard) {
+		/* org.wi-fi.wfds match found */
+		p2p_buf_add_service_info(tmp_buf, p2p, 0, 0, P2PS_WILD_HASH_STR,
+					 &ie_len, &pos, &total_len, attr_len);
+	}
+
+	/* add advertised service info of matching services */
+	for (adv = adv_list; adv && total_len <= MAX_SVC_ADV_LEN;
+	     adv = adv->next) {
+		const u8 *test = hash;
+		u8 i;
+
+		for (i = 0; i < hash_count; i++) {
+			/* exact name hash match */
+			if (os_memcmp(test, adv->hash, P2PS_HASH_LEN) == 0 &&
+			    p2p_buf_add_service_info(tmp_buf, p2p,
+						     adv->id,
+						     adv->config_methods,
+						     adv->svc_name,
+						     &ie_len, &pos,
+						     &total_len,
+						     attr_len))
+				break;
+
+			test += P2PS_HASH_LEN;
+		}
+	}
+
+	if (total_len)
+		wpabuf_put_buf(buf, tmp_buf);
+	wpabuf_free(tmp_buf);
+}
+
+
+void p2p_buf_add_session_id(struct wpabuf *buf, u32 id, const u8 *mac)
+{
+	if (!buf || !mac)
+		return;
+
+	/* Session ID Info */
+	wpabuf_put_u8(buf, P2P_ATTR_SESSION_ID);
+	wpabuf_put_le16(buf, (u16) (sizeof(u32) + ETH_ALEN));
+	wpabuf_put_le32(buf, id);
+	wpabuf_put_data(buf, mac, ETH_ALEN);
+	wpa_printf(MSG_DEBUG, "P2P: * Session ID Info (%x) " MACSTR,
+		   id, MAC2STR(mac));
+}
+
+
+void p2p_buf_add_feature_capability(struct wpabuf *buf, u16 len, const u8 *mask)
+{
+	if (!buf || !len || !mask)
+		return;
+
+	/* Feature Capability */
+	wpabuf_put_u8(buf, P2P_ATTR_FEATURE_CAPABILITY);
+	wpabuf_put_le16(buf, len);
+	wpabuf_put_data(buf, mask, len);
+	wpa_printf(MSG_DEBUG, "P2P: * Feature Capability (%d)", len);
+}
+
+
+void p2p_buf_add_persistent_group_info(struct wpabuf *buf, const u8 *dev_addr,
+				       const u8 *ssid, size_t ssid_len)
+{
+	/* P2P Group ID */
+	wpabuf_put_u8(buf, P2P_ATTR_PERSISTENT_GROUP);
+	wpabuf_put_le16(buf, ETH_ALEN + ssid_len);
+	wpabuf_put_data(buf, dev_addr, ETH_ALEN);
+	wpabuf_put_data(buf, ssid, ssid_len);
+	wpa_printf(MSG_DEBUG, "P2P: * P2P Group ID " MACSTR,
+		   MAC2STR(dev_addr));
+}
+
+
 static int p2p_add_wps_string(struct wpabuf *buf, enum wps_attribute attr,
 			      const char *val)
 {
diff --git a/src/p2p/p2p_dev_disc.c b/src/p2p/p2p_dev_disc.c
index 86bae1a..98805fe 100644
--- a/src/p2p/p2p_dev_disc.c
+++ b/src/p2p/p2p_dev_disc.c
@@ -314,7 +314,7 @@
 
 	p2p_dbg(p2p, "Received GO Discoverability Request - remain awake for 100 TU");
 
-	ies = p2p_build_probe_resp_ies(p2p);
+	ies = p2p_build_probe_resp_ies(p2p, NULL, 0);
 	if (ies == NULL)
 		return;
 
diff --git a/src/p2p/p2p_go_neg.c b/src/p2p/p2p_go_neg.c
index 21fae3f..19f1daa 100644
--- a/src/p2p/p2p_go_neg.c
+++ b/src/p2p/p2p_go_neg.c
@@ -9,6 +9,7 @@
 #include "includes.h"
 
 #include "common.h"
+#include "utils/eloop.h"
 #include "common/ieee802_11_defs.h"
 #include "common/wpa_ctrl.h"
 #include "wps/wps_defs.h"
@@ -106,6 +107,8 @@
 		return DEV_PW_PUSHBUTTON;
 	case WPS_NFC:
 		return DEV_PW_NFC_CONNECTION_HANDOVER;
+	case WPS_P2PS:
+		return DEV_PW_P2PS_DEFAULT;
 	default:
 		return DEV_PW_DEFAULT;
 	}
@@ -123,6 +126,8 @@
 		return "PBC";
 	case WPS_NFC:
 		return "NFC";
+	case WPS_P2PS:
+		return "P2PS";
 	default:
 		return "??";
 	}
@@ -217,10 +222,12 @@
 			config_method = WPS_CONFIG_DISPLAY;
 		else if (dev->wps_method == WPS_PBC)
 			config_method = WPS_CONFIG_PUSHBUTTON;
+		else if (dev->wps_method == WPS_P2PS)
+			config_method = WPS_CONFIG_P2PS;
 		else
 			return -1;
 		return p2p_prov_disc_req(p2p, dev->info.p2p_device_addr,
-					 config_method, 0, 0, 1);
+					 NULL, config_method, 0, 0, 1);
 	}
 
 	freq = dev->listen_freq > 0 ? dev->listen_freq : dev->oper_freq;
@@ -240,6 +247,7 @@
 	p2p_set_state(p2p, P2P_CONNECT);
 	p2p->pending_action_state = P2P_PENDING_GO_NEG_REQUEST;
 	p2p->go_neg_peer = dev;
+	eloop_cancel_timeout(p2p_go_neg_wait_timeout, p2p, NULL);
 	dev->flags |= P2P_DEV_WAIT_GO_NEG_RESPONSE;
 	dev->connect_reqs++;
 	if (p2p_send_action(p2p, freq, dev->info.p2p_device_addr,
@@ -371,7 +379,7 @@
 	int freq;
 	u8 op_reg_class, op_channel;
 	unsigned int i;
-	const int op_classes_5ghz[] = { 124, 115, 0 };
+	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 };
 
@@ -486,8 +494,8 @@
 }
 
 
-static int p2p_go_select_channel(struct p2p_data *p2p, struct p2p_device *dev,
-				 u8 *status)
+int p2p_go_select_channel(struct p2p_data *p2p, struct p2p_device *dev,
+			  u8 *status)
 {
 	struct p2p_channels tmp, intersection;
 
@@ -621,7 +629,7 @@
 			 * Request frame.
 			 */
 			p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
-			p2p_go_neg_failed(p2p, dev, *msg.status);
+			p2p_go_neg_failed(p2p, *msg.status);
 			p2p_parse_free(&msg);
 			return;
 		}
@@ -645,6 +653,9 @@
 		p2p_add_dev_info(p2p, sa, dev, &msg);
 	}
 
+	if (p2p->go_neg_peer && p2p->go_neg_peer == dev)
+		eloop_cancel_timeout(p2p_go_neg_wait_timeout, p2p, NULL);
+
 	if (dev && dev->flags & P2P_DEV_USER_REJECTED) {
 		p2p_dbg(p2p, "User has rejected this peer");
 		status = P2P_SC_FAIL_REJECTED_BY_USER;
@@ -657,7 +668,9 @@
 			MAC2STR(sa));
 		status = P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE;
 		p2p->cfg->go_neg_req_rx(p2p->cfg->cb_ctx, sa,
-					msg.dev_password_id);
+					msg.dev_password_id,
+					msg.go_intent ? (*msg.go_intent >> 1) :
+					0);
 	} else if (p2p->go_neg_peer && p2p->go_neg_peer != dev) {
 		p2p_dbg(p2p, "Already in Group Formation with another peer");
 		status = P2P_SC_FAIL_UNABLE_TO_ACCOMMODATE;
@@ -738,6 +751,16 @@
 				goto fail;
 			}
 			break;
+		case DEV_PW_P2PS_DEFAULT:
+			p2p_dbg(p2p, "Peer using P2PS pin");
+			if (dev->wps_method != WPS_P2PS) {
+				p2p_dbg(p2p,
+					"We have wps_method=%s -> incompatible",
+					p2p_wps_method_str(dev->wps_method));
+				status = P2P_SC_FAIL_INCOMPATIBLE_PROV_METHOD;
+				goto fail;
+			}
+			break;
 		default:
 			if (msg.dev_password_id &&
 			    msg.dev_password_id == dev->oob_pw_id) {
@@ -789,6 +812,7 @@
 		dev->dialog_token = msg.dialog_token;
 		os_memcpy(dev->intended_addr, msg.intended_addr, ETH_ALEN);
 		p2p->go_neg_peer = dev;
+		eloop_cancel_timeout(p2p_go_neg_wait_timeout, p2p, NULL);
 		status = P2P_SC_SUCCESS;
 	}
 
@@ -957,7 +981,10 @@
 		if (*msg.status == P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE) {
 			p2p_dbg(p2p, "Wait for the peer to become ready for GO Negotiation");
 			dev->flags |= P2P_DEV_NOT_YET_READY;
-			os_get_reltime(&dev->go_neg_wait_started);
+			eloop_cancel_timeout(p2p_go_neg_wait_timeout, p2p,
+					     NULL);
+			eloop_register_timeout(120, 0, p2p_go_neg_wait_timeout,
+					       p2p, NULL);
 			if (p2p->state == P2P_CONNECT_LISTEN)
 				p2p_set_state(p2p, P2P_WAIT_PEER_CONNECT);
 			else
@@ -965,7 +992,7 @@
 			p2p_set_timeout(p2p, 0, 0);
 		} else {
 			p2p_dbg(p2p, "Stop GO Negotiation attempt");
-			p2p_go_neg_failed(p2p, dev, *msg.status);
+			p2p_go_neg_failed(p2p, *msg.status);
 		}
 		p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
 		p2p_parse_free(&msg);
@@ -1093,6 +1120,15 @@
 			goto fail;
 		}
 		break;
+	case DEV_PW_P2PS_DEFAULT:
+		p2p_dbg(p2p, "P2P: Peer using P2PS default pin");
+		if (dev->wps_method != WPS_P2PS) {
+			p2p_dbg(p2p, "We have wps_method=%s -> incompatible",
+				p2p_wps_method_str(dev->wps_method));
+			status = P2P_SC_FAIL_INCOMPATIBLE_PROV_METHOD;
+			goto fail;
+		}
+		break;
 	default:
 		if (msg.dev_password_id &&
 		    msg.dev_password_id == dev->oob_pw_id) {
@@ -1147,13 +1183,13 @@
 			    wpabuf_head(dev->go_neg_conf),
 			    wpabuf_len(dev->go_neg_conf), 200) < 0) {
 		p2p_dbg(p2p, "Failed to send Action frame");
-		p2p_go_neg_failed(p2p, dev, -1);
+		p2p_go_neg_failed(p2p, -1);
 		p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
 	} else
 		dev->go_neg_conf_sent++;
 	if (status != P2P_SC_SUCCESS) {
 		p2p_dbg(p2p, "GO Negotiation failed");
-		p2p_go_neg_failed(p2p, dev, status);
+		p2p_go_neg_failed(p2p, status);
 	}
 }
 
@@ -1204,7 +1240,7 @@
 	}
 	if (*msg.status) {
 		p2p_dbg(p2p, "GO Negotiation rejected: status %d", *msg.status);
-		p2p_go_neg_failed(p2p, dev, *msg.status);
+		p2p_go_neg_failed(p2p, *msg.status);
 		p2p_parse_free(&msg);
 		return;
 	}
@@ -1216,7 +1252,7 @@
 	} else if (dev->go_state == REMOTE_GO) {
 		p2p_dbg(p2p, "Mandatory P2P Group ID attribute missing from GO Negotiation Confirmation");
 		p2p->ssid_len = 0;
-		p2p_go_neg_failed(p2p, dev, P2P_SC_FAIL_INVALID_PARAMS);
+		p2p_go_neg_failed(p2p, P2P_SC_FAIL_INVALID_PARAMS);
 		p2p_parse_free(&msg);
 		return;
 	}
diff --git a/src/p2p/p2p_group.c b/src/p2p/p2p_group.c
index da8588a..41ca99f 100644
--- a/src/p2p/p2p_group.c
+++ b/src/p2p/p2p_group.c
@@ -981,10 +981,22 @@
 
 unsigned int p2p_get_group_num_members(struct p2p_group *group)
 {
+	if (!group)
+		return 0;
+
 	return group->num_members;
 }
 
 
+int p2p_client_limit_reached(struct p2p_group *group)
+{
+	if (!group || !group->cfg)
+		return 1;
+
+	return group->num_members >= group->cfg->max_clients;
+}
+
+
 const u8 * p2p_iterate_group_members(struct p2p_group *group, void **next)
 {
 	struct p2p_group_member *iter = *next;
diff --git a/src/p2p/p2p_i.h b/src/p2p/p2p_i.h
index 3b60582..a1042d2 100644
--- a/src/p2p/p2p_i.h
+++ b/src/p2p/p2p_i.h
@@ -14,8 +14,21 @@
 
 #define P2P_GO_NEG_CNF_MAX_RETRY_COUNT 1
 
+/*
+ * A threshold (in seconds) to prefer a direct Probe Response frame from a P2P
+ * Device over the P2P Client Info received from a GO.
+ */
+#define P2P_DEV_GROUP_CLIENT_RESP_THRESHOLD 1
+
 enum p2p_role_indication;
 
+/*
+ * To force Service Instances to fit within a single P2P Tag, MAX_SVC_ADV_LEN
+ * must equal 248 or less. Must have a minimum size of 19.
+ */
+#define MAX_SVC_ADV_LEN	600
+#define MAX_SVC_ADV_IE_LEN (9 + MAX_SVC_ADV_LEN + (5 * (MAX_SVC_ADV_LEN / 240)))
+
 enum p2p_go_state {
 	UNKNOWN_GO,
 	LOCAL_GO,
@@ -64,7 +77,7 @@
 	char country[3];
 	struct p2p_channels channels;
 	int oper_freq;
-	u8 oper_ssid[32];
+	u8 oper_ssid[SSID_MAX_LEN];
 	size_t oper_ssid_len;
 
 	/**
@@ -98,13 +111,17 @@
 #define P2P_DEV_PD_BEFORE_GO_NEG BIT(17)
 #define P2P_DEV_NO_PREF_CHAN BIT(18)
 #define P2P_DEV_WAIT_INV_REQ_ACK BIT(19)
+#define P2P_DEV_P2PS_REPORTED BIT(20)
+#define P2P_DEV_PD_PEER_P2PS BIT(21)
+#define P2P_DEV_LAST_SEEN_AS_GROUP_CLIENT BIT(22)
+
 	unsigned int flags;
 
 	int status; /* enum p2p_status_code */
-	struct os_reltime go_neg_wait_started;
 	unsigned int wait_count;
 	unsigned int connect_reqs;
 	unsigned int invitation_reqs;
+	unsigned int sd_reqs;
 
 	u16 ext_listen_period;
 	u16 ext_listen_interval;
@@ -260,10 +277,18 @@
 	 */
 	struct p2p_device *invite_peer;
 
+	/**
+	 * last_p2p_find_oper - Pointer to last pre-find operation peer
+	 */
+	struct p2p_device *last_p2p_find_oper;
+
 	const u8 *invite_go_dev_addr;
 	u8 invite_go_dev_addr_buf[ETH_ALEN];
 	int invite_dev_pw_id;
 
+	unsigned int retry_invite_req:1;
+	unsigned int retry_invite_req_sent:1;
+
 	/**
 	 * sd_peer - Pointer to Service Discovery peer
 	 */
@@ -305,7 +330,7 @@
 	/**
 	 * ssid - Selected SSID for GO Negotiation (if local end will be GO)
 	 */
-	u8 ssid[32];
+	u8 ssid[SSID_MAX_LEN];
 
 	/**
 	 * ssid_len - ssid length in octets
@@ -346,6 +371,7 @@
 		P2P_PENDING_GO_NEG_CONFIRM,
 		P2P_PENDING_SD,
 		P2P_PENDING_PD,
+		P2P_PENDING_PD_RESPONSE,
 		P2P_PENDING_INVITATION_REQUEST,
 		P2P_PENDING_INVITATION_RESPONSE,
 		P2P_PENDING_DEV_DISC_REQUEST,
@@ -385,7 +411,7 @@
 	enum p2p_invite_role inv_role;
 	u8 inv_bssid[ETH_ALEN];
 	int inv_bssid_set;
-	u8 inv_ssid[32];
+	u8 inv_ssid[SSID_MAX_LEN];
 	size_t inv_ssid_len;
 	u8 inv_sa[ETH_ALEN];
 	u8 inv_group_bssid[ETH_ALEN];
@@ -484,6 +510,14 @@
 	u8 pending_channel;
 	u8 pending_channel_forced;
 
+	/* ASP Support */
+	struct p2ps_advertisement *p2ps_adv_list;
+	struct p2ps_provision *p2ps_prov;
+	u8 wild_card_hash[P2PS_HASH_LEN];
+	u8 p2ps_seek;
+	u8 p2ps_seek_hash[P2P_MAX_QUERY_HASH * P2PS_HASH_LEN];
+	u8 p2ps_seek_count;
+
 #ifdef CONFIG_WIFI_DISPLAY
 	struct wpabuf *wfd_ie_beacon;
 	struct wpabuf *wfd_ie_probe_req;
@@ -550,7 +584,7 @@
 	const u8 *p2p_device_addr;
 	const u8 *pri_dev_type;
 	u8 num_sec_dev_types;
-	char device_name[33];
+	char device_name[WPS_DEV_NAME_MAX_LEN + 1];
 	u16 config_methods;
 
 	/* WPS IE */
@@ -578,6 +612,31 @@
 
 	/* SSID IE */
 	const u8 *ssid;
+
+	/* P2PS */
+	u8 service_hash_count;
+	const u8 *service_hash;
+
+	const u8 *session_info;
+	size_t session_info_len;
+
+	const u8 *conn_cap;
+
+	const u8 *adv_id;
+	const u8 *adv_mac;
+
+	const u8 *adv_service_instance;
+	size_t adv_service_instance_len;
+
+	const u8 *session_id;
+	const u8 *session_mac;
+
+	const u8 *feature_cap;
+	size_t feature_cap_len;
+
+	const u8 *persistent_dev;
+	const u8 *persistent_ssid;
+	size_t persistent_ssid_len;
 };
 
 
@@ -606,6 +665,8 @@
 void p2p_channels_intersect(const struct p2p_channels *a,
 			    const struct p2p_channels *b,
 			    struct p2p_channels *res);
+void p2p_channels_union_inplace(struct p2p_channels *res,
+				const struct p2p_channels *b);
 void p2p_channels_union(const struct p2p_channels *a,
 			const struct p2p_channels *b,
 			struct p2p_channels *res);
@@ -688,6 +749,18 @@
 void p2p_buf_add_oob_go_neg_channel(struct wpabuf *buf, const char *country,
 				    u8 oper_class, u8 channel,
 				    enum p2p_role_indication role);
+void p2p_buf_add_service_hash(struct wpabuf *buf, struct p2p_data *p2p);
+void p2p_buf_add_session_info(struct wpabuf *buf, const char *info);
+void p2p_buf_add_connection_capability(struct wpabuf *buf, u8 connection_cap);
+void p2p_buf_add_advertisement_id(struct wpabuf *buf, u32 id, const u8 *mac);
+void p2p_buf_add_service_instance(struct wpabuf *buf, struct p2p_data *p2p,
+				  u8 count, const u8 *hash,
+				  struct p2ps_advertisement *adv_list);
+void p2p_buf_add_session_id(struct wpabuf *buf, u32 id, const u8 *mac);
+void p2p_buf_add_feature_capability(struct wpabuf *buf, u16 len,
+				    const u8 *mask);
+void p2p_buf_add_persistent_group_info(struct wpabuf *buf, const u8 *dev_addr,
+				       const u8 *ssid, size_t ssid_len);
 int p2p_build_wps_ie(struct p2p_data *p2p, struct wpabuf *buf, int pw_id,
 		     int all_attr);
 
@@ -728,6 +801,7 @@
 int p2p_send_prov_disc_req(struct p2p_data *p2p, struct p2p_device *dev,
 			   int join, int force_freq);
 void p2p_reset_pending_pd(struct p2p_data *p2p);
+void p2ps_prov_free(struct p2p_data *p2p);
 
 /* p2p_invitation.c */
 void p2p_process_invitation_req(struct p2p_data *p2p, const u8 *sa,
@@ -768,13 +842,14 @@
 struct p2p_device * p2p_get_device(struct p2p_data *p2p, const u8 *addr);
 struct p2p_device * p2p_get_device_interface(struct p2p_data *p2p,
 					     const u8 *addr);
-void p2p_go_neg_failed(struct p2p_data *p2p, struct p2p_device *peer,
-		       int status);
+void p2p_go_neg_failed(struct p2p_data *p2p, int status);
 void p2p_go_complete(struct p2p_data *p2p, struct p2p_device *peer);
 int p2p_match_dev_type(struct p2p_data *p2p, struct wpabuf *wps);
 int dev_type_list_match(const u8 *dev_type, const u8 *req_dev_type[],
 			size_t num_req_dev_type);
-struct wpabuf * p2p_build_probe_resp_ies(struct p2p_data *p2p);
+struct wpabuf * p2p_build_probe_resp_ies(struct p2p_data *p2p,
+					 const u8 *query_hash,
+					 u8 query_count);
 void p2p_build_ssid(struct p2p_data *p2p, u8 *ssid, size_t *ssid_len);
 int p2p_send_action(struct p2p_data *p2p, unsigned int freq, const u8 *dst,
 		    const u8 *src, const u8 *bssid, const u8 *buf,
@@ -783,6 +858,9 @@
 int p2p_prepare_channel(struct p2p_data *p2p, struct p2p_device *dev,
 			unsigned int force_freq, unsigned int pref_freq,
 			int go);
+void p2p_go_neg_wait_timeout(void *eloop_ctx, void *timeout_ctx);
+int p2p_go_select_channel(struct p2p_data *p2p, struct p2p_device *dev,
+			  u8 *status);
 void p2p_dbg(struct p2p_data *p2p, const char *fmt, ...)
 PRINTF_FORMAT(2, 3);
 void p2p_info(struct p2p_data *p2p, const char *fmt, ...)
diff --git a/src/p2p/p2p_invitation.c b/src/p2p/p2p_invitation.c
index ef01a66..f5454f7 100644
--- a/src/p2p/p2p_invitation.c
+++ b/src/p2p/p2p_invitation.c
@@ -134,6 +134,9 @@
 		extra = wpabuf_len(wfd_ie);
 #endif /* CONFIG_WIFI_DISPLAY */
 
+	if (p2p->vendor_elem && p2p->vendor_elem[VENDOR_ELEM_P2P_INV_RESP])
+		extra += wpabuf_len(p2p->vendor_elem[VENDOR_ELEM_P2P_INV_RESP]);
+
 	buf = wpabuf_alloc(1000 + extra);
 	if (buf == NULL)
 		return NULL;
@@ -158,6 +161,9 @@
 		wpabuf_put_buf(buf, wfd_ie);
 #endif /* CONFIG_WIFI_DISPLAY */
 
+	if (p2p->vendor_elem && p2p->vendor_elem[VENDOR_ELEM_P2P_INV_RESP])
+		wpabuf_put_buf(buf, p2p->vendor_elem[VENDOR_ELEM_P2P_INV_RESP]);
+
 	return buf;
 }
 
@@ -174,7 +180,7 @@
 	u8 group_bssid[ETH_ALEN], *bssid;
 	int op_freq = 0;
 	u8 reg_class = 0, channel = 0;
-	struct p2p_channels intersection, *channels = NULL;
+	struct p2p_channels all_channels, intersection, *channels = NULL;
 	int persistent;
 
 	os_memset(group_bssid, 0, sizeof(group_bssid));
@@ -226,7 +232,10 @@
 		persistent = 1;
 	}
 
-	if (p2p_peer_channels_check(p2p, &p2p->cfg->channels, dev,
+	p2p_channels_union(&p2p->cfg->channels, &p2p->cfg->cli_channels,
+			   &all_channels);
+
+	if (p2p_peer_channels_check(p2p, &all_channels, dev,
 				    msg.channel_list, msg.channel_list_len) <
 	    0) {
 		p2p_dbg(p2p, "No common channels found");
@@ -235,8 +244,9 @@
 	}
 
 	p2p_channels_dump(p2p, "own channels", &p2p->cfg->channels);
+	p2p_channels_dump(p2p, "own client channels", &all_channels);
 	p2p_channels_dump(p2p, "peer channels", &dev->channels);
-	p2p_channels_intersect(&p2p->cfg->channels, &dev->channels,
+	p2p_channels_intersect(&all_channels, &dev->channels,
 			       &intersection);
 	p2p_channels_dump(p2p, "intersection", &intersection);
 
@@ -248,6 +258,17 @@
 			msg.dev_password_id_present ? msg.dev_password_id : -1);
 	}
 
+	if (go) {
+		p2p_channels_intersect(&p2p->cfg->channels, &dev->channels,
+				       &intersection);
+		p2p_channels_dump(p2p, "intersection(GO)", &intersection);
+		if (intersection.reg_classes == 0) {
+			p2p_dbg(p2p, "No common channels found (GO)");
+			status = P2P_SC_FAIL_NO_COMMON_CHANNELS;
+			goto fail;
+		}
+	}
+
 	if (op_freq) {
 		p2p_dbg(p2p, "Invitation processing forced frequency %d MHz",
 			op_freq);
@@ -372,7 +393,7 @@
 	} else
 		p2p->inv_group_bssid_ptr = NULL;
 	if (msg.group_id) {
-		if (msg.group_id_len - ETH_ALEN <= 32) {
+		if (msg.group_id_len - ETH_ALEN <= SSID_MAX_LEN) {
 			os_memcpy(p2p->inv_ssid, msg.group_id + ETH_ALEN,
 				  msg.group_id_len - ETH_ALEN);
 			p2p->inv_ssid_len = msg.group_id_len - ETH_ALEN;
@@ -412,25 +433,68 @@
 	if (dev == NULL) {
 		p2p_dbg(p2p, "Ignore Invitation Response from unknown peer "
 			MACSTR, MAC2STR(sa));
+		p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
 		return;
 	}
 
 	if (dev != p2p->invite_peer) {
 		p2p_dbg(p2p, "Ignore unexpected Invitation Response from peer "
 			MACSTR, MAC2STR(sa));
+		p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
 		return;
 	}
 
-	if (p2p_parse(data, len, &msg))
+	if (p2p_parse(data, len, &msg)) {
+		p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
 		return;
+	}
 
 	if (!msg.status) {
 		p2p_dbg(p2p, "Mandatory Status attribute missing in Invitation Response from "
 			MACSTR, MAC2STR(sa));
 		p2p_parse_free(&msg);
+		p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
 		return;
 	}
 
+	/*
+	 * We should not really receive a replayed response twice since
+	 * duplicate frames are supposed to be dropped. However, not all drivers
+	 * do that for pre-association frames. We did not use to verify dialog
+	 * token matches for invitation response frames, but that check can be
+	 * safely used to drop a replayed response to the previous Invitation
+	 * Request in case the suggested operating channel was changed. This
+	 * allows a duplicated reject frame to be dropped with the assumption
+	 * that the real response follows after it.
+	 */
+	if (*msg.status == P2P_SC_FAIL_NO_COMMON_CHANNELS &&
+	    p2p->retry_invite_req_sent &&
+	    msg.dialog_token != dev->dialog_token) {
+		p2p_dbg(p2p, "Unexpected Dialog Token %u (expected %u)",
+			msg.dialog_token, dev->dialog_token);
+		p2p_parse_free(&msg);
+		return;
+	}
+
+	if (*msg.status == P2P_SC_FAIL_NO_COMMON_CHANNELS &&
+	    p2p->retry_invite_req &&
+	    p2p_channel_random_social(&p2p->cfg->channels, &p2p->op_reg_class,
+				      &p2p->op_channel) == 0) {
+		p2p->retry_invite_req = 0;
+		p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
+		p2p->cfg->stop_listen(p2p->cfg->cb_ctx);
+		p2p_set_state(p2p, P2P_INVITE);
+		p2p_dbg(p2p, "Resend Invitation Request setting op_class %u channel %u as operating channel",
+			p2p->op_reg_class, p2p->op_channel);
+		p2p->retry_invite_req_sent = 1;
+		p2p_invite_send(p2p, p2p->invite_peer, p2p->invite_go_dev_addr,
+				p2p->invite_dev_pw_id);
+		p2p_parse_free(&msg);
+		return;
+	}
+	p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
+	p2p->retry_invite_req = 0;
+
 	if (!msg.channel_list && *msg.status == P2P_SC_SUCCESS) {
 		p2p_dbg(p2p, "Mandatory Channel List attribute missing in Invitation Response from "
 			MACSTR, MAC2STR(sa));
@@ -592,6 +656,9 @@
 			dev_pw_id);
 	}
 	p2p->invite_dev_pw_id = dev_pw_id;
+	p2p->retry_invite_req = role == P2P_INVITE_ROLE_GO &&
+		persistent_group && !force_freq;
+	p2p->retry_invite_req_sent = 0;
 
 	dev = p2p_get_device(p2p, peer);
 	if (dev == NULL || (dev->listen_freq <= 0 && dev->oper_freq <= 0 &&
diff --git a/src/p2p/p2p_parse.c b/src/p2p/p2p_parse.c
index d6144a0..980dddf 100644
--- a/src/p2p/p2p_parse.c
+++ b/src/p2p/p2p_parse.c
@@ -149,7 +149,8 @@
 		pos += 2;
 		nlen = WPA_GET_BE16(pos);
 		pos += 2;
-		if (data + len - pos < (int) nlen || nlen > 32) {
+		if (data + len - pos < (int) nlen ||
+		    nlen > WPS_DEV_NAME_MAX_LEN) {
 			wpa_printf(MSG_DEBUG, "P2P: Invalid Device Name "
 				   "length %d (buf len %d)", (int) nlen,
 				   (int) (data + len - pos));
@@ -160,8 +161,7 @@
 		for (i = 0; i < nlen; i++) {
 			if (msg->device_name[i] == '\0')
 				break;
-			if (msg->device_name[i] > 0 &&
-			    msg->device_name[i] < 32)
+			if (is_ctrl_char(msg->device_name[i]))
 				msg->device_name[i] = '_';
 		}
 		wpa_printf(MSG_DEBUG, "P2P: * Device Info: addr " MACSTR
@@ -203,7 +203,7 @@
 			   MAC2STR(msg->group_bssid));
 		break;
 	case P2P_ATTR_GROUP_ID:
-		if (len < ETH_ALEN || len > ETH_ALEN + 32) {
+		if (len < ETH_ALEN || len > ETH_ALEN + SSID_MAX_LEN) {
 			wpa_printf(MSG_DEBUG, "P2P: Invalid P2P Group ID "
 				   "attribute length %d", len);
 			return -1;
@@ -281,6 +281,112 @@
 			   data[0], data[1], data[2], data[3], data[4],
 			   data[5]);
 		break;
+	case P2P_ATTR_SERVICE_HASH:
+		if (len < P2PS_HASH_LEN) {
+			wpa_printf(MSG_DEBUG,
+				   "P2P: Too short Service Hash (length %u)",
+				   len);
+			return -1;
+		}
+		msg->service_hash_count = len / P2PS_HASH_LEN;
+		msg->service_hash = data;
+		wpa_hexdump(MSG_DEBUG, "P2P: * Service Hash(s)", data, len);
+		break;
+	case P2P_ATTR_SESSION_INFORMATION_DATA:
+		msg->session_info = data;
+		msg->session_info_len = len;
+		wpa_printf(MSG_DEBUG, "P2P: * Service Instance: %u bytes - %p",
+			   len, data);
+		break;
+	case P2P_ATTR_CONNECTION_CAPABILITY:
+		if (len < 1) {
+			wpa_printf(MSG_DEBUG,
+				   "P2P: Too short Connection Capability (length %u)",
+				   len);
+			return -1;
+		}
+		msg->conn_cap = data;
+		wpa_printf(MSG_DEBUG, "P2P: * Connection Capability: 0x%x",
+			   *msg->conn_cap);
+		break;
+	case P2P_ATTR_ADVERTISEMENT_ID:
+		if (len < 10) {
+			wpa_printf(MSG_DEBUG,
+				   "P2P: Too short Advertisement ID (length %u)",
+				   len);
+			return -1;
+		}
+		msg->adv_id = data;
+		msg->adv_mac = &data[sizeof(u32)];
+		wpa_printf(MSG_DEBUG, "P2P: * Advertisement ID %x",
+			   WPA_GET_LE32(data));
+		break;
+	case P2P_ATTR_ADVERTISED_SERVICE:
+		if (len < 8) {
+			wpa_printf(MSG_DEBUG,
+				   "P2P: Too short Service Instance (length %u)",
+				   len);
+			return -1;
+		}
+		msg->adv_service_instance = data;
+		msg->adv_service_instance_len = len;
+		if (len <= 255 + 8) {
+			char str[256];
+			u8 namelen;
+
+			namelen = data[6];
+			if (namelen > len - 7)
+				break;
+			os_memcpy(str, &data[7], namelen);
+			str[namelen] = '\0';
+			wpa_printf(MSG_DEBUG, "P2P: * Service Instance: %x-%s",
+				   WPA_GET_LE32(data), str);
+		} else {
+			wpa_printf(MSG_DEBUG, "P2P: * Service Instance: %p",
+				   data);
+		}
+		break;
+	case P2P_ATTR_SESSION_ID:
+		if (len < sizeof(u32) + ETH_ALEN) {
+			wpa_printf(MSG_DEBUG,
+				   "P2P: Too short Session ID Info (length %u)",
+				   len);
+			return -1;
+		}
+		msg->session_id = data;
+		msg->session_mac = &data[sizeof(u32)];
+		wpa_printf(MSG_DEBUG, "P2P: * Session ID: %x " MACSTR,
+			   WPA_GET_LE32(data), MAC2STR(msg->session_mac));
+		break;
+	case P2P_ATTR_FEATURE_CAPABILITY:
+		if (!len) {
+			wpa_printf(MSG_DEBUG,
+				   "P2P: Too short Feature Capability (length %u)",
+				   len);
+			return -1;
+		}
+		msg->feature_cap = data;
+		msg->feature_cap_len = len;
+		wpa_printf(MSG_DEBUG, "P2P: * Feature Cap (length=%u)", len);
+		break;
+	case P2P_ATTR_PERSISTENT_GROUP:
+	{
+		if (len < ETH_ALEN || len > ETH_ALEN + SSID_MAX_LEN) {
+			wpa_printf(MSG_DEBUG,
+				   "P2P: Invalid Persistent Group Info (length %u)",
+				   len);
+			return -1;
+		}
+
+		msg->persistent_dev = data;
+		msg->persistent_ssid_len = len - ETH_ALEN;
+		msg->persistent_ssid = &data[ETH_ALEN];
+		wpa_printf(MSG_DEBUG, "P2P: * Persistent Group: " MACSTR " %s",
+			   MAC2STR(msg->persistent_dev),
+			   wpa_ssid_txt(msg->persistent_ssid,
+					msg->persistent_ssid_len));
+		break;
+	}
 	default:
 		wpa_printf(MSG_DEBUG, "P2P: Skipped unknown attribute %d "
 			   "(length %d)", id, len);
@@ -309,23 +415,27 @@
 
 	while (pos < end) {
 		u16 attr_len;
-		if (pos + 2 >= end) {
+		u8 id;
+
+		if (end - pos < 3) {
 			wpa_printf(MSG_DEBUG, "P2P: Invalid P2P attribute");
 			return -1;
 		}
-		attr_len = WPA_GET_LE16(pos + 1);
+		id = *pos++;
+		attr_len = WPA_GET_LE16(pos);
+		pos += 2;
 		wpa_printf(MSG_DEBUG, "P2P: Attribute %d length %u",
-			   pos[0], attr_len);
-		if (pos + 3 + attr_len > end) {
+			   id, attr_len);
+		if (attr_len > end - pos) {
 			wpa_printf(MSG_DEBUG, "P2P: Attribute underflow "
 				   "(len=%u left=%d)",
-				   attr_len, (int) (end - pos - 3));
+				   attr_len, (int) (end - pos));
 			wpa_hexdump(MSG_MSGDUMP, "P2P: Data", pos, end - pos);
 			return -1;
 		}
-		if (p2p_parse_attribute(pos[0], pos + 3, attr_len, msg))
+		if (p2p_parse_attribute(id, pos, attr_len, msg))
 			return -1;
-		pos += 3 + attr_len;
+		pos += attr_len;
 	}
 
 	return 0;
@@ -406,7 +516,7 @@
 	struct ieee802_11_elems elems;
 
 	ieee802_11_parse_elems(data, len, &elems, 0);
-	if (elems.ds_params && elems.ds_params_len >= 1)
+	if (elems.ds_params)
 		msg->ds_params = elems.ds_params;
 	if (elems.ssid)
 		msg->ssid = elems.ssid - 2;
@@ -564,8 +674,8 @@
 		t += 2;
 		if (count > cend - t)
 			return -1; /* invalid Device Name TLV */
-		if (count >= 32)
-			count = 32;
+		if (count >= WPS_DEV_NAME_MAX_LEN)
+			count = WPS_DEV_NAME_MAX_LEN;
 		cli->dev_name = (const char *) t;
 		cli->dev_name_len = count;
 
@@ -593,7 +703,7 @@
 
 	for (i = 0; i < info.num_clients; i++) {
 		struct p2p_client_info *cli;
-		char name[33];
+		char name[WPS_DEV_NAME_MAX_LEN + 1];
 		char devtype[WPS_DEV_TYPE_BUFSIZE];
 		u8 s;
 		int count;
@@ -603,7 +713,7 @@
 				  "dev=" MACSTR " iface=" MACSTR,
 				  MAC2STR(cli->p2p_device_addr),
 				  MAC2STR(cli->p2p_interface_addr));
-		if (ret < 0 || ret >= end - pos)
+		if (os_snprintf_error(end - pos, ret))
 			return pos - buf;
 		pos += ret;
 
@@ -614,7 +724,7 @@
 				  wps_dev_type_bin2str(cli->pri_dev_type,
 						       devtype,
 						       sizeof(devtype)));
-		if (ret < 0 || ret >= end - pos)
+		if (os_snprintf_error(end - pos, ret))
 			return pos - buf;
 		pos += ret;
 
@@ -623,7 +733,7 @@
 					  wps_dev_type_bin2str(
 						  &cli->sec_dev_types[s * 8],
 						  devtype, sizeof(devtype)));
-			if (ret < 0 || ret >= end - pos)
+			if (os_snprintf_error(end - pos, ret))
 				return pos - buf;
 			pos += ret;
 		}
@@ -632,13 +742,13 @@
 		name[cli->dev_name_len] = '\0';
 		count = (int) cli->dev_name_len - 1;
 		while (count >= 0) {
-			if (name[count] > 0 && name[count] < 32)
+			if (is_ctrl_char(name[count]))
 				name[count] = '_';
 			count--;
 		}
 
 		ret = os_snprintf(pos, end - pos, " dev_name='%s'\n", name);
-		if (ret < 0 || ret >= end - pos)
+		if (os_snprintf_error(end - pos, ret))
 			return pos - buf;
 		pos += ret;
 	}
@@ -672,7 +782,7 @@
 				  "p2p_dev_capab=0x%x\n"
 				  "p2p_group_capab=0x%x\n",
 				  msg.capability[0], msg.capability[1]);
-		if (ret < 0 || ret >= end - pos)
+		if (os_snprintf_error(end - pos, ret))
 			return pos - buf;
 		pos += ret;
 	}
@@ -684,14 +794,14 @@
 				  wps_dev_type_bin2str(msg.pri_dev_type,
 						       devtype,
 						       sizeof(devtype)));
-		if (ret < 0 || ret >= end - pos)
+		if (os_snprintf_error(end - pos, ret))
 			return pos - buf;
 		pos += ret;
 	}
 
 	ret = os_snprintf(pos, end - pos, "p2p_device_name=%s\n",
 			  msg.device_name);
-	if (ret < 0 || ret >= end - pos)
+	if (os_snprintf_error(end - pos, ret))
 		return pos - buf;
 	pos += ret;
 
@@ -699,14 +809,14 @@
 		ret = os_snprintf(pos, end - pos, "p2p_device_addr=" MACSTR
 				  "\n",
 				  MAC2STR(msg.p2p_device_addr));
-		if (ret < 0 || ret >= end - pos)
+		if (os_snprintf_error(end - pos, ret))
 			return pos - buf;
 		pos += ret;
 	}
 
 	ret = os_snprintf(pos, end - pos, "p2p_config_methods=0x%x\n",
 			  msg.config_methods);
-	if (ret < 0 || ret >= end - pos)
+	if (os_snprintf_error(end - pos, ret))
 		return pos - buf;
 	pos += ret;
 
diff --git a/src/p2p/p2p_pd.c b/src/p2p/p2p_pd.c
index e101367..86558f7 100644
--- a/src/p2p/p2p_pd.c
+++ b/src/p2p/p2p_pd.c
@@ -40,14 +40,132 @@
 }
 
 
+static void p2ps_add_new_group_info(struct p2p_data *p2p, struct wpabuf *buf)
+{
+	int found;
+	u8 intended_addr[ETH_ALEN];
+	u8 ssid[SSID_MAX_LEN];
+	size_t ssid_len;
+	int group_iface;
+
+	if (!p2p->cfg->get_go_info)
+		return;
+
+	found = p2p->cfg->get_go_info(
+		p2p->cfg->cb_ctx, intended_addr, ssid,
+		&ssid_len, &group_iface);
+	if (found) {
+		p2p_buf_add_group_id(buf, p2p->cfg->dev_addr,
+				     ssid, ssid_len);
+		p2p_buf_add_intended_addr(buf, intended_addr);
+	} else {
+		if (!p2p->ssid_set) {
+			p2p_build_ssid(p2p, p2p->ssid, &p2p->ssid_len);
+			p2p->ssid_set = 1;
+		}
+
+		/* Add pre-composed P2P Group ID */
+		p2p_buf_add_group_id(buf, p2p->cfg->dev_addr,
+				     p2p->ssid, p2p->ssid_len);
+
+		if (group_iface)
+			p2p_buf_add_intended_addr(
+				buf, p2p->intended_addr);
+		else
+			p2p_buf_add_intended_addr(
+				buf, p2p->cfg->dev_addr);
+	}
+}
+
+
+static void p2ps_add_pd_req_attrs(struct p2p_data *p2p, struct p2p_device *dev,
+				  struct wpabuf *buf, u16 config_methods)
+{
+	struct p2ps_provision *prov = p2p->p2ps_prov;
+	u8 feat_cap_mask[] = { 1, 0 };
+	int shared_group = 0;
+	u8 ssid[SSID_MAX_LEN];
+	size_t ssid_len;
+	u8 go_dev_addr[ETH_ALEN];
+
+	/* If we might be explicite group owner, add GO details */
+	if (prov->conncap & (P2PS_SETUP_GROUP_OWNER |
+			     P2PS_SETUP_NEW))
+		p2ps_add_new_group_info(p2p, buf);
+
+	if (prov->status >= 0)
+		p2p_buf_add_status(buf, (u8) prov->status);
+	else
+		prov->method = config_methods;
+
+	if (p2p->cfg->get_persistent_group) {
+		shared_group = p2p->cfg->get_persistent_group(
+			p2p->cfg->cb_ctx, dev->info.p2p_device_addr, NULL, 0,
+			go_dev_addr, ssid, &ssid_len);
+	}
+
+	/* Add Operating Channel if conncap includes GO */
+	if (shared_group ||
+	    (prov->conncap & (P2PS_SETUP_GROUP_OWNER |
+			      P2PS_SETUP_NEW))) {
+		u8 tmp;
+
+		p2p_go_select_channel(p2p, dev, &tmp);
+
+		if (p2p->op_reg_class && p2p->op_channel)
+			p2p_buf_add_operating_channel(buf, p2p->cfg->country,
+						      p2p->op_reg_class,
+						      p2p->op_channel);
+		else
+			p2p_buf_add_operating_channel(buf, p2p->cfg->country,
+						      p2p->cfg->op_reg_class,
+						      p2p->cfg->op_channel);
+	}
+
+	p2p_buf_add_channel_list(buf, p2p->cfg->country, &p2p->cfg->channels);
+
+	if (prov->info[0])
+		p2p_buf_add_session_info(buf, prov->info);
+
+	p2p_buf_add_connection_capability(buf, prov->conncap);
+
+	p2p_buf_add_advertisement_id(buf, prov->adv_id, prov->adv_mac);
+
+	if (shared_group || prov->conncap == P2PS_SETUP_NEW ||
+	    prov->conncap ==
+	    (P2PS_SETUP_GROUP_OWNER | P2PS_SETUP_NEW) ||
+	    prov->conncap ==
+	    (P2PS_SETUP_GROUP_OWNER | P2PS_SETUP_CLIENT)) {
+		/* Add Config Timeout */
+		p2p_buf_add_config_timeout(buf, p2p->go_timeout,
+					   p2p->client_timeout);
+	}
+
+	p2p_buf_add_listen_channel(buf, p2p->cfg->country, p2p->cfg->reg_class,
+				   p2p->cfg->channel);
+
+	p2p_buf_add_session_id(buf, prov->session_id, prov->session_mac);
+
+	p2p_buf_add_feature_capability(buf, sizeof(feat_cap_mask),
+				       feat_cap_mask);
+
+	if (shared_group)
+		p2p_buf_add_persistent_group_info(buf, go_dev_addr,
+						  ssid, ssid_len);
+}
+
+
 static struct wpabuf * p2p_build_prov_disc_req(struct p2p_data *p2p,
-					       u8 dialog_token,
-					       u16 config_methods,
-					       struct p2p_device *go)
+					       struct p2p_device *dev,
+					       int join)
 {
 	struct wpabuf *buf;
 	u8 *len;
 	size_t extra = 0;
+	u8 dialog_token = dev->dialog_token;
+	u16 config_methods = dev->req_config_methods;
+	struct p2p_device *go = join ? dev : NULL;
+	u8 group_capab;
 
 #ifdef CONFIG_WIFI_DISPLAY
 	if (p2p->wfd_ie_prov_disc_req)
@@ -57,6 +175,10 @@
 	if (p2p->vendor_elem && p2p->vendor_elem[VENDOR_ELEM_P2P_PD_REQ])
 		extra += wpabuf_len(p2p->vendor_elem[VENDOR_ELEM_P2P_PD_REQ]);
 
+	if (p2p->p2ps_prov)
+		extra += os_strlen(p2p->p2ps_prov->info) + 1 +
+			sizeof(struct p2ps_provision);
+
 	buf = wpabuf_alloc(1000 + extra);
 	if (buf == NULL)
 		return NULL;
@@ -64,10 +186,23 @@
 	p2p_buf_add_public_action_hdr(buf, P2P_PROV_DISC_REQ, dialog_token);
 
 	len = p2p_buf_add_ie_hdr(buf);
+
+	group_capab = 0;
+	if (p2p->p2ps_prov) {
+		group_capab |= P2P_GROUP_CAPAB_PERSISTENT_GROUP;
+		group_capab |= P2P_GROUP_CAPAB_PERSISTENT_RECONN;
+		if (p2p->cross_connect)
+			group_capab |= P2P_GROUP_CAPAB_CROSS_CONN;
+		if (p2p->cfg->p2p_intra_bss)
+			group_capab |= P2P_GROUP_CAPAB_INTRA_BSS_DIST;
+	}
 	p2p_buf_add_capability(buf, p2p->dev_capab &
-			       ~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY, 0);
+			       ~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY,
+			       group_capab);
 	p2p_buf_add_device_info(buf, p2p, NULL);
-	if (go) {
+	if (p2p->p2ps_prov) {
+		p2ps_add_pd_req_attrs(p2p, dev, buf, config_methods);
+	} else if (go) {
 		p2p_buf_add_group_id(buf, go->info.p2p_device_addr,
 				     go->oper_ssid, go->oper_ssid_len);
 	}
@@ -89,13 +224,19 @@
 
 
 static struct wpabuf * p2p_build_prov_disc_resp(struct p2p_data *p2p,
+						struct p2p_device *dev,
 						u8 dialog_token,
+						enum p2p_status_code status,
 						u16 config_methods,
+						u32 adv_id,
 						const u8 *group_id,
-						size_t group_id_len)
+						size_t group_id_len,
+						const u8 *persist_ssid,
+						size_t persist_ssid_len)
 {
 	struct wpabuf *buf;
 	size_t extra = 0;
+	int persist = 0;
 
 #ifdef CONFIG_WIFI_DISPLAY
 	struct wpabuf *wfd_ie = p2p->wfd_ie_prov_disc_resp;
@@ -121,12 +262,103 @@
 	if (p2p->vendor_elem && p2p->vendor_elem[VENDOR_ELEM_P2P_PD_RESP])
 		extra += wpabuf_len(p2p->vendor_elem[VENDOR_ELEM_P2P_PD_RESP]);
 
-	buf = wpabuf_alloc(100 + extra);
+	buf = wpabuf_alloc(1000 + extra);
 	if (buf == NULL)
 		return NULL;
 
 	p2p_buf_add_public_action_hdr(buf, P2P_PROV_DISC_RESP, dialog_token);
 
+	/* Add P2P IE for P2PS */
+	if (p2p->p2ps_prov && p2p->p2ps_prov->adv_id == adv_id) {
+		u8 feat_cap_mask[] = { 1, 0 };
+		u8 *len = p2p_buf_add_ie_hdr(buf);
+		struct p2ps_provision *prov = p2p->p2ps_prov;
+		u8 group_capab;
+
+		if (!status && prov->status != -1)
+			status = prov->status;
+
+		p2p_buf_add_status(buf, status);
+		group_capab = P2P_GROUP_CAPAB_PERSISTENT_GROUP |
+			P2P_GROUP_CAPAB_PERSISTENT_RECONN;
+		if (p2p->cross_connect)
+			group_capab |= P2P_GROUP_CAPAB_CROSS_CONN;
+		if (p2p->cfg->p2p_intra_bss)
+			group_capab |= P2P_GROUP_CAPAB_INTRA_BSS_DIST;
+		p2p_buf_add_capability(buf, p2p->dev_capab &
+				       ~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY,
+				       group_capab);
+		p2p_buf_add_device_info(buf, p2p, NULL);
+
+		if (persist_ssid && p2p->cfg->get_persistent_group &&
+		    (status == P2P_SC_SUCCESS ||
+		     status == P2P_SC_SUCCESS_DEFERRED)) {
+			u8 ssid[SSID_MAX_LEN];
+			size_t ssid_len;
+			u8 go_dev_addr[ETH_ALEN];
+
+			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)
+				p2p_buf_add_persistent_group_info(
+					buf, go_dev_addr, ssid, ssid_len);
+		}
+
+		if (!persist && (prov->conncap & P2PS_SETUP_GROUP_OWNER))
+			p2ps_add_new_group_info(p2p, buf);
+
+		/* Add Operating Channel if conncap indicates GO */
+		if (persist || (prov->conncap & P2PS_SETUP_GROUP_OWNER)) {
+			u8 tmp;
+
+			if (dev)
+				p2p_go_select_channel(p2p, dev, &tmp);
+
+			if (p2p->op_reg_class && p2p->op_channel)
+				p2p_buf_add_operating_channel(
+					buf, p2p->cfg->country,
+					p2p->op_reg_class,
+					p2p->op_channel);
+			else
+				p2p_buf_add_operating_channel(
+					buf, p2p->cfg->country,
+					p2p->cfg->op_reg_class,
+					p2p->cfg->op_channel);
+		}
+
+		p2p_buf_add_channel_list(buf, p2p->cfg->country,
+					 &p2p->cfg->channels);
+
+		if (!persist && (status == P2P_SC_SUCCESS ||
+				 status == P2P_SC_SUCCESS_DEFERRED))
+			p2p_buf_add_connection_capability(buf, prov->conncap);
+
+		p2p_buf_add_advertisement_id(buf, adv_id, prov->adv_mac);
+
+		p2p_buf_add_config_timeout(buf, p2p->go_timeout,
+					   p2p->client_timeout);
+
+		p2p_buf_add_session_id(buf, prov->session_id,
+				       prov->session_mac);
+
+		p2p_buf_add_feature_capability(buf, sizeof(feat_cap_mask),
+					       feat_cap_mask);
+		p2p_buf_update_ie_hdr(buf, len);
+	} else if (status != P2P_SC_SUCCESS || adv_id) {
+		u8 *len = p2p_buf_add_ie_hdr(buf);
+
+		p2p_buf_add_status(buf, status);
+
+		if (p2p->p2ps_prov)
+			p2p_buf_add_advertisement_id(buf, adv_id,
+						     p2p->p2ps_prov->adv_mac);
+
+		p2p_buf_update_ie_hdr(buf, len);
+	}
+
 	/* WPS IE with Config Methods attribute */
 	p2p_build_wps_ie_config_methods(buf, config_methods);
 
@@ -142,14 +374,50 @@
 }
 
 
+static int p2ps_setup_p2ps_prov(struct p2p_data *p2p, u32 adv_id,
+				u32 session_id, u16 method,
+				const u8 *session_mac, const u8 *adv_mac)
+{
+	struct p2ps_provision *tmp;
+
+	if (!p2p->p2ps_prov) {
+		p2p->p2ps_prov = os_zalloc(sizeof(struct p2ps_provision) + 1);
+		if (!p2p->p2ps_prov)
+			return -1;
+	} else {
+		os_memset(p2p->p2ps_prov, 0, sizeof(struct p2ps_provision) + 1);
+	}
+
+	tmp = p2p->p2ps_prov;
+	tmp->adv_id = adv_id;
+	tmp->session_id = session_id;
+	tmp->method = method;
+	os_memcpy(tmp->session_mac, session_mac, ETH_ALEN);
+	os_memcpy(tmp->adv_mac, adv_mac, ETH_ALEN);
+	tmp->info[0] = '\0';
+
+	return 0;
+}
+
+
 void p2p_process_prov_disc_req(struct p2p_data *p2p, const u8 *sa,
 			       const u8 *data, size_t len, int rx_freq)
 {
 	struct p2p_message msg;
 	struct p2p_device *dev;
 	int freq;
-	int reject = 1;
+	enum p2p_status_code reject = P2P_SC_FAIL_INCOMPATIBLE_PARAMS;
 	struct wpabuf *resp;
+	u32 adv_id = 0;
+	struct p2ps_advertisement *p2ps_adv = NULL;
+	u8 conncap = P2PS_SETUP_NEW;
+	u8 auto_accept = 0;
+	u32 session_id = 0;
+	u8 session_mac[ETH_ALEN];
+	u8 adv_mac[ETH_ALEN];
+	u8 group_mac[ETH_ALEN];
+	int passwd_id = DEV_PW_DEFAULT;
+	u16 config_methods;
 
 	if (p2p_parse(data, len, &msg))
 		return;
@@ -175,12 +443,13 @@
 
 	if (!(msg.wps_config_methods &
 	      (WPS_CONFIG_DISPLAY | WPS_CONFIG_KEYPAD |
-	       WPS_CONFIG_PUSHBUTTON))) {
+	       WPS_CONFIG_PUSHBUTTON | WPS_CONFIG_P2PS))) {
 		p2p_dbg(p2p, "Unsupported Config Methods in Provision Discovery Request");
 		goto out;
 	}
 
-	if (msg.group_id) {
+	/* 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],
@@ -194,28 +463,203 @@
 		}
 	}
 
-	if (dev)
+	if (dev) {
 		dev->flags &= ~(P2P_DEV_PD_PEER_DISPLAY |
-				P2P_DEV_PD_PEER_KEYPAD);
+				P2P_DEV_PD_PEER_KEYPAD |
+				P2P_DEV_PD_PEER_P2PS);
+
+		/* Remove stale persistent groups */
+		if (p2p->cfg->remove_stale_groups) {
+			p2p->cfg->remove_stale_groups(
+				p2p->cfg->cb_ctx, dev->info.p2p_device_addr,
+				msg.persistent_dev,
+				msg.persistent_ssid, msg.persistent_ssid_len);
+		}
+	}
 	if (msg.wps_config_methods & WPS_CONFIG_DISPLAY) {
 		p2p_dbg(p2p, "Peer " MACSTR
 			" requested us to show a PIN on display", MAC2STR(sa));
 		if (dev)
 			dev->flags |= P2P_DEV_PD_PEER_KEYPAD;
+		passwd_id = DEV_PW_USER_SPECIFIED;
 	} else if (msg.wps_config_methods & WPS_CONFIG_KEYPAD) {
 		p2p_dbg(p2p, "Peer " MACSTR
 			" requested us to write its PIN using keypad",
 			MAC2STR(sa));
 		if (dev)
 			dev->flags |= P2P_DEV_PD_PEER_DISPLAY;
+		passwd_id = DEV_PW_REGISTRAR_SPECIFIED;
+	} else if (msg.wps_config_methods & WPS_CONFIG_P2PS) {
+		p2p_dbg(p2p, "Peer " MACSTR " requesting P2PS PIN",
+			MAC2STR(sa));
+		if (dev)
+			dev->flags |= P2P_DEV_PD_PEER_P2PS;
+		passwd_id = DEV_PW_P2PS_DEFAULT;
 	}
 
-	reject = 0;
+	reject = P2P_SC_SUCCESS;
+
+	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);
+
+		os_memcpy(session_mac, msg.session_mac, ETH_ALEN);
+		os_memcpy(adv_mac, msg.adv_mac, ETH_ALEN);
+
+		session_id = WPA_GET_LE32(msg.session_id);
+		adv_id = WPA_GET_LE32(msg.adv_id);
+
+		if (!msg.status)
+			p2ps_adv = p2p_service_p2ps_id(p2p, adv_id);
+
+		p2p_dbg(p2p, "adv_id: %x - p2ps_adv - %p", adv_id, p2ps_adv);
+
+		if (msg.conn_cap)
+			conncap = *msg.conn_cap;
+		remote_conncap = conncap;
+
+		if (p2ps_adv) {
+			auto_accept = p2ps_adv->auto_accept;
+			conncap = p2p->cfg->p2ps_group_capability(
+				p2p->cfg->cb_ctx, conncap, auto_accept);
+
+			p2p_dbg(p2p, "Conncap: local:%d remote:%d result:%d",
+				auto_accept, remote_conncap, conncap);
+
+			if (p2ps_adv->config_methods &&
+			    !(msg.wps_config_methods &
+			      p2ps_adv->config_methods)) {
+				p2p_dbg(p2p,
+					"Unsupported config methods in Provision Discovery Request (own=0x%x peer=0x%x)",
+					p2ps_adv->config_methods,
+					msg.wps_config_methods);
+				reject = P2P_SC_FAIL_INCOMPATIBLE_PARAMS;
+			} else if (!p2ps_adv->state) {
+				p2p_dbg(p2p, "P2PS state unavailable");
+				reject = P2P_SC_FAIL_UNABLE_TO_ACCOMMODATE;
+			} else if (!conncap) {
+				p2p_dbg(p2p, "Conncap resolution failed");
+				reject = P2P_SC_FAIL_INCOMPATIBLE_PARAMS;
+			}
+
+			if (msg.wps_config_methods & WPS_CONFIG_KEYPAD) {
+				p2p_dbg(p2p, "Keypad - always defer");
+				auto_accept = 0;
+			}
+
+			if (auto_accept || reject != P2P_SC_SUCCESS) {
+				struct p2ps_provision *tmp;
+
+				if (reject == P2P_SC_SUCCESS && !conncap) {
+					reject =
+						P2P_SC_FAIL_INCOMPATIBLE_PARAMS;
+				}
+
+				if (p2ps_setup_p2ps_prov(
+					    p2p, adv_id, session_id,
+					    msg.wps_config_methods,
+					    session_mac, adv_mac) < 0) {
+					reject = P2P_SC_FAIL_UNABLE_TO_ACCOMMODATE;
+					goto out;
+				}
+
+				tmp = p2p->p2ps_prov;
+				if (conncap) {
+					tmp->conncap = conncap;
+					tmp->status = P2P_SC_SUCCESS;
+				} else {
+					tmp->conncap = auto_accept;
+					tmp->status = P2P_SC_FAIL_INCOMPATIBLE_PARAMS;
+				}
+
+				if (reject != P2P_SC_SUCCESS)
+					goto out;
+			}
+		} else if (!msg.status) {
+			reject = P2P_SC_FAIL_INCOMPATIBLE_PARAMS;
+			goto out;
+		}
+
+		if (!msg.status && !auto_accept &&
+		    (!p2p->p2ps_prov || p2p->p2ps_prov->adv_id != adv_id)) {
+			struct p2ps_provision *tmp;
+
+			if (!conncap) {
+				reject = P2P_SC_FAIL_INCOMPATIBLE_PARAMS;
+				goto out;
+			}
+
+			if (p2ps_setup_p2ps_prov(p2p, adv_id, session_id,
+						 msg.wps_config_methods,
+						 session_mac, adv_mac) < 0) {
+				reject = P2P_SC_FAIL_UNABLE_TO_ACCOMMODATE;
+				goto out;
+			}
+			tmp = p2p->p2ps_prov;
+			reject = P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE;
+			tmp->status = reject;
+		}
+
+		if (msg.status) {
+			if (*msg.status &&
+			    *msg.status != P2P_SC_SUCCESS_DEFERRED) {
+				reject = *msg.status;
+			} else if (*msg.status == P2P_SC_SUCCESS_DEFERRED &&
+				   p2p->p2ps_prov) {
+				u16 method = p2p->p2ps_prov->method;
+
+				conncap = p2p->cfg->p2ps_group_capability(
+					p2p->cfg->cb_ctx, remote_conncap,
+					p2p->p2ps_prov->conncap);
+
+				p2p_dbg(p2p,
+					"Conncap: local:%d remote:%d result:%d",
+					p2p->p2ps_prov->conncap,
+					remote_conncap, conncap);
+
+				/*
+				 * 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;
+			}
+		}
+	}
 
 out:
-	resp = p2p_build_prov_disc_resp(p2p, msg.dialog_token,
-					reject ? 0 : msg.wps_config_methods,
-					msg.group_id, msg.group_id_len);
+	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;
@@ -232,7 +676,7 @@
 		p2p_parse_free(&msg);
 		return;
 	}
-	p2p->pending_action_state = P2P_NO_PENDING_ACTION;
+	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) {
@@ -242,7 +686,91 @@
 
 	wpabuf_free(resp);
 
-	if (!reject && p2p->cfg->prov_disc_req) {
+	if (!p2p->cfg->p2ps_prov_complete) {
+		/* Don't emit anything */
+	} else if (msg.status && *msg.status != P2P_SC_SUCCESS &&
+		   *msg.status != P2P_SC_SUCCESS_DEFERRED) {
+		reject = *msg.status;
+		p2p->cfg->p2ps_prov_complete(p2p->cfg->cb_ctx, reject,
+					     sa, adv_mac, session_mac,
+					     NULL, adv_id, session_id,
+					     0, 0, msg.persistent_ssid,
+					     msg.persistent_ssid_len,
+					     0, 0, NULL);
+	} else if (msg.status && *msg.status == P2P_SC_SUCCESS_DEFERRED &&
+		   p2p->p2ps_prov) {
+		p2p->p2ps_prov->status = reject;
+		p2p->p2ps_prov->conncap = conncap;
+
+		if (reject != P2P_SC_SUCCESS)
+			p2p->cfg->p2ps_prov_complete(p2p->cfg->cb_ctx, reject,
+						     sa, adv_mac, session_mac,
+						     NULL, adv_id,
+						     session_id, conncap, 0,
+						     msg.persistent_ssid,
+						     msg.persistent_ssid_len, 0,
+						     0, NULL);
+		else
+			p2p->cfg->p2ps_prov_complete(p2p->cfg->cb_ctx,
+						     *msg.status,
+						     sa, adv_mac, session_mac,
+						     group_mac, adv_id,
+						     session_id, conncap,
+						     passwd_id,
+						     msg.persistent_ssid,
+						     msg.persistent_ssid_len, 0,
+						     0, NULL);
+	} else if (msg.status && p2p->p2ps_prov) {
+		p2p->p2ps_prov->status = P2P_SC_SUCCESS;
+		p2p->cfg->p2ps_prov_complete(p2p->cfg->cb_ctx, *msg.status, sa,
+					     adv_mac, session_mac, group_mac,
+					     adv_id, session_id, conncap,
+					     passwd_id,
+					     msg.persistent_ssid,
+					     msg.persistent_ssid_len,
+					     0, 0, NULL);
+	} else if (msg.status) {
+	} else if (auto_accept && reject == P2P_SC_SUCCESS) {
+		p2p->cfg->p2ps_prov_complete(p2p->cfg->cb_ctx, P2P_SC_SUCCESS,
+					     sa, adv_mac, session_mac,
+					     group_mac, adv_id, session_id,
+					     conncap, passwd_id,
+					     msg.persistent_ssid,
+					     msg.persistent_ssid_len,
+					     0, 0, NULL);
+	} else if (reject == P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE &&
+		   (!msg.session_info || !msg.session_info_len)) {
+		p2p->p2ps_prov->method = msg.wps_config_methods;
+
+		p2p->cfg->p2ps_prov_complete(p2p->cfg->cb_ctx, P2P_SC_SUCCESS,
+					     sa, adv_mac, session_mac,
+					     group_mac, adv_id, session_id,
+					     conncap, passwd_id,
+					     msg.persistent_ssid,
+					     msg.persistent_ssid_len,
+					     0, 1, NULL);
+	} else if (reject == P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE) {
+		size_t buf_len = msg.session_info_len;
+		char *buf = os_malloc(2 * buf_len + 1);
+
+		if (buf) {
+			p2p->p2ps_prov->method = msg.wps_config_methods;
+
+			utf8_escape((char *) msg.session_info, buf_len,
+				    buf, 2 * buf_len + 1);
+
+			p2p->cfg->p2ps_prov_complete(
+				p2p->cfg->cb_ctx, P2P_SC_SUCCESS, sa,
+				adv_mac, session_mac, group_mac, adv_id,
+				session_id, conncap, passwd_id,
+				msg.persistent_ssid, msg.persistent_ssid_len,
+				0, 1, buf);
+
+			os_free(buf);
+		}
+	}
+
+	if (reject == P2P_SC_SUCCESS && p2p->cfg->prov_disc_req) {
 		const u8 *dev_addr = sa;
 		if (msg.p2p_device_addr)
 			dev_addr = msg.p2p_device_addr;
@@ -265,11 +793,48 @@
 	struct p2p_message msg;
 	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];
+	int passwd_id = DEV_PW_DEFAULT;
 
 	if (p2p_parse(data, len, &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);
+
+	if (msg.adv_mac)
+		os_memcpy(adv_mac, msg.adv_mac, ETH_ALEN);
+	else
+		os_memset(adv_mac, 0, ETH_ALEN);
+
+	if (msg.adv_id)
+		adv_id = WPA_GET_LE32(msg.adv_id);
+
+	if (msg.conn_cap) {
+		conncap = *msg.conn_cap;
+
+		/* Switch bits to local relative */
+		switch (conncap) {
+		case P2PS_SETUP_GROUP_OWNER:
+			conncap = P2PS_SETUP_CLIENT;
+			break;
+		case P2PS_SETUP_CLIENT:
+			conncap = P2PS_SETUP_GROUP_OWNER;
+			break;
+		}
+	}
+
 	p2p_dbg(p2p, "Received Provision Discovery Response from " MACSTR
 		" with config methods 0x%x",
 		MAC2STR(sa), msg.wps_config_methods);
@@ -313,23 +878,104 @@
 			msg.wps_config_methods, req_config_methods);
 		if (p2p->cfg->prov_disc_fail)
 			p2p->cfg->prov_disc_fail(p2p->cfg->cb_ctx, sa,
-						 P2P_PROV_DISC_REJECTED);
+						 P2P_PROV_DISC_REJECTED,
+						 adv_id, adv_mac, NULL);
 		p2p_parse_free(&msg);
+		p2ps_prov_free(p2p);
 		goto out;
 	}
 
 	report_config_methods = req_config_methods;
 	dev->flags &= ~(P2P_DEV_PD_PEER_DISPLAY |
-			P2P_DEV_PD_PEER_KEYPAD);
+			P2P_DEV_PD_PEER_KEYPAD |
+			P2P_DEV_PD_PEER_P2PS);
 	if (req_config_methods & WPS_CONFIG_DISPLAY) {
 		p2p_dbg(p2p, "Peer " MACSTR
 			" accepted to show a PIN on display", MAC2STR(sa));
 		dev->flags |= P2P_DEV_PD_PEER_DISPLAY;
+		passwd_id = DEV_PW_REGISTRAR_SPECIFIED;
 	} else if (msg.wps_config_methods & WPS_CONFIG_KEYPAD) {
 		p2p_dbg(p2p, "Peer " MACSTR
 			" accepted to write our PIN using keypad",
 			MAC2STR(sa));
 		dev->flags |= P2P_DEV_PD_PEER_KEYPAD;
+		passwd_id = DEV_PW_USER_SPECIFIED;
+	} else if (msg.wps_config_methods & WPS_CONFIG_P2PS) {
+		p2p_dbg(p2p, "Peer " MACSTR " accepted P2PS PIN",
+			MAC2STR(sa));
+		dev->flags |= P2P_DEV_PD_PEER_P2PS;
+		passwd_id = DEV_PW_P2PS_DEFAULT;
+	}
+
+	if ((msg.conn_cap || msg.persistent_dev) &&
+	    msg.adv_id &&
+	    (status == P2P_SC_SUCCESS || status == P2P_SC_SUCCESS_DEFERRED) &&
+	    p2p->p2ps_prov) {
+		if (p2p->cfg->p2ps_prov_complete) {
+			p2p->cfg->p2ps_prov_complete(
+				p2p->cfg->cb_ctx, status, sa, adv_mac,
+				p2p->p2ps_prov->session_mac,
+				group_mac, adv_id, p2p->p2ps_prov->session_id,
+				conncap, passwd_id, msg.persistent_ssid,
+				msg.persistent_ssid_len, 1, 0, NULL);
+		}
+		p2ps_prov_free(p2p);
+	}
+
+	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);
+		p2ps_prov_free(p2p);
+	}
+
+	if (status == P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE) {
+		if (p2p->cfg->remove_stale_groups) {
+			p2p->cfg->remove_stale_groups(p2p->cfg->cb_ctx,
+						      dev->info.p2p_device_addr,
+						      NULL, NULL, 0);
+		}
+
+		if (msg.session_info && msg.session_info_len) {
+			size_t info_len = msg.session_info_len;
+			char *deferred_sess_resp = os_malloc(2 * info_len + 1);
+
+			if (!deferred_sess_resp) {
+				p2p_parse_free(&msg);
+				p2ps_prov_free(p2p);
+				goto out;
+			}
+			utf8_escape((char *) msg.session_info, info_len,
+				    deferred_sess_resp, 2 * info_len + 1);
+
+			if (p2p->cfg->prov_disc_fail)
+				p2p->cfg->prov_disc_fail(
+					p2p->cfg->cb_ctx, sa,
+					P2P_PROV_DISC_INFO_UNAVAILABLE,
+					adv_id, adv_mac,
+					deferred_sess_resp);
+			os_free(deferred_sess_resp);
+		} else
+			if (p2p->cfg->prov_disc_fail)
+				p2p->cfg->prov_disc_fail(
+					p2p->cfg->cb_ctx, sa,
+					P2P_PROV_DISC_INFO_UNAVAILABLE,
+					adv_id, adv_mac, NULL);
+	} else if (msg.wps_config_methods != dev->req_config_methods ||
+		   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_parse_free(&msg);
+		p2ps_prov_free(p2p);
+		goto out;
 	}
 
 	/* Store the provisioning info */
@@ -388,9 +1034,33 @@
 		/* TODO: use device discoverability request through GO */
 	}
 
-	req = p2p_build_prov_disc_req(p2p, dev->dialog_token,
-				      dev->req_config_methods,
-				      join ? dev : NULL);
+	if (p2p->p2ps_prov) {
+		if (p2p->p2ps_prov->status == P2P_SC_SUCCESS_DEFERRED) {
+			if (p2p->p2ps_prov->method == WPS_CONFIG_DISPLAY)
+				dev->req_config_methods = WPS_CONFIG_KEYPAD;
+			else if (p2p->p2ps_prov->method == WPS_CONFIG_KEYPAD)
+				dev->req_config_methods = WPS_CONFIG_DISPLAY;
+			else
+				dev->req_config_methods = WPS_CONFIG_P2PS;
+		} else {
+			/* Order of preference, based on peer's capabilities */
+			if (p2p->p2ps_prov->method)
+				dev->req_config_methods =
+					p2p->p2ps_prov->method;
+			else if (dev->info.config_methods & WPS_CONFIG_P2PS)
+				dev->req_config_methods = WPS_CONFIG_P2PS;
+			else if (dev->info.config_methods & WPS_CONFIG_DISPLAY)
+				dev->req_config_methods = WPS_CONFIG_DISPLAY;
+			else
+				dev->req_config_methods = WPS_CONFIG_KEYPAD;
+		}
+		p2p_dbg(p2p,
+			"Building PD Request based on P2PS config method 0x%x status %d --> req_config_methods 0x%x",
+			p2p->p2ps_prov->method, p2p->p2ps_prov->status,
+			dev->req_config_methods);
+	}
+
+	req = p2p_build_prov_disc_req(p2p, dev, join);
 	if (req == NULL)
 		return -1;
 
@@ -413,6 +1083,7 @@
 
 
 int p2p_prov_disc_req(struct p2p_data *p2p, const u8 *peer_addr,
+		      struct p2ps_provision *p2ps_prov,
 		      u16 config_methods, int join, int force_freq,
 		      int user_initiated_pd)
 {
@@ -424,17 +1095,28 @@
 	if (dev == NULL || (dev->flags & P2P_DEV_PROBE_REQ_ONLY)) {
 		p2p_dbg(p2p, "Provision Discovery Request destination " MACSTR
 			" not yet known", MAC2STR(peer_addr));
+		os_free(p2ps_prov);
 		return -1;
 	}
 
 	p2p_dbg(p2p, "Provision Discovery Request with " MACSTR
 		" (config methods 0x%x)",
 		MAC2STR(peer_addr), config_methods);
-	if (config_methods == 0)
+	if (config_methods == 0 && !p2ps_prov) {
+		os_free(p2ps_prov);
 		return -1;
+	}
+
+	if (p2ps_prov && p2ps_prov->status == P2P_SC_SUCCESS_DEFERRED &&
+	    p2p->p2ps_prov) {
+		/* Use cached method from deferred provisioning */
+		p2ps_prov->method = p2p->p2ps_prov->method;
+	}
 
 	/* Reset provisioning info */
 	dev->wps_prov_info = 0;
+	p2ps_prov_free(p2p);
+	p2p->p2ps_prov = p2ps_prov;
 
 	dev->req_config_methods = config_methods;
 	if (join)
@@ -489,3 +1171,10 @@
 	p2p->pd_retries = 0;
 	p2p->pd_force_freq = 0;
 }
+
+
+void p2ps_prov_free(struct p2p_data *p2p)
+{
+	os_free(p2p->p2ps_prov);
+	p2p->p2ps_prov = NULL;
+}
diff --git a/src/p2p/p2p_sd.c b/src/p2p/p2p_sd.c
index 13119c2..1a2af04 100644
--- a/src/p2p/p2p_sd.c
+++ b/src/p2p/p2p_sd.c
@@ -75,16 +75,25 @@
 				return NULL;
 			/* query number that needs to be send to the device */
 			if (count == dev->sd_pending_bcast_queries - 1)
-				return q;
+				goto found;
 			count++;
 		}
 		if (!q->for_all_peers &&
 		    os_memcmp(q->peer, dev->info.p2p_device_addr, ETH_ALEN) ==
 		    0)
-			return q;
+			goto found;
 	}
 
 	return NULL;
+
+found:
+	if (dev->sd_reqs > 100) {
+		p2p_dbg(p2p, "Too many SD request attempts to " MACSTR
+			" - skip remaining queries",
+			MAC2STR(dev->info.p2p_device_addr));
+		return NULL;
+	}
+	return q;
 }
 
 
@@ -287,6 +296,7 @@
 	if (req == NULL)
 		return -1;
 
+	dev->sd_reqs++;
 	p2p->sd_peer = dev;
 	p2p->sd_query = query;
 	p2p->pending_action_state = P2P_PENDING_SD;
diff --git a/src/p2p/p2p_utils.c b/src/p2p/p2p_utils.c
index 23acce7..eee3c5a 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/ieee802_11_common.h"
 #include "p2p_i.h"
 
 
@@ -54,56 +55,7 @@
  */
 int p2p_channel_to_freq(int op_class, int channel)
 {
-	/* Table E-4 in IEEE Std 802.11-2012 - Global operating classes */
-	/* TODO: more operating classes */
-	switch (op_class) {
-	case 81:
-		/* channels 1..13 */
-		if (channel < 1 || channel > 13)
-			return -1;
-		return 2407 + 5 * channel;
-	case 82:
-		/* channel 14 */
-		if (channel != 14)
-			return -1;
-		return 2414 + 5 * channel;
-	case 83: /* channels 1..9; 40 MHz */
-	case 84: /* channels 5..13; 40 MHz */
-		if (channel < 1 || channel > 13)
-			return -1;
-		return 2407 + 5 * channel;
-	case 115: /* channels 36,40,44,48; indoor only */
-	case 118: /* channels 52,56,60,64; dfs */
-		if (channel < 36 || channel > 64)
-			return -1;
-		return 5000 + 5 * channel;
-	case 124: /* channels 149,153,157,161 */
-	case 125: /* channels 149,153,157,161,165,169 */
-		if (channel < 149 || channel > 161)
-			return -1;
-		return 5000 + 5 * channel;
-	case 116: /* channels 36,44; 40 MHz; indoor only */
-	case 117: /* channels 40,48; 40 MHz; indoor only */
-	case 119: /* channels 52,60; 40 MHz; dfs */
-	case 120: /* channels 56,64; 40 MHz; dfs */
-		if (channel < 36 || channel > 64)
-			return -1;
-		return 5000 + 5 * channel;
-	case 126: /* channels 149,157; 40 MHz */
-	case 127: /* channels 153,161; 40 MHz */
-		if (channel < 149 || channel > 161)
-			return -1;
-		return 5000 + 5 * channel;
-	case 128: /* center freqs 42, 58, 106, 122, 138, 155; 80 MHz */
-		if (channel < 36 || channel > 161)
-			return -1;
-		return 5000 + 5 * channel;
-	case 180: /* 60 GHz band, channels 1..4 */
-		if (channel < 1 || channel > 4)
-			return -1;
-		return 56160 + 2160 * channel;
-	}
-	return -1;
+	return ieee80211_chan_to_freq(NULL, op_class, channel);
 }
 
 
@@ -149,6 +101,15 @@
 		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;
@@ -241,20 +202,15 @@
 
 
 /**
- * p2p_channels_union - Union of channel lists
- * @a: First set of channels
+ * p2p_channels_union_inplace - Inplace union of channel lists
+ * @res: Input data and place for returning union of the channel sets
  * @b: Second set of channels
- * @res: Data structure for returning the union of channels
  */
-void p2p_channels_union(const struct p2p_channels *a,
-			const struct p2p_channels *b,
-			struct p2p_channels *res)
+void p2p_channels_union_inplace(struct p2p_channels *res,
+				const struct p2p_channels *b)
 {
 	size_t i, j;
 
-	if (a != res)
-		os_memcpy(res, a, sizeof(*res));
-
 	for (i = 0; i < res->reg_classes; i++) {
 		struct p2p_reg_class *cl = &res->reg_class[i];
 		for (j = 0; j < b->reg_classes; j++) {
@@ -284,6 +240,21 @@
 }
 
 
+/**
+ * p2p_channels_union - Union of channel lists
+ * @a: First set of channels
+ * @b: Second set of channels
+ * @res: Data structure for returning the union of channels
+ */
+void p2p_channels_union(const struct p2p_channels *a,
+			const struct p2p_channels *b,
+			struct p2p_channels *res)
+{
+	os_memcpy(res, a, sizeof(*res));
+	p2p_channels_union_inplace(res, b);
+}
+
+
 void p2p_channels_remove_freqs(struct p2p_channels *chan,
 			       const struct wpa_freq_range_list *list)
 {
@@ -428,7 +399,7 @@
 		const struct p2p_reg_class *c;
 		c = &chan->reg_class[i];
 		ret = os_snprintf(pos, end - pos, " %u:", c->reg_class);
-		if (ret < 0 || ret >= end - pos)
+		if (os_snprintf_error(end - pos, ret))
 			break;
 		pos += ret;
 
@@ -436,7 +407,7 @@
 			ret = os_snprintf(pos, end - pos, "%s%u",
 					  j == 0 ? "" : ",",
 					  c->channel[j]);
-			if (ret < 0 || ret >= end - pos)
+			if (os_snprintf_error(end - pos, ret))
 				break;
 			pos += ret;
 		}
@@ -517,3 +488,35 @@
 
 	return 0;
 }
+
+
+int p2p_channels_to_freqs(const struct p2p_channels *channels, int *freq_list,
+			  unsigned int max_len)
+{
+	unsigned int i, idx;
+
+	if (!channels || max_len == 0)
+		return 0;
+
+	for (i = 0, idx = 0; i < channels->reg_classes; i++) {
+		const struct p2p_reg_class *c = &channels->reg_class[i];
+		unsigned int j;
+
+		if (idx + 1 == max_len)
+			break;
+		for (j = 0; j < c->channels; j++) {
+			int freq;
+			if (idx + 1 == max_len)
+				break;
+			freq = p2p_channel_to_freq(c->reg_class,
+						   c->channel[j]);
+			if (freq < 0)
+				continue;
+			freq_list[idx++] = freq;
+		}
+	}
+
+	freq_list[idx] = 0;
+
+	return idx;
+}
diff --git a/src/pae/ieee802_1x_kay.c b/src/pae/ieee802_1x_kay.c
index b1cf32d..ef74430 100644
--- a/src/pae/ieee802_1x_kay.c
+++ b/src/pae/ieee802_1x_kay.c
@@ -729,7 +729,8 @@
 
 	/* If the peer's MI is my MI, I will choose new MI */
 	if (os_memcmp(body->actor_mi, participant->mi, MI_LEN) == 0) {
-		os_get_random(participant->mi, sizeof(participant->mi));
+		if (os_get_random(participant->mi, sizeof(participant->mi)) < 0)
+			return NULL;
 		participant->mn = 0;
 	}
 
@@ -1003,8 +1004,10 @@
 		if (os_memcmp(peer_mi, participant->mi, MI_LEN) == 0) {
 			/* My message id is used by other participant */
 			if (peer_mn > participant->mn) {
-				os_get_random(participant->mi,
-					      sizeof(participant->mi));
+				if (os_get_random(participant->mi,
+						  sizeof(participant->mi)) < 0)
+					wpa_printf(MSG_DEBUG,
+						   "KaY: Could not update mi");
 				participant->mn = 0;
 			}
 			continue;
@@ -1054,8 +1057,10 @@
 		if (os_memcmp(peer_mi, participant->mi, MI_LEN) == 0) {
 			/* My message id is used by other participant */
 			if (peer_mn > participant->mn) {
-				os_get_random(participant->mi,
-					      sizeof(participant->mi));
+				if (os_get_random(participant->mi,
+						  sizeof(participant->mi)) < 0)
+					wpa_printf(MSG_DEBUG,
+						   "KaY: Could not update mi");
 				participant->mn = 0;
 			}
 			continue;
@@ -1998,7 +2003,12 @@
 		return -1;
 	}
 	ctx_offset = 0;
-	os_get_random(context + ctx_offset, conf->key_len);
+	if (os_get_random(context + ctx_offset, conf->key_len) < 0) {
+		os_free(context);
+		os_free(conf->key);
+		os_free(conf);
+		return -1;
+	}
 	ctx_offset += conf->key_len;
 	dl_list_for_each(peer, &participant->live_peers,
 			 struct ieee802_1x_kay_peer, list) {
@@ -3159,7 +3169,7 @@
 		kay->macsec_capable = MACSEC_CAP_NOT_IMPLEMENTED;
 		kay->macsec_desired = FALSE;
 		kay->macsec_protect = FALSE;
-		kay->macsec_validate = FALSE;
+		kay->macsec_validate = Disabled;
 		kay->macsec_replay_protect = FALSE;
 		kay->macsec_replay_window = 0;
 		kay->macsec_confidentiality = CONFIDENTIALITY_NONE;
@@ -3167,7 +3177,7 @@
 		kay->macsec_capable = MACSEC_CAP_INTEG_AND_CONF_0_30_50;
 		kay->macsec_desired = TRUE;
 		kay->macsec_protect = TRUE;
-		kay->macsec_validate = TRUE;
+		kay->macsec_validate = Strict;
 		kay->macsec_replay_protect = FALSE;
 		kay->macsec_replay_window = 0;
 		kay->macsec_confidentiality = CONFIDENTIALITY_OFFSET_0;
@@ -3325,7 +3335,8 @@
 	participant->retry_count = 0;
 	participant->kay = kay;
 
-	os_get_random(participant->mi, sizeof(participant->mi));
+	if (os_get_random(participant->mi, sizeof(participant->mi)) < 0)
+		goto fail;
 	participant->mn = 0;
 
 	participant->lrx = FALSE;
@@ -3340,6 +3351,9 @@
 	dl_list_init(&participant->rxsc_list);
 	participant->txsc = ieee802_1x_kay_init_transmit_sc(&kay->actor_sci,
 							    kay->sc_ch);
+	secy_cp_control_protect_frames(kay, kay->macsec_protect);
+	secy_cp_control_replay(kay, kay->macsec_replay_protect,
+			       kay->macsec_replay_window);
 	secy_create_transmit_sc(kay, participant->txsc);
 
 	/* to derive KEK from CAK and CKN */
diff --git a/src/radius/Makefile b/src/radius/Makefile
index b5d063d..3ad4751 100644
--- a/src/radius/Makefile
+++ b/src/radius/Makefile
@@ -14,6 +14,7 @@
 LIB_OBJS= \
 	radius.o \
 	radius_client.o \
+	radius_das.o \
 	radius_server.o
 
 libradius.a: $(LIB_OBJS)
diff --git a/src/radius/radius.c b/src/radius/radius.c
index f3b645d..1ebfd11 100644
--- a/src/radius/radius.c
+++ b/src/radius/radius.c
@@ -167,7 +167,7 @@
 	} data_type;
 };
 
-static struct radius_attr_type radius_attrs[] =
+static const struct radius_attr_type radius_attrs[] =
 {
 	{ RADIUS_ATTR_USER_NAME, "User-Name", RADIUS_ATTR_TEXT },
 	{ RADIUS_ATTR_USER_PASSWORD, "User-Password", RADIUS_ATTR_UNDIST },
@@ -233,6 +233,17 @@
 	{ RADIUS_ATTR_NAS_IPV6_ADDRESS, "NAS-IPv6-Address", RADIUS_ATTR_IPV6 },
 	{ RADIUS_ATTR_ERROR_CAUSE, "Error-Cause", RADIUS_ATTR_INT32 },
 	{ RADIUS_ATTR_EAP_KEY_NAME, "EAP-Key-Name", RADIUS_ATTR_HEXDUMP },
+	{ RADIUS_ATTR_OPERATOR_NAME, "Operator-Name", RADIUS_ATTR_TEXT },
+	{ RADIUS_ATTR_LOCATION_INFO, "Location-Information",
+	  RADIUS_ATTR_HEXDUMP },
+	{ RADIUS_ATTR_LOCATION_DATA, "Location-Data", RADIUS_ATTR_HEXDUMP },
+	{ RADIUS_ATTR_BASIC_LOCATION_POLICY_RULES,
+	  "Basic-Location-Policy-Rules", RADIUS_ATTR_HEXDUMP },
+	{ RADIUS_ATTR_EXTENDED_LOCATION_POLICY_RULES,
+	  "Extended-Location-Policy-Rules", RADIUS_ATTR_HEXDUMP },
+	{ RADIUS_ATTR_LOCATION_CAPABLE, "Location-Capable", RADIUS_ATTR_INT32 },
+	{ RADIUS_ATTR_REQUESTED_LOCATION_INFO, "Requested-Location-Info",
+	  RADIUS_ATTR_INT32 },
 	{ RADIUS_ATTR_MOBILITY_DOMAIN_ID, "Mobility-Domain-Id",
 	  RADIUS_ATTR_INT32 },
 	{ RADIUS_ATTR_WLAN_HESSID, "WLAN-HESSID", RADIUS_ATTR_TEXT },
@@ -248,7 +259,7 @@
 #define RADIUS_ATTRS ARRAY_SIZE(radius_attrs)
 
 
-static struct radius_attr_type *radius_get_attr_type(u8 type)
+static const struct radius_attr_type *radius_get_attr_type(u8 type)
 {
 	size_t i;
 
@@ -263,7 +274,7 @@
 
 static void radius_msg_dump_attr(struct radius_attr_hdr *hdr)
 {
-	struct radius_attr_type *attr;
+	const struct radius_attr_type *attr;
 	int len;
 	unsigned char *pos;
 	char buf[1000];
@@ -945,7 +956,6 @@
 			vhdr = (struct radius_attr_vendor *) pos;
 			if (vhdr->vendor_length > left ||
 			    vhdr->vendor_length < sizeof(*vhdr)) {
-				left = 0;
 				break;
 			}
 			if (vhdr->vendor_type != subtype) {
@@ -983,13 +993,16 @@
 
 	/* key: 16-bit salt followed by encrypted key info */
 
-	if (len < 2 + 16)
+	if (len < 2 + 16) {
+		wpa_printf(MSG_DEBUG, "RADIUS: %s: Len is too small: %d",
+			   __func__, (int) len);
 		return NULL;
+	}
 
 	pos = key + 2;
 	left = len - 2;
 	if (left % 16) {
-		wpa_printf(MSG_INFO, "Invalid ms key len %lu",
+		wpa_printf(MSG_INFO, "RADIUS: Invalid ms key len %lu",
 			   (unsigned long) left);
 		return NULL;
 	}
@@ -1024,7 +1037,7 @@
 	}
 
 	if (plain[0] == 0 || plain[0] > plen - 1) {
-		wpa_printf(MSG_INFO, "Failed to decrypt MPPE key");
+		wpa_printf(MSG_INFO, "RADIUS: Failed to decrypt MPPE key");
 		os_free(plain);
 		return NULL;
 	}
@@ -1113,6 +1126,10 @@
 					    sent_msg->hdr->authenticator,
 					    secret, secret_len,
 					    &keys->send_len);
+		if (!keys->send) {
+			wpa_printf(MSG_DEBUG,
+				   "RADIUS: Failed to decrypt send key");
+		}
 		os_free(key);
 	}
 
@@ -1124,6 +1141,10 @@
 					    sent_msg->hdr->authenticator,
 					    secret, secret_len,
 					    &keys->recv_len);
+		if (!keys->recv) {
+			wpa_printf(MSG_DEBUG,
+				   "RADIUS: Failed to decrypt recv key");
+		}
 		os_free(key);
 	}
 
@@ -1204,7 +1225,7 @@
 	}
 
 	/* MS-MPPE-Recv-Key */
-	buf = os_malloc(hlen + send_key_len + 16);
+	buf = os_malloc(hlen + recv_key_len + 16);
 	if (buf == NULL) {
 		return 0;
 	}
@@ -1404,7 +1425,7 @@
 /**
  * radius_msg_get_vlanid - Parse RADIUS attributes for VLAN tunnel information
  * @msg: RADIUS message
- * Returns: VLAN ID for the first tunnel configuration of -1 if none is found
+ * Returns: VLAN ID for the first tunnel configuration or 0 if none is found
  */
 int radius_msg_get_vlanid(struct radius_msg *msg)
 {
@@ -1467,7 +1488,7 @@
 			return tun->vlanid;
 	}
 
-	return -1;
+	return 0;
 }
 
 
diff --git a/src/radius/radius.h b/src/radius/radius.h
index 62faae1..5977339 100644
--- a/src/radius/radius.h
+++ b/src/radius/radius.h
@@ -92,6 +92,13 @@
        RADIUS_ATTR_NAS_IPV6_ADDRESS = 95,
        RADIUS_ATTR_ERROR_CAUSE = 101,
        RADIUS_ATTR_EAP_KEY_NAME = 102,
+       RADIUS_ATTR_OPERATOR_NAME = 126,
+       RADIUS_ATTR_LOCATION_INFO = 127,
+       RADIUS_ATTR_LOCATION_DATA = 128,
+       RADIUS_ATTR_BASIC_LOCATION_POLICY_RULES = 129,
+       RADIUS_ATTR_EXTENDED_LOCATION_POLICY_RULES = 130,
+       RADIUS_ATTR_LOCATION_CAPABLE = 131,
+       RADIUS_ATTR_REQUESTED_LOCATION_INFO = 132,
        RADIUS_ATTR_MOBILITY_DOMAIN_ID = 177,
        RADIUS_ATTR_WLAN_HESSID = 181,
        RADIUS_ATTR_WLAN_PAIRWISE_CIPHER = 186,
diff --git a/src/radius/radius_client.c b/src/radius/radius_client.c
index e2766e2..693f61e 100644
--- a/src/radius/radius_client.c
+++ b/src/radius/radius_client.c
@@ -1,6 +1,6 @@
 /*
  * RADIUS client
- * 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.
@@ -236,6 +236,8 @@
 		     int sock, int sock6, int auth);
 static int radius_client_init_acct(struct radius_client_data *radius);
 static int radius_client_init_auth(struct radius_client_data *radius);
+static void radius_client_auth_failover(struct radius_client_data *radius);
+static void radius_client_acct_failover(struct radius_client_data *radius);
 
 
 static void radius_client_msg_free(struct radius_msg_list *req)
@@ -304,7 +306,7 @@
 {
 #ifndef CONFIG_NATIVE_WINDOWS
 	int _errno = errno;
-	wpa_printf(MSG_INFO, "send[RADIUS]: %s", strerror(errno));
+	wpa_printf(MSG_INFO, "send[RADIUS,s=%d]: %s", s, strerror(errno));
 	if (_errno == ENOTCONN || _errno == EDESTADDRREQ || _errno == EINVAL ||
 	    _errno == EBADF || _errno == ENETUNREACH) {
 		hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS,
@@ -333,9 +335,18 @@
 	struct hostapd_radius_servers *conf = radius->conf;
 	int s;
 	struct wpabuf *buf;
+	size_t prev_num_msgs;
 
 	if (entry->msg_type == RADIUS_ACCT ||
 	    entry->msg_type == RADIUS_ACCT_INTERIM) {
+		if (radius->acct_sock < 0)
+			radius_client_init_acct(radius);
+		if (radius->acct_sock < 0 && conf->num_acct_servers > 1) {
+			prev_num_msgs = radius->num_msgs;
+			radius_client_acct_failover(radius);
+			if (prev_num_msgs != radius->num_msgs)
+				return 0;
+		}
 		s = radius->acct_sock;
 		if (entry->attempts == 0)
 			conf->acct_server->requests++;
@@ -344,6 +355,14 @@
 			conf->acct_server->retransmissions++;
 		}
 	} else {
+		if (radius->auth_sock < 0)
+			radius_client_init_auth(radius);
+		if (radius->auth_sock < 0 && conf->num_auth_servers > 1) {
+			prev_num_msgs = radius->num_msgs;
+			radius_client_auth_failover(radius);
+			if (prev_num_msgs != radius->num_msgs)
+				return 0;
+		}
 		s = radius->auth_sock;
 		if (entry->attempts == 0)
 			conf->auth_server->requests++;
@@ -352,6 +371,11 @@
 			conf->auth_server->retransmissions++;
 		}
 	}
+	if (s < 0) {
+		wpa_printf(MSG_INFO,
+			   "RADIUS: No valid socket for retransmission");
+		return 1;
+	}
 
 	/* retransmit; remove entry if too many attempts */
 	entry->attempts++;
@@ -388,7 +412,6 @@
 	os_time_t first;
 	struct radius_msg_list *entry, *prev, *tmp;
 	int auth_failover = 0, acct_failover = 0;
-	char abuf[50];
 	size_t prev_num_msgs;
 	int s;
 
@@ -453,54 +476,70 @@
 			       (long int) (first - now.sec));
 	}
 
-	if (auth_failover && conf->num_auth_servers > 1) {
-		struct hostapd_radius_server *next, *old;
-		old = conf->auth_server;
-		hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS,
-			       HOSTAPD_LEVEL_NOTICE,
-			       "No response from Authentication server "
-			       "%s:%d - failover",
-			       hostapd_ip_txt(&old->addr, abuf, sizeof(abuf)),
-			       old->port);
+	if (auth_failover && conf->num_auth_servers > 1)
+		radius_client_auth_failover(radius);
 
-		for (entry = radius->msgs; entry; entry = entry->next) {
-			if (entry->msg_type == RADIUS_AUTH)
-				old->timeouts++;
-		}
+	if (acct_failover && conf->num_acct_servers > 1)
+		radius_client_acct_failover(radius);
+}
 
-		next = old + 1;
-		if (next > &(conf->auth_servers[conf->num_auth_servers - 1]))
-			next = conf->auth_servers;
-		conf->auth_server = next;
-		radius_change_server(radius, next, old,
-				     radius->auth_serv_sock,
-				     radius->auth_serv_sock6, 1);
+
+static void radius_client_auth_failover(struct radius_client_data *radius)
+{
+	struct hostapd_radius_servers *conf = radius->conf;
+	struct hostapd_radius_server *next, *old;
+	struct radius_msg_list *entry;
+	char abuf[50];
+
+	old = conf->auth_server;
+	hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS,
+		       HOSTAPD_LEVEL_NOTICE,
+		       "No response from Authentication server %s:%d - failover",
+		       hostapd_ip_txt(&old->addr, abuf, sizeof(abuf)),
+		       old->port);
+
+	for (entry = radius->msgs; entry; entry = entry->next) {
+		if (entry->msg_type == RADIUS_AUTH)
+			old->timeouts++;
 	}
 
-	if (acct_failover && conf->num_acct_servers > 1) {
-		struct hostapd_radius_server *next, *old;
-		old = conf->acct_server;
-		hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS,
-			       HOSTAPD_LEVEL_NOTICE,
-			       "No response from Accounting server "
-			       "%s:%d - failover",
-			       hostapd_ip_txt(&old->addr, abuf, sizeof(abuf)),
-			       old->port);
+	next = old + 1;
+	if (next > &(conf->auth_servers[conf->num_auth_servers - 1]))
+		next = conf->auth_servers;
+	conf->auth_server = next;
+	radius_change_server(radius, next, old,
+			     radius->auth_serv_sock,
+			     radius->auth_serv_sock6, 1);
+}
 
-		for (entry = radius->msgs; entry; entry = entry->next) {
-			if (entry->msg_type == RADIUS_ACCT ||
-			    entry->msg_type == RADIUS_ACCT_INTERIM)
-				old->timeouts++;
-		}
 
-		next = old + 1;
-		if (next > &conf->acct_servers[conf->num_acct_servers - 1])
-			next = conf->acct_servers;
-		conf->acct_server = next;
-		radius_change_server(radius, next, old,
-				     radius->acct_serv_sock,
-				     radius->acct_serv_sock6, 0);
+static void radius_client_acct_failover(struct radius_client_data *radius)
+{
+	struct hostapd_radius_servers *conf = radius->conf;
+	struct hostapd_radius_server *next, *old;
+	struct radius_msg_list *entry;
+	char abuf[50];
+
+	old = conf->acct_server;
+	hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS,
+		       HOSTAPD_LEVEL_NOTICE,
+		       "No response from Accounting server %s:%d - failover",
+		       hostapd_ip_txt(&old->addr, abuf, sizeof(abuf)),
+		       old->port);
+
+	for (entry = radius->msgs; entry; entry = entry->next) {
+		if (entry->msg_type == RADIUS_ACCT ||
+		    entry->msg_type == RADIUS_ACCT_INTERIM)
+			old->timeouts++;
 	}
+
+	next = old + 1;
+	if (next > &conf->acct_servers[conf->num_acct_servers - 1])
+		next = conf->acct_servers;
+	conf->acct_server = next;
+	radius_change_server(radius, next, old,
+			     radius->acct_serv_sock,
+			     radius->acct_serv_sock6, 0);
 }
 
 
@@ -658,7 +697,11 @@
 	}
 
 	if (msg_type == RADIUS_ACCT || msg_type == RADIUS_ACCT_INTERIM) {
-		if (conf->acct_server == NULL || radius->acct_sock < 0) {
+		if (conf->acct_server && radius->acct_sock < 0)
+			radius_client_init_acct(radius);
+
+		if (conf->acct_server == NULL || radius->acct_sock < 0 ||
+		    conf->acct_server->shared_secret == NULL) {
 			hostapd_logger(radius->ctx, NULL,
 				       HOSTAPD_MODULE_RADIUS,
 				       HOSTAPD_LEVEL_INFO,
@@ -672,7 +715,11 @@
 		s = radius->acct_sock;
 		conf->acct_server->requests++;
 	} else {
-		if (conf->auth_server == NULL || radius->auth_sock < 0) {
+		if (conf->auth_server && radius->auth_sock < 0)
+			radius_client_init_auth(radius);
+
+		if (conf->auth_server == NULL || radius->auth_sock < 0 ||
+		    conf->auth_server->shared_secret == NULL) {
 			hostapd_logger(radius->ctx, NULL,
 				       HOSTAPD_MODULE_RADIUS,
 				       HOSTAPD_LEVEL_INFO,
@@ -1039,6 +1086,13 @@
 		return -1;
 	}
 
+	if (sel_sock < 0) {
+		wpa_printf(MSG_INFO,
+			   "RADIUS: No server socket available (af=%d sock=%d sock6=%d auth=%d",
+			   nserv->addr.af, sock, sock6, auth);
+		return -1;
+	}
+
 	if (conf->force_client_addr) {
 		switch (conf->client_addr.af) {
 		case AF_INET:
@@ -1122,18 +1176,28 @@
 	    conf->auth_server != conf->auth_servers) {
 		oserv = conf->auth_server;
 		conf->auth_server = conf->auth_servers;
-		radius_change_server(radius, conf->auth_server, oserv,
-				     radius->auth_serv_sock,
-				     radius->auth_serv_sock6, 1);
+		if (radius_change_server(radius, conf->auth_server, oserv,
+					 radius->auth_serv_sock,
+					 radius->auth_serv_sock6, 1) < 0) {
+			conf->auth_server = oserv;
+			radius_change_server(radius, oserv, conf->auth_server,
+					     radius->auth_serv_sock,
+					     radius->auth_serv_sock6, 1);
+		}
 	}
 
 	if (radius->acct_sock >= 0 && conf->acct_servers &&
 	    conf->acct_server != conf->acct_servers) {
 		oserv = conf->acct_server;
 		conf->acct_server = conf->acct_servers;
-		radius_change_server(radius, conf->acct_server, oserv,
-				     radius->acct_serv_sock,
-				     radius->acct_serv_sock6, 0);
+		if (radius_change_server(radius, conf->acct_server, oserv,
+					 radius->acct_serv_sock,
+					 radius->acct_serv_sock6, 0) < 0) {
+			conf->acct_server = oserv;
+			radius_change_server(radius, oserv, conf->acct_server,
+					     radius->acct_serv_sock,
+					     radius->acct_serv_sock6, 0);
+		}
 	}
 
 	if (conf->retry_primary_interval)
diff --git a/src/radius/radius_das.c b/src/radius/radius_das.c
index 9655f4c..39ceea8 100644
--- a/src/radius/radius_das.c
+++ b/src/radius/radius_das.c
@@ -42,6 +42,7 @@
 		RADIUS_ATTR_CALLING_STATION_ID,
 		RADIUS_ATTR_NAS_IDENTIFIER,
 		RADIUS_ATTR_ACCT_SESSION_ID,
+		RADIUS_ATTR_ACCT_MULTI_SESSION_ID,
 		RADIUS_ATTR_EVENT_TIMESTAMP,
 		RADIUS_ATTR_MESSAGE_AUTHENTICATOR,
 		RADIUS_ATTR_CHARGEABLE_USER_IDENTITY,
@@ -129,6 +130,12 @@
 		attrs.acct_session_id_len = len;
 	}
 
+	if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_ACCT_MULTI_SESSION_ID,
+				    &buf, &len, NULL) == 0) {
+		attrs.acct_multi_session_id = buf;
+		attrs.acct_multi_session_id_len = len;
+	}
+
 	if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_CHARGEABLE_USER_IDENTITY,
 				    &buf, &len, NULL) == 0) {
 		attrs.cui = buf;
@@ -147,6 +154,12 @@
 			   "%s:%d", abuf, from_port);
 		error = 503;
 		break;
+	case RADIUS_DAS_MULTI_SESSION_MATCH:
+		wpa_printf(MSG_INFO,
+			   "DAS: Multiple sessions match for request from %s:%d",
+			   abuf, from_port);
+		error = 508;
+		break;
 	case RADIUS_DAS_SUCCESS:
 		error = 0;
 		break;
diff --git a/src/radius/radius_das.h b/src/radius/radius_das.h
index e3ed540..ce731d4 100644
--- a/src/radius/radius_das.h
+++ b/src/radius/radius_das.h
@@ -14,7 +14,8 @@
 enum radius_das_res {
 	RADIUS_DAS_SUCCESS,
 	RADIUS_DAS_NAS_MISMATCH,
-	RADIUS_DAS_SESSION_NOT_FOUND
+	RADIUS_DAS_SESSION_NOT_FOUND,
+	RADIUS_DAS_MULTI_SESSION_MATCH,
 };
 
 struct radius_das_attrs {
@@ -30,6 +31,8 @@
 	size_t user_name_len;
 	const u8 *acct_session_id;
 	size_t acct_session_id_len;
+	const u8 *acct_multi_session_id;
+	size_t acct_multi_session_id_len;
 	const u8 *cui;
 	size_t cui_len;
 };
diff --git a/src/radius/radius_server.c b/src/radius/radius_server.c
index 00394b4..bdb7e42 100644
--- a/src/radius/radius_server.c
+++ b/src/radius/radius_server.c
@@ -35,7 +35,7 @@
  */
 #define RADIUS_MAX_MSG_LEN 3000
 
-static struct eapol_callbacks radius_server_eapol_cb;
+static const struct eapol_callbacks radius_server_eapol_cb;
 
 struct radius_client;
 struct radius_server_data;
@@ -252,6 +252,20 @@
 	const char *server_id;
 
 	/**
+	 * erp - Whether EAP Re-authentication Protocol (ERP) is enabled
+	 *
+	 * This controls whether the authentication server derives ERP key
+	 * hierarchy (rRK and rIK) from full EAP authentication and allows
+	 * these keys to be used to perform ERP to derive rMSK instead of full
+	 * EAP authentication to derive MSK.
+	 */
+	int erp;
+
+	const char *erp_domain;
+
+	struct dl_list erp_keys; /* struct eap_server_erp_key */
+
+	/**
 	 * wps - Wi-Fi Protected Setup context
 	 *
 	 * If WPS is used with an external RADIUS server (which is quite
@@ -673,6 +687,7 @@
 	eap_conf.pwd_group = data->pwd_group;
 	eap_conf.server_id = (const u8 *) data->server_id;
 	eap_conf.server_id_len = os_strlen(data->server_id);
+	eap_conf.erp = data->erp;
 	radius_server_testing_options(sess, &eap_conf);
 	sess->eap = eap_server_sm_init(sess, &radius_server_eapol_cb,
 				       &eap_conf);
@@ -1687,6 +1702,7 @@
 	if (data == NULL)
 		return NULL;
 
+	dl_list_init(&data->erp_keys);
 	os_get_reltime(&data->start_time);
 	data->conf_ctx = conf->conf_ctx;
 	data->eap_sim_db_priv = conf->eap_sim_db_priv;
@@ -1695,8 +1711,10 @@
 	data->ipv6 = conf->ipv6;
 	if (conf->pac_opaque_encr_key) {
 		data->pac_opaque_encr_key = os_malloc(16);
-		os_memcpy(data->pac_opaque_encr_key, conf->pac_opaque_encr_key,
-			  16);
+		if (data->pac_opaque_encr_key) {
+			os_memcpy(data->pac_opaque_encr_key,
+				  conf->pac_opaque_encr_key, 16);
+		}
 	}
 	if (conf->eap_fast_a_id) {
 		data->eap_fast_a_id = os_malloc(conf->eap_fast_a_id_len);
@@ -1725,6 +1743,8 @@
 			data->eap_req_id_text_len = conf->eap_req_id_text_len;
 		}
 	}
+	data->erp = conf->erp;
+	data->erp_domain = conf->erp_domain;
 
 	if (conf->subscr_remediation_url) {
 		data->subscr_remediation_url =
@@ -1802,6 +1822,24 @@
 
 
 /**
+ * radius_server_erp_flush - Flush all ERP keys
+ * @data: RADIUS server context from radius_server_init()
+ */
+void radius_server_erp_flush(struct radius_server_data *data)
+{
+	struct eap_server_erp_key *erp;
+
+	if (data == NULL)
+		return;
+	while ((erp = dl_list_first(&data->erp_keys, struct eap_server_erp_key,
+				    list)) != NULL) {
+		dl_list_del(&erp->list);
+		bin_clear_free(erp, sizeof(*erp));
+	}
+}
+
+
+/**
  * radius_server_deinit - Deinitialize RADIUS server
  * @data: RADIUS server context from radius_server_init()
  */
@@ -1836,6 +1874,8 @@
 		sqlite3_close(data->db);
 #endif /* CONFIG_SQLITE */
 
+	radius_server_erp_flush(data);
+
 	os_free(data);
 }
 
@@ -1874,7 +1914,7 @@
 			  "radiusAuthServResetTime=0\n"
 			  "radiusAuthServConfigReset=4\n",
 			  uptime);
-	if (ret < 0 || ret >= end - pos) {
+	if (os_snprintf_error(end - pos, ret)) {
 		*pos = '\0';
 		return pos - buf;
 	}
@@ -1913,7 +1953,7 @@
 			  data->counters.malformed_acct_requests,
 			  data->counters.acct_bad_authenticators,
 			  data->counters.unknown_acct_types);
-	if (ret < 0 || ret >= end - pos) {
+	if (os_snprintf_error(end - pos, ret)) {
 		*pos = '\0';
 		return pos - buf;
 	}
@@ -1971,7 +2011,7 @@
 				  cli->counters.malformed_acct_requests,
 				  cli->counters.acct_bad_authenticators,
 				  cli->counters.unknown_acct_types);
-		if (ret < 0 || ret >= end - pos) {
+		if (os_snprintf_error(end - pos, ret)) {
 			*pos = '\0';
 			return pos - buf;
 		}
@@ -1997,6 +2037,12 @@
 		sess->remediation = user->remediation;
 		sess->macacl = user->macacl;
 	}
+
+	if (ret) {
+		RADIUS_DEBUG("%s: User-Name not found from user database",
+			     __func__);
+	}
+
 	return ret;
 }
 
@@ -2017,11 +2063,57 @@
 }
 
 
-static struct eapol_callbacks radius_server_eapol_cb =
+#ifdef CONFIG_ERP
+
+static const char * radius_server_get_erp_domain(void *ctx)
+{
+	struct radius_session *sess = ctx;
+	struct radius_server_data *data = sess->server;
+
+	return data->erp_domain;
+}
+
+
+static struct eap_server_erp_key *
+radius_server_erp_get_key(void *ctx, const char *keyname)
+{
+	struct radius_session *sess = ctx;
+	struct radius_server_data *data = sess->server;
+	struct eap_server_erp_key *erp;
+
+	dl_list_for_each(erp, &data->erp_keys, struct eap_server_erp_key,
+			 list) {
+		if (os_strcmp(erp->keyname_nai, keyname) == 0)
+			return erp;
+	}
+
+	return NULL;
+}
+
+
+static int radius_server_erp_add_key(void *ctx, struct eap_server_erp_key *erp)
+{
+	struct radius_session *sess = ctx;
+	struct radius_server_data *data = sess->server;
+
+	dl_list_add(&data->erp_keys, &erp->list);
+	return 0;
+}
+
+#endif /* CONFIG_ERP */
+
+
+static const struct eapol_callbacks radius_server_eapol_cb =
 {
 	.get_eap_user = radius_server_get_eap_user,
 	.get_eap_req_id_text = radius_server_get_eap_req_id_text,
 	.log_msg = radius_server_log_msg,
+#ifdef CONFIG_ERP
+	.get_erp_send_reauth_start = NULL,
+	.get_erp_domain = radius_server_get_erp_domain,
+	.erp_get_key = radius_server_erp_get_key,
+	.erp_add_key = radius_server_erp_add_key,
+#endif /* CONFIG_ERP */
 };
 
 
diff --git a/src/radius/radius_server.h b/src/radius/radius_server.h
index 46ac312..ca4e38c 100644
--- a/src/radius/radius_server.h
+++ b/src/radius/radius_server.h
@@ -159,6 +159,18 @@
 	const char *server_id;
 
 	/**
+	 * erp - Whether EAP Re-authentication Protocol (ERP) is enabled
+	 *
+	 * This controls whether the authentication server derives ERP key
+	 * hierarchy (rRK and rIK) from full EAP authentication and allows
+	 * these keys to be used to perform ERP to derive rMSK instead of full
+	 * EAP authentication to derive MSK.
+	 */
+	int erp;
+
+	const char *erp_domain;
+
+	/**
 	 * wps - Wi-Fi Protected Setup context
 	 *
 	 * If WPS is used with an external RADIUS server (which is quite
@@ -223,6 +235,7 @@
 struct radius_server_data *
 radius_server_init(struct radius_server_conf *conf);
 
+void radius_server_erp_flush(struct radius_server_data *data);
 void radius_server_deinit(struct radius_server_data *data);
 
 int radius_server_get_mib(struct radius_server_data *data, char *buf,
diff --git a/src/rsn_supp/Makefile b/src/rsn_supp/Makefile
index adfd3df..d5e61fe 100644
--- a/src/rsn_supp/Makefile
+++ b/src/rsn_supp/Makefile
@@ -1,8 +1,30 @@
-all:
-	@echo Nothing to be made.
+all: librsn_supp.a
 
 clean:
-	rm -f *~ *.o *.d *.gcno *.gcda *.gcov
+	rm -f *~ *.o *.d *.gcno *.gcda *.gcov librsn_supp.a
 
 install:
 	@echo Nothing to be made.
+
+include ../lib.rules
+
+CFLAGS += -DCONFIG_IEEE80211W
+CFLAGS += -DCONFIG_IEEE80211R
+CFLAGS += -DCONFIG_PEERKEY
+CFLAGS += -DCONFIG_TDLS
+CFLAGS += -DCONFIG_WNM
+CFLAGS += -DIEEE8021X_EAPOL
+
+LIB_OBJS= \
+	pmksa_cache.o \
+	wpa_ft.o \
+	peerkey.o \
+	tdls.o \
+	preauth.o \
+	wpa.o \
+	wpa_ie.o
+
+librsn_supp.a: $(LIB_OBJS)
+	$(AR) crT $@ $?
+
+-include $(OBJS:%.o=%.d)
diff --git a/src/rsn_supp/peerkey.c b/src/rsn_supp/peerkey.c
index aab8b7e..79764d9 100644
--- a/src/rsn_supp/peerkey.c
+++ b/src/rsn_supp/peerkey.c
@@ -1,6 +1,6 @@
 /*
  * WPA Supplicant - PeerKey for Direct Link Setup (DLS)
- * Copyright (c) 2006-2008, 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.
@@ -65,6 +65,7 @@
 {
 	size_t rlen;
 	struct wpa_eapol_key *err;
+	struct wpa_eapol_key_192 *err192;
 	struct rsn_error_kde error;
 	u8 *rbuf, *pos;
 	size_t kde_len;
@@ -79,6 +80,7 @@
 				  (void *) &err);
 	if (rbuf == NULL)
 		return -1;
+	err192 = (struct wpa_eapol_key_192 *) err;
 
 	err->type = EAPOL_KEY_TYPE_RSN;
 	key_info = ver | WPA_KEY_INFO_SMK_MESSAGE | WPA_KEY_INFO_MIC |
@@ -112,8 +114,8 @@
 			   "(mui %d error_type %d)", mui, error_type);
 	}
 
-	wpa_eapol_key_send(sm, sm->ptk.kck, ver, dst, ETH_P_EAPOL,
-			   rbuf, rlen, err->key_mic);
+	wpa_eapol_key_send(sm, sm->ptk.kck, sm->ptk.kck_len, ver, dst,
+			   ETH_P_EAPOL, rbuf, rlen, err192->key_mic);
 
 	return 0;
 }
@@ -126,6 +128,7 @@
 {
 	size_t rlen;
 	struct wpa_eapol_key *reply;
+	struct wpa_eapol_key_192 *reply192;
 	u8 *rbuf, *pos;
 	size_t kde_len;
 	u16 key_info;
@@ -140,6 +143,7 @@
 				  (void *) &reply);
 	if (rbuf == NULL)
 		return -1;
+	reply192 = (struct wpa_eapol_key_192 *) reply;
 
 	reply->type = EAPOL_KEY_TYPE_RSN;
 	key_info = ver | WPA_KEY_INFO_SMK_MESSAGE | WPA_KEY_INFO_MIC |
@@ -164,8 +168,8 @@
 	wpa_add_kde(pos, RSN_KEY_DATA_NONCE, peerkey->inonce, WPA_NONCE_LEN);
 
 	wpa_printf(MSG_DEBUG, "RSN: Sending EAPOL-Key SMK M3");
-	wpa_eapol_key_send(sm, sm->ptk.kck, ver, src_addr, ETH_P_EAPOL,
-			   rbuf, rlen, reply->key_mic);
+	wpa_eapol_key_send(sm, sm->ptk.kck, sm->ptk.kck_len, ver, src_addr,
+			   ETH_P_EAPOL, rbuf, rlen, reply192->key_mic);
 
 	return 0;
 }
@@ -240,11 +244,7 @@
 	os_memcpy(peerkey->rsnie_i, kde.rsn_ie, kde.rsn_ie_len);
 	peerkey->rsnie_i_len = kde.rsn_ie_len;
 	peerkey->cipher = cipher;
-#ifdef CONFIG_IEEE80211W
-	if (ie.key_mgmt & (WPA_KEY_MGMT_IEEE8021X_SHA256 |
-			   WPA_KEY_MGMT_PSK_SHA256))
-		peerkey->use_sha256 = 1;
-#endif /* CONFIG_IEEE80211W */
+	peerkey->akmp = ie.key_mgmt;
 
 	if (random_get_bytes(peerkey->pnonce, WPA_NONCE_LEN)) {
 		wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
@@ -288,14 +288,14 @@
  * @mac_p: Peer MAC address
  * @inonce: Initiator Nonce
  * @mac_i: Initiator MAC address
- * @use_sha256: Whether to use SHA256-based KDF
+ * @akmp: Negotiated AKM
  *
  * 8.5.1.4 Station to station (STK) key hierarchy
  * SMKID = HMAC-SHA1-128(SMK, "SMK Name" || PNonce || MAC_P || INonce || MAC_I)
  */
 static void rsn_smkid(const u8 *smk, const u8 *pnonce, const u8 *mac_p,
 		      const u8 *inonce, const u8 *mac_i, u8 *smkid,
-		      int use_sha256)
+		      int akmp)
 {
 	char *title = "SMK Name";
 	const u8 *addr[5];
@@ -310,7 +310,7 @@
 	addr[4] = mac_i;
 
 #ifdef CONFIG_IEEE80211W
-	if (use_sha256)
+	if (wpa_key_mgmt_sha256(akmp))
 		hmac_sha256_vector(smk, PMK_LEN, 5, addr, len, hash);
 	else
 #endif /* CONFIG_IEEE80211W */
@@ -371,7 +371,7 @@
 
 	wpa_printf(MSG_DEBUG, "RSN: Sending EAPOL-Key STK 1/4 to " MACSTR,
 		   MAC2STR(peerkey->addr));
-	wpa_eapol_key_send(sm, NULL, ver, peerkey->addr, ETH_P_EAPOL,
+	wpa_eapol_key_send(sm, NULL, 0, ver, peerkey->addr, ETH_P_EAPOL,
 			   mbuf, mlen, NULL);
 }
 
@@ -426,8 +426,9 @@
 
 	wpa_printf(MSG_DEBUG, "RSN: Sending EAPOL-Key STK 3/4 to " MACSTR,
 		   MAC2STR(peerkey->addr));
-	wpa_eapol_key_send(sm, peerkey->stk.kck, ver, peerkey->addr,
-			   ETH_P_EAPOL, mbuf, mlen, msg->key_mic);
+	wpa_eapol_key_send(sm, peerkey->stk.kck, peerkey->stk.kck_len, ver,
+			   peerkey->addr, ETH_P_EAPOL, mbuf, mlen,
+			   msg->key_mic);
 }
 
 
@@ -575,12 +576,12 @@
 	if (peerkey->initiator) {
 		rsn_smkid(peerkey->smk, peerkey->pnonce, peerkey->addr,
 			  peerkey->inonce, sm->own_addr, peerkey->smkid,
-			  peerkey->use_sha256);
+			  peerkey->akmp);
 		wpa_supplicant_send_stk_1_of_4(sm, peerkey);
 	} else {
 		rsn_smkid(peerkey->smk, peerkey->pnonce, sm->own_addr,
 			  peerkey->inonce, peerkey->addr, peerkey->smkid,
-			  peerkey->use_sha256);
+			  peerkey->akmp);
 	}
 	wpa_hexdump(MSG_DEBUG, "RSN: SMKID", peerkey->smkid, PMKID_LEN);
 
@@ -694,12 +695,11 @@
 	wpa_pmk_to_ptk(peerkey->smk, PMK_LEN, "Peer key expansion",
 		       sm->own_addr, peerkey->addr,
 		       peerkey->pnonce, key->key_nonce,
-		       (u8 *) stk, sizeof(*stk),
-		       peerkey->use_sha256);
+		       stk, peerkey->akmp, peerkey->cipher);
 	/* Supplicant: swap tx/rx Mic keys */
-	os_memcpy(buf, stk->u.auth.tx_mic_key, 8);
-	os_memcpy(stk->u.auth.tx_mic_key, stk->u.auth.rx_mic_key, 8);
-	os_memcpy(stk->u.auth.rx_mic_key, buf, 8);
+	os_memcpy(buf, &stk->tk[16], 8);
+	os_memcpy(&stk->tk[16], &stk->tk[24], 8);
+	os_memcpy(&stk->tk[24], buf, 8);
 	peerkey->tstk_set = 1;
 
 	kde_buf_len = peerkey->rsnie_p_len +
@@ -855,12 +855,12 @@
 				       &peerkey->stk))
 		return;
 
-	_key = (u8 *) peerkey->stk.tk1;
+	_key = peerkey->stk.tk;
 	if (peerkey->cipher == WPA_CIPHER_TKIP) {
 		/* Swap Tx/Rx keys for Michael MIC */
 		os_memcpy(key_buf, _key, 16);
-		os_memcpy(key_buf + 16, peerkey->stk.u.auth.rx_mic_key, 8);
-		os_memcpy(key_buf + 24, peerkey->stk.u.auth.tx_mic_key, 8);
+		os_memcpy(key_buf + 16, _key + 24, 8);
+		os_memcpy(key_buf + 24, _key + 16, 8);
 		_key = key_buf;
 		key_len = 32;
 	} else
@@ -869,10 +869,12 @@
 	os_memset(rsc, 0, 6);
 	if (wpa_sm_set_key(sm, peerkey->cipher, peerkey->addr, 0, 1,
 			   rsc, sizeof(rsc), _key, key_len) < 0) {
+		os_memset(key_buf, 0, sizeof(key_buf));
 		wpa_printf(MSG_WARNING, "RSN: Failed to set STK to the "
 			   "driver.");
 		return;
 	}
+	os_memset(key_buf, 0, sizeof(key_buf));
 }
 
 
@@ -888,7 +890,7 @@
 
 	os_memset(rsc, 0, 6);
 	if (wpa_sm_set_key(sm, peerkey->cipher, peerkey->addr, 0, 1,
-			   rsc, sizeof(rsc), (u8 *) peerkey->stk.tk1,
+			   rsc, sizeof(rsc), peerkey->stk.tk,
 			   peerkey->cipher == WPA_CIPHER_TKIP ? 32 : 16) < 0) {
 		wpa_printf(MSG_WARNING, "RSN: Failed to set STK to the "
 			   "driver.");
@@ -909,27 +911,27 @@
  */
 int peerkey_verify_eapol_key_mic(struct wpa_sm *sm,
 				 struct wpa_peerkey *peerkey,
-				 struct wpa_eapol_key *key, u16 ver,
+				 struct wpa_eapol_key_192 *key, u16 ver,
 				 const u8 *buf, size_t len)
 {
-	u8 mic[16];
+	u8 mic[WPA_EAPOL_KEY_MIC_MAX_LEN];
+	size_t mic_len = 16;
 	int ok = 0;
 
 	if (peerkey->initiator && !peerkey->stk_set) {
 		wpa_pmk_to_ptk(peerkey->smk, PMK_LEN, "Peer key expansion",
 			       sm->own_addr, peerkey->addr,
 			       peerkey->inonce, key->key_nonce,
-			       (u8 *) &peerkey->stk, sizeof(peerkey->stk),
-			       peerkey->use_sha256);
+			       &peerkey->stk, peerkey->akmp, peerkey->cipher);
 		peerkey->stk_set = 1;
 	}
 
-	os_memcpy(mic, key->key_mic, 16);
+	os_memcpy(mic, key->key_mic, mic_len);
 	if (peerkey->tstk_set) {
-		os_memset(key->key_mic, 0, 16);
-		wpa_eapol_key_mic(peerkey->tstk.kck, ver, buf, len,
-				  key->key_mic);
-		if (os_memcmp_const(mic, key->key_mic, 16) != 0) {
+		os_memset(key->key_mic, 0, mic_len);
+		wpa_eapol_key_mic(peerkey->tstk.kck, peerkey->tstk.kck_len,
+				  sm->key_mgmt, ver, buf, len, key->key_mic);
+		if (os_memcmp_const(mic, key->key_mic, mic_len) != 0) {
 			wpa_printf(MSG_WARNING, "RSN: Invalid EAPOL-Key MIC "
 				   "when using TSTK - ignoring TSTK");
 		} else {
@@ -938,14 +940,15 @@
 			peerkey->stk_set = 1;
 			os_memcpy(&peerkey->stk, &peerkey->tstk,
 				  sizeof(peerkey->stk));
+			os_memset(&peerkey->tstk, 0, sizeof(peerkey->tstk));
 		}
 	}
 
 	if (!ok && peerkey->stk_set) {
-		os_memset(key->key_mic, 0, 16);
-		wpa_eapol_key_mic(peerkey->stk.kck, ver, buf, len,
-				  key->key_mic);
-		if (os_memcmp_const(mic, key->key_mic, 16) != 0) {
+		os_memset(key->key_mic, 0, mic_len);
+		wpa_eapol_key_mic(peerkey->stk.kck, peerkey->stk.kck_len,
+				  sm->key_mgmt, ver, buf, len, key->key_mic);
+		if (os_memcmp_const(mic, key->key_mic, mic_len) != 0) {
 			wpa_printf(MSG_WARNING, "RSN: Invalid EAPOL-Key MIC "
 				   "- dropping packet");
 			return -1;
@@ -1014,10 +1017,7 @@
 		return -1;
 	peerkey->initiator = 1;
 	os_memcpy(peerkey->addr, peer, ETH_ALEN);
-#ifdef CONFIG_IEEE80211W
-	if (wpa_key_mgmt_sha256(sm->key_mgmt))
-		peerkey->use_sha256 = 1;
-#endif /* CONFIG_IEEE80211W */
+	peerkey->akmp = sm->key_mgmt;
 
 	/* SMK M1:
 	 * EAPOL-Key(S=1, M=1, A=0, I=0, K=0, SM=1, KeyRSC=0, Nonce=INonce,
@@ -1084,8 +1084,8 @@
 
 	wpa_printf(MSG_INFO, "RSN: Sending EAPOL-Key SMK M1 Request (peer "
 		   MACSTR ")", MAC2STR(peer));
-	wpa_eapol_key_send(sm, sm->ptk.kck, ver, bssid, ETH_P_EAPOL,
-			   rbuf, rlen, req->key_mic);
+	wpa_eapol_key_send(sm, sm->ptk.kck, sm->ptk.kck_len, ver, bssid,
+			   ETH_P_EAPOL, rbuf, rlen, req->key_mic);
 
 	peerkey->next = sm->peerkey;
 	sm->peerkey = peerkey;
diff --git a/src/rsn_supp/peerkey.h b/src/rsn_supp/peerkey.h
index 4c17eae..6ccd948 100644
--- a/src/rsn_supp/peerkey.h
+++ b/src/rsn_supp/peerkey.h
@@ -1,6 +1,6 @@
 /*
  * WPA Supplicant - PeerKey for Direct Link Setup (DLS)
- * Copyright (c) 2006-2008, 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,7 +27,7 @@
 	int cipher; /* Selected cipher (WPA_CIPHER_*) */
 	u8 replay_counter[WPA_REPLAY_COUNTER_LEN];
 	int replay_counter_set;
-	int use_sha256; /* whether AKMP indicate SHA256-based derivations */
+	int akmp;
 
 	struct wpa_ptk stk, tstk;
 	int stk_set, tstk_set;
@@ -38,7 +38,7 @@
 
 int peerkey_verify_eapol_key_mic(struct wpa_sm *sm,
 				 struct wpa_peerkey *peerkey,
-				 struct wpa_eapol_key *key, u16 ver,
+				 struct wpa_eapol_key_192 *key, u16 ver,
 				 const u8 *buf, size_t len);
 void peerkey_rx_eapol_4way(struct wpa_sm *sm, struct wpa_peerkey *peerkey,
 			   struct wpa_eapol_key *key, u16 key_info, u16 ver,
diff --git a/src/rsn_supp/pmksa_cache.c b/src/rsn_supp/pmksa_cache.c
index 885291a..ef7b683 100644
--- a/src/rsn_supp/pmksa_cache.c
+++ b/src/rsn_supp/pmksa_cache.c
@@ -1,6 +1,6 @@
 /*
  * WPA Supplicant - RSN PMKSA cache
- * Copyright (c) 2004-2009, 2011-2012, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2009, 2011-2015, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -109,6 +109,8 @@
  * @pmksa: Pointer to PMKSA cache data from pmksa_cache_init()
  * @pmk: The new pairwise master key
  * @pmk_len: PMK length in bytes, usually PMK_LEN (32)
+ * @kck: Key confirmation key or %NULL if not yet derived
+ * @kck_len: KCK length in bytes
  * @aa: Authenticator address
  * @spa: Supplicant address
  * @network_ctx: Network configuration context for this PMK
@@ -122,6 +124,7 @@
  */
 struct rsn_pmksa_cache_entry *
 pmksa_cache_add(struct rsn_pmksa_cache *pmksa, const u8 *pmk, size_t pmk_len,
+		const u8 *kck, size_t kck_len,
 		const u8 *aa, const u8 *spa, void *network_ctx, int akmp)
 {
 	struct rsn_pmksa_cache_entry *entry, *pos, *prev;
@@ -130,13 +133,21 @@
 	if (pmk_len > PMK_LEN)
 		return NULL;
 
+	if (wpa_key_mgmt_suite_b(akmp) && !kck)
+		return NULL;
+
 	entry = os_zalloc(sizeof(*entry));
 	if (entry == NULL)
 		return NULL;
 	os_memcpy(entry->pmk, pmk, pmk_len);
 	entry->pmk_len = pmk_len;
-	rsn_pmkid(pmk, pmk_len, aa, spa, entry->pmkid,
-		  wpa_key_mgmt_sha256(akmp));
+	if (akmp == WPA_KEY_MGMT_IEEE8021X_SUITE_B_192)
+		rsn_pmkid_suite_b_192(kck, kck_len, aa, spa, entry->pmkid);
+	else if (wpa_key_mgmt_suite_b(akmp))
+		rsn_pmkid_suite_b(kck, kck_len, aa, spa, entry->pmkid);
+	else
+		rsn_pmkid(pmk, pmk_len, aa, spa, entry->pmkid,
+			  wpa_key_mgmt_sha256(akmp));
 	os_get_reltime(&now);
 	entry->expiration = now.sec + pmksa->sm->dot11RSNAConfigPMKLifetime;
 	entry->reauth_time = now.sec + pmksa->sm->dot11RSNAConfigPMKLifetime *
@@ -333,6 +344,7 @@
 	struct rsn_pmksa_cache_entry *new_entry;
 
 	new_entry = pmksa_cache_add(pmksa, old_entry->pmk, old_entry->pmk_len,
+				    NULL, 0,
 				    aa, pmksa->sm->own_addr,
 				    old_entry->network_ctx, old_entry->akmp);
 	if (new_entry == NULL)
@@ -472,7 +484,7 @@
 	ret = os_snprintf(pos, buf + len - pos,
 			  "Index / AA / PMKID / expiration (in seconds) / "
 			  "opportunistic\n");
-	if (ret < 0 || ret >= buf + len - pos)
+	if (os_snprintf_error(buf + len - pos, ret))
 		return pos - buf;
 	pos += ret;
 	i = 0;
@@ -481,7 +493,7 @@
 		i++;
 		ret = os_snprintf(pos, buf + len - pos, "%d " MACSTR " ",
 				  i, MAC2STR(entry->aa));
-		if (ret < 0 || ret >= buf + len - pos)
+		if (os_snprintf_error(buf + len - pos, ret))
 			return pos - buf;
 		pos += ret;
 		pos += wpa_snprintf_hex(pos, buf + len - pos, entry->pmkid,
@@ -489,7 +501,7 @@
 		ret = os_snprintf(pos, buf + len - pos, " %d %d\n",
 				  (int) (entry->expiration - now.sec),
 				  entry->opportunistic);
-		if (ret < 0 || ret >= buf + len - pos)
+		if (os_snprintf_error(buf + len - pos, ret))
 			return pos - buf;
 		pos += ret;
 		entry = entry->next;
diff --git a/src/rsn_supp/pmksa_cache.h b/src/rsn_supp/pmksa_cache.h
index 6cbf89a..f8e040e 100644
--- a/src/rsn_supp/pmksa_cache.h
+++ b/src/rsn_supp/pmksa_cache.h
@@ -57,6 +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 *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);
@@ -104,6 +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 *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 915f85e..c6534af 100644
--- a/src/rsn_supp/preauth.c
+++ b/src/rsn_supp/preauth.c
@@ -1,6 +1,6 @@
 /*
  * RSN pre-authentication (supplicant)
- * 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.
@@ -94,6 +94,7 @@
 					pmk, pmk_len);
 			sm->pmk_len = pmk_len;
 			pmksa_cache_add(sm->pmksa, pmk, pmk_len,
+					NULL, 0,
 					sm->preauth_bssid, sm->own_addr,
 					sm->network_ctx,
 					WPA_KEY_MGMT_IEEE8021X);
@@ -171,6 +172,7 @@
 {
 	struct eapol_config eapol_conf;
 	struct eapol_ctx *ctx;
+	int ret;
 
 	if (sm->preauth_eapol)
 		return -1;
@@ -196,14 +198,16 @@
 			wpa_printf(MSG_WARNING, "RSN: Failed to initialize L2 "
 				   "packet processing (bridge) for "
 				   "pre-authentication");
-			return -2;
+			ret = -2;
+			goto fail;
 		}
 	}
 
 	ctx = os_zalloc(sizeof(*ctx));
 	if (ctx == NULL) {
 		wpa_printf(MSG_WARNING, "Failed to allocate EAPOL context.");
-		return -4;
+		ret = -4;
+		goto fail;
 	}
 	ctx->ctx = sm->ctx->ctx;
 	ctx->msg_ctx = sm->ctx->ctx;
@@ -221,7 +225,8 @@
 		os_free(ctx);
 		wpa_printf(MSG_WARNING, "RSN: Failed to initialize EAPOL "
 			   "state machines for pre-authentication");
-		return -3;
+		ret = -3;
+		goto fail;
 	}
 	os_memset(&eapol_conf, 0, sizeof(eapol_conf));
 	eapol_conf.accept_802_1x_keys = 0;
@@ -246,6 +251,15 @@
 			       rsn_preauth_timeout, sm, NULL);
 
 	return 0;
+
+fail:
+	if (sm->l2_preauth_br) {
+		l2_packet_deinit(sm->l2_preauth_br);
+		sm->l2_preauth_br = NULL;
+	}
+	l2_packet_deinit(sm->l2_preauth);
+	sm->l2_preauth = NULL;
+	return ret;
 }
 
 
@@ -298,7 +312,9 @@
 	    sm->proto != WPA_PROTO_RSN ||
 	    wpa_sm_get_state(sm) != WPA_COMPLETED ||
 	    (sm->key_mgmt != WPA_KEY_MGMT_IEEE8021X &&
-	     sm->key_mgmt != WPA_KEY_MGMT_IEEE8021X_SHA256)) {
+	     sm->key_mgmt != WPA_KEY_MGMT_IEEE8021X_SHA256 &&
+	     sm->key_mgmt != WPA_KEY_MGMT_IEEE8021X_SUITE_B &&
+	     sm->key_mgmt != WPA_KEY_MGMT_IEEE8021X_SUITE_B_192)) {
 		wpa_msg(sm->ctx->msg_ctx, MSG_DEBUG, "RSN: not in suitable "
 			"state for new pre-authentication");
 		return; /* invalid state for new pre-auth */
@@ -391,6 +407,18 @@
 	dl_list_for_each(pos, &sm->pmksa_candidates,
 			 struct rsn_pmksa_candidate, list) {
 		if (cand->priority <= pos->priority) {
+			if (!pos->list.prev) {
+				/*
+				 * This cannot really happen in pracrice since
+				 * pos was fetched from the list and the prev
+				 * pointer must be set. It looks like clang
+				 * static analyzer gets confused with the
+				 * dl_list_del(&cand->list) call above and ends
+				 * up assuming pos->list.prev could be NULL.
+				 */
+				os_free(cand);
+				return;
+			}
 			dl_list_add(pos->list.prev, &cand->list);
 			cand = NULL;
 			break;
@@ -487,7 +515,7 @@
 	if (sm->preauth_eapol) {
 		ret = os_snprintf(pos, end - pos, "Pre-authentication "
 				  "EAPOL state machines:\n");
-		if (ret < 0 || ret >= end - pos)
+		if (os_snprintf_error(end - pos, ret))
 			return pos - buf;
 		pos += ret;
 		res = eapol_sm_get_status(sm->preauth_eapol,
diff --git a/src/rsn_supp/tdls.c b/src/rsn_supp/tdls.c
index 8cb19a2..6b1df71 100644
--- a/src/rsn_supp/tdls.c
+++ b/src/rsn_supp/tdls.c
@@ -148,6 +148,9 @@
 	size_t supp_oper_classes_len;
 
 	u8 wmm_capable;
+
+	/* channel switch currently enabled */
+	int chan_switch_enabled;
 };
 
 
@@ -687,6 +690,7 @@
 	peer->qos_info = 0;
 	peer->wmm_capable = 0;
 	peer->tpk_set = peer->tpk_success = 0;
+	peer->chan_switch_enabled = 0;
 	os_memset(&peer->tpk, 0, sizeof(peer->tpk));
 	os_memset(peer->inonce, 0, WPA_NONCE_LEN);
 	os_memset(peer->rnonce, 0, WPA_NONCE_LEN);
@@ -742,6 +746,13 @@
 		return 0;
 	}
 
+	/* Cancel active channel switch before teardown */
+	if (peer->chan_switch_enabled) {
+		wpa_printf(MSG_DEBUG, "TDLS: First returning link with " MACSTR
+			   " to base channel", MAC2STR(addr));
+		wpa_sm_tdls_disable_channel_switch(sm, peer->addr);
+	}
+
 	dialog_token = peer->dtoken;
 
 	wpa_printf(MSG_DEBUG, "TDLS: TDLS Teardown for " MACSTR,
@@ -858,9 +869,11 @@
 
 	if (wpa_tdls_is_external_setup(sm)) {
 		/*
-		 * Disable the link, send a teardown packet through the
-		 * AP, and then reset link data.
+		 * Get us on the base channel, disable the link, send a
+		 * teardown packet through the AP, and then reset link data.
 		 */
+		if (peer->chan_switch_enabled)
+			wpa_sm_tdls_disable_channel_switch(sm, peer->addr);
 		wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK, addr);
 		wpa_tdls_send_teardown(sm, addr,
 				       WLAN_REASON_TDLS_TEARDOWN_UNREACHABLE);
@@ -926,10 +939,15 @@
 		   " (reason code %u)", MAC2STR(src_addr), reason_code);
 
 	ielen = len - (pos - buf); /* start of IE in buf */
-	if (wpa_supplicant_parse_ies((const u8 *) pos, ielen, &kde) < 0) {
-		wpa_printf(MSG_INFO, "TDLS: Failed to parse IEs in Teardown");
-		return -1;
-	}
+
+	/*
+	 * Don't reject the message if failing to parse IEs. The IEs we need are
+	 * explicitly checked below. Some APs may add arbitrary padding to the
+	 * end of short TDLS frames and that would look like invalid IEs.
+	 */
+	if (wpa_supplicant_parse_ies((const u8 *) pos, ielen, &kde) < 0)
+		wpa_printf(MSG_DEBUG,
+			   "TDLS: Failed to parse IEs in Teardown - ignore as an interop workaround");
 
 	if (kde.lnkid == NULL || kde.lnkid_len < 3 * ETH_ALEN) {
 		wpa_printf(MSG_INFO, "TDLS: No Link Identifier IE in TDLS "
@@ -1559,9 +1577,7 @@
 static int copy_peer_ht_capab(const struct wpa_eapol_ie_parse *kde,
 			      struct wpa_tdls_peer *peer)
 {
-	if (!kde->ht_capabilities ||
-	    kde->ht_capabilities_len <
-	    sizeof(struct ieee80211_ht_capabilities) ) {
+	if (!kde->ht_capabilities) {
 		wpa_printf(MSG_DEBUG, "TDLS: No supported ht capabilities "
 			   "received");
 		return 0;
@@ -1587,9 +1603,7 @@
 static int copy_peer_vht_capab(const struct wpa_eapol_ie_parse *kde,
 			      struct wpa_tdls_peer *peer)
 {
-	if (!kde->vht_capabilities ||
-	    kde->vht_capabilities_len <
-	    sizeof(struct ieee80211_vht_capabilities) ) {
+	if (!kde->vht_capabilities) {
 		wpa_printf(MSG_DEBUG, "TDLS: No supported vht capabilities "
 			   "received");
 		return 0;
@@ -1810,10 +1824,15 @@
 	cpos += 2;
 
 	ielen = len - (cpos - buf); /* start of IE in buf */
-	if (wpa_supplicant_parse_ies(cpos, ielen, &kde) < 0) {
-		wpa_printf(MSG_INFO, "TDLS: Failed to parse IEs in TPK M1");
-		goto error;
-	}
+
+	/*
+	 * Don't reject the message if failing to parse IEs. The IEs we need are
+	 * explicitly checked below. Some APs may add arbitrary padding to the
+	 * end of short TDLS frames and that would look like invalid IEs.
+	 */
+	if (wpa_supplicant_parse_ies(cpos, ielen, &kde) < 0)
+		wpa_printf(MSG_DEBUG,
+			   "TDLS: Failed to parse IEs in TPK M1 - ignore as an interop workaround");
 
 	if (kde.lnkid == NULL || kde.lnkid_len < 3 * ETH_ALEN) {
 		wpa_printf(MSG_INFO, "TDLS: No valid Link Identifier IE in "
@@ -2186,10 +2205,15 @@
 	pos += 2;
 
 	ielen = len - (pos - buf); /* start of IE in buf */
-	if (wpa_supplicant_parse_ies(pos, ielen, &kde) < 0) {
-		wpa_printf(MSG_INFO, "TDLS: Failed to parse IEs in TPK M2");
-		goto error;
-	}
+
+	/*
+	 * Don't reject the message if failing to parse IEs. The IEs we need are
+	 * explicitly checked below. Some APs may add arbitrary padding to the
+	 * end of short TDLS frames and that would look like invalid IEs.
+	 */
+	if (wpa_supplicant_parse_ies(pos, ielen, &kde) < 0)
+		wpa_printf(MSG_DEBUG,
+			   "TDLS: Failed to parse IEs in TPK M2 - ignore as an interop workaround");
 
 #ifdef CONFIG_TDLS_TESTING
 	if (tdls_testing & TDLS_TESTING_DECLINE_RESP) {
@@ -2742,7 +2766,8 @@
 	 * are assumed to perform everything internally
 	 */
 	if (wpa_sm_tdls_get_capa(sm, &sm->tdls_supported,
-				 &sm->tdls_external_setup) < 0) {
+				 &sm->tdls_external_setup,
+				 &sm->tdls_chan_switch) < 0) {
 		sm->tdls_supported = 1;
 		sm->tdls_external_setup = 0;
 	}
@@ -2751,6 +2776,8 @@
 		   "driver", sm->tdls_supported ? "" : " not");
 	wpa_printf(MSG_DEBUG, "TDLS: Driver uses %s link setup",
 		   sm->tdls_external_setup ? "external" : "internal");
+	wpa_printf(MSG_DEBUG, "TDLS: Driver %s TDLS channel switching",
+		   sm->tdls_chan_switch ? "supports" : "does not support");
 
 	return 0;
 }
@@ -2760,6 +2787,8 @@
 {
 	struct wpa_tdls_peer *peer, *tmp;
 
+	if (!sm)
+		return;
 	peer = sm->tdls;
 
 	wpa_printf(MSG_DEBUG, "TDLS: Tear down peers");
@@ -2830,39 +2859,61 @@
 }
 
 
-static int wpa_tdls_prohibited(const u8 *ies, size_t len)
+static int wpa_tdls_prohibited(struct wpa_eapol_ie_parse *elems)
 {
-	struct wpa_eapol_ie_parse elems;
+	/* bit 38 - TDLS Prohibited */
+	return !!(elems->ext_capab[2 + 4] & 0x40);
+}
 
-	if (ies == NULL)
-		return 0;
 
-	if (wpa_supplicant_parse_ies(ies, len, &elems) < 0)
-		return 0;
-
-	if (elems.ext_capab == NULL || elems.ext_capab_len < 2 + 5)
-		return 0;
-
-	 /* bit 38 - TDLS Prohibited */
-	return !!(elems.ext_capab[2 + 4] & 0x40);
+static int wpa_tdls_chan_switch_prohibited(struct wpa_eapol_ie_parse *elems)
+{
+	/* bit 39 - TDLS Channel Switch Prohibited */
+	return !!(elems->ext_capab[2 + 4] & 0x80);
 }
 
 
 void wpa_tdls_ap_ies(struct wpa_sm *sm, const u8 *ies, size_t len)
 {
-	sm->tdls_prohibited = wpa_tdls_prohibited(ies, len);
+	struct wpa_eapol_ie_parse elems;
+
+	sm->tdls_prohibited = 0;
+	sm->tdls_chan_switch_prohibited = 0;
+
+	if (ies == NULL || wpa_supplicant_parse_ies(ies, len, &elems) < 0 ||
+	    elems.ext_capab == NULL || elems.ext_capab_len < 2 + 5)
+		return;
+
+	sm->tdls_prohibited = wpa_tdls_prohibited(&elems);
 	wpa_printf(MSG_DEBUG, "TDLS: TDLS is %s in the target BSS",
 		   sm->tdls_prohibited ? "prohibited" : "allowed");
+	sm->tdls_chan_switch_prohibited =
+		wpa_tdls_chan_switch_prohibited(&elems);
+	wpa_printf(MSG_DEBUG, "TDLS: TDLS channel switch %s in the target BSS",
+		   sm->tdls_chan_switch_prohibited ? "prohibited" : "allowed");
 }
 
 
 void wpa_tdls_assoc_resp_ies(struct wpa_sm *sm, const u8 *ies, size_t len)
 {
-	if (!sm->tdls_prohibited && wpa_tdls_prohibited(ies, len)) {
+	struct wpa_eapol_ie_parse elems;
+
+	if (ies == NULL || wpa_supplicant_parse_ies(ies, len, &elems) < 0 ||
+	    elems.ext_capab == NULL || elems.ext_capab_len < 2 + 5)
+		return;
+
+	if (!sm->tdls_prohibited && wpa_tdls_prohibited(&elems)) {
 		wpa_printf(MSG_DEBUG, "TDLS: TDLS prohibited based on "
 			   "(Re)Association Response IEs");
 		sm->tdls_prohibited = 1;
 	}
+
+	if (!sm->tdls_chan_switch_prohibited &&
+	    wpa_tdls_chan_switch_prohibited(&elems)) {
+		wpa_printf(MSG_DEBUG,
+			   "TDLS: TDLS channel switch prohibited based on (Re)Association Response IEs");
+		sm->tdls_chan_switch_prohibited = 1;
+	}
 }
 
 
@@ -2877,3 +2928,78 @@
 {
 	return sm->tdls_external_setup;
 }
+
+
+int wpa_tdls_enable_chan_switch(struct wpa_sm *sm, const u8 *addr,
+				u8 oper_class,
+				struct hostapd_freq_params *freq_params)
+{
+	struct wpa_tdls_peer *peer;
+	int ret;
+
+	if (sm->tdls_disabled || !sm->tdls_supported)
+		return -1;
+
+	if (!sm->tdls_chan_switch) {
+		wpa_printf(MSG_DEBUG,
+			   "TDLS: Channel switching not supported by the driver");
+		return -1;
+	}
+
+	if (sm->tdls_chan_switch_prohibited) {
+		wpa_printf(MSG_DEBUG,
+			   "TDLS: Channel switching is prohibited in this BSS - reject request to switch channel");
+		return -1;
+	}
+
+	for (peer = sm->tdls; peer; peer = peer->next) {
+		if (os_memcmp(peer->addr, addr, ETH_ALEN) == 0)
+			break;
+	}
+
+	if (peer == NULL || !peer->tpk_success) {
+		wpa_printf(MSG_ERROR, "TDLS: Peer " MACSTR
+			   " not found for channel switching", MAC2STR(addr));
+		return -1;
+	}
+
+	if (peer->chan_switch_enabled) {
+		wpa_printf(MSG_DEBUG, "TDLS: Peer " MACSTR
+			   " already has channel switching enabled",
+			   MAC2STR(addr));
+		return 0;
+	}
+
+	ret = wpa_sm_tdls_enable_channel_switch(sm, peer->addr,
+						oper_class, freq_params);
+	if (!ret)
+		peer->chan_switch_enabled = 1;
+
+	return ret;
+}
+
+
+int wpa_tdls_disable_chan_switch(struct wpa_sm *sm, const u8 *addr)
+{
+	struct wpa_tdls_peer *peer;
+
+	if (sm->tdls_disabled || !sm->tdls_supported)
+		return -1;
+
+	for (peer = sm->tdls; peer; peer = peer->next) {
+		if (os_memcmp(peer->addr, addr, ETH_ALEN) == 0)
+			break;
+	}
+
+	if (!peer || !peer->chan_switch_enabled) {
+		wpa_printf(MSG_ERROR, "TDLS: Channel switching not enabled for "
+			   MACSTR, MAC2STR(addr));
+		return -1;
+	}
+
+	/* ignore the return value */
+	wpa_sm_tdls_disable_channel_switch(sm, peer->addr);
+
+	peer->chan_switch_enabled = 0;
+	return 0;
+}
diff --git a/src/rsn_supp/wpa.c b/src/rsn_supp/wpa.c
index 8f561b9..8adeef4 100644
--- a/src/rsn_supp/wpa.c
+++ b/src/rsn_supp/wpa.c
@@ -1,6 +1,6 @@
 /*
  * WPA Supplicant - WPA state machine and EAPOL-Key processing
- * 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.
@@ -27,6 +27,7 @@
  * wpa_eapol_key_send - Send WPA/RSN EAPOL-Key message
  * @sm: Pointer to WPA state machine data from wpa_sm_init()
  * @kck: Key Confirmation Key (KCK, part of PTK)
+ * @kck_len: KCK length in octets
  * @ver: Version field from Key Info
  * @dest: Destination address for the frame
  * @proto: Ethertype (usually ETH_P_EAPOL)
@@ -34,10 +35,12 @@
  * @msg_len: Length of message
  * @key_mic: Pointer to the buffer to which the EAPOL-Key MIC is written
  */
-void wpa_eapol_key_send(struct wpa_sm *sm, const u8 *kck,
+void wpa_eapol_key_send(struct wpa_sm *sm, const u8 *kck, size_t kck_len,
 			int ver, const u8 *dest, u16 proto,
 			u8 *msg, size_t msg_len, u8 *key_mic)
 {
+	size_t mic_len = wpa_mic_len(sm->key_mgmt);
+
 	if (is_zero_ether_addr(dest) && is_zero_ether_addr(sm->bssid)) {
 		/*
 		 * Association event was not yet received; try to fetch
@@ -56,14 +59,15 @@
 		}
 	}
 	if (key_mic &&
-	    wpa_eapol_key_mic(kck, ver, msg, msg_len, key_mic)) {
+	    wpa_eapol_key_mic(kck, kck_len, sm->key_mgmt, ver, msg, msg_len,
+			      key_mic)) {
 		wpa_msg(sm->ctx->msg_ctx, MSG_ERROR,
-			"WPA: Failed to generate EAPOL-Key "
-			"version %d MIC", ver);
+			"WPA: Failed to generate EAPOL-Key version %d key_mgmt 0x%x MIC",
+			ver, sm->key_mgmt);
 		goto out;
 	}
-	wpa_hexdump_key(MSG_DEBUG, "WPA: KCK", kck, 16);
-	wpa_hexdump(MSG_DEBUG, "WPA: Derived Key MIC", key_mic, 16);
+	wpa_hexdump_key(MSG_DEBUG, "WPA: KCK", kck, kck_len);
+	wpa_hexdump(MSG_DEBUG, "WPA: Derived Key MIC", key_mic, mic_len);
 	wpa_hexdump(MSG_MSGDUMP, "WPA: TX EAPOL-Key", msg, msg_len);
 	wpa_sm_ether_send(sm, dest, proto, msg, msg_len);
 	eapol_sm_notify_tx_eapol_key(sm->eapol);
@@ -84,12 +88,14 @@
  */
 void wpa_sm_key_request(struct wpa_sm *sm, int error, int pairwise)
 {
-	size_t rlen;
+	size_t mic_len, hdrlen, rlen;
 	struct wpa_eapol_key *reply;
+	struct wpa_eapol_key_192 *reply192;
 	int key_info, ver;
-	u8 bssid[ETH_ALEN], *rbuf;
+	u8 bssid[ETH_ALEN], *rbuf, *key_mic;
 
-	if (sm->key_mgmt == WPA_KEY_MGMT_OSEN)
+	if (sm->key_mgmt == WPA_KEY_MGMT_OSEN ||
+	    wpa_key_mgmt_suite_b(sm->key_mgmt))
 		ver = WPA_KEY_INFO_TYPE_AKM_DEFINED;
 	else if (wpa_key_mgmt_ft(sm->key_mgmt) ||
 		 wpa_key_mgmt_sha256(sm->key_mgmt))
@@ -105,10 +111,13 @@
 		return;
 	}
 
+	mic_len = wpa_mic_len(sm->key_mgmt);
+	hdrlen = mic_len == 24 ? sizeof(*reply192) : sizeof(*reply);
 	rbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY, NULL,
-				  sizeof(*reply), &rlen, (void *) &reply);
+				  hdrlen, &rlen, (void *) &reply);
 	if (rbuf == NULL)
 		return;
+	reply192 = (struct wpa_eapol_key_192 *) reply;
 
 	reply->type = (sm->proto == WPA_PROTO_RSN ||
 		       sm->proto == WPA_PROTO_OSEN) ?
@@ -126,15 +135,39 @@
 		  WPA_REPLAY_COUNTER_LEN);
 	inc_byte_array(sm->request_counter, WPA_REPLAY_COUNTER_LEN);
 
-	WPA_PUT_BE16(reply->key_data_length, 0);
+	if (mic_len == 24)
+		WPA_PUT_BE16(reply192->key_data_length, 0);
+	else
+		WPA_PUT_BE16(reply->key_data_length, 0);
+	if (!(key_info & WPA_KEY_INFO_MIC))
+		key_mic = NULL;
+	else
+		key_mic = reply192->key_mic; /* same offset in reply */
 
 	wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
 		"WPA: Sending EAPOL-Key Request (error=%d "
 		"pairwise=%d ptk_set=%d len=%lu)",
 		error, pairwise, sm->ptk_set, (unsigned long) rlen);
-	wpa_eapol_key_send(sm, sm->ptk.kck, ver, bssid, ETH_P_EAPOL,
-			   rbuf, rlen, key_info & WPA_KEY_INFO_MIC ?
-			   reply->key_mic : NULL);
+	wpa_eapol_key_send(sm, sm->ptk.kck, sm->ptk.kck_len, ver, bssid,
+			   ETH_P_EAPOL, rbuf, rlen, key_mic);
+}
+
+
+static void wpa_supplicant_key_mgmt_set_pmk(struct wpa_sm *sm)
+{
+#ifdef CONFIG_IEEE80211R
+	if (sm->key_mgmt == WPA_KEY_MGMT_FT_IEEE8021X) {
+		if (wpa_sm_key_mgmt_set_pmk(sm, sm->xxkey, sm->xxkey_len))
+			wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+				"RSN: Cannot set low order 256 bits of MSK for key management offload");
+	} else {
+#endif /* CONFIG_IEEE80211R */
+		if (wpa_sm_key_mgmt_set_pmk(sm, sm->pmk, sm->pmk_len))
+			wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+				"RSN: Cannot set PMK for key management offload");
+#ifdef CONFIG_IEEE80211R
+	}
+#endif /* CONFIG_IEEE80211R */
 }
 
 
@@ -198,10 +231,13 @@
 			wpa_hexdump_key(MSG_DEBUG, "WPA: PMK from EAPOL state "
 					"machines", sm->pmk, pmk_len);
 			sm->pmk_len = pmk_len;
+			wpa_supplicant_key_mgmt_set_pmk(sm);
 			if (sm->proto == WPA_PROTO_RSN &&
+			    !wpa_key_mgmt_suite_b(sm->key_mgmt) &&
 			    !wpa_key_mgmt_ft(sm->key_mgmt)) {
 				sa = pmksa_cache_add(sm->pmksa,
 						     sm->pmk, pmk_len,
+						     NULL, 0,
 						     src_addr, sm->own_addr,
 						     sm->network_ctx,
 						     sm->key_mgmt);
@@ -235,6 +271,7 @@
 	}
 
 	if (abort_cached && wpa_key_mgmt_wpa_ieee8021x(sm->key_mgmt) &&
+	    !wpa_key_mgmt_suite_b(sm->key_mgmt) &&
 	    !wpa_key_mgmt_ft(sm->key_mgmt) && sm->key_mgmt != WPA_KEY_MGMT_OSEN)
 	{
 		/* Send EAPOL-Start to trigger full EAP authentication. */
@@ -278,9 +315,10 @@
 			       const u8 *wpa_ie, size_t wpa_ie_len,
 			       struct wpa_ptk *ptk)
 {
-	size_t rlen;
+	size_t mic_len, hdrlen, rlen;
 	struct wpa_eapol_key *reply;
-	u8 *rbuf;
+	struct wpa_eapol_key_192 *reply192;
+	u8 *rbuf, *key_mic;
 	u8 *rsn_ie_buf = NULL;
 
 	if (wpa_ie == NULL) {
@@ -322,13 +360,16 @@
 
 	wpa_hexdump(MSG_DEBUG, "WPA: WPA IE for msg 2/4", wpa_ie, wpa_ie_len);
 
+	mic_len = wpa_mic_len(sm->key_mgmt);
+	hdrlen = mic_len == 24 ? sizeof(*reply192) : sizeof(*reply);
 	rbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY,
-				  NULL, sizeof(*reply) + wpa_ie_len,
+				  NULL, hdrlen + wpa_ie_len,
 				  &rlen, (void *) &reply);
 	if (rbuf == NULL) {
 		os_free(rsn_ie_buf);
 		return -1;
 	}
+	reply192 = (struct wpa_eapol_key_192 *) reply;
 
 	reply->type = (sm->proto == WPA_PROTO_RSN ||
 		       sm->proto == WPA_PROTO_OSEN) ?
@@ -344,35 +385,38 @@
 	wpa_hexdump(MSG_DEBUG, "WPA: Replay Counter", reply->replay_counter,
 		    WPA_REPLAY_COUNTER_LEN);
 
-	WPA_PUT_BE16(reply->key_data_length, wpa_ie_len);
-	os_memcpy(reply + 1, wpa_ie, wpa_ie_len);
+	key_mic = reply192->key_mic; /* same offset for reply and reply192 */
+	if (mic_len == 24) {
+		WPA_PUT_BE16(reply192->key_data_length, wpa_ie_len);
+		os_memcpy(reply192 + 1, wpa_ie, wpa_ie_len);
+	} else {
+		WPA_PUT_BE16(reply->key_data_length, wpa_ie_len);
+		os_memcpy(reply + 1, wpa_ie, wpa_ie_len);
+	}
 	os_free(rsn_ie_buf);
 
 	os_memcpy(reply->key_nonce, nonce, WPA_NONCE_LEN);
 
 	wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Sending EAPOL-Key 2/4");
-	wpa_eapol_key_send(sm, ptk->kck, ver, dst, ETH_P_EAPOL,
-			   rbuf, rlen, reply->key_mic);
+	wpa_eapol_key_send(sm, ptk->kck, ptk->kck_len, ver, dst, ETH_P_EAPOL,
+			   rbuf, rlen, key_mic);
 
 	return 0;
 }
 
 
 static int wpa_derive_ptk(struct wpa_sm *sm, const unsigned char *src_addr,
-			  const struct wpa_eapol_key *key,
-			  struct wpa_ptk *ptk)
+			  const struct wpa_eapol_key *key, struct wpa_ptk *ptk)
 {
-	size_t ptk_len = wpa_cipher_key_len(sm->pairwise_cipher) + 32;
 #ifdef CONFIG_IEEE80211R
 	if (wpa_key_mgmt_ft(sm->key_mgmt))
-		return wpa_derive_ptk_ft(sm, src_addr, key, ptk, ptk_len);
+		return wpa_derive_ptk_ft(sm, src_addr, key, ptk);
 #endif /* CONFIG_IEEE80211R */
 
-	wpa_pmk_to_ptk(sm->pmk, sm->pmk_len, "Pairwise key expansion",
-		       sm->own_addr, sm->bssid, sm->snonce, key->key_nonce,
-		       (u8 *) ptk, ptk_len,
-		       wpa_key_mgmt_sha256(sm->key_mgmt));
-	return 0;
+	return wpa_pmk_to_ptk(sm->pmk, sm->pmk_len, "Pairwise key expansion",
+			      sm->own_addr, sm->bssid, sm->snonce,
+			      key->key_nonce, ptk, sm->key_mgmt,
+			      sm->pairwise_cipher);
 }
 
 
@@ -439,9 +483,9 @@
 	if (sm->pairwise_cipher == WPA_CIPHER_TKIP) {
 		u8 buf[8];
 		/* Supplicant: swap tx/rx Mic keys */
-		os_memcpy(buf, ptk->u.auth.tx_mic_key, 8);
-		os_memcpy(ptk->u.auth.tx_mic_key, ptk->u.auth.rx_mic_key, 8);
-		os_memcpy(ptk->u.auth.rx_mic_key, buf, 8);
+		os_memcpy(buf, &ptk->tk[16], 8);
+		os_memcpy(&ptk->tk[16], &ptk->tk[24], 8);
+		os_memcpy(&ptk->tk[24], buf, 8);
 		os_memset(buf, 0, sizeof(buf));
 	}
 	sm->tptk_set = 1;
@@ -578,7 +622,7 @@
 	}
 
 	if (wpa_sm_set_key(sm, alg, sm->bssid, 0, 1, key_rsc, rsclen,
-			   (u8 *) sm->ptk.tk1, keylen) < 0) {
+			   sm->ptk.tk, keylen) < 0) {
 		wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
 			"WPA: Failed to set PTK to the "
 			"driver (alg=%d keylen=%d bssid=" MACSTR ")",
@@ -586,6 +630,9 @@
 		return -1;
 	}
 
+	/* TK is not needed anymore in supplicant */
+	os_memset(sm->ptk.tk, 0, WPA_TK_MAX_LEN);
+
 	if (sm->wpa_ptk_rekey) {
 		eloop_cancel_timeout(wpa_sm_rekey_ptk, sm, NULL);
 		eloop_register_timeout(sm->wpa_ptk_rekey, 0, wpa_sm_rekey_ptk,
@@ -1035,14 +1082,18 @@
 			       u16 ver, u16 key_info,
 			       struct wpa_ptk *ptk)
 {
-	size_t rlen;
+	size_t mic_len, hdrlen, rlen;
 	struct wpa_eapol_key *reply;
-	u8 *rbuf;
+	struct wpa_eapol_key_192 *reply192;
+	u8 *rbuf, *key_mic;
 
+	mic_len = wpa_mic_len(sm->key_mgmt);
+	hdrlen = mic_len == 24 ? sizeof(*reply192) : sizeof(*reply);
 	rbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY, NULL,
-				  sizeof(*reply), &rlen, (void *) &reply);
+				  hdrlen, &rlen, (void *) &reply);
 	if (rbuf == NULL)
 		return -1;
+	reply192 = (struct wpa_eapol_key_192 *) reply;
 
 	reply->type = (sm->proto == WPA_PROTO_RSN ||
 		       sm->proto == WPA_PROTO_OSEN) ?
@@ -1057,11 +1108,15 @@
 	os_memcpy(reply->replay_counter, key->replay_counter,
 		  WPA_REPLAY_COUNTER_LEN);
 
-	WPA_PUT_BE16(reply->key_data_length, 0);
+	key_mic = reply192->key_mic; /* same offset for reply and reply192 */
+	if (mic_len == 24)
+		WPA_PUT_BE16(reply192->key_data_length, 0);
+	else
+		WPA_PUT_BE16(reply->key_data_length, 0);
 
 	wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Sending EAPOL-Key 4/4");
-	wpa_eapol_key_send(sm, ptk->kck, ver, dst, ETH_P_EAPOL,
-			   rbuf, rlen, reply->key_mic);
+	wpa_eapol_key_send(sm, ptk->kck, ptk->kck_len, ver, dst, ETH_P_EAPOL,
+			   rbuf, rlen, key_mic);
 
 	return 0;
 }
@@ -1178,6 +1233,18 @@
 	if (ie.gtk)
 		wpa_sm_set_rekey_offload(sm);
 
+	if (sm->proto == WPA_PROTO_RSN && wpa_key_mgmt_suite_b(sm->key_mgmt)) {
+		struct rsn_pmksa_cache_entry *sa;
+
+		sa = pmksa_cache_add(sm->pmksa, sm->pmk, sm->pmk_len,
+				     sm->ptk.kck, sm->ptk.kck_len,
+				     sm->bssid, sm->own_addr,
+				     sm->network_ctx, sm->key_mgmt);
+		if (!sm->cur_pmksa)
+			sm->cur_pmksa = sa;
+	}
+
+	sm->msg_3_of_4_ok = 1;
 	return;
 
 failed:
@@ -1242,8 +1309,9 @@
 					     u16 ver, struct wpa_gtk_data *gd)
 {
 	size_t maxkeylen;
+	u16 gtk_len;
 
-	gd->gtk_len = WPA_GET_BE16(key->key_length);
+	gtk_len = WPA_GET_BE16(key->key_length);
 	maxkeylen = key_data_len;
 	if (ver == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) {
 		if (maxkeylen < 8) {
@@ -1255,14 +1323,16 @@
 		maxkeylen -= 8;
 	}
 
-	if (wpa_supplicant_check_group_cipher(sm, sm->group_cipher,
-					      gd->gtk_len, maxkeylen,
+	if (gtk_len > maxkeylen ||
+	    wpa_supplicant_check_group_cipher(sm, sm->group_cipher,
+					      gtk_len, maxkeylen,
 					      &gd->key_rsc_len, &gd->alg))
 		return -1;
 
+	gd->gtk_len = gtk_len;
 	gd->keyidx = (key_info & WPA_KEY_INFO_KEY_INDEX_MASK) >>
 		WPA_KEY_INFO_KEY_INDEX_SHIFT;
-	if (ver == WPA_KEY_INFO_TYPE_HMAC_MD5_RC4) {
+	if (ver == WPA_KEY_INFO_TYPE_HMAC_MD5_RC4 && sm->ptk.kek_len == 16) {
 		u8 ek[32];
 		if (key_data_len > sizeof(gd->gtk)) {
 			wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
@@ -1271,7 +1341,7 @@
 			return -1;
 		}
 		os_memcpy(ek, key->key_iv, 16);
-		os_memcpy(ek + 16, sm->ptk.kek, 16);
+		os_memcpy(ek + 16, sm->ptk.kek, sm->ptk.kek_len);
 		os_memcpy(gd->gtk, key_data, key_data_len);
 		if (rc4_skip(ek, 32, 256, gd->gtk, key_data_len)) {
 			os_memset(ek, 0, sizeof(ek));
@@ -1295,8 +1365,8 @@
 				(unsigned long) maxkeylen);
 			return -1;
 		}
-		if (aes_unwrap(sm->ptk.kek, 16, maxkeylen / 8, key_data,
-			       gd->gtk)) {
+		if (aes_unwrap(sm->ptk.kek, sm->ptk.kek_len, maxkeylen / 8,
+			       key_data, gd->gtk)) {
 			wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
 				"WPA: AES unwrap failed - could not decrypt "
 				"GTK");
@@ -1317,14 +1387,18 @@
 				      const struct wpa_eapol_key *key,
 				      int ver, u16 key_info)
 {
-	size_t rlen;
+	size_t mic_len, hdrlen, rlen;
 	struct wpa_eapol_key *reply;
-	u8 *rbuf;
+	struct wpa_eapol_key_192 *reply192;
+	u8 *rbuf, *key_mic;
 
+	mic_len = wpa_mic_len(sm->key_mgmt);
+	hdrlen = mic_len == 24 ? sizeof(*reply192) : sizeof(*reply);
 	rbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY, NULL,
-				  sizeof(*reply), &rlen, (void *) &reply);
+				  hdrlen, &rlen, (void *) &reply);
 	if (rbuf == NULL)
 		return -1;
+	reply192 = (struct wpa_eapol_key_192 *) reply;
 
 	reply->type = (sm->proto == WPA_PROTO_RSN ||
 		       sm->proto == WPA_PROTO_OSEN) ?
@@ -1339,11 +1413,15 @@
 	os_memcpy(reply->replay_counter, key->replay_counter,
 		  WPA_REPLAY_COUNTER_LEN);
 
-	WPA_PUT_BE16(reply->key_data_length, 0);
+	key_mic = reply192->key_mic; /* same offset for reply and reply192 */
+	if (mic_len == 24)
+		WPA_PUT_BE16(reply192->key_data_length, 0);
+	else
+		WPA_PUT_BE16(reply->key_data_length, 0);
 
 	wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Sending EAPOL-Key 2/2");
-	wpa_eapol_key_send(sm, sm->ptk.kck, ver, sm->bssid, ETH_P_EAPOL,
-			   rbuf, rlen, reply->key_mic);
+	wpa_eapol_key_send(sm, sm->ptk.kck, sm->ptk.kck_len, ver, sm->bssid,
+			   ETH_P_EAPOL, rbuf, rlen, key_mic);
 
 	return 0;
 }
@@ -1359,6 +1437,12 @@
 	int rekey, ret;
 	struct wpa_gtk_data gd;
 
+	if (!sm->msg_3_of_4_ok) {
+		wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+			"WPA: Group Key Handshake started prior to completion of 4-way handshake");
+		goto failed;
+	}
+
 	os_memset(&gd, 0, sizeof(gd));
 
 	rekey = wpa_sm_get_state(sm) == WPA_COMPLETED;
@@ -1385,6 +1469,7 @@
 	if (wpa_supplicant_install_gtk(sm, &gd, key->key_rsc) ||
 	    wpa_supplicant_send_2_of_2(sm, key, ver, key_info))
 		goto failed;
+	os_memset(&gd, 0, sizeof(gd));
 
 	if (rekey) {
 		wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "WPA: Group rekeying "
@@ -1403,24 +1488,26 @@
 	return;
 
 failed:
+	os_memset(&gd, 0, sizeof(gd));
 	wpa_sm_deauthenticate(sm, WLAN_REASON_UNSPECIFIED);
 }
 
 
 static int wpa_supplicant_verify_eapol_key_mic(struct wpa_sm *sm,
-					       struct wpa_eapol_key *key,
+					       struct wpa_eapol_key_192 *key,
 					       u16 ver,
 					       const u8 *buf, size_t len)
 {
-	u8 mic[16];
+	u8 mic[WPA_EAPOL_KEY_MIC_MAX_LEN];
 	int ok = 0;
+	size_t mic_len = wpa_mic_len(sm->key_mgmt);
 
-	os_memcpy(mic, key->key_mic, 16);
+	os_memcpy(mic, key->key_mic, mic_len);
 	if (sm->tptk_set) {
-		os_memset(key->key_mic, 0, 16);
-		wpa_eapol_key_mic(sm->tptk.kck, ver, buf, len,
-				  key->key_mic);
-		if (os_memcmp_const(mic, key->key_mic, 16) != 0) {
+		os_memset(key->key_mic, 0, mic_len);
+		wpa_eapol_key_mic(sm->tptk.kck, sm->tptk.kck_len, sm->key_mgmt,
+				  ver, buf, len, key->key_mic);
+		if (os_memcmp_const(mic, key->key_mic, mic_len) != 0) {
 			wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
 				"WPA: Invalid EAPOL-Key MIC "
 				"when using TPTK - ignoring TPTK");
@@ -1434,10 +1521,10 @@
 	}
 
 	if (!ok && sm->ptk_set) {
-		os_memset(key->key_mic, 0, 16);
-		wpa_eapol_key_mic(sm->ptk.kck, ver, buf, len,
-				  key->key_mic);
-		if (os_memcmp_const(mic, key->key_mic, 16) != 0) {
+		os_memset(key->key_mic, 0, mic_len);
+		wpa_eapol_key_mic(sm->ptk.kck, sm->ptk.kck_len, sm->key_mgmt,
+				  ver, buf, len, key->key_mic);
+		if (os_memcmp_const(mic, key->key_mic, mic_len) != 0) {
 			wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
 				"WPA: Invalid EAPOL-Key MIC - "
 				"dropping packet");
@@ -1476,10 +1563,10 @@
 
 	/* 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) {
+	if (ver == WPA_KEY_INFO_TYPE_HMAC_MD5_RC4 && sm->ptk.kek_len == 16) {
 		u8 ek[32];
 		os_memcpy(ek, key->key_iv, 16);
-		os_memcpy(ek + 16, sm->ptk.kek, 16);
+		os_memcpy(ek + 16, sm->ptk.kek, sm->ptk.kek_len);
 		if (rc4_skip(ek, 32, 256, key_data, *key_data_len)) {
 			os_memset(ek, 0, sizeof(ek));
 			wpa_msg(sm->ctx->msg_ctx, MSG_ERROR,
@@ -1489,7 +1576,8 @@
 		os_memset(ek, 0, sizeof(ek));
 	} 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) {
+		   sm->key_mgmt == WPA_KEY_MGMT_OSEN ||
+		   wpa_key_mgmt_suite_b(sm->key_mgmt)) {
 		u8 *buf;
 		if (*key_data_len < 8 || *key_data_len % 8) {
 			wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
@@ -1504,7 +1592,7 @@
 				"WPA: No memory for AES-UNWRAP buffer");
 			return -1;
 		}
-		if (aes_unwrap(sm->ptk.kek, 16, *key_data_len / 8,
+		if (aes_unwrap(sm->ptk.kek, sm->ptk.kek_len, *key_data_len / 8,
 			       key_data, buf)) {
 			os_free(buf);
 			wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
@@ -1541,7 +1629,9 @@
 
 
 static void wpa_eapol_key_dump(struct wpa_sm *sm,
-			       const struct wpa_eapol_key *key)
+			       const struct wpa_eapol_key *key,
+			       unsigned int key_data_len,
+			       const u8 *mic, unsigned int mic_len)
 {
 #ifndef CONFIG_NO_STDOUT_DEBUG
 	u16 key_info = WPA_GET_BE16(key->key_info);
@@ -1563,15 +1653,14 @@
 		key_info & WPA_KEY_INFO_ENCR_KEY_DATA ? " Encr" : "");
 	wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
 		"  key_length=%u key_data_length=%u",
-		WPA_GET_BE16(key->key_length),
-		WPA_GET_BE16(key->key_data_length));
+		WPA_GET_BE16(key->key_length), key_data_len);
 	wpa_hexdump(MSG_DEBUG, "  replay_counter",
 		    key->replay_counter, WPA_REPLAY_COUNTER_LEN);
 	wpa_hexdump(MSG_DEBUG, "  key_nonce", key->key_nonce, WPA_NONCE_LEN);
 	wpa_hexdump(MSG_DEBUG, "  key_iv", key->key_iv, 16);
 	wpa_hexdump(MSG_DEBUG, "  key_rsc", key->key_rsc, 8);
 	wpa_hexdump(MSG_DEBUG, "  key_id (reserved)", key->key_id, 8);
-	wpa_hexdump(MSG_DEBUG, "  key_mic", key->key_mic, 16);
+	wpa_hexdump(MSG_DEBUG, "  key_mic", mic, mic_len);
 #endif /* CONFIG_NO_STDOUT_DEBUG */
 }
 
@@ -1598,22 +1687,27 @@
 	size_t plen, data_len, key_data_len;
 	const struct ieee802_1x_hdr *hdr;
 	struct wpa_eapol_key *key;
+	struct wpa_eapol_key_192 *key192;
 	u16 key_info, ver;
 	u8 *tmp = NULL;
 	int ret = -1;
 	struct wpa_peerkey *peerkey = NULL;
 	u8 *key_data;
+	size_t mic_len, keyhdrlen;
 
 #ifdef CONFIG_IEEE80211R
 	sm->ft_completed = 0;
 #endif /* CONFIG_IEEE80211R */
 
-	if (len < sizeof(*hdr) + sizeof(*key)) {
+	mic_len = wpa_mic_len(sm->key_mgmt);
+	keyhdrlen = mic_len == 24 ? sizeof(*key192) : sizeof(*key);
+
+	if (len < sizeof(*hdr) + keyhdrlen) {
 		wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
 			"WPA: EAPOL frame too short to be a WPA "
 			"EAPOL-Key (len %lu, expecting at least %lu)",
 			(unsigned long) len,
-			(unsigned long) sizeof(*hdr) + sizeof(*key));
+			(unsigned long) sizeof(*hdr) + keyhdrlen);
 		return 0;
 	}
 
@@ -1635,7 +1729,7 @@
 		goto out;
 	}
 	wpa_hexdump(MSG_MSGDUMP, "WPA: RX EAPOL-Key", buf, len);
-	if (plen > len - sizeof(*hdr) || plen < sizeof(*key)) {
+	if (plen > len - sizeof(*hdr) || plen < keyhdrlen) {
 		wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
 			"WPA: EAPOL frame payload size %lu "
 			"invalid (frame size %lu)",
@@ -1658,7 +1752,12 @@
 		goto out;
 	os_memcpy(tmp, buf, data_len);
 	key = (struct wpa_eapol_key *) (tmp + sizeof(struct ieee802_1x_hdr));
-	key_data = (u8 *) (key + 1);
+	key192 = (struct wpa_eapol_key_192 *)
+		(tmp + sizeof(struct ieee802_1x_hdr));
+	if (mic_len == 24)
+		key_data = (u8 *) (key192 + 1);
+	else
+		key_data = (u8 *) (key + 1);
 
 	if (key->type != EAPOL_KEY_TYPE_WPA && key->type != EAPOL_KEY_TYPE_RSN)
 	{
@@ -1668,14 +1767,18 @@
 		ret = 0;
 		goto out;
 	}
-	wpa_eapol_key_dump(sm, key);
 
-	key_data_len = WPA_GET_BE16(key->key_data_length);
-	if (key_data_len > plen - sizeof(struct wpa_eapol_key)) {
+	if (mic_len == 24)
+		key_data_len = WPA_GET_BE16(key192->key_data_length);
+	else
+		key_data_len = WPA_GET_BE16(key->key_data_length);
+	wpa_eapol_key_dump(sm, key, key_data_len, key192->key_mic, mic_len);
+
+	if (key_data_len > plen - keyhdrlen) {
 		wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "WPA: Invalid EAPOL-Key "
 			"frame - key_data overflow (%u > %u)",
 			(unsigned int) key_data_len,
-			(unsigned int) (plen - sizeof(struct wpa_eapol_key)));
+			(unsigned int) (plen - keyhdrlen));
 		goto out;
 	}
 
@@ -1687,6 +1790,7 @@
 	    ver != WPA_KEY_INFO_TYPE_AES_128_CMAC &&
 #endif /* CONFIG_IEEE80211R || CONFIG_IEEE80211W */
 	    ver != WPA_KEY_INFO_TYPE_HMAC_SHA1_AES &&
+	    !wpa_key_mgmt_suite_b(sm->key_mgmt) &&
 	    sm->key_mgmt != WPA_KEY_MGMT_OSEN) {
 		wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
 			"WPA: Unsupported EAPOL-Key descriptor version %d",
@@ -1702,6 +1806,14 @@
 		goto out;
 	}
 
+	if (wpa_key_mgmt_suite_b(sm->key_mgmt) &&
+	    ver != WPA_KEY_INFO_TYPE_AKM_DEFINED) {
+		wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+			"RSN: Unsupported EAPOL-Key descriptor version %d (expected AKM defined = 0)",
+			ver);
+		goto out;
+	}
+
 #ifdef CONFIG_IEEE80211R
 	if (wpa_key_mgmt_ft(sm->key_mgmt)) {
 		/* IEEE 802.11r uses a new key_info type (AES-128-CMAC). */
@@ -1715,7 +1827,8 @@
 #ifdef CONFIG_IEEE80211W
 	if (wpa_key_mgmt_sha256(sm->key_mgmt)) {
 		if (ver != WPA_KEY_INFO_TYPE_AES_128_CMAC &&
-		    sm->key_mgmt != WPA_KEY_MGMT_OSEN) {
+		    sm->key_mgmt != WPA_KEY_MGMT_OSEN &&
+		    !wpa_key_mgmt_suite_b(sm->key_mgmt)) {
 			wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
 				"WPA: AP did not use the "
 				"negotiated AES-128-CMAC");
@@ -1724,6 +1837,7 @@
 	} else
 #endif /* CONFIG_IEEE80211W */
 	if (sm->pairwise_cipher == WPA_CIPHER_CCMP &&
+	    !wpa_key_mgmt_suite_b(sm->key_mgmt) &&
 	    ver != WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) {
 		wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
 			"WPA: CCMP is used, but EAPOL-Key "
@@ -1743,6 +1857,7 @@
 		} else
 			goto out;
 	} else if (sm->pairwise_cipher == WPA_CIPHER_GCMP &&
+		   !wpa_key_mgmt_suite_b(sm->key_mgmt) &&
 		   ver != WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) {
 		wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
 			"WPA: GCMP is used, but EAPOL-Key "
@@ -1813,12 +1928,13 @@
 	}
 
 	if ((key_info & WPA_KEY_INFO_MIC) && !peerkey &&
-	    wpa_supplicant_verify_eapol_key_mic(sm, key, ver, tmp, data_len))
+	    wpa_supplicant_verify_eapol_key_mic(sm, key192, ver, tmp, data_len))
 		goto out;
 
 #ifdef CONFIG_PEERKEY
 	if ((key_info & WPA_KEY_INFO_MIC) && peerkey &&
-	    peerkey_verify_eapol_key_mic(sm, peerkey, key, ver, tmp, data_len))
+	    peerkey_verify_eapol_key_mic(sm, peerkey, key192, ver, tmp,
+					 data_len))
 		goto out;
 #endif /* CONFIG_PEERKEY */
 
@@ -1870,7 +1986,7 @@
 	ret = 1;
 
 out:
-	os_free(tmp);
+	bin_clear_free(tmp, data_len);
 	return ret;
 }
 
@@ -1906,6 +2022,10 @@
 			WPA_AUTH_KEY_MGMT_CCKM);
 	case WPA_KEY_MGMT_WPA_NONE:
 		return WPA_AUTH_KEY_MGMT_NONE;
+	case WPA_KEY_MGMT_IEEE8021X_SUITE_B:
+		return RSN_AUTH_KEY_MGMT_802_1X_SUITE_B;
+	case WPA_KEY_MGMT_IEEE8021X_SUITE_B_192:
+		return RSN_AUTH_KEY_MGMT_802_1X_SUITE_B_192;
 	default:
 		return 0;
 	}
@@ -1963,7 +2083,7 @@
 			  sm->dot11RSNAConfigPMKLifetime,
 			  sm->dot11RSNAConfigPMKReauthThreshold,
 			  sm->dot11RSNAConfigSATimeout);
-	if (ret < 0 || (size_t) ret >= buflen)
+	if (os_snprintf_error(buflen, ret))
 		return 0;
 	len = ret;
 
@@ -1990,7 +2110,7 @@
 		RSN_SUITE_ARG(wpa_cipher_to_suite(sm->proto,
 						  sm->group_cipher)),
 		sm->dot11RSNA4WayHandshakeFailures);
-	if (ret >= 0 && (size_t) ret < buflen)
+	if (!os_snprintf_error(buflen - len, ret))
 		len += ret;
 
 	return (int) len;
@@ -2088,6 +2208,7 @@
 	os_free(sm->assoc_wpa_ie);
 	os_free(sm->ap_wpa_ie);
 	os_free(sm->ap_rsn_ie);
+	wpa_sm_drop_sa(sm);
 	os_free(sm->ctx);
 	peerkey_deinit(sm);
 #ifdef CONFIG_IEEE80211R
@@ -2168,6 +2289,8 @@
  */
 void wpa_sm_notify_disassoc(struct wpa_sm *sm)
 {
+	eloop_cancel_timeout(wpa_sm_start_preauth, sm, NULL);
+	eloop_cancel_timeout(wpa_sm_rekey_ptk, sm, NULL);
 	peerkey_deinit(sm);
 	rsn_preauth_deinit(sm);
 	pmksa_cache_clear_current(sm);
@@ -2176,6 +2299,11 @@
 #ifdef CONFIG_TDLS
 	wpa_tdls_disassoc(sm);
 #endif /* CONFIG_TDLS */
+
+	/* Keys are not needed in the WPA state machine anymore */
+	wpa_sm_drop_sa(sm);
+
+	sm->msg_3_of_4_ok = 0;
 }
 
 
@@ -2184,10 +2312,12 @@
  * @sm: Pointer to WPA state machine data from wpa_sm_init()
  * @pmk: The new PMK
  * @pmk_len: The length of the new PMK in bytes
+ * @bssid: AA to add into PMKSA cache or %NULL to not cache the PMK
  *
  * Configure the PMK for WPA state machine.
  */
-void wpa_sm_set_pmk(struct wpa_sm *sm, const u8 *pmk, size_t pmk_len)
+void wpa_sm_set_pmk(struct wpa_sm *sm, const u8 *pmk, size_t pmk_len,
+		    const u8 *bssid)
 {
 	if (sm == NULL)
 		return;
@@ -2200,6 +2330,12 @@
 	sm->xxkey_len = pmk_len;
 	os_memcpy(sm->xxkey, pmk, pmk_len);
 #endif /* CONFIG_IEEE80211R */
+
+	if (bssid) {
+		pmksa_cache_add(sm->pmksa, pmk, pmk_len, NULL, 0,
+				bssid, sm->own_addr,
+				sm->network_ctx, sm->key_mgmt);
+	}
 }
 
 
@@ -2424,7 +2560,7 @@
 			  wpa_cipher_txt(sm->pairwise_cipher),
 			  wpa_cipher_txt(sm->group_cipher),
 			  wpa_key_mgmt_txt(sm->key_mgmt, sm->proto));
-	if (ret < 0 || ret >= end - pos)
+	if (os_snprintf_error(end - pos, ret))
 		return pos - buf;
 	pos += ret;
 
@@ -2437,7 +2573,7 @@
 			ret = os_snprintf(pos, end - pos, "pmf=%d\n",
 					  (rsn.capabilities &
 					   WPA_CAPABILITY_MFPR) ? 2 : 1);
-			if (ret < 0 || ret >= end - pos)
+			if (os_snprintf_error(end - pos, ret))
 				return pos - buf;
 			pos += ret;
 		}
@@ -2640,7 +2776,6 @@
 }
 
 
-#ifdef CONFIG_TESTING_OPTIONS
 void wpa_sm_drop_sa(struct wpa_sm *sm)
 {
 	wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Clear old PMK and PTK");
@@ -2649,8 +2784,12 @@
 	os_memset(sm->pmk, 0, sizeof(sm->pmk));
 	os_memset(&sm->ptk, 0, sizeof(sm->ptk));
 	os_memset(&sm->tptk, 0, sizeof(sm->tptk));
+#ifdef CONFIG_IEEE80211R
+	os_memset(sm->xxkey, 0, sizeof(sm->xxkey));
+	os_memset(sm->pmk_r0, 0, sizeof(sm->pmk_r0));
+	os_memset(sm->pmk_r1, 0, sizeof(sm->pmk_r1));
+#endif /* CONFIG_IEEE80211R */
 }
-#endif /* CONFIG_TESTING_OPTIONS */
 
 
 int wpa_sm_has_ptk(struct wpa_sm *sm)
@@ -2783,3 +2922,33 @@
 }
 
 #endif /* CONFIG_P2P */
+
+
+void wpa_sm_set_rx_replay_ctr(struct wpa_sm *sm, const u8 *rx_replay_counter)
+{
+	if (rx_replay_counter == NULL)
+		return;
+
+	os_memcpy(sm->rx_replay_counter, rx_replay_counter,
+		  WPA_REPLAY_COUNTER_LEN);
+	sm->rx_replay_counter_set = 1;
+	wpa_printf(MSG_DEBUG, "Updated key replay counter");
+}
+
+
+void wpa_sm_set_ptk_kck_kek(struct wpa_sm *sm,
+			    const u8 *ptk_kck, size_t ptk_kck_len,
+			    const u8 *ptk_kek, size_t ptk_kek_len)
+{
+	if (ptk_kck && ptk_kck_len <= WPA_KCK_MAX_LEN) {
+		os_memcpy(sm->ptk.kck, ptk_kck, ptk_kck_len);
+		sm->ptk.kck_len = ptk_kck_len;
+		wpa_printf(MSG_DEBUG, "Updated PTK KCK");
+	}
+	if (ptk_kek && ptk_kek_len <= WPA_KEK_MAX_LEN) {
+		os_memcpy(sm->ptk.kek, ptk_kek, ptk_kek_len);
+		sm->ptk.kek_len = ptk_kek_len;
+		wpa_printf(MSG_DEBUG, "Updated PTK KEK");
+	}
+	sm->ptk_set = 1;
+}
diff --git a/src/rsn_supp/wpa.h b/src/rsn_supp/wpa.h
index 63032b0..e163b70 100644
--- a/src/rsn_supp/wpa.h
+++ b/src/rsn_supp/wpa.h
@@ -1,6 +1,6 @@
 /*
  * wpa_supplicant - WPA definitions
- * Copyright (c) 2003-2007, 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.
@@ -17,6 +17,7 @@
 struct wpa_sm;
 struct eapol_sm;
 struct wpa_config_blob;
+struct hostapd_freq_params;
 
 struct wpa_sm_ctx {
 	void *ctx; /* pointer to arbitrary upper level context */
@@ -51,7 +52,7 @@
 	int (*mark_authenticated)(void *ctx, const u8 *target_ap);
 #ifdef CONFIG_TDLS
 	int (*tdls_get_capa)(void *ctx, int *tdls_supported,
-			     int *tdls_ext_setup);
+			     int *tdls_ext_setup, int *tdls_chan_switch);
 	int (*send_tdls_mgmt)(void *ctx, const u8 *dst,
 			      u8 action_code, u8 dialog_token,
 			      u16 status_code, u32 peer_capab,
@@ -67,9 +68,15 @@
 				size_t supp_channels_len,
 				const u8 *supp_oper_classes,
 				size_t supp_oper_classes_len);
+	int (*tdls_enable_channel_switch)(
+		void *ctx, const u8 *addr, u8 oper_class,
+		const struct hostapd_freq_params *params);
+	int (*tdls_disable_channel_switch)(void *ctx, const u8 *addr);
 #endif /* CONFIG_TDLS */
-	void (*set_rekey_offload)(void *ctx, const u8 *kek, const u8 *kck,
+	void (*set_rekey_offload)(void *ctx, const u8 *kek, size_t kek_len,
+				  const u8 *kck, size_t kck_len,
 				  const u8 *replay_ctr);
+	int (*key_mgmt_set_pmk)(void *ctx, const u8 *pmk, size_t pmk_len);
 };
 
 
@@ -105,7 +112,8 @@
 void wpa_sm_deinit(struct wpa_sm *sm);
 void wpa_sm_notify_assoc(struct wpa_sm *sm, const u8 *bssid);
 void wpa_sm_notify_disassoc(struct wpa_sm *sm);
-void wpa_sm_set_pmk(struct wpa_sm *sm, const u8 *pmk, size_t pmk_len);
+void wpa_sm_set_pmk(struct wpa_sm *sm, const u8 *pmk, size_t pmk_len,
+		    const u8 *bssid);
 void wpa_sm_set_pmk_from_pmksa(struct wpa_sm *sm);
 void wpa_sm_set_fast_reauth(struct wpa_sm *sm, int fast_reauth);
 void wpa_sm_set_scard_ctx(struct wpa_sm *sm, void *scard_ctx);
@@ -147,6 +155,11 @@
 
 int wpa_sm_get_p2p_ip_addr(struct wpa_sm *sm, u8 *buf);
 
+void wpa_sm_set_rx_replay_ctr(struct wpa_sm *sm, const u8 *rx_replay_counter);
+void wpa_sm_set_ptk_kck_kek(struct wpa_sm *sm,
+			    const u8 *ptk_kck, size_t ptk_kck_len,
+			    const u8 *ptk_kek, size_t ptk_kek_len);
+
 #else /* CONFIG_NO_WPA */
 
 static inline struct wpa_sm * wpa_sm_init(struct wpa_sm_ctx *ctx)
@@ -301,6 +314,16 @@
 {
 }
 
+static inline void wpa_sm_set_rx_replay_ctr(struct wpa_sm *sm,
+					    const u8 *rx_replay_counter)
+{
+}
+
+static inline void wpa_sm_set_ptk_kck_kek(struct wpa_sm *sm, const u8 *ptk_kck,
+					  const u8 *ptk_kek)
+{
+}
+
 #endif /* CONFIG_NO_WPA */
 
 #ifdef CONFIG_PEERKEY
@@ -388,6 +411,10 @@
 void wpa_tdls_disable_unreachable_link(struct wpa_sm *sm, const u8 *addr);
 const char * wpa_tdls_get_link_status(struct wpa_sm *sm, const u8 *addr);
 int wpa_tdls_is_external_setup(struct wpa_sm *sm);
+int wpa_tdls_enable_chan_switch(struct wpa_sm *sm, const u8 *addr,
+				u8 oper_class,
+				struct hostapd_freq_params *freq_params);
+int wpa_tdls_disable_chan_switch(struct wpa_sm *sm, const u8 *addr);
 
 int wpa_wnmsleep_install_key(struct wpa_sm *sm, u8 subelem_id, u8 *buf);
 
diff --git a/src/rsn_supp/wpa_ft.c b/src/rsn_supp/wpa_ft.c
index 3b3c9d0..06dea05 100644
--- a/src/rsn_supp/wpa_ft.c
+++ b/src/rsn_supp/wpa_ft.c
@@ -1,6 +1,6 @@
 /*
  * WPA Supplicant - IEEE 802.11r - Fast BSS Transition
- * Copyright (c) 2006-2007, 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.
@@ -19,8 +19,7 @@
 #ifdef CONFIG_IEEE80211R
 
 int wpa_derive_ptk_ft(struct wpa_sm *sm, const unsigned char *src_addr,
-		      const struct wpa_eapol_key *key,
-		      struct wpa_ptk *ptk, size_t ptk_len)
+		      const struct wpa_eapol_key *key, struct wpa_ptk *ptk)
 {
 	u8 ptk_name[WPA_PMK_NAME_LEN];
 	const u8 *anonce = key->key_nonce;
@@ -43,13 +42,9 @@
 	wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", sm->pmk_r1, PMK_LEN);
 	wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name", sm->pmk_r1_name,
 		    WPA_PMK_NAME_LEN);
-	wpa_pmk_r1_to_ptk(sm->pmk_r1, sm->snonce, anonce, sm->own_addr,
-			  sm->bssid, sm->pmk_r1_name,
-			  (u8 *) ptk, ptk_len, ptk_name);
-	wpa_hexdump_key(MSG_DEBUG, "FT: PTK", (u8 *) ptk, ptk_len);
-	wpa_hexdump(MSG_DEBUG, "FT: PTKName", ptk_name, WPA_PMK_NAME_LEN);
-
-	return 0;
+	return wpa_pmk_r1_to_ptk(sm->pmk_r1, sm->snonce, anonce, sm->own_addr,
+				 sm->bssid, sm->pmk_r1_name, ptk, ptk_name,
+				 sm->key_mgmt, sm->pairwise_cipher);
 }
 
 
@@ -134,6 +129,7 @@
  * @anonce: ANonce or %NULL if not yet available
  * @pmk_name: PMKR0Name or PMKR1Name to be added into the RSN IE PMKID List
  * @kck: 128-bit KCK for MIC or %NULL if no MIC is used
+ * @kck_len: KCK length in octets
  * @target_ap: Target AP address
  * @ric_ies: Optional IE(s), e.g., WMM TSPEC(s), for RIC-Request or %NULL
  * @ric_ies_len: Length of ric_ies buffer in octets
@@ -144,7 +140,8 @@
  */
 static u8 * wpa_ft_gen_req_ies(struct wpa_sm *sm, size_t *len,
 			       const u8 *anonce, const u8 *pmk_name,
-			       const u8 *kck, const u8 *target_ap,
+			       const u8 *kck, size_t kck_len,
+			       const u8 *target_ap,
 			       const u8 *ric_ies, size_t ric_ies_len,
 			       const u8 *ap_mdie)
 {
@@ -298,7 +295,7 @@
 		/* Information element count */
 		ftie->mic_control[1] = 3 + ieee802_11_ie_count(ric_ies,
 							       ric_ies_len);
-		if (wpa_ft_mic(kck, sm->own_addr, target_ap, 5,
+		if (wpa_ft_mic(kck, kck_len, sm->own_addr, target_ap, 5,
 			       ((u8 *) mdie) - 2, 2 + sizeof(*mdie),
 			       ftie_pos, 2 + *ftie_len,
 			       (u8 *) rsnie, 2 + rsnie->len, ric_ies,
@@ -333,7 +330,7 @@
 	keylen = wpa_cipher_key_len(sm->pairwise_cipher);
 
 	if (wpa_sm_set_key(sm, alg, bssid, 0, 1, null_rsc,
-			   sizeof(null_rsc), (u8 *) sm->ptk.tk1, keylen) < 0) {
+			   sizeof(null_rsc), (u8 *) sm->ptk.tk, keylen) < 0) {
 		wpa_printf(MSG_WARNING, "FT: Failed to set PTK to the driver");
 		return -1;
 	}
@@ -360,7 +357,7 @@
 	}
 
 	ft_ies = wpa_ft_gen_req_ies(sm, &ft_ies_len, NULL, sm->pmk_r0_name,
-				    NULL, sm->bssid, NULL, 0, mdie);
+				    NULL, 0, sm->bssid, NULL, 0, mdie);
 	if (ft_ies) {
 		wpa_sm_update_ft_ies(sm, sm->mobility_domain,
 				     ft_ies, ft_ies_len);
@@ -376,7 +373,7 @@
 			    const u8 *ric_ies, size_t ric_ies_len)
 {
 	u8 *ft_ies;
-	size_t ft_ies_len, ptk_len;
+	size_t ft_ies_len;
 	struct wpa_ft_ies parse;
 	struct rsn_mdie *mdie;
 	struct rsn_ftie *ftie;
@@ -478,16 +475,14 @@
 		    sm->pmk_r1_name, WPA_PMK_NAME_LEN);
 
 	bssid = target_ap;
-	ptk_len = sm->pairwise_cipher != WPA_CIPHER_TKIP ? 48 : 64;
-	wpa_pmk_r1_to_ptk(sm->pmk_r1, sm->snonce, ftie->anonce, sm->own_addr,
-			  bssid, sm->pmk_r1_name,
-			  (u8 *) &sm->ptk, ptk_len, ptk_name);
-	wpa_hexdump_key(MSG_DEBUG, "FT: PTK",
-			(u8 *) &sm->ptk, ptk_len);
-	wpa_hexdump(MSG_DEBUG, "FT: PTKName", ptk_name, WPA_PMK_NAME_LEN);
+	if (wpa_pmk_r1_to_ptk(sm->pmk_r1, sm->snonce, ftie->anonce,
+			      sm->own_addr, bssid, sm->pmk_r1_name, &sm->ptk,
+			      ptk_name, sm->key_mgmt, sm->pairwise_cipher) < 0)
+		return -1;
 
 	ft_ies = wpa_ft_gen_req_ies(sm, &ft_ies_len, ftie->anonce,
-				    sm->pmk_r1_name, sm->ptk.kck, bssid,
+				    sm->pmk_r1_name,
+				    sm->ptk.kck, sm->ptk.kck_len, bssid,
 				    ric_ies, ric_ies_len,
 				    parse.mdie ? parse.mdie - 2 : NULL);
 	if (ft_ies) {
@@ -566,7 +561,8 @@
 		return -1;
 	}
 	gtk_len = gtk_elem_len - 19;
-	if (aes_unwrap(sm->ptk.kek, 16, gtk_len / 8, gtk_elem + 11, gtk)) {
+	if (aes_unwrap(sm->ptk.kek, sm->ptk.kek_len, gtk_len / 8, gtk_elem + 11,
+		       gtk)) {
 		wpa_printf(MSG_WARNING, "FT: AES unwrap failed - could not "
 			   "decrypt GTK");
 		return -1;
@@ -645,8 +641,8 @@
 		return -1;
 	}
 
-	if (aes_unwrap(sm->ptk.kek, 16, WPA_IGTK_LEN / 8, igtk_elem + 9, igtk))
-	{
+	if (aes_unwrap(sm->ptk.kek, sm->ptk.kek_len, WPA_IGTK_LEN / 8,
+		       igtk_elem + 9, igtk)) {
 		wpa_printf(MSG_WARNING, "FT: AES unwrap failed - could not "
 			   "decrypt IGTK");
 		return -1;
@@ -677,7 +673,7 @@
 	struct rsn_mdie *mdie;
 	struct rsn_ftie *ftie;
 	unsigned int count;
-	u8 mic[16];
+	u8 mic[WPA_EAPOL_KEY_MIC_MAX_LEN];
 
 	wpa_hexdump(MSG_DEBUG, "FT: Response IEs", ies, ies_len);
 
@@ -770,7 +766,7 @@
 		return -1;
 	}
 
-	if (wpa_ft_mic(sm->ptk.kck, sm->own_addr, src_addr, 6,
+	if (wpa_ft_mic(sm->ptk.kck, sm->ptk.kck_len, sm->own_addr, src_addr, 6,
 		       parse.mdie - 2, parse.mdie_len + 2,
 		       parse.ftie - 2, parse.ftie_len + 2,
 		       parse.rsn - 2, parse.rsn_len + 2,
@@ -839,7 +835,7 @@
 	}
 
 	ft_ies = wpa_ft_gen_req_ies(sm, &ft_ies_len, NULL, sm->pmk_r0_name,
-				    NULL, target_ap, NULL, 0, mdie);
+				    NULL, 0, target_ap, NULL, 0, mdie);
 	if (ft_ies) {
 		sm->over_the_ds_in_progress = 1;
 		os_memcpy(sm->target_ap, target_ap, ETH_ALEN);
diff --git a/src/rsn_supp/wpa_i.h b/src/rsn_supp/wpa_i.h
index 839b545..965a9c1 100644
--- a/src/rsn_supp/wpa_i.h
+++ b/src/rsn_supp/wpa_i.h
@@ -1,6 +1,6 @@
 /*
  * Internal WPA/RSN supplicant state machine definitions
- * Copyright (c) 2004-2010, 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,6 +23,7 @@
 	size_t pmk_len;
 	struct wpa_ptk ptk, tptk;
 	int ptk_set, tptk_set;
+	unsigned int msg_3_of_4_ok:1;
 	u8 snonce[WPA_NONCE_LEN];
 	u8 anonce[WPA_NONCE_LEN]; /* ANonce from the last 1/4 msg */
 	int renew_snonce;
@@ -92,6 +93,7 @@
 #ifdef CONFIG_TDLS
 	struct wpa_tdls_peer *tdls;
 	int tdls_prohibited;
+	int tdls_chan_switch_prohibited;
 	int tdls_disabled;
 
 	/* The driver supports TDLS */
@@ -102,6 +104,9 @@
 	 * to it via tdls_mgmt.
 	 */
 	int tdls_external_setup;
+
+	/* The driver supports TDLS channel switching */
+	int tdls_chan_switch;
 #endif /* CONFIG_TDLS */
 
 #ifdef CONFIG_IEEE80211R
@@ -250,18 +255,20 @@
 {
 	if (!sm->ctx->set_rekey_offload)
 		return;
-	sm->ctx->set_rekey_offload(sm->ctx->ctx, sm->ptk.kek,
-				   sm->ptk.kck, sm->rx_replay_counter);
+	sm->ctx->set_rekey_offload(sm->ctx->ctx, sm->ptk.kek, sm->ptk.kek_len,
+				   sm->ptk.kck, sm->ptk.kck_len,
+				   sm->rx_replay_counter);
 }
 
 #ifdef CONFIG_TDLS
 static inline int wpa_sm_tdls_get_capa(struct wpa_sm *sm,
 				       int *tdls_supported,
-				       int *tdls_ext_setup)
+				       int *tdls_ext_setup,
+				       int *tdls_chan_switch)
 {
 	if (sm->ctx->tdls_get_capa)
 		return sm->ctx->tdls_get_capa(sm->ctx->ctx, tdls_supported,
-					      tdls_ext_setup);
+					      tdls_ext_setup, tdls_chan_switch);
 	return -1;
 }
 
@@ -310,9 +317,39 @@
 						 supp_oper_classes_len);
 	return -1;
 }
+
+static inline int
+wpa_sm_tdls_enable_channel_switch(struct wpa_sm *sm, const u8 *addr,
+				  u8 oper_class,
+				  const struct hostapd_freq_params *freq_params)
+{
+	if (sm->ctx->tdls_enable_channel_switch)
+		return sm->ctx->tdls_enable_channel_switch(sm->ctx->ctx, addr,
+							   oper_class,
+							   freq_params);
+	return -1;
+}
+
+static inline int
+wpa_sm_tdls_disable_channel_switch(struct wpa_sm *sm, const u8 *addr)
+{
+	if (sm->ctx->tdls_disable_channel_switch)
+		return sm->ctx->tdls_disable_channel_switch(sm->ctx->ctx, addr);
+	return -1;
+}
 #endif /* CONFIG_TDLS */
 
-void wpa_eapol_key_send(struct wpa_sm *sm, const u8 *kck,
+static inline int wpa_sm_key_mgmt_set_pmk(struct wpa_sm *sm,
+					  const u8 *pmk, size_t pmk_len)
+{
+	if (!sm->proactive_key_caching)
+		return 0;
+	if (!sm->ctx->key_mgmt_set_pmk)
+		return -1;
+	return sm->ctx->key_mgmt_set_pmk(sm->ctx->ctx, pmk, pmk_len);
+}
+
+void wpa_eapol_key_send(struct wpa_sm *sm, const u8 *kck, size_t kck_len,
 			int ver, const u8 *dest, u16 proto,
 			u8 *msg, size_t msg_len, u8 *key_mic);
 int wpa_supplicant_send_2_of_4(struct wpa_sm *sm, const unsigned char *dst,
@@ -326,8 +363,7 @@
 			       struct wpa_ptk *ptk);
 
 int wpa_derive_ptk_ft(struct wpa_sm *sm, const unsigned char *src_addr,
-		      const struct wpa_eapol_key *key,
-		      struct wpa_ptk *ptk, size_t ptk_len);
+		      const struct wpa_eapol_key *key, struct wpa_ptk *ptk);
 
 void wpa_tdls_assoc(struct wpa_sm *sm);
 void wpa_tdls_disassoc(struct wpa_sm *sm);
diff --git a/src/rsn_supp/wpa_ie.c b/src/rsn_supp/wpa_ie.c
index 93e8cf6..0c37b35 100644
--- a/src/rsn_supp/wpa_ie.c
+++ b/src/rsn_supp/wpa_ie.c
@@ -1,6 +1,6 @@
 /*
  * wpa_supplicant - WPA/RSN IE and KDE processing
- * Copyright (c) 2003-2008, 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.
@@ -30,6 +30,9 @@
 {
 	if (wpa_ie_len >= 1 && wpa_ie[0] == WLAN_EID_RSN)
 		return wpa_parse_wpa_ie_rsn(wpa_ie, wpa_ie_len, data);
+	if (wpa_ie_len >= 6 && wpa_ie[0] == WLAN_EID_VENDOR_SPECIFIC &&
+	    wpa_ie[1] >= 4 && WPA_GET_BE32(&wpa_ie[2]) == OSEN_IE_VENDOR_TYPE)
+		return wpa_parse_wpa_ie_rsn(wpa_ie, wpa_ie_len, data);
 	else
 		return wpa_parse_wpa_ie_wpa(wpa_ie, wpa_ie_len, data);
 }
@@ -173,6 +176,10 @@
 	} else if (key_mgmt == WPA_KEY_MGMT_FT_SAE) {
 		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_SAE);
 #endif /* CONFIG_SAE */
+	} else if (key_mgmt == WPA_KEY_MGMT_IEEE8021X_SUITE_B_192) {
+		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_802_1X_SUITE_B_192);
+	} else if (key_mgmt == WPA_KEY_MGMT_IEEE8021X_SUITE_B) {
+		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_802_1X_SUITE_B);
 	} else {
 		wpa_printf(MSG_WARNING, "Invalid key management type (%d).",
 			   key_mgmt);
@@ -504,12 +511,14 @@
 			ie->rsn_ie_len = pos[1] + 2;
 			wpa_hexdump(MSG_DEBUG, "WPA: RSN IE in EAPOL-Key",
 				    ie->rsn_ie, ie->rsn_ie_len);
-		} else if (*pos == WLAN_EID_MOBILITY_DOMAIN) {
+		} else if (*pos == WLAN_EID_MOBILITY_DOMAIN &&
+			   pos[1] >= sizeof(struct rsn_mdie)) {
 			ie->mdie = pos;
 			ie->mdie_len = pos[1] + 2;
 			wpa_hexdump(MSG_DEBUG, "WPA: MDIE in EAPOL-Key",
 				    ie->mdie, ie->mdie_len);
-		} else if (*pos == WLAN_EID_FAST_BSS_TRANSITION) {
+		} else if (*pos == WLAN_EID_FAST_BSS_TRANSITION &&
+			   pos[1] >= sizeof(struct rsn_ftie)) {
 			ie->ftie = pos;
 			ie->ftie_len = pos[1] + 2;
 			wpa_hexdump(MSG_DEBUG, "WPA: FTIE in EAPOL-Key",
@@ -544,15 +553,16 @@
 		} else if (*pos == WLAN_EID_EXT_SUPP_RATES) {
 			ie->ext_supp_rates = pos;
 			ie->ext_supp_rates_len = pos[1] + 2;
-		} else if (*pos == WLAN_EID_HT_CAP) {
+		} else if (*pos == WLAN_EID_HT_CAP &&
+			   pos[1] >= sizeof(struct ieee80211_ht_capabilities)) {
 			ie->ht_capabilities = pos + 2;
-			ie->ht_capabilities_len = pos[1];
 		} else if (*pos == WLAN_EID_VHT_AID) {
 			if (pos[1] >= 2)
 				ie->aid = WPA_GET_LE16(pos + 2) & 0x3fff;
-		} else if (*pos == WLAN_EID_VHT_CAP) {
+		} else if (*pos == WLAN_EID_VHT_CAP &&
+			   pos[1] >= sizeof(struct ieee80211_vht_capabilities))
+		{
 			ie->vht_capabilities = pos + 2;
-			ie->vht_capabilities_len = pos[1];
 		} else if (*pos == WLAN_EID_QOS && pos[1] >= 1) {
 			ie->qosinfo = pos[2];
 		} else if (*pos == WLAN_EID_SUPPORTED_CHANNELS) {
diff --git a/src/rsn_supp/wpa_ie.h b/src/rsn_supp/wpa_ie.h
index 0fc42cc..fe95af0 100644
--- a/src/rsn_supp/wpa_ie.h
+++ b/src/rsn_supp/wpa_ie.h
@@ -50,9 +50,7 @@
 	const u8 *ext_supp_rates;
 	size_t ext_supp_rates_len;
 	const u8 *ht_capabilities;
-	size_t ht_capabilities_len;
 	const u8 *vht_capabilities;
-	size_t vht_capabilities_len;
 	const u8 *supp_channels;
 	size_t supp_channels_len;
 	const u8 *supp_oper_classes;
diff --git a/src/tls/asn1.c b/src/tls/asn1.c
index 97462fa..cec1092 100644
--- a/src/tls/asn1.c
+++ b/src/tls/asn1.c
@@ -166,7 +166,7 @@
 		ret = os_snprintf(pos, buf + len - pos,
 				  "%s%lu",
 				  i == 0 ? "" : ".", oid->oid[i]);
-		if (ret < 0 || ret >= buf + len - pos)
+		if (os_snprintf_error(buf + len - pos, ret))
 			break;
 		pos += ret;
 	}
diff --git a/src/tls/libtommath.c b/src/tls/libtommath.c
index 3fb8fbe..8bc824f 100644
--- a/src/tls/libtommath.c
+++ b/src/tls/libtommath.c
@@ -1472,8 +1472,7 @@
                 cur_arg = va_arg(clean_args, mp_int*);
             }
             va_end(clean_args);
-            res = MP_MEM;
-            break;
+            return MP_MEM;
         }
         n++;
         cur_arg = va_arg(args, mp_int*);
@@ -1631,7 +1630,7 @@
   }
 	
   /* init our temps */
-  if ((res = mp_init_multi(&ta, &tb, &tq, &q, NULL) != MP_OKAY)) {
+  if ((res = mp_init_multi(&ta, &tb, &tq, &q, NULL)) != MP_OKAY) {
      return res;
   }
 
diff --git a/src/tls/tlsv1_client.c b/src/tls/tlsv1_client.c
index 4a4f0b6..533286c 100644
--- a/src/tls/tlsv1_client.c
+++ b/src/tls/tlsv1_client.c
@@ -570,8 +570,26 @@
 	case TLS_RSA_WITH_3DES_EDE_CBC_SHA:
 		cipher = "DES-CBC3-SHA";
 		break;
-	case TLS_DH_anon_WITH_AES_128_CBC_SHA256:
-		cipher = "ADH-AES-128-SHA256";
+	case TLS_DHE_RSA_WITH_DES_CBC_SHA:
+		cipher = "DHE-RSA-DES-CBC-SHA";
+		break;
+	case TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA:
+		cipher = "DHE-RSA-DES-CBC3-SHA";
+		break;
+	case TLS_DH_anon_WITH_RC4_128_MD5:
+		cipher = "ADH-RC4-MD5";
+		break;
+	case TLS_DH_anon_WITH_DES_CBC_SHA:
+		cipher = "ADH-DES-SHA";
+		break;
+	case TLS_DH_anon_WITH_3DES_EDE_CBC_SHA:
+		cipher = "ADH-DES-CBC3-SHA";
+		break;
+	case TLS_RSA_WITH_AES_128_CBC_SHA:
+		cipher = "AES-128-SHA";
+		break;
+	case TLS_DHE_RSA_WITH_AES_128_CBC_SHA:
+		cipher = "DHE-RSA-AES-128-SHA";
 		break;
 	case TLS_DH_anon_WITH_AES_128_CBC_SHA:
 		cipher = "ADH-AES-128-SHA";
@@ -579,15 +597,30 @@
 	case TLS_RSA_WITH_AES_256_CBC_SHA:
 		cipher = "AES-256-SHA";
 		break;
-	case TLS_RSA_WITH_AES_256_CBC_SHA256:
-		cipher = "AES-256-SHA256";
+	case TLS_DHE_RSA_WITH_AES_256_CBC_SHA:
+		cipher = "DHE-RSA-AES-256-SHA";
 		break;
-	case TLS_RSA_WITH_AES_128_CBC_SHA:
-		cipher = "AES-128-SHA";
+	case TLS_DH_anon_WITH_AES_256_CBC_SHA:
+		cipher = "ADH-AES-256-SHA";
 		break;
 	case TLS_RSA_WITH_AES_128_CBC_SHA256:
 		cipher = "AES-128-SHA256";
 		break;
+	case TLS_RSA_WITH_AES_256_CBC_SHA256:
+		cipher = "AES-256-SHA256";
+		break;
+	case TLS_DHE_RSA_WITH_AES_128_CBC_SHA256:
+		cipher = "DHE-RSA-AES-128-SHA256";
+		break;
+	case TLS_DHE_RSA_WITH_AES_256_CBC_SHA256:
+		cipher = "DHE-RSA-AES-256-SHA256";
+		break;
+	case TLS_DH_anon_WITH_AES_128_CBC_SHA256:
+		cipher = "ADH-AES-128-SHA256";
+		break;
+	case TLS_DH_anon_WITH_AES_256_CBC_SHA256:
+		cipher = "ADH-AES-256-SHA256";
+		break;
 	default:
 		return -1;
 	}
@@ -698,8 +731,6 @@
 	if (conn->state != SERVER_HELLO) {
 		keys->server_random = conn->server_random;
 		keys->server_random_len = TLS_RANDOM_LEN;
-		keys->master_key = conn->master_secret;
-		keys->master_key_len = TLS_MASTER_SECRET_LEN;
 	}
 
 	return 0;
diff --git a/src/tls/tlsv1_client_read.c b/src/tls/tlsv1_client_read.c
index 4f08e0f..9ce9680 100644
--- a/src/tls/tlsv1_client_read.c
+++ b/src/tls/tlsv1_client_read.c
@@ -440,6 +440,7 @@
 	const u8 *pos, *end, *server_params, *server_params_end;
 	u8 alert;
 	unsigned int bits;
+	u16 val;
 
 	tlsv1_client_free_dh(conn);
 
@@ -449,13 +450,13 @@
 	if (end - pos < 3)
 		goto fail;
 	server_params = pos;
-	conn->dh_p_len = WPA_GET_BE16(pos);
+	val = WPA_GET_BE16(pos);
 	pos += 2;
-	if (conn->dh_p_len == 0 || end - pos < (int) conn->dh_p_len) {
-		wpa_printf(MSG_DEBUG, "TLSv1: Invalid dh_p length %lu",
-			   (unsigned long) conn->dh_p_len);
+	if (val == 0 || val > (size_t) (end - pos)) {
+		wpa_printf(MSG_DEBUG, "TLSv1: Invalid dh_p length %u", val);
 		goto fail;
 	}
+	conn->dh_p_len = val;
 	bits = count_bits(pos, conn->dh_p_len);
 	if (bits < 768) {
 		wpa_printf(MSG_INFO, "TLSv1: Reject under 768-bit DH prime (insecure; only %u bits)",
@@ -474,10 +475,11 @@
 
 	if (end - pos < 3)
 		goto fail;
-	conn->dh_g_len = WPA_GET_BE16(pos);
+	val = WPA_GET_BE16(pos);
 	pos += 2;
-	if (conn->dh_g_len == 0 || end - pos < (int) conn->dh_g_len)
+	if (val == 0 || val > (size_t) (end - pos))
 		goto fail;
+	conn->dh_g_len = val;
 	conn->dh_g = os_malloc(conn->dh_g_len);
 	if (conn->dh_g == NULL)
 		goto fail;
@@ -490,10 +492,11 @@
 
 	if (end - pos < 3)
 		goto fail;
-	conn->dh_ys_len = WPA_GET_BE16(pos);
+	val = WPA_GET_BE16(pos);
 	pos += 2;
-	if (conn->dh_ys_len == 0 || end - pos < (int) conn->dh_ys_len)
+	if (val == 0 || val > (size_t) (end - pos))
 		goto fail;
+	conn->dh_ys_len = val;
 	conn->dh_ys = os_malloc(conn->dh_ys_len);
 	if (conn->dh_ys == NULL)
 		goto fail;
diff --git a/src/tls/tlsv1_client_write.c b/src/tls/tlsv1_client_write.c
index 839eb90..d192f44 100644
--- a/src/tls/tlsv1_client_write.c
+++ b/src/tls/tlsv1_client_write.c
@@ -432,7 +432,6 @@
 	u8 *pos, *rhdr, *hs_start, *hs_length, *signed_start;
 	size_t rlen, hlen, clen;
 	u8 hash[100], *hpos;
-	enum { SIGN_ALG_RSA, SIGN_ALG_DSA } alg = SIGN_ALG_RSA;
 
 	pos = *msgpos;
 
@@ -505,21 +504,17 @@
 	} else {
 #endif /* CONFIG_TLSV12 */
 
-	if (alg == SIGN_ALG_RSA) {
-		hlen = MD5_MAC_LEN;
-		if (conn->verify.md5_cert == NULL ||
-		    crypto_hash_finish(conn->verify.md5_cert, hpos, &hlen) < 0)
-		{
-			tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
-				  TLS_ALERT_INTERNAL_ERROR);
-			conn->verify.md5_cert = NULL;
-			crypto_hash_finish(conn->verify.sha1_cert, NULL, NULL);
-			conn->verify.sha1_cert = NULL;
-			return -1;
-		}
-		hpos += MD5_MAC_LEN;
-	} else
-		crypto_hash_finish(conn->verify.md5_cert, NULL, NULL);
+	hlen = MD5_MAC_LEN;
+	if (conn->verify.md5_cert == NULL ||
+	    crypto_hash_finish(conn->verify.md5_cert, hpos, &hlen) < 0) {
+		tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+			  TLS_ALERT_INTERNAL_ERROR);
+		conn->verify.md5_cert = NULL;
+		crypto_hash_finish(conn->verify.sha1_cert, NULL, NULL);
+		conn->verify.sha1_cert = NULL;
+		return -1;
+	}
+	hpos += MD5_MAC_LEN;
 
 	conn->verify.md5_cert = NULL;
 	hlen = SHA1_MAC_LEN;
@@ -532,8 +527,7 @@
 	}
 	conn->verify.sha1_cert = NULL;
 
-	if (alg == SIGN_ALG_RSA)
-		hlen += MD5_MAC_LEN;
+	hlen += MD5_MAC_LEN;
 
 #ifdef CONFIG_TLSV12
 	}
diff --git a/src/tls/tlsv1_common.c b/src/tls/tlsv1_common.c
index ced28cf..dabc12a 100644
--- a/src/tls/tlsv1_common.c
+++ b/src/tls/tlsv1_common.c
@@ -366,23 +366,20 @@
 {
 	u8 *hpos;
 	size_t hlen;
-	enum { SIGN_ALG_RSA, SIGN_ALG_DSA } alg = SIGN_ALG_RSA;
 	struct crypto_hash *ctx;
 
 	hpos = hash;
 
-	if (alg == SIGN_ALG_RSA) {
-		ctx = crypto_hash_init(CRYPTO_HASH_ALG_MD5, NULL, 0);
-		if (ctx == NULL)
-			return -1;
-		crypto_hash_update(ctx, client_random, TLS_RANDOM_LEN);
-		crypto_hash_update(ctx, server_random, TLS_RANDOM_LEN);
-		crypto_hash_update(ctx, server_params, server_params_len);
-		hlen = MD5_MAC_LEN;
-		if (crypto_hash_finish(ctx, hash, &hlen) < 0)
-			return -1;
-		hpos += hlen;
-	}
+	ctx = crypto_hash_init(CRYPTO_HASH_ALG_MD5, NULL, 0);
+	if (ctx == NULL)
+		return -1;
+	crypto_hash_update(ctx, client_random, TLS_RANDOM_LEN);
+	crypto_hash_update(ctx, server_random, TLS_RANDOM_LEN);
+	crypto_hash_update(ctx, server_params, server_params_len);
+	hlen = MD5_MAC_LEN;
+	if (crypto_hash_finish(ctx, hash, &hlen) < 0)
+		return -1;
+	hpos += hlen;
 
 	ctx = crypto_hash_init(CRYPTO_HASH_ALG_SHA1, NULL, 0);
 	if (ctx == NULL)
diff --git a/src/tls/tlsv1_server.c b/src/tls/tlsv1_server.c
index 23d0b81..4df756f 100644
--- a/src/tls/tlsv1_server.c
+++ b/src/tls/tlsv1_server.c
@@ -516,14 +516,56 @@
 	case TLS_RSA_WITH_3DES_EDE_CBC_SHA:
 		cipher = "DES-CBC3-SHA";
 		break;
+	case TLS_DHE_RSA_WITH_DES_CBC_SHA:
+		cipher = "DHE-RSA-DES-CBC-SHA";
+		break;
+	case TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA:
+		cipher = "DHE-RSA-DES-CBC3-SHA";
+		break;
+	case TLS_DH_anon_WITH_RC4_128_MD5:
+		cipher = "ADH-RC4-MD5";
+		break;
+	case TLS_DH_anon_WITH_DES_CBC_SHA:
+		cipher = "ADH-DES-SHA";
+		break;
+	case TLS_DH_anon_WITH_3DES_EDE_CBC_SHA:
+		cipher = "ADH-DES-CBC3-SHA";
+		break;
+	case TLS_RSA_WITH_AES_128_CBC_SHA:
+		cipher = "AES-128-SHA";
+		break;
+	case TLS_DHE_RSA_WITH_AES_128_CBC_SHA:
+		cipher = "DHE-RSA-AES-128-SHA";
+		break;
 	case TLS_DH_anon_WITH_AES_128_CBC_SHA:
 		cipher = "ADH-AES-128-SHA";
 		break;
 	case TLS_RSA_WITH_AES_256_CBC_SHA:
 		cipher = "AES-256-SHA";
 		break;
-	case TLS_RSA_WITH_AES_128_CBC_SHA:
-		cipher = "AES-128-SHA";
+	case TLS_DHE_RSA_WITH_AES_256_CBC_SHA:
+		cipher = "DHE-RSA-AES-256-SHA";
+		break;
+	case TLS_DH_anon_WITH_AES_256_CBC_SHA:
+		cipher = "ADH-AES-256-SHA";
+		break;
+	case TLS_RSA_WITH_AES_128_CBC_SHA256:
+		cipher = "AES-128-SHA256";
+		break;
+	case TLS_RSA_WITH_AES_256_CBC_SHA256:
+		cipher = "AES-256-SHA256";
+		break;
+	case TLS_DHE_RSA_WITH_AES_128_CBC_SHA256:
+		cipher = "DHE-RSA-AES-128-SHA256";
+		break;
+	case TLS_DHE_RSA_WITH_AES_256_CBC_SHA256:
+		cipher = "DHE-RSA-AES-256-SHA256";
+		break;
+	case TLS_DH_anon_WITH_AES_128_CBC_SHA256:
+		cipher = "ADH-AES-128-SHA256";
+		break;
+	case TLS_DH_anon_WITH_AES_256_CBC_SHA256:
+		cipher = "ADH-AES-256-SHA256";
 		break;
 	default:
 		return -1;
@@ -585,8 +627,6 @@
 	if (conn->state != SERVER_HELLO) {
 		keys->server_random = conn->server_random;
 		keys->server_random_len = TLS_RANDOM_LEN;
-		keys->master_key = conn->master_secret;
-		keys->master_key_len = TLS_MASTER_SECRET_LEN;
 	}
 
 	return 0;
diff --git a/src/tls/tlsv1_server_read.c b/src/tls/tlsv1_server_read.c
index 728e137..0f237ba 100644
--- a/src/tls/tlsv1_server_read.c
+++ b/src/tls/tlsv1_server_read.c
@@ -626,7 +626,7 @@
 	dh_yc_len = WPA_GET_BE16(pos);
 	dh_yc = pos + 2;
 
-	if (dh_yc + dh_yc_len > end) {
+	if (dh_yc_len > end - dh_yc) {
 		tlsv1_server_log(conn, "Client public value overflow (length %d)",
 				 dh_yc_len);
 		tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
@@ -775,7 +775,6 @@
 	u8 type;
 	size_t hlen;
 	u8 hash[MD5_MAC_LEN + SHA1_MAC_LEN], *hpos;
-	enum { SIGN_ALG_RSA, SIGN_ALG_DSA } alg = SIGN_ALG_RSA;
 	u8 alert;
 
 	if (ct == TLS_CONTENT_TYPE_CHANGE_CIPHER_SPEC) {
@@ -883,21 +882,17 @@
 	} else {
 #endif /* CONFIG_TLSV12 */
 
-	if (alg == SIGN_ALG_RSA) {
-		hlen = MD5_MAC_LEN;
-		if (conn->verify.md5_cert == NULL ||
-		    crypto_hash_finish(conn->verify.md5_cert, hpos, &hlen) < 0)
-		{
-			tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
-					   TLS_ALERT_INTERNAL_ERROR);
-			conn->verify.md5_cert = NULL;
-			crypto_hash_finish(conn->verify.sha1_cert, NULL, NULL);
-			conn->verify.sha1_cert = NULL;
-			return -1;
-		}
-		hpos += MD5_MAC_LEN;
-	} else
-		crypto_hash_finish(conn->verify.md5_cert, NULL, NULL);
+	hlen = MD5_MAC_LEN;
+	if (conn->verify.md5_cert == NULL ||
+	    crypto_hash_finish(conn->verify.md5_cert, hpos, &hlen) < 0) {
+		tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+				   TLS_ALERT_INTERNAL_ERROR);
+		conn->verify.md5_cert = NULL;
+		crypto_hash_finish(conn->verify.sha1_cert, NULL, NULL);
+		conn->verify.sha1_cert = NULL;
+		return -1;
+	}
+	hpos += MD5_MAC_LEN;
 
 	conn->verify.md5_cert = NULL;
 	hlen = SHA1_MAC_LEN;
@@ -910,8 +905,7 @@
 	}
 	conn->verify.sha1_cert = NULL;
 
-	if (alg == SIGN_ALG_RSA)
-		hlen += MD5_MAC_LEN;
+	hlen += MD5_MAC_LEN;
 
 #ifdef CONFIG_TLSV12
 	}
diff --git a/src/tls/x509v3.c b/src/tls/x509v3.c
index e1e4df8..b51dfcd 100644
--- a/src/tls/x509v3.c
+++ b/src/tls/x509v3.c
@@ -512,7 +512,7 @@
 		ret = os_snprintf(pos, end - pos, "%s=%s, ",
 				  x509_name_attr_str(name->attr[i].type),
 				  name->attr[i].value);
-		if (ret < 0 || ret >= end - pos)
+		if (os_snprintf_error(end - pos, ret))
 			goto done;
 		pos += ret;
 	}
@@ -527,7 +527,7 @@
 	if (name->email) {
 		ret = os_snprintf(pos, end - pos, "/emailAddress=%s",
 				  name->email);
-		if (ret < 0 || ret >= end - pos)
+		if (os_snprintf_error(end - pos, ret))
 			goto done;
 		pos += ret;
 	}
@@ -1511,7 +1511,7 @@
 	if (pos + hdr.length < end) {
 		wpa_hexdump(MSG_MSGDUMP, "X509: Ignoring extra data after DER "
 			    "encoded certificate",
-			    pos + hdr.length, end - pos + hdr.length);
+			    pos + hdr.length, end - (pos + hdr.length));
 		end = pos + hdr.length;
 	}
 
diff --git a/src/utils/base64.c b/src/utils/base64.c
index af1307f..d44f290 100644
--- a/src/utils/base64.c
+++ b/src/utils/base64.c
@@ -48,9 +48,11 @@
 	pos = out;
 	line_len = 0;
 	while (end - in >= 3) {
-		*pos++ = base64_table[in[0] >> 2];
-		*pos++ = base64_table[((in[0] & 0x03) << 4) | (in[1] >> 4)];
-		*pos++ = base64_table[((in[1] & 0x0f) << 2) | (in[2] >> 6)];
+		*pos++ = base64_table[(in[0] >> 2) & 0x3f];
+		*pos++ = base64_table[(((in[0] & 0x03) << 4) |
+				       (in[1] >> 4)) & 0x3f];
+		*pos++ = base64_table[(((in[1] & 0x0f) << 2) |
+				       (in[2] >> 6)) & 0x3f];
 		*pos++ = base64_table[in[2] & 0x3f];
 		in += 3;
 		line_len += 4;
@@ -61,14 +63,14 @@
 	}
 
 	if (end - in) {
-		*pos++ = base64_table[in[0] >> 2];
+		*pos++ = base64_table[(in[0] >> 2) & 0x3f];
 		if (end - in == 1) {
-			*pos++ = base64_table[(in[0] & 0x03) << 4];
+			*pos++ = base64_table[((in[0] & 0x03) << 4) & 0x3f];
 			*pos++ = '=';
 		} else {
-			*pos++ = base64_table[((in[0] & 0x03) << 4) |
-					      (in[1] >> 4)];
-			*pos++ = base64_table[(in[1] & 0x0f) << 2];
+			*pos++ = base64_table[(((in[0] & 0x03) << 4) |
+					       (in[1] >> 4)) & 0x3f];
+			*pos++ = base64_table[((in[1] & 0x0f) << 2) & 0x3f];
 		}
 		*pos++ = '=';
 		line_len += 4;
diff --git a/src/utils/bitfield.c b/src/utils/bitfield.c
index f90e4be..8dcec39 100644
--- a/src/utils/bitfield.c
+++ b/src/utils/bitfield.c
@@ -76,11 +76,11 @@
 int bitfield_get_first_zero(struct bitfield *bf)
 {
 	size_t i;
-	for (i = 0; i <= (bf->max_bits + 7) / 8; i++) {
+	for (i = 0; i < (bf->max_bits + 7) / 8; i++) {
 		if (bf->bits[i] != 0xff)
 			break;
 	}
-	if (i > (bf->max_bits + 7) / 8)
+	if (i == (bf->max_bits + 7) / 8)
 		return -1;
 	i = i * 8 + first_zero(bf->bits[i]);
 	if (i >= bf->max_bits)
diff --git a/src/utils/browser-android.c b/src/utils/browser-android.c
index d5ff5b5..9ce1a5c 100644
--- a/src/utils/browser-android.c
+++ b/src/utils/browser-android.c
@@ -64,24 +64,15 @@
 
 int hs20_web_browser(const char *url)
 {
-	char cmd[2000];
-	int ret;
 	struct http_server *http;
 	struct in_addr addr;
 	struct browser_data data;
+	pid_t pid;
 
 	wpa_printf(MSG_INFO, "Launching Android browser to %s", url);
 
 	os_memset(&data, 0, sizeof(data));
 
-	ret = os_snprintf(cmd, sizeof(cmd),
-			  "start -a android.intent.action.VIEW -d %s "
-			  "-n com.android.browser/.BrowserActivity", url);
-	if (ret < 0 || (size_t) ret >= sizeof(cmd)) {
-		wpa_printf(MSG_ERROR, "Too long URL");
-		return -1;
-	}
-
 	if (eloop_init() < 0) {
 		wpa_printf(MSG_ERROR, "eloop_init failed");
 		return -1;
@@ -94,14 +85,34 @@
 		return -1;
 	}
 
-	if (os_exec("/system/bin/am", cmd, 1) != 0) {
-		wpa_printf(MSG_INFO, "Failed to launch Android browser");
-		eloop_cancel_timeout(browser_timeout, NULL, NULL);
+	pid = fork();
+	if (pid < 0) {
+		wpa_printf(MSG_ERROR, "fork: %s", strerror(errno));
 		http_server_deinit(http);
 		eloop_destroy();
 		return -1;
 	}
 
+	if (pid == 0) {
+		/* run the external command in the child process */
+		char *argv[9];
+
+		argv[0] = "browser-android";
+		argv[1] = "start";
+		argv[2] = "-a";
+		argv[3] = "android.intent.action.VIEW";
+		argv[4] = "-d";
+		argv[5] = (void *) url;
+		argv[6] = "-n";
+		argv[7] = "com.android.browser/.BrowserActivity";
+		argv[8] = NULL;
+
+		execv("/system/bin/am", argv);
+		wpa_printf(MSG_ERROR, "execv: %s", strerror(errno));
+		exit(0);
+		return -1;
+	}
+
 	eloop_register_timeout(30, 0, browser_timeout, &data, NULL);
 	eloop_run();
 	eloop_cancel_timeout(browser_timeout, &data, NULL);
@@ -109,7 +120,7 @@
 	eloop_destroy();
 
 	wpa_printf(MSG_INFO, "Closing Android browser");
-	if (os_exec("/system/bin/input", "keyevent 3", 1) != 0) {
+	if (system("/system/bin/input keyevent KEYCODE_HOME") != 0) {
 		wpa_printf(MSG_INFO, "Failed to inject keyevent");
 	}
 
diff --git a/src/utils/browser-system.c b/src/utils/browser-system.c
index a080e2c..aed3970 100644
--- a/src/utils/browser-system.c
+++ b/src/utils/browser-system.c
@@ -64,22 +64,15 @@
 
 int hs20_web_browser(const char *url)
 {
-	char cmd[2000];
-	int ret;
 	struct http_server *http;
 	struct in_addr addr;
 	struct browser_data data;
+	pid_t pid;
 
-	wpa_printf(MSG_INFO, "Launching Android browser to %s", url);
+	wpa_printf(MSG_INFO, "Launching system browser to %s", url);
 
 	os_memset(&data, 0, sizeof(data));
 
-	ret = os_snprintf(cmd, sizeof(cmd), "x-www-browser '%s' &", url);
-	if (ret < 0 || (size_t) ret >= sizeof(cmd)) {
-		wpa_printf(MSG_ERROR, "Too long URL");
-		return -1;
-	}
-
 	if (eloop_init() < 0) {
 		wpa_printf(MSG_ERROR, "eloop_init failed");
 		return -1;
@@ -92,14 +85,28 @@
 		return -1;
 	}
 
-	if (os_exec("/usr/bin/x-www-browser", url, 0) != 0) {
-		wpa_printf(MSG_INFO, "Failed to launch browser");
-		eloop_cancel_timeout(browser_timeout, NULL, NULL);
+	pid = fork();
+	if (pid < 0) {
+		wpa_printf(MSG_ERROR, "fork: %s", strerror(errno));
 		http_server_deinit(http);
 		eloop_destroy();
 		return -1;
 	}
 
+	if (pid == 0) {
+		/* run the external command in the child process */
+		char *argv[3];
+
+		argv[0] = "browser-system";
+		argv[1] = (void *) url;
+		argv[2] = NULL;
+
+		execv("/usr/bin/x-www-browser", argv);
+		wpa_printf(MSG_ERROR, "execv: %s", strerror(errno));
+		exit(0);
+		return -1;
+	}
+
 	eloop_register_timeout(120, 0, browser_timeout, &data, NULL);
 	eloop_run();
 	eloop_cancel_timeout(browser_timeout, &data, NULL);
diff --git a/src/utils/browser-wpadebug.c b/src/utils/browser-wpadebug.c
index ce3054b..5fc40fa 100644
--- a/src/utils/browser-wpadebug.c
+++ b/src/utils/browser-wpadebug.c
@@ -65,26 +65,15 @@
 
 int hs20_web_browser(const char *url)
 {
-	char cmd[2000];
-	int ret;
 	struct http_server *http;
 	struct in_addr addr;
 	struct browser_data data;
+	pid_t pid;
 
 	wpa_printf(MSG_INFO, "Launching wpadebug browser to %s", url);
 
 	os_memset(&data, 0, sizeof(data));
 
-	ret = os_snprintf(cmd, sizeof(cmd),
-			  "start -a android.action.MAIN "
-			  "-c android.intent.category.LAUNCHER "
-			  "-n w1.fi.wpadebug/.WpaWebViewActivity "
-			  "-e w1.fi.wpadebug.URL '%s'", url);
-	if (ret < 0 || (size_t) ret >= sizeof(cmd)) {
-		wpa_printf(MSG_ERROR, "Too long URL");
-		return -1;
-	}
-
 	if (eloop_init() < 0) {
 		wpa_printf(MSG_ERROR, "eloop_init failed");
 		return -1;
@@ -97,14 +86,37 @@
 		return -1;
 	}
 
-	if (os_exec("/system/bin/am", cmd, 1) != 0) {
-		wpa_printf(MSG_INFO, "Failed to launch wpadebug browser");
-		eloop_cancel_timeout(browser_timeout, NULL, NULL);
+	pid = fork();
+	if (pid < 0) {
+		wpa_printf(MSG_ERROR, "fork: %s", strerror(errno));
 		http_server_deinit(http);
 		eloop_destroy();
 		return -1;
 	}
 
+	if (pid == 0) {
+		/* run the external command in the child process */
+		char *argv[12];
+
+		argv[0] = "browser-wpadebug";
+		argv[1] = "start";
+		argv[2] = "-a";
+		argv[3] = "android.action.MAIN";
+		argv[4] = "-c";
+		argv[5] = "android.intent.category.LAUNCHER";
+		argv[6] = "-n";
+		argv[7] = "w1.fi.wpadebug/.WpaWebViewActivity";
+		argv[8] = "-e";
+		argv[9] = "w1.fi.wpadebug.URL";
+		argv[10] = (void *) url;
+		argv[11] = NULL;
+
+		execv("/system/bin/am", argv);
+		wpa_printf(MSG_ERROR, "execv: %s", strerror(errno));
+		exit(0);
+		return -1;
+	}
+
 	eloop_register_timeout(300, 0, browser_timeout, &data, NULL);
 	eloop_run();
 	eloop_cancel_timeout(browser_timeout, &data, NULL);
diff --git a/src/utils/common.c b/src/utils/common.c
index 9902004..5cf0d57 100644
--- a/src/utils/common.c
+++ b/src/utils/common.c
@@ -8,6 +8,7 @@
 
 #include "includes.h"
 
+#include "common/ieee802_11_defs.h"
 #include "common.h"
 
 
@@ -36,6 +37,25 @@
 }
 
 
+static const char * hwaddr_parse(const char *txt, u8 *addr)
+{
+	size_t i;
+
+	for (i = 0; i < ETH_ALEN; i++) {
+		int a;
+
+		a = hex2byte(txt);
+		if (a < 0)
+			return NULL;
+		txt += 2;
+		addr[i] = a;
+		if (i < ETH_ALEN - 1 && *txt++ != ':')
+			return NULL;
+	}
+	return txt;
+}
+
+
 /**
  * hwaddr_aton - Convert ASCII string to MAC address (colon-delimited format)
  * @txt: MAC address as a string (e.g., "00:11:22:33:44:55")
@@ -44,25 +64,46 @@
  */
 int hwaddr_aton(const char *txt, u8 *addr)
 {
-	int i;
+	return hwaddr_parse(txt, addr) ? 0 : -1;
+}
 
-	for (i = 0; i < 6; i++) {
-		int a, b;
 
-		a = hex2num(*txt++);
-		if (a < 0)
+/**
+ * hwaddr_masked_aton - Convert ASCII string with optional mask to MAC address (colon-delimited format)
+ * @txt: MAC address with optional mask as a string (e.g., "00:11:22:33:44:55/ff:ff:ff:ff:00:00")
+ * @addr: Buffer for the MAC address (ETH_ALEN = 6 bytes)
+ * @mask: Buffer for the MAC address mask (ETH_ALEN = 6 bytes)
+ * @maskable: Flag to indicate whether a mask is allowed
+ * Returns: 0 on success, -1 on failure (e.g., string not a MAC address)
+ */
+int hwaddr_masked_aton(const char *txt, u8 *addr, u8 *mask, u8 maskable)
+{
+	const char *r;
+
+	/* parse address part */
+	r = hwaddr_parse(txt, addr);
+	if (!r)
+		return -1;
+
+	/* check for optional mask */
+	if (*r == '\0' || isspace(*r)) {
+		/* no mask specified, assume default */
+		os_memset(mask, 0xff, ETH_ALEN);
+	} else if (maskable && *r == '/') {
+		/* mask specified and allowed */
+		r = hwaddr_parse(r + 1, mask);
+		/* parser error? */
+		if (!r)
 			return -1;
-		b = hex2num(*txt++);
-		if (b < 0)
-			return -1;
-		*addr++ = (a << 4) | b;
-		if (i < 5 && *txt++ != ':')
-			return -1;
+	} else {
+		/* mask specified but not allowed or trailing garbage */
+		return -1;
 	}
 
 	return 0;
 }
 
+
 /**
  * hwaddr_compact_aton - Convert ASCII string to MAC address (no colon delimitors format)
  * @txt: MAC address as a string (e.g., "001122334455")
@@ -144,6 +185,30 @@
 }
 
 
+int hwaddr_mask_txt(char *buf, size_t len, const u8 *addr, const u8 *mask)
+{
+	size_t i;
+	int print_mask = 0;
+	int res;
+
+	for (i = 0; i < ETH_ALEN; i++) {
+		if (mask[i] != 0xff) {
+			print_mask = 1;
+			break;
+		}
+	}
+
+	if (print_mask)
+		res = os_snprintf(buf, len, MACSTR "/" MACSTR,
+				  MAC2STR(addr), MAC2STR(mask));
+	else
+		res = os_snprintf(buf, len, MACSTR, MAC2STR(addr));
+	if (os_snprintf_error(len, res))
+		return -1;
+	return res;
+}
+
+
 /**
  * inc_byte_array - Increment arbitrary length byte array by one
  * @counter: Pointer to byte array
@@ -183,6 +248,60 @@
 	os_memcpy(buf + 4, (u8 *) &tmp, 4);
 }
 
+/**
+ * wpa_scnprintf - Simpler-to-use snprintf function
+ * @buf: Output buffer
+ * @size: Buffer size
+ * @fmt: format
+ *
+ * Simpler snprintf version that doesn't require further error checks - the
+ * return value only indicates how many bytes were actually written, excluding
+ * the NULL byte (i.e., 0 on error, size-1 if buffer is not big enough).
+ */
+int wpa_scnprintf(char *buf, size_t size, const char *fmt, ...)
+{
+	va_list ap;
+	int ret;
+
+	if (!size)
+		return 0;
+
+	va_start(ap, fmt);
+	ret = vsnprintf(buf, size, fmt, ap);
+	va_end(ap);
+
+	if (ret < 0)
+		return 0;
+	if ((size_t) ret >= size)
+		return size - 1;
+
+	return ret;
+}
+
+
+int wpa_snprintf_hex_sep(char *buf, size_t buf_size, const u8 *data, size_t len,
+			 char sep)
+{
+	size_t i;
+	char *pos = buf, *end = buf + buf_size;
+	int ret;
+
+	if (buf_size == 0)
+		return 0;
+
+	for (i = 0; i < len; i++) {
+		ret = os_snprintf(pos, end - pos, "%02x%c",
+				  data[i], sep);
+		if (os_snprintf_error(end - pos, ret)) {
+			end[-1] = '\0';
+			return pos - buf;
+		}
+		pos += ret;
+	}
+	pos[-1] = '\0';
+	return pos - buf;
+}
+
 
 static inline int _wpa_snprintf_hex(char *buf, size_t buf_size, const u8 *data,
 				    size_t len, int uppercase)
@@ -195,7 +314,7 @@
 	for (i = 0; i < len; i++) {
 		ret = os_snprintf(pos, end - pos, uppercase ? "%02X" : "%02x",
 				  data[i]);
-		if (ret < 0 || ret >= end - pos) {
+		if (os_snprintf_error(end - pos, ret)) {
 			end[-1] = '\0';
 			return pos - buf;
 		}
@@ -491,7 +610,7 @@
  */
 const char * wpa_ssid_txt(const u8 *ssid, size_t ssid_len)
 {
-	static char ssid_txt[32 * 4 + 1];
+	static char ssid_txt[SSID_MAX_LEN * 4 + 1];
 
 	if (ssid == NULL) {
 		ssid_txt[0] = '\0';
@@ -578,21 +697,6 @@
 }
 
 
-int find_first_bit(u32 value)
-{
-	int pos = 0;
-
-	while (value) {
-		if (value & 0x1)
-			return pos;
-		value >>= 1;
-		pos++;
-	}
-
-	return -1;
-}
-
-
 size_t merge_byte_arrays(u8 *res, size_t res_len,
 			 const u8 *src1, size_t src1_len,
 			 const u8 *src2, size_t src2_len)
@@ -726,7 +830,7 @@
 			res = os_snprintf(pos, end - pos, "%s%u-%u",
 					  i == 0 ? "" : ",",
 					  range->min, range->max);
-		if (res < 0 || res > end - pos) {
+		if (os_snprintf_error(end - pos, res)) {
 			os_free(buf);
 			return NULL;
 		}
@@ -866,3 +970,127 @@
 	addr[0] |= 0x02; /* locally administered */
 	return 0;
 }
+
+
+/**
+ * str_token - Get next token from a string
+ * @buf: String to tokenize. Note that the string might be modified.
+ * @delim: String of delimiters
+ * @context: Pointer to save our context. Should be initialized with
+ *	NULL on the first call, and passed for any further call.
+ * Returns: The next token, NULL if there are no more valid tokens.
+ */
+char * str_token(char *str, const char *delim, char **context)
+{
+	char *end, *pos = str;
+
+	if (*context)
+		pos = *context;
+
+	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;
+}
+
+
+size_t utf8_unescape(const char *inp, size_t in_size,
+		     char *outp, size_t out_size)
+{
+	size_t res_size = 0;
+
+	if (!inp || !outp)
+		return 0;
+
+	if (!in_size)
+		in_size = os_strlen(inp);
+
+	/* Advance past leading single quote */
+	if (*inp == '\'' && in_size) {
+		inp++;
+		in_size--;
+	}
+
+	while (in_size--) {
+		if (res_size >= out_size)
+			return 0;
+
+		switch (*inp) {
+		case '\'':
+			/* Terminate on bare single quote */
+			*outp = '\0';
+			return res_size;
+
+		case '\\':
+			if (!in_size--)
+				return 0;
+			inp++;
+			/* fall through */
+
+		default:
+			*outp++ = *inp++;
+			res_size++;
+		}
+	}
+
+	/* NUL terminate if space allows */
+	if (res_size < out_size)
+		*outp = '\0';
+
+	return res_size;
+}
+
+
+size_t utf8_escape(const char *inp, size_t in_size,
+		   char *outp, size_t out_size)
+{
+	size_t res_size = 0;
+
+	if (!inp || !outp)
+		return 0;
+
+	/* inp may or may not be NUL terminated, but must be if 0 size
+	 * is specified */
+	if (!in_size)
+		in_size = os_strlen(inp);
+
+	while (in_size--) {
+		if (res_size++ >= out_size)
+			return 0;
+
+		switch (*inp) {
+		case '\\':
+		case '\'':
+			if (res_size++ >= out_size)
+				return 0;
+			*outp++ = '\\';
+			/* fall through */
+
+		default:
+			*outp++ = *inp++;
+			break;
+		}
+	}
+
+	/* NUL terminate if space allows */
+	if (res_size < out_size)
+		*outp = '\0';
+
+	return res_size;
+}
+
+
+int is_ctrl_char(char c)
+{
+	return c > 0 && c < 32;
+}
diff --git a/src/utils/common.h b/src/utils/common.h
index 14d9ad1..88318f5 100644
--- a/src/utils/common.h
+++ b/src/utils/common.h
@@ -164,6 +164,7 @@
 #define be_to_host16(n) wpa_swap_16(n)
 #define host_to_be16(n) wpa_swap_16(n)
 #define le_to_host32(n) (n)
+#define host_to_le32(n) (n)
 #define be_to_host32(n) wpa_swap_32(n)
 #define host_to_be32(n) wpa_swap_32(n)
 
@@ -329,6 +330,9 @@
 #ifndef ETH_ALEN
 #define ETH_ALEN 6
 #endif
+#ifndef ETH_HLEN
+#define ETH_HLEN 14
+#endif
 #ifndef IFNAMSIZ
 #define IFNAMSIZ 16
 #endif
@@ -468,16 +472,22 @@
 #endif /* __must_check */
 
 int hwaddr_aton(const char *txt, u8 *addr);
+int hwaddr_masked_aton(const char *txt, u8 *addr, u8 *mask, u8 maskable);
 int hwaddr_compact_aton(const char *txt, u8 *addr);
 int hwaddr_aton2(const char *txt, u8 *addr);
 int hex2byte(const char *hex);
 int hexstr2bin(const char *hex, u8 *buf, size_t len);
 void inc_byte_array(u8 *counter, size_t len);
 void wpa_get_ntp_timestamp(u8 *buf);
+int wpa_scnprintf(char *buf, size_t size, const char *fmt, ...);
+int wpa_snprintf_hex_sep(char *buf, size_t buf_size, const u8 *data, size_t len,
+			 char sep);
 int wpa_snprintf_hex(char *buf, size_t buf_size, const u8 *data, size_t len);
 int wpa_snprintf_hex_uppercase(char *buf, size_t buf_size, const u8 *data,
 			       size_t len);
 
+int hwaddr_mask_txt(char *buf, size_t len, const u8 *addr, const u8 *mask);
+
 #ifdef CONFIG_NATIVE_WINDOWS
 void wpa_unicode2ascii_inplace(TCHAR *str);
 TCHAR * wpa_strdup_tchar(const char *str);
@@ -493,7 +503,6 @@
 
 char * wpa_config_parse_string(const char *value, size_t *len);
 int is_hex(const u8 *data, size_t len);
-int find_first_bit(u32 value);
 size_t merge_byte_arrays(u8 *res, size_t res_len,
 			 const u8 *src1, size_t src1_len,
 			 const u8 *src2, size_t src2_len);
@@ -534,13 +543,19 @@
 
 #define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
 
-
 void str_clear_free(char *str);
 void bin_clear_free(void *bin, size_t len);
 
 int random_mac_addr(u8 *addr);
 int random_mac_addr_keep_oui(u8 *addr);
 
+char * str_token(char *str, const char *delim, char **context);
+size_t utf8_escape(const char *inp, size_t in_size,
+		   char *outp, size_t out_size);
+size_t utf8_unescape(const char *inp, size_t in_size,
+		     char *outp, size_t out_size);
+int is_ctrl_char(char c);
+
 
 /*
  * gcc 4.4 ends up generating strict-aliasing warnings about some very common
diff --git a/src/utils/eloop.c b/src/utils/eloop.c
index 0da6de4..4a565eb 100644
--- a/src/utils/eloop.c
+++ b/src/utils/eloop.c
@@ -242,8 +242,10 @@
 	eloop_trace_sock_remove_ref(table);
 	tmp = os_realloc_array(table->table, table->count + 1,
 			       sizeof(struct eloop_sock));
-	if (tmp == NULL)
+	if (tmp == NULL) {
+		eloop_trace_sock_add_ref(table);
 		return -1;
+	}
 
 	tmp[table->count].sock = sock;
 	tmp[table->count].eloop_data = eloop_data;
diff --git a/src/utils/http_curl.c b/src/utils/http_curl.c
index 0c18269..653eb54 100644
--- a/src/utils/http_curl.c
+++ b/src/utils/http_curl.c
@@ -855,8 +855,10 @@
 	struct http_cert hcert;
 	int ret;
 
-	if (ctx->cert_cb == NULL)
+	if (ctx->cert_cb == NULL) {
+		wpa_printf(MSG_DEBUG, "%s: no cert_cb configured", __func__);
 		return 0;
+	}
 
 	if (0) {
 		BIO *out;
@@ -950,7 +952,8 @@
 	ssl_ctx = ssl->ctx;
 	ctx = SSL_CTX_get_app_data(ssl_ctx);
 
-	wpa_printf(MSG_DEBUG, "curl_cb_ssl_verify");
+	wpa_printf(MSG_DEBUG, "curl_cb_ssl_verify, preverify_ok: %d",
+		   preverify_ok);
 
 	err = X509_STORE_CTX_get_error(x509_ctx);
 	err_str = X509_verify_cert_error_string(err);
@@ -1084,7 +1087,7 @@
 
 		if (X509_STORE_add_cert(store, ctx->peer_issuer) != 1) {
 			tls_show_errors(__func__,
-					"OpenSSL: Could not add issuer to certificate store\n");
+					"OpenSSL: Could not add issuer to certificate store");
 		}
 		certs = sk_X509_new_null();
 		if (certs) {
@@ -1093,17 +1096,17 @@
 			if (cert && !sk_X509_push(certs, cert)) {
 				tls_show_errors(
 					__func__,
-					"OpenSSL: Could not add issuer to OCSP responder trust store\n");
+					"OpenSSL: Could not add issuer to OCSP responder trust store");
 				X509_free(cert);
 				sk_X509_free(certs);
 				certs = NULL;
 			}
-			if (ctx->peer_issuer_issuer) {
+			if (certs && ctx->peer_issuer_issuer) {
 				cert = X509_dup(ctx->peer_issuer_issuer);
 				if (cert && !sk_X509_push(certs, cert)) {
 					tls_show_errors(
 						__func__,
-						"OpenSSL: Could not add issuer to OCSP responder trust store\n");
+						"OpenSSL: Could not add issuer's issuer to OCSP responder trust store");
 					X509_free(cert);
 				}
 			}
@@ -1249,9 +1252,14 @@
 			      const char *client_key)
 {
 	CURL *curl;
+#ifdef EAP_TLS_OPENSSL
+	const char *extra = " tls=openssl";
+#else /* EAP_TLS_OPENSSL */
+	const char *extra = "";
+#endif /* EAP_TLS_OPENSSL */
 
 	wpa_printf(MSG_DEBUG, "Start HTTP client: address=%s ca_fname=%s "
-		   "username=%s", address, ca_fname, username);
+		   "username=%s%s", address, ca_fname, username, extra);
 
 	curl = curl_easy_init();
 	if (curl == NULL)
diff --git a/src/utils/list.h b/src/utils/list.h
index 6881130..ee2f485 100644
--- a/src/utils/list.h
+++ b/src/utils/list.h
@@ -17,6 +17,8 @@
 	struct dl_list *prev;
 };
 
+#define DL_LIST_HEAD_INIT(l) { &(l), &(l) }
+
 static inline void dl_list_init(struct dl_list *list)
 {
 	list->next = list;
diff --git a/src/utils/os.h b/src/utils/os.h
index b9247d8..8913854 100644
--- a/src/utils/os.h
+++ b/src/utils/os.h
@@ -247,6 +247,13 @@
 int os_file_exists(const char *fname);
 
 /**
+ * os_fsync - 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);
+
+/**
  * os_zalloc - Allocate and zero memory
  * @size: Number of bytes to allocate
  * Returns: Pointer to allocated and zeroed memory or %NULL on failure
@@ -549,6 +556,12 @@
 #endif /* OS_NO_C_LIB_DEFINES */
 
 
+static inline int os_snprintf_error(size_t size, int res)
+{
+	return res < 0 || (unsigned int) res >= size;
+}
+
+
 static inline void * os_realloc_array(void *ptr, size_t nmemb, size_t size)
 {
 	if (size && nmemb > (~(size_t) 0) / size)
diff --git a/src/utils/os_internal.c b/src/utils/os_internal.c
index 90b6688..b8fb2db 100644
--- a/src/utils/os_internal.c
+++ b/src/utils/os_internal.c
@@ -17,9 +17,11 @@
  */
 
 #include "includes.h"
+#include <time.h>
+#include <sys/wait.h>
 
 #undef OS_REJECT_C_LIB_FUNCTIONS
-#include "os.h"
+#include "common.h"
 
 void os_sleep(os_time_t sec, os_time_t usec)
 {
@@ -96,7 +98,7 @@
 int os_daemonize(const char *pid_file)
 {
 	if (daemon(0, 0)) {
-		perror("daemon");
+		wpa_printf(MSG_ERROR, "daemon: %s", strerror(errno));
 		return -1;
 	}
 
@@ -167,8 +169,8 @@
 		}
 	}
 
-	cwd_len = strlen(cwd);
-	rel_len = strlen(rel_path);
+	cwd_len = os_strlen(cwd);
+	rel_len = os_strlen(rel_path);
 	ret_len = cwd_len + 1 + rel_len + 1;
 	ret = os_malloc(ret_len);
 	if (ret) {
@@ -241,6 +243,12 @@
 }
 
 
+int os_fsync(FILE *stream)
+{
+	return 0;
+}
+
+
 void * os_zalloc(size_t size)
 {
 	void *n = os_malloc(size);
@@ -506,3 +514,57 @@
 		str[size - 1] = '\0';
 	return ret;
 }
+
+
+int os_exec(const char *program, const char *arg, int wait_completion)
+{
+	pid_t pid;
+	int pid_status;
+
+	pid = fork();
+	if (pid < 0) {
+		wpa_printf(MSG_ERROR, "fork: %s", strerror(errno));
+		return -1;
+	}
+
+	if (pid == 0) {
+		/* run the external command in the child process */
+		const int MAX_ARG = 30;
+		char *_program, *_arg, *pos;
+		char *argv[MAX_ARG + 1];
+		int i;
+
+		_program = os_strdup(program);
+		_arg = os_strdup(arg);
+
+		argv[0] = _program;
+
+		i = 1;
+		pos = _arg;
+		while (i < MAX_ARG && pos && *pos) {
+			while (*pos == ' ')
+				pos++;
+			if (*pos == '\0')
+				break;
+			argv[i++] = pos;
+			pos = os_strchr(pos, ' ');
+			if (pos)
+				*pos++ = '\0';
+		}
+		argv[i] = NULL;
+
+		execv(program, argv);
+		wpa_printf(MSG_ERROR, "execv: %s", strerror(errno));
+		os_free(_program);
+		os_free(_arg);
+		exit(0);
+		return -1;
+	}
+
+	if (wait_completion) {
+		/* wait for the child process to complete in the parent */
+		waitpid(pid, &pid_status, 0);
+	}
+
+	return 0;
+}
diff --git a/src/utils/os_none.c b/src/utils/os_none.c
index 2649111..96d243d 100644
--- a/src/utils/os_none.c
+++ b/src/utils/os_none.c
@@ -102,6 +102,12 @@
 }
 
 
+int os_fsync(FILE *stream)
+{
+	return 0;
+}
+
+
 void * os_zalloc(size_t size)
 {
 	return NULL;
@@ -234,3 +240,9 @@
 	return 0;
 }
 #endif /* OS_NO_C_LIB_DEFINES */
+
+
+int os_exec(const char *program, const char *arg, int wait_completion)
+{
+	return -1;
+}
diff --git a/src/utils/os_unix.c b/src/utils/os_unix.c
index 523a4d0..ac73f7a 100644
--- a/src/utils/os_unix.c
+++ b/src/utils/os_unix.c
@@ -26,7 +26,7 @@
 #include "trace.h"
 #include "list.h"
 
-static struct dl_list alloc_list;
+static struct dl_list alloc_list = DL_LIST_HEAD_INIT(alloc_list);
 
 #define ALLOC_MAGIC 0xa84ef1b2
 #define FREED_MAGIC 0x67fd487a
@@ -321,9 +321,6 @@
 	capset(&header, &cap);
 #endif /* ANDROID */
 
-#ifdef WPA_TRACE
-	dl_list_init(&alloc_list);
-#endif /* WPA_TRACE */
 	return 0;
 }
 
@@ -418,6 +415,14 @@
 }
 
 
+int os_fsync(FILE *stream)
+{
+	if (!fflush(stream))
+		return fsync(fileno(stream));
+	return -1;
+}
+
+
 #ifndef WPA_TRACE
 void * os_zalloc(size_t size)
 {
@@ -467,9 +472,105 @@
 
 #ifdef WPA_TRACE
 
+#if defined(WPA_TRACE_BFD) && defined(CONFIG_TESTING_OPTIONS)
+char wpa_trace_fail_func[256] = { 0 };
+unsigned int wpa_trace_fail_after;
+
+static int testing_fail_alloc(void)
+{
+	const char *func[WPA_TRACE_LEN];
+	size_t i, res, len;
+	char *pos, *next;
+	int match;
+
+	if (!wpa_trace_fail_after)
+		return 0;
+
+	res = wpa_trace_calling_func(func, WPA_TRACE_LEN);
+	i = 0;
+	if (i < res && os_strcmp(func[i], __func__) == 0)
+		i++;
+	if (i < res && os_strcmp(func[i], "os_malloc") == 0)
+		i++;
+	if (i < res && os_strcmp(func[i], "os_zalloc") == 0)
+		i++;
+	if (i < res && os_strcmp(func[i], "os_calloc") == 0)
+		i++;
+	if (i < res && os_strcmp(func[i], "os_realloc") == 0)
+		i++;
+	if (i < res && os_strcmp(func[i], "os_realloc_array") == 0)
+		i++;
+	if (i < res && os_strcmp(func[i], "os_strdup") == 0)
+		i++;
+
+	pos = wpa_trace_fail_func;
+
+	match = 0;
+	while (i < res) {
+		int allow_skip = 1;
+		int maybe = 0;
+
+		if (*pos == '=') {
+			allow_skip = 0;
+			pos++;
+		} else if (*pos == '?') {
+			maybe = 1;
+			pos++;
+		}
+		next = os_strchr(pos, ';');
+		if (next)
+			len = next - pos;
+		else
+			len = os_strlen(pos);
+		if (os_memcmp(pos, func[i], len) != 0) {
+			if (maybe && next) {
+				pos = next + 1;
+				continue;
+			}
+			if (allow_skip) {
+				i++;
+				continue;
+			}
+			return 0;
+		}
+		if (!next) {
+			match = 1;
+			break;
+		}
+		pos = next + 1;
+		i++;
+	}
+	if (!match)
+		return 0;
+
+	wpa_trace_fail_after--;
+	if (wpa_trace_fail_after == 0) {
+		wpa_printf(MSG_INFO, "TESTING: fail allocation at %s",
+			   wpa_trace_fail_func);
+		for (i = 0; i < res; i++)
+			wpa_printf(MSG_INFO, "backtrace[%d] = %s",
+				   (int) i, func[i]);
+		return 1;
+	}
+
+	return 0;
+}
+
+#else
+
+static inline int testing_fail_alloc(void)
+{
+	return 0;
+}
+#endif
+
 void * os_malloc(size_t size)
 {
 	struct os_alloc_trace *a;
+
+	if (testing_fail_alloc())
+		return NULL;
+
 	a = malloc(sizeof(*a) + size);
 	if (a == NULL)
 		return NULL;
diff --git a/src/utils/os_win32.c b/src/utils/os_win32.c
index 57ee132..890abf4 100644
--- a/src/utils/os_win32.c
+++ b/src/utils/os_win32.c
@@ -12,6 +12,7 @@
 #include <wincrypt.h>
 
 #include "os.h"
+#include "common.h"
 
 void os_sleep(os_time_t sec, os_time_t usec)
 {
@@ -215,6 +216,24 @@
 }
 
 
+int os_fsync(FILE *stream)
+{
+	HANDLE hFile;
+
+	if (stream == NULL)
+		return -1;
+
+	hFile = _get_osfhandle(_fileno(stream));
+	if (hFile == INVALID_HANDLE_VALUE)
+		return -1;
+
+	if (!FlushFileBuffers(hFile))
+		return -1;
+
+	return 0;
+}
+
+
 void * os_zalloc(size_t size)
 {
 	return calloc(1, size);
diff --git a/src/utils/pcsc_funcs.c b/src/utils/pcsc_funcs.c
index d955dc4..6f5ea93 100644
--- a/src/utils/pcsc_funcs.c
+++ b/src/utils/pcsc_funcs.c
@@ -281,77 +281,82 @@
 	wpa_hexdump(MSG_DEBUG, "SCARD: file header FSP template",
 		    pos, end - pos);
 
-	while (pos + 1 < end) {
+	while (end - pos >= 2) {
+		unsigned char type, len;
+
+		type = pos[0];
+		len = pos[1];
 		wpa_printf(MSG_MSGDUMP, "SCARD: file header TLV 0x%02x len=%d",
-			   pos[0], pos[1]);
-		if (pos + 2 + pos[1] > end)
+			   type, len);
+		pos += 2;
+
+		if (len > (unsigned int) (end - pos))
 			break;
 
-		switch (pos[0]) {
+		switch (type) {
 		case USIM_TLV_FILE_DESC:
 			wpa_hexdump(MSG_MSGDUMP, "SCARD: File Descriptor TLV",
-				    pos + 2, pos[1]);
+				    pos, len);
 			break;
 		case USIM_TLV_FILE_ID:
 			wpa_hexdump(MSG_MSGDUMP, "SCARD: File Identifier TLV",
-				    pos + 2, pos[1]);
+				    pos, len);
 			break;
 		case USIM_TLV_DF_NAME:
 			wpa_hexdump(MSG_MSGDUMP, "SCARD: DF name (AID) TLV",
-				    pos + 2, pos[1]);
+				    pos, len);
 			break;
 		case USIM_TLV_PROPR_INFO:
 			wpa_hexdump(MSG_MSGDUMP, "SCARD: Proprietary "
-				    "information TLV", pos + 2, pos[1]);
+				    "information TLV", pos, len);
 			break;
 		case USIM_TLV_LIFE_CYCLE_STATUS:
 			wpa_hexdump(MSG_MSGDUMP, "SCARD: Life Cycle Status "
-				    "Integer TLV", pos + 2, pos[1]);
+				    "Integer TLV", pos, len);
 			break;
 		case USIM_TLV_FILE_SIZE:
 			wpa_hexdump(MSG_MSGDUMP, "SCARD: File size TLV",
-				    pos + 2, pos[1]);
-			if ((pos[1] == 1 || pos[1] == 2) && file_len) {
-				if (pos[1] == 1)
-					*file_len = (int) pos[2];
+				    pos, len);
+			if ((len == 1 || len == 2) && file_len) {
+				if (len == 1)
+					*file_len = (int) pos[0];
 				else
-					*file_len = ((int) pos[2] << 8) |
-						(int) pos[3];
+					*file_len = WPA_GET_BE16(pos);
 				wpa_printf(MSG_DEBUG, "SCARD: file_size=%d",
 					   *file_len);
 			}
 			break;
 		case USIM_TLV_TOTAL_FILE_SIZE:
 			wpa_hexdump(MSG_MSGDUMP, "SCARD: Total file size TLV",
-				    pos + 2, pos[1]);
+				    pos, len);
 			break;
 		case USIM_TLV_PIN_STATUS_TEMPLATE:
 			wpa_hexdump(MSG_MSGDUMP, "SCARD: PIN Status Template "
-				    "DO TLV", pos + 2, pos[1]);
-			if (pos[1] >= 2 && pos[2] == USIM_PS_DO_TAG &&
-			    pos[3] >= 1 && ps_do) {
+				    "DO TLV", pos, len);
+			if (len >= 2 && pos[0] == USIM_PS_DO_TAG &&
+			    pos[1] >= 1 && ps_do) {
 				wpa_printf(MSG_DEBUG, "SCARD: PS_DO=0x%02x",
-					   pos[4]);
-				*ps_do = (int) pos[4];
+					   pos[2]);
+				*ps_do = (int) pos[2];
 			}
 			break;
 		case USIM_TLV_SHORT_FILE_ID:
 			wpa_hexdump(MSG_MSGDUMP, "SCARD: Short File "
-				    "Identifier (SFI) TLV", pos + 2, pos[1]);
+				    "Identifier (SFI) TLV", pos, len);
 			break;
 		case USIM_TLV_SECURITY_ATTR_8B:
 		case USIM_TLV_SECURITY_ATTR_8C:
 		case USIM_TLV_SECURITY_ATTR_AB:
 			wpa_hexdump(MSG_MSGDUMP, "SCARD: Security attribute "
-				    "TLV", pos + 2, pos[1]);
+				    "TLV", pos, len);
 			break;
 		default:
 			wpa_hexdump(MSG_MSGDUMP, "SCARD: Unrecognized TLV",
-				    pos, 2 + pos[1]);
+				    pos, len);
 			break;
 		}
 
-		pos += 2 + pos[1];
+		pos += len;
 
 		if (pos == end)
 			return 0;
@@ -397,10 +402,12 @@
 		unsigned char rid[5];
 		unsigned char appl_code[2]; /* 0x1002 for 3G USIM */
 	} *efdir;
-	unsigned char buf[127];
+	unsigned char buf[127], *aid_pos;
 	size_t blen;
+	unsigned int aid_len = 0;
 
 	efdir = (struct efdir *) buf;
+	aid_pos = &buf[4];
 	blen = sizeof(buf);
 	if (scard_select_file(scard, SCARD_FILE_EF_DIR, buf, &blen)) {
 		wpa_printf(MSG_DEBUG, "SCARD: Failed to read EF_DIR");
@@ -449,14 +456,15 @@
 			continue;
 		}
 
-		if (efdir->aid_len < 1 || efdir->aid_len > 16) {
-			wpa_printf(MSG_DEBUG, "SCARD: Invalid AID length %d",
-				   efdir->aid_len);
+		aid_len = efdir->aid_len;
+		if (aid_len < 1 || aid_len > 16) {
+			wpa_printf(MSG_DEBUG, "SCARD: Invalid AID length %u",
+				   aid_len);
 			continue;
 		}
 
 		wpa_hexdump(MSG_DEBUG, "SCARD: AID from EF_DIR record",
-			    efdir->rid, efdir->aid_len);
+			    aid_pos, aid_len);
 
 		if (efdir->appl_code[0] == 0x10 &&
 		    efdir->appl_code[1] == 0x02) {
@@ -472,14 +480,14 @@
 		return -1;
 	}
 
-	if (efdir->aid_len > maxlen) {
+	if (aid_len > maxlen) {
 		wpa_printf(MSG_DEBUG, "SCARD: Too long AID");
 		return -1;
 	}
 
-	os_memcpy(aid, efdir->rid, efdir->aid_len);
+	os_memcpy(aid, aid_pos, aid_len);
 
-	return efdir->aid_len;
+	return aid_len;
 }
 
 
@@ -1096,7 +1104,7 @@
 	}
 
 	if (scard->sim_type == SCARD_GSM_SIM) {
-		blen = (buf[2] << 8) | buf[3];
+		blen = WPA_GET_BE16(&buf[2]);
 	} else {
 		int file_size;
 		if (scard_parse_fsp_templ(buf, blen, NULL, &file_size))
@@ -1170,7 +1178,7 @@
 	}
 
 	if (scard->sim_type == SCARD_GSM_SIM) {
-		file_size = (buf[2] << 8) | buf[3];
+		file_size = WPA_GET_BE16(&buf[2]);
 	} else {
 		if (scard_parse_fsp_templ(buf, blen, NULL, &file_size))
 			return -3;
diff --git a/src/utils/radiotap.c b/src/utils/radiotap.c
index 197a4af..f8f815a 100644
--- a/src/utils/radiotap.c
+++ b/src/utils/radiotap.c
@@ -109,6 +109,7 @@
 	iterator->_arg_index = 0;
 	iterator->_bitmap_shifter = get_unaligned_le32(&radiotap_header->it_present);
 	iterator->_arg = (uint8_t *)radiotap_header + sizeof(*radiotap_header);
+	iterator->_next_ns_data = NULL;
 	iterator->_reset_on_ext = 0;
 	iterator->_next_bitmap = &radiotap_header->it_present;
 	iterator->_next_bitmap++;
@@ -154,6 +155,8 @@
 	}
 
 	iterator->this_arg = iterator->_arg;
+	iterator->this_arg_index = 0;
+	iterator->this_arg_size = 0;
 
 	/* we are all initialized happily */
 
diff --git a/src/utils/trace.c b/src/utils/trace.c
index 6044f5f..8484d27 100644
--- a/src/utils/trace.c
+++ b/src/utils/trace.c
@@ -33,7 +33,7 @@
 	os_snprintf(exe, sizeof(exe) - 1, "/proc/%u/exe", getpid());
 	len = readlink(exe, fname, sizeof(fname) - 1);
 	if (len < 0 || len >= (int) sizeof(fname)) {
-		perror("readlink");
+		wpa_printf(MSG_ERROR, "readlink: %s", strerror(errno));
 		return;
 	}
 	fname[len] = '\0';
@@ -160,7 +160,7 @@
 	if (abfd == NULL)
 		return;
 
-	data.pc = (bfd_vma) pc;
+	data.pc = (bfd_hostptr_t) pc;
 	data.found = FALSE;
 	bfd_map_over_sections(abfd, find_addr_sect, &data);
 
@@ -201,7 +201,7 @@
 	if (abfd == NULL)
 		return NULL;
 
-	data.pc = (bfd_vma) pc;
+	data.pc = (bfd_hostptr_t) pc;
 	data.found = FALSE;
 	bfd_map_over_sections(abfd, find_addr_sect, &data);
 
@@ -243,6 +243,53 @@
 	wpa_trace_bfd_addr(pc);
 }
 
+
+size_t wpa_trace_calling_func(const char *buf[], size_t len)
+{
+	bfd *abfd;
+	void *btrace_res[WPA_TRACE_LEN];
+	int i, btrace_num;
+	size_t pos = 0;
+
+	if (len == 0)
+		return 0;
+	if (len > WPA_TRACE_LEN)
+		len = WPA_TRACE_LEN;
+
+	wpa_trace_bfd_init();
+	abfd = cached_abfd;
+	if (!abfd)
+		return 0;
+
+	btrace_num = backtrace(btrace_res, len);
+	if (btrace_num < 1)
+		return 0;
+
+	for (i = 0; i < btrace_num; i++) {
+		struct bfd_data data;
+
+		data.pc = (bfd_hostptr_t) btrace_res[i];
+		data.found = FALSE;
+		bfd_map_over_sections(abfd, find_addr_sect, &data);
+
+		while (data.found) {
+			if (data.function &&
+			    (pos > 0 ||
+			     os_strcmp(data.function, __func__) != 0)) {
+				buf[pos++] = data.function;
+				if (pos == len)
+					return pos;
+			}
+
+			data.found = bfd_find_inliner_info(abfd, &data.filename,
+							   &data.function,
+							   &data.line);
+		}
+	}
+
+	return pos;
+}
+
 #else /* WPA_TRACE_BFD */
 
 #define wpa_trace_bfd_init() do { } while (0)
diff --git a/src/utils/trace.h b/src/utils/trace.h
index 38f43fb..43ed86c 100644
--- a/src/utils/trace.h
+++ b/src/utils/trace.h
@@ -40,6 +40,7 @@
 			dl_list_del(&(ptr)->wpa_trace_ref_##name.list); \
 	} while (0)
 void wpa_trace_check_ref(const void *addr);
+size_t wpa_trace_calling_func(const char *buf[], size_t len);
 
 #else /* WPA_TRACE */
 
diff --git a/src/utils/utils_module_tests.c b/src/utils/utils_module_tests.c
index 9a9ec40..b2c7e08 100644
--- a/src/utils/utils_module_tests.c
+++ b/src/utils/utils_module_tests.c
@@ -1,6 +1,6 @@
 /*
  * utils module tests
- * Copyright (c) 2014, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2014-2015, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -9,9 +9,11 @@
 #include "utils/includes.h"
 
 #include "utils/common.h"
+#include "common/ieee802_11_defs.h"
 #include "utils/bitfield.h"
 #include "utils/ext_password.h"
 #include "utils/trace.h"
+#include "utils/base64.h"
 
 
 struct printf_test_data {
@@ -84,6 +86,15 @@
 		}
 	}
 
+	if (printf_decode(bin, 3, "abcde") != 2)
+		errors++;
+
+	if (printf_decode(bin, 3, "\\xa") != 1 || bin[0] != 10)
+		errors++;
+
+	if (printf_decode(bin, 3, "\\a") != 1 || bin[0] != 'a')
+		errors++;
+
 	if (errors) {
 		wpa_printf(MSG_ERROR, "%d printf test(s) failed", errors);
 		return -1;
@@ -167,6 +178,17 @@
 
 	bitfield_free(bf);
 
+	bf = bitfield_alloc(8);
+	if (bf == NULL)
+		return -1;
+	if (bitfield_get_first_zero(bf) != 0)
+		errors++;
+	for (i = 0; i < 8; i++)
+		bitfield_set(bf, i);
+	if (bitfield_get_first_zero(bf) != -1)
+		errors++;
+	bitfield_free(bf);
+
 	if (errors) {
 		wpa_printf(MSG_ERROR, "%d bitfield test(s) failed", errors);
 		return -1;
@@ -249,6 +271,153 @@
 }
 
 
+static int base64_tests(void)
+{
+	int errors = 0;
+	unsigned char *res;
+	size_t res_len;
+
+	wpa_printf(MSG_INFO, "base64 tests");
+
+	res = base64_encode((const unsigned char *) "", ~0, &res_len);
+	if (res) {
+		errors++;
+		os_free(res);
+	}
+
+	res = base64_encode((const unsigned char *) "=", 1, &res_len);
+	if (!res || res_len != 5 || res[0] != 'P' || res[1] != 'Q' ||
+	    res[2] != '=' || res[3] != '=' || res[4] != '\n')
+		errors++;
+	os_free(res);
+
+	res = base64_encode((const unsigned char *) "=", 1, NULL);
+	if (!res || res[0] != 'P' || res[1] != 'Q' ||
+	    res[2] != '=' || res[3] != '=' || res[4] != '\n')
+		errors++;
+	os_free(res);
+
+	res = base64_decode((const unsigned char *) "", 0, &res_len);
+	if (res) {
+		errors++;
+		os_free(res);
+	}
+
+	res = base64_decode((const unsigned char *) "a", 1, &res_len);
+	if (res) {
+		errors++;
+		os_free(res);
+	}
+
+	res = base64_decode((const unsigned char *) "====", 4, &res_len);
+	if (res) {
+		errors++;
+		os_free(res);
+	}
+
+	res = base64_decode((const unsigned char *) "PQ==", 4, &res_len);
+	if (!res || res_len != 1 || res[0] != '=')
+		errors++;
+	os_free(res);
+
+	res = base64_decode((const unsigned char *) "P.Q-=!=*", 8, &res_len);
+	if (!res || res_len != 1 || res[0] != '=')
+		errors++;
+	os_free(res);
+
+	if (errors) {
+		wpa_printf(MSG_ERROR, "%d base64 test(s) failed", errors);
+		return -1;
+	}
+
+	return 0;
+}
+
+
+static int common_tests(void)
+{
+	char buf[3];
+	u8 addr[ETH_ALEN] = { 1, 2, 3, 4, 5, 6 };
+	u8 bin[3];
+	int errors = 0;
+	struct wpa_freq_range_list ranges;
+	size_t len;
+	const char *txt;
+	u8 ssid[255];
+
+	wpa_printf(MSG_INFO, "common tests");
+
+	if (hwaddr_mask_txt(buf, 3, addr, addr) != -1)
+		errors++;
+
+	if (wpa_scnprintf(buf, 0, "hello") != 0 ||
+	    wpa_scnprintf(buf, 3, "hello") != 2)
+		errors++;
+
+	if (wpa_snprintf_hex(buf, 0, addr, ETH_ALEN) != 0 ||
+	    wpa_snprintf_hex(buf, 3, addr, ETH_ALEN) != 2)
+		errors++;
+
+	if (merge_byte_arrays(bin, 3, addr, ETH_ALEN, NULL, 0) != 3 ||
+	    merge_byte_arrays(bin, 3, NULL, 0, addr, ETH_ALEN) != 3)
+		errors++;
+
+	if (dup_binstr(NULL, 0) != NULL)
+		errors++;
+
+	if (freq_range_list_includes(NULL, 0) != 0)
+		errors++;
+
+	os_memset(&ranges, 0, sizeof(ranges));
+	if (freq_range_list_parse(&ranges, "") != 0 ||
+	    freq_range_list_includes(&ranges, 0) != 0 ||
+	    freq_range_list_str(&ranges) != NULL)
+		errors++;
+
+	if (utf8_unescape(NULL, 0, buf, sizeof(buf)) != 0 ||
+	    utf8_unescape("a", 1, NULL, 0) != 0 ||
+	    utf8_unescape("a\\", 2, buf, sizeof(buf)) != 0 ||
+	    utf8_unescape("abcde", 5, buf, sizeof(buf)) != 0 ||
+	    utf8_unescape("abc", 3, buf, 3) != 3)
+		errors++;
+
+	if (utf8_unescape("a", 0, buf, sizeof(buf)) != 1 || buf[0] != 'a')
+		errors++;
+
+	if (utf8_unescape("\\b", 2, buf, sizeof(buf)) != 1 || buf[0] != 'b')
+		errors++;
+
+	if (utf8_escape(NULL, 0, buf, sizeof(buf)) != 0 ||
+	    utf8_escape("a", 1, NULL, 0) != 0 ||
+	    utf8_escape("abcde", 5, buf, sizeof(buf)) != 0 ||
+	    utf8_escape("a\\bcde", 6, buf, sizeof(buf)) != 0 ||
+	    utf8_escape("ab\\cde", 6, buf, sizeof(buf)) != 0 ||
+	    utf8_escape("abc\\de", 6, buf, sizeof(buf)) != 0 ||
+	    utf8_escape("abc", 3, buf, 3) != 3)
+		errors++;
+
+	if (utf8_escape("a", 0, buf, sizeof(buf)) != 1 || buf[0] != 'a')
+		errors++;
+
+	os_memset(ssid, 0, sizeof(ssid));
+	txt = wpa_ssid_txt(ssid, sizeof(ssid));
+	len = os_strlen(txt);
+	/* Verify that SSID_MAX_LEN * 4 buffer limit is enforced. */
+	if (len != SSID_MAX_LEN * 4) {
+		wpa_printf(MSG_ERROR,
+			   "Unexpected wpa_ssid_txt() result with too long SSID");
+		errors++;
+	}
+
+	if (errors) {
+		wpa_printf(MSG_ERROR, "%d common test(s) failed", errors);
+		return -1;
+	}
+
+	return 0;
+}
+
+
 int utils_module_tests(void)
 {
 	int ret = 0;
@@ -259,6 +428,8 @@
 	    ext_password_tests() < 0 ||
 	    trace_tests() < 0 ||
 	    bitfield_tests() < 0 ||
+	    base64_tests() < 0 ||
+	    common_tests() < 0 ||
 	    int_array_tests() < 0)
 		ret = -1;
 
diff --git a/src/utils/uuid.c b/src/utils/uuid.c
index 2aa4bcb..0f224f9 100644
--- a/src/utils/uuid.c
+++ b/src/utils/uuid.c
@@ -55,7 +55,7 @@
 			  bin[4], bin[5], bin[6], bin[7],
 			  bin[8], bin[9], bin[10], bin[11],
 			  bin[12], bin[13], bin[14], bin[15]);
-	if (len < 0 || (size_t) len >= max_len)
+	if (os_snprintf_error(max_len, len))
 		return -1;
 	return 0;
 }
diff --git a/src/utils/wpa_debug.c b/src/utils/wpa_debug.c
index 68cbace..b7a6dba 100644
--- a/src/utils/wpa_debug.c
+++ b/src/utils/wpa_debug.c
@@ -307,7 +307,7 @@
 				    "%s - hexdump(len=%lu):%s%s",
 				    title, (long unsigned int) len, display,
 				    len > slen ? " ..." : "");
-		os_free(strbuf);
+		bin_clear_free(strbuf, 1 + 3 * slen);
 		return;
 	}
 #else /* CONFIG_ANDROID_LOG */
@@ -339,7 +339,7 @@
 
 		syslog(syslog_priority(level), "%s - hexdump(len=%lu):%s",
 		       title, (unsigned long) len, display);
-		os_free(strbuf);
+		bin_clear_free(strbuf, 1 + 3 * len);
 		return;
 	}
 #endif /* CONFIG_DEBUG_SYSLOG */
@@ -555,6 +555,8 @@
 #ifndef _WIN32
 	setvbuf(out_file, NULL, _IOLBF, 0);
 #endif /* _WIN32 */
+#else /* CONFIG_DEBUG_FILE */
+	(void)path;
 #endif /* CONFIG_DEBUG_FILE */
 	return 0;
 }
@@ -572,6 +574,14 @@
 #endif /* CONFIG_DEBUG_FILE */
 }
 
+
+void wpa_debug_setup_stdout(void)
+{
+#ifndef _WIN32
+	setvbuf(stdout, NULL, _IOLBF, 0);
+#endif /* _WIN32 */
+}
+
 #endif /* CONFIG_NO_STDOUT_DEBUG */
 
 
@@ -617,7 +627,7 @@
 		if (ifname) {
 			int res = os_snprintf(prefix, sizeof(prefix), "%s: ",
 					      ifname);
-			if (res < 0 || res >= (int) sizeof(prefix))
+			if (os_snprintf_error(sizeof(prefix), res))
 				prefix[0] = '\0';
 		}
 	}
@@ -625,8 +635,8 @@
 	va_end(ap);
 	wpa_printf(level, "%s%s", prefix, buf);
 	if (wpa_msg_cb)
-		wpa_msg_cb(ctx, level, 0, buf, len);
-	os_free(buf);
+		wpa_msg_cb(ctx, level, WPA_MSG_PER_INTERFACE, buf, len);
+	bin_clear_free(buf, buflen);
 }
 
 
@@ -653,8 +663,8 @@
 	va_start(ap, fmt);
 	len = vsnprintf(buf, buflen, fmt, ap);
 	va_end(ap);
-	wpa_msg_cb(ctx, level, 0, buf, len);
-	os_free(buf);
+	wpa_msg_cb(ctx, level, WPA_MSG_PER_INTERFACE, buf, len);
+	bin_clear_free(buf, buflen);
 }
 
 
@@ -680,8 +690,8 @@
 	va_end(ap);
 	wpa_printf(level, "%s", buf);
 	if (wpa_msg_cb)
-		wpa_msg_cb(ctx, level, 1, buf, len);
-	os_free(buf);
+		wpa_msg_cb(ctx, level, WPA_MSG_GLOBAL, buf, len);
+	bin_clear_free(buf, buflen);
 }
 
 
@@ -708,8 +718,8 @@
 	va_start(ap, fmt);
 	len = vsnprintf(buf, buflen, fmt, ap);
 	va_end(ap);
-	wpa_msg_cb(ctx, level, 1, buf, len);
-	os_free(buf);
+	wpa_msg_cb(ctx, level, WPA_MSG_GLOBAL, buf, len);
+	bin_clear_free(buf, buflen);
 }
 
 
@@ -735,8 +745,8 @@
 	va_end(ap);
 	wpa_printf(level, "%s", buf);
 	if (wpa_msg_cb)
-		wpa_msg_cb(ctx, level, 2, buf, len);
-	os_free(buf);
+		wpa_msg_cb(ctx, level, WPA_MSG_NO_GLOBAL, buf, len);
+	bin_clear_free(buf, buflen);
 }
 
 #endif /* CONFIG_NO_WPA_MSG */
@@ -779,6 +789,6 @@
 			   MAC2STR(addr), buf);
 	else
 		wpa_printf(MSG_DEBUG, "hostapd_logger: %s", buf);
-	os_free(buf);
+	bin_clear_free(buf, buflen);
 }
 #endif /* CONFIG_NO_HOSTAPD_LOGGER */
diff --git a/src/utils/wpa_debug.h b/src/utils/wpa_debug.h
index 391f197..5fdc50e 100644
--- a/src/utils/wpa_debug.h
+++ b/src/utils/wpa_debug.h
@@ -34,6 +34,7 @@
 #define wpa_hexdump_ascii_key(l,t,b,le) do { } while (0)
 #define wpa_debug_open_file(p) do { } while (0)
 #define wpa_debug_close_file() do { } while (0)
+#define wpa_debug_setup_stdout() do { } while (0)
 #define wpa_dbg(args...) do { } while (0)
 
 static inline int wpa_debug_reopen_file(void)
@@ -46,6 +47,7 @@
 int wpa_debug_open_file(const char *path);
 int wpa_debug_reopen_file(void);
 void wpa_debug_close_file(void);
+void wpa_debug_setup_stdout(void);
 
 /**
  * wpa_debug_printf_timestamp - Print timestamp for debug output
@@ -241,7 +243,13 @@
 void wpa_msg_no_global(void *ctx, int level, const char *fmt, ...)
 PRINTF_FORMAT(3, 4);
 
-typedef void (*wpa_msg_cb_func)(void *ctx, int level, int global,
+enum wpa_msg_type {
+	WPA_MSG_PER_INTERFACE,
+	WPA_MSG_GLOBAL,
+	WPA_MSG_NO_GLOBAL,
+};
+
+typedef void (*wpa_msg_cb_func)(void *ctx, int level, enum wpa_msg_type type,
 				const char *txt, size_t len);
 
 /**
diff --git a/src/utils/wpabuf.c b/src/utils/wpabuf.c
index b257b36..7aafa0a 100644
--- a/src/utils/wpabuf.c
+++ b/src/utils/wpabuf.c
@@ -205,6 +205,15 @@
 }
 
 
+void wpabuf_clear_free(struct wpabuf *buf)
+{
+	if (buf) {
+		os_memset(wpabuf_mhead(buf), 0, wpabuf_len(buf));
+		wpabuf_free(buf);
+	}
+}
+
+
 void * wpabuf_put(struct wpabuf *buf, size_t len)
 {
 	void *tmp = wpabuf_mhead_u8(buf) + wpabuf_len(buf);
diff --git a/src/utils/wpabuf.h b/src/utils/wpabuf.h
index dbce925..c3ef1ba 100644
--- a/src/utils/wpabuf.h
+++ b/src/utils/wpabuf.h
@@ -32,6 +32,7 @@
 struct wpabuf * wpabuf_alloc_copy(const void *data, size_t len);
 struct wpabuf * wpabuf_dup(const struct wpabuf *src);
 void wpabuf_free(struct wpabuf *buf);
+void wpabuf_clear_free(struct wpabuf *buf);
 void * wpabuf_put(struct wpabuf *buf, size_t len);
 struct wpabuf * wpabuf_concat(struct wpabuf *a, struct wpabuf *b);
 struct wpabuf * wpabuf_zeropad(struct wpabuf *buf, size_t len);
diff --git a/src/wps/Makefile b/src/wps/Makefile
index adfd3df..4806fe8 100644
--- a/src/wps/Makefile
+++ b/src/wps/Makefile
@@ -1,8 +1,41 @@
-all:
-	@echo Nothing to be made.
+all: libwps.a
 
 clean:
-	rm -f *~ *.o *.d *.gcno *.gcda *.gcov
+	rm -f *~ *.o *.d *.gcno *.gcda *.gcov libwps.a
 
 install:
 	@echo Nothing to be made.
+
+include ../lib.rules
+
+CFLAGS += -DCONFIG_P2P
+CFLAGS += -DCONFIG_WPS_OOB
+CFLAGS += -DCONFIG_WPS_NFC
+
+LIB_OBJS= \
+	http_client.o \
+	httpread.o \
+	http_server.o \
+	ndef.o \
+	upnp_xml.o \
+	wps_attr_build.o \
+	wps_attr_parse.o \
+	wps_attr_process.o \
+	wps.o \
+	wps_common.o \
+	wps_dev_attr.o \
+	wps_enrollee.o \
+	wps_er.o \
+	wps_er_ssdp.o \
+	wps_module_tests.o \
+	wps_registrar.o \
+	wps_upnp_ap.o \
+	wps_upnp.o \
+	wps_upnp_event.o \
+	wps_upnp_ssdp.o \
+	wps_upnp_web.o
+
+libwps.a: $(LIB_OBJS)
+	$(AR) crT $@ $?
+
+-include $(OBJS:%.o=%.d)
diff --git a/src/wps/httpread.c b/src/wps/httpread.c
index 2f08f37..180b572 100644
--- a/src/wps/httpread.c
+++ b/src/wps/httpread.c
@@ -44,16 +44,6 @@
 #define HTTPREAD_HEADER_MAX_SIZE 4096   /* max allowed for headers */
 #define HTTPREAD_BODYBUF_DELTA 4096     /* increase allocation by this */
 
-#if 0
-/* httpread_debug -- set this global variable > 0 e.g. from debugger
- * to enable debugs (larger numbers for more debugs)
- * Make this a #define of 0 to eliminate the debugging code.
- */
-int httpread_debug = 99;
-#else
-#define httpread_debug 0        /* eliminates even the debugging code */
-#endif
-
 
 /* control instance -- actual definition (opaque to application)
  */
@@ -136,8 +126,7 @@
  */
 void httpread_destroy(struct httpread *h)
 {
-	if (httpread_debug >= 10)
-		wpa_printf(MSG_DEBUG, "ENTER httpread_destroy(%p)", h);
+	wpa_printf(MSG_DEBUG, "httpread_destroy(%p)", h);
 	if (!h)
 		return;
 
@@ -177,6 +166,12 @@
 		if (!isdigit(*hbp))
 			return -1;
 		h->content_length = atol(hbp);
+		if (h->content_length < 0 || h->content_length > h->max_bytes) {
+			wpa_printf(MSG_DEBUG,
+				   "httpread: Unacceptable Content-Length %d",
+				   h->content_length);
+			return -1;
+		}
 		h->got_content_length = 1;
 		return 0;
 	}
@@ -380,15 +375,16 @@
 	char *bbp;      /* pointer into body buffer */
 	char readbuf[HTTPREAD_READBUF_SIZE];  /* temp use to read into */
 
-	if (httpread_debug >= 20)
-		wpa_printf(MSG_DEBUG, "ENTER httpread_read_handler(%p)", h);
-
 	/* read some at a time, then search for the interal
 	 * boundaries between header and data and etc.
 	 */
+	wpa_printf(MSG_DEBUG, "httpread: Trying to read more data(%p)", h);
 	nread = read(h->sd, readbuf, sizeof(readbuf));
-	if (nread < 0)
+	if (nread < 0) {
+		wpa_printf(MSG_DEBUG, "httpread failed: %s", strerror(errno));
 		goto bad;
+	}
+	wpa_hexdump_ascii(MSG_MSGDUMP, "httpread - read", readbuf, nread);
 	if (nread == 0) {
 		/* end of transmission... this may be normal
 		 * or may be an error... in some cases we can't
@@ -411,8 +407,7 @@
 		 * although dropped connections can cause false
 		 * end
 		 */
-		if (httpread_debug >= 10)
-			wpa_printf(MSG_DEBUG, "httpread ok eof(%p)", h);
+		wpa_printf(MSG_DEBUG, "httpread ok eof(%p)", h);
 		h->got_body = 1;
 		goto got_file;
 	}
@@ -432,6 +427,8 @@
 			if (nread == 0)
 				goto get_more;
 			if (h->hdr_nbytes == HTTPREAD_HEADER_MAX_SIZE) {
+				wpa_printf(MSG_DEBUG,
+					   "httpread: Too long header");
 				goto bad;
 			}
 			*hbp++ = *rbp++;
@@ -453,16 +450,13 @@
 			goto bad;
 		}
 		if (h->max_bytes == 0) {
-			if (httpread_debug >= 10)
-				wpa_printf(MSG_DEBUG,
-					   "httpread no body hdr end(%p)", h);
+			wpa_printf(MSG_DEBUG, "httpread no body hdr end(%p)",
+				   h);
 			goto got_file;
 		}
 		if (h->got_content_length && h->content_length == 0) {
-			if (httpread_debug >= 10)
-				wpa_printf(MSG_DEBUG,
-					   "httpread zero content length(%p)",
-					   h);
+			wpa_printf(MSG_DEBUG,
+				   "httpread zero content length(%p)", h);
 			goto got_file;
 		}
 	}
@@ -475,9 +469,7 @@
 	    !os_strncasecmp(h->hdr, "HEAD", 4) ||
 	    !os_strncasecmp(h->hdr, "GET", 3)) {
 		if (!h->got_body) {
-			if (httpread_debug >= 10)
-				wpa_printf(MSG_DEBUG,
-					   "httpread NO BODY for sp. type");
+			wpa_printf(MSG_DEBUG, "httpread NO BODY for sp. type");
 		}
 		h->got_body = 1;
 		goto got_file;
@@ -498,8 +490,12 @@
 			char *new_body;
 			int new_alloc_nbytes;
 
-			if (h->body_nbytes >= h->max_bytes)
+			if (h->body_nbytes >= h->max_bytes) {
+				wpa_printf(MSG_DEBUG,
+					   "httpread: body_nbytes=%d >= max_bytes=%d",
+					   h->body_nbytes, h->max_bytes);
 				goto bad;
+			}
 			new_alloc_nbytes = h->body_alloc_nbytes +
 				HTTPREAD_BODYBUF_DELTA;
 			/* For content-length case, the first time
@@ -509,9 +505,20 @@
 			if (h->got_content_length &&
 			    new_alloc_nbytes < (h->content_length + 1))
 				new_alloc_nbytes = h->content_length + 1;
-			if ((new_body = os_realloc(h->body, new_alloc_nbytes))
-			    == NULL)
+			if (new_alloc_nbytes < h->body_alloc_nbytes ||
+			    new_alloc_nbytes > h->max_bytes) {
+				wpa_printf(MSG_DEBUG,
+					   "httpread: Unacceptable body length %d",
+					   new_alloc_nbytes);
 				goto bad;
+			}
+			if ((new_body = os_realloc(h->body, new_alloc_nbytes))
+			    == NULL) {
+				wpa_printf(MSG_DEBUG,
+					   "httpread: Failed to reallocate buffer (len=%d)",
+					   new_alloc_nbytes);
+				goto bad;
+			}
 
 			h->body = new_body;
 			h->body_alloc_nbytes = new_alloc_nbytes;
@@ -530,9 +537,19 @@
 					/* hdr line consists solely
 					 * of a hex numeral and CFLF
 					 */
-					if (!isxdigit(*cbp))
+					if (!isxdigit(*cbp)) {
+						wpa_printf(MSG_DEBUG,
+							   "httpread: Unexpected chunk header value (not a hex digit)");
 						goto bad;
+					}
 					h->chunk_size = strtoul(cbp, NULL, 16);
+					if (h->chunk_size < 0 ||
+					    h->chunk_size > h->max_bytes) {
+						wpa_printf(MSG_DEBUG,
+							   "httpread: Invalid chunk size %d",
+							   h->chunk_size);
+						goto bad;
+					}
 					/* throw away chunk header
 					 * so we have only real data
 					 */
@@ -542,10 +559,9 @@
 						/* end of chunking */
 						/* trailer follows */
 						h->in_trailer = 1;
-						if (httpread_debug >= 20)
-							wpa_printf(
-								MSG_DEBUG,
-								"httpread end chunks(%p)", h);
+						wpa_printf(MSG_DEBUG,
+							   "httpread end chunks(%p)",
+							   h);
 						break;
 					}
 					h->in_chunk_data = 1;
@@ -563,8 +579,11 @@
 					 */
 					if (bbp[-1] == '\n' &&
 					    bbp[-2] == '\r') {
-					} else
+					} else {
+						wpa_printf(MSG_DEBUG,
+							   "httpread: Invalid chunk end");
 						goto bad;
+					}
 					h->body_nbytes -= 2;
 					bbp -= 2;
 					h->chunk_start = h->body_nbytes;
@@ -574,10 +593,8 @@
 			} else if (h->got_content_length &&
 				   h->body_nbytes >= h->content_length) {
 				h->got_body = 1;
-				if (httpread_debug >= 10)
-					wpa_printf(
-						MSG_DEBUG,
-						"httpread got content(%p)", h);
+				wpa_printf(MSG_DEBUG,
+					   "httpread got content(%p)", h);
 				goto got_file;
 			}
 			if (nread <= 0)
@@ -601,6 +618,11 @@
 				ncopy = nread;
 			}
 			/* Note: should never be 0 */
+			if (ncopy < 0) {
+				wpa_printf(MSG_DEBUG,
+					   "httpread: Invalid ncopy=%d", ncopy);
+				goto bad;
+			}
 			if (ncopy > nread)
 				ncopy = nread;
 			os_memcpy(bbp, rbp, ncopy);
@@ -635,10 +657,9 @@
 				if (c == '\n') {
 					h->trailer_state = trailer_line_begin;
 					h->in_trailer = 0;
-					if (httpread_debug >= 10)
-						wpa_printf(
-							MSG_DEBUG,
-							"httpread got content(%p)", h);
+					wpa_printf(MSG_DEBUG,
+						   "httpread got content(%p)",
+						   h);
 					h->got_body = 1;
 					goto got_file;
 				}
@@ -666,13 +687,14 @@
 	return;
 
 get_more:
+	wpa_printf(MSG_DEBUG, "httpread: get more (%p)", h);
 	return;
 
 got_file:
-	if (httpread_debug >= 10)
-		wpa_printf(MSG_DEBUG,
-			   "httpread got file %d bytes type %d",
-			   h->body_nbytes, h->hdr_type);
+	wpa_printf(MSG_DEBUG, "httpread got file %d bytes type %d",
+		   h->body_nbytes, h->hdr_type);
+	wpa_hexdump_ascii(MSG_MSGDUMP, "httpread: body",
+			  h->body, h->body_nbytes);
 	/* Null terminate for convenience of some applications */
 	if (h->body)
 		h->body[h->body_nbytes] = 0; /* null terminate */
diff --git a/src/wps/ndef.c b/src/wps/ndef.c
index d45dfc8..8d1ce1e 100644
--- a/src/wps/ndef.c
+++ b/src/wps/ndef.c
@@ -29,8 +29,8 @@
 	u32 total_length;
 };
 
-static char wifi_handover_type[] = "application/vnd.wfa.wsc";
-static char p2p_handover_type[] = "application/vnd.wfa.p2p";
+static const char wifi_handover_type[] = "application/vnd.wfa.wsc";
+static const char p2p_handover_type[] = "application/vnd.wfa.p2p";
 
 static int ndef_parse_record(const u8 *data, u32 size,
 			     struct ndef_record *record)
@@ -97,7 +97,7 @@
 }
 
 
-static struct wpabuf * ndef_build_record(u8 flags, void *type,
+static struct wpabuf * ndef_build_record(u8 flags, const void *type,
 					 u8 type_length, void *id,
 					 u8 id_length,
 					 const struct wpabuf *payload)
diff --git a/src/wps/wps.c b/src/wps/wps.c
index b0f6887..498f11f 100644
--- a/src/wps/wps.c
+++ b/src/wps/wps.c
@@ -560,7 +560,7 @@
 					  "wps_state=configured\n");
 		else
 			ret = 0;
-		if (ret < 0 || ret >= end - pos)
+		if (os_snprintf_error(end - pos, ret))
 			return pos - buf;
 		pos += ret;
 	}
@@ -568,7 +568,7 @@
 	if (attr.ap_setup_locked && *attr.ap_setup_locked) {
 		ret = os_snprintf(pos, end - pos,
 				  "wps_ap_setup_locked=1\n");
-		if (ret < 0 || ret >= end - pos)
+		if (os_snprintf_error(end - pos, ret))
 			return pos - buf;
 		pos += ret;
 	}
@@ -576,7 +576,7 @@
 	if (attr.selected_registrar && *attr.selected_registrar) {
 		ret = os_snprintf(pos, end - pos,
 				  "wps_selected_registrar=1\n");
-		if (ret < 0 || ret >= end - pos)
+		if (os_snprintf_error(end - pos, ret))
 			return pos - buf;
 		pos += ret;
 	}
@@ -585,7 +585,7 @@
 		ret = os_snprintf(pos, end - pos,
 				  "wps_device_password_id=%u\n",
 				  WPA_GET_BE16(attr.dev_password_id));
-		if (ret < 0 || ret >= end - pos)
+		if (os_snprintf_error(end - pos, ret))
 			return pos - buf;
 		pos += ret;
 	}
@@ -595,7 +595,7 @@
 				  "wps_selected_registrar_config_methods="
 				  "0x%04x\n",
 				  WPA_GET_BE16(attr.sel_reg_config_methods));
-		if (ret < 0 || ret >= end - pos)
+		if (os_snprintf_error(end - pos, ret))
 			return pos - buf;
 		pos += ret;
 	}
@@ -607,7 +607,7 @@
 				  wps_dev_type_bin2str(attr.primary_dev_type,
 						       devtype,
 						       sizeof(devtype)));
-		if (ret < 0 || ret >= end - pos)
+		if (os_snprintf_error(end - pos, ret))
 			return pos - buf;
 		pos += ret;
 	}
@@ -618,7 +618,8 @@
 		if (str == NULL)
 			return pos - buf;
 		for (i = 0; i < attr.dev_name_len; i++) {
-			if (attr.dev_name[i] < 32)
+			if (attr.dev_name[i] == 0 ||
+			    is_ctrl_char(attr.dev_name[i]))
 				str[i] = '_';
 			else
 				str[i] = attr.dev_name[i];
@@ -626,7 +627,7 @@
 		str[i] = '\0';
 		ret = os_snprintf(pos, end - pos, "wps_device_name=%s\n", str);
 		os_free(str);
-		if (ret < 0 || ret >= end - pos)
+		if (os_snprintf_error(end - pos, ret))
 			return pos - buf;
 		pos += ret;
 	}
@@ -635,7 +636,7 @@
 		ret = os_snprintf(pos, end - pos,
 				  "wps_config_methods=0x%04x\n",
 				  WPA_GET_BE16(attr.config_methods));
-		if (ret < 0 || ret >= end - pos)
+		if (os_snprintf_error(end - pos, ret))
 			return pos - buf;
 		pos += ret;
 	}
diff --git a/src/wps/wps.h b/src/wps/wps.h
index 192d283..2c91d16 100644
--- a/src/wps/wps.h
+++ b/src/wps/wps.h
@@ -9,6 +9,7 @@
 #ifndef WPS_H
 #define WPS_H
 
+#include "common/ieee802_11_defs.h"
 #include "wps_defs.h"
 
 /**
@@ -44,7 +45,7 @@
  * @cred_attr_len: Length of cred_attr in octets
  */
 struct wps_credential {
-	u8 ssid[32];
+	u8 ssid[SSID_MAX_LEN];
 	size_t ssid_len;
 	u16 auth_type;
 	u16 encr_type;
@@ -78,7 +79,7 @@
  * @sec_dev_type: Array of secondary device types
  * @num_sec_dev_type: Number of secondary device types
  * @os_version: OS Version
- * @rf_bands: RF bands (WPS_RF_24GHZ, WPS_RF_50GHZ flags)
+ * @rf_bands: RF bands (WPS_RF_24GHZ, WPS_RF_50GHZ, WPS_RF_60GHZ flags)
  * @p2p: Whether the device is a P2P device
  */
 struct wps_device_data {
@@ -623,7 +624,7 @@
 	 * Credentials. In addition, AP uses it when acting as an Enrollee to
 	 * notify Registrar of the current configuration.
 	 */
-	u8 ssid[32];
+	u8 ssid[SSID_MAX_LEN];
 
 	/**
 	 * ssid_len - Length of ssid in octets
@@ -819,6 +820,7 @@
 int wps_registrar_add_nfc_password_token(struct wps_registrar *reg,
 					 const u8 *oob_dev_pw,
 					 size_t oob_dev_pw_len);
+void wps_registrar_flush(struct wps_registrar *reg);
 
 int wps_build_credential_wrap(struct wpabuf *msg,
 			      const struct wps_credential *cred);
diff --git a/src/wps/wps_attr_parse.c b/src/wps/wps_attr_parse.c
index 40bc1ad..11a967b 100644
--- a/src/wps/wps_attr_parse.c
+++ b/src/wps/wps_attr_parse.c
@@ -447,25 +447,55 @@
 		break;
 	case ATTR_MANUFACTURER:
 		attr->manufacturer = pos;
-		attr->manufacturer_len = len;
+		if (len > WPS_MANUFACTURER_MAX_LEN)
+			attr->manufacturer_len = WPS_MANUFACTURER_MAX_LEN;
+		else
+			attr->manufacturer_len = len;
 		break;
 	case ATTR_MODEL_NAME:
 		attr->model_name = pos;
-		attr->model_name_len = len;
+		if (len > WPS_MODEL_NAME_MAX_LEN)
+			attr->model_name_len = WPS_MODEL_NAME_MAX_LEN;
+		else
+			attr->model_name_len = len;
 		break;
 	case ATTR_MODEL_NUMBER:
 		attr->model_number = pos;
-		attr->model_number_len = len;
+		if (len > WPS_MODEL_NUMBER_MAX_LEN)
+			attr->model_number_len = WPS_MODEL_NUMBER_MAX_LEN;
+		else
+			attr->model_number_len = len;
 		break;
 	case ATTR_SERIAL_NUMBER:
 		attr->serial_number = pos;
-		attr->serial_number_len = len;
+		if (len > WPS_SERIAL_NUMBER_MAX_LEN)
+			attr->serial_number_len = WPS_SERIAL_NUMBER_MAX_LEN;
+		else
+			attr->serial_number_len = len;
 		break;
 	case ATTR_DEV_NAME:
+		if (len > WPS_DEV_NAME_MAX_LEN) {
+			wpa_printf(MSG_DEBUG,
+				   "WPS: Ignore too long Device Name (len=%u)",
+				   len);
+			break;
+		}
 		attr->dev_name = pos;
 		attr->dev_name_len = len;
 		break;
 	case ATTR_PUBLIC_KEY:
+		/*
+		 * The Public Key attribute is supposed to be exactly 192 bytes
+		 * in length. Allow couple of bytes shorter one to try to
+		 * interoperate with implementations that do not use proper
+		 * zero-padding.
+		 */
+		if (len < 190 || len > 192) {
+			wpa_printf(MSG_DEBUG,
+				   "WPS: Ignore Public Key with unexpected length %u",
+				   len);
+			break;
+		}
 		attr->public_key = pos;
 		attr->public_key_len = len;
 		break;
@@ -485,6 +515,11 @@
 		attr->num_cred++;
 		break;
 	case ATTR_SSID:
+		if (len > SSID_MAX_LEN) {
+			wpa_printf(MSG_DEBUG,
+				   "WPS: Ignore too long SSID (len=%u)", len);
+			break;
+		}
 		attr->ssid = pos;
 		attr->ssid_len = len;
 		break;
diff --git a/src/wps/wps_common.c b/src/wps/wps_common.c
index a282348..16d466e 100644
--- a/src/wps/wps_common.c
+++ b/src/wps/wps_common.c
@@ -489,7 +489,7 @@
 	ret = os_snprintf(buf, buf_len, "%u-%08X-%u",
 			  WPA_GET_BE16(dev_type), WPA_GET_BE32(&dev_type[2]),
 			  WPA_GET_BE16(&dev_type[6]));
-	if (ret < 0 || (unsigned int) ret >= buf_len)
+	if (os_snprintf_error(buf_len, ret))
 		return NULL;
 
 	return buf;
@@ -535,6 +535,9 @@
 #ifdef CONFIG_WPS_NFC
 		methods |= WPS_CONFIG_NFC_INTERFACE;
 #endif /* CONFIG_WPS_NFC */
+#ifdef CONFIG_P2P
+		methods |= WPS_CONFIG_P2PS;
+#endif /* CONFIG_P2P */
 	} else {
 		if (os_strstr(str, "ethernet"))
 			methods |= WPS_CONFIG_ETHERNET;
@@ -560,6 +563,8 @@
 			methods |= WPS_CONFIG_VIRT_PUSHBUTTON;
 		if (os_strstr(str, "physical_push_button"))
 			methods |= WPS_CONFIG_PHY_PUSHBUTTON;
+		if (os_strstr(str, "p2ps"))
+			methods |= WPS_CONFIG_P2PS;
 	}
 
 	return methods;
@@ -759,6 +764,8 @@
 		rf_band = WPS_RF_24GHZ;
 	else if (mode == HOSTAPD_MODE_IEEE80211A)
 		rf_band = WPS_RF_50GHZ;
+	else if (mode == HOSTAPD_MODE_IEEE80211AD)
+		rf_band = WPS_RF_60GHZ;
 	else
 		return 0; /* Unknown band */
 	ap_channel = channel;
diff --git a/src/wps/wps_defs.h b/src/wps/wps_defs.h
index f483e2e..a23b979 100644
--- a/src/wps/wps_defs.h
+++ b/src/wps/wps_defs.h
@@ -41,6 +41,11 @@
 #define WPS_OOB_DEVICE_PASSWORD_MIN_LEN 16
 #define WPS_OOB_DEVICE_PASSWORD_LEN 32
 #define WPS_OOB_PUBKEY_HASH_LEN 20
+#define WPS_DEV_NAME_MAX_LEN 32
+#define WPS_MANUFACTURER_MAX_LEN 64
+#define WPS_MODEL_NAME_MAX_LEN 32
+#define WPS_MODEL_NUMBER_MAX_LEN 32
+#define WPS_SERIAL_NUMBER_MAX_LEN 32
 
 /* Attribute Types */
 enum wps_attribute {
@@ -154,7 +159,8 @@
 	DEV_PW_REKEY = 0x0003,
 	DEV_PW_PUSHBUTTON = 0x0004,
 	DEV_PW_REGISTRAR_SPECIFIED = 0x0005,
-	DEV_PW_NFC_CONNECTION_HANDOVER = 0x0007
+	DEV_PW_NFC_CONNECTION_HANDOVER = 0x0007,
+	DEV_PW_P2PS_DEFAULT = 0x0008
 };
 
 /* Message Type */
@@ -231,6 +237,7 @@
 /* RF Bands */
 #define WPS_RF_24GHZ 0x01
 #define WPS_RF_50GHZ 0x02
+#define WPS_RF_60GHZ 0x04
 
 /* Config Methods */
 #define WPS_CONFIG_USBA 0x0001
@@ -244,6 +251,7 @@
 #define WPS_CONFIG_KEYPAD 0x0100
 #define WPS_CONFIG_VIRT_PUSHBUTTON 0x0280
 #define WPS_CONFIG_PHY_PUSHBUTTON 0x0480
+#define WPS_CONFIG_P2PS 0x1000
 #define WPS_CONFIG_VIRT_DISPLAY 0x2008
 #define WPS_CONFIG_PHY_DISPLAY 0x4008
 
@@ -279,30 +287,71 @@
 	WPS_DEV_DISPLAY = 7,
 	WPS_DEV_MULTIMEDIA = 8,
 	WPS_DEV_GAMING = 9,
-	WPS_DEV_PHONE = 10
+	WPS_DEV_PHONE = 10,
+	WPS_DEV_AUDIO = 11,
 };
 
 enum wps_dev_subcateg {
 	WPS_DEV_COMPUTER_PC = 1,
 	WPS_DEV_COMPUTER_SERVER = 2,
 	WPS_DEV_COMPUTER_MEDIA_CENTER = 3,
+	WPS_DEV_COMPUTER_ULTRA_MOBILE = 4,
+	WPS_DEV_COMPUTER_NOTEBOOK = 5,
+	WPS_DEV_COMPUTER_DESKTOP = 6,
+	WPS_DEV_COMPUTER_MID = 7,
+	WPS_DEV_COMPUTER_NETBOOK = 8,
+	WPS_DEV_COMPUTER_TABLET = 9,
+	WPS_DEV_INPUT_KEYBOARD = 1,
+	WPS_DEV_INPUT_MOUSE = 2,
+	WPS_DEV_INPUT_JOYSTICK = 3,
+	WPS_DEV_INPUT_TRACKBALL = 4,
+	WPS_DEV_INPUT_GAMING = 5,
+	WPS_DEV_INPUT_REMOTE = 6,
+	WPS_DEV_INPUT_TOUCHSCREEN = 7,
+	WPS_DEV_INPUT_BIOMETRIC_READER = 8,
+	WPS_DEV_INPUT_BARCODE_READER = 9,
 	WPS_DEV_PRINTER_PRINTER = 1,
 	WPS_DEV_PRINTER_SCANNER = 2,
+	WPS_DEV_PRINTER_FAX = 3,
+	WPS_DEV_PRINTER_COPIER = 4,
+	WPS_DEV_PRINTER_ALL_IN_ONE = 5,
 	WPS_DEV_CAMERA_DIGITAL_STILL_CAMERA = 1,
+	WPS_DEV_CAMERA_VIDEO = 2,
+	WPS_DEV_CAMERA_WEB = 3,
+	WPS_DEV_CAMERA_SECURITY = 4,
 	WPS_DEV_STORAGE_NAS = 1,
 	WPS_DEV_NETWORK_INFRA_AP = 1,
 	WPS_DEV_NETWORK_INFRA_ROUTER = 2,
 	WPS_DEV_NETWORK_INFRA_SWITCH = 3,
+	WPS_DEV_NETWORK_INFRA_GATEWAY = 4,
+	WPS_DEV_NETWORK_INFRA_BRIDGE = 5,
 	WPS_DEV_DISPLAY_TV = 1,
 	WPS_DEV_DISPLAY_PICTURE_FRAME = 2,
 	WPS_DEV_DISPLAY_PROJECTOR = 3,
+	WPS_DEV_DISPLAY_MONITOR = 4,
 	WPS_DEV_MULTIMEDIA_DAR = 1,
 	WPS_DEV_MULTIMEDIA_PVR = 2,
 	WPS_DEV_MULTIMEDIA_MCX = 3,
+	WPS_DEV_MULTIMEDIA_SET_TOP_BOX = 4,
+	WPS_DEV_MULTIMEDIA_MEDIA_SERVER = 5,
+	WPS_DEV_MULTIMEDIA_PORTABLE_VIDEO_PLAYER = 6,
 	WPS_DEV_GAMING_XBOX = 1,
 	WPS_DEV_GAMING_XBOX360 = 2,
 	WPS_DEV_GAMING_PLAYSTATION = 3,
-	WPS_DEV_PHONE_WINDOWS_MOBILE = 1
+	WPS_DEV_GAMING_GAME_CONSOLE = 4,
+	WPS_DEV_GAMING_PORTABLE_DEVICE = 5,
+	WPS_DEV_PHONE_WINDOWS_MOBILE = 1,
+	WPS_DEV_PHONE_SINGLE_MODE = 2,
+	WPS_DEV_PHONE_DUAL_MODE = 3,
+	WPS_DEV_PHONE_SP_SINGLE_MODE = 4,
+	WPS_DEV_PHONE_SP_DUAL_MODE = 5,
+	WPS_DEV_AUDIO_TUNER_RECV = 1,
+	WPS_DEV_AUDIO_SPEAKERS = 2,
+	WPS_DEV_AUDIO_PMP = 3,
+	WPS_DEV_AUDIO_HEADSET = 4,
+	WPS_DEV_AUDIO_HEADPHONES = 5,
+	WPS_DEV_AUDIO_MICROPHONE = 6,
+	WPS_DEV_AUDIO_HOME_THEATRE = 7,
 };
 
 
diff --git a/src/wps/wps_enrollee.c b/src/wps/wps_enrollee.c
index 9f5a90c..89957b1 100644
--- a/src/wps/wps_enrollee.c
+++ b/src/wps/wps_enrollee.c
@@ -247,22 +247,48 @@
 
 static int wps_build_cred_auth_type(struct wps_data *wps, struct wpabuf *msg)
 {
-	wpa_printf(MSG_DEBUG, "WPS:  * Authentication Type (0x%x)",
-		   wps->wps->ap_auth_type);
+	u16 auth_type = wps->wps->ap_auth_type;
+
+	/*
+	 * Work around issues with Windows 7 WPS implementation not liking
+	 * multiple Authentication Type bits in M7 AP Settings attribute by
+	 * showing only the most secure option from current configuration.
+	 */
+	if (auth_type & WPS_AUTH_WPA2PSK)
+		auth_type = WPS_AUTH_WPA2PSK;
+	else if (auth_type & WPS_AUTH_WPAPSK)
+		auth_type = WPS_AUTH_WPAPSK;
+	else if (auth_type & WPS_AUTH_OPEN)
+		auth_type = WPS_AUTH_OPEN;
+
+	wpa_printf(MSG_DEBUG, "WPS:  * Authentication Type (0x%x)", auth_type);
 	wpabuf_put_be16(msg, ATTR_AUTH_TYPE);
 	wpabuf_put_be16(msg, 2);
-	wpabuf_put_be16(msg, wps->wps->ap_auth_type);
+	wpabuf_put_be16(msg, auth_type);
 	return 0;
 }
 
 
 static int wps_build_cred_encr_type(struct wps_data *wps, struct wpabuf *msg)
 {
-	wpa_printf(MSG_DEBUG, "WPS:  * Encryption Type (0x%x)",
-		   wps->wps->ap_encr_type);
+	u16 encr_type = wps->wps->ap_encr_type;
+
+	/*
+	 * Work around issues with Windows 7 WPS implementation not liking
+	 * multiple Encryption Type bits in M7 AP Settings attribute by
+	 * showing only the most secure option from current configuration.
+	 */
+	if (wps->wps->ap_auth_type & (WPS_AUTH_WPA2PSK | WPS_AUTH_WPAPSK)) {
+		if (encr_type & WPS_ENCR_AES)
+			encr_type = WPS_ENCR_AES;
+		else if (encr_type & WPS_ENCR_TKIP)
+			encr_type = WPS_ENCR_TKIP;
+	}
+
+	wpa_printf(MSG_DEBUG, "WPS:  * Encryption Type (0x%x)", encr_type);
 	wpabuf_put_be16(msg, ATTR_ENCR_TYPE);
 	wpabuf_put_be16(msg, 2);
-	wpabuf_put_be16(msg, wps->wps->ap_encr_type);
+	wpabuf_put_be16(msg, encr_type);
 	return 0;
 }
 
diff --git a/src/wps/wps_module_tests.c b/src/wps/wps_module_tests.c
index 6800e86..3506307 100644
--- a/src/wps/wps_module_tests.c
+++ b/src/wps/wps_module_tests.c
@@ -17,7 +17,7 @@
 	int extra;
 };
 
-struct wps_attr_parse_test wps_attr_parse_test_cases[] = {
+const struct wps_attr_parse_test wps_attr_parse_test_cases[] = {
 	/* Empty message */
 	{ "", 0, 0 },
 	/* Truncated attribute header */
@@ -271,7 +271,7 @@
 	for (i = 0; i < ARRAY_SIZE(wps_attr_parse_test_cases); i++) {
 		struct wpabuf *buf;
 		size_t len;
-		struct wps_attr_parse_test *test =
+		const struct wps_attr_parse_test *test =
 			&wps_attr_parse_test_cases[i];
 
 		len = os_strlen(test->data) / 2;
diff --git a/src/wps/wps_registrar.c b/src/wps/wps_registrar.c
index b90cc25..8bcf2b3 100644
--- a/src/wps/wps_registrar.c
+++ b/src/wps/wps_registrar.c
@@ -676,6 +676,22 @@
 }
 
 
+void wps_registrar_flush(struct wps_registrar *reg)
+{
+	if (reg == NULL)
+		return;
+	wps_free_pins(&reg->pins);
+	wps_free_nfc_pw_tokens(&reg->nfc_pw_tokens, 0);
+	wps_free_pbc_sessions(reg->pbc_sessions);
+	reg->pbc_sessions = NULL;
+	wps_free_devices(reg->devices);
+	reg->devices = NULL;
+#ifdef WPS_WORKAROUNDS
+	reg->pbc_ignore_start.sec = 0;
+#endif /* WPS_WORKAROUNDS */
+}
+
+
 /**
  * wps_registrar_deinit - Deinitialize WPS Registrar data
  * @reg: Registrar data from wps_registrar_init()
@@ -686,11 +702,8 @@
 		return;
 	eloop_cancel_timeout(wps_registrar_pbc_timeout, reg, NULL);
 	eloop_cancel_timeout(wps_registrar_set_selected_timeout, reg, NULL);
-	wps_free_pins(&reg->pins);
-	wps_free_nfc_pw_tokens(&reg->nfc_pw_tokens, 0);
-	wps_free_pbc_sessions(reg->pbc_sessions);
+	wps_registrar_flush(reg);
 	wpabuf_free(reg->extra_cred);
-	wps_free_devices(reg->devices);
 	os_free(reg);
 }
 
@@ -2565,6 +2578,7 @@
 
 	if (wps->dev_pw_id < 0x10 &&
 	    wps->dev_pw_id != DEV_PW_DEFAULT &&
+	    wps->dev_pw_id != DEV_PW_P2PS_DEFAULT &&
 	    wps->dev_pw_id != DEV_PW_USER_SPECIFIED &&
 	    wps->dev_pw_id != DEV_PW_MACHINE_SPECIFIED &&
 	    wps->dev_pw_id != DEV_PW_REGISTRAR_SPECIFIED &&
@@ -3212,8 +3226,13 @@
 		os_memset(&cred, 0, sizeof(cred));
 		os_memcpy(cred.ssid, wps->wps->ssid, wps->wps->ssid_len);
 		cred.ssid_len = wps->wps->ssid_len;
-		cred.auth_type = WPS_AUTH_WPAPSK | WPS_AUTH_WPA2PSK;
-		cred.encr_type = WPS_ENCR_TKIP | WPS_ENCR_AES;
+		if (wps->wps->rf_band_cb(wps->wps->cb_ctx) == WPS_RF_60GHZ) {
+			cred.auth_type = WPS_AUTH_WPA2PSK;
+			cred.encr_type = WPS_ENCR_AES;
+		} else {
+			cred.auth_type = WPS_AUTH_WPAPSK | WPS_AUTH_WPA2PSK;
+			cred.encr_type = WPS_ENCR_TKIP | WPS_ENCR_AES;
+		}
 		os_memcpy(cred.key, wps->new_psk, wps->new_psk_len);
 		cred.key_len = wps->new_psk_len;
 
@@ -3495,7 +3514,7 @@
 			  d->dev.model_name ? d->dev.model_name : "",
 			  d->dev.model_number ? d->dev.model_number : "",
 			  d->dev.serial_number ? d->dev.serial_number : "");
-	if (ret < 0 || (size_t) ret >= buflen - len)
+	if (os_snprintf_error(buflen - len, ret))
 		return len;
 	len += ret;
 
diff --git a/src/wps/wps_upnp.c b/src/wps/wps_upnp.c
index ae94a9f..933d734 100644
--- a/src/wps/wps_upnp.c
+++ b/src/wps/wps_upnp.c
@@ -251,13 +251,16 @@
  * use for constructing UUIDs for subscriptions. Presumably any method from
  * rfc4122 is good enough; I've chosen random number method.
  */
-static void uuid_make(u8 uuid[UUID_LEN])
+static int uuid_make(u8 uuid[UUID_LEN])
 {
-	os_get_random(uuid, UUID_LEN);
+	if (os_get_random(uuid, UUID_LEN) < 0)
+		return -1;
 
 	/* Replace certain bits as specified in rfc4122 or X.667 */
 	uuid[6] &= 0x0f; uuid[6] |= (4 << 4);   /* version 4 == random gen */
 	uuid[8] &= 0x3f; uuid[8] |= 0x80;
+
+	return 0;
 }
 
 
@@ -700,10 +703,12 @@
 	if (dl_list_len(&sm->subscriptions) >= MAX_SUBSCRIPTIONS) {
 		s = dl_list_first(&sm->subscriptions, struct subscription,
 				  list);
-		wpa_printf(MSG_INFO, "WPS UPnP: Too many subscriptions, "
-			   "trashing oldest");
-		dl_list_del(&s->list);
-		subscription_destroy(s);
+		if (s) {
+			wpa_printf(MSG_INFO,
+				   "WPS UPnP: Too many subscriptions, trashing oldest");
+			dl_list_del(&s->list);
+			subscription_destroy(s);
+		}
 	}
 
 	s = os_zalloc(sizeof(*s));
@@ -714,7 +719,10 @@
 
 	s->sm = sm;
 	s->timeout_time = expire;
-	uuid_make(s->uuid);
+	if (uuid_make(s->uuid) < 0) {
+		subscription_destroy(s);
+		return NULL;
+	}
 	subscr_addr_list_create(s, callback_urls);
 	if (dl_list_empty(&s->addr_list)) {
 		wpa_printf(MSG_DEBUG, "WPS UPnP: No valid callback URLs in "
diff --git a/src/wps/wps_upnp_ssdp.c b/src/wps/wps_upnp_ssdp.c
index 098571c..26a740d 100644
--- a/src/wps/wps_upnp_ssdp.c
+++ b/src/wps/wps_upnp_ssdp.c
@@ -317,7 +317,8 @@
 			 * (see notes above)
 			 */
 			next_timeout_msec = 0;
-			os_get_random((void *) &r, sizeof(r));
+			if (os_get_random((void *) &r, sizeof(r)) < 0)
+				r = 32768;
 			next_timeout_sec = UPNP_CACHE_SEC / 4 +
 				(((UPNP_CACHE_SEC / 4) * r) >> 16);
 			sm->advertise_count++;
diff --git a/src/wps/wps_validate.c b/src/wps/wps_validate.c
index 1c6a14b..267b565 100644
--- a/src/wps/wps_validate.c
+++ b/src/wps/wps_validate.c
@@ -224,6 +224,8 @@
 		return 0;
 	}
 	if (*rf_bands != WPS_RF_24GHZ && *rf_bands != WPS_RF_50GHZ &&
+	    *rf_bands != WPS_RF_60GHZ &&
+	    *rf_bands != (WPS_RF_24GHZ | WPS_RF_50GHZ | WPS_RF_60GHZ) &&
 	    *rf_bands != (WPS_RF_24GHZ | WPS_RF_50GHZ)) {
 		wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Rf Bands "
 			   "attribute value 0x%x", *rf_bands);
diff --git a/wpa_supplicant/Android.mk b/wpa_supplicant/Android.mk
index b5c9660..4b45f8c 100644
--- a/wpa_supplicant/Android.mk
+++ b/wpa_supplicant/Android.mk
@@ -10,8 +10,6 @@
 
 ifneq ($(BOARD_WPA_SUPPLICANT_DRIVER),)
   CONFIG_DRIVER_$(BOARD_WPA_SUPPLICANT_DRIVER) := y
-else
-  CONFIG_DRIVER_TEST := y
 endif
 
 include $(LOCAL_PATH)/android.config
@@ -29,8 +27,9 @@
 
 # Set Android extended P2P functionality
 L_CFLAGS += -DANDROID_P2P
+
 ifeq ($(BOARD_WPA_SUPPLICANT_PRIVATE_LIB),)
-L_CFLAGS += -DANDROID_P2P_STUB
+L_CFLAGS += -DANDROID_LIB_STUB
 endif
 
 # Disable roaming in wpa_supplicant
@@ -42,6 +41,9 @@
 L_CFLAGS += -DCONFIG_CTRL_IFACE_CLIENT_DIR=\"/data/misc/wifi/sockets\"
 L_CFLAGS += -DCONFIG_CTRL_IFACE_DIR=\"/data/system/wpa_supplicant\"
 
+# Use Android specific directory for wpa_cli command completion history
+L_CFLAGS += -DCONFIG_WPA_CLI_HISTORY_DIR=\"/data/misc/wifi\"
+
 # To force sizeof(enum) = 4
 ifeq ($(TARGET_ARCH),arm)
 L_CFLAGS += -mabi=aapcs-linux
@@ -84,6 +86,7 @@
 OBJS += src/utils/common.c
 OBJS += src/utils/wpa_debug.c
 OBJS += src/utils/wpabuf.c
+OBJS += wmm_ac.c
 OBJS_p = wpa_passphrase.c
 OBJS_p += src/utils/common.c
 OBJS_p += src/utils/wpa_debug.c
@@ -183,6 +186,17 @@
 L_CFLAGS += -DCONFIG_NO_SCAN_PROCESSING
 endif
 
+ifdef CONFIG_SUITEB
+L_CFLAGS += -DCONFIG_SUITEB
+NEED_SHA256=y
+NEED_AES_OMAC1=y
+endif
+
+ifdef CONFIG_SUITEB192
+L_CFLAGS += -DCONFIG_SUITEB192
+NEED_SHA384=y
+endif
+
 ifdef CONFIG_IEEE80211W
 L_CFLAGS += -DCONFIG_IEEE80211W
 NEED_SHA256=y
@@ -192,11 +206,24 @@
 ifdef CONFIG_IEEE80211R
 L_CFLAGS += -DCONFIG_IEEE80211R
 OBJS += src/rsn_supp/wpa_ft.c
-NEED_80211_COMMON=y
 NEED_SHA256=y
 NEED_AES_OMAC1=y
 endif
 
+ifdef CONFIG_MESH
+NEED_80211_COMMON=y
+NEED_SHA256=y
+NEED_AES_SIV=y
+NEED_AES_OMAC1=y
+NEED_AES_CTR=y
+CONFIG_SAE=y
+CONFIG_AP=y
+L_CFLAGS += -DCONFIG_MESH
+OBJS += mesh.c
+OBJS += mesh_mpm.c
+OBJS += mesh_rsn.c
+endif
+
 ifdef CONFIG_SAE
 L_CFLAGS += -DCONFIG_SAE
 OBJS += src/common/sae.c
@@ -247,6 +274,7 @@
 
 ifdef CONFIG_P2P
 OBJS += p2p_supplicant.c
+OBJS += p2p_supplicant_sd.c
 OBJS += src/p2p/p2p.c
 OBJS += src/p2p/p2p_utils.c
 OBJS += src/p2p/p2p_parse.c
@@ -262,7 +290,6 @@
 L_CFLAGS += -DCONFIG_P2P
 NEED_GAS=y
 NEED_OFFCHANNEL=y
-NEED_80211_COMMON=y
 CONFIG_WPS=y
 CONFIG_AP=y
 ifdef CONFIG_P2P_STRICT
@@ -328,6 +355,12 @@
 LIBS += -lpcap
 endif
 
+ifdef CONFIG_ERP
+L_CFLAGS += -DCONFIG_ERP
+NEED_SHA256=y
+NEED_HMAC_SHA256_KDF=y
+endif
+
 ifdef CONFIG_EAP_TLS
 # EAP-TLS
 ifeq ($(CONFIG_EAP_TLS), dyn)
@@ -345,7 +378,7 @@
 ifdef CONFIG_EAP_UNAUTH_TLS
 # EAP-UNAUTH-TLS
 L_CFLAGS += -DEAP_UNAUTH_TLS
-ifndef CONFIG_EAP_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
@@ -634,7 +667,6 @@
 NEED_DH_GROUPS=y
 NEED_SHA256=y
 NEED_BASE64=y
-NEED_80211_COMMON=y
 NEED_AES_CBC=y
 NEED_MODEXP=y
 
@@ -743,7 +775,6 @@
 endif
 
 ifdef CONFIG_AP
-NEED_80211_COMMON=y
 NEED_EAP_COMMON=y
 NEED_RSN_AUTHENTICATOR=y
 L_CFLAGS += -DCONFIG_AP
@@ -767,6 +798,7 @@
 OBJS += src/ap/drv_callbacks.c
 OBJS += src/ap/ap_drv_ops.c
 OBJS += src/ap/beacon.c
+OBJS += src/ap/bss_load.c
 OBJS += src/ap/eap_user_db.c
 ifdef CONFIG_IEEE80211N
 OBJS += src/ap/ieee802_11_ht.c
@@ -967,38 +999,6 @@
 CONFIG_INTERNAL_DH_GROUP5=y
 endif
 
-ifeq ($(CONFIG_TLS), schannel)
-ifdef TLS_FUNCS
-OBJS += src/crypto/tls_schannel.c
-endif
-OBJS += src/crypto/crypto_cryptoapi.c
-OBJS_p += src/crypto/crypto_cryptoapi.c
-ifdef NEED_FIPS186_2_PRF
-OBJS += src/crypto/fips_prf_internal.c
-OBJS += src/crypto/sha1-internal.c
-endif
-CONFIG_INTERNAL_SHA256=y
-CONFIG_INTERNAL_RC4=y
-CONFIG_INTERNAL_DH_GROUP5=y
-endif
-
-ifeq ($(CONFIG_TLS), nss)
-ifdef TLS_FUNCS
-OBJS += src/crypto/tls_nss.c
-LIBS += -lssl3
-endif
-OBJS += src/crypto/crypto_nss.c
-OBJS_p += src/crypto/crypto_nss.c
-ifdef NEED_FIPS186_2_PRF
-OBJS += src/crypto/fips_prf_internal.c
-OBJS += src/crypto/sha1-internal.c
-endif
-LIBS += -lnss3
-LIBS_p += -lnss3
-CONFIG_INTERNAL_MD4=y
-CONFIG_INTERNAL_DH_GROUP5=y
-endif
-
 ifeq ($(CONFIG_TLS), internal)
 ifndef CONFIG_CRYPTO
 CONFIG_CRYPTO=internal
@@ -1116,7 +1116,9 @@
 AESOBJS += src/crypto/aes-internal.c src/crypto/aes-internal-dec.c
 endif
 
+ifneq ($(CONFIG_TLS), openssl)
 AESOBJS += src/crypto/aes-unwrap.c
+endif
 ifdef NEED_AES_EAX
 AESOBJS += src/crypto/aes-eax.c
 NEED_AES_CTR=y
@@ -1137,17 +1139,24 @@
 endif
 ifdef NEED_AES_WRAP
 NEED_AES_ENC=y
+ifneq ($(CONFIG_TLS), openssl)
 AESOBJS += src/crypto/aes-wrap.c
 endif
+endif
 ifdef NEED_AES_CBC
 NEED_AES_ENC=y
+ifneq ($(CONFIG_TLS), openssl)
 AESOBJS += src/crypto/aes-cbc.c
 endif
+endif
 ifdef NEED_AES_ENC
 ifdef CONFIG_INTERNAL_AES
 AESOBJS += src/crypto/aes-internal-enc.c
 endif
 endif
+ifdef NEED_AES_SIV
+AESOBJS += src/crypto/aes-siv.c
+endif
 ifdef NEED_AES
 OBJS += $(AESOBJS)
 endif
@@ -1181,8 +1190,10 @@
 
 MD5OBJS =
 ifndef CONFIG_FIPS
+ifneq ($(CONFIG_TLS), openssl)
 MD5OBJS += src/crypto/md5.c
 endif
+endif
 ifdef NEED_MD5
 ifdef CONFIG_INTERNAL_MD5
 MD5OBJS += src/crypto/md5-internal.c
@@ -1223,8 +1234,14 @@
 ifdef NEED_TLS_PRF_SHA256
 SHA256OBJS += src/crypto/sha256-tlsprf.c
 endif
+ifdef NEED_HMAC_SHA256_KDF
+SHA256OBJS += src/crypto/sha256-kdf.c
+endif
 OBJS += $(SHA256OBJS)
 endif
+ifdef NEED_SHA384
+L_CFLAGS += -DCONFIG_SHA384
+endif
 
 ifdef NEED_DH_GROUPS
 OBJS += src/crypto/dh_groups.c
@@ -1367,14 +1384,12 @@
 endif
 
 ifdef NEED_SME
-NEED_80211_COMMON=y
 OBJS += sme.c
 L_CFLAGS += -DCONFIG_SME
 endif
 
-ifdef NEED_80211_COMMON
 OBJS += src/common/ieee802_11_common.c
-endif
+OBJS += src/common/hw_features_common.c
 
 ifdef NEED_EAP_COMMON
 OBJS += src/eap_common/eap_common.c
@@ -1499,26 +1514,6 @@
 ifdef CONFIG_DRIVER_NL80211
 OBJS_priv += src/common/ieee802_11_common.c
 endif
-ifdef CONFIG_DRIVER_TEST
-OBJS_priv += $(SHA1OBJS)
-OBJS_priv += $(MD5OBJS)
-ifeq ($(CONFIG_TLS), openssl)
-OBJS_priv += src/crypto/crypto_openssl.c
-endif
-ifeq ($(CONFIG_TLS), gnutls)
-OBJS_priv += src/crypto/crypto_gnutls.c
-endif
-ifeq ($(CONFIG_TLS), nss)
-OBJS_priv += src/crypto/crypto_nss.c
-endif
-ifeq ($(CONFIG_TLS), internal)
-ifeq ($(CONFIG_CRYPTO), libtomcrypt)
-OBJS_priv += src/crypto/crypto_libtomcrypt.c
-else
-OBJS_priv += src/crypto/crypto_internal.c
-endif
-endif
-endif # CONFIG_DRIVER_TEST
 OBJS += src/l2_packet/l2_packet_privsep.c
 OBJS += src/drivers/driver_privsep.c
 EXTRA_progs += wpa_priv
@@ -1570,6 +1565,13 @@
 ifeq ($(CONFIG_TLS), openssl)
 LOCAL_SHARED_LIBRARIES += libcrypto libssl libkeystore_binder
 endif
+
+# With BoringSSL we need libkeystore-engine in order to provide access to
+# keystore keys.
+ifneq (,$(wildcard external/boringssl/flavor.mk))
+LOCAL_SHARED_LIBRARIES += libkeystore-engine
+endif
+
 ifdef CONFIG_DRIVER_NL80211
 ifneq ($(wildcard external/libnl),)
 LOCAL_SHARED_LIBRARIES += libnl
diff --git a/wpa_supplicant/ChangeLog b/wpa_supplicant/ChangeLog
index f09e7a0..1ac79b4 100644
--- a/wpa_supplicant/ChangeLog
+++ b/wpa_supplicant/ChangeLog
@@ -1,5 +1,83 @@
 ChangeLog for wpa_supplicant
 
+2015-03-15 - v2.4
+	* allow OpenSSL cipher configuration to be set for internal EAP server
+	  (openssl_ciphers parameter)
+	* fixed number of small issues based on hwsim test case failures and
+	  static analyzer reports
+	* P2P:
+	  - add new=<0/1> flag to P2P-DEVICE-FOUND events
+	  - add passive channels in invitation response from P2P Client
+	  - enable nl80211 P2P_DEVICE support by default
+	  - fix regresssion in disallow_freq preventing search on social
+	    channels
+	  - fix regressions in P2P SD query processing
+	  - try to re-invite with social operating channel if no common channels
+	    in invitation
+	  - allow cross connection on parent interface (this fixes number of
+	    use cases with nl80211)
+	  - add support for P2P services (P2PS)
+	  - add p2p_go_ctwindow configuration parameter to allow GO CTWindow to
+	    be configured
+	* increase postponing of EAPOL-Start by one second with AP/GO that
+	  supports WPS 2.0 (this makes it less likely to trigger extra roundtrip
+	  of identity frames)
+	* add support for PMKSA caching with SAE
+	* add support for control mesh BSS (IEEE 802.11s) operations
+	* fixed number of issues with D-Bus P2P commands
+	* fixed regression in ap_scan=2 special case for WPS
+	* fixed macsec_validate configuration
+	* add a workaround for incorrectly behaving APs that try to use
+	  EAPOL-Key descriptor version 3 when the station supports PMF even if
+	  PMF is not enabled on the AP
+	* allow TLS v1.1 and v1.2 to be negotiated by default; previous behavior
+	  of disabling these can be configured to work around issues with broken
+	  servers with phase1="tls_disable_tlsv1_1=1 tls_disable_tlsv1_2=1"
+	* add support for Suite B (128-bit and 192-bit level) key management and
+	  cipher suites
+	* add WMM-AC support (WMM_AC_ADDTS/WMM_AC_DELTS)
+	* improved BSS Transition Management processing
+	* add support for neighbor report
+	* add support for link measurement
+	* fixed expiration of BSS entry with all-zeros BSSID
+	* add optional LAST_ID=x argument to LIST_NETWORK to allow all
+	  configured networks to be listed even with huge number of network
+	  profiles
+	* add support for EAP Re-Authentication Protocol (ERP)
+	* fixed EAP-IKEv2 fragmentation reassembly
+	* improved PKCS#11 configuration for OpenSSL
+	* set stdout to be line-buffered
+	* add TDLS channel switch configuration
+	* add support for MAC address randomization in scans with nl80211
+	* enable HT for IBSS if supported by the driver
+	* add BSSID black and white lists (bssid_blacklist, bssid_whitelist)
+	* add support for domain_suffix_match with GnuTLS
+	* add OCSP stapling client support with GnuTLS
+	* include peer certificate in EAP events even without a separate probe
+	  operation; old behavior can be restored with cert_in_cb=0
+	* add peer ceritficate alt subject name to EAP events
+	  (CTRL-EVENT-EAP-PEER-ALT)
+	* add domain_match network profile parameter (similar to
+	  domain_suffix_match, but full match is required)
+	* enable AP/GO mode HT Tx STBC automatically based on driver support
+	* add ANQP-QUERY-DONE event to provide information on ANQP parsing
+	  status
+	* allow passive scanning to be forced with passive_scan=1
+	* add a workaround for Linux packet socket behavior when interface is in
+	  bridge
+	* increase 5 GHz band preference in BSS selection (estimate SNR, if info
+	  not available from driver; estimate maximum throughput based on common
+	  HT/VHT/specific TX rate support)
+	* add INTERWORKING_ADD_NETWORK ctrl_iface command; this can be used to
+	  implement Interworking network selection behavior in upper layers
+	  software components
+	* add optional reassoc_same_bss_optim=1 (disabled by default)
+	  optimization to avoid unnecessary Authentication frame exchange
+	* extend TDLS frame padding workaround to cover all packets
+	* allow wpa_supplicant to recover nl80211 functionality if the cfg80211
+	  module gets removed and reloaded without restarting wpa_supplicant
+	* allow hostapd DFS implementation to be used in wpa_supplicant AP mode
+
 2014-10-09 - v2.3
 	* fixed number of minor issues identified in static analyzer warnings
 	* fixed wfd_dev_info to be more careful and not read beyond the buffer
diff --git a/wpa_supplicant/Makefile b/wpa_supplicant/Makefile
index 8f7c23f..05d8e0a 100644
--- a/wpa_supplicant/Makefile
+++ b/wpa_supplicant/Makefile
@@ -7,9 +7,11 @@
 endif
 
 export LIBDIR ?= /usr/local/lib/
+export INCDIR ?= /usr/local/include/
 export BINDIR ?= /usr/local/sbin/
 PKG_CONFIG ?= pkg-config
 
+CFLAGS += $(EXTRA_CFLAGS)
 CFLAGS += -I$(abspath ../src)
 CFLAGS += -I$(abspath ../src/utils)
 
@@ -34,6 +36,9 @@
 ALL += systemd/wpa_supplicant-wired@.service
 ALL += dbus/fi.epitest.hostap.WPASupplicant.service
 ALL += dbus/fi.w1.wpa_supplicant1.service
+ifdef CONFIG_BUILD_WPA_CLIENT_SO
+ALL += libwpa_client.so
+endif
 
 
 all: verify_config $(ALL) dynamic_eap_methods
@@ -60,6 +65,10 @@
 
 install: $(addprefix $(DESTDIR)$(BINDIR)/,$(BINALL))
 	$(MAKE) -C ../src install
+ifdef CONFIG_BUILD_WPA_CLIENT_SO
+	install -m 0644 -D libwpa_client.so $(DESTDIR)/$(LIBDIR)/libwpa_client.so
+	install -m 0644 -D ../src/common/wpa_ctrl.h $(DESTDIR)/$(INCDIR)/wpa_ctrl.h
+endif
 
 ifdef CONFIG_FIPS
 CONFIG_NO_RANDOM_POOL=
@@ -80,6 +89,7 @@
 OBJS_c = wpa_cli.o ../src/common/wpa_ctrl.o
 OBJS_c += ../src/utils/wpa_debug.o
 OBJS_c += ../src/utils/common.o
+OBJS += wmm_ac.o
 
 ifndef CONFIG_OS
 ifdef CONFIG_NATIVE_WINDOWS
@@ -103,6 +113,8 @@
 OBJS_p += ../src/utils/trace.o
 OBJS_c += ../src/utils/trace.o
 OBJS_priv += ../src/utils/trace.o
+LIBCTRL += ../src/utils/trace.o
+LIBCTRLSO += ../src/utils/trace.c
 LDFLAGS += -rdynamic
 CFLAGS += -funwind-tables
 ifdef CONFIG_WPA_TRACE_BFD
@@ -185,6 +197,17 @@
 CFLAGS += -DCONFIG_NO_SCAN_PROCESSING
 endif
 
+ifdef CONFIG_SUITEB
+CFLAGS += -DCONFIG_SUITEB
+NEED_SHA256=y
+NEED_AES_OMAC1=y
+endif
+
+ifdef CONFIG_SUITEB192
+CFLAGS += -DCONFIG_SUITEB192
+NEED_SHA384=y
+endif
+
 ifdef CONFIG_IEEE80211W
 CFLAGS += -DCONFIG_IEEE80211W
 NEED_SHA256=y
@@ -194,11 +217,24 @@
 ifdef CONFIG_IEEE80211R
 CFLAGS += -DCONFIG_IEEE80211R
 OBJS += ../src/rsn_supp/wpa_ft.o
-NEED_80211_COMMON=y
 NEED_SHA256=y
 NEED_AES_OMAC1=y
 endif
 
+ifdef CONFIG_MESH
+NEED_80211_COMMON=y
+NEED_SHA256=y
+NEED_AES_SIV=y
+NEED_AES_OMAC1=y
+NEED_AES_CTR=y
+CONFIG_SAE=y
+CONFIG_AP=y
+CFLAGS += -DCONFIG_MESH
+OBJS += mesh.o
+OBJS += mesh_mpm.o
+OBJS += mesh_rsn.o
+endif
+
 ifdef CONFIG_SAE
 CFLAGS += -DCONFIG_SAE
 OBJS += ../src/common/sae.o
@@ -249,6 +285,7 @@
 
 ifdef CONFIG_P2P
 OBJS += p2p_supplicant.o
+OBJS += p2p_supplicant_sd.o
 OBJS += ../src/p2p/p2p.o
 OBJS += ../src/p2p/p2p_utils.o
 OBJS += ../src/p2p/p2p_parse.o
@@ -264,7 +301,6 @@
 CFLAGS += -DCONFIG_P2P
 NEED_GAS=y
 NEED_OFFCHANNEL=y
-NEED_80211_COMMON=y
 CONFIG_WPS=y
 CONFIG_AP=y
 ifdef CONFIG_P2P_STRICT
@@ -290,6 +326,10 @@
 NEED_GAS=y
 endif
 
+ifdef CONFIG_NO_ROAMING
+CFLAGS += -DCONFIG_NO_ROAMING
+endif
+
 include ../src/drivers/drivers.mak
 ifdef CONFIG_AP
 OBJS_d += $(DRV_BOTH_OBJS)
@@ -329,6 +369,12 @@
 LIBS += -lpcap
 endif
 
+ifdef CONFIG_ERP
+CFLAGS += -DCONFIG_ERP
+NEED_SHA256=y
+NEED_HMAC_SHA256_KDF=y
+endif
+
 ifdef CONFIG_EAP_TLS
 # EAP-TLS
 ifeq ($(CONFIG_EAP_TLS), dyn)
@@ -346,7 +392,7 @@
 ifdef CONFIG_EAP_UNAUTH_TLS
 # EAP-UNAUTH-TLS
 CFLAGS += -DEAP_UNAUTH_TLS
-ifndef CONFIG_EAP_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
@@ -635,7 +681,6 @@
 NEED_DH_GROUPS=y
 NEED_SHA256=y
 NEED_BASE64=y
-NEED_80211_COMMON=y
 NEED_AES_CBC=y
 NEED_MODEXP=y
 
@@ -757,7 +802,6 @@
 endif
 
 ifdef CONFIG_AP
-NEED_80211_COMMON=y
 NEED_EAP_COMMON=y
 NEED_RSN_AUTHENTICATOR=y
 CFLAGS += -DCONFIG_AP
@@ -781,6 +825,7 @@
 OBJS += ../src/ap/drv_callbacks.o
 OBJS += ../src/ap/ap_drv_ops.o
 OBJS += ../src/ap/beacon.o
+OBJS += ../src/ap/bss_load.o
 OBJS += ../src/ap/eap_user_db.o
 ifdef CONFIG_IEEE80211N
 OBJS += ../src/ap/ieee802_11_ht.o
@@ -952,6 +997,7 @@
 endif
 OBJS += ../src/crypto/crypto_openssl.o
 OBJS_p += ../src/crypto/crypto_openssl.o
+OBJS_priv += ../src/crypto/crypto_openssl.o
 ifdef NEED_FIPS186_2_PRF
 OBJS += ../src/crypto/fips_prf_openssl.o
 endif
@@ -970,6 +1016,7 @@
 endif
 OBJS += ../src/crypto/crypto_gnutls.o
 OBJS_p += ../src/crypto/crypto_gnutls.o
+OBJS_priv += ../src/crypto/crypto_gnutls.o
 ifdef NEED_FIPS186_2_PRF
 OBJS += ../src/crypto/fips_prf_internal.o
 SHA1OBJS += ../src/crypto/sha1-internal.o
@@ -981,38 +1028,6 @@
 CONFIG_INTERNAL_DH_GROUP5=y
 endif
 
-ifeq ($(CONFIG_TLS), schannel)
-ifdef TLS_FUNCS
-OBJS += ../src/crypto/tls_schannel.o
-endif
-OBJS += ../src/crypto/crypto_cryptoapi.o
-OBJS_p += ../src/crypto/crypto_cryptoapi.o
-ifdef NEED_FIPS186_2_PRF
-OBJS += ../src/crypto/fips_prf_internal.o
-SHA1OBJS += ../src/crypto/sha1-internal.o
-endif
-CONFIG_INTERNAL_SHA256=y
-CONFIG_INTERNAL_RC4=y
-CONFIG_INTERNAL_DH_GROUP5=y
-endif
-
-ifeq ($(CONFIG_TLS), nss)
-ifdef TLS_FUNCS
-OBJS += ../src/crypto/tls_nss.o
-LIBS += -lssl3
-endif
-OBJS += ../src/crypto/crypto_nss.o
-OBJS_p += ../src/crypto/crypto_nss.o
-ifdef NEED_FIPS186_2_PRF
-OBJS += ../src/crypto/fips_prf_internal.o
-SHA1OBJS += ../src/crypto/sha1-internal.o
-endif
-LIBS += -lnss3
-LIBS_p += -lnss3
-CONFIG_INTERNAL_MD4=y
-CONFIG_INTERNAL_DH_GROUP5=y
-endif
-
 ifeq ($(CONFIG_TLS), internal)
 ifndef CONFIG_CRYPTO
 CONFIG_CRYPTO=internal
@@ -1130,7 +1145,9 @@
 AESOBJS += ../src/crypto/aes-internal.o ../src/crypto/aes-internal-dec.o
 endif
 
+ifneq ($(CONFIG_TLS), openssl)
 AESOBJS += ../src/crypto/aes-unwrap.o
+endif
 ifdef NEED_AES_EAX
 AESOBJS += ../src/crypto/aes-eax.o
 NEED_AES_CTR=y
@@ -1149,14 +1166,21 @@
 AESOBJS += ../src/crypto/aes-omac1.o
 endif
 endif
+ifdef NEED_AES_SIV
+AESOBJS += ../src/crypto/aes-siv.o
+endif
 ifdef NEED_AES_WRAP
 NEED_AES_ENC=y
+ifneq ($(CONFIG_TLS), openssl)
 AESOBJS += ../src/crypto/aes-wrap.o
 endif
+endif
 ifdef NEED_AES_CBC
 NEED_AES_ENC=y
+ifneq ($(CONFIG_TLS), openssl)
 AESOBJS += ../src/crypto/aes-cbc.o
 endif
+endif
 ifdef NEED_AES_ENC
 ifdef CONFIG_INTERNAL_AES
 AESOBJS += ../src/crypto/aes-internal-enc.o
@@ -1193,14 +1217,17 @@
 endif
 
 ifndef CONFIG_FIPS
+ifneq ($(CONFIG_TLS), openssl)
 MD5OBJS += ../src/crypto/md5.o
 endif
+endif
 ifdef NEED_MD5
 ifdef CONFIG_INTERNAL_MD5
 MD5OBJS += ../src/crypto/md5-internal.o
 endif
 OBJS += $(MD5OBJS)
 OBJS_p += $(MD5OBJS)
+OBJS_priv += $(MD5OBJS)
 endif
 
 ifdef NEED_MD4
@@ -1235,8 +1262,14 @@
 ifdef NEED_TLS_PRF_SHA256
 SHA256OBJS += ../src/crypto/sha256-tlsprf.o
 endif
+ifdef NEED_HMAC_SHA256_KDF
+OBJS += ../src/crypto/sha256-kdf.o
+endif
 OBJS += $(SHA256OBJS)
 endif
+ifdef NEED_SHA384
+CFLAGS += -DCONFIG_SHA384
+endif
 
 ifdef NEED_DH_GROUPS
 OBJS += ../src/crypto/dh_groups.o
@@ -1386,14 +1419,12 @@
 endif
 
 ifdef NEED_SME
-NEED_80211_COMMON=y
 OBJS += sme.o
 CFLAGS += -DCONFIG_SME
 endif
 
-ifdef NEED_80211_COMMON
 OBJS += ../src/common/ieee802_11_common.o
-endif
+OBJS += ../src/common/hw_features_common.o
 
 ifdef NEED_EAP_COMMON
 OBJS += ../src/eap_common/eap_common.o
@@ -1433,6 +1464,7 @@
 
 OBJS_p += $(SHA1OBJS)
 OBJS_p += $(SHA256OBJS)
+OBJS_priv += $(SHA1OBJS)
 
 ifdef CONFIG_BGSCAN_SIMPLE
 CFLAGS += -DCONFIG_BGSCAN_SIMPLE
@@ -1496,6 +1528,7 @@
 OBJS += wpas_module_tests.o
 OBJS += ../src/utils/utils_module_tests.o
 OBJS += ../src/common/common_module_tests.o
+OBJS += ../src/crypto/crypto_module_tests.o
 ifdef CONFIG_WPS
 OBJS += ../src/wps/wps_module_tests.o
 endif
@@ -1539,26 +1572,6 @@
 ifdef CONFIG_DRIVER_NL80211
 OBJS_priv += ../src/common/ieee802_11_common.o
 endif
-ifdef CONFIG_DRIVER_TEST
-OBJS_priv += $(SHA1OBJS)
-OBJS_priv += $(MD5OBJS)
-ifeq ($(CONFIG_TLS), openssl)
-OBJS_priv += ../src/crypto/crypto_openssl.o
-endif
-ifeq ($(CONFIG_TLS), gnutls)
-OBJS_priv += ../src/crypto/crypto_gnutls.o
-endif
-ifeq ($(CONFIG_TLS), nss)
-OBJS_priv += ../src/crypto/crypto_nss.o
-endif
-ifeq ($(CONFIG_TLS), internal)
-ifeq ($(CONFIG_CRYPTO), libtomcrypt)
-OBJS_priv += ../src/crypto/crypto_libtomcrypt.o
-else
-OBJS_priv += ../src/crypto/crypto_internal.o
-endif
-endif
-endif # CONFIG_DRIVER_TEST
 OBJS += ../src/l2_packet/l2_packet_privsep.o
 OBJS += ../src/drivers/driver_privsep.o
 EXTRA_progs += wpa_priv
@@ -1588,6 +1601,10 @@
 Q=
 E=true
 endif
+ifeq ($(QUIET), 1)
+Q=@
+E=true
+endif
 
 dynamic_eap_methods: $(EAPDYN)
 
@@ -1625,6 +1642,22 @@
 	$(Q)$(LDO) $(LDFLAGS) -o wpa_cli $(OBJS_c) $(LIBS_c)
 	@$(E) "  LD " $@
 
+LIBCTRL += ../src/common/wpa_ctrl.o
+LIBCTRL += ../src/utils/os_$(CONFIG_OS).o
+LIBCTRL += ../src/utils/wpa_debug.o
+LIBCTRLSO += ../src/common/wpa_ctrl.c
+LIBCTRLSO += ../src/utils/os_$(CONFIG_OS).c
+LIBCTRLSO += ../src/utils/wpa_debug.c
+
+libwpa_client.a: $(LIBCTRL)
+	$(Q)rm -f $@
+	$(Q)$(AR) crs $@ $?
+	@$(E) "  AR " $@
+
+libwpa_client.so: $(LIBCTRLSO)
+	@$(E) "  CC  $@ ($^)"
+	$(Q)$(CC) $(LDFLAGS) -o $@ $(CFLAGS) -shared -fPIC $^
+
 link_test: $(OBJS) $(OBJS_h) tests/link_test.o
 	$(Q)$(LDO) $(LDFLAGS) -o link_test $(OBJS) $(OBJS_h) tests/link_test.o $(LIBS)
 	@$(E) "  LD " $@
@@ -1680,10 +1713,12 @@
 endif
 
 %.service: %.service.in
-	sed -e 's|\@BINDIR\@|$(BINDIR)|g' $< >$@
+	$(Q)sed -e 's|\@BINDIR\@|$(BINDIR)|g' $< >$@
+	@$(E) "  sed" $<
 
 %@.service: %.service.arg.in
-	sed -e 's|\@BINDIR\@|$(BINDIR)|g' $< >$@
+	$(Q)sed -e 's|\@BINDIR\@|$(BINDIR)|g' $< >$@
+	@$(E) "  sed" $<
 
 wpa_supplicant.exe: wpa_supplicant
 	mv -f $< $@
@@ -1705,7 +1740,7 @@
 	@echo "wpa_gui has been removed - see wpa_gui-qt4 for replacement"
 
 wpa_gui-qt4/Makefile:
-	qmake -o wpa_gui-qt4/Makefile wpa_gui-qt4/wpa_gui.pro 
+	qmake -o wpa_gui-qt4/Makefile wpa_gui-qt4/wpa_gui.pro
 
 wpa_gui-qt4/lang/wpa_gui_de.qm: wpa_gui-qt4/lang/wpa_gui_de.ts
 	lrelease wpa_gui-qt4/wpa_gui.pro
@@ -1742,5 +1777,7 @@
 	rm -f nfc_pw_token
 	rm -f lcov.info
 	rm -rf lcov-html
+	rm -f libwpa_client.a
+	rm -f libwpa_client.so
 
 -include $(OBJS:%.o=%.d)
diff --git a/wpa_supplicant/README b/wpa_supplicant/README
index 653848e..f9c65d2 100644
--- a/wpa_supplicant/README
+++ b/wpa_supplicant/README
@@ -1,7 +1,7 @@
 WPA Supplicant
 ==============
 
-Copyright (c) 2003-2014, Jouni Malinen <j@w1.fi> and contributors
+Copyright (c) 2003-2015, Jouni Malinen <j@w1.fi> and contributors
 All Rights Reserved.
 
 This program is licensed under the BSD license (the one with
diff --git a/wpa_supplicant/README-HS20 b/wpa_supplicant/README-HS20
index 58c2475..161dc06 100644
--- a/wpa_supplicant/README-HS20
+++ b/wpa_supplicant/README-HS20
@@ -172,7 +172,7 @@
 #	If set, this FQDN is used as a suffix match requirement for the AAA
 #	server certificate in SubjectAltName dNSName element(s). If a
 #	matching dNSName is found, this constraint is met. If no dNSName
-#	values are present, this constraint is matched against SubjetName CN
+#	values are present, this constraint is matched against SubjectName CN
 #	using same suffix match comparison. Suffix match here means that the
 #	host/domain name is compared one label at a time starting from the
 #	top-level domain and all the labels in @domain_suffix_match shall be
diff --git a/wpa_supplicant/README-P2P b/wpa_supplicant/README-P2P
index 5c1e4f9..6a5b032 100644
--- a/wpa_supplicant/README-P2P
+++ b/wpa_supplicant/README-P2P
@@ -73,7 +73,7 @@
 
 p2p_find [timeout in seconds] [type=<social|progressive>] \
 	[dev_id=<addr>] [dev_type=<device type>] \
-	[delay=<search delay in ms>]
+	[delay=<search delay in ms>] [seek=<service name>] [freq=<MHz>]
 
 The default behavior is to run a single full scan in the beginning and
 then scan only social channels. type=social will scan only social
@@ -81,7 +81,9 @@
 like the default behavior, but it will scan through all the channels
 progressively one channel at the time in the Search state rounds. This
 will help in finding new groups or groups missed during the initial
-full scan.
+full scan. When the type parameter is not included (i.e., full scan), the
+optional freq parameter can be used to override the first scan to use only
+the specified channel after which only social channels are scanned.
 
 The optional dev_id option can be used to specify a single P2P peer to
 search for. The optional delay parameter can be used to request an extra
@@ -92,6 +94,24 @@
 (primary or secondary) to search for, e.g.,
 "p2p_find dev_type=1-0050F204-1".
 
+
+With one or more seek arguments, the command sends Probe Request frames
+for a P2PS service. For example,
+p2p_find 5 dev_id=11:22:33:44:55:66 seek=alt.example.chat seek=alt.example.video
+
+Parameters description:
+    Timeout - Optional ASCII base-10-encoded u16. If missing, request will not
+	time out and must be canceled manually
+    dev_id - Optional to request responses from a single known remote device
+    Service Name - Mandatory UTF-8 string for ASP seeks
+	Service name must match the remote service being advertised exactly
+	(no prefix matching).
+	Service name may be empty, in which case all ASP services will be
+	returned, and may be filtered with p2p_serv_disc_req settings, and
+	p2p_serv_asp_resp results.
+	Multiple service names may be requested, but if it exceeds internal
+	limit, it will automatically revert to requesting all ASP services.
+
 p2p_listen [timeout in seconds]
 
 Start Listen-only state (become discoverable without searching for
@@ -128,9 +148,9 @@
 out whether the peer device is operating as a GO and if so, use
 join-a-group style PD instead of GO Negotiation style PD.
 
-p2p_connect <peer device address> <pbc|pin|PIN#> [display|keypad]
+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]
+	[go_intent=<0..15>] [freq=<in MHz>] [ht40] [vht] [provdisc] [auto]
 
 Start P2P group formation with a discovered P2P peer. This includes
 optional group owner negotiation, group interface setup, provisioning,
@@ -171,6 +191,69 @@
 P2P implementations that require this to allow the user to accept the
 connection.
 
+"auto" can be used to request wpa_supplicant to automatically figure
+out whether the peer device is operating as a GO and if so, use
+join-a-group operation rather than GO Negotiation.
+
+P2PS attribute changes to p2p_connect command:
+
+P2PS supports two WPS provisioning methods namely PIN method and P2PS default.
+The remaining paramters hold same role as in legacy P2P. In case of P2PS default
+config method "p2ps" keyword is added in p2p_connect command.
+
+For example:
+p2p_connect 02:0a:f5:85:11:00 12345670 p2ps persistent join
+	(WPS Method = P2PS default)
+
+p2p_connect 02:0a:f5:85:11:00 45629034 keypad persistent
+	(WPS Method = PIN)
+
+p2p_asp_provision <peer MAC address> <adv_id=peer adv id>
+	<adv_mac=peer MAC address> [role=2|4|1] <session=session id>
+	<session_mac=initiator mac address>
+	[info='service info'] <method=Default|keypad|Display>
+
+This command starts provision discovery with the P2PS enabled peer device.
+
+For example,
+p2p_asp_provision 00:11:22:33:44:55 adv_id=4d6fc7 adv_mac=00:55:44:33:22:11 role=1 session=12ab34 session_mac=00:11:22:33:44:55 info='name=john' method=1000
+
+Parameter description:
+    MAC address - Mandatory
+    adv_id - Mandatory remote Advertising ID of service connection is being
+	established for
+    adv_mac - Mandatory MAC address that owns/registered the service
+    role - Optional
+	2 (group client only) or 4 (group owner only)
+	if not present (or 1) role is negotiated by the two peers.
+    session - Mandatory Session ID of the first session to be established
+    session_mac - Mandatory MAC address that owns/initiated the session
+    method - Optional method to request for provisioning (1000 - P2PS Default,
+	100 - Keypad(PIN), 8 - Display(PIN))
+    info - Optional UTF-8 string. Hint for service to indicate possible usage
+	parameters - Escape single quote & backslash:
+	with a backslash 0x27 == ' == \', and 0x5c == \ == \\
+
+p2p_asp_provision_resp <peer mac address> <adv_id= local adv id>
+	<adv_mac=local MAC address> <role=1|2|4> <status=0>
+	<session=session id> <session_mac=peer MAC address>
+
+This command sends a provision discovery response from responder side.
+
+For example,
+p2p_asp_provision_resp 00:55:44:33:22:11 adv_id=4d6fc7 adv_mac=00:55:44:33:22:11 role=1 status=0 session=12ab34 session_mac=00:11:22:33:44:55
+
+Parameters definition:
+    MAC address - Mandatory
+    adv_id - Mandatory local Advertising ID of service connection is being
+	established for
+    adv_mac - Mandatory MAC address that owns/registered the service
+    role -  Optional 2 (group client only) or 4 (group owner only)
+	if not present (or 1) role is negotiated by the two peers.
+    status - Mandatory Acceptance/Rejection code of Provisioning
+    session - Mandatory Session ID of the first session to be established
+    session_mac - Mandatory MAC address that owns/initiated the session
+
 p2p_group_add [persistent|persistent=<network id>] [freq=<freq in MHz>]
 	[ht40] [vht]
 
@@ -215,6 +298,70 @@
 
 Service Discovery
 
+p2p_service_add asp <auto accept> <adv id> <status 0/1> <Config Methods>
+	<Service name> [Service Information] [Response Info]
+
+This command can be used to search for a P2PS service which includes
+Play, Send, Display, and Print service. The parameters for this command
+are "asp" to identify the command as P2PS one, auto accept value,
+advertisement id which uniquely identifies the service requests, state
+of the service whether the service is available or not, config methods
+which can be either P2PS method or PIN method, service name followed by
+two optional parameters service information, and response info.
+
+For example,
+p2p_service_add asp 1 4d6fc7 0 1108 alt.example.chat svc_info='name=john' rsp_info='enter PIN 1234'
+
+Parameters definition:
+    asp - Mandatory for ASP service registration
+    auto accept - Mandatory ASCII hex-encoded boolean (0 == no auto-accept,
+	1 == auto-accept ANY role, 2 == auto-accept CLIENT role,
+	4 == auto-accept GO role)
+    Advertisement ID - Mandatory non-zero ASCII hex-encoded u32
+	(Must be unique/not yet exist in svc db)
+    State - Mandatory ASCII hex-encoded u8 (0 -- Svc not available,
+	1 -- Svc available, 2-0xff  Application defined)
+    Config Methods - Mandatory ASCII hex-encoded u16 (bitmask of WSC config
+	methods)
+    Service Name - Mandatory UTF-8 string
+    Service Information - Optional UTF-8 string
+	Escape single quote & backslash with a backslash:
+	0x27 == ' == \', and 0x5c == \ == \\
+    Session response information -  Optional (used only if auto accept is TRUE)
+	UTF-8 string
+	Escape single quote & backslash with a backslash:
+	0x27 == ' == \', and 0x5c == \ == \\
+
+p2p_service_rep asp <auto accept> <adv id> <status 0/1> <Config Methods>
+	<Service name> [Service Information] [Response Info]
+
+This command can be used to replace the existing service request
+attributes from the initiator side. The replacement is only allowed if
+the advertisement id issued in the command matches with any one entry in
+the list of existing SD queries. If advertisement id doesn't match the
+command returns a failure.
+
+For example,
+p2p_service_rep asp 1 4d6fc7 1 1108 alt.example.chat svc_info='name=john' rsp_info='enter PIN 1234'
+
+Parameters definition:
+    asp - Mandatory for ASP service registration
+    auto accept - Mandatory ASCII hex-encoded boolean (1 == true, 0 == false)
+    Advertisement ID - Mandatory non-zero ASCII hex-encoded u32
+	(Must already exist in svc db)
+    State - Mandatory ASCII hex-encoded u8 (can be used to indicate svc
+	available or not available for instance)
+    Config Methods - Mandatory ASCII hex-encoded u16 (bitmask of WSC config
+	methods)
+    Service Name - Mandatory UTF-8 string (Must match existing string in svc db)
+    Service Information - Optional UTF-8 string
+	Escape single quote & backslash with a backslash:
+	0x27 == ' == \', and 0x5c == \ == \\
+    Session response information -  Optional (used only if auto accept is TRUE)
+	UTF-8 string
+	Escape single quote & backslash with a backslash:
+	0x27 == ' == \', and 0x5c == \ == \\
+
 p2p_serv_disc_req
 
 Schedule a P2P service discovery request. The parameters for this
@@ -296,6 +443,27 @@
 p2p_serv_disc_req 00:00:00:00:00:00 wifi-display [source+sink] 2,3,4,5
 p2p_serv_disc_req 00:00:00:00:00:00 wifi-display [source][pri-sink] 2,3,4,5
 
+p2p_serv_disc_req <Unicast|Broadcast mac address> asp <Transaction ID>
+	<Service Name> [Service Information]
+
+The command can be used for service discovery for P2PS enabled devices.
+
+For example: p2p_serv_disc_req 00:00:00:00:00:00 asp a1 alt.example 'john'
+
+Parameters definition:
+    MAC address - Mandatory Existing
+    asp - Mandatory for ASP queries
+    Transaction ID - Mandatory non-zero ASCII hex-encoded u8 for GAS
+    Service Name Prefix - Mandatory UTF-8 string.
+	Will match from beginning of remote Service Name
+    Service Information Substring - Optional UTF-8 string
+	If Service Information Substring is not included, all services matching
+	Service Name Prefix will be returned.
+	If Service Information Substring is included, both the Substring and the
+	Service Name Prefix must match for service to be returned.
+	If remote service has no Service Information, all Substring searches
+	will fail.
+
 p2p_serv_disc_cancel_req <query identifier>
 
 Cancel a pending P2P service discovery request. This command takes a
@@ -371,6 +539,11 @@
 
 Remove a local UPnP service from internal SD query processing.
 
+p2p_service_del asp <adv id>
+
+Removes the local asp service from internal SD query list.
+For example: p2p_service_del asp 4d6fc7
+
 p2p_service_flush
 
 Remove all local services from internal SD query processing.
@@ -605,6 +778,63 @@
 Remove a network entry from configuration. 
 
 
+P2PS Events/Responses:
+
+P2PS-PROV-START: This events gets triggered when provisioning is issued for
+either seeker or advertiser.
+
+For example,
+P2PS-PROV-START 00:55:44:33:22:11 adv_id=111 adv_mac=00:55:44:33:22:11 conncap=1 session=1234567 session_mac=00:11:22:33:44:55 info='xxxx'
+
+Parameters definition:
+    MAC address - always
+    adv_id - always ASCII hex-encoded u32
+    adv_mac - always MAC address that owns/registered the service
+    conncap - always mask of 0x01 (new), 0x02 (group client), 0x04 (group owner)
+	bits
+    session - always Session ID of the first session to be established
+    session_mac - always MAC address that owns/initiated the session
+    info - if available, UTF-8 string
+	Escaped single quote & backslash with a backslash:
+	\' == 0x27 == ', and \\ == 0x5c == \
+
+P2PS-PROV-DONE: When provisioning is completed then this event gets triggered.
+
+For example,
+P2PS-PROV-DONE 00:11:22:33:44:55 status=0 adv_id=111 adv_mac=00:55:44:33:22:11 conncap=1 session=1234567 session_mac=00:11:22:33:44:55 [dev_passwd_id=8 | go=p2p-wlan0-0 | join=11:22:33:44:55:66 | persist=0]
+
+Parameters definition:
+    MAC address - always main device address of peer. May be different from MAC
+	ultimately connected to.
+    status - always ascii hex-encoded u8 (0 == success, 12 == deferred success)
+    adv_id - always ascii hex-encoded u32
+    adv_mac - always MAC address that owns/registered the service
+    conncap - always One of: 1 (new), 2 (group client), 4 (group owner) bits
+    session - always Session ID of the first session to be established
+    session_mac - always MAC address that owns/initiated the session
+    dev_passwd_id - only if conncap value == 1 (New GO negotiation)
+	8 - "p2ps" password must be passed in p2p_connect command
+	1 - "display" password must be passed in p2p_connect command
+	5 - "keypad" password must be passed in p2p_connect command
+    join only - if conncap value == 2 (Client Only). Display password and "join"
+	must be passed in p2p_connect and address must be the MAC specified
+    go only - if conncap value == 4 (GO Only). Interface name must be set with a
+	password
+    persist - only if previous persistent group existed between peers and shall
+	be re-used. Group is restarted by sending "p2p_group_add persistent=0"
+	where value is taken from P2P-PROV-DONE
+
+Extended Events/Response
+
+P2P-DEVICE-FOUND 00:11:22:33:44:55 p2p_dev_addr=00:11:22:33:44:55 pri_dev_type=0-00000000-0 name='' config_methods=0x108 dev_capab=0x21 group_capab=0x0 adv_id=111 asp_svc=alt.example.chat
+
+Parameters definition:
+    adv_id - if ASP ASCII hex-encoded u32. If it is reporting the
+	"wildcard service", this value will be 0
+    asp_svc - if ASP this is the service string. If it is reporting the
+	"wildcard service", this value will be org.wi-fi.wfds
+
+
 wpa_cli action script
 ---------------------
 
diff --git a/wpa_supplicant/android.config b/wpa_supplicant/android.config
index 3ed734d..8d27bb2 100644
--- a/wpa_supplicant/android.config
+++ b/wpa_supplicant/android.config
@@ -55,9 +55,6 @@
 # wpa_supplicant.
 # CONFIG_USE_NDISUIO=y
 
-# Driver interface for development testing
-#CONFIG_DRIVER_TEST=y
-
 # Driver interface for wired Ethernet drivers
 #CONFIG_DRIVER_WIRED=y
 
diff --git a/wpa_supplicant/ap.c b/wpa_supplicant/ap.c
index 7c93498..f3960c5 100644
--- a/wpa_supplicant/ap.c
+++ b/wpa_supplicant/ap.c
@@ -26,6 +26,7 @@
 #include "ap/ieee802_1x.h"
 #include "ap/wps_hostapd.h"
 #include "ap/ctrl_iface_ap.h"
+#include "ap/dfs.h"
 #include "wps/wps.h"
 #include "common/ieee802_11_defs.h"
 #include "config_ssid.h"
@@ -75,24 +76,10 @@
 #endif /* CONFIG_IEEE80211N */
 
 
-static int wpa_supplicant_conf_ap(struct wpa_supplicant *wpa_s,
-				  struct wpa_ssid *ssid,
-				  struct hostapd_config *conf)
+void wpa_supplicant_conf_ap_ht(struct wpa_supplicant *wpa_s,
+			       struct wpa_ssid *ssid,
+			       struct hostapd_config *conf)
 {
-	struct hostapd_bss_config *bss = conf->bss[0];
-
-	conf->driver = wpa_s->driver;
-
-	os_strlcpy(bss->iface, wpa_s->ifname, sizeof(bss->iface));
-
-	conf->hw_mode = ieee80211_freq_to_chan(ssid->frequency,
-					       &conf->channel);
-	if (conf->hw_mode == NUM_HOSTAPD_MODES) {
-		wpa_printf(MSG_ERROR, "Unsupported AP mode frequency: %d MHz",
-			   ssid->frequency);
-		return -1;
-	}
-
 	/* TODO: enable HT40 if driver supports it;
 	 * drop to 11b if driver does not support 11g */
 
@@ -146,6 +133,7 @@
 				 HT_CAP_INFO_SHORT_GI20MHZ |
 				 HT_CAP_INFO_SHORT_GI40MHZ |
 				 HT_CAP_INFO_RX_STBC_MASK |
+				 HT_CAP_INFO_TX_STBC |
 				 HT_CAP_INFO_MAX_AMSDU_SIZE);
 
 			if (mode->vht_capab && ssid->vht) {
@@ -155,6 +143,35 @@
 		}
 	}
 #endif /* CONFIG_IEEE80211N */
+}
+
+
+static int wpa_supplicant_conf_ap(struct wpa_supplicant *wpa_s,
+				  struct wpa_ssid *ssid,
+				  struct hostapd_config *conf)
+{
+	struct hostapd_bss_config *bss = conf->bss[0];
+
+	conf->driver = wpa_s->driver;
+
+	os_strlcpy(bss->iface, wpa_s->ifname, sizeof(bss->iface));
+
+	conf->hw_mode = ieee80211_freq_to_chan(ssid->frequency,
+					       &conf->channel);
+	if (conf->hw_mode == NUM_HOSTAPD_MODES) {
+		wpa_printf(MSG_ERROR, "Unsupported AP mode frequency: %d MHz",
+			   ssid->frequency);
+		return -1;
+	}
+
+	wpa_supplicant_conf_ap_ht(wpa_s, ssid, conf);
+
+	if (ieee80211_is_dfs(ssid->frequency) && wpa_s->conf->country[0]) {
+		conf->ieee80211h = 1;
+		conf->ieee80211d = 1;
+		conf->country[0] = wpa_s->conf->country[0];
+		conf->country[1] = wpa_s->conf->country[1];
+	}
 
 #ifdef CONFIG_P2P
 	if (conf->hw_mode == HOSTAPD_MODE_IEEE80211G &&
@@ -217,7 +234,7 @@
 	bss->wpa_key_mgmt = ssid->key_mgmt;
 	bss->wpa_pairwise = ssid->pairwise_cipher;
 	if (ssid->psk_set) {
-		os_free(bss->ssid.wpa_psk);
+		bin_clear_free(bss->ssid.wpa_psk, sizeof(*bss->ssid.wpa_psk));
 		bss->ssid.wpa_psk = os_zalloc(sizeof(struct hostapd_wpa_psk));
 		if (bss->ssid.wpa_psk == NULL)
 			return -1;
@@ -256,6 +273,17 @@
 	else if (wpa_s->conf->beacon_int)
 		conf->beacon_int = wpa_s->conf->beacon_int;
 
+#ifdef CONFIG_P2P
+	if (wpa_s->conf->p2p_go_ctwindow > conf->beacon_int) {
+		wpa_printf(MSG_INFO,
+			   "CTWindow (%d) is bigger than beacon interval (%d) - avoid configuring it",
+			   wpa_s->conf->p2p_go_ctwindow, conf->beacon_int);
+		conf->p2p_go_ctwindow = 0;
+	} else {
+		conf->p2p_go_ctwindow = wpa_s->conf->p2p_go_ctwindow;
+	}
+#endif /* CONFIG_P2P */
+
 	if ((bss->wpa & 2) && bss->rsn_pairwise == 0)
 		bss->rsn_pairwise = bss->wpa_pairwise;
 	bss->wpa_group = wpa_select_ap_group_cipher(bss->wpa, bss->wpa_pairwise,
@@ -317,7 +345,8 @@
 	    bss->ssid.security_policy != SECURITY_PLAINTEXT)
 		goto no_wps;
 	if (bss->ssid.security_policy == SECURITY_WPA_PSK &&
-	    (!(bss->rsn_pairwise & WPA_CIPHER_CCMP) || !(bss->wpa & 2)))
+	    (!(bss->rsn_pairwise & (WPA_CIPHER_CCMP | WPA_CIPHER_GCMP)) ||
+	     !(bss->wpa & 2)))
 		goto no_wps; /* WPS2 does not allow WPA/TKIP-only
 			      * configuration */
 	bss->eap_server = 1;
@@ -456,8 +485,13 @@
 			   int ssi_signal)
 {
 	struct wpa_supplicant *wpa_s = ctx;
+	unsigned int freq = 0;
+
+	if (wpa_s->ap_iface)
+		freq = wpa_s->ap_iface->freq;
+
 	return wpas_p2p_probe_req_rx(wpa_s, sa, da, bssid, ie, ie_len,
-				     ssi_signal);
+				     freq, ssi_signal);
 }
 
 
@@ -545,6 +579,9 @@
 	else
 		params.uapsd = -1;
 
+	if (ieee80211_is_dfs(params.freq.freq))
+		params.freq.freq = 0; /* set channel after CAC */
+
 	if (wpa_drv_associate(wpa_s, &params) < 0) {
 		wpa_msg(wpa_s, MSG_INFO, "Failed to start AP functionality");
 		return -1;
@@ -555,6 +592,7 @@
 		return -1;
 	hapd_iface->owner = wpa_s;
 	hapd_iface->drv_flags = wpa_s->drv_flags;
+	hapd_iface->smps_modes = wpa_s->drv_smps_modes;
 	hapd_iface->probe_resp_offloads = wpa_s->probe_resp_offloads;
 	hapd_iface->extended_capa = wpa_s->extended_capa;
 	hapd_iface->extended_capa_mask = wpa_s->extended_capa_mask;
@@ -629,6 +667,10 @@
 #endif /* CONFIG_P2P */
 		hapd_iface->bss[i]->setup_complete_cb = wpas_ap_configured_cb;
 		hapd_iface->bss[i]->setup_complete_cb_ctx = wpa_s;
+#ifdef CONFIG_TESTING_OPTIONS
+		hapd_iface->bss[i]->ext_eapol_frame_io =
+			wpa_s->ext_eapol_frame_io;
+#endif /* CONFIG_TESTING_OPTIONS */
 	}
 
 	os_memcpy(hapd_iface->bss[0]->own_addr, wpa_s->own_addr, ETH_ALEN);
@@ -670,6 +712,9 @@
 	hostapd_interface_free(wpa_s->ap_iface);
 	wpa_s->ap_iface = NULL;
 	wpa_drv_deinit_ap(wpa_s);
+	wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_DISCONNECTED "bssid=" MACSTR
+		" reason=%d locally_generated=1",
+		MAC2STR(wpa_s->own_addr), WLAN_REASON_DEAUTH_LEAVING);
 }
 
 
@@ -798,9 +843,14 @@
 	if (pin == NULL) {
 		unsigned int rpin = wps_generate_pin();
 		ret_len = os_snprintf(buf, buflen, "%08d", rpin);
+		if (os_snprintf_error(buflen, ret_len))
+			return -1;
 		pin = buf;
-	} else
+	} else if (buf) {
 		ret_len = os_snprintf(buf, buflen, "%s", pin);
+		if (os_snprintf_error(buflen, ret_len))
+			return -1;
+	}
 
 	ret = hostapd_wps_add_pin(wpa_s->ap_iface->bss[0], bssid, "any", pin,
 				  timeout);
@@ -890,7 +940,7 @@
 		return -1;
 	hapd = wpa_s->ap_iface->bss[0];
 	ret = os_snprintf(pin_txt, sizeof(pin_txt), "%s", pin);
-	if (ret < 0 || ret >= (int) sizeof(pin_txt))
+	if (os_snprintf_error(sizeof(pin_txt), ret))
 		return -1;
 	os_free(hapd->conf->ap_pin);
 	hapd->conf->ap_pin = os_strdup(pin_txt);
@@ -975,30 +1025,45 @@
 int ap_ctrl_iface_sta_first(struct wpa_supplicant *wpa_s,
 			    char *buf, size_t buflen)
 {
-	if (wpa_s->ap_iface == NULL)
+	struct hostapd_data *hapd;
+
+	if (wpa_s->ap_iface)
+		hapd = wpa_s->ap_iface->bss[0];
+	else if (wpa_s->ifmsh)
+		hapd = wpa_s->ifmsh->bss[0];
+	else
 		return -1;
-	return hostapd_ctrl_iface_sta_first(wpa_s->ap_iface->bss[0],
-					    buf, buflen);
+	return hostapd_ctrl_iface_sta_first(hapd, buf, buflen);
 }
 
 
 int ap_ctrl_iface_sta(struct wpa_supplicant *wpa_s, const char *txtaddr,
 		      char *buf, size_t buflen)
 {
-	if (wpa_s->ap_iface == NULL)
+	struct hostapd_data *hapd;
+
+	if (wpa_s->ap_iface)
+		hapd = wpa_s->ap_iface->bss[0];
+	else if (wpa_s->ifmsh)
+		hapd = wpa_s->ifmsh->bss[0];
+	else
 		return -1;
-	return hostapd_ctrl_iface_sta(wpa_s->ap_iface->bss[0], txtaddr,
-				      buf, buflen);
+	return hostapd_ctrl_iface_sta(hapd, txtaddr, buf, buflen);
 }
 
 
 int ap_ctrl_iface_sta_next(struct wpa_supplicant *wpa_s, const char *txtaddr,
 			   char *buf, size_t buflen)
 {
-	if (wpa_s->ap_iface == NULL)
+	struct hostapd_data *hapd;
+
+	if (wpa_s->ap_iface)
+		hapd = wpa_s->ap_iface->bss[0];
+	else if (wpa_s->ifmsh)
+		hapd = wpa_s->ifmsh->bss[0];
+	else
 		return -1;
-	return hostapd_ctrl_iface_sta_next(wpa_s->ap_iface->bss[0], txtaddr,
-					   buf, buflen);
+	return hostapd_ctrl_iface_sta_next(hapd, txtaddr, buf, buflen);
 }
 
 
@@ -1044,7 +1109,7 @@
 			  wpa_cipher_txt(conf->wpa_group),
 			  wpa_key_mgmt_txt(conf->wpa_key_mgmt,
 					   conf->wpa));
-	if (ret < 0 || ret >= end - pos)
+	if (os_snprintf_error(end - pos, ret))
 		return pos - buf;
 	pos += ret;
 	return pos - buf;
@@ -1096,6 +1161,7 @@
 }
 
 
+#ifdef CONFIG_CTRL_IFACE
 int ap_ctrl_iface_chanswitch(struct wpa_supplicant *wpa_s, const char *pos)
 {
 	struct csa_settings settings;
@@ -1106,6 +1172,7 @@
 
 	return ap_switch_channel(wpa_s, &settings);
 }
+#endif /* CONFIG_CTRL_IFACE */
 
 
 void wpas_ap_ch_switch(struct wpa_supplicant *wpa_s, int freq, int ht,
@@ -1203,3 +1270,79 @@
 					      pw ? wpabuf_len(pw) : 0, 1);
 }
 #endif /* CONFIG_WPS_NFC */
+
+
+#ifdef CONFIG_CTRL_IFACE
+int wpas_ap_stop_ap(struct wpa_supplicant *wpa_s)
+{
+	struct hostapd_data *hapd;
+
+	if (!wpa_s->ap_iface)
+		return -1;
+	hapd = wpa_s->ap_iface->bss[0];
+	return hostapd_ctrl_iface_stop_ap(hapd);
+}
+#endif /* CONFIG_CTRL_IFACE */
+
+
+#ifdef NEED_AP_MLME
+void wpas_event_dfs_radar_detected(struct wpa_supplicant *wpa_s,
+				   struct dfs_event *radar)
+{
+	if (!wpa_s->ap_iface || !wpa_s->ap_iface->bss[0])
+		return;
+	wpa_printf(MSG_DEBUG, "DFS radar detected on %d MHz", radar->freq);
+	hostapd_dfs_radar_detected(wpa_s->ap_iface, radar->freq,
+				   radar->ht_enabled, radar->chan_offset,
+				   radar->chan_width,
+				   radar->cf1, radar->cf2);
+}
+
+
+void wpas_event_dfs_cac_started(struct wpa_supplicant *wpa_s,
+				struct dfs_event *radar)
+{
+	if (!wpa_s->ap_iface || !wpa_s->ap_iface->bss[0])
+		return;
+	wpa_printf(MSG_DEBUG, "DFS CAC started on %d MHz", radar->freq);
+	hostapd_dfs_start_cac(wpa_s->ap_iface, radar->freq,
+			      radar->ht_enabled, radar->chan_offset,
+			      radar->chan_width, radar->cf1, radar->cf2);
+}
+
+
+void wpas_event_dfs_cac_finished(struct wpa_supplicant *wpa_s,
+				 struct dfs_event *radar)
+{
+	if (!wpa_s->ap_iface || !wpa_s->ap_iface->bss[0])
+		return;
+	wpa_printf(MSG_DEBUG, "DFS CAC finished on %d MHz", radar->freq);
+	hostapd_dfs_complete_cac(wpa_s->ap_iface, 1, radar->freq,
+				 radar->ht_enabled, radar->chan_offset,
+				 radar->chan_width, radar->cf1, radar->cf2);
+}
+
+
+void wpas_event_dfs_cac_aborted(struct wpa_supplicant *wpa_s,
+				struct dfs_event *radar)
+{
+	if (!wpa_s->ap_iface || !wpa_s->ap_iface->bss[0])
+		return;
+	wpa_printf(MSG_DEBUG, "DFS CAC aborted on %d MHz", radar->freq);
+	hostapd_dfs_complete_cac(wpa_s->ap_iface, 0, radar->freq,
+				 radar->ht_enabled, radar->chan_offset,
+				 radar->chan_width, radar->cf1, radar->cf2);
+}
+
+
+void wpas_event_dfs_cac_nop_finished(struct wpa_supplicant *wpa_s,
+				     struct dfs_event *radar)
+{
+	if (!wpa_s->ap_iface || !wpa_s->ap_iface->bss[0])
+		return;
+	wpa_printf(MSG_DEBUG, "DFS NOP finished on %d MHz", radar->freq);
+	hostapd_dfs_nop_finished(wpa_s->ap_iface, radar->freq,
+				 radar->ht_enabled, radar->chan_offset,
+				 radar->chan_width, radar->cf1, radar->cf2);
+}
+#endif /* NEED_AP_MLME */
diff --git a/wpa_supplicant/ap.h b/wpa_supplicant/ap.h
index 8aa5ffa..3f4151d 100644
--- a/wpa_supplicant/ap.h
+++ b/wpa_supplicant/ap.h
@@ -75,4 +75,22 @@
 int wpas_ap_wps_add_nfc_pw(struct wpa_supplicant *wpa_s, u16 pw_id,
 			   const struct wpabuf *pw, const u8 *pubkey_hash);
 
+struct hostapd_config;
+void wpa_supplicant_conf_ap_ht(struct wpa_supplicant *wpa_s,
+			       struct wpa_ssid *ssid,
+			       struct hostapd_config *conf);
+
+int wpas_ap_stop_ap(struct wpa_supplicant *wpa_s);
+
+void wpas_event_dfs_radar_detected(struct wpa_supplicant *wpa_s,
+				   struct dfs_event *radar);
+void wpas_event_dfs_cac_started(struct wpa_supplicant *wpa_s,
+				struct dfs_event *radar);
+void wpas_event_dfs_cac_finished(struct wpa_supplicant *wpa_s,
+				 struct dfs_event *radar);
+void wpas_event_dfs_cac_aborted(struct wpa_supplicant *wpa_s,
+				struct dfs_event *radar);
+void wpas_event_dfs_cac_nop_finished(struct wpa_supplicant *wpa_s,
+				     struct dfs_event *radar);
+
 #endif /* AP_H */
diff --git a/wpa_supplicant/bgscan_learn.c b/wpa_supplicant/bgscan_learn.c
index 6a92b73..a320cc4 100644
--- a/wpa_supplicant/bgscan_learn.c
+++ b/wpa_supplicant/bgscan_learn.c
@@ -294,7 +294,7 @@
 			int ret;
 			ret = os_snprintf(pos, msg + sizeof(msg) - pos, " %d",
 					  freqs[i]);
-			if (ret < 0 || ret >= msg + sizeof(msg) - pos)
+			if (os_snprintf_error(msg + sizeof(msg) - pos, ret))
 				break;
 			pos += ret;
 		}
diff --git a/wpa_supplicant/bss.c b/wpa_supplicant/bss.c
index 1de51e5..8134562 100644
--- a/wpa_supplicant/bss.c
+++ b/wpa_supplicant/bss.c
@@ -1,6 +1,6 @@
 /*
  * BSS table
- * Copyright (c) 2009-2012, 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.
@@ -85,6 +85,7 @@
 
 #define ANQP_DUP(f) if (anqp->f) n->f = wpabuf_dup(anqp->f)
 #ifdef CONFIG_INTERWORKING
+	ANQP_DUP(capability_list);
 	ANQP_DUP(venue_name);
 	ANQP_DUP(network_auth_type);
 	ANQP_DUP(roaming_consortium);
@@ -94,6 +95,7 @@
 	ANQP_DUP(domain_name);
 #endif /* CONFIG_INTERWORKING */
 #ifdef CONFIG_HS20
+	ANQP_DUP(hs20_capability_list);
 	ANQP_DUP(hs20_operator_friendly_name);
 	ANQP_DUP(hs20_wan_metrics);
 	ANQP_DUP(hs20_connection_capability);
@@ -154,6 +156,7 @@
 	}
 
 #ifdef CONFIG_INTERWORKING
+	wpabuf_free(anqp->capability_list);
 	wpabuf_free(anqp->venue_name);
 	wpabuf_free(anqp->network_auth_type);
 	wpabuf_free(anqp->roaming_consortium);
@@ -163,6 +166,7 @@
 	wpabuf_free(anqp->domain_name);
 #endif /* CONFIG_INTERWORKING */
 #ifdef CONFIG_HS20
+	wpabuf_free(anqp->hs20_capability_list);
 	wpabuf_free(anqp->hs20_operator_friendly_name);
 	wpabuf_free(anqp->hs20_wan_metrics);
 	wpabuf_free(anqp->hs20_connection_capability);
@@ -282,6 +286,8 @@
 	dst->noise = src->noise;
 	dst->level = src->level;
 	dst->tsf = src->tsf;
+	dst->est_throughput = src->est_throughput;
+	dst->snr = src->snr;
 
 	calculate_update_time(fetch_time, src->age, &dst->last_update);
 }
@@ -306,8 +312,9 @@
 static int wpa_bss_in_use(struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
 {
 	return bss == wpa_s->current_bss ||
-		os_memcmp(bss->bssid, wpa_s->bssid, ETH_ALEN) == 0 ||
-		os_memcmp(bss->bssid, wpa_s->pending_bssid, ETH_ALEN) == 0;
+		(!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));
 }
 
 
@@ -391,7 +398,7 @@
 
 
 static int are_ies_equal(const struct wpa_bss *old,
-			 const struct wpa_scan_res *new, u32 ie)
+			 const struct wpa_scan_res *new_res, u32 ie)
 {
 	const u8 *old_ie, *new_ie;
 	struct wpabuf *old_ie_buff = NULL;
@@ -401,19 +408,19 @@
 	switch (ie) {
 	case WPA_IE_VENDOR_TYPE:
 		old_ie = wpa_bss_get_vendor_ie(old, ie);
-		new_ie = wpa_scan_get_vendor_ie(new, ie);
+		new_ie = wpa_scan_get_vendor_ie(new_res, ie);
 		is_multi = 0;
 		break;
 	case WPS_IE_VENDOR_TYPE:
 		old_ie_buff = wpa_bss_get_vendor_ie_multi(old, ie);
-		new_ie_buff = wpa_scan_get_vendor_ie_multi(new, ie);
+		new_ie_buff = wpa_scan_get_vendor_ie_multi(new_res, ie);
 		is_multi = 1;
 		break;
 	case WLAN_EID_RSN:
 	case WLAN_EID_SUPP_RATES:
 	case WLAN_EID_EXT_SUPP_RATES:
 		old_ie = wpa_bss_get_ie(old, ie);
-		new_ie = wpa_scan_get_ie(new, ie);
+		new_ie = wpa_scan_get_ie(new_res, ie);
 		is_multi = 0;
 		break;
 	default:
@@ -447,15 +454,15 @@
 
 
 static u32 wpa_bss_compare_res(const struct wpa_bss *old,
-			       const struct wpa_scan_res *new)
+			       const struct wpa_scan_res *new_res)
 {
 	u32 changes = 0;
-	int caps_diff = old->caps ^ new->caps;
+	int caps_diff = old->caps ^ new_res->caps;
 
-	if (old->freq != new->freq)
+	if (old->freq != new_res->freq)
 		changes |= WPA_BSS_FREQ_CHANGED_FLAG;
 
-	if (old->level != new->level)
+	if (old->level != new_res->level)
 		changes |= WPA_BSS_SIGNAL_CHANGED_FLAG;
 
 	if (caps_diff & IEEE80211_CAP_PRIVACY)
@@ -464,22 +471,22 @@
 	if (caps_diff & IEEE80211_CAP_IBSS)
 		changes |= WPA_BSS_MODE_CHANGED_FLAG;
 
-	if (old->ie_len == new->ie_len &&
-	    os_memcmp(old + 1, new + 1, old->ie_len) == 0)
+	if (old->ie_len == new_res->ie_len &&
+	    os_memcmp(old + 1, new_res + 1, old->ie_len) == 0)
 		return changes;
 	changes |= WPA_BSS_IES_CHANGED_FLAG;
 
-	if (!are_ies_equal(old, new, WPA_IE_VENDOR_TYPE))
+	if (!are_ies_equal(old, new_res, WPA_IE_VENDOR_TYPE))
 		changes |= WPA_BSS_WPAIE_CHANGED_FLAG;
 
-	if (!are_ies_equal(old, new, WLAN_EID_RSN))
+	if (!are_ies_equal(old, new_res, WLAN_EID_RSN))
 		changes |= WPA_BSS_RSNIE_CHANGED_FLAG;
 
-	if (!are_ies_equal(old, new, WPS_IE_VENDOR_TYPE))
+	if (!are_ies_equal(old, new_res, WPS_IE_VENDOR_TYPE))
 		changes |= WPA_BSS_WPS_CHANGED_FLAG;
 
-	if (!are_ies_equal(old, new, WLAN_EID_SUPP_RATES) ||
-	    !are_ies_equal(old, new, WLAN_EID_EXT_SUPP_RATES))
+	if (!are_ies_equal(old, new_res, WLAN_EID_SUPP_RATES) ||
+	    !are_ies_equal(old, new_res, WLAN_EID_EXT_SUPP_RATES))
 		changes |= WPA_BSS_RATES_CHANGED_FLAG;
 
 	return changes;
@@ -620,7 +627,7 @@
 			     struct wpa_scan_res *res,
 			     struct os_reltime *fetch_time)
 {
-	const u8 *ssid, *p2p;
+	const u8 *ssid, *p2p, *mesh;
 	struct wpa_bss *bss;
 
 	if (wpa_s->conf->ignore_old_scan_res) {
@@ -645,7 +652,7 @@
 			MACSTR, MAC2STR(res->bssid));
 		return;
 	}
-	if (ssid[1] > 32) {
+	if (ssid[1] > SSID_MAX_LEN) {
 		wpa_dbg(wpa_s, MSG_DEBUG, "BSS: Too long SSID IE included for "
 			MACSTR, MAC2STR(res->bssid));
 		return;
@@ -670,6 +677,11 @@
 
 	/* TODO: add option for ignoring BSSes we are not interested in
 	 * (to save memory) */
+
+	mesh = wpa_scan_get_ie(res, WLAN_EID_MESH_ID);
+	if (mesh && mesh[1] <= SSID_MAX_LEN)
+		ssid = mesh;
+
 	bss = wpa_bss_get(wpa_s, res->bssid, ssid + 2, ssid[1]);
 	if (bss == NULL)
 		bss = wpa_bss_add(wpa_s, ssid + 2, ssid[1], res, fetch_time);
diff --git a/wpa_supplicant/bss.h b/wpa_supplicant/bss.h
index 4a624c5..b215380 100644
--- a/wpa_supplicant/bss.h
+++ b/wpa_supplicant/bss.h
@@ -1,6 +1,6 @@
 /*
  * BSS table
- * Copyright (c) 2009-2010, 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.
@@ -26,6 +26,7 @@
 	/** Number of BSS entries referring to this ANQP data instance */
 	unsigned int users;
 #ifdef CONFIG_INTERWORKING
+	struct wpabuf *capability_list;
 	struct wpabuf *venue_name;
 	struct wpabuf *network_auth_type;
 	struct wpabuf *roaming_consortium;
@@ -35,6 +36,7 @@
 	struct wpabuf *domain_name;
 #endif /* CONFIG_INTERWORKING */
 #ifdef CONFIG_HS20
+	struct wpabuf *hs20_capability_list;
 	struct wpabuf *hs20_operator_friendly_name;
 	struct wpabuf *hs20_wan_metrics;
 	struct wpabuf *hs20_connection_capability;
@@ -67,7 +69,7 @@
 	/** HESSID */
 	u8 hessid[ETH_ALEN];
 	/** SSID */
-	u8 ssid[32];
+	u8 ssid[SSID_MAX_LEN];
 	/** Length of SSID */
 	size_t ssid_len;
 	/** Frequency of the channel in MHz (e.g., 2412 = channel 1) */
@@ -86,6 +88,10 @@
 	u64 tsf;
 	/** Time of the last update (i.e., Beacon or Probe Response RX) */
 	struct os_reltime last_update;
+	/** Estimated throughput in kbps */
+	unsigned int est_throughput;
+	/** Signal-to-noise ratio in dB */
+	int snr;
 	/** ANQP data */
 	struct wpa_bss_anqp *anqp;
 	/** Length of the following IE field in octets (from Probe Response) */
@@ -135,4 +141,10 @@
 	return bss->freq > 45000;
 }
 
+static inline void wpa_bss_update_level(struct wpa_bss *bss, int new_level)
+{
+	if (bss != NULL && new_level < 0)
+		bss->level = new_level;
+}
+
 #endif /* BSS_H */
diff --git a/wpa_supplicant/config.c b/wpa_supplicant/config.c
index 467f586..8a34e84 100644
--- a/wpa_supplicant/config.c
+++ b/wpa_supplicant/config.c
@@ -1,6 +1,6 @@
 /*
  * WPA Supplicant / Configuration parser and common functions
- * Copyright (c) 2003-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.
@@ -225,7 +225,7 @@
 	if (value == NULL)
 		return NULL;
 	res = os_snprintf(value, 20, "%d", *src);
-	if (res < 0 || res >= 20) {
+	if (os_snprintf_error(20, res)) {
 		os_free(value);
 		return NULL;
 	}
@@ -235,6 +235,99 @@
 #endif /* NO_CONFIG_WRITE */
 
 
+static int wpa_config_parse_addr_list(const struct parse_data *data,
+				      int line, const char *value,
+				      u8 **list, size_t *num, char *name,
+				      u8 abort_on_error, u8 masked)
+{
+	const char *pos;
+	u8 *buf, *n, addr[2 * ETH_ALEN];
+	size_t count;
+
+	buf = NULL;
+	count = 0;
+
+	pos = value;
+	while (pos && *pos) {
+		while (*pos == ' ')
+			pos++;
+
+		if (hwaddr_masked_aton(pos, addr, &addr[ETH_ALEN], masked)) {
+			if (abort_on_error || count == 0) {
+				wpa_printf(MSG_ERROR,
+					   "Line %d: Invalid %s address '%s'",
+					   line, name, value);
+				os_free(buf);
+				return -1;
+			}
+			/* continue anyway since this could have been from a
+			 * truncated configuration file line */
+			wpa_printf(MSG_INFO,
+				   "Line %d: Ignore likely truncated %s address '%s'",
+				   line, name, pos);
+		} else {
+			n = os_realloc_array(buf, count + 1, 2 * ETH_ALEN);
+			if (n == NULL) {
+				os_free(buf);
+				return -1;
+			}
+			buf = n;
+			os_memmove(buf + 2 * ETH_ALEN, buf,
+				   count * 2 * ETH_ALEN);
+			os_memcpy(buf, addr, 2 * ETH_ALEN);
+			count++;
+			wpa_printf(MSG_MSGDUMP,
+				   "%s: addr=" MACSTR " mask=" MACSTR,
+				   name, MAC2STR(addr),
+				   MAC2STR(&addr[ETH_ALEN]));
+		}
+
+		pos = os_strchr(pos, ' ');
+	}
+
+	os_free(*list);
+	*list = buf;
+	*num = count;
+
+	return 0;
+}
+
+
+#ifndef NO_CONFIG_WRITE
+static char * wpa_config_write_addr_list(const struct parse_data *data,
+					 const u8 *list, size_t num, char *name)
+{
+	char *value, *end, *pos;
+	int res;
+	size_t i;
+
+	if (list == NULL || num == 0)
+		return NULL;
+
+	value = os_malloc(2 * 20 * num);
+	if (value == NULL)
+		return NULL;
+	pos = value;
+	end = value + 2 * 20 * num;
+
+	for (i = num; i > 0; i--) {
+		const u8 *a = list + (i - 1) * 2 * ETH_ALEN;
+		const u8 *m = a + ETH_ALEN;
+
+		if (i < num)
+			*pos++ = ' ';
+		res = hwaddr_mask_txt(pos, end - pos, a, m);
+		if (res < 0) {
+			os_free(value);
+			return NULL;
+		}
+		pos += res;
+	}
+
+	return value;
+}
+#endif /* NO_CONFIG_WRITE */
+
 static int wpa_config_parse_bssid(const struct parse_data *data,
 				  struct wpa_ssid *ssid, int line,
 				  const char *value)
@@ -270,7 +363,7 @@
 	if (value == NULL)
 		return NULL;
 	res = os_snprintf(value, 20, MACSTR, MAC2STR(ssid->bssid));
-	if (res < 0 || res >= 20) {
+	if (os_snprintf_error(20, res)) {
 		os_free(value);
 		return NULL;
 	}
@@ -280,6 +373,50 @@
 #endif /* NO_CONFIG_WRITE */
 
 
+static int wpa_config_parse_bssid_blacklist(const struct parse_data *data,
+					    struct wpa_ssid *ssid, int line,
+					    const char *value)
+{
+	return wpa_config_parse_addr_list(data, line, value,
+					  &ssid->bssid_blacklist,
+					  &ssid->num_bssid_blacklist,
+					  "bssid_blacklist", 1, 1);
+}
+
+
+#ifndef NO_CONFIG_WRITE
+static char * wpa_config_write_bssid_blacklist(const struct parse_data *data,
+					       struct wpa_ssid *ssid)
+{
+	return wpa_config_write_addr_list(data, ssid->bssid_blacklist,
+					  ssid->num_bssid_blacklist,
+					  "bssid_blacklist");
+}
+#endif /* NO_CONFIG_WRITE */
+
+
+static int wpa_config_parse_bssid_whitelist(const struct parse_data *data,
+					    struct wpa_ssid *ssid, int line,
+					    const char *value)
+{
+	return wpa_config_parse_addr_list(data, line, value,
+					  &ssid->bssid_whitelist,
+					  &ssid->num_bssid_whitelist,
+					  "bssid_whitelist", 1, 1);
+}
+
+
+#ifndef NO_CONFIG_WRITE
+static char * wpa_config_write_bssid_whitelist(const struct parse_data *data,
+					       struct wpa_ssid *ssid)
+{
+	return wpa_config_write_addr_list(data, ssid->bssid_whitelist,
+					  ssid->num_bssid_whitelist,
+					  "bssid_whitelist");
+}
+#endif /* NO_CONFIG_WRITE */
+
+
 static int wpa_config_parse_psk(const struct parse_data *data,
 				struct wpa_ssid *ssid, int line,
 				const char *value)
@@ -358,9 +495,15 @@
 	if (ssid->ext_psk) {
 		size_t len = 4 + os_strlen(ssid->ext_psk) + 1;
 		char *buf = os_malloc(len);
+		int res;
+
 		if (buf == NULL)
 			return NULL;
-		os_snprintf(buf, len, "ext:%s", ssid->ext_psk);
+		res = os_snprintf(buf, len, "ext:%s", ssid->ext_psk);
+		if (os_snprintf_error(len, res)) {
+			os_free(buf);
+			buf = NULL;
+		}
 		return buf;
 	}
 #endif /* CONFIG_EXT_PASSWORD */
@@ -446,7 +589,7 @@
 	if (ssid->proto & WPA_PROTO_WPA) {
 		ret = os_snprintf(pos, end - pos, "%sWPA",
 				  pos == buf ? "" : " ");
-		if (ret < 0 || ret >= end - pos)
+		if (os_snprintf_error(end - pos, ret))
 			return buf;
 		pos += ret;
 	}
@@ -454,7 +597,7 @@
 	if (ssid->proto & WPA_PROTO_RSN) {
 		ret = os_snprintf(pos, end - pos, "%sRSN",
 				  pos == buf ? "" : " ");
-		if (ret < 0 || ret >= end - pos)
+		if (os_snprintf_error(end - pos, ret))
 			return buf;
 		pos += ret;
 	}
@@ -462,7 +605,7 @@
 	if (ssid->proto & WPA_PROTO_OSEN) {
 		ret = os_snprintf(pos, end - pos, "%sOSEN",
 				  pos == buf ? "" : " ");
-		if (ret < 0 || ret >= end - pos)
+		if (os_snprintf_error(end - pos, ret))
 			return buf;
 		pos += ret;
 	}
@@ -535,6 +678,14 @@
 		else if (os_strcmp(start, "OSEN") == 0)
 			val |= WPA_KEY_MGMT_OSEN;
 #endif /* CONFIG_HS20 */
+#ifdef CONFIG_SUITEB
+		else if (os_strcmp(start, "WPA-EAP-SUITE-B") == 0)
+			val |= WPA_KEY_MGMT_IEEE8021X_SUITE_B;
+#endif /* CONFIG_SUITEB */
+#ifdef CONFIG_SUITEB192
+		else if (os_strcmp(start, "WPA-EAP-SUITE-B-192") == 0)
+			val |= WPA_KEY_MGMT_IEEE8021X_SUITE_B_192;
+#endif /* CONFIG_SUITEB192 */
 		else {
 			wpa_printf(MSG_ERROR, "Line %d: invalid key_mgmt '%s'",
 				   line, start);
@@ -574,7 +725,7 @@
 	if (ssid->key_mgmt & WPA_KEY_MGMT_PSK) {
 		ret = os_snprintf(pos, end - pos, "%sWPA-PSK",
 				  pos == buf ? "" : " ");
-		if (ret < 0 || ret >= end - pos) {
+		if (os_snprintf_error(end - pos, ret)) {
 			end[-1] = '\0';
 			return buf;
 		}
@@ -584,7 +735,7 @@
 	if (ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X) {
 		ret = os_snprintf(pos, end - pos, "%sWPA-EAP",
 				  pos == buf ? "" : " ");
-		if (ret < 0 || ret >= end - pos) {
+		if (os_snprintf_error(end - pos, ret)) {
 			end[-1] = '\0';
 			return buf;
 		}
@@ -594,7 +745,7 @@
 	if (ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA) {
 		ret = os_snprintf(pos, end - pos, "%sIEEE8021X",
 				  pos == buf ? "" : " ");
-		if (ret < 0 || ret >= end - pos) {
+		if (os_snprintf_error(end - pos, ret)) {
 			end[-1] = '\0';
 			return buf;
 		}
@@ -604,7 +755,7 @@
 	if (ssid->key_mgmt & WPA_KEY_MGMT_NONE) {
 		ret = os_snprintf(pos, end - pos, "%sNONE",
 				  pos == buf ? "" : " ");
-		if (ret < 0 || ret >= end - pos) {
+		if (os_snprintf_error(end - pos, ret)) {
 			end[-1] = '\0';
 			return buf;
 		}
@@ -614,7 +765,7 @@
 	if (ssid->key_mgmt & WPA_KEY_MGMT_WPA_NONE) {
 		ret = os_snprintf(pos, end - pos, "%sWPA-NONE",
 				  pos == buf ? "" : " ");
-		if (ret < 0 || ret >= end - pos) {
+		if (os_snprintf_error(end - pos, ret)) {
 			end[-1] = '\0';
 			return buf;
 		}
@@ -625,7 +776,7 @@
 	if (ssid->key_mgmt & WPA_KEY_MGMT_FT_PSK) {
 		ret = os_snprintf(pos, end - pos, "%sFT-PSK",
 				  pos == buf ? "" : " ");
-		if (ret < 0 || ret >= end - pos) {
+		if (os_snprintf_error(end - pos, ret)) {
 			end[-1] = '\0';
 			return buf;
 		}
@@ -635,7 +786,7 @@
 	if (ssid->key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X) {
 		ret = os_snprintf(pos, end - pos, "%sFT-EAP",
 				  pos == buf ? "" : " ");
-		if (ret < 0 || ret >= end - pos) {
+		if (os_snprintf_error(end - pos, ret)) {
 			end[-1] = '\0';
 			return buf;
 		}
@@ -647,7 +798,7 @@
 	if (ssid->key_mgmt & WPA_KEY_MGMT_PSK_SHA256) {
 		ret = os_snprintf(pos, end - pos, "%sWPA-PSK-SHA256",
 				  pos == buf ? "" : " ");
-		if (ret < 0 || ret >= end - pos) {
+		if (os_snprintf_error(end - pos, ret)) {
 			end[-1] = '\0';
 			return buf;
 		}
@@ -657,7 +808,7 @@
 	if (ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_SHA256) {
 		ret = os_snprintf(pos, end - pos, "%sWPA-EAP-SHA256",
 				  pos == buf ? "" : " ");
-		if (ret < 0 || ret >= end - pos) {
+		if (os_snprintf_error(end - pos, ret)) {
 			end[-1] = '\0';
 			return buf;
 		}
@@ -669,7 +820,7 @@
 	if (ssid->key_mgmt & WPA_KEY_MGMT_WPS) {
 		ret = os_snprintf(pos, end - pos, "%sWPS",
 				  pos == buf ? "" : " ");
-		if (ret < 0 || ret >= end - pos) {
+		if (os_snprintf_error(end - pos, ret)) {
 			end[-1] = '\0';
 			return buf;
 		}
@@ -677,6 +828,64 @@
 	}
 #endif /* CONFIG_WPS */
 
+#ifdef CONFIG_SAE
+	if (ssid->key_mgmt & WPA_KEY_MGMT_SAE) {
+		ret = os_snprintf(pos, end - pos, "%sSAE",
+				  pos == buf ? "" : " ");
+		if (os_snprintf_error(end - pos, ret)) {
+			end[-1] = '\0';
+			return buf;
+		}
+		pos += ret;
+	}
+
+	if (ssid->key_mgmt & WPA_KEY_MGMT_FT_SAE) {
+		ret = os_snprintf(pos, end - pos, "%sFT-SAE",
+				  pos == buf ? "" : " ");
+		if (os_snprintf_error(end - pos, ret)) {
+			end[-1] = '\0';
+			return buf;
+		}
+		pos += ret;
+	}
+#endif /* CONFIG_SAE */
+
+#ifdef CONFIG_HS20
+	if (ssid->key_mgmt & WPA_KEY_MGMT_OSEN) {
+		ret = os_snprintf(pos, end - pos, "%sOSEN",
+				  pos == buf ? "" : " ");
+		if (os_snprintf_error(end - pos, ret)) {
+			end[-1] = '\0';
+			return buf;
+		}
+		pos += ret;
+	}
+#endif /* CONFIG_HS20 */
+
+#ifdef CONFIG_SUITEB
+	if (ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B) {
+		ret = os_snprintf(pos, end - pos, "%sWPA-EAP-SUITE-B",
+				  pos == buf ? "" : " ");
+		if (os_snprintf_error(end - pos, ret)) {
+			end[-1] = '\0';
+			return buf;
+		}
+		pos += ret;
+	}
+#endif /* CONFIG_SUITEB */
+
+#ifdef CONFIG_SUITEB192
+	if (ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B_192) {
+		ret = os_snprintf(pos, end - pos, "%sWPA-EAP-SUITE-B-192",
+				  pos == buf ? "" : " ");
+		if (os_snprintf_error(end - pos, ret)) {
+			end[-1] = '\0';
+			return buf;
+		}
+		pos += ret;
+	}
+#endif /* CONFIG_SUITEB192 */
+
 	if (pos == buf) {
 		os_free(buf);
 		buf = NULL;
@@ -758,6 +967,13 @@
 	val = wpa_config_parse_cipher(line, value);
 	if (val == -1)
 		return -1;
+
+	/*
+	 * Backwards compatibility - filter out WEP ciphers that were previously
+	 * allowed.
+	 */
+	val &= ~(WPA_CIPHER_WEP104 | WPA_CIPHER_WEP40);
+
 	if (val & ~WPA_ALLOWED_GROUP_CIPHERS) {
 		wpa_printf(MSG_ERROR, "Line %d: not allowed group cipher "
 			   "(0x%x).", line, val);
@@ -846,7 +1062,7 @@
 	if (ssid->auth_alg & WPA_AUTH_ALG_OPEN) {
 		ret = os_snprintf(pos, end - pos, "%sOPEN",
 				  pos == buf ? "" : " ");
-		if (ret < 0 || ret >= end - pos) {
+		if (os_snprintf_error(end - pos, ret)) {
 			end[-1] = '\0';
 			return buf;
 		}
@@ -856,7 +1072,7 @@
 	if (ssid->auth_alg & WPA_AUTH_ALG_SHARED) {
 		ret = os_snprintf(pos, end - pos, "%sSHARED",
 				  pos == buf ? "" : " ");
-		if (ret < 0 || ret >= end - pos) {
+		if (os_snprintf_error(end - pos, ret)) {
 			end[-1] = '\0';
 			return buf;
 		}
@@ -866,7 +1082,7 @@
 	if (ssid->auth_alg & WPA_AUTH_ALG_LEAP) {
 		ret = os_snprintf(pos, end - pos, "%sLEAP",
 				  pos == buf ? "" : " ");
-		if (ret < 0 || ret >= end - pos) {
+		if (os_snprintf_error(end - pos, ret)) {
 			end[-1] = '\0';
 			return buf;
 		}
@@ -987,7 +1203,7 @@
 	for (i = 0; freqs[i]; i++) {
 		ret = os_snprintf(pos, end - pos, "%s%u",
 				  i == 0 ? "" : " ", freqs[i]);
-		if (ret < 0 || ret >= end - pos) {
+		if (os_snprintf_error(end - pos, ret)) {
 			end[-1] = '\0';
 			return buf;
 		}
@@ -1087,6 +1303,7 @@
 }
 
 
+#ifndef NO_CONFIG_WRITE
 static char * wpa_config_write_eap(const struct parse_data *data,
 				   struct wpa_ssid *ssid)
 {
@@ -1110,7 +1327,7 @@
 		if (name) {
 			ret = os_snprintf(pos, end - pos, "%s%s",
 					  pos == buf ? "" : " ", name);
-			if (ret < 0 || ret >= end - pos)
+			if (os_snprintf_error(end - pos, ret))
 				break;
 			pos += ret;
 		}
@@ -1120,6 +1337,7 @@
 
 	return buf;
 }
+#endif /* NO_CONFIG_WRITE */
 
 
 static int wpa_config_parse_password(const struct parse_data *data,
@@ -1202,6 +1420,7 @@
 }
 
 
+#ifndef NO_CONFIG_WRITE
 static char * wpa_config_write_password(const struct parse_data *data,
 					struct wpa_ssid *ssid)
 {
@@ -1235,6 +1454,7 @@
 
 	return buf;
 }
+#endif /* NO_CONFIG_WRITE */
 #endif /* IEEE8021X_EAPOL */
 
 
@@ -1264,7 +1484,7 @@
 	os_memcpy(key, buf, *len);
 	str_clear_free(buf);
 	res = os_snprintf(title, sizeof(title), "wep_key%d", idx);
-	if (res >= 0 && (size_t) res < sizeof(title))
+	if (!os_snprintf_error(sizeof(title), res))
 		wpa_hexdump_key(MSG_MSGDUMP, title, key, *len);
 	return 0;
 }
@@ -1387,7 +1607,7 @@
 	if (value == NULL)
 		return NULL;
 	res = os_snprintf(value, 20, MACSTR, MAC2STR(ssid->go_p2p_dev_addr));
-	if (res < 0 || res >= 20) {
+	if (os_snprintf_error(20, res)) {
 		os_free(value);
 		return NULL;
 	}
@@ -1401,53 +1621,10 @@
 					    struct wpa_ssid *ssid, int line,
 					    const char *value)
 {
-	const char *pos;
-	u8 *buf, *n, addr[ETH_ALEN];
-	size_t count;
-
-	buf = NULL;
-	count = 0;
-
-	pos = value;
-	while (pos && *pos) {
-		while (*pos == ' ')
-			pos++;
-
-		if (hwaddr_aton(pos, addr)) {
-			if (count == 0) {
-				wpa_printf(MSG_ERROR, "Line %d: Invalid "
-					   "p2p_client_list address '%s'.",
-					   line, value);
-				os_free(buf);
-				return -1;
-			}
-			/* continue anyway since this could have been from a
-			 * truncated configuration file line */
-			wpa_printf(MSG_INFO, "Line %d: Ignore likely "
-				   "truncated p2p_client_list address '%s'",
-				   line, pos);
-		} else {
-			n = os_realloc_array(buf, count + 1, ETH_ALEN);
-			if (n == NULL) {
-				os_free(buf);
-				return -1;
-			}
-			buf = n;
-			os_memmove(buf + ETH_ALEN, buf, count * ETH_ALEN);
-			os_memcpy(buf, addr, ETH_ALEN);
-			count++;
-			wpa_hexdump(MSG_MSGDUMP, "p2p_client_list",
-				    addr, ETH_ALEN);
-		}
-
-		pos = os_strchr(pos, ' ');
-	}
-
-	os_free(ssid->p2p_client_list);
-	ssid->p2p_client_list = buf;
-	ssid->num_p2p_clients = count;
-
-	return 0;
+	return wpa_config_parse_addr_list(data, line, value,
+					  &ssid->p2p_client_list,
+					  &ssid->num_p2p_clients,
+					  "p2p_client_list", 0, 0);
 }
 
 
@@ -1455,34 +1632,9 @@
 static char * wpa_config_write_p2p_client_list(const struct parse_data *data,
 					       struct wpa_ssid *ssid)
 {
-	char *value, *end, *pos;
-	int res;
-	size_t i;
-
-	if (ssid->p2p_client_list == NULL || ssid->num_p2p_clients == 0)
-		return NULL;
-
-	value = os_malloc(20 * ssid->num_p2p_clients);
-	if (value == NULL)
-		return NULL;
-	pos = value;
-	end = value + 20 * ssid->num_p2p_clients;
-
-	for (i = ssid->num_p2p_clients; i > 0; i--) {
-		res = os_snprintf(pos, end - pos, MACSTR " ",
-				  MAC2STR(ssid->p2p_client_list +
-					  (i - 1) * ETH_ALEN));
-		if (res < 0 || res >= end - pos) {
-			os_free(value);
-			return NULL;
-		}
-		pos += res;
-	}
-
-	if (pos > value)
-		pos[-1] = '\0';
-
-	return value;
+	return wpa_config_write_addr_list(data, ssid->p2p_client_list,
+					  ssid->num_p2p_clients,
+					  "p2p_client_list");
 }
 #endif /* NO_CONFIG_WRITE */
 
@@ -1542,6 +1694,45 @@
 
 #endif /* CONFIG_P2P */
 
+
+#ifdef CONFIG_MESH
+
+static int wpa_config_parse_mesh_basic_rates(const struct parse_data *data,
+					     struct wpa_ssid *ssid, int line,
+					     const char *value)
+{
+	int *rates = wpa_config_parse_int_array(value);
+
+	if (rates == NULL) {
+		wpa_printf(MSG_ERROR, "Line %d: Invalid mesh_basic_rates '%s'",
+			   line, value);
+		return -1;
+	}
+	if (rates[0] == 0) {
+		os_free(rates);
+		rates = NULL;
+	}
+
+	os_free(ssid->mesh_basic_rates);
+	ssid->mesh_basic_rates = rates;
+
+	return 0;
+}
+
+
+#ifndef NO_CONFIG_WRITE
+
+static char * wpa_config_write_mesh_basic_rates(const struct parse_data *data,
+						struct wpa_ssid *ssid)
+{
+	return wpa_config_write_freqs(data, ssid->mesh_basic_rates);
+}
+
+#endif /* NO_CONFIG_WRITE */
+
+#endif /* CONFIG_MESH */
+
+
 /* Helper macros for network block parser */
 
 #ifdef OFFSET
@@ -1630,10 +1821,13 @@
  * functions.
  */
 static const struct parse_data ssid_fields[] = {
-	{ STR_RANGE(ssid, 0, MAX_SSID_LEN) },
+	{ STR_RANGE(ssid, 0, SSID_MAX_LEN) },
 	{ INT_RANGE(scan_ssid, 0, 1) },
 	{ FUNC(bssid) },
+	{ FUNC(bssid_blacklist) },
+	{ FUNC(bssid_whitelist) },
 	{ FUNC_KEY(psk) },
+	{ INT(mem_only_psk) },
 	{ FUNC(proto) },
 	{ FUNC(key_mgmt) },
 	{ INT(bg_scan_period) },
@@ -1656,6 +1850,7 @@
 	{ STRe(subject_match) },
 	{ STRe(altsubject_match) },
 	{ STRe(domain_suffix_match) },
+	{ STRe(domain_match) },
 	{ STRe(ca_cert2) },
 	{ STRe(ca_path2) },
 	{ STRe(client_cert2) },
@@ -1665,6 +1860,7 @@
 	{ STRe(subject_match2) },
 	{ STRe(altsubject_match2) },
 	{ STRe(domain_suffix_match2) },
+	{ STRe(domain_match2) },
 	{ STRe(phase1) },
 	{ STRe(phase2) },
 	{ STRe(pcsc) },
@@ -1682,6 +1878,8 @@
 	{ INTe(engine2) },
 	{ INT(eapol_flags) },
 	{ INTe(sim_num) },
+	{ STRe(openssl_ciphers) },
+	{ INTe(erp) },
 #endif /* IEEE8021X_EAPOL */
 	{ FUNC_KEY(wep_key0) },
 	{ FUNC_KEY(wep_key1) },
@@ -1695,7 +1893,12 @@
 	{ INTe(fragment_size) },
 	{ INTe(ocsp) },
 #endif /* IEEE8021X_EAPOL */
+#ifdef CONFIG_MESH
+	{ INT_RANGE(mode, 0, 5) },
+	{ INT_RANGE(no_auto_peer, 0, 1) },
+#else /* CONFIG_MESH */
 	{ INT_RANGE(mode, 0, 4) },
+#endif /* CONFIG_MESH */
 	{ INT_RANGE(proactive_key_caching, 0, 1) },
 	{ INT_RANGE(disabled, 0, 2) },
 	{ STR(id_str) },
@@ -1705,6 +1908,14 @@
 	{ INT_RANGE(peerkey, 0, 1) },
 	{ INT_RANGE(mixed_cell, 0, 1) },
 	{ INT_RANGE(frequency, 0, 65000) },
+	{ INT_RANGE(fixed_freq, 0, 1) },
+#ifdef CONFIG_MESH
+	{ FUNC(mesh_basic_rates) },
+	{ INT(dot11MeshMaxRetries) },
+	{ INT(dot11MeshRetryTimeout) },
+	{ INT(dot11MeshConfirmTimeout) },
+	{ INT(dot11MeshHoldingTimeout) },
+#endif /* CONFIG_MESH */
 	{ INT(wpa_ptk_rekey) },
 	{ STR(bgscan) },
 	{ INT_RANGE(ignore_broadcast_ssid, 0, 2) },
@@ -1876,6 +2087,7 @@
 	os_free(eap->subject_match);
 	os_free(eap->altsubject_match);
 	os_free(eap->domain_suffix_match);
+	os_free(eap->domain_match);
 	os_free(eap->ca_cert2);
 	os_free(eap->ca_path2);
 	os_free(eap->client_cert2);
@@ -1885,6 +2097,7 @@
 	os_free(eap->subject_match2);
 	os_free(eap->altsubject_match2);
 	os_free(eap->domain_suffix_match2);
+	os_free(eap->domain_match2);
 	os_free(eap->phase1);
 	os_free(eap->phase2);
 	os_free(eap->pcsc);
@@ -1903,6 +2116,7 @@
 	os_free(eap->pac_file);
 	bin_clear_free(eap->new_password, eap->new_password_len);
 	str_clear_free(eap->external_sim_resp);
+	os_free(eap->openssl_ciphers);
 }
 #endif /* IEEE8021X_EAPOL */
 
@@ -1919,7 +2133,6 @@
 	struct psk_list_entry *psk;
 
 	os_free(ssid->ssid);
-	os_memset(ssid->psk, 0, sizeof(ssid->psk));
 	str_clear_free(ssid->passphrase);
 	os_free(ssid->ext_psk);
 #ifdef IEEE8021X_EAPOL
@@ -1930,15 +2143,20 @@
 	os_free(ssid->freq_list);
 	os_free(ssid->bgscan);
 	os_free(ssid->p2p_client_list);
+	os_free(ssid->bssid_blacklist);
+	os_free(ssid->bssid_whitelist);
 #ifdef CONFIG_HT_OVERRIDES
 	os_free(ssid->ht_mcs);
 #endif /* CONFIG_HT_OVERRIDES */
+#ifdef CONFIG_MESH
+	os_free(ssid->mesh_basic_rates);
+#endif /* CONFIG_MESH */
 	while ((psk = dl_list_first(&ssid->psk_list, struct psk_list_entry,
 				    list))) {
 		dl_list_del(&psk->list);
-		os_free(psk);
+		bin_clear_free(psk, sizeof(*psk));
 	}
-	os_free(ssid);
+	bin_clear_free(ssid, sizeof(*ssid));
 }
 
 
@@ -2000,6 +2218,7 @@
 {
 	struct wpa_ssid *ssid, *prev = NULL;
 	struct wpa_cred *cred, *cprev;
+	int i;
 
 	ssid = config->ssid;
 	while (ssid) {
@@ -2018,11 +2237,14 @@
 	wpa_config_flush_blobs(config);
 
 	wpabuf_free(config->wps_vendor_ext_m1);
+	for (i = 0; i < MAX_WPS_VENDOR_EXT; i++)
+		wpabuf_free(config->wps_vendor_ext[i]);
 	os_free(config->ctrl_interface);
 	os_free(config->ctrl_interface_group);
 	os_free(config->opensc_engine_path);
 	os_free(config->pkcs11_engine_path);
 	os_free(config->pkcs11_module_path);
+	os_free(config->openssl_ciphers);
 	os_free(config->pcsc_reader);
 	str_clear_free(config->pcsc_pin);
 	os_free(config->driver_param);
@@ -2045,6 +2267,7 @@
 	os_free(config->sae_groups);
 	wpabuf_free(config->ap_vendor_elements);
 	os_free(config->osu_dir);
+	os_free(config->bgscan);
 	os_free(config->wowlan_triggers);
 	os_free(config);
 }
@@ -2181,6 +2404,12 @@
 	ssid->eap.fragment_size = DEFAULT_FRAGMENT_SIZE;
 	ssid->eap.sim_num = DEFAULT_USER_SELECTED_SIM;
 #endif /* IEEE8021X_EAPOL */
+#ifdef CONFIG_MESH
+	ssid->dot11MeshMaxRetries = DEFAULT_MESH_MAX_RETRIES;
+	ssid->dot11MeshRetryTimeout = DEFAULT_MESH_RETRY_TIMEOUT;
+	ssid->dot11MeshConfirmTimeout = DEFAULT_MESH_CONFIRM_TIMEOUT;
+	ssid->dot11MeshHoldingTimeout = DEFAULT_MESH_HOLDING_TIMEOUT;
+#endif /* CONFIG_MESH */
 #ifdef CONFIG_HT_OVERRIDES
 	ssid->disable_ht = DEFAULT_DISABLE_HT;
 	ssid->disable_ht40 = DEFAULT_DISABLE_HT40;
@@ -2299,6 +2528,9 @@
  */
 char ** wpa_config_get_all(struct wpa_ssid *ssid, int get_keys)
 {
+#ifdef NO_CONFIG_WRITE
+	return NULL;
+#else /* NO_CONFIG_WRITE */
 	const struct parse_data *field;
 	char *key, *value;
 	size_t i;
@@ -2344,6 +2576,7 @@
 		os_free(value++);
 	os_free(props);
 	return NULL;
+#endif /* NO_CONFIG_WRITE */
 }
 
 
@@ -2740,7 +2973,7 @@
 	if (os_strcmp(var, "excluded_ssid") == 0) {
 		struct excluded_ssid *e;
 
-		if (len > MAX_SSID_LEN) {
+		if (len > SSID_MAX_LEN) {
 			wpa_printf(MSG_ERROR, "Line %d: invalid "
 				   "excluded_ssid length %d", line, (int) len);
 			os_free(val);
@@ -2841,12 +3074,18 @@
 
 static char * alloc_int_str(int val)
 {
+	const unsigned int bufsize = 20;
 	char *buf;
+	int res;
 
-	buf = os_malloc(20);
+	buf = os_malloc(bufsize);
 	if (buf == NULL)
 		return NULL;
-	os_snprintf(buf, 20, "%d", val);
+	res = os_snprintf(buf, bufsize, "%d", val);
+	if (os_snprintf_error(bufsize, res)) {
+		os_free(buf);
+		buf = NULL;
+	}
 	return buf;
 }
 
@@ -2917,7 +3156,7 @@
 			ret = os_snprintf(pos, end - pos, "%s%u",
 					  i > 0 ? "\n" : "",
 					  cred->req_conn_capab_proto[i]);
-			if (ret < 0 || ret >= end - pos)
+			if (os_snprintf_error(end - pos, ret))
 				return buf;
 			pos += ret;
 
@@ -2929,7 +3168,7 @@
 							  "%s%d",
 							  j > 0 ? "," : ":",
 							  ports[j]);
-					if (ret < 0 || ret >= end - pos)
+					if (os_snprintf_error(end - pos, ret))
 						return buf;
 					pos += ret;
 				}
@@ -2998,7 +3237,7 @@
 		for (i = 0; i < cred->num_domain; i++) {
 			ret = os_snprintf(pos, end - pos, "%s%s",
 					  i > 0 ? "\n" : "", cred->domain[i]);
-			if (ret < 0 || ret >= end - pos)
+			if (os_snprintf_error(end - pos, ret))
 				return buf;
 			pos += ret;
 		}
@@ -3063,7 +3302,7 @@
 			ret = os_snprintf(pos, end - pos, "%s%s",
 					  i > 0 ? "\n" : "",
 					  wpa_ssid_txt(e->ssid, e->ssid_len));
-			if (ret < 0 || ret >= end - pos)
+			if (os_snprintf_error(end - pos, ret))
 				return buf;
 			pos += ret;
 		}
@@ -3093,7 +3332,7 @@
 					  i > 0 ? "\n" : "",
 					  p->fqdn, p->exact_match, p->priority,
 					  p->country);
-			if (ret < 0 || ret >= end - pos)
+			if (os_snprintf_error(end - pos, ret))
 				return buf;
 			pos += ret;
 		}
@@ -3283,11 +3522,15 @@
 		return NULL;
 	config->eapol_version = DEFAULT_EAPOL_VERSION;
 	config->ap_scan = DEFAULT_AP_SCAN;
+	config->user_mpm = DEFAULT_USER_MPM;
+	config->max_peer_links = DEFAULT_MAX_PEER_LINKS;
+	config->mesh_max_inactivity = DEFAULT_MESH_MAX_INACTIVITY;
 	config->fast_reauth = DEFAULT_FAST_REAUTH;
 	config->p2p_go_intent = DEFAULT_P2P_GO_INTENT;
 	config->p2p_intra_bss = DEFAULT_P2P_INTRA_BSS;
 	config->p2p_go_max_inactivity = DEFAULT_P2P_GO_MAX_INACTIVITY;
 	config->p2p_optimize_listen_chan = DEFAULT_P2P_OPTIMIZE_LISTEN_CHAN;
+	config->p2p_go_ctwindow = DEFAULT_P2P_GO_CTWINDOW;
 	config->bss_max_count = DEFAULT_BSS_MAX_COUNT;
 	config->bss_expiration_age = DEFAULT_BSS_EXPIRATION_AGE;
 	config->bss_expiration_scan_count = DEFAULT_BSS_EXPIRATION_SCAN_COUNT;
@@ -3300,6 +3543,8 @@
 	config->wmm_ac_params[3] = ac_vo;
 	config->p2p_search_delay = DEFAULT_P2P_SEARCH_DELAY;
 	config->rand_addr_lifetime = DEFAULT_RAND_ADDR_LIFETIME;
+	config->key_mgmt_offload = DEFAULT_KEY_MGMT_OFFLOAD;
+	config->cert_in_cb = DEFAULT_CERT_IN_CB;
 
 	if (ctrl_interface)
 		config->ctrl_interface = os_strdup(ctrl_interface);
@@ -3339,6 +3584,8 @@
 	char *name;
 	int (*parser)(const struct global_parse_data *data,
 		      struct wpa_config *config, int line, const char *value);
+	int (*get)(const char *name, struct wpa_config *config, long offset,
+		   char *buf, size_t buflen, int pretty_print);
 	void *param1, *param2, *param3;
 	unsigned int changed_flag;
 };
@@ -3798,22 +4045,81 @@
 #endif /* CONFIG_CTRL_IFACE */
 
 
+static int wpa_config_get_int(const char *name, struct wpa_config *config,
+			      long offset, char *buf, size_t buflen,
+			      int pretty_print)
+{
+	int *val = (int *) (((u8 *) config) + (long) offset);
+
+	if (pretty_print)
+		return os_snprintf(buf, buflen, "%s=%d\n", name, *val);
+	return os_snprintf(buf, buflen, "%d", *val);
+}
+
+
+static int wpa_config_get_str(const char *name, struct wpa_config *config,
+			      long offset, char *buf, size_t buflen,
+			      int pretty_print)
+{
+	char **val = (char **) (((u8 *) config) + (long) offset);
+	int res;
+
+	if (pretty_print)
+		res = os_snprintf(buf, buflen, "%s=%s\n", name,
+				  *val ? *val : "null");
+	else if (!*val)
+		return -1;
+	else
+		res = os_snprintf(buf, buflen, "%s", *val);
+	if (os_snprintf_error(buflen, res))
+		res = -1;
+
+	return res;
+}
+
+
+#ifdef CONFIG_P2P
+static int wpa_config_get_ipv4(const char *name, struct wpa_config *config,
+			       long offset, char *buf, size_t buflen,
+			       int pretty_print)
+{
+	void *val = ((u8 *) config) + (long) offset;
+	int res;
+	char addr[INET_ADDRSTRLEN];
+
+	if (!val || !inet_ntop(AF_INET, val, addr, sizeof(addr)))
+		return -1;
+
+	if (pretty_print)
+		res = os_snprintf(buf, buflen, "%s=%s\n", name, addr);
+	else
+		res = os_snprintf(buf, buflen, "%s", addr);
+
+	if (os_snprintf_error(buflen, res))
+		res = -1;
+
+	return res;
+}
+#endif /* CONFIG_P2P */
+
+
 #ifdef OFFSET
 #undef OFFSET
 #endif /* OFFSET */
 /* OFFSET: Get offset of a variable within the wpa_config structure */
 #define OFFSET(v) ((void *) &((struct wpa_config *) 0)->v)
 
-#define FUNC(f) #f, wpa_config_process_ ## f, OFFSET(f), NULL, NULL
-#define FUNC_NO_VAR(f) #f, wpa_config_process_ ## f, NULL, NULL, NULL
-#define _INT(f) #f, wpa_global_config_parse_int, OFFSET(f)
+#define FUNC(f) #f, wpa_config_process_ ## f, NULL, OFFSET(f), NULL, NULL
+#define FUNC_NO_VAR(f) #f, wpa_config_process_ ## f, NULL, NULL, NULL, NULL
+#define _INT(f) #f, wpa_global_config_parse_int, wpa_config_get_int, OFFSET(f)
 #define INT(f) _INT(f), NULL, NULL
 #define INT_RANGE(f, min, max) _INT(f), (void *) min, (void *) max
-#define _STR(f) #f, wpa_global_config_parse_str, OFFSET(f)
+#define _STR(f) #f, wpa_global_config_parse_str, wpa_config_get_str, OFFSET(f)
 #define STR(f) _STR(f), NULL, NULL
 #define STR_RANGE(f, min, max) _STR(f), (void *) min, (void *) max
-#define BIN(f) #f, wpa_global_config_parse_bin, OFFSET(f), NULL, NULL
-#define IPV4(f) #f, wpa_global_config_parse_ipv4, OFFSET(f), NULL, NULL
+#define BIN(f) #f, wpa_global_config_parse_bin, NULL, OFFSET(f), NULL, NULL
+#define IPV4(f) #f, wpa_global_config_parse_ipv4, wpa_config_get_ipv4,  \
+	OFFSET(f), NULL, NULL
 
 static const struct global_parse_data global_fields[] = {
 #ifdef CONFIG_CTRL_IFACE
@@ -3828,11 +4134,17 @@
 #endif /* CONFIG_MACSEC */
 	{ INT(ap_scan), 0 },
 	{ FUNC(bgscan), 0 },
+#ifdef CONFIG_MESH
+	{ INT(user_mpm), 0 },
+	{ INT_RANGE(max_peer_links, 0, 255), 0 },
+	{ INT(mesh_max_inactivity), 0 },
+#endif /* CONFIG_MESH */
 	{ INT(disable_scan_offload), 0 },
 	{ INT(fast_reauth), 0 },
 	{ STR(opensc_engine_path), 0 },
 	{ STR(pkcs11_engine_path), 0 },
 	{ STR(pkcs11_module_path), 0 },
+	{ STR(openssl_ciphers), 0 },
 	{ STR(pcsc_reader), 0 },
 	{ STR(pcsc_pin), 0 },
 	{ INT(external_sim), 0 },
@@ -3846,7 +4158,8 @@
 	{ FUNC_NO_VAR(load_dynamic_eap), 0 },
 #ifdef CONFIG_WPS
 	{ FUNC(uuid), CFG_CHANGED_UUID },
-	{ STR_RANGE(device_name, 0, 32), CFG_CHANGED_DEVICE_NAME },
+	{ STR_RANGE(device_name, 0, WPS_DEV_NAME_MAX_LEN),
+	  CFG_CHANGED_DEVICE_NAME },
 	{ STR_RANGE(manufacturer, 0, 64), CFG_CHANGED_WPS_STRING },
 	{ STR_RANGE(model_name, 0, 32), CFG_CHANGED_WPS_STRING },
 	{ STR_RANGE(model_number, 0, 32), CFG_CHANGED_WPS_STRING },
@@ -3877,12 +4190,14 @@
 	{ INT(p2p_go_ht40), 0 },
 	{ INT(p2p_go_vht), 0 },
 	{ INT(p2p_disabled), 0 },
+	{ INT_RANGE(p2p_go_ctwindow, 0, 127), 0 },
 	{ INT(p2p_no_group_iface), 0 },
 	{ INT_RANGE(p2p_ignore_shared_freq, 0, 1), 0 },
 	{ IPV4(ip_addr_go), 0 },
 	{ IPV4(ip_addr_mask), 0 },
 	{ IPV4(ip_addr_start), 0 },
 	{ IPV4(ip_addr_end), 0 },
+	{ INT_RANGE(p2p_cli_probe, 0, 1), 0 },
 #endif /* CONFIG_P2P */
 	{ FUNC(country), CFG_CHANGED_COUNTRY },
 	{ INT(bss_max_count), 0 },
@@ -3925,6 +4240,10 @@
 	{ INT(mac_addr), 0 },
 	{ INT(rand_addr_lifetime), 0 },
 	{ INT(preassoc_mac_addr), 0 },
+	{ INT(key_mgmt_offload), 0},
+	{ INT(passive_scan), 0 },
+	{ INT(reassoc_same_bss_optim), 0 },
+	{ INT(wps_priority), 0},
 };
 
 #undef FUNC
@@ -3939,6 +4258,50 @@
 #define NUM_GLOBAL_FIELDS ARRAY_SIZE(global_fields)
 
 
+int wpa_config_dump_values(struct wpa_config *config, char *buf, size_t buflen)
+{
+	int result = 0;
+	size_t i;
+
+	for (i = 0; i < NUM_GLOBAL_FIELDS; i++) {
+		const struct global_parse_data *field = &global_fields[i];
+		int tmp;
+
+		if (!field->get)
+			continue;
+
+		tmp = field->get(field->name, config, (long) field->param1,
+				 buf, buflen, 1);
+		if (tmp < 0)
+			return -1;
+		buf += tmp;
+		buflen -= tmp;
+		result += tmp;
+	}
+	return result;
+}
+
+
+int wpa_config_get_value(const char *name, struct wpa_config *config,
+			 char *buf, size_t buflen)
+{
+	size_t i;
+
+	for (i = 0; i < NUM_GLOBAL_FIELDS; i++) {
+		const struct global_parse_data *field = &global_fields[i];
+
+		if (os_strcmp(name, field->name) != 0)
+			continue;
+		if (!field->get)
+			break;
+		return field->get(name, config, (long) field->param1,
+				  buf, buflen, 0);
+	}
+
+	return -1;
+}
+
+
 int wpa_config_process_global(struct wpa_config *config, char *pos, int line)
 {
 	size_t i;
diff --git a/wpa_supplicant/config.h b/wpa_supplicant/config.h
index 3fd4192..d8ca054 100644
--- a/wpa_supplicant/config.h
+++ b/wpa_supplicant/config.h
@@ -15,6 +15,9 @@
 #else /* CONFIG_NO_SCAN_PROCESSING */
 #define DEFAULT_AP_SCAN 1
 #endif /* CONFIG_NO_SCAN_PROCESSING */
+#define DEFAULT_USER_MPM 1
+#define DEFAULT_MAX_PEER_LINKS 99
+#define DEFAULT_MESH_MAX_INACTIVITY 300
 #define DEFAULT_FAST_REAUTH 1
 #define DEFAULT_P2P_GO_INTENT 7
 #define DEFAULT_P2P_INTRA_BSS 1
@@ -28,9 +31,13 @@
 #define DEFAULT_SCAN_CUR_FREQ 0
 #define DEFAULT_P2P_SEARCH_DELAY 500
 #define DEFAULT_RAND_ADDR_LIFETIME 60
+#define DEFAULT_KEY_MGMT_OFFLOAD 1
+#define DEFAULT_CERT_IN_CB 1
+#define DEFAULT_P2P_GO_CTWINDOW 0
 
 #include "config_ssid.h"
 #include "wps/wps.h"
+#include "common/ieee802_11_defs.h"
 #include "common/ieee802_11_common.h"
 
 
@@ -163,7 +170,7 @@
 	 * If set, this FQDN is used as a suffix match requirement for the AAA
 	 * server certificate in SubjectAltName dNSName element(s). If a
 	 * matching dNSName is found, this constraint is met. If no dNSName
-	 * values are present, this constraint is matched against SubjetName CN
+	 * values are present, this constraint is matched against SubjectName CN
 	 * using same suffix match comparison. Suffix match here means that the
 	 * host/domain name is compared one label at a time starting from the
 	 * top-level domain and all the labels in @domain_suffix_match shall be
@@ -235,7 +242,7 @@
 	char *phase2;
 
 	struct excluded_ssid {
-		u8 ssid[MAX_SSID_LEN];
+		u8 ssid[SSID_MAX_LEN];
 		size_t ssid_len;
 	} *excluded_ssid;
 	size_t num_excluded_ssid;
@@ -517,6 +524,15 @@
 	char *pkcs11_module_path;
 
 	/**
+	 * openssl_ciphers - OpenSSL cipher string
+	 *
+	 * This is an OpenSSL specific configuration option for configuring the
+	 * default ciphers. If not set, "DEFAULT:!EXP:!LOW" is used as the
+	 * default.
+	 */
+	char *openssl_ciphers;
+
+	/**
 	 * pcsc_reader - PC/SC reader name prefix
 	 *
 	 * If not %NULL, PC/SC reader with a name that matches this prefix is
@@ -928,6 +944,14 @@
 	int p2p_go_vht;
 
 	/**
+	 * p2p_go_ctwindow - CTWindow to use when operating as GO
+	 *
+	 * By default: 0 (no CTWindow). Values 0-127 can be used to indicate
+	 * the length of the CTWindow in TUs.
+	 */
+	int p2p_go_ctwindow;
+
+	/**
 	 * p2p_disabled - Whether P2P operations are disabled for this interface
 	 */
 	int p2p_disabled;
@@ -944,6 +968,18 @@
 	int p2p_no_group_iface;
 
 	/**
+	 * p2p_cli_probe - Enable/disable P2P CLI probe request handling
+	 *
+	 * If this parameter is set to 1, a connected P2P Client will receive
+	 * and handle Probe Request frames. Setting this parameter to 0
+	 * disables this option. Default value: 0.
+	 *
+	 * Note: Setting this property at run time takes effect on the following
+	 * interface state transition to/from the WPA_COMPLETED state.
+	 */
+	int p2p_cli_probe;
+
+	/**
 	 * okc - Whether to enable opportunistic key caching by default
 	 *
 	 * By default, OKC is disabled unless enabled by the per-network
@@ -1079,6 +1115,75 @@
 	 * 2 = like 1, but maintain OUI (with local admin bit set)
 	 */
 	int preassoc_mac_addr;
+
+	/**
+	 * key_mgmt_offload - Use key management offload
+	 *
+	 * Key management offload should be used if the device supports it.
+	 * Key management offload is the capability of a device operating as
+	 * a station to do the exchange necessary to establish temporal keys
+	 * during initial RSN connection, after roaming, or during a PTK
+	 * rekeying operation.
+	 */
+	int key_mgmt_offload;
+
+	/**
+	 * user_mpm - MPM residency
+	 *
+	 * 0: MPM lives in driver.
+	 * 1: wpa_supplicant handles peering and station allocation.
+	 *
+	 * If AMPE or SAE is enabled, the MPM is always in userspace.
+	 */
+	int user_mpm;
+
+	/**
+	 * max_peer_links - Maximum number of peer links
+	 *
+	 * Maximum number of mesh peering currently maintained by the STA.
+	 */
+	int max_peer_links;
+
+	/**
+	 * cert_in_cb - Whether to include a peer certificate dump in events
+	 *
+	 * This controls whether peer certificates for authentication server and
+	 * its certificate chain are included in EAP peer certificate events.
+	 */
+	int cert_in_cb;
+
+	/**
+	 * mesh_max_inactivity - Timeout in seconds to detect STA inactivity
+	 *
+	 * This timeout value is used in mesh STA to clean up inactive stations.
+	 * By default: 300 seconds.
+	 */
+	int mesh_max_inactivity;
+
+	/**
+	 * passive_scan - Whether to force passive scan for network connection
+	 *
+	 * This parameter can be used to force only passive scanning to be used
+	 * for network connection cases. It should be noted that this will slow
+	 * down scan operations and reduce likelihood of finding the AP. In
+	 * addition, some use cases will override this due to functional
+	 * requirements, e.g., for finding an AP that uses hidden SSID
+	 * (scan_ssid=1) or P2P device discovery.
+	 */
+	int passive_scan;
+
+	/**
+	 * reassoc_same_bss_optim - Whether to optimize reassoc-to-same-BSS
+	 */
+	int reassoc_same_bss_optim;
+
+	/**
+	 * wps_priority - Priority for the networks added through WPS
+	 *
+	 * This priority value will be set to each network profile that is added
+	 * by executing the WPS protocol.
+	 */
+	int wps_priority;
 };
 
 
@@ -1097,6 +1202,11 @@
 		   int line);
 int wpa_config_set_quoted(struct wpa_ssid *ssid, const char *var,
 			  const char *value);
+int wpa_config_dump_values(struct wpa_config *config, char *buf,
+			   size_t buflen);
+int wpa_config_get_value(const char *name, struct wpa_config *config,
+			 char *buf, size_t buflen);
+
 char ** wpa_config_get_all(struct wpa_ssid *ssid, int get_keys);
 char * wpa_config_get(struct wpa_ssid *ssid, const char *var);
 char * wpa_config_get_no_key(struct wpa_ssid *ssid, const char *var);
diff --git a/wpa_supplicant/config_file.c b/wpa_supplicant/config_file.c
index ef909a1..aeea70c 100644
--- a/wpa_supplicant/config_file.c
+++ b/wpa_supplicant/config_file.c
@@ -11,6 +11,9 @@
  */
 
 #include "includes.h"
+#ifdef ANDROID
+#include <sys/stat.h>
+#endif /* ANDROID */
 
 #include "common.h"
 #include "config.h"
@@ -20,9 +23,6 @@
 #include "eap_peer/eap_methods.h"
 #include "eap_peer/eap.h"
 
-#ifdef ANDROID
-#include <sys/stat.h>
-#endif
 
 static int newline_terminated(const char *buf, size_t buflen)
 {
@@ -146,6 +146,15 @@
 		ssid->group_cipher &= ~WPA_CIPHER_CCMP;
 	}
 
+	if (ssid->mode == WPAS_MODE_MESH &&
+	    (ssid->key_mgmt != WPA_KEY_MGMT_NONE &&
+	    ssid->key_mgmt != WPA_KEY_MGMT_SAE)) {
+		wpa_printf(MSG_ERROR,
+			   "Line %d: key_mgmt for mesh network should be open or SAE",
+			   line);
+		errors++;
+	}
+
 	return errors;
 }
 
@@ -492,7 +501,12 @@
 
 static void write_psk(FILE *f, struct wpa_ssid *ssid)
 {
-	char *value = wpa_config_get(ssid, "psk");
+	char *value;
+
+	if (ssid->mem_only_psk)
+		return;
+
+	value = wpa_config_get(ssid, "psk");
 	if (value == NULL)
 		return;
 	fprintf(f, "\tpsk=%s\n", value);
@@ -602,7 +616,7 @@
 	int res;
 
 	res = os_snprintf(field, sizeof(field), "wep_key%d", idx);
-	if (res < 0 || (size_t) res >= sizeof(field))
+	if (os_snprintf_error(sizeof(field), res))
 		return;
 	value = wpa_config_get(ssid, field);
 	if (value) {
@@ -661,7 +675,10 @@
 	STR(ssid);
 	INT(scan_ssid);
 	write_bssid(f, ssid);
+	write_str(f, "bssid_blacklist", ssid);
+	write_str(f, "bssid_whitelist", ssid);
 	write_psk(f, ssid);
+	INT(mem_only_psk);
 	write_proto(f, ssid);
 	write_key_mgmt(f, ssid);
 	INT_DEF(bg_scan_period, DEFAULT_BG_SCAN_PERIOD);
@@ -685,6 +702,7 @@
 	STR(subject_match);
 	STR(altsubject_match);
 	STR(domain_suffix_match);
+	STR(domain_match);
 	STR(ca_cert2);
 	STR(ca_path2);
 	STR(client_cert2);
@@ -694,6 +712,7 @@
 	STR(subject_match2);
 	STR(altsubject_match2);
 	STR(domain_suffix_match2);
+	STR(domain_match2);
 	STR(phase1);
 	STR(phase2);
 	STR(pcsc);
@@ -710,6 +729,8 @@
 	INTe(engine);
 	INTe(engine2);
 	INT_DEF(eapol_flags, DEFAULT_EAPOL_FLAGS);
+	STR(openssl_ciphers);
+	INTe(erp);
 #endif /* IEEE8021X_EAPOL */
 	for (i = 0; i < 4; i++)
 		write_wep_key(f, i, ssid);
@@ -723,10 +744,13 @@
 	INT_DEFe(sim_num, DEFAULT_USER_SELECTED_SIM);
 #endif /* IEEE8021X_EAPOL */
 	INT(mode);
+	INT(no_auto_peer);
 	INT(frequency);
+	INT(fixed_freq);
 	write_int(f, "proactive_key_caching", ssid->proactive_key_caching, -1);
 	INT(disabled);
 	INT(peerkey);
+	INT(mixed_cell);
 #ifdef CONFIG_IEEE80211W
 	write_int(f, "ieee80211w", ssid->ieee80211w,
 		  MGMT_FRAME_PROTECTION_DEFAULT);
@@ -737,6 +761,7 @@
 	write_p2p_client_list(f, ssid);
 	write_psk_list(f, ssid);
 #endif /* CONFIG_P2P */
+	INT(ap_max_inactivity);
 	INT(dtim_period);
 	INT(beacon_int);
 #ifdef CONFIG_MACSEC
@@ -746,6 +771,47 @@
 	INT(update_identifier);
 #endif /* CONFIG_HS20 */
 	write_int(f, "mac_addr", ssid->mac_addr, -1);
+#ifdef CONFIG_MESH
+	STR(mesh_basic_rates);
+	INT_DEF(dot11MeshMaxRetries, DEFAULT_MESH_MAX_RETRIES);
+	INT_DEF(dot11MeshRetryTimeout, DEFAULT_MESH_RETRY_TIMEOUT);
+	INT_DEF(dot11MeshConfirmTimeout, DEFAULT_MESH_CONFIRM_TIMEOUT);
+	INT_DEF(dot11MeshHoldingTimeout, DEFAULT_MESH_HOLDING_TIMEOUT);
+#endif /* CONFIG_MESH */
+	INT(wpa_ptk_rekey);
+	INT(ignore_broadcast_ssid);
+#ifdef CONFIG_HT_OVERRIDES
+	INT_DEF(disable_ht, DEFAULT_DISABLE_HT);
+	INT_DEF(disable_ht40, DEFAULT_DISABLE_HT40);
+	INT_DEF(disable_sgi, DEFAULT_DISABLE_SGI);
+	INT_DEF(disable_ldpc, DEFAULT_DISABLE_LDPC);
+	INT(ht40_intolerant);
+	INT_DEF(disable_max_amsdu, DEFAULT_DISABLE_MAX_AMSDU);
+	INT_DEF(ampdu_factor, DEFAULT_AMPDU_FACTOR);
+	INT_DEF(ampdu_density, DEFAULT_AMPDU_DENSITY);
+	STR(ht_mcs);
+#endif /* CONFIG_HT_OVERRIDES */
+#ifdef CONFIG_VHT_OVERRIDES
+	INT(disable_vht);
+	INT(vht_capa);
+	INT(vht_capa_mask);
+	INT_DEF(vht_rx_mcs_nss_1, -1);
+	INT_DEF(vht_rx_mcs_nss_2, -1);
+	INT_DEF(vht_rx_mcs_nss_3, -1);
+	INT_DEF(vht_rx_mcs_nss_4, -1);
+	INT_DEF(vht_rx_mcs_nss_5, -1);
+	INT_DEF(vht_rx_mcs_nss_6, -1);
+	INT_DEF(vht_rx_mcs_nss_7, -1);
+	INT_DEF(vht_rx_mcs_nss_8, -1);
+	INT_DEF(vht_tx_mcs_nss_1, -1);
+	INT_DEF(vht_tx_mcs_nss_2, -1);
+	INT_DEF(vht_tx_mcs_nss_3, -1);
+	INT_DEF(vht_tx_mcs_nss_4, -1);
+	INT_DEF(vht_tx_mcs_nss_5, -1);
+	INT_DEF(vht_tx_mcs_nss_6, -1);
+	INT_DEF(vht_tx_mcs_nss_7, -1);
+	INT_DEF(vht_tx_mcs_nss_8, -1);
+#endif /* CONFIG_VHT_OVERRIDES */
 
 #undef STR
 #undef INT
@@ -941,6 +1007,8 @@
 	if (config->pkcs11_module_path)
 		fprintf(f, "pkcs11_module_path=%s\n",
 			config->pkcs11_module_path);
+	if (config->openssl_ciphers)
+		fprintf(f, "openssl_ciphers=%s\n", config->openssl_ciphers);
 	if (config->pcsc_reader)
 		fprintf(f, "pcsc_reader=%s\n", config->pcsc_reader);
 	if (config->pcsc_pin)
@@ -948,13 +1016,13 @@
 	if (config->driver_param)
 		fprintf(f, "driver_param=%s\n", config->driver_param);
 	if (config->dot11RSNAConfigPMKLifetime)
-		fprintf(f, "dot11RSNAConfigPMKLifetime=%d\n",
+		fprintf(f, "dot11RSNAConfigPMKLifetime=%u\n",
 			config->dot11RSNAConfigPMKLifetime);
 	if (config->dot11RSNAConfigPMKReauthThreshold)
-		fprintf(f, "dot11RSNAConfigPMKReauthThreshold=%d\n",
+		fprintf(f, "dot11RSNAConfigPMKReauthThreshold=%u\n",
 			config->dot11RSNAConfigPMKReauthThreshold);
 	if (config->dot11RSNAConfigSATimeout)
-		fprintf(f, "dot11RSNAConfigSATimeout=%d\n",
+		fprintf(f, "dot11RSNAConfigSATimeout=%u\n",
 			config->dot11RSNAConfigSATimeout);
 	if (config->update_config)
 		fprintf(f, "update_config=%d\n", config->update_config);
@@ -1002,27 +1070,27 @@
 #endif /* CONFIG_WPS */
 #ifdef CONFIG_P2P
 	if (config->p2p_listen_reg_class)
-		fprintf(f, "p2p_listen_reg_class=%u\n",
+		fprintf(f, "p2p_listen_reg_class=%d\n",
 			config->p2p_listen_reg_class);
 	if (config->p2p_listen_channel)
-		fprintf(f, "p2p_listen_channel=%u\n",
+		fprintf(f, "p2p_listen_channel=%d\n",
 			config->p2p_listen_channel);
 	if (config->p2p_oper_reg_class)
-		fprintf(f, "p2p_oper_reg_class=%u\n",
+		fprintf(f, "p2p_oper_reg_class=%d\n",
 			config->p2p_oper_reg_class);
 	if (config->p2p_oper_channel)
-		fprintf(f, "p2p_oper_channel=%u\n", config->p2p_oper_channel);
+		fprintf(f, "p2p_oper_channel=%d\n", config->p2p_oper_channel);
 	if (config->p2p_go_intent != DEFAULT_P2P_GO_INTENT)
-		fprintf(f, "p2p_go_intent=%u\n", config->p2p_go_intent);
+		fprintf(f, "p2p_go_intent=%d\n", config->p2p_go_intent);
 	if (config->p2p_ssid_postfix)
 		fprintf(f, "p2p_ssid_postfix=%s\n", config->p2p_ssid_postfix);
 	if (config->persistent_reconnect)
-		fprintf(f, "persistent_reconnect=%u\n",
+		fprintf(f, "persistent_reconnect=%d\n",
 			config->persistent_reconnect);
 	if (config->p2p_intra_bss != DEFAULT_P2P_INTRA_BSS)
-		fprintf(f, "p2p_intra_bss=%u\n", config->p2p_intra_bss);
+		fprintf(f, "p2p_intra_bss=%d\n", config->p2p_intra_bss);
 	if (config->p2p_group_idle)
-		fprintf(f, "p2p_group_idle=%u\n", config->p2p_group_idle);
+		fprintf(f, "p2p_group_idle=%d\n", config->p2p_group_idle);
 	if (config->p2p_passphrase_len)
 		fprintf(f, "p2p_passphrase_len=%u\n",
 			config->p2p_passphrase_len);
@@ -1050,17 +1118,21 @@
 		fprintf(f, "p2p_optimize_listen_chan=%d\n",
 			config->p2p_optimize_listen_chan);
 	if (config->p2p_go_ht40)
-		fprintf(f, "p2p_go_ht40=%u\n", config->p2p_go_ht40);
+		fprintf(f, "p2p_go_ht40=%d\n", config->p2p_go_ht40);
 	if (config->p2p_go_vht)
-		fprintf(f, "p2p_go_vht=%u\n", config->p2p_go_vht);
+		fprintf(f, "p2p_go_vht=%d\n", config->p2p_go_vht);
+	if (config->p2p_go_ctwindow != DEFAULT_P2P_GO_CTWINDOW)
+		fprintf(f, "p2p_go_ctwindow=%d\n", config->p2p_go_ctwindow);
 	if (config->p2p_disabled)
-		fprintf(f, "p2p_disabled=%u\n", config->p2p_disabled);
+		fprintf(f, "p2p_disabled=%d\n", config->p2p_disabled);
 	if (config->p2p_no_group_iface)
-		fprintf(f, "p2p_no_group_iface=%u\n",
+		fprintf(f, "p2p_no_group_iface=%d\n",
 			config->p2p_no_group_iface);
 	if (config->p2p_ignore_shared_freq)
-		fprintf(f, "p2p_ignore_shared_freq=%u\n",
+		fprintf(f, "p2p_ignore_shared_freq=%d\n",
 			config->p2p_ignore_shared_freq);
+	if (config->p2p_cli_probe)
+		fprintf(f, "p2p_cli_probe=%d\n", config->p2p_cli_probe);
 #endif /* CONFIG_P2P */
 	if (config->country[0] && config->country[1]) {
 		fprintf(f, "country=%c%c\n",
@@ -1080,14 +1152,14 @@
 	if (config->max_num_sta != DEFAULT_MAX_NUM_STA)
 		fprintf(f, "max_num_sta=%u\n", config->max_num_sta);
 	if (config->disassoc_low_ack)
-		fprintf(f, "disassoc_low_ack=%u\n", config->disassoc_low_ack);
+		fprintf(f, "disassoc_low_ack=%d\n", config->disassoc_low_ack);
 #ifdef CONFIG_HS20
 	if (config->hs20)
 		fprintf(f, "hs20=1\n");
 #endif /* CONFIG_HS20 */
 #ifdef CONFIG_INTERWORKING
 	if (config->interworking)
-		fprintf(f, "interworking=%u\n", config->interworking);
+		fprintf(f, "interworking=%d\n", config->interworking);
 	if (!is_zero_ether_addr(config->hessid))
 		fprintf(f, "hessid=" MACSTR "\n", MAC2STR(config->hessid));
 	if (config->access_network_type != DEFAULT_ACCESS_NETWORK_TYPE)
@@ -1095,7 +1167,7 @@
 			config->access_network_type);
 #endif /* CONFIG_INTERWORKING */
 	if (config->pbc_in_m1)
-		fprintf(f, "pbc_in_m1=%u\n", config->pbc_in_m1);
+		fprintf(f, "pbc_in_m1=%d\n", config->pbc_in_m1);
 	if (config->wps_nfc_pw_from_config) {
 		if (config->wps_nfc_dev_pw_id)
 			fprintf(f, "wps_nfc_dev_pw_id=%d\n",
@@ -1154,7 +1226,7 @@
 		int i;
 		fprintf(f, "freq_list=");
 		for (i = 0; config->freq_list[i]; i++) {
-			fprintf(f, "%s%u", i > 0 ? " " : "",
+			fprintf(f, "%s%d", i > 0 ? " " : "",
 				config->freq_list[i]);
 		}
 		fprintf(f, "\n");
@@ -1193,6 +1265,32 @@
 
 	if (config->preassoc_mac_addr)
 		fprintf(f, "preassoc_mac_addr=%d\n", config->preassoc_mac_addr);
+
+	if (config->key_mgmt_offload != DEFAULT_KEY_MGMT_OFFLOAD)
+		fprintf(f, "key_mgmt_offload=%d\n", config->key_mgmt_offload);
+
+	if (config->user_mpm != DEFAULT_USER_MPM)
+		fprintf(f, "user_mpm=%d\n", config->user_mpm);
+
+	if (config->max_peer_links != DEFAULT_MAX_PEER_LINKS)
+		fprintf(f, "max_peer_links=%d\n", config->max_peer_links);
+
+	if (config->cert_in_cb != DEFAULT_CERT_IN_CB)
+		fprintf(f, "cert_in_cb=%d\n", config->cert_in_cb);
+
+	if (config->mesh_max_inactivity != DEFAULT_MESH_MAX_INACTIVITY)
+		fprintf(f, "mesh_max_inactivity=%d\n",
+			config->mesh_max_inactivity);
+
+	if (config->passive_scan)
+		fprintf(f, "passive_scan=%d\n", config->passive_scan);
+
+	if (config->reassoc_same_bss_optim)
+		fprintf(f, "reassoc_same_bss_optim=%d\n",
+			config->reassoc_same_bss_optim);
+
+	if (config->wps_priority)
+		fprintf(f, "wps_priority=%d\n", config->wps_priority);
 }
 
 #endif /* CONFIG_NO_CONFIG_WRITE */
@@ -1208,21 +1306,21 @@
 	struct wpa_config_blob *blob;
 #endif /* CONFIG_NO_CONFIG_BLOBS */
 	int ret = 0;
-	int tmp_len = os_strlen(name) + 5;       /* allow space for .tmp suffix */
+	const char *orig_name = name;
+	int tmp_len = os_strlen(name) + 5; /* allow space for .tmp suffix */
 	char *tmp_name = os_malloc(tmp_len);
 
-	if (tmp_name == NULL)
-		tmp_name = (char *)name;
-	else
+	if (tmp_name) {
 		os_snprintf(tmp_name, tmp_len, "%s.tmp", name);
+		name = tmp_name;
+	}
 
-	wpa_printf(MSG_DEBUG, "Writing configuration file '%s'", tmp_name);
+	wpa_printf(MSG_DEBUG, "Writing configuration file '%s'", name);
 
-	f = fopen(tmp_name, "w");
+	f = fopen(name, "w");
 	if (f == NULL) {
-		wpa_printf(MSG_DEBUG, "Failed to open '%s' for writing", tmp_name);
-		if (tmp_name != name)
-			os_free(tmp_name);
+		wpa_printf(MSG_DEBUG, "Failed to open '%s' for writing", name);
+		os_free(tmp_name);
 		return -1;
 	}
 
@@ -1255,21 +1353,25 @@
 	}
 #endif /* CONFIG_NO_CONFIG_BLOBS */
 
+	os_fsync(f);
+
 	fclose(f);
 
-	if (tmp_name != name) {
+	if (tmp_name) {
 		int chmod_ret = 0;
+
 #ifdef ANDROID
-		chmod_ret = chmod(tmp_name, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
-#endif
-		if (chmod_ret != 0 || rename(tmp_name, name) != 0)
+		chmod_ret = chmod(tmp_name,
+				  S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
+#endif /* ANDROID */
+		if (chmod_ret != 0 || rename(tmp_name, orig_name) != 0)
 			ret = -1;
 
 		os_free(tmp_name);
 	}
 
 	wpa_printf(MSG_DEBUG, "Configuration file '%s' written %ssuccessfully",
-		   name, ret ? "un" : "");
+		   orig_name, ret ? "un" : "");
 	return ret;
 #else /* CONFIG_NO_CONFIG_WRITE */
 	return -1;
diff --git a/wpa_supplicant/config_ssid.h b/wpa_supplicant/config_ssid.h
index f50b2d4..1c63e51 100644
--- a/wpa_supplicant/config_ssid.h
+++ b/wpa_supplicant/config_ssid.h
@@ -13,8 +13,6 @@
 #include "utils/list.h"
 #include "eap_peer/eap_config.h"
 
-#define MAX_SSID_LEN 32
-
 
 #define DEFAULT_EAP_WORKAROUND ((unsigned int) -1)
 #define DEFAULT_EAPOL_FLAGS (EAPOL_FLAG_REQUIRE_KEY_UNICAST | \
@@ -22,11 +20,14 @@
 #define DEFAULT_PROTO (WPA_PROTO_WPA | WPA_PROTO_RSN)
 #define DEFAULT_KEY_MGMT (WPA_KEY_MGMT_PSK | WPA_KEY_MGMT_IEEE8021X)
 #define DEFAULT_PAIRWISE (WPA_CIPHER_CCMP | WPA_CIPHER_TKIP)
-#define DEFAULT_GROUP (WPA_CIPHER_CCMP | WPA_CIPHER_TKIP | \
-		       WPA_CIPHER_WEP104 | WPA_CIPHER_WEP40)
+#define DEFAULT_GROUP (WPA_CIPHER_CCMP | WPA_CIPHER_TKIP)
 #define DEFAULT_FRAGMENT_SIZE 1398
 
 #define DEFAULT_BG_SCAN_PERIOD -1
+#define DEFAULT_MESH_MAX_RETRIES 2
+#define DEFAULT_MESH_RETRY_TIMEOUT 40
+#define DEFAULT_MESH_CONFIRM_TIMEOUT 40
+#define DEFAULT_MESH_HOLDING_TIMEOUT 40
 #define DEFAULT_DISABLE_HT 0
 #define DEFAULT_DISABLE_HT40 0
 #define DEFAULT_DISABLE_SGI 0
@@ -128,6 +129,18 @@
 	u8 bssid[ETH_ALEN];
 
 	/**
+	 * bssid_blacklist - List of inacceptable BSSIDs
+	 */
+	u8 *bssid_blacklist;
+	size_t num_bssid_blacklist;
+
+	/**
+	 * bssid_blacklist - List of acceptable BSSIDs
+	 */
+	u8 *bssid_whitelist;
+	size_t num_bssid_whitelist;
+
+	/**
 	 * bssid_set - Whether BSSID is configured for this network
 	 */
 	int bssid_set;
@@ -165,6 +178,14 @@
 	char *ext_psk;
 
 	/**
+	 * mem_only_psk - Whether to keep PSK/passphrase only in memory
+	 *
+	 * 0 = allow psk/passphrase to be stored to the configuration file
+	 * 1 = do not store psk/passphrase to the configuration file
+	 */
+	int mem_only_psk;
+
+	/**
 	 * pairwise_cipher - Bitfield of allowed pairwise ciphers, WPA_CIPHER_*
 	 */
 	int pairwise_cipher;
@@ -317,6 +338,8 @@
 	 * 4 = P2P Group Formation (used internally; not in configuration
 	 * files)
 	 *
+	 * 5 = Mesh
+	 *
 	 * Note: IBSS can only be used with key_mgmt NONE (plaintext and static
 	 * WEP) and WPA-PSK (with proto=RSN). In addition, key_mgmt=WPA-NONE
 	 * (fixed group key TKIP/CCMP) is available for backwards compatibility,
@@ -331,6 +354,7 @@
 		WPAS_MODE_AP = 2,
 		WPAS_MODE_P2P_GO = 3,
 		WPAS_MODE_P2P_GROUP_FORMATION = 4,
+		WPAS_MODE_MESH = 5,
 	} mode;
 
 	/**
@@ -400,6 +424,25 @@
 	 */
 	int frequency;
 
+	/**
+	 * fixed_freq - Use fixed frequency for IBSS
+	 */
+	int fixed_freq;
+
+	/**
+	 * mesh_basic_rates - BSS Basic rate set for mesh network
+	 *
+	 */
+	int *mesh_basic_rates;
+
+	/**
+	 * Mesh network plink parameters
+	 */
+	int dot11MeshMaxRetries;
+	int dot11MeshRetryTimeout; /* msec */
+	int dot11MeshConfirmTimeout; /* msec */
+	int dot11MeshHoldingTimeout; /* msec */
+
 	int ht40;
 
 	int vht;
@@ -666,6 +709,14 @@
 	 * followed).
 	 */
 	int mac_addr;
+
+	/**
+	 * no_auto_peer - Do not automatically peer with compatible mesh peers
+	 *
+	 * When unset, the reception of a beacon from a another mesh peer in
+	 * this MBSS will trigger a peering attempt.
+	 */
+	int no_auto_peer;
 };
 
 #endif /* CONFIG_SSID_H */
diff --git a/wpa_supplicant/ctrl_iface.c b/wpa_supplicant/ctrl_iface.c
index a4c26e4..d0d70e9 100644
--- a/wpa_supplicant/ctrl_iface.c
+++ b/wpa_supplicant/ctrl_iface.c
@@ -1,12 +1,16 @@
 /*
  * WPA Supplicant / Control interface (shared code for all backends)
- * Copyright (c) 2004-2014, 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.
  */
 
 #include "utils/includes.h"
+#ifdef CONFIG_TESTING_OPTIONS
+#include <net/ethernet.h>
+#include <netinet/ip.h>
+#endif /* CONFIG_TESTING_OPTIONS */
 
 #include "utils/common.h"
 #include "utils/eloop.h"
@@ -15,6 +19,8 @@
 #include "common/ieee802_11_defs.h"
 #include "common/ieee802_11_common.h"
 #include "common/wpa_ctrl.h"
+#include "crypto/tls.h"
+#include "ap/hostapd.h"
 #include "eap_peer/eap.h"
 #include "eapol_supp/eapol_supp_sm.h"
 #include "rsn_supp/wpa.h"
@@ -42,6 +48,7 @@
 #include "wnm_sta.h"
 #include "offchannel.h"
 #include "drivers/driver.h"
+#include "mesh.h"
 
 static int wpa_supplicant_global_iface_list(struct wpa_global *global,
 					    char *buf, int len);
@@ -146,7 +153,8 @@
 			}
 			ssid = ns;
 
-			if ((end - pos) & 0x01 || end - pos > 2 * 32 ||
+			if ((end - pos) & 0x01 ||
+			    end - pos > 2 * SSID_MAX_LEN ||
 			    hexstr2bin(pos, ssid[ssid_count].ssid,
 				       (end - pos) / 2) < 0) {
 				os_free(ssid);
@@ -202,6 +210,7 @@
 	wpa_s->sme.prev_bssid_set = 0;
 #endif /* CONFIG_SME */
 	wpa_s->reassociate = 1;
+	wpa_s->own_disconnect_req = 1;
 	wpa_supplicant_deauthenticate(wpa_s, WLAN_REASON_DEAUTH_LEAVING);
 	wpa_supplicant_req_scan(wpa_s, 0, 0);
 
@@ -420,11 +429,32 @@
 #ifdef CONFIG_TESTING_OPTIONS
 	} else if (os_strcasecmp(cmd, "ext_mgmt_frame_handling") == 0) {
 		wpa_s->ext_mgmt_frame_handling = !!atoi(value);
+	} else if (os_strcasecmp(cmd, "ext_eapol_frame_io") == 0) {
+		wpa_s->ext_eapol_frame_io = !!atoi(value);
+#ifdef CONFIG_AP
+		if (wpa_s->ap_iface) {
+			wpa_s->ap_iface->bss[0]->ext_eapol_frame_io =
+				wpa_s->ext_eapol_frame_io;
+		}
+#endif /* CONFIG_AP */
+	} else if (os_strcasecmp(cmd, "extra_roc_dur") == 0) {
+		wpa_s->extra_roc_dur = atoi(value);
+	} else if (os_strcasecmp(cmd, "test_failure") == 0) {
+		wpa_s->test_failure = atoi(value);
 #endif /* CONFIG_TESTING_OPTIONS */
 #ifndef CONFIG_NO_CONFIG_BLOBS
 	} else if (os_strcmp(cmd, "blob") == 0) {
 		ret = wpas_ctrl_set_blob(wpa_s, value);
 #endif /* CONFIG_NO_CONFIG_BLOBS */
+	} else if (os_strcasecmp(cmd, "setband") == 0) {
+		if (os_strcmp(value, "AUTO") == 0)
+			wpa_s->setband = WPA_SETBAND_AUTO;
+		else if (os_strcmp(value, "5G") == 0)
+			wpa_s->setband = WPA_SETBAND_5G;
+		else if (os_strcmp(value, "2G") == 0)
+			wpa_s->setband = WPA_SETBAND_2G;
+		else
+			ret = -1;
 	} else {
 		value[-1] = '=';
 		ret = wpa_config_process_global(wpa_s->conf, cmd, -1);
@@ -459,9 +489,6 @@
 		else
 			enabled = wpa_s->global->wifi_display;
 		res = os_snprintf(buf, buflen, "%d", enabled);
-		if (res < 0 || (unsigned int) res >= buflen)
-			return -1;
-		return res;
 #endif /* CONFIG_WIFI_DISPLAY */
 #ifdef CONFIG_TESTING_GET_GTK
 	} else if (os_strcmp(cmd, "gtk") == 0) {
@@ -471,9 +498,13 @@
 				       wpa_s->last_gtk_len);
 		return res;
 #endif /* CONFIG_TESTING_GET_GTK */
+	} else if (os_strcmp(cmd, "tls_library") == 0) {
+		res = tls_get_library_version(buf, buflen);
+	} else {
+		res = wpa_config_get_value(cmd, wpa_s->conf, buf, buflen);
 	}
 
-	if (res < 0 || (unsigned int) res >= buflen)
+	if (os_snprintf_error(buflen, res))
 		return -1;
 	return res;
 }
@@ -626,14 +657,189 @@
 			  (wpa_s->drv_flags &
 			   WPA_DRIVER_FLAGS_TDLS_EXTERNAL_SETUP ?
 			   "EXTERNAL" : "INTERNAL") : "UNSUPPORTED");
-	if (ret < 0 || (size_t) ret > buflen)
+	if (os_snprintf_error(buflen, ret))
 		return -1;
 	return ret;
 }
 
+
+static int wpa_supplicant_ctrl_iface_tdls_chan_switch(
+	struct wpa_supplicant *wpa_s, char *cmd)
+{
+	u8 peer[ETH_ALEN];
+	struct hostapd_freq_params freq_params;
+	u8 oper_class;
+	char *pos, *end;
+
+	if (!wpa_tdls_is_external_setup(wpa_s->wpa)) {
+		wpa_printf(MSG_INFO,
+			   "tdls_chanswitch: Only supported with external setup");
+		return -1;
+	}
+
+	os_memset(&freq_params, 0, sizeof(freq_params));
+
+	pos = os_strchr(cmd, ' ');
+	if (pos == NULL)
+		return -1;
+	*pos++ = '\0';
+
+	oper_class = strtol(pos, &end, 10);
+	if (pos == end) {
+		wpa_printf(MSG_INFO,
+			   "tdls_chanswitch: Invalid op class provided");
+		return -1;
+	}
+
+	pos = end;
+	freq_params.freq = atoi(pos);
+	if (freq_params.freq == 0) {
+		wpa_printf(MSG_INFO, "tdls_chanswitch: Invalid freq provided");
+		return -1;
+	}
+
+#define SET_FREQ_SETTING(str) \
+	do { \
+		const char *pos2 = os_strstr(pos, " " #str "="); \
+		if (pos2) { \
+			pos2 += sizeof(" " #str "=") - 1; \
+			freq_params.str = atoi(pos2); \
+		} \
+	} while (0)
+
+	SET_FREQ_SETTING(center_freq1);
+	SET_FREQ_SETTING(center_freq2);
+	SET_FREQ_SETTING(bandwidth);
+	SET_FREQ_SETTING(sec_channel_offset);
+#undef SET_FREQ_SETTING
+
+	freq_params.ht_enabled = !!os_strstr(pos, " ht");
+	freq_params.vht_enabled = !!os_strstr(pos, " vht");
+
+	if (hwaddr_aton(cmd, peer)) {
+		wpa_printf(MSG_DEBUG,
+			   "CTRL_IFACE TDLS_CHAN_SWITCH: Invalid address '%s'",
+			   cmd);
+		return -1;
+	}
+
+	wpa_printf(MSG_DEBUG, "CTRL_IFACE TDLS_CHAN_SWITCH " MACSTR
+		   " OP CLASS %d FREQ %d CENTER1 %d CENTER2 %d BW %d SEC_OFFSET %d%s%s",
+		   MAC2STR(peer), oper_class, freq_params.freq,
+		   freq_params.center_freq1, freq_params.center_freq2,
+		   freq_params.bandwidth, freq_params.sec_channel_offset,
+		   freq_params.ht_enabled ? " HT" : "",
+		   freq_params.vht_enabled ? " VHT" : "");
+
+	return wpa_tdls_enable_chan_switch(wpa_s->wpa, peer, oper_class,
+					   &freq_params);
+}
+
+
+static int wpa_supplicant_ctrl_iface_tdls_cancel_chan_switch(
+	struct wpa_supplicant *wpa_s, char *cmd)
+{
+	u8 peer[ETH_ALEN];
+
+	if (!wpa_tdls_is_external_setup(wpa_s->wpa)) {
+		wpa_printf(MSG_INFO,
+			   "tdls_chanswitch: Only supported with external setup");
+		return -1;
+	}
+
+	if (hwaddr_aton(cmd, peer)) {
+		wpa_printf(MSG_DEBUG,
+			   "CTRL_IFACE TDLS_CANCEL_CHAN_SWITCH: Invalid address '%s'",
+			   cmd);
+		return -1;
+	}
+
+	wpa_printf(MSG_DEBUG, "CTRL_IFACE TDLS_CANCEL_CHAN_SWITCH " MACSTR,
+		   MAC2STR(peer));
+
+	return wpa_tdls_disable_chan_switch(wpa_s->wpa, peer);
+}
+
+
+static int wpa_supplicant_ctrl_iface_tdls_link_status(
+	struct wpa_supplicant *wpa_s, const char *addr,
+	char *buf, size_t buflen)
+{
+	u8 peer[ETH_ALEN];
+	const char *tdls_status;
+	int ret;
+
+	if (hwaddr_aton(addr, peer)) {
+		wpa_printf(MSG_DEBUG,
+			   "CTRL_IFACE TDLS_LINK_STATUS: Invalid address '%s'",
+			   addr);
+		return -1;
+	}
+	wpa_printf(MSG_DEBUG, "CTRL_IFACE TDLS_LINK_STATUS " MACSTR,
+		   MAC2STR(peer));
+
+	tdls_status = wpa_tdls_get_link_status(wpa_s->wpa, peer);
+	wpa_printf(MSG_DEBUG, "CTRL_IFACE TDLS_LINK_STATUS: %s", tdls_status);
+	ret = os_snprintf(buf, buflen, "TDLS link status: %s\n", tdls_status);
+	if (os_snprintf_error(buflen, ret))
+		return -1;
+
+	return ret;
+}
+
 #endif /* CONFIG_TDLS */
 
 
+static int wmm_ac_ctrl_addts(struct wpa_supplicant *wpa_s, char *cmd)
+{
+	char *token, *context = NULL;
+	struct wmm_ac_ts_setup_params params = {
+		.tsid = 0xff,
+		.direction = 0xff,
+	};
+
+	while ((token = str_token(cmd, " ", &context))) {
+		if (sscanf(token, "tsid=%i", &params.tsid) == 1 ||
+		    sscanf(token, "up=%i", &params.user_priority) == 1 ||
+		    sscanf(token, "nominal_msdu_size=%i",
+			   &params.nominal_msdu_size) == 1 ||
+		    sscanf(token, "mean_data_rate=%i",
+			   &params.mean_data_rate) == 1 ||
+		    sscanf(token, "min_phy_rate=%i",
+			   &params.minimum_phy_rate) == 1 ||
+		    sscanf(token, "sba=%i",
+			   &params.surplus_bandwidth_allowance) == 1)
+			continue;
+
+		if (os_strcasecmp(token, "downlink") == 0) {
+			params.direction = WMM_TSPEC_DIRECTION_DOWNLINK;
+		} else if (os_strcasecmp(token, "uplink") == 0) {
+			params.direction = WMM_TSPEC_DIRECTION_UPLINK;
+		} else if (os_strcasecmp(token, "bidi") == 0) {
+			params.direction = WMM_TSPEC_DIRECTION_BI_DIRECTIONAL;
+		} else if (os_strcasecmp(token, "fixed_nominal_msdu") == 0) {
+			params.fixed_nominal_msdu = 1;
+		} else {
+			wpa_printf(MSG_DEBUG,
+				   "CTRL: Invalid WMM_AC_ADDTS parameter: '%s'",
+				   token);
+			return -1;
+		}
+
+	}
+
+	return wpas_wmm_ac_addts(wpa_s, &params);
+}
+
+
+static int wmm_ac_ctrl_delts(struct wpa_supplicant *wpa_s, char *cmd)
+{
+	u8 tsid = atoi(cmd);
+
+	return wpas_wmm_ac_delts(wpa_s, tsid);
+}
+
+
 #ifdef CONFIG_IEEE80211R
 static int wpa_supplicant_ctrl_iface_ft_ds(
 	struct wpa_supplicant *wpa_s, char *addr)
@@ -747,7 +953,7 @@
 		if (ret < 0)
 			return -1;
 		ret = os_snprintf(buf, buflen, "%s", pin);
-		if (ret < 0 || (size_t) ret >= buflen)
+		if (os_snprintf_error(buflen, ret))
 			return -1;
 		return ret;
 	}
@@ -759,7 +965,7 @@
 done:
 	/* Return the generated PIN */
 	ret = os_snprintf(buf, buflen, "%08d", ret);
-	if (ret < 0 || (size_t) ret >= buflen)
+	if (os_snprintf_error(buflen, ret))
 		return -1;
 	return ret;
 }
@@ -796,14 +1002,14 @@
 		if (!wps_pin_valid(pin_val)) {
 			wpa_printf(MSG_DEBUG, "WPS: Invalid checksum digit");
 			ret = os_snprintf(buf, buflen, "FAIL-CHECKSUM\n");
-			if (ret < 0 || (size_t) ret >= buflen)
+			if (os_snprintf_error(buflen, ret))
 				return -1;
 			return ret;
 		}
 	}
 
 	ret = os_snprintf(buf, buflen, "%s", pin);
-	if (ret < 0 || (size_t) ret >= buflen)
+	if (os_snprintf_error(buflen, ret))
 		return -1;
 
 	return ret;
@@ -1526,6 +1732,8 @@
 #ifdef CONFIG_HS20
 	const u8 *hs20;
 #endif /* CONFIG_HS20 */
+	const u8 *sess_id;
+	size_t sess_id_len;
 
 	if (os_strcmp(params, "-DRIVER") == 0)
 		return wpa_drv_status(wpa_s, buf, buflen);
@@ -1537,18 +1745,18 @@
 		struct wpa_ssid *ssid = wpa_s->current_ssid;
 		ret = os_snprintf(pos, end - pos, "bssid=" MACSTR "\n",
 				  MAC2STR(wpa_s->bssid));
-		if (ret < 0 || ret >= end - pos)
+		if (os_snprintf_error(end - pos, ret))
 			return pos - buf;
 		pos += ret;
 		ret = os_snprintf(pos, end - pos, "freq=%u\n",
 				  wpa_s->assoc_freq);
-		if (ret < 0 || ret >= end - pos)
+		if (os_snprintf_error(end - pos, ret))
 			return pos - buf;
 		pos += ret;
 		if (ssid) {
 			u8 *_ssid = ssid->ssid;
 			size_t ssid_len = ssid->ssid_len;
-			u8 ssid_buf[MAX_SSID_LEN];
+			u8 ssid_buf[SSID_MAX_LEN];
 			if (ssid_len == 0) {
 				int _res = wpa_drv_get_ssid(wpa_s, ssid_buf);
 				if (_res < 0)
@@ -1560,7 +1768,7 @@
 			ret = os_snprintf(pos, end - pos, "ssid=%s\nid=%d\n",
 					  wpa_ssid_txt(_ssid, ssid_len),
 					  ssid->id);
-			if (ret < 0 || ret >= end - pos)
+			if (os_snprintf_error(end - pos, ret))
 				return pos - buf;
 			pos += ret;
 
@@ -1571,7 +1779,7 @@
 				ret = os_snprintf(pos, end - pos,
 						  "passphrase=%s\n",
 						  ssid->passphrase);
-				if (ret < 0 || ret >= end - pos)
+				if (os_snprintf_error(end - pos, ret))
 					return pos - buf;
 				pos += ret;
 			}
@@ -1579,7 +1787,7 @@
 				ret = os_snprintf(pos, end - pos,
 						  "id_str=%s\n",
 						  ssid->id_str);
-				if (ret < 0 || ret >= end - pos)
+				if (os_snprintf_error(end - pos, ret))
 					return pos - buf;
 				pos += ret;
 			}
@@ -1610,7 +1818,7 @@
 				ret = 0;
 				break;
 			}
-			if (ret < 0 || ret >= end - pos)
+			if (os_snprintf_error(end - pos, ret))
 				return pos - buf;
 			pos += ret;
 		}
@@ -1632,21 +1840,21 @@
 	    wpa_s->sme.sae.state == SAE_ACCEPTED) {
 		ret = os_snprintf(pos, end - pos, "sae_group=%d\n",
 				  wpa_s->sme.sae.group);
-		if (ret < 0 || ret >= end - pos)
+		if (os_snprintf_error(end - pos, ret))
 			return pos - buf;
 		pos += ret;
 	}
 #endif /* CONFIG_SAE */
 	ret = os_snprintf(pos, end - pos, "wpa_state=%s\n",
 			  wpa_supplicant_state_txt(wpa_s->wpa_state));
-	if (ret < 0 || ret >= end - pos)
+	if (os_snprintf_error(end - pos, ret))
 		return pos - buf;
 	pos += ret;
 
 	if (wpa_s->l2 &&
 	    l2_packet_get_ip_addr(wpa_s->l2, tmp, sizeof(tmp)) >= 0) {
 		ret = os_snprintf(pos, end - pos, "ip_address=%s\n", tmp);
-		if (ret < 0 || ret >= end - pos)
+		if (os_snprintf_error(end - pos, ret))
 			return pos - buf;
 		pos += ret;
 	}
@@ -1655,7 +1863,7 @@
 	if (wpa_s->global->p2p) {
 		ret = os_snprintf(pos, end - pos, "p2p_device_address=" MACSTR
 				  "\n", MAC2STR(wpa_s->global->p2p_dev_addr));
-		if (ret < 0 || ret >= end - pos)
+		if (os_snprintf_error(end - pos, ret))
 			return pos - buf;
 		pos += ret;
 	}
@@ -1663,7 +1871,7 @@
 
 	ret = os_snprintf(pos, end - pos, "address=" MACSTR "\n",
 			  MAC2STR(wpa_s->own_addr));
-	if (ret < 0 || ret >= end - pos)
+	if (os_snprintf_error(end - pos, ret))
 		return pos - buf;
 	pos += ret;
 
@@ -1679,7 +1887,7 @@
 			release = rel_num + 1;
 		}
 		ret = os_snprintf(pos, end - pos, "hs20=%d\n", release);
-		if (ret < 0 || ret >= end - pos)
+		if (os_snprintf_error(end - pos, ret))
 			return pos - buf;
 		pos += ret;
 	}
@@ -1698,7 +1906,7 @@
 				ret = os_snprintf(pos, end - pos,
 						  "provisioning_sp=%s\n",
 						  cred->provisioning_sp);
-				if (ret < 0 || ret >= end - pos)
+				if (os_snprintf_error(end - pos, ret))
 					return pos - buf;
 				pos += ret;
 			}
@@ -1721,7 +1929,7 @@
 			}
 			ret = os_snprintf(pos, end - pos, "home_sp=%s\n",
 					  cred->domain[i]);
-			if (ret < 0 || ret >= end - pos)
+			if (os_snprintf_error(end - pos, ret))
 				return pos - buf;
 			pos += ret;
 
@@ -1741,7 +1949,7 @@
 				type = "unknown";
 
 			ret = os_snprintf(pos, end - pos, "sp_type=%s\n", type);
-			if (ret < 0 || ret >= end - pos)
+			if (os_snprintf_error(end - pos, ret))
 				return pos - buf;
 			pos += ret;
 
@@ -1758,6 +1966,24 @@
 			pos += res;
 	}
 
+	sess_id = eapol_sm_get_session_id(wpa_s->eapol, &sess_id_len);
+	if (sess_id) {
+		char *start = pos;
+
+		ret = os_snprintf(pos, end - pos, "eap_session_id=");
+		if (os_snprintf_error(end - pos, ret))
+			return start - buf;
+		pos += ret;
+		ret = wpa_snprintf_hex(pos, end - pos, sess_id, sess_id_len);
+		if (ret <= 0)
+			return start - buf;
+		pos += ret;
+		ret = os_snprintf(pos, end - pos, "\n");
+		if (os_snprintf_error(end - pos, ret))
+			return start - buf;
+		pos += ret;
+	}
+
 	res = rsn_preauth_get_status(wpa_s->wpa, pos, end - pos, verbose);
 	if (res >= 0)
 		pos += res;
@@ -1767,7 +1993,7 @@
 		char uuid_str[100];
 		uuid_bin2str(wpa_s->wps->uuid, uuid_str, sizeof(uuid_str));
 		ret = os_snprintf(pos, end - pos, "uuid=%s\n", uuid_str);
-		if (ret < 0 || ret >= end - pos)
+		if (os_snprintf_error(end - pos, ret))
 			return pos - buf;
 		pos += ret;
 	}
@@ -1855,7 +2081,7 @@
 		while (e) {
 			ret = os_snprintf(pos, end - pos, MACSTR "\n",
 					  MAC2STR(e->bssid));
-			if (ret < 0 || ret >= end - pos)
+			if (os_snprintf_error(end - pos, ret))
 				return pos - buf;
 			pos += ret;
 			e = e->next;
@@ -1937,10 +2163,6 @@
 	char *pos, *end, *stamp;
 	int ret;
 
-	if (cmd == NULL) {
-		return -1;
-	}
-
 	/* cmd: "LOG_LEVEL [<level>]" */
 	if (*cmd == '\0') {
 		pos = buf;
@@ -1949,7 +2171,7 @@
 				  "Timestamp: %d\n",
 				  debug_level_str(wpa_debug_level),
 				  wpa_debug_timestamp);
-		if (ret < 0 || ret >= end - pos)
+		if (os_snprintf_error(end - pos, ret))
 			ret = 0;
 
 		return ret;
@@ -1992,7 +2214,7 @@
 	end = buf + buflen;
 	ret = os_snprintf(pos, end - pos,
 			  "network id / ssid / bssid / flags\n");
-	if (ret < 0 || ret >= end - pos)
+	if (os_snprintf_error(end - pos, ret))
 		return pos - buf;
 	pos += ret;
 
@@ -2013,7 +2235,7 @@
 		ret = os_snprintf(pos, end - pos, "%d\t%s",
 				  ssid->id,
 				  wpa_ssid_txt(ssid->ssid, ssid->ssid_len));
-		if (ret < 0 || ret >= end - pos)
+		if (os_snprintf_error(end - pos, ret))
 			return prev - buf;
 		pos += ret;
 		if (ssid->bssid_set) {
@@ -2022,7 +2244,7 @@
 		} else {
 			ret = os_snprintf(pos, end - pos, "\tany");
 		}
-		if (ret < 0 || ret >= end - pos)
+		if (os_snprintf_error(end - pos, ret))
 			return prev - buf;
 		pos += ret;
 		ret = os_snprintf(pos, end - pos, "\t%s%s%s%s",
@@ -2033,11 +2255,11 @@
 				  "[TEMP-DISABLED]" : "",
 				  ssid->disabled == 2 ? "[P2P-PERSISTENT]" :
 				  "");
-		if (ret < 0 || ret >= end - pos)
+		if (os_snprintf_error(end - pos, ret))
 			return prev - buf;
 		pos += ret;
 		ret = os_snprintf(pos, end - pos, "\n");
-		if (ret < 0 || ret >= end - pos)
+		if (os_snprintf_error(end - pos, ret))
 			return prev - buf;
 		pos += ret;
 
@@ -2052,7 +2274,7 @@
 {
 	int ret;
 	ret = os_snprintf(pos, end - pos, "-");
-	if (ret < 0 || ret >= end - pos)
+	if (os_snprintf_error(end - pos, ret))
 		return pos;
 	pos += ret;
 	ret = wpa_write_ciphers(pos, end, cipher, "+");
@@ -2071,13 +2293,13 @@
 	int ret;
 
 	ret = os_snprintf(pos, end - pos, "[%s-", proto);
-	if (ret < 0 || ret >= end - pos)
+	if (os_snprintf_error(end - pos, ret))
 		return pos;
 	pos += ret;
 
 	if (wpa_parse_wpa_ie(ie, ie_len, &data) < 0) {
 		ret = os_snprintf(pos, end - pos, "?]");
-		if (ret < 0 || ret >= end - pos)
+		if (os_snprintf_error(end - pos, ret))
 			return pos;
 		pos += ret;
 		return pos;
@@ -2087,21 +2309,28 @@
 	if (data.key_mgmt & WPA_KEY_MGMT_IEEE8021X) {
 		ret = os_snprintf(pos, end - pos, "%sEAP",
 				  pos == start ? "" : "+");
-		if (ret < 0 || ret >= end - pos)
+		if (os_snprintf_error(end - pos, ret))
 			return pos;
 		pos += ret;
 	}
 	if (data.key_mgmt & WPA_KEY_MGMT_PSK) {
 		ret = os_snprintf(pos, end - pos, "%sPSK",
 				  pos == start ? "" : "+");
-		if (ret < 0 || ret >= end - pos)
+		if (os_snprintf_error(end - pos, ret))
 			return pos;
 		pos += ret;
 	}
 	if (data.key_mgmt & WPA_KEY_MGMT_WPA_NONE) {
 		ret = os_snprintf(pos, end - pos, "%sNone",
 				  pos == start ? "" : "+");
-		if (ret < 0 || ret >= end - pos)
+		if (os_snprintf_error(end - pos, ret))
+			return pos;
+		pos += ret;
+	}
+	if (data.key_mgmt & WPA_KEY_MGMT_SAE) {
+		ret = os_snprintf(pos, end - pos, "%sSAE",
+				  pos == start ? "" : "+");
+		if (os_snprintf_error(end - pos, ret))
 			return pos;
 		pos += ret;
 	}
@@ -2109,14 +2338,21 @@
 	if (data.key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X) {
 		ret = os_snprintf(pos, end - pos, "%sFT/EAP",
 				  pos == start ? "" : "+");
-		if (ret < 0 || ret >= end - pos)
+		if (os_snprintf_error(end - pos, ret))
 			return pos;
 		pos += ret;
 	}
 	if (data.key_mgmt & WPA_KEY_MGMT_FT_PSK) {
 		ret = os_snprintf(pos, end - pos, "%sFT/PSK",
 				  pos == start ? "" : "+");
-		if (ret < 0 || ret >= end - pos)
+		if (os_snprintf_error(end - pos, ret))
+			return pos;
+		pos += ret;
+	}
+	if (data.key_mgmt & WPA_KEY_MGMT_FT_SAE) {
+		ret = os_snprintf(pos, end - pos, "%sFT/SAE",
+				  pos == start ? "" : "+");
+		if (os_snprintf_error(end - pos, ret))
 			return pos;
 		pos += ret;
 	}
@@ -2125,30 +2361,58 @@
 	if (data.key_mgmt & WPA_KEY_MGMT_IEEE8021X_SHA256) {
 		ret = os_snprintf(pos, end - pos, "%sEAP-SHA256",
 				  pos == start ? "" : "+");
-		if (ret < 0 || ret >= end - pos)
+		if (os_snprintf_error(end - pos, ret))
 			return pos;
 		pos += ret;
 	}
 	if (data.key_mgmt & WPA_KEY_MGMT_PSK_SHA256) {
 		ret = os_snprintf(pos, end - pos, "%sPSK-SHA256",
 				  pos == start ? "" : "+");
-		if (ret < 0 || ret >= end - pos)
+		if (os_snprintf_error(end - pos, ret))
 			return pos;
 		pos += ret;
 	}
 #endif /* CONFIG_IEEE80211W */
 
+#ifdef CONFIG_SUITEB
+	if (data.key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B) {
+		ret = os_snprintf(pos, end - pos, "%sEAP-SUITE-B",
+				  pos == start ? "" : "+");
+		if (os_snprintf_error(end - pos, ret))
+			return pos;
+		pos += ret;
+	}
+#endif /* CONFIG_SUITEB */
+
+#ifdef CONFIG_SUITEB192
+	if (data.key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B_192) {
+		ret = os_snprintf(pos, end - pos, "%sEAP-SUITE-B-192",
+				  pos == start ? "" : "+");
+		if (os_snprintf_error(end - pos, ret))
+			return pos;
+		pos += ret;
+	}
+#endif /* CONFIG_SUITEB192 */
+
+	if (data.key_mgmt & WPA_KEY_MGMT_OSEN) {
+		ret = os_snprintf(pos, end - pos, "%sOSEN",
+				  pos == start ? "" : "+");
+		if (os_snprintf_error(end - pos, ret))
+			return pos;
+		pos += ret;
+	}
+
 	pos = wpa_supplicant_cipher_txt(pos, end, data.pairwise_cipher);
 
 	if (data.capabilities & WPA_CAPABILITY_PREAUTH) {
 		ret = os_snprintf(pos, end - pos, "-preauth");
-		if (ret < 0 || ret >= end - pos)
+		if (os_snprintf_error(end - pos, ret))
 			return pos;
 		pos += ret;
 	}
 
 	ret = os_snprintf(pos, end - pos, "]");
-	if (ret < 0 || ret >= end - pos)
+	if (os_snprintf_error(end - pos, ret))
 		return pos;
 	pos += ret;
 
@@ -2176,7 +2440,7 @@
 		txt = "[WPS]";
 
 	ret = os_snprintf(pos, end - pos, "%s", txt);
-	if (ret >= 0 && ret < end - pos)
+	if (!os_snprintf_error(end - pos, ret))
 		pos += ret;
 	wpabuf_free(wps_ie);
 	return pos;
@@ -2205,8 +2469,9 @@
 {
 	char *pos, *end;
 	int ret;
-	const u8 *ie, *ie2, *p2p;
+	const u8 *ie, *ie2, *osen_ie, *p2p, *mesh;
 
+	mesh = wpa_bss_get_ie(bss, WLAN_EID_MESH_ID);
 	p2p = wpa_bss_get_vendor_ie(bss, P2P_IE_VENDOR_TYPE);
 	if (!p2p)
 		p2p = wpa_bss_get_vendor_ie_beacon(bss, P2P_IE_VENDOR_TYPE);
@@ -2220,26 +2485,38 @@
 
 	ret = os_snprintf(pos, end - pos, MACSTR "\t%d\t%d\t",
 			  MAC2STR(bss->bssid), bss->freq, bss->level);
-	if (ret < 0 || ret >= end - pos)
+	if (os_snprintf_error(end - pos, ret))
 		return -1;
 	pos += ret;
 	ie = wpa_bss_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE);
 	if (ie)
 		pos = wpa_supplicant_ie_txt(pos, end, "WPA", ie, 2 + ie[1]);
 	ie2 = wpa_bss_get_ie(bss, WLAN_EID_RSN);
-	if (ie2)
-		pos = wpa_supplicant_ie_txt(pos, end, "WPA2", ie2, 2 + ie2[1]);
+	if (ie2) {
+		pos = wpa_supplicant_ie_txt(pos, end, mesh ? "RSN" : "WPA2",
+					    ie2, 2 + ie2[1]);
+	}
+	osen_ie = wpa_bss_get_vendor_ie(bss, OSEN_IE_VENDOR_TYPE);
+	if (osen_ie)
+		pos = wpa_supplicant_ie_txt(pos, end, "OSEN",
+					    osen_ie, 2 + osen_ie[1]);
 	pos = wpa_supplicant_wps_ie_txt(wpa_s, pos, end, bss);
-	if (!ie && !ie2 && bss->caps & IEEE80211_CAP_PRIVACY) {
+	if (!ie && !ie2 && !osen_ie && (bss->caps & IEEE80211_CAP_PRIVACY)) {
 		ret = os_snprintf(pos, end - pos, "[WEP]");
-		if (ret < 0 || ret >= end - pos)
+		if (os_snprintf_error(end - pos, ret))
+			return -1;
+		pos += ret;
+	}
+	if (mesh) {
+		ret = os_snprintf(pos, end - pos, "[MESH]");
+		if (os_snprintf_error(end - pos, ret))
 			return -1;
 		pos += ret;
 	}
 	if (bss_is_dmg(bss)) {
 		const char *s;
 		ret = os_snprintf(pos, end - pos, "[DMG]");
-		if (ret < 0 || ret >= end - pos)
+		if (os_snprintf_error(end - pos, ret))
 			return -1;
 		pos += ret;
 		switch (bss->caps & IEEE80211_CAP_DMG_MASK) {
@@ -2257,33 +2534,33 @@
 			break;
 		}
 		ret = os_snprintf(pos, end - pos, "%s", s);
-		if (ret < 0 || ret >= end - pos)
+		if (os_snprintf_error(end - pos, ret))
 			return -1;
 		pos += ret;
 	} else {
 		if (bss->caps & IEEE80211_CAP_IBSS) {
 			ret = os_snprintf(pos, end - pos, "[IBSS]");
-			if (ret < 0 || ret >= end - pos)
+			if (os_snprintf_error(end - pos, ret))
 				return -1;
 			pos += ret;
 		}
 		if (bss->caps & IEEE80211_CAP_ESS) {
 			ret = os_snprintf(pos, end - pos, "[ESS]");
-			if (ret < 0 || ret >= end - pos)
+			if (os_snprintf_error(end - pos, ret))
 				return -1;
 			pos += ret;
 		}
 	}
 	if (p2p) {
 		ret = os_snprintf(pos, end - pos, "[P2P]");
-		if (ret < 0 || ret >= end - pos)
+		if (os_snprintf_error(end - pos, ret))
 			return -1;
 		pos += ret;
 	}
 #ifdef CONFIG_HS20
 	if (wpa_bss_get_vendor_ie(bss, HS20_IE_VENDOR_TYPE) && ie2) {
 		ret = os_snprintf(pos, end - pos, "[HS20]");
-		if (ret < 0 || ret >= end - pos)
+		if (os_snprintf_error(end - pos, ret))
 			return -1;
 		pos += ret;
 	}
@@ -2291,12 +2568,12 @@
 
 	ret = os_snprintf(pos, end - pos, "\t%s",
 			  wpa_ssid_txt(bss->ssid, bss->ssid_len));
-	if (ret < 0 || ret >= end - pos)
+	if (os_snprintf_error(end - pos, ret))
 		return -1;
 	pos += ret;
 
 	ret = os_snprintf(pos, end - pos, "\n");
-	if (ret < 0 || ret >= end - pos)
+	if (os_snprintf_error(end - pos, ret))
 		return -1;
 	pos += ret;
 
@@ -2315,7 +2592,7 @@
 	end = buf + buflen;
 	ret = os_snprintf(pos, end - pos, "bssid / frequency / signal level / "
 			  "flags / ssid\n");
-	if (ret < 0 || ret >= end - pos)
+	if (os_snprintf_error(end - pos, ret))
 		return pos - buf;
 	pos += ret;
 
@@ -2331,6 +2608,116 @@
 }
 
 
+#ifdef CONFIG_MESH
+
+static int wpa_supplicant_ctrl_iface_mesh_interface_add(
+	struct wpa_supplicant *wpa_s, char *cmd, char *reply, size_t max_len)
+{
+	char *pos, ifname[IFNAMSIZ + 1];
+
+	ifname[0] = '\0';
+
+	pos = os_strstr(cmd, "ifname=");
+	if (pos) {
+		pos += 7;
+		os_strlcpy(ifname, pos, sizeof(ifname));
+	}
+
+	if (wpas_mesh_add_interface(wpa_s, ifname, sizeof(ifname)) < 0)
+		return -1;
+
+	os_strlcpy(reply, ifname, max_len);
+	return os_strlen(ifname);
+}
+
+
+static int wpa_supplicant_ctrl_iface_mesh_group_add(
+	struct wpa_supplicant *wpa_s, char *cmd)
+{
+	int id;
+	struct wpa_ssid *ssid;
+
+	id = atoi(cmd);
+	wpa_printf(MSG_DEBUG, "CTRL_IFACE: MESH_GROUP_ADD id=%d", id);
+
+	ssid = wpa_config_get_network(wpa_s->conf, id);
+	if (ssid == NULL) {
+		wpa_printf(MSG_DEBUG,
+			   "CTRL_IFACE: Could not find network id=%d", id);
+		return -1;
+	}
+	if (ssid->mode != WPAS_MODE_MESH) {
+		wpa_printf(MSG_DEBUG,
+			   "CTRL_IFACE: Cannot use MESH_GROUP_ADD on a non mesh network");
+		return -1;
+	}
+	if (ssid->key_mgmt != WPA_KEY_MGMT_NONE &&
+	    ssid->key_mgmt != WPA_KEY_MGMT_SAE) {
+		wpa_printf(MSG_ERROR,
+			   "CTRL_IFACE: key_mgmt for mesh network should be open or SAE");
+		return -1;
+	}
+
+	/*
+	 * TODO: If necessary write our own group_add function,
+	 * for now we can reuse select_network
+	 */
+	wpa_supplicant_select_network(wpa_s, ssid);
+
+	return 0;
+}
+
+
+static int wpa_supplicant_ctrl_iface_mesh_group_remove(
+	struct wpa_supplicant *wpa_s, char *cmd)
+{
+	struct wpa_supplicant *orig;
+	struct wpa_global *global;
+	int found = 0;
+
+	wpa_printf(MSG_DEBUG, "CTRL_IFACE: MESH_GROUP_REMOVE ifname=%s", cmd);
+
+	global = wpa_s->global;
+	orig = wpa_s;
+
+	for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) {
+		if (os_strcmp(wpa_s->ifname, cmd) == 0) {
+			found = 1;
+			break;
+		}
+	}
+	if (!found) {
+		wpa_printf(MSG_ERROR,
+			   "CTRL_IFACE: MESH_GROUP_REMOVE ifname=%s not found",
+			   cmd);
+		return -1;
+	}
+	if (wpa_s->mesh_if_created && wpa_s == orig) {
+		wpa_printf(MSG_ERROR,
+			   "CTRL_IFACE: MESH_GROUP_REMOVE can't remove itself");
+		return -1;
+	}
+
+	wpa_s->reassociate = 0;
+	wpa_s->disconnected = 1;
+	wpa_supplicant_cancel_sched_scan(wpa_s);
+	wpa_supplicant_cancel_scan(wpa_s);
+
+	/*
+	 * TODO: If necessary write our own group_remove function,
+	 * for now we can reuse deauthenticate
+	 */
+	wpa_supplicant_deauthenticate(wpa_s, WLAN_REASON_DEAUTH_LEAVING);
+
+	if (wpa_s->mesh_if_created)
+		wpa_supplicant_remove_iface(global, wpa_s, 0);
+
+	return 0;
+}
+
+#endif /* CONFIG_MESH */
+
+
 static int wpa_supplicant_ctrl_iface_select_network(
 	struct wpa_supplicant *wpa_s, char *cmd)
 {
@@ -2463,7 +2850,7 @@
 	wpa_config_set_network_defaults(ssid);
 
 	ret = os_snprintf(buf, buflen, "%d\n", ssid->id);
-	if (ret < 0 || (size_t) ret >= buflen)
+	if (os_snprintf_error(buflen, ret))
 		return -1;
 	return ret;
 }
@@ -2489,6 +2876,8 @@
 #endif /* CONFIG_SME */
 			wpa_sm_set_config(wpa_s->wpa, NULL);
 			eapol_sm_notify_config(wpa_s->eapol, NULL, NULL);
+			if (wpa_s->wpa_state >= WPA_AUTHENTICATING)
+				wpa_s->own_disconnect_req = 1;
 			wpa_supplicant_deauthenticate(
 				wpa_s, WLAN_REASON_DEAUTH_LEAVING);
 		}
@@ -2535,6 +2924,8 @@
 		wpa_sm_set_config(wpa_s->wpa, NULL);
 		eapol_sm_notify_config(wpa_s->eapol, NULL, NULL);
 
+		if (wpa_s->wpa_state >= WPA_AUTHENTICATING)
+			wpa_s->own_disconnect_req = 1;
 		wpa_supplicant_deauthenticate(wpa_s,
 					      WLAN_REASON_DEAUTH_LEAVING);
 	}
@@ -2594,7 +2985,7 @@
 static int wpa_supplicant_ctrl_iface_set_network(
 	struct wpa_supplicant *wpa_s, char *cmd)
 {
-	int id, ret, prev_bssid_set;
+	int id, ret, prev_bssid_set, prev_disabled;
 	struct wpa_ssid *ssid;
 	char *name, *value;
 	u8 prev_bssid[ETH_ALEN];
@@ -2624,6 +3015,7 @@
 	}
 
 	prev_bssid_set = ssid->bssid_set;
+	prev_disabled = ssid->disabled;
 	os_memcpy(prev_bssid, ssid->bssid, ETH_ALEN);
 	ret = wpa_supplicant_ctrl_iface_update_network(wpa_s, ssid, name,
 						       value);
@@ -2631,6 +3023,11 @@
 	    (ssid->bssid_set != prev_bssid_set ||
 	     os_memcmp(ssid->bssid, prev_bssid, ETH_ALEN) != 0))
 		wpas_notify_network_bssid_set_changed(wpa_s, ssid);
+
+	if (prev_disabled != ssid->disabled &&
+	    (prev_disabled == 2 || ssid->disabled == 2))
+		wpas_notify_network_type_changed(wpa_s, ssid);
+
 	return ret;
 }
 
@@ -2712,7 +3109,7 @@
 	ssid_d = wpa_config_get_network(wpa_s->conf, id_d);
 	if (ssid_d == NULL) {
 		wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find "
-			   "network id=%d", id_s);
+			   "network id=%d", id_d);
 		return -1;
 	}
 
@@ -2743,7 +3140,7 @@
 	end = buf + buflen;
 	ret = os_snprintf(pos, end - pos,
 			  "cred id / realm / username / domain / imsi\n");
-	if (ret < 0 || ret >= end - pos)
+	if (os_snprintf_error(end - pos, ret))
 		return pos - buf;
 	pos += ret;
 
@@ -2754,7 +3151,7 @@
 				  cred->username ? cred->username : "",
 				  cred->domain ? cred->domain[0] : "",
 				  cred->imsi ? cred->imsi : "");
-		if (ret < 0 || ret >= end - pos)
+		if (os_snprintf_error(end - pos, ret))
 			return pos - buf;
 		pos += ret;
 
@@ -2780,7 +3177,7 @@
 	wpa_msg(wpa_s, MSG_INFO, CRED_ADDED "%d", cred->id);
 
 	ret = os_snprintf(buf, buflen, "%d\n", cred->id);
-	if (ret < 0 || (size_t) ret >= buflen)
+	if (os_snprintf_error(buflen, ret))
 		return -1;
 	return ret;
 }
@@ -2810,9 +3207,13 @@
 	ssid = wpa_s->conf->ssid;
 	while (ssid) {
 		if (ssid->parent_cred == cred) {
+			int res;
+
 			wpa_printf(MSG_DEBUG, "Remove network id %d since it "
 				   "used the removed credential", ssid->id);
-			os_snprintf(str, sizeof(str), "%d", ssid->id);
+			res = os_snprintf(str, sizeof(str), "%d", ssid->id);
+			if (os_snprintf_error(sizeof(str), res))
+				str[sizeof(str) - 1] = '\0';
 			ssid = ssid->next;
 			wpa_supplicant_ctrl_iface_remove_network(wpa_s, str);
 		} else
@@ -3015,6 +3416,13 @@
 	{ WPA_DRIVER_CAPA_ENC_WEP40, "WEP40", 1 }
 };
 
+static const struct cipher_info ciphers_group_mgmt[] = {
+	{ WPA_DRIVER_CAPA_ENC_BIP, "AES-128-CMAC", 1 },
+	{ WPA_DRIVER_CAPA_ENC_BIP_GMAC_128, "BIP-GMAC-128", 1 },
+	{ WPA_DRIVER_CAPA_ENC_BIP_GMAC_256, "BIP-GMAC-256", 1 },
+	{ WPA_DRIVER_CAPA_ENC_BIP_CMAC_256, "BIP-CMAC-256", 1 },
+};
+
 
 static int ctrl_iface_get_capability_pairwise(int res, char *strict,
 					      struct wpa_driver_capa *capa,
@@ -3042,7 +3450,7 @@
 			ret = os_snprintf(pos, end - pos, "%s%s",
 					  pos == buf ? "" : " ",
 					  ciphers[i].name);
-			if (ret < 0 || ret >= end - pos)
+			if (os_snprintf_error(end - pos, ret))
 				return pos - buf;
 			pos += ret;
 		}
@@ -3078,7 +3486,36 @@
 			ret = os_snprintf(pos, end - pos, "%s%s",
 					  pos == buf ? "" : " ",
 					  ciphers[i].name);
-			if (ret < 0 || ret >= end - pos)
+			if (os_snprintf_error(end - pos, ret))
+				return pos - buf;
+			pos += ret;
+		}
+	}
+
+	return pos - buf;
+}
+
+
+static int ctrl_iface_get_capability_group_mgmt(int res, char *strict,
+						struct wpa_driver_capa *capa,
+						char *buf, size_t buflen)
+{
+	int ret;
+	char *pos, *end;
+	unsigned int i;
+
+	pos = buf;
+	end = pos + buflen;
+
+	if (res < 0)
+		return 0;
+
+	for (i = 0; i < ARRAY_SIZE(ciphers_group_mgmt); i++) {
+		if (capa->enc & ciphers_group_mgmt[i].capa) {
+			ret = os_snprintf(pos, end - pos, "%s%s",
+					  pos == buf ? "" : " ",
+					  ciphers_group_mgmt[i].name);
+			if (os_snprintf_error(end - pos, ret))
 				return pos - buf;
 			pos += ret;
 		}
@@ -3110,14 +3547,14 @@
 	}
 
 	ret = os_snprintf(pos, end - pos, "NONE IEEE8021X");
-	if (ret < 0 || ret >= end - pos)
+	if (os_snprintf_error(end - pos, ret))
 		return pos - buf;
 	pos += ret;
 
 	if (capa->key_mgmt & (WPA_DRIVER_CAPA_KEY_MGMT_WPA |
 			      WPA_DRIVER_CAPA_KEY_MGMT_WPA2)) {
 		ret = os_snprintf(pos, end - pos, " WPA-EAP");
-		if (ret < 0 || ret >= end - pos)
+		if (os_snprintf_error(end - pos, ret))
 			return pos - buf;
 		pos += ret;
 	}
@@ -3125,18 +3562,35 @@
 	if (capa->key_mgmt & (WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK |
 			      WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK)) {
 		ret = os_snprintf(pos, end - pos, " WPA-PSK");
-		if (ret < 0 || ret >= end - pos)
+		if (os_snprintf_error(end - pos, ret))
 			return pos - buf;
 		pos += ret;
 	}
 
 	if (capa->key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_WPA_NONE) {
 		ret = os_snprintf(pos, end - pos, " WPA-NONE");
-		if (ret < 0 || ret >= end - pos)
+		if (os_snprintf_error(end - pos, ret))
 			return pos - buf;
 		pos += ret;
 	}
 
+#ifdef CONFIG_SUITEB
+	if (capa->key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_SUITE_B) {
+		ret = os_snprintf(pos, end - pos, " WPA-EAP-SUITE-B");
+		if (os_snprintf_error(end - pos, ret))
+			return pos - buf;
+		pos += ret;
+	}
+#endif /* CONFIG_SUITEB */
+#ifdef CONFIG_SUITEB192
+	if (capa->key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_SUITE_B_192) {
+		ret = os_snprintf(pos, end - pos, " WPA-EAP-SUITE-B-192");
+		if (os_snprintf_error(end - pos, ret))
+			return pos - buf;
+		pos += ret;
+	}
+#endif /* CONFIG_SUITEB192 */
+
 	return pos - buf;
 }
 
@@ -3165,7 +3619,7 @@
 			      WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK)) {
 		ret = os_snprintf(pos, end - pos, "%sRSN",
 				  pos == buf ? "" : " ");
-		if (ret < 0 || ret >= end - pos)
+		if (os_snprintf_error(end - pos, ret))
 			return pos - buf;
 		pos += ret;
 	}
@@ -3174,7 +3628,7 @@
 			      WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK)) {
 		ret = os_snprintf(pos, end - pos, "%sWPA",
 				  pos == buf ? "" : " ");
-		if (ret < 0 || ret >= end - pos)
+		if (os_snprintf_error(end - pos, ret))
 			return pos - buf;
 		pos += ret;
 	}
@@ -3183,7 +3637,8 @@
 }
 
 
-static int ctrl_iface_get_capability_auth_alg(int res, char *strict,
+static int ctrl_iface_get_capability_auth_alg(struct wpa_supplicant *wpa_s,
+					      int res, char *strict,
 					      struct wpa_driver_capa *capa,
 					      char *buf, size_t buflen)
 {
@@ -3206,7 +3661,7 @@
 	if (capa->auth & (WPA_DRIVER_AUTH_OPEN)) {
 		ret = os_snprintf(pos, end - pos, "%sOPEN",
 				  pos == buf ? "" : " ");
-		if (ret < 0 || ret >= end - pos)
+		if (os_snprintf_error(end - pos, ret))
 			return pos - buf;
 		pos += ret;
 	}
@@ -3214,7 +3669,7 @@
 	if (capa->auth & (WPA_DRIVER_AUTH_SHARED)) {
 		ret = os_snprintf(pos, end - pos, "%sSHARED",
 				  pos == buf ? "" : " ");
-		if (ret < 0 || ret >= end - pos)
+		if (os_snprintf_error(end - pos, ret))
 			return pos - buf;
 		pos += ret;
 	}
@@ -3222,11 +3677,21 @@
 	if (capa->auth & (WPA_DRIVER_AUTH_LEAP)) {
 		ret = os_snprintf(pos, end - pos, "%sLEAP",
 				  pos == buf ? "" : " ");
-		if (ret < 0 || ret >= end - pos)
+		if (os_snprintf_error(end - pos, ret))
 			return pos - buf;
 		pos += ret;
 	}
 
+#ifdef CONFIG_SAE
+	if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SAE) {
+		ret = os_snprintf(pos, end - pos, "%sSAE",
+				  pos == buf ? "" : " ");
+		if (os_snprintf_error(end - pos, ret))
+			return pos - buf;
+		pos += ret;
+	}
+#endif /* CONFIG_SAE */
+
 	return pos - buf;
 }
 
@@ -3254,7 +3719,7 @@
 	if (capa->flags & WPA_DRIVER_FLAGS_IBSS) {
 		ret = os_snprintf(pos, end - pos, "%sIBSS",
 				  pos == buf ? "" : " ");
-		if (ret < 0 || ret >= end - pos)
+		if (os_snprintf_error(end - pos, ret))
 			return pos - buf;
 		pos += ret;
 	}
@@ -3262,11 +3727,21 @@
 	if (capa->flags & WPA_DRIVER_FLAGS_AP) {
 		ret = os_snprintf(pos, end - pos, "%sAP",
 				  pos == buf ? "" : " ");
-		if (ret < 0 || ret >= end - pos)
+		if (os_snprintf_error(end - pos, ret))
 			return pos - buf;
 		pos += ret;
 	}
 
+#ifdef CONFIG_MESH
+	if (capa->flags & WPA_DRIVER_FLAGS_MESH) {
+		ret = os_snprintf(pos, end - pos, "%sMESH",
+				  pos == buf ? "" : " ");
+		if (os_snprintf_error(end - pos, ret))
+			return pos - buf;
+		pos += ret;
+	}
+#endif /* CONFIG_MESH */
+
 	return pos - buf;
 }
 
@@ -3299,7 +3774,7 @@
 			continue;
 		}
 		ret = os_snprintf(pos, end - pos, "Mode[%s] Channels:", hmode);
-		if (ret < 0 || ret >= end - pos)
+		if (os_snprintf_error(end - pos, ret))
 			return pos - buf;
 		pos += ret;
 		chnl = wpa_s->hw.modes[j].channels;
@@ -3307,12 +3782,12 @@
 			if (chnl[i].flag & HOSTAPD_CHAN_DISABLED)
 				continue;
 			ret = os_snprintf(pos, end - pos, " %d", chnl[i].chan);
-			if (ret < 0 || ret >= end - pos)
+			if (os_snprintf_error(end - pos, ret))
 				return pos - buf;
 			pos += ret;
 		}
 		ret = os_snprintf(pos, end - pos, "\n");
-		if (ret < 0 || ret >= end - pos)
+		if (os_snprintf_error(end - pos, ret))
 			return pos - buf;
 		pos += ret;
 	}
@@ -3350,7 +3825,7 @@
 		}
 		ret = os_snprintf(pos, end - pos, "Mode[%s] Channels:\n",
 				  hmode);
-		if (ret < 0 || ret >= end - pos)
+		if (os_snprintf_error(end - pos, ret))
 			return pos - buf;
 		pos += ret;
 		chnl = wpa_s->hw.modes[j].channels;
@@ -3359,17 +3834,17 @@
 				continue;
 			ret = os_snprintf(pos, end - pos, " %d = %d MHz%s%s\n",
 					  chnl[i].chan, chnl[i].freq,
-					  chnl[i].flag & HOSTAPD_CHAN_NO_IBSS ?
-					  " (NO_IBSS)" : "",
+					  chnl[i].flag & HOSTAPD_CHAN_NO_IR ?
+					  " (NO_IR)" : "",
 					  chnl[i].flag & HOSTAPD_CHAN_RADAR ?
 					  " (DFS)" : "");
 
-			if (ret < 0 || ret >= end - pos)
+			if (os_snprintf_error(end - pos, ret))
 				return pos - buf;
 			pos += ret;
 		}
 		ret = os_snprintf(pos, end - pos, "\n");
-		if (ret < 0 || ret >= end - pos)
+		if (os_snprintf_error(end - pos, ret))
 			return pos - buf;
 		pos += ret;
 	}
@@ -3416,6 +3891,10 @@
 		return ctrl_iface_get_capability_group(res, strict, &capa,
 						       buf, buflen);
 
+	if (os_strcmp(field, "group_mgmt") == 0)
+		return ctrl_iface_get_capability_group_mgmt(res, strict, &capa,
+							    buf, buflen);
+
 	if (os_strcmp(field, "key_mgmt") == 0)
 		return ctrl_iface_get_capability_key_mgmt(res, strict, &capa,
 							  buf, buflen);
@@ -3425,8 +3904,8 @@
 						       buf, buflen);
 
 	if (os_strcmp(field, "auth_alg") == 0)
-		return ctrl_iface_get_capability_auth_alg(res, strict, &capa,
-							  buf, buflen);
+		return ctrl_iface_get_capability_auth_alg(wpa_s, res, strict,
+							  &capa, buf, buflen);
 
 	if (os_strcmp(field, "modes") == 0)
 		return ctrl_iface_get_capability_modes(res, strict, &capa,
@@ -3443,6 +3922,15 @@
 		return ctrl_iface_get_capability_tdls(wpa_s, buf, buflen);
 #endif /* CONFIG_TDLS */
 
+#ifdef CONFIG_ERP
+	if (os_strcmp(field, "erp") == 0) {
+		res = os_snprintf(buf, buflen, "ERP");
+		if (os_snprintf_error(buflen, res))
+			return -1;
+		return res;
+	}
+#endif /* CONFIG_EPR */
+
 	wpa_printf(MSG_DEBUG, "CTRL_IFACE: Unknown GET_CAPABILITY field '%s'",
 		   field);
 
@@ -3463,20 +3951,20 @@
 		return start;
 
 	ret = os_snprintf(pos, end - pos, "%s=", title);
-	if (ret < 0 || ret >= end - pos)
+	if (os_snprintf_error(end - pos, ret))
 		return start;
 	pos += ret;
 
 	d = wpabuf_head_u8(data);
 	for (i = 0; i < wpabuf_len(data); i++) {
 		ret = os_snprintf(pos, end - pos, "%02x", *d++);
-		if (ret < 0 || ret >= end - pos)
+		if (os_snprintf_error(end - pos, ret))
 			return start;
 		pos += ret;
 	}
 
 	ret = os_snprintf(pos, end - pos, "\n");
-	if (ret < 0 || ret >= end - pos)
+	if (os_snprintf_error(end - pos, ret))
 		return start;
 	pos += ret;
 
@@ -3491,14 +3979,14 @@
 	size_t i;
 	int ret;
 	char *pos, *end;
-	const u8 *ie, *ie2;
+	const u8 *ie, *ie2, *osen_ie;
 
 	pos = buf;
 	end = buf + buflen;
 
 	if (mask & WPA_BSS_MASK_ID) {
 		ret = os_snprintf(pos, end - pos, "id=%u\n", bss->id);
-		if (ret < 0 || ret >= end - pos)
+		if (os_snprintf_error(end - pos, ret))
 			return 0;
 		pos += ret;
 	}
@@ -3506,14 +3994,14 @@
 	if (mask & WPA_BSS_MASK_BSSID) {
 		ret = os_snprintf(pos, end - pos, "bssid=" MACSTR "\n",
 				  MAC2STR(bss->bssid));
-		if (ret < 0 || ret >= end - pos)
+		if (os_snprintf_error(end - pos, ret))
 			return 0;
 		pos += ret;
 	}
 
 	if (mask & WPA_BSS_MASK_FREQ) {
 		ret = os_snprintf(pos, end - pos, "freq=%d\n", bss->freq);
-		if (ret < 0 || ret >= end - pos)
+		if (os_snprintf_error(end - pos, ret))
 			return 0;
 		pos += ret;
 	}
@@ -3521,7 +4009,7 @@
 	if (mask & WPA_BSS_MASK_BEACON_INT) {
 		ret = os_snprintf(pos, end - pos, "beacon_int=%d\n",
 				  bss->beacon_int);
-		if (ret < 0 || ret >= end - pos)
+		if (os_snprintf_error(end - pos, ret))
 			return 0;
 		pos += ret;
 	}
@@ -3529,28 +4017,28 @@
 	if (mask & WPA_BSS_MASK_CAPABILITIES) {
 		ret = os_snprintf(pos, end - pos, "capabilities=0x%04x\n",
 				  bss->caps);
-		if (ret < 0 || ret >= end - pos)
+		if (os_snprintf_error(end - pos, ret))
 			return 0;
 		pos += ret;
 	}
 
 	if (mask & WPA_BSS_MASK_QUAL) {
 		ret = os_snprintf(pos, end - pos, "qual=%d\n", bss->qual);
-		if (ret < 0 || ret >= end - pos)
+		if (os_snprintf_error(end - pos, ret))
 			return 0;
 		pos += ret;
 	}
 
 	if (mask & WPA_BSS_MASK_NOISE) {
 		ret = os_snprintf(pos, end - pos, "noise=%d\n", bss->noise);
-		if (ret < 0 || ret >= end - pos)
+		if (os_snprintf_error(end - pos, ret))
 			return 0;
 		pos += ret;
 	}
 
 	if (mask & WPA_BSS_MASK_LEVEL) {
 		ret = os_snprintf(pos, end - pos, "level=%d\n", bss->level);
-		if (ret < 0 || ret >= end - pos)
+		if (os_snprintf_error(end - pos, ret))
 			return 0;
 		pos += ret;
 	}
@@ -3558,7 +4046,7 @@
 	if (mask & WPA_BSS_MASK_TSF) {
 		ret = os_snprintf(pos, end - pos, "tsf=%016llu\n",
 				  (unsigned long long) bss->tsf);
-		if (ret < 0 || ret >= end - pos)
+		if (os_snprintf_error(end - pos, ret))
 			return 0;
 		pos += ret;
 	}
@@ -3569,34 +4057,34 @@
 		os_get_reltime(&now);
 		ret = os_snprintf(pos, end - pos, "age=%d\n",
 				  (int) (now.sec - bss->last_update.sec));
-		if (ret < 0 || ret >= end - pos)
+		if (os_snprintf_error(end - pos, ret))
 			return 0;
 		pos += ret;
 	}
 
 	if (mask & WPA_BSS_MASK_IE) {
 		ret = os_snprintf(pos, end - pos, "ie=");
-		if (ret < 0 || ret >= end - pos)
+		if (os_snprintf_error(end - pos, ret))
 			return 0;
 		pos += ret;
 
 		ie = (const u8 *) (bss + 1);
 		for (i = 0; i < bss->ie_len; i++) {
 			ret = os_snprintf(pos, end - pos, "%02x", *ie++);
-			if (ret < 0 || ret >= end - pos)
+			if (os_snprintf_error(end - pos, ret))
 				return 0;
 			pos += ret;
 		}
 
 		ret = os_snprintf(pos, end - pos, "\n");
-		if (ret < 0 || ret >= end - pos)
+		if (os_snprintf_error(end - pos, ret))
 			return 0;
 		pos += ret;
 	}
 
 	if (mask & WPA_BSS_MASK_FLAGS) {
 		ret = os_snprintf(pos, end - pos, "flags=");
-		if (ret < 0 || ret >= end - pos)
+		if (os_snprintf_error(end - pos, ret))
 			return 0;
 		pos += ret;
 
@@ -3608,17 +4096,22 @@
 		if (ie2)
 			pos = wpa_supplicant_ie_txt(pos, end, "WPA2", ie2,
 						    2 + ie2[1]);
+		osen_ie = wpa_bss_get_vendor_ie(bss, OSEN_IE_VENDOR_TYPE);
+		if (osen_ie)
+			pos = wpa_supplicant_ie_txt(pos, end, "OSEN",
+						    osen_ie, 2 + osen_ie[1]);
 		pos = wpa_supplicant_wps_ie_txt(wpa_s, pos, end, bss);
-		if (!ie && !ie2 && bss->caps & IEEE80211_CAP_PRIVACY) {
+		if (!ie && !ie2 && !osen_ie &&
+		    (bss->caps & IEEE80211_CAP_PRIVACY)) {
 			ret = os_snprintf(pos, end - pos, "[WEP]");
-			if (ret < 0 || ret >= end - pos)
+			if (os_snprintf_error(end - pos, ret))
 				return 0;
 			pos += ret;
 		}
 		if (bss_is_dmg(bss)) {
 			const char *s;
 			ret = os_snprintf(pos, end - pos, "[DMG]");
-			if (ret < 0 || ret >= end - pos)
+			if (os_snprintf_error(end - pos, ret))
 				return 0;
 			pos += ret;
 			switch (bss->caps & IEEE80211_CAP_DMG_MASK) {
@@ -3636,19 +4129,19 @@
 				break;
 			}
 			ret = os_snprintf(pos, end - pos, "%s", s);
-			if (ret < 0 || ret >= end - pos)
+			if (os_snprintf_error(end - pos, ret))
 				return 0;
 			pos += ret;
 		} else {
 			if (bss->caps & IEEE80211_CAP_IBSS) {
 				ret = os_snprintf(pos, end - pos, "[IBSS]");
-				if (ret < 0 || ret >= end - pos)
+				if (os_snprintf_error(end - pos, ret))
 					return 0;
 				pos += ret;
 			}
 			if (bss->caps & IEEE80211_CAP_ESS) {
 				ret = os_snprintf(pos, end - pos, "[ESS]");
-				if (ret < 0 || ret >= end - pos)
+				if (os_snprintf_error(end - pos, ret))
 					return 0;
 				pos += ret;
 			}
@@ -3656,21 +4149,21 @@
 		if (wpa_bss_get_vendor_ie(bss, P2P_IE_VENDOR_TYPE) ||
 		    wpa_bss_get_vendor_ie_beacon(bss, P2P_IE_VENDOR_TYPE)) {
 			ret = os_snprintf(pos, end - pos, "[P2P]");
-			if (ret < 0 || ret >= end - pos)
+			if (os_snprintf_error(end - pos, ret))
 				return 0;
 			pos += ret;
 		}
 #ifdef CONFIG_HS20
 		if (wpa_bss_get_vendor_ie(bss, HS20_IE_VENDOR_TYPE)) {
 			ret = os_snprintf(pos, end - pos, "[HS20]");
-			if (ret < 0 || ret >= end - pos)
+			if (os_snprintf_error(end - pos, ret))
 				return 0;
 			pos += ret;
 		}
 #endif /* CONFIG_HS20 */
 
 		ret = os_snprintf(pos, end - pos, "\n");
-		if (ret < 0 || ret >= end - pos)
+		if (os_snprintf_error(end - pos, ret))
 			return 0;
 		pos += ret;
 	}
@@ -3678,7 +4171,7 @@
 	if (mask & WPA_BSS_MASK_SSID) {
 		ret = os_snprintf(pos, end - pos, "ssid=%s\n",
 				  wpa_ssid_txt(bss->ssid, bss->ssid_len));
-		if (ret < 0 || ret >= end - pos)
+		if (os_snprintf_error(end - pos, ret))
 			return 0;
 		pos += ret;
 	}
@@ -3711,7 +4204,7 @@
 						  WFD_IE_VENDOR_TYPE);
 		if (wfd) {
 			ret = os_snprintf(pos, end - pos, "wfd_subelems=");
-			if (ret < 0 || ret >= end - pos) {
+			if (os_snprintf_error(end - pos, ret)) {
 				wpabuf_free(wfd);
 				return 0;
 			}
@@ -3723,7 +4216,7 @@
 			wpabuf_free(wfd);
 
 			ret = os_snprintf(pos, end - pos, "\n");
-			if (ret < 0 || ret >= end - pos)
+			if (os_snprintf_error(end - pos, ret))
 				return 0;
 			pos += ret;
 		}
@@ -3733,6 +4226,8 @@
 #ifdef CONFIG_INTERWORKING
 	if ((mask & WPA_BSS_MASK_INTERNETW) && bss->anqp) {
 		struct wpa_bss_anqp *anqp = bss->anqp;
+		pos = anqp_add_hex(pos, end, "anqp_capability_list",
+				   anqp->capability_list);
 		pos = anqp_add_hex(pos, end, "anqp_venue_name",
 				   anqp->venue_name);
 		pos = anqp_add_hex(pos, end, "anqp_network_auth_type",
@@ -3747,6 +4242,8 @@
 		pos = anqp_add_hex(pos, end, "anqp_domain_name",
 				   anqp->domain_name);
 #ifdef CONFIG_HS20
+		pos = anqp_add_hex(pos, end, "hs20_capability_list",
+				   anqp->hs20_capability_list);
 		pos = anqp_add_hex(pos, end, "hs20_operator_friendly_name",
 				   anqp->hs20_operator_friendly_name);
 		pos = anqp_add_hex(pos, end, "hs20_wan_metrics",
@@ -3761,9 +4258,34 @@
 	}
 #endif /* CONFIG_INTERWORKING */
 
+#ifdef CONFIG_MESH
+	if (mask & WPA_BSS_MASK_MESH_SCAN) {
+		ie = (const u8 *) (bss + 1);
+		ret = wpas_mesh_scan_result_text(ie, bss->ie_len, pos, end);
+		if (ret < 0 || ret >= end - pos)
+			return 0;
+		pos += ret;
+	}
+#endif /* CONFIG_MESH */
+
+	if (mask & WPA_BSS_MASK_SNR) {
+		ret = os_snprintf(pos, end - pos, "snr=%d\n", bss->snr);
+		if (os_snprintf_error(end - pos, ret))
+			return 0;
+		pos += ret;
+	}
+
+	if (mask & WPA_BSS_MASK_EST_THROUGHPUT) {
+		ret = os_snprintf(pos, end - pos, "est_throughput=%d\n",
+				  bss->est_throughput);
+		if (os_snprintf_error(end - pos, ret))
+			return 0;
+		pos += ret;
+	}
+
 	if (mask & WPA_BSS_MASK_DELIM) {
 		ret = os_snprintf(pos, end - pos, "====\n");
-		if (ret < 0 || ret >= end - pos)
+		if (os_snprintf_error(end - pos, ret))
 			return 0;
 		pos += ret;
 	}
@@ -3783,7 +4305,7 @@
 	struct dl_list *next;
 	int ret = 0;
 	int len;
-	char *ctmp;
+	char *ctmp, *end = buf + buflen;
 	unsigned long mask = WPA_BSS_MASK_ALL;
 
 	if (os_strncmp(cmd, "RANGE=", 6) == 0) {
@@ -3892,8 +4414,16 @@
 		if (bss == bsslast) {
 			if ((mask & WPA_BSS_MASK_DELIM) && len &&
 			    (bss == dl_list_last(&wpa_s->bss_id,
-						 struct wpa_bss, list_id)))
-				os_snprintf(buf - 5, 5, "####\n");
+						 struct wpa_bss, list_id))) {
+				int res;
+
+				res = os_snprintf(buf - 5, end - buf + 5,
+						  "####\n");
+				if (os_snprintf_error(end - buf + 5, res)) {
+					wpa_printf(MSG_DEBUG,
+						   "Could not add end delim");
+				}
+			}
 			break;
 		}
 		next = bss->list_id.next;
@@ -3938,7 +4468,7 @@
 }
 
 
-static int wpa_supplicant_ctrl_iface_bss_flush(
+static void wpa_supplicant_ctrl_iface_bss_flush(
 	struct wpa_supplicant *wpa_s, char *cmd)
 {
 	int flush_age = atoi(cmd);
@@ -3947,7 +4477,6 @@
 		wpa_bss_flush(wpa_s);
 	else
 		wpa_bss_flush_by_age(wpa_s, flush_age);
-	return 0;
 }
 
 
@@ -4029,6 +4558,9 @@
 	u8 dev_type[WPS_DEV_TYPE_LEN], *_dev_type = NULL;
 	char *pos;
 	unsigned int search_delay;
+	const char *_seek[P2P_MAX_QUERY_HASH + 1], **seek = NULL;
+	u8 seek_count = 0;
+	int freq = 0;
 
 	if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED) {
 		wpa_dbg(wpa_s, MSG_INFO,
@@ -4063,8 +4595,185 @@
 	} else
 		search_delay = wpas_p2p_search_delay(wpa_s);
 
+	pos = os_strstr(cmd, "freq=");
+	if (pos) {
+		pos += 5;
+		freq = atoi(pos);
+		if (freq <= 0)
+			return -1;
+	}
+
+	/* Must be searched for last, because it adds nul termination */
+	pos = os_strstr(cmd, " seek=");
+	if (pos)
+		pos += 6;
+	while (pos && seek_count < P2P_MAX_QUERY_HASH + 1) {
+		char *term;
+
+		_seek[seek_count++] = pos;
+		seek = _seek;
+		term = os_strchr(pos, ' ');
+		if (!term)
+			break;
+		*term = '\0';
+		pos = os_strstr(term + 1, "seek=");
+		if (pos)
+			pos += 5;
+	}
+	if (seek_count > P2P_MAX_QUERY_HASH) {
+		seek[0] = NULL;
+		seek_count = 1;
+	}
+
 	return wpas_p2p_find(wpa_s, timeout, type, _dev_type != NULL, _dev_type,
-			     _dev_id, search_delay);
+			     _dev_id, search_delay, seek_count, seek, freq);
+}
+
+
+static struct p2ps_provision * p2p_parse_asp_provision_cmd(const char *cmd)
+{
+	struct p2ps_provision *p2ps_prov;
+	char *pos;
+	size_t info_len = 0;
+	char *info = NULL;
+	u8 role = P2PS_SETUP_NONE;
+	long long unsigned val;
+
+	pos = os_strstr(cmd, "info=");
+	if (pos) {
+		pos += 5;
+		info_len = os_strlen(pos);
+
+		if (info_len) {
+			info = os_malloc(info_len + 1);
+			if (info) {
+				info_len = utf8_unescape(pos, info_len,
+							 info, info_len + 1);
+			} else
+				info_len = 0;
+		}
+	}
+
+	p2ps_prov = os_zalloc(sizeof(struct p2ps_provision) + info_len + 1);
+	if (p2ps_prov == NULL) {
+		os_free(info);
+		return NULL;
+	}
+
+	if (info) {
+		os_memcpy(p2ps_prov->info, info, info_len);
+		p2ps_prov->info[info_len] = '\0';
+		os_free(info);
+	}
+
+	pos = os_strstr(cmd, "status=");
+	if (pos)
+		p2ps_prov->status = atoi(pos + 7);
+	else
+		p2ps_prov->status = -1;
+
+	pos = os_strstr(cmd, "adv_id=");
+	if (!pos || sscanf(pos + 7, "%llx", &val) != 1 || val > 0xffffffffULL)
+		goto invalid_args;
+	p2ps_prov->adv_id = val;
+
+	pos = os_strstr(cmd, "method=");
+	if (pos)
+		p2ps_prov->method = strtol(pos + 7, NULL, 16);
+	else
+		p2ps_prov->method = 0;
+
+	pos = os_strstr(cmd, "session=");
+	if (!pos || sscanf(pos + 8, "%llx", &val) != 1 || val > 0xffffffffULL)
+		goto invalid_args;
+	p2ps_prov->session_id = val;
+
+	pos = os_strstr(cmd, "adv_mac=");
+	if (!pos || hwaddr_aton(pos + 8, p2ps_prov->adv_mac))
+		goto invalid_args;
+
+	pos = os_strstr(cmd, "session_mac=");
+	if (!pos || hwaddr_aton(pos + 12, p2ps_prov->session_mac))
+		goto invalid_args;
+
+	/* force conncap with tstCap (no sanity checks) */
+	pos = os_strstr(cmd, "tstCap=");
+	if (pos) {
+		role = strtol(pos + 7, NULL, 16);
+	} else {
+		pos = os_strstr(cmd, "role=");
+		if (pos) {
+			role = strtol(pos + 5, NULL, 16);
+			if (role != P2PS_SETUP_CLIENT &&
+			    role != P2PS_SETUP_GROUP_OWNER)
+				role = P2PS_SETUP_NONE;
+		}
+	}
+	p2ps_prov->role = role;
+
+	return p2ps_prov;
+
+invalid_args:
+	os_free(p2ps_prov);
+	return NULL;
+}
+
+
+static int p2p_ctrl_asp_provision_resp(struct wpa_supplicant *wpa_s, char *cmd)
+{
+	u8 addr[ETH_ALEN];
+	struct p2ps_provision *p2ps_prov;
+	char *pos;
+
+	/* <addr> id=<adv_id> [role=<conncap>] [info=<infodata>] */
+
+	wpa_printf(MSG_DEBUG, "%s: %s", __func__, cmd);
+
+	if (hwaddr_aton(cmd, addr))
+		return -1;
+
+	pos = cmd + 17;
+	if (*pos != ' ')
+		return -1;
+
+	p2ps_prov = p2p_parse_asp_provision_cmd(pos);
+	if (!p2ps_prov)
+		return -1;
+
+	if (p2ps_prov->status < 0) {
+		os_free(p2ps_prov);
+		return -1;
+	}
+
+	return wpas_p2p_prov_disc(wpa_s, addr, NULL, WPAS_P2P_PD_FOR_ASP,
+				  p2ps_prov);
+}
+
+
+static int p2p_ctrl_asp_provision(struct wpa_supplicant *wpa_s, char *cmd)
+{
+	u8 addr[ETH_ALEN];
+	struct p2ps_provision *p2ps_prov;
+	char *pos;
+
+	/* <addr> id=<adv_id> adv_mac=<adv_mac> conncap=<conncap>
+	 *        session=<ses_id> mac=<ses_mac> [info=<infodata>]
+	 */
+
+	wpa_printf(MSG_DEBUG, "%s: %s", __func__, cmd);
+	if (hwaddr_aton(cmd, addr))
+		return -1;
+
+	pos = cmd + 17;
+	if (*pos != ' ')
+		return -1;
+
+	p2ps_prov = p2p_parse_asp_provision_cmd(pos);
+	if (!p2ps_prov)
+		return -1;
+
+	return wpas_p2p_prov_disc(wpa_s, addr, NULL, WPAS_P2P_PD_FOR_ASP,
+				  p2ps_prov);
 }
 
 
@@ -4086,10 +4795,18 @@
 	int pd;
 	int ht40, vht;
 
-	/* <addr> <"pbc" | "pin" | PIN> [label|display|keypad]
+	if (!wpa_s->global->p2p_init_wpa_s)
+		return -1;
+	if (wpa_s->global->p2p_init_wpa_s != wpa_s) {
+		wpa_dbg(wpa_s, MSG_DEBUG, "Direct P2P_CONNECT command to %s",
+			wpa_s->global->p2p_init_wpa_s->ifname);
+		wpa_s = wpa_s->global->p2p_init_wpa_s;
+	}
+
+	/* <addr> <"pbc" | "pin" | PIN> [label|display|keypad|p2ps]
 	 * [persistent|persistent=<network id>]
 	 * [join] [auth] [go_intent=<0..15>] [freq=<in MHz>] [provdisc]
-	 * [ht40] [vht] */
+	 * [ht40] [vht] [auto] */
 
 	if (hwaddr_aton(cmd, addr))
 		return -1;
@@ -4150,6 +4867,8 @@
 			*pos++ = '\0';
 			if (os_strncmp(pos, "display", 7) == 0)
 				wps_method = WPS_PIN_DISPLAY;
+			else if (os_strncmp(pos, "p2ps", 4) == 0)
+				wps_method = WPS_P2PS;
 		}
 		if (!wps_pin_str_valid(pin)) {
 			os_memcpy(buf, "FAIL-INVALID-PIN\n", 17);
@@ -4173,7 +4892,7 @@
 		return -1;
 	if (wps_method == WPS_PIN_DISPLAY && pin == NULL) {
 		ret = os_snprintf(buf, buflen, "%08d", new_pin);
-		if (ret < 0 || (size_t) ret >= buflen)
+		if (os_snprintf_error(buflen, ret))
 			return -1;
 		return ret;
 	}
@@ -4216,7 +4935,7 @@
 	else if (os_strstr(pos, " auto") != NULL)
 		use = WPAS_P2P_PD_AUTO;
 
-	return wpas_p2p_prov_disc(wpa_s, addr, pos, use);
+	return wpas_p2p_prov_disc(wpa_s, addr, pos, use, NULL);
 }
 
 
@@ -4269,6 +4988,40 @@
 	} else if (os_strncmp(pos, "wifi-display ", 13) == 0) {
 		ref = wpas_p2p_sd_request_wifi_display(wpa_s, dst, pos + 13);
 #endif /* CONFIG_WIFI_DISPLAY */
+	} else if (os_strncmp(pos, "asp ", 4) == 0) {
+		char *svc_str;
+		char *svc_info = NULL;
+		u32 id;
+
+		pos += 4;
+		if (sscanf(pos, "%x", &id) != 1 || id > 0xff)
+			return -1;
+
+		pos = os_strchr(pos, ' ');
+		if (pos == NULL || pos[1] == '\0' || pos[1] == ' ')
+			return -1;
+
+		svc_str = pos + 1;
+
+		pos = os_strchr(svc_str, ' ');
+
+		if (pos)
+			*pos++ = '\0';
+
+		/* All remaining data is the svc_info string */
+		if (pos && pos[0] && pos[0] != ' ') {
+			len = os_strlen(pos);
+
+			/* Unescape in place */
+			len = utf8_unescape(pos, len, pos, len);
+			if (len > 0xff)
+				return -1;
+
+			svc_info = pos;
+		}
+
+		ref = wpas_p2p_sd_request_asp(wpa_s, dst, (u8) id,
+					      svc_str, svc_info);
 	} else {
 		len = os_strlen(pos);
 		if (len & 1)
@@ -4288,7 +5041,7 @@
 	if (ref == 0)
 		return -1;
 	res = os_snprintf(buf, buflen, "%llx", (long long unsigned) ref);
-	if (res < 0 || (unsigned) res >= buflen)
+	if (os_snprintf_error(buflen, res))
 		return -1;
 	return res;
 }
@@ -4431,6 +5184,106 @@
 }
 
 
+static int p2p_ctrl_service_add_asp(struct wpa_supplicant *wpa_s,
+				    u8 replace, char *cmd)
+{
+	char *pos;
+	char *adv_str;
+	u32 auto_accept, adv_id, svc_state, config_methods;
+	char *svc_info = NULL;
+
+	pos = os_strchr(cmd, ' ');
+	if (pos == NULL)
+		return -1;
+	*pos++ = '\0';
+
+	/* Auto-Accept value is mandatory, and must be one of the
+	 * single values (0, 1, 2, 4) */
+	auto_accept = atoi(cmd);
+	switch (auto_accept) {
+	case P2PS_SETUP_NONE: /* No auto-accept */
+	case P2PS_SETUP_NEW:
+	case P2PS_SETUP_CLIENT:
+	case P2PS_SETUP_GROUP_OWNER:
+		break;
+	default:
+		return -1;
+	}
+
+	/* Advertisement ID is mandatory */
+	cmd = pos;
+	pos = os_strchr(cmd, ' ');
+	if (pos == NULL)
+		return -1;
+	*pos++ = '\0';
+
+	/* Handle Adv_ID == 0 (wildcard "org.wi-fi.wfds") internally. */
+	if (sscanf(cmd, "%x", &adv_id) != 1 || adv_id == 0)
+		return -1;
+
+	/* Only allow replacements if exist, and adds if not */
+	if (wpas_p2p_service_p2ps_id_exists(wpa_s, adv_id)) {
+		if (!replace)
+			return -1;
+	} else {
+		if (replace)
+			return -1;
+	}
+
+	/* svc_state between 0 - 0xff is mandatory */
+	if (sscanf(pos, "%x", &svc_state) != 1 || svc_state > 0xff)
+		return -1;
+
+	pos = os_strchr(pos, ' ');
+	if (pos == NULL)
+		return -1;
+
+	/* config_methods is mandatory */
+	pos++;
+	if (sscanf(pos, "%x", &config_methods) != 1)
+		return -1;
+
+	if (!(config_methods &
+	      (WPS_CONFIG_DISPLAY | WPS_CONFIG_KEYPAD | WPS_CONFIG_P2PS)))
+		return -1;
+
+	pos = os_strchr(pos, ' ');
+	if (pos == NULL)
+		return -1;
+
+	pos++;
+	adv_str = pos;
+
+	/* Advertisement string is mandatory */
+	if (!pos[0] || pos[0] == ' ')
+		return -1;
+
+	/* Terminate svc string */
+	pos = os_strchr(pos, ' ');
+	if (pos != NULL)
+		*pos++ = '\0';
+
+	/* Service and Response Information are optional */
+	if (pos && pos[0]) {
+		size_t len;
+
+		/* Note the bare ' included, which cannot exist legally
+		 * in unescaped string. */
+		svc_info = os_strstr(pos, "svc_info='");
+
+		if (svc_info) {
+			svc_info += 9;
+			len = os_strlen(svc_info);
+			utf8_unescape(svc_info, len, svc_info, len);
+		}
+	}
+
+	return wpas_p2p_service_add_asp(wpa_s, auto_accept, adv_id, adv_str,
+					(u8) svc_state, (u16) config_methods,
+					svc_info);
+}
+
+
 static int p2p_ctrl_service_add(struct wpa_supplicant *wpa_s, char *cmd)
 {
 	char *pos;
@@ -4444,6 +5297,8 @@
 		return p2p_ctrl_service_add_bonjour(wpa_s, pos);
 	if (os_strcmp(cmd, "upnp") == 0)
 		return p2p_ctrl_service_add_upnp(wpa_s, pos);
+	if (os_strcmp(cmd, "asp") == 0)
+		return p2p_ctrl_service_add_asp(wpa_s, 0, pos);
 	wpa_printf(MSG_DEBUG, "Unknown service '%s'", cmd);
 	return -1;
 }
@@ -4491,6 +5346,22 @@
 }
 
 
+static int p2p_ctrl_service_del_asp(struct wpa_supplicant *wpa_s, char *cmd)
+{
+	u32 adv_id;
+
+	if (os_strcmp(cmd, "all") == 0) {
+		wpas_p2p_service_flush_asp(wpa_s);
+		return 0;
+	}
+
+	if (sscanf(cmd, "%x", &adv_id) != 1)
+		return -1;
+
+	return wpas_p2p_service_del_asp(wpa_s, adv_id);
+}
+
+
 static int p2p_ctrl_service_del(struct wpa_supplicant *wpa_s, char *cmd)
 {
 	char *pos;
@@ -4504,6 +5375,25 @@
 		return p2p_ctrl_service_del_bonjour(wpa_s, pos);
 	if (os_strcmp(cmd, "upnp") == 0)
 		return p2p_ctrl_service_del_upnp(wpa_s, pos);
+	if (os_strcmp(cmd, "asp") == 0)
+		return p2p_ctrl_service_del_asp(wpa_s, pos);
+	wpa_printf(MSG_DEBUG, "Unknown service '%s'", cmd);
+	return -1;
+}
+
+
+static int p2p_ctrl_service_replace(struct wpa_supplicant *wpa_s, char *cmd)
+{
+	char *pos;
+
+	pos = os_strchr(cmd, ' ');
+	if (pos == NULL)
+		return -1;
+	*pos++ = '\0';
+
+	if (os_strcmp(cmd, "asp") == 0)
+		return p2p_ctrl_service_add_asp(wpa_s, 1, pos);
+
 	wpa_printf(MSG_DEBUG, "Unknown service '%s'", cmd);
 	return -1;
 }
@@ -4615,13 +5505,10 @@
 
 
 static int p2p_ctrl_group_add_persistent(struct wpa_supplicant *wpa_s,
-					 char *cmd, int freq, int ht40,
-					 int vht)
+					 int id, int freq, int ht40, int vht)
 {
-	int id;
 	struct wpa_ssid *ssid;
 
-	id = atoi(cmd);
 	ssid = wpa_config_get_network(wpa_s->conf, id);
 	if (ssid == NULL || ssid->disabled != 2) {
 		wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find SSID id=%d "
@@ -4637,31 +5524,35 @@
 
 static int p2p_ctrl_group_add(struct wpa_supplicant *wpa_s, char *cmd)
 {
-	int freq = 0, ht40, vht;
-	char *pos;
+	int freq = 0, persistent = 0, group_id = -1;
+	int vht = wpa_s->conf->p2p_go_vht;
+	int ht40 = wpa_s->conf->p2p_go_ht40 || vht;
+	char *token, *context = NULL;
 
-	pos = os_strstr(cmd, "freq=");
-	if (pos)
-		freq = atoi(pos + 5);
+	while ((token = str_token(cmd, " ", &context))) {
+		if (sscanf(token, "freq=%d", &freq) == 1 ||
+		    sscanf(token, "persistent=%d", &group_id) == 1) {
+			continue;
+		} else if (os_strcmp(token, "ht40") == 0) {
+			ht40 = 1;
+		} else if (os_strcmp(token, "vht") == 0) {
+			vht = 1;
+			ht40 = 1;
+		} else if (os_strcmp(token, "persistent") == 0) {
+			persistent = 1;
+		} else {
+			wpa_printf(MSG_DEBUG,
+				   "CTRL: Invalid P2P_GROUP_ADD parameter: '%s'",
+				   token);
+			return -1;
+		}
+	}
 
-	vht = (os_strstr(cmd, "vht") != NULL) || wpa_s->conf->p2p_go_vht;
-	ht40 = (os_strstr(cmd, "ht40") != NULL) || wpa_s->conf->p2p_go_ht40 ||
-		vht;
+	if (group_id >= 0)
+		return p2p_ctrl_group_add_persistent(wpa_s, group_id,
+						     freq, ht40, vht);
 
-	if (os_strncmp(cmd, "persistent=", 11) == 0)
-		return p2p_ctrl_group_add_persistent(wpa_s, cmd + 11, freq,
-						     ht40, vht);
-	if (os_strcmp(cmd, "persistent") == 0 ||
-	    os_strncmp(cmd, "persistent ", 11) == 0)
-		return wpas_p2p_group_add(wpa_s, 1, freq, ht40, vht);
-	if (os_strncmp(cmd, "freq=", 5) == 0)
-		return wpas_p2p_group_add(wpa_s, 0, freq, ht40, vht);
-	if (ht40)
-		return wpas_p2p_group_add(wpa_s, 0, freq, ht40, vht);
-
-	wpa_printf(MSG_DEBUG, "CTRL: Invalid P2P_GROUP_ADD parameters '%s'",
-		   cmd);
-	return -1;
+	return wpas_p2p_group_add(wpa_s, persistent, freq, ht40, vht);
 }
 
 
@@ -4724,7 +5615,7 @@
 			  info->dev_capab,
 			  info->group_capab,
 			  info->level);
-	if (res < 0 || res >= end - pos)
+	if (os_snprintf_error(end - pos, res))
 		return pos - buf;
 	pos += res;
 
@@ -4735,7 +5626,7 @@
 		res = os_snprintf(pos, end - pos, "sec_dev_type=%s\n",
 				  wps_dev_type_bin2str(t, devtype,
 						       sizeof(devtype)));
-		if (res < 0 || res >= end - pos)
+		if (os_snprintf_error(end - pos, res))
 			return pos - buf;
 		pos += res;
 	}
@@ -4743,7 +5634,7 @@
 	ssid = wpas_p2p_get_persistent(wpa_s, info->p2p_device_addr, NULL, 0);
 	if (ssid) {
 		res = os_snprintf(pos, end - pos, "persistent=%d\n", ssid->id);
-		if (res < 0 || res >= end - pos)
+		if (os_snprintf_error(end - pos, res))
 			return pos - buf;
 		pos += res;
 	}
@@ -4755,7 +5646,7 @@
 
 	if (info->vendor_elems) {
 		res = os_snprintf(pos, end - pos, "vendor_elems=");
-		if (res < 0 || res >= end - pos)
+		if (os_snprintf_error(end - pos, res))
 			return pos - buf;
 		pos += res;
 
@@ -4764,7 +5655,7 @@
 					wpabuf_len(info->vendor_elems));
 
 		res = os_snprintf(pos, end - pos, "\n");
-		if (res < 0 || res >= end - pos)
+		if (os_snprintf_error(end - pos, res))
 			return pos - buf;
 		pos += res;
 	}
@@ -5007,6 +5898,7 @@
 {
 	os_memset(wpa_s->p2p_auth_invite, 0, ETH_ALEN);
 	wpa_s->force_long_sd = 0;
+	wpas_p2p_stop_find(wpa_s);
 	if (wpa_s->global->p2p)
 		p2p_flush(wpa_s->global->p2p);
 }
@@ -5145,7 +6037,8 @@
 }
 
 
-static int ctrl_interworking_connect(struct wpa_supplicant *wpa_s, char *dst)
+static int ctrl_interworking_connect(struct wpa_supplicant *wpa_s, char *dst,
+				     int only_add)
 {
 	u8 bssid[ETH_ALEN];
 	struct wpa_bss *bss;
@@ -5162,7 +6055,28 @@
 		return -1;
 	}
 
-	return interworking_connect(wpa_s, bss);
+	if (bss->ssid_len == 0) {
+		int found = 0;
+
+		wpa_printf(MSG_DEBUG, "Selected BSS entry for " MACSTR
+			   " does not have SSID information", MAC2STR(bssid));
+
+		dl_list_for_each_reverse(bss, &wpa_s->bss, struct wpa_bss,
+					 list) {
+			if (os_memcmp(bss->bssid, bssid, ETH_ALEN) == 0 &&
+			    bss->ssid_len > 0) {
+				found = 1;
+				break;
+			}
+		}
+
+		if (!found)
+			return -1;
+		wpa_printf(MSG_DEBUG,
+			   "Found another matching BSS entry with SSID");
+	}
+
+	return interworking_connect(wpa_s, bss, only_add);
 }
 
 
@@ -5180,6 +6094,8 @@
 	if (used < 0)
 		return -1;
 	pos = dst + used;
+	if (*pos == ' ')
+		pos++;
 	while (num_id < MAX_ANQP_INFO_ID) {
 		if (os_strncmp(pos, "hs20:", 5) == 0) {
 #ifdef CONFIG_HS20
@@ -5359,6 +6275,8 @@
 	if (used < 0)
 		return -1;
 	pos = dst + used;
+	if (*pos == ' ')
+		pos++;
 	for (;;) {
 		int num = atoi(pos);
 		if (num <= 0 || num > 31)
@@ -5471,14 +6389,6 @@
 #endif /* CONFIG_HS20 */
 
 
-static int wpa_supplicant_ctrl_iface_sta_autoconnect(
-	struct wpa_supplicant *wpa_s, char *cmd)
-{
-	wpa_s->auto_reconnect_disabled = atoi(cmd) == 0 ? 1 : 0;
-	return 0;
-}
-
-
 #ifdef CONFIG_AUTOSCAN
 
 static int wpa_supplicant_ctrl_iface_autoscan(struct wpa_supplicant *wpa_s,
@@ -5594,14 +6504,14 @@
 			  "NOISE=%d\nFREQUENCY=%u\n",
 			  si.current_signal, si.current_txrate / 1000,
 			  si.current_noise, si.frequency);
-	if (ret < 0 || ret > end - pos)
+	if (os_snprintf_error(end - pos, ret))
 		return -1;
 	pos += ret;
 
 	if (si.chanwidth != CHAN_WIDTH_UNKNOWN) {
 		ret = os_snprintf(pos, end - pos, "WIDTH=%s\n",
 				  channel_width_to_string(si.chanwidth));
-		if (ret < 0 || ret > end - pos)
+		if (os_snprintf_error(end - pos, ret))
 			return -1;
 		pos += ret;
 	}
@@ -5610,7 +6520,7 @@
 		ret = os_snprintf(pos, end - pos,
 				  "CENTER_FRQ1=%d\nCENTER_FRQ2=%d\n",
 				  si.center_frq1, si.center_frq2);
-		if (ret < 0 || ret > end - pos)
+		if (os_snprintf_error(end - pos, ret))
 			return -1;
 		pos += ret;
 	}
@@ -5618,7 +6528,15 @@
 	if (si.avg_signal) {
 		ret = os_snprintf(pos, end - pos,
 				  "AVG_RSSI=%d\n", si.avg_signal);
-		if (ret < 0 || ret >= end - pos)
+		if (os_snprintf_error(end - pos, ret))
+			return -1;
+		pos += ret;
+	}
+
+	if (si.avg_beacon_signal) {
+		ret = os_snprintf(pos, end - pos,
+				  "AVG_BEACON_RSSI=%d\n", si.avg_beacon_signal);
+		if (os_snprintf_error(end - pos, ret))
 			return -1;
 		pos += ret;
 	}
@@ -5639,7 +6557,7 @@
 
 	ret = os_snprintf(buf, buflen, "TXGOOD=%lu\nTXBAD=%lu\nRXGOOD=%lu\n",
 			  sta.tx_packets, sta.tx_retry_failed, sta.rx_packets);
-	if (ret < 0 || (size_t) ret > buflen)
+	if (os_snprintf_error(buflen, ret))
 		return -1;
 	return ret;
 }
@@ -5664,6 +6582,8 @@
 			}
 		}
 		ret = os_snprintf(buf, buflen, "%s\n", "OK");
+		if (os_snprintf_error(buflen, ret))
+			ret = -1;
 	}
 	return ret;
 }
@@ -5729,20 +6649,25 @@
 
 static void wpa_supplicant_ctrl_iface_flush(struct wpa_supplicant *wpa_s)
 {
+#ifdef CONFIG_P2P
+	struct wpa_supplicant *p2p_wpa_s = wpa_s->global->p2p_init_wpa_s ?
+		wpa_s->global->p2p_init_wpa_s : wpa_s;
+#endif /* CONFIG_P2P */
+
 	wpa_dbg(wpa_s, MSG_DEBUG, "Flush all wpa_supplicant state");
 
 #ifdef CONFIG_P2P
-	wpas_p2p_cancel(wpa_s);
-	wpas_p2p_stop_find(wpa_s);
-	p2p_ctrl_flush(wpa_s);
-	wpas_p2p_group_remove(wpa_s, "*");
-	wpas_p2p_service_flush(wpa_s);
-	wpa_s->global->p2p_disabled = 0;
-	wpa_s->global->p2p_per_sta_psk = 0;
-	wpa_s->conf->num_sec_device_types = 0;
-	wpa_s->p2p_disable_ip_addr_req = 0;
-	os_free(wpa_s->global->p2p_go_avoid_freq.range);
-	wpa_s->global->p2p_go_avoid_freq.range = NULL;
+	wpas_p2p_cancel(p2p_wpa_s);
+	p2p_ctrl_flush(p2p_wpa_s);
+	wpas_p2p_group_remove(p2p_wpa_s, "*");
+	wpas_p2p_service_flush(p2p_wpa_s);
+	p2p_wpa_s->global->p2p_disabled = 0;
+	p2p_wpa_s->global->p2p_per_sta_psk = 0;
+	p2p_wpa_s->conf->num_sec_device_types = 0;
+	p2p_wpa_s->p2p_disable_ip_addr_req = 0;
+	os_free(p2p_wpa_s->global->p2p_go_avoid_freq.range);
+	p2p_wpa_s->global->p2p_go_avoid_freq.range = NULL;
+	p2p_wpa_s->global->pending_p2ps_group = 0;
 #endif /* CONFIG_P2P */
 
 #ifdef CONFIG_WPS_TESTING
@@ -5753,6 +6678,7 @@
 #ifdef CONFIG_WPS
 	wpa_s->wps_fragment_size = 0;
 	wpas_wps_cancel(wpa_s);
+	wps_registrar_flush(wpa_s->wps->registrar);
 #endif /* CONFIG_WPS */
 	wpa_s->after_wps = 0;
 	wpa_s->known_wps_freq = 0;
@@ -5770,6 +6696,7 @@
 	wpa_supplicant_stop_countermeasures(wpa_s, NULL);
 
 	wpa_s->no_keep_alive = 0;
+	wpa_s->own_disconnect_req = 0;
 
 	os_free(wpa_s->disallow_aps_bssid);
 	wpa_s->disallow_aps_bssid = NULL;
@@ -5782,8 +6709,6 @@
 	wpa_s->sta_uapsd = 0;
 
 	wpa_drv_radio_disable(wpa_s, 0);
-
-	wpa_bss_flush(wpa_s);
 	wpa_blacklist_clear(wpa_s);
 	wpa_s->extra_blacklist_count = 0;
 	wpa_supplicant_ctrl_iface_remove_network(wpa_s, "all");
@@ -5792,12 +6717,16 @@
 	wpa_s->conf->auto_interworking = 0;
 	wpa_s->conf->okc = 0;
 
+	wpa_sm_pmksa_cache_flush(wpa_s->wpa, NULL);
+	rsn_preauth_deinit(wpa_s->wpa);
+
 	wpa_sm_set_param(wpa_s->wpa, RSNA_PMK_LIFETIME, 43200);
 	wpa_sm_set_param(wpa_s->wpa, RSNA_PMK_REAUTH_THRESHOLD, 70);
 	wpa_sm_set_param(wpa_s->wpa, RSNA_SA_TIMEOUT, 60);
 	eapol_sm_notify_logoff(wpa_s->eapol, FALSE);
 
 	radio_remove_works(wpa_s, NULL, 1);
+	wpa_s->ext_work_in_progress = 0;
 
 	wpa_s->next_ssid = NULL;
 
@@ -5806,6 +6735,27 @@
 #endif /* CONFIG_INTERWORKING */
 
 	wpa_s->ext_mgmt_frame_handling = 0;
+	wpa_s->ext_eapol_frame_io = 0;
+#ifdef CONFIG_TESTING_OPTIONS
+	wpa_s->extra_roc_dur = 0;
+	wpa_s->test_failure = WPAS_TEST_FAILURE_NONE;
+#endif /* CONFIG_TESTING_OPTIONS */
+
+	wpa_s->disconnected = 0;
+	os_free(wpa_s->next_scan_freqs);
+	wpa_s->next_scan_freqs = NULL;
+
+	wpa_bss_flush(wpa_s);
+	if (!dl_list_empty(&wpa_s->bss)) {
+		wpa_printf(MSG_DEBUG,
+			   "BSS table not empty after flush: %u entries, current_bss=%p bssid="
+			   MACSTR " pending_bssid=" MACSTR,
+			   dl_list_len(&wpa_s->bss), wpa_s->current_bss,
+			   MAC2STR(wpa_s->bssid),
+			   MAC2STR(wpa_s->pending_bssid));
+	}
+
+	eloop_cancel_timeout(wpas_network_reenabled, wpa_s, NULL);
 }
 
 
@@ -5829,7 +6779,7 @@
 		ret = os_snprintf(pos, end - pos, "%s@%s:%u:%u:%ld.%06ld\n",
 				  work->type, work->wpa_s->ifname, work->freq,
 				  work->started, diff.sec, diff.usec);
-		if (ret < 0 || ret >= end - pos)
+		if (os_snprintf_error(end - pos, ret))
 			break;
 		pos += ret;
 	}
@@ -5847,6 +6797,7 @@
 		"Timing out external radio work %u (%s)",
 		ework->id, work->type);
 	wpa_msg(work->wpa_s, MSG_INFO, EXT_RADIO_WORK_TIMEOUT "%u", ework->id);
+	work->wpa_s->ext_work_in_progress = 0;
 	radio_work_done(work);
 	os_free(ework);
 }
@@ -5868,6 +6819,7 @@
 	wpa_dbg(work->wpa_s, MSG_DEBUG, "Starting external radio work %u (%s)",
 		ework->id, ework->type);
 	wpa_msg(work->wpa_s, MSG_INFO, EXT_RADIO_WORK_START "%u", ework->id);
+	work->wpa_s->ext_work_in_progress = 1;
 	if (!ework->timeout)
 		ework->timeout = 10;
 	eloop_register_timeout(ework->timeout, 0, wpas_ctrl_radio_work_timeout,
@@ -5923,7 +6875,7 @@
 	}
 
 	ret = os_snprintf(buf, buflen, "%u", ework->id);
-	if (ret < 0 || (size_t) ret >= buflen)
+	if (os_snprintf_error(buflen, ret))
 		return -1;
 	return ret;
 }
@@ -5947,6 +6899,7 @@
 			"Completed external radio work %u (%s)",
 			ework->id, ework->type);
 		eloop_cancel_timeout(wpas_ctrl_radio_work_timeout, work, NULL);
+		wpa_s->ext_work_in_progress = 0;
 		radio_work_done(work);
 		os_free(ework);
 		return 3; /* "OK\n" */
@@ -6003,31 +6956,17 @@
 }
 
 
-static int set_scan_freqs(struct wpa_supplicant *wpa_s, char *val)
-{
-	int *freqs = NULL;
-
-	freqs = freq_range_to_channel_list(wpa_s, val);
-	if (freqs == NULL)
-		return -1;
-
-	os_free(wpa_s->manual_scan_freqs);
-	wpa_s->manual_scan_freqs = freqs;
-
-	return 0;
-}
-
-
-static int scan_id_list_parse(struct wpa_supplicant *wpa_s, const char *value)
+static int scan_id_list_parse(struct wpa_supplicant *wpa_s, const char *value,
+			      unsigned int *scan_id_count, int scan_id[])
 {
 	const char *pos = value;
 
 	while (pos) {
 		if (*pos == ' ' || *pos == '\0')
 			break;
-		if (wpa_s->scan_id_count == MAX_SCAN_ID)
+		if (*scan_id_count == MAX_SCAN_ID)
 			return -1;
-		wpa_s->scan_id[wpa_s->scan_id_count++] = atoi(pos);
+		scan_id[(*scan_id_count)++] = atoi(pos);
 		pos = os_strchr(pos, ',');
 		if (pos)
 			pos++;
@@ -6041,54 +6980,147 @@
 			   char *reply, int reply_size, int *reply_len)
 {
 	char *pos;
+	unsigned int manual_scan_passive = 0;
+	unsigned int manual_scan_use_id = 0;
+	unsigned int manual_scan_only_new = 0;
+	unsigned int scan_only = 0;
+	unsigned int scan_id_count = 0;
+	int scan_id[MAX_SCAN_ID];
+	void (*scan_res_handler)(struct wpa_supplicant *wpa_s,
+				 struct wpa_scan_results *scan_res);
+	int *manual_scan_freqs = NULL;
+	struct wpa_ssid_value *ssid = NULL, *ns;
+	unsigned int ssid_count = 0;
 
 	if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED) {
 		*reply_len = -1;
 		return;
 	}
 
-	wpa_s->manual_scan_passive = 0;
-	wpa_s->manual_scan_use_id = 0;
-	wpa_s->manual_scan_only_new = 0;
-	wpa_s->scan_id_count = 0;
+	if (radio_work_pending(wpa_s, "scan")) {
+		wpa_printf(MSG_DEBUG,
+			   "Pending scan scheduled - reject new request");
+		*reply_len = os_snprintf(reply, reply_size, "FAIL-BUSY\n");
+		return;
+	}
+
+#ifdef CONFIG_INTERWORKING
+	if (wpa_s->fetch_anqp_in_progress || wpa_s->network_select) {
+		wpa_printf(MSG_DEBUG,
+			   "Interworking select in progress - reject new scan");
+		*reply_len = os_snprintf(reply, reply_size, "FAIL-BUSY\n");
+		return;
+	}
+#endif /* CONFIG_INTERWORKING */
 
 	if (params) {
 		if (os_strncasecmp(params, "TYPE=ONLY", 9) == 0)
-			wpa_s->scan_res_handler = scan_only_handler;
+			scan_only = 1;
 
 		pos = os_strstr(params, "freq=");
-		if (pos && set_scan_freqs(wpa_s, pos + 5) < 0) {
-			*reply_len = -1;
-			return;
+		if (pos) {
+			manual_scan_freqs = freq_range_to_channel_list(wpa_s,
+								       pos + 5);
+			if (manual_scan_freqs == NULL) {
+				*reply_len = -1;
+				goto done;
+			}
 		}
 
 		pos = os_strstr(params, "passive=");
 		if (pos)
-			wpa_s->manual_scan_passive = !!atoi(pos + 8);
+			manual_scan_passive = !!atoi(pos + 8);
 
 		pos = os_strstr(params, "use_id=");
 		if (pos)
-			wpa_s->manual_scan_use_id = atoi(pos + 7);
+			manual_scan_use_id = atoi(pos + 7);
 
 		pos = os_strstr(params, "only_new=1");
 		if (pos)
-			wpa_s->manual_scan_only_new = 1;
+			manual_scan_only_new = 1;
 
 		pos = os_strstr(params, "scan_id=");
-		if (pos && scan_id_list_parse(wpa_s, pos + 8) < 0) {
+		if (pos && scan_id_list_parse(wpa_s, pos + 8, &scan_id_count,
+					      scan_id) < 0) {
 			*reply_len = -1;
-			return;
+			goto done;
 		}
-	} else {
-		os_free(wpa_s->manual_scan_freqs);
-		wpa_s->manual_scan_freqs = NULL;
-		if (wpa_s->scan_res_handler == scan_only_handler)
-			wpa_s->scan_res_handler = NULL;
+
+		pos = params;
+		while (pos && *pos != '\0') {
+			if (os_strncmp(pos, "ssid ", 5) == 0) {
+				char *end;
+
+				pos += 5;
+				end = pos;
+				while (*end) {
+					if (*end == '\0' || *end == ' ')
+						break;
+					end++;
+				}
+
+				ns = os_realloc_array(
+					ssid, ssid_count + 1,
+					sizeof(struct wpa_ssid_value));
+				if (ns == NULL) {
+					*reply_len = -1;
+					goto done;
+				}
+				ssid = ns;
+
+				if ((end - pos) & 0x01 ||
+				    end - pos > 2 * SSID_MAX_LEN ||
+				    hexstr2bin(pos, ssid[ssid_count].ssid,
+					       (end - pos) / 2) < 0) {
+					wpa_printf(MSG_DEBUG,
+						   "Invalid SSID value '%s'",
+						   pos);
+					*reply_len = -1;
+					goto done;
+				}
+				ssid[ssid_count].ssid_len = (end - pos) / 2;
+				wpa_hexdump_ascii(MSG_DEBUG, "scan SSID",
+						  ssid[ssid_count].ssid,
+						  ssid[ssid_count].ssid_len);
+				ssid_count++;
+				pos = end;
+			}
+
+			pos = os_strchr(pos, ' ');
+			if (pos)
+				pos++;
+		}
 	}
 
+	wpa_s->num_ssids_from_scan_req = ssid_count;
+	os_free(wpa_s->ssids_from_scan_req);
+	if (ssid_count) {
+		wpa_s->ssids_from_scan_req = ssid;
+		ssid = NULL;
+	} else {
+		wpa_s->ssids_from_scan_req = NULL;
+	}
+
+	if (scan_only)
+		scan_res_handler = scan_only_handler;
+	else if (wpa_s->scan_res_handler == scan_only_handler)
+		scan_res_handler = NULL;
+	else
+		scan_res_handler = wpa_s->scan_res_handler;
+
 	if (!wpa_s->sched_scanning && !wpa_s->scanning &&
 	    ((wpa_s->wpa_state <= WPA_SCANNING) ||
 	     (wpa_s->wpa_state == WPA_COMPLETED))) {
+		wpa_s->manual_scan_passive = manual_scan_passive;
+		wpa_s->manual_scan_use_id = manual_scan_use_id;
+		wpa_s->manual_scan_only_new = manual_scan_only_new;
+		wpa_s->scan_id_count = scan_id_count;
+		os_memcpy(wpa_s->scan_id, scan_id, scan_id_count * sizeof(int));
+		wpa_s->scan_res_handler = scan_res_handler;
+		os_free(wpa_s->manual_scan_freqs);
+		wpa_s->manual_scan_freqs = manual_scan_freqs;
+		manual_scan_freqs = NULL;
+
 		wpa_s->normal_scans = 0;
 		wpa_s->scan_req = MANUAL_SCAN_REQ;
 		wpa_s->after_wps = 0;
@@ -6102,6 +7134,16 @@
 						 wpa_s->manual_scan_id);
 		}
 	} else if (wpa_s->sched_scanning) {
+		wpa_s->manual_scan_passive = manual_scan_passive;
+		wpa_s->manual_scan_use_id = manual_scan_use_id;
+		wpa_s->manual_scan_only_new = manual_scan_only_new;
+		wpa_s->scan_id_count = scan_id_count;
+		os_memcpy(wpa_s->scan_id, scan_id, scan_id_count * sizeof(int));
+		wpa_s->scan_res_handler = scan_res_handler;
+		os_free(wpa_s->manual_scan_freqs);
+		wpa_s->manual_scan_freqs = manual_scan_freqs;
+		manual_scan_freqs = NULL;
+
 		wpa_printf(MSG_DEBUG, "Stop ongoing sched_scan to allow requested full scan to proceed");
 		wpa_supplicant_cancel_sched_scan(wpa_s);
 		wpa_s->scan_req = MANUAL_SCAN_REQ;
@@ -6117,6 +7159,10 @@
 		wpa_printf(MSG_DEBUG, "Ongoing scan action - reject new request");
 		*reply_len = os_snprintf(reply, reply_size, "FAIL-BUSY\n");
 	}
+
+done:
+	os_free(manual_scan_freqs);
+	os_free(ssid);
 }
 
 
@@ -6256,6 +7302,266 @@
 	return 0;
 }
 
+
+static int wpas_ctrl_iface_eapol_rx(struct wpa_supplicant *wpa_s, char *cmd)
+{
+	char *pos;
+	u8 src[ETH_ALEN], *buf;
+	int used;
+	size_t len;
+
+	wpa_printf(MSG_DEBUG, "External EAPOL RX: %s", cmd);
+
+	pos = cmd;
+	used = hwaddr_aton2(pos, src);
+	if (used < 0)
+		return -1;
+	pos += used;
+	while (*pos == ' ')
+		pos++;
+
+	len = os_strlen(pos);
+	if (len & 1)
+		return -1;
+	len /= 2;
+
+	buf = os_malloc(len);
+	if (buf == NULL)
+		return -1;
+
+	if (hexstr2bin(pos, buf, len) < 0) {
+		os_free(buf);
+		return -1;
+	}
+
+	wpa_supplicant_rx_eapol(wpa_s, src, buf, len);
+	os_free(buf);
+
+	return 0;
+}
+
+
+static u16 ipv4_hdr_checksum(const void *buf, size_t len)
+{
+	size_t i;
+	u32 sum = 0;
+	const u16 *pos = buf;
+
+	for (i = 0; i < len / 2; i++)
+		sum += *pos++;
+
+	while (sum >> 16)
+		sum = (sum & 0xffff) + (sum >> 16);
+
+	return sum ^ 0xffff;
+}
+
+
+#define HWSIM_PACKETLEN 1500
+#define HWSIM_IP_LEN (HWSIM_PACKETLEN - sizeof(struct ether_header))
+
+void wpas_data_test_rx(void *ctx, const u8 *src_addr, const u8 *buf, size_t len)
+{
+	struct wpa_supplicant *wpa_s = ctx;
+	const struct ether_header *eth;
+	const struct iphdr *ip;
+	const u8 *pos;
+	unsigned int i;
+
+	if (len != HWSIM_PACKETLEN)
+		return;
+
+	eth = (const struct ether_header *) buf;
+	ip = (const struct iphdr *) (eth + 1);
+	pos = (const u8 *) (ip + 1);
+
+	if (ip->ihl != 5 || ip->version != 4 ||
+	    ntohs(ip->tot_len) != HWSIM_IP_LEN)
+		return;
+
+	for (i = 0; i < HWSIM_IP_LEN - sizeof(*ip); i++) {
+		if (*pos != (u8) i)
+			return;
+		pos++;
+	}
+
+	wpa_msg(wpa_s, MSG_INFO, "DATA-TEST-RX " MACSTR " " MACSTR,
+		MAC2STR(eth->ether_dhost), MAC2STR(eth->ether_shost));
+}
+
+
+static int wpas_ctrl_iface_data_test_config(struct wpa_supplicant *wpa_s,
+					    char *cmd)
+{
+	int enabled = atoi(cmd);
+
+	if (!enabled) {
+		if (wpa_s->l2_test) {
+			l2_packet_deinit(wpa_s->l2_test);
+			wpa_s->l2_test = NULL;
+			wpa_dbg(wpa_s, MSG_DEBUG, "test data: Disabled");
+		}
+		return 0;
+	}
+
+	if (wpa_s->l2_test)
+		return 0;
+
+	wpa_s->l2_test = l2_packet_init(wpa_s->ifname, wpa_s->own_addr,
+					ETHERTYPE_IP, wpas_data_test_rx,
+					wpa_s, 1);
+	if (wpa_s->l2_test == NULL)
+		return -1;
+
+	wpa_dbg(wpa_s, MSG_DEBUG, "test data: Enabled");
+
+	return 0;
+}
+
+
+static int wpas_ctrl_iface_data_test_tx(struct wpa_supplicant *wpa_s, char *cmd)
+{
+	u8 dst[ETH_ALEN], src[ETH_ALEN];
+	char *pos;
+	int used;
+	long int val;
+	u8 tos;
+	u8 buf[HWSIM_PACKETLEN];
+	struct ether_header *eth;
+	struct iphdr *ip;
+	u8 *dpos;
+	unsigned int i;
+
+	if (wpa_s->l2_test == NULL)
+		return -1;
+
+	/* format: <dst> <src> <tos> */
+
+	pos = cmd;
+	used = hwaddr_aton2(pos, dst);
+	if (used < 0)
+		return -1;
+	pos += used;
+	while (*pos == ' ')
+		pos++;
+	used = hwaddr_aton2(pos, src);
+	if (used < 0)
+		return -1;
+	pos += used;
+
+	val = strtol(pos, NULL, 0);
+	if (val < 0 || val > 0xff)
+		return -1;
+	tos = val;
+
+	eth = (struct ether_header *) buf;
+	os_memcpy(eth->ether_dhost, dst, ETH_ALEN);
+	os_memcpy(eth->ether_shost, src, ETH_ALEN);
+	eth->ether_type = htons(ETHERTYPE_IP);
+	ip = (struct iphdr *) (eth + 1);
+	os_memset(ip, 0, sizeof(*ip));
+	ip->ihl = 5;
+	ip->version = 4;
+	ip->ttl = 64;
+	ip->tos = tos;
+	ip->tot_len = htons(HWSIM_IP_LEN);
+	ip->protocol = 1;
+	ip->saddr = htonl(192 << 24 | 168 << 16 | 1 << 8 | 1);
+	ip->daddr = htonl(192 << 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,
+			   HWSIM_PACKETLEN) < 0)
+		return -1;
+
+	wpa_dbg(wpa_s, MSG_DEBUG, "test data: TX dst=" MACSTR " src=" MACSTR
+		" tos=0x%x", MAC2STR(dst), MAC2STR(src), tos);
+
+	return 0;
+}
+
+
+static int wpas_ctrl_iface_data_test_frame(struct wpa_supplicant *wpa_s,
+					   char *cmd)
+{
+	u8 *buf;
+	struct ether_header *eth;
+	struct l2_packet_data *l2 = NULL;
+	size_t len;
+	u16 ethertype;
+	int res = -1;
+
+	len = os_strlen(cmd);
+	if (len & 1 || len < ETH_HLEN * 2)
+		return -1;
+	len /= 2;
+
+	buf = os_malloc(len);
+	if (buf == NULL)
+		return -1;
+
+	if (hexstr2bin(cmd, buf, len) < 0)
+		goto done;
+
+	eth = (struct ether_header *) buf;
+	ethertype = ntohs(eth->ether_type);
+
+	l2 = l2_packet_init(wpa_s->ifname, wpa_s->own_addr, ethertype,
+			    wpas_data_test_rx, wpa_s, 1);
+	if (l2 == NULL)
+		goto done;
+
+	res = l2_packet_send(l2, eth->ether_dhost, ethertype, buf, len);
+	wpa_dbg(wpa_s, MSG_DEBUG, "test data: TX frame res=%d", res);
+done:
+	if (l2)
+		l2_packet_deinit(l2);
+	os_free(buf);
+
+	return res < 0 ? -1 : 0;
+}
+
+
+static int wpas_ctrl_test_alloc_fail(struct wpa_supplicant *wpa_s, char *cmd)
+{
+#ifdef WPA_TRACE_BFD
+	extern char wpa_trace_fail_func[256];
+	extern unsigned int wpa_trace_fail_after;
+	char *pos;
+
+	wpa_trace_fail_after = atoi(cmd);
+	pos = os_strchr(cmd, ':');
+	if (pos) {
+		pos++;
+		os_strlcpy(wpa_trace_fail_func, pos,
+			   sizeof(wpa_trace_fail_func));
+	} else {
+		wpa_trace_fail_after = 0;
+	}
+	return 0;
+#else /* WPA_TRACE_BFD */
+	return -1;
+#endif /* WPA_TRACE_BFD */
+}
+
+
+static int wpas_ctrl_get_alloc_fail(struct wpa_supplicant *wpa_s,
+				    char *buf, size_t buflen)
+{
+#ifdef WPA_TRACE_BFD
+	extern char wpa_trace_fail_func[256];
+	extern unsigned int wpa_trace_fail_after;
+
+	return os_snprintf(buf, buflen, "%u:%s", wpa_trace_fail_after,
+			   wpa_trace_fail_func);
+#else /* WPA_TRACE_BFD */
+	return -1;
+#endif /* WPA_TRACE_BFD */
+}
+
 #endif /* CONFIG_TESTING_OPTIONS */
 
 
@@ -6268,8 +7574,13 @@
 
 	for (i = 0; i < NUM_VENDOR_ELEM_FRAMES; i++) {
 		if (wpa_s->vendor_elem[i]) {
-			os_snprintf(buf, sizeof(buf), "frame[%u]", i);
-			wpa_hexdump_buf(MSG_DEBUG, buf, wpa_s->vendor_elem[i]);
+			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]);
+			}
 		}
 	}
 
@@ -6463,6 +7774,171 @@
 }
 
 
+static void wpas_ctrl_neighbor_rep_cb(void *ctx, struct wpabuf *neighbor_rep)
+{
+	struct wpa_supplicant *wpa_s = ctx;
+
+	if (neighbor_rep) {
+		wpa_msg_ctrl(wpa_s, MSG_INFO, RRM_EVENT_NEIGHBOR_REP_RXED
+			     "length=%u",
+			     (unsigned int) wpabuf_len(neighbor_rep));
+		wpabuf_free(neighbor_rep);
+	} else {
+		wpa_msg_ctrl(wpa_s, MSG_INFO, RRM_EVENT_NEIGHBOR_REP_FAILED);
+	}
+}
+
+
+static int wpas_ctrl_iface_send_neigbor_rep(struct wpa_supplicant *wpa_s,
+					    char *cmd)
+{
+	struct wpa_ssid ssid;
+	struct wpa_ssid *ssid_p = NULL;
+	int ret = 0;
+
+	if (os_strncmp(cmd, " ssid=", 6) == 0) {
+		ssid.ssid_len = os_strlen(cmd + 6);
+		if (ssid.ssid_len > SSID_MAX_LEN)
+			return -1;
+		ssid.ssid = (u8 *) (cmd + 6);
+		ssid_p = &ssid;
+	}
+
+	ret = wpas_rrm_send_neighbor_rep_request(wpa_s, ssid_p,
+						 wpas_ctrl_neighbor_rep_cb,
+						 wpa_s);
+
+	return ret;
+}
+
+
+static int wpas_ctrl_iface_erp_flush(struct wpa_supplicant *wpa_s)
+{
+	eapol_sm_erp_flush(wpa_s->eapol);
+	return 0;
+}
+
+
+static int wpas_ctrl_iface_mac_rand_scan(struct wpa_supplicant *wpa_s,
+					 char *cmd)
+{
+	char *token, *context = NULL;
+	unsigned int enable = ~0, type = 0;
+	u8 _addr[ETH_ALEN], _mask[ETH_ALEN];
+	u8 *addr = NULL, *mask = NULL;
+
+	while ((token = str_token(cmd, " ", &context))) {
+		if (os_strcasecmp(token, "scan") == 0) {
+			type |= MAC_ADDR_RAND_SCAN;
+		} else if (os_strcasecmp(token, "sched") == 0) {
+			type |= MAC_ADDR_RAND_SCHED_SCAN;
+		} else if (os_strcasecmp(token, "pno") == 0) {
+			type |= MAC_ADDR_RAND_PNO;
+		} else if (os_strcasecmp(token, "all") == 0) {
+			type = wpa_s->mac_addr_rand_supported;
+		} else if (os_strncasecmp(token, "enable=", 7) == 0) {
+			enable = atoi(token + 7);
+		} else if (os_strncasecmp(token, "addr=", 5) == 0) {
+			addr = _addr;
+			if (hwaddr_aton(token + 5, addr)) {
+				wpa_printf(MSG_INFO,
+					   "CTRL: Invalid MAC address: %s",
+					   token);
+				return -1;
+			}
+		} else if (os_strncasecmp(token, "mask=", 5) == 0) {
+			mask = _mask;
+			if (hwaddr_aton(token + 5, mask)) {
+				wpa_printf(MSG_INFO,
+					   "CTRL: Invalid MAC address mask: %s",
+					   token);
+				return -1;
+			}
+		} else {
+			wpa_printf(MSG_INFO,
+				   "CTRL: Invalid MAC_RAND_SCAN parameter: %s",
+				   token);
+			return -1;
+		}
+	}
+
+	if (!type) {
+		wpa_printf(MSG_INFO, "CTRL: MAC_RAND_SCAN no type specified");
+		return -1;
+	}
+
+	if ((wpa_s->mac_addr_rand_supported & type) != type) {
+		wpa_printf(MSG_INFO,
+			   "CTRL: MAC_RAND_SCAN types=%u != supported=%u",
+			   type, wpa_s->mac_addr_rand_supported);
+		return -1;
+	}
+
+	if (enable > 1) {
+		wpa_printf(MSG_INFO,
+			   "CTRL: MAC_RAND_SCAN enable=<0/1> not specified");
+		return -1;
+	}
+
+	if (!enable) {
+		wpas_mac_addr_rand_scan_clear(wpa_s, type);
+		if (wpa_s->pno) {
+			if (type & MAC_ADDR_RAND_PNO) {
+				wpas_stop_pno(wpa_s);
+				wpas_start_pno(wpa_s);
+			}
+		} else if (wpa_s->sched_scanning &&
+			   (type & MAC_ADDR_RAND_SCHED_SCAN)) {
+			/* simulate timeout to restart the sched scan */
+			wpa_s->sched_scan_timed_out = 1;
+			wpa_s->prev_sched_ssid = NULL;
+			wpa_supplicant_cancel_sched_scan(wpa_s);
+		}
+		return 0;
+	}
+
+	if ((addr && !mask) || (!addr && mask)) {
+		wpa_printf(MSG_INFO,
+			   "CTRL: MAC_RAND_SCAN invalid addr/mask combination");
+		return -1;
+	}
+
+	if (addr && mask && (!(mask[0] & 0x01) || (addr[0] & 0x01))) {
+		wpa_printf(MSG_INFO,
+			   "CTRL: MAC_RAND_SCAN cannot allow multicast address");
+		return -1;
+	}
+
+	if (type & MAC_ADDR_RAND_SCAN) {
+		wpas_mac_addr_rand_scan_set(wpa_s, MAC_ADDR_RAND_SCAN,
+					    addr, mask);
+	}
+
+	if (type & MAC_ADDR_RAND_SCHED_SCAN) {
+		wpas_mac_addr_rand_scan_set(wpa_s, MAC_ADDR_RAND_SCHED_SCAN,
+					    addr, mask);
+
+		if (wpa_s->sched_scanning && !wpa_s->pno) {
+			/* simulate timeout to restart the sched scan */
+			wpa_s->sched_scan_timed_out = 1;
+			wpa_s->prev_sched_ssid = NULL;
+			wpa_supplicant_cancel_sched_scan(wpa_s);
+		}
+	}
+
+	if (type & MAC_ADDR_RAND_PNO) {
+		wpas_mac_addr_rand_scan_set(wpa_s, MAC_ADDR_RAND_PNO,
+					    addr, mask);
+		if (wpa_s->pno) {
+			wpas_stop_pno(wpa_s);
+			wpas_start_pno(wpa_s);
+		}
+	}
+
+	return 0;
+}
+
+
 char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s,
 					 char *buf, size_t *resp_len)
 {
@@ -6515,13 +7991,9 @@
 	} else if (os_strcmp(buf, "MIB") == 0) {
 		reply_len = wpa_sm_get_mib(wpa_s->wpa, reply, reply_size);
 		if (reply_len >= 0) {
-			int res;
-			res = eapol_sm_get_mib(wpa_s->eapol, reply + reply_len,
-					       reply_size - reply_len);
-			if (res < 0)
-				reply_len = -1;
-			else
-				reply_len += res;
+			reply_len += eapol_sm_get_mib(wpa_s->eapol,
+						      reply + reply_len,
+						      reply_size - reply_len);
 		}
 	} else if (os_strncmp(buf, "STATUS", 6) == 0) {
 		reply_len = wpa_supplicant_ctrl_iface_status(
@@ -6534,6 +8006,9 @@
 	} else if (os_strncmp(buf, "SET ", 4) == 0) {
 		if (wpa_supplicant_ctrl_iface_set(wpa_s, buf + 4))
 			reply_len = -1;
+	} else if (os_strncmp(buf, "DUMP", 4) == 0) {
+		reply_len = wpa_config_dump_values(wpa_s->conf,
+						   reply, reply_size);
 	} else if (os_strncmp(buf, "GET ", 4) == 0) {
 		reply_len = wpa_supplicant_ctrl_iface_get(wpa_s, buf + 4,
 							  reply, reply_size);
@@ -6642,8 +8117,7 @@
 		if (wpas_wps_er_start(wpa_s, buf + 13))
 			reply_len = -1;
 	} else if (os_strcmp(buf, "WPS_ER_STOP") == 0) {
-		if (wpas_wps_er_stop(wpa_s))
-			reply_len = -1;
+		wpas_wps_er_stop(wpa_s);
 	} else if (os_strncmp(buf, "WPS_ER_PIN ", 11) == 0) {
 		if (wpa_supplicant_ctrl_iface_wps_er_pin(wpa_s, buf + 11))
 			reply_len = -1;
@@ -6682,15 +8156,36 @@
 		if (wpa_supplicant_ctrl_iface_ibss_rsn(wpa_s, buf + 9))
 			reply_len = -1;
 #endif /* CONFIG_IBSS_RSN */
+#ifdef CONFIG_MESH
+	} else if (os_strncmp(buf, "MESH_INTERFACE_ADD ", 19) == 0) {
+		reply_len = wpa_supplicant_ctrl_iface_mesh_interface_add(
+			wpa_s, buf + 19, reply, reply_size);
+	} else if (os_strcmp(buf, "MESH_INTERFACE_ADD") == 0) {
+		reply_len = wpa_supplicant_ctrl_iface_mesh_interface_add(
+			wpa_s, "", reply, reply_size);
+	} else if (os_strncmp(buf, "MESH_GROUP_ADD ", 15) == 0) {
+		if (wpa_supplicant_ctrl_iface_mesh_group_add(wpa_s, buf + 15))
+			reply_len = -1;
+	} else if (os_strncmp(buf, "MESH_GROUP_REMOVE ", 18) == 0) {
+		if (wpa_supplicant_ctrl_iface_mesh_group_remove(wpa_s,
+								buf + 18))
+			reply_len = -1;
+#endif /* CONFIG_MESH */
 #ifdef CONFIG_P2P
 	} else if (os_strncmp(buf, "P2P_FIND ", 9) == 0) {
-		if (p2p_ctrl_find(wpa_s, buf + 9))
+		if (p2p_ctrl_find(wpa_s, buf + 8))
 			reply_len = -1;
 	} else if (os_strcmp(buf, "P2P_FIND") == 0) {
 		if (p2p_ctrl_find(wpa_s, ""))
 			reply_len = -1;
 	} else if (os_strcmp(buf, "P2P_STOP_FIND") == 0) {
 		wpas_p2p_stop_find(wpa_s);
+	} else if (os_strncmp(buf, "P2P_ASP_PROVISION ", 18) == 0) {
+		if (p2p_ctrl_asp_provision(wpa_s, buf + 18))
+			reply_len = -1;
+	} else if (os_strncmp(buf, "P2P_ASP_PROVISION_RESP ", 23) == 0) {
+		if (p2p_ctrl_asp_provision_resp(wpa_s, buf + 23))
+			reply_len = -1;
 	} else if (os_strncmp(buf, "P2P_CONNECT ", 12) == 0) {
 		reply_len = p2p_ctrl_connect(wpa_s, buf + 12, reply,
 					     reply_size);
@@ -6704,7 +8199,7 @@
 		if (wpas_p2p_group_remove(wpa_s, buf + 17))
 			reply_len = -1;
 	} else if (os_strcmp(buf, "P2P_GROUP_ADD") == 0) {
-		if (wpas_p2p_group_add(wpa_s, 0, 0, 0, 0))
+		if (p2p_ctrl_group_add(wpa_s, ""))
 			reply_len = -1;
 	} else if (os_strncmp(buf, "P2P_GROUP_ADD ", 14) == 0) {
 		if (p2p_ctrl_group_add(wpa_s, buf + 14))
@@ -6736,6 +8231,9 @@
 	} else if (os_strncmp(buf, "P2P_SERVICE_DEL ", 16) == 0) {
 		if (p2p_ctrl_service_del(wpa_s, buf + 16) < 0)
 			reply_len = -1;
+	} else if (os_strncmp(buf, "P2P_SERVICE_REP ", 16) == 0) {
+		if (p2p_ctrl_service_replace(wpa_s, buf + 16) < 0)
+			reply_len = -1;
 	} else if (os_strncmp(buf, "P2P_REJECT ", 11) == 0) {
 		if (p2p_ctrl_reject(wpa_s, buf + 11) < 0)
 			reply_len = -1;
@@ -6793,8 +8291,19 @@
 		if (ctrl_interworking_select(wpa_s, buf + 20) < 0)
 			reply_len = -1;
 	} else if (os_strncmp(buf, "INTERWORKING_CONNECT ", 21) == 0) {
-		if (ctrl_interworking_connect(wpa_s, buf + 21) < 0)
+		if (ctrl_interworking_connect(wpa_s, buf + 21, 0) < 0)
 			reply_len = -1;
+	} else if (os_strncmp(buf, "INTERWORKING_ADD_NETWORK ", 25) == 0) {
+		int id;
+
+		id = ctrl_interworking_connect(wpa_s, buf + 25, 1);
+		if (id < 0)
+			reply_len = -1;
+		else {
+			reply_len = os_snprintf(reply, reply_size, "%d\n", id);
+			if (os_snprintf_error(reply_size, reply_len))
+				reply_len = -1;
+		}
 	} else if (os_strncmp(buf, "ANQP_GET ", 9) == 0) {
 		if (get_anqp(wpa_s, buf + 9) < 0)
 			reply_len = -1;
@@ -6864,6 +8373,7 @@
 		wpa_supplicant_cancel_scan(wpa_s);
 		wpa_supplicant_deauthenticate(wpa_s,
 					      WLAN_REASON_DEAUTH_LEAVING);
+		eloop_cancel_timeout(wpas_network_reenabled, wpa_s, NULL);
 	} else if (os_strcmp(buf, "SCAN") == 0) {
 		wpas_ctrl_scan(wpa_s, NULL, reply, reply_size, &reply_len);
 	} else if (os_strncmp(buf, "SCAN ", 5) == 0) {
@@ -6952,6 +8462,9 @@
 	} else if (os_strncmp(buf, "CHAN_SWITCH ", 12) == 0) {
 		if (ap_ctrl_iface_chanswitch(wpa_s, buf + 12))
 			reply_len = -1;
+	} else if (os_strcmp(buf, "STOP_AP") == 0) {
+		if (wpas_ap_stop_ap(wpa_s))
+			reply_len = -1;
 #endif /* CONFIG_AP */
 	} else if (os_strcmp(buf, "SUSPEND") == 0) {
 		wpas_notify_suspend(wpa_s->global);
@@ -6965,8 +8478,7 @@
 		if (wpa_supplicant_ctrl_iface_roam(wpa_s, buf + 5))
 			reply_len = -1;
 	} else if (os_strncmp(buf, "STA_AUTOCONNECT ", 16) == 0) {
-		if (wpa_supplicant_ctrl_iface_sta_autoconnect(wpa_s, buf + 16))
-			reply_len = -1;
+		wpa_s->auto_reconnect_disabled = atoi(buf + 16) == 0;
 	} else if (os_strncmp(buf, "BSS_EXPIRE_AGE ", 15) == 0) {
 		if (wpa_supplicant_ctrl_iface_bss_expire_age(wpa_s, buf + 15))
 			reply_len = -1;
@@ -6975,8 +8487,7 @@
 							       buf + 17))
 			reply_len = -1;
 	} else if (os_strncmp(buf, "BSS_FLUSH ", 10) == 0) {
-		if (wpa_supplicant_ctrl_iface_bss_flush(wpa_s, buf + 10))
-			reply_len = -1;
+		wpa_supplicant_ctrl_iface_bss_flush(wpa_s, buf + 10);
 #ifdef CONFIG_TDLS
 	} else if (os_strncmp(buf, "TDLS_DISCOVER ", 14) == 0) {
 		if (wpa_supplicant_ctrl_iface_tdls_discover(wpa_s, buf + 14))
@@ -6987,7 +8498,26 @@
 	} else if (os_strncmp(buf, "TDLS_TEARDOWN ", 14) == 0) {
 		if (wpa_supplicant_ctrl_iface_tdls_teardown(wpa_s, buf + 14))
 			reply_len = -1;
+	} else if (os_strncmp(buf, "TDLS_CHAN_SWITCH ", 17) == 0) {
+		if (wpa_supplicant_ctrl_iface_tdls_chan_switch(wpa_s,
+							       buf + 17))
+			reply_len = -1;
+	} else if (os_strncmp(buf, "TDLS_CANCEL_CHAN_SWITCH ", 24) == 0) {
+		if (wpa_supplicant_ctrl_iface_tdls_cancel_chan_switch(wpa_s,
+								      buf + 24))
+			reply_len = -1;
+	} else if (os_strncmp(buf, "TDLS_LINK_STATUS ", 17) == 0) {
+		reply_len = wpa_supplicant_ctrl_iface_tdls_link_status(
+			wpa_s, buf + 17, reply, reply_size);
 #endif /* CONFIG_TDLS */
+	} else if (os_strcmp(buf, "WMM_AC_STATUS") == 0) {
+		reply_len = wpas_wmm_ac_status(wpa_s, reply, reply_size);
+	} else if (os_strncmp(buf, "WMM_AC_ADDTS ", 13) == 0) {
+		if (wmm_ac_ctrl_addts(wpa_s, buf + 13))
+			reply_len = -1;
+	} else if (os_strncmp(buf, "WMM_AC_DELTS ", 13) == 0) {
+		if (wmm_ac_ctrl_delts(wpa_s, buf + 13))
+			reply_len = -1;
 	} else if (os_strncmp(buf, "SIGNAL_POLL", 11) == 0) {
 		reply_len = wpa_supplicant_signal_poll(wpa_s, reply,
 						       reply_size);
@@ -7014,8 +8544,8 @@
 	} else if (os_strncmp(buf, "WNM_SLEEP ", 10) == 0) {
 		if (wpas_ctrl_iface_wnm_sleep(wpa_s, buf + 10))
 			reply_len = -1;
-	} else if (os_strncmp(buf, "WNM_BSS_QUERY ", 10) == 0) {
-		if (wpas_ctrl_iface_wnm_bss_query(wpa_s, buf + 10))
+	} else if (os_strncmp(buf, "WNM_BSS_QUERY ", 14) == 0) {
+		if (wpas_ctrl_iface_wnm_bss_query(wpa_s, buf + 14))
 				reply_len = -1;
 #endif /* CONFIG_WNM */
 	} else if (os_strcmp(buf, "FLUSH") == 0) {
@@ -7032,6 +8562,23 @@
 	} else if (os_strncmp(buf, "DRIVER_EVENT ", 13) == 0) {
 		if (wpas_ctrl_iface_driver_event(wpa_s, buf + 13) < 0)
 			reply_len = -1;
+	} else if (os_strncmp(buf, "EAPOL_RX ", 9) == 0) {
+		if (wpas_ctrl_iface_eapol_rx(wpa_s, buf + 9) < 0)
+			reply_len = -1;
+	} else if (os_strncmp(buf, "DATA_TEST_CONFIG ", 17) == 0) {
+		if (wpas_ctrl_iface_data_test_config(wpa_s, buf + 17) < 0)
+			reply_len = -1;
+	} else if (os_strncmp(buf, "DATA_TEST_TX ", 13) == 0) {
+		if (wpas_ctrl_iface_data_test_tx(wpa_s, buf + 13) < 0)
+			reply_len = -1;
+	} else if (os_strncmp(buf, "DATA_TEST_FRAME ", 16) == 0) {
+		if (wpas_ctrl_iface_data_test_frame(wpa_s, buf + 16) < 0)
+			reply_len = -1;
+	} else if (os_strncmp(buf, "TEST_ALLOC_FAIL ", 16) == 0) {
+		if (wpas_ctrl_test_alloc_fail(wpa_s, buf + 16) < 0)
+			reply_len = -1;
+	} else if (os_strcmp(buf, "GET_ALLOC_FAIL") == 0) {
+		reply_len = wpas_ctrl_get_alloc_fail(wpa_s, reply, reply_size);
 #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)
@@ -7042,6 +8589,14 @@
 	} else if (os_strncmp(buf, "VENDOR_ELEM_REMOVE ", 19) == 0) {
 		if (wpas_ctrl_vendor_elem_remove(wpa_s, buf + 19) < 0)
 			reply_len = -1;
+	} else if (os_strncmp(buf, "NEIGHBOR_REP_REQUEST", 20) == 0) {
+		if (wpas_ctrl_iface_send_neigbor_rep(wpa_s, buf + 20))
+			reply_len = -1;
+	} else if (os_strcmp(buf, "ERP_FLUSH") == 0) {
+		wpas_ctrl_iface_erp_flush(wpa_s);
+	} else if (os_strncmp(buf, "MAC_RAND_SCAN ", 14) == 0) {
+		if (wpas_ctrl_iface_mac_rand_scan(wpa_s, buf + 14))
+			reply_len = -1;
 	} else {
 		os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
 		reply_len = 16;
@@ -7061,11 +8616,14 @@
 					   char *cmd)
 {
 	struct wpa_interface iface;
-	char *pos;
+	char *pos, *extra;
+	struct wpa_supplicant *wpa_s;
+	unsigned int create_iface = 0;
+	u8 mac_addr[ETH_ALEN];
 
 	/*
 	 * <ifname>TAB<confname>TAB<driver>TAB<ctrl_interface>TAB<driver_param>
-	 * TAB<bridge_ifname>
+	 * TAB<bridge_ifname>[TAB<create>]
 	 */
 	wpa_printf(MSG_DEBUG, "CTRL_IFACE GLOBAL INTERFACE_ADD '%s'", cmd);
 
@@ -7125,12 +8683,54 @@
 			iface.bridge_ifname = NULL;
 		if (pos == NULL)
 			break;
+
+		extra = pos;
+		pos = os_strchr(pos, '\t');
+		if (pos)
+			*pos++ = '\0';
+		if (!extra[0])
+			break;
+
+		if (os_strcmp(extra, "create") == 0)
+			create_iface = 1;
+		else {
+			wpa_printf(MSG_DEBUG,
+				   "INTERFACE_ADD unsupported extra parameter: '%s'",
+				   extra);
+			return -1;
+		}
 	} while (0);
 
-	if (wpa_supplicant_get_iface(global, iface.ifname))
-		return -1;
+	if (create_iface) {
+		wpa_printf(MSG_DEBUG, "CTRL_IFACE creating interface '%s'",
+			   iface.ifname);
+		if (!global->ifaces)
+			return -1;
+		if (wpa_drv_if_add(global->ifaces, WPA_IF_STATION, iface.ifname,
+				   NULL, NULL, NULL, mac_addr, NULL) < 0) {
+			wpa_printf(MSG_ERROR,
+				   "CTRL_IFACE interface creation failed");
+			return -1;
+		}
 
-	return wpa_supplicant_add_iface(global, &iface) ? 0 : -1;
+		wpa_printf(MSG_DEBUG,
+			   "CTRL_IFACE interface '%s' created with MAC addr: "
+			   MACSTR, iface.ifname, MAC2STR(mac_addr));
+	}
+
+	if (wpa_supplicant_get_iface(global, iface.ifname))
+		goto fail;
+
+	wpa_s = wpa_supplicant_add_iface(global, &iface, NULL);
+	if (!wpa_s)
+		goto fail;
+	wpa_s->added_vif = create_iface;
+	return 0;
+
+fail:
+	if (create_iface)
+		wpa_drv_if_remove(global->ifaces, WPA_IF_STATION, iface.ifname);
+	return -1;
 }
 
 
@@ -7138,13 +8738,22 @@
 					      char *cmd)
 {
 	struct wpa_supplicant *wpa_s;
+	int ret;
+	unsigned int delete_iface;
 
 	wpa_printf(MSG_DEBUG, "CTRL_IFACE GLOBAL INTERFACE_REMOVE '%s'", cmd);
 
 	wpa_s = wpa_supplicant_get_iface(global, cmd);
 	if (wpa_s == NULL)
 		return -1;
-	return wpa_supplicant_remove_iface(global, wpa_s, 0);
+	delete_iface = wpa_s->added_vif;
+	ret = wpa_supplicant_remove_iface(global, wpa_s, 0);
+	if (!ret && delete_iface) {
+		wpa_printf(MSG_DEBUG, "CTRL_IFACE deleting the interface '%s'",
+			   cmd);
+		ret = wpa_drv_if_remove(global->ifaces, WPA_IF_STATION, cmd);
+	}
+	return ret;
 }
 
 
@@ -7171,7 +8780,7 @@
 	char *pos, *end;
 
 	for (i = 0; wpa_drivers[i]; i++) {
-		struct wpa_driver_ops *drv = wpa_drivers[i];
+		const struct wpa_driver_ops *drv = wpa_drivers[i];
 		if (drv->get_interfaces == NULL)
 			continue;
 		tmp = drv->get_interfaces(global->drv_priv[i]);
@@ -7192,7 +8801,7 @@
 		res = os_snprintf(pos, end - pos, "%s\t%s\t%s\n",
 				  tmp->drv_name, tmp->ifname,
 				  tmp->desc ? tmp->desc : "");
-		if (res < 0 || res >= end - pos) {
+		if (os_snprintf_error(end - pos, res)) {
 			*pos = '\0';
 			break;
 		}
@@ -7218,7 +8827,7 @@
 
 	while (wpa_s) {
 		res = os_snprintf(pos, end - pos, "%s\n", wpa_s->ifname);
-		if (res < 0 || res >= end - pos) {
+		if (os_snprintf_error(end - pos, res)) {
 			*pos = '\0';
 			break;
 		}
@@ -7290,6 +8899,7 @@
 		"P2P_SERV_DISC_EXTERNAL ",
 		"P2P_SERVICE_ADD ",
 		"P2P_SERVICE_DEL ",
+		"P2P_SERVICE_REP ",
 		"P2P_REJECT ",
 		"P2P_INVITE ",
 		"P2P_PEER ",
@@ -7298,9 +8908,13 @@
 		"P2P_PRESENCE_REQ ",
 		"P2P_EXT_LISTEN ",
 		"P2P_REMOVE_CLIENT ",
+		"WPS_NFC_TOKEN ",
+		"WPS_NFC_TAG_READ ",
 		"NFC_GET_HANDOVER_SEL ",
 		"NFC_GET_HANDOVER_REQ ",
 		"NFC_REPORT_HANDOVER ",
+		"P2P_ASP_PROVISION ",
+		"P2P_ASP_PROVISION_RESP ",
 		NULL
 	};
 	int found = 0;
@@ -7433,12 +9047,12 @@
 				  "p2p_state=%s\n",
 				  MAC2STR(global->p2p_dev_addr),
 				  p2p_get_state_txt(global->p2p));
-		if (ret < 0 || ret >= end - pos)
+		if (os_snprintf_error(end - pos, ret))
 			return pos - buf;
 		pos += ret;
 	} else if (global->p2p) {
 		ret = os_snprintf(pos, end - pos, "p2p_state=DISABLED\n");
-		if (ret < 0 || ret >= end - pos)
+		if (os_snprintf_error(end - pos, ret))
 			return pos - buf;
 		pos += ret;
 	}
@@ -7447,7 +9061,7 @@
 #ifdef CONFIG_WIFI_DISPLAY
 	ret = os_snprintf(pos, end - pos, "wifi_display=%d\n",
 			  !!global->wifi_display);
-	if (ret < 0 || ret >= end - pos)
+	if (os_snprintf_error(end - pos, ret))
 		return pos - buf;
 	pos += ret;
 #endif /* CONFIG_WIFI_DISPLAY */
@@ -7456,7 +9070,7 @@
 		ret = os_snprintf(pos, end - pos, "ifname=%s\n"
 				  "address=" MACSTR "\n",
 				  wpa_s->ifname, MAC2STR(wpa_s->own_addr));
-		if (ret < 0 || ret >= end - pos)
+		if (os_snprintf_error(end - pos, ret))
 			return pos - buf;
 		pos += ret;
 	}
@@ -7550,6 +9164,9 @@
 		if (wpas_module_tests() < 0)
 			reply_len = -1;
 #endif /* CONFIG_MODULE_TESTS */
+	} else if (os_strncmp(buf, "RELOG", 5) == 0) {
+		if (wpa_debug_reopen_file() < 0)
+			reply_len = -1;
 	} else {
 		os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
 		reply_len = 16;
diff --git a/wpa_supplicant/ctrl_iface_named_pipe.c b/wpa_supplicant/ctrl_iface_named_pipe.c
index dc02db2..54e0e2f 100644
--- a/wpa_supplicant/ctrl_iface_named_pipe.c
+++ b/wpa_supplicant/ctrl_iface_named_pipe.c
@@ -423,7 +423,8 @@
 }
 
 
-static void wpa_supplicant_ctrl_iface_msg_cb(void *ctx, int level, int global,
+static void wpa_supplicant_ctrl_iface_msg_cb(void *ctx, int level,
+					     enum wpa_msg_type type,
 					     const char *txt, size_t len)
 {
 	struct wpa_supplicant *wpa_s = ctx;
diff --git a/wpa_supplicant/ctrl_iface_udp.c b/wpa_supplicant/ctrl_iface_udp.c
index 9d0674d..76f69f2 100644
--- a/wpa_supplicant/ctrl_iface_udp.c
+++ b/wpa_supplicant/ctrl_iface_udp.c
@@ -218,7 +218,8 @@
 	res = recvfrom(sock, buf, sizeof(buf) - 1, 0,
 		       (struct sockaddr *) &from, &fromlen);
 	if (res < 0) {
-		perror("recvfrom(ctrl_iface)");
+		wpa_printf(MSG_ERROR, "recvfrom(ctrl_iface): %s",
+			   strerror(errno));
 		return;
 	}
 
@@ -321,7 +322,8 @@
 }
 
 
-static void wpa_supplicant_ctrl_iface_msg_cb(void *ctx, int level, int global,
+static void wpa_supplicant_ctrl_iface_msg_cb(void *ctx, int level,
+					     enum wpa_msg_type type,
 					     const char *txt, size_t len)
 {
 	struct wpa_supplicant *wpa_s = ctx;
@@ -356,7 +358,7 @@
 
 	priv->sock = socket(domain, SOCK_DGRAM, 0);
 	if (priv->sock < 0) {
-		perror("socket(PF_INET)");
+		wpa_printf(MSG_ERROR, "socket(PF_INET): %s", strerror(errno));
 		goto fail;
 	}
 
@@ -386,7 +388,7 @@
 		port--;
 		if ((WPA_CTRL_IFACE_PORT - port) < WPA_CTRL_IFACE_PORT_LIMIT)
 			goto try_again;
-		perror("bind(AF_INET)");
+		wpa_printf(MSG_ERROR, "bind(AF_INET): %s", strerror(errno));
 		goto fail;
 	}
 
@@ -482,7 +484,9 @@
 			if (sendto(priv->sock, sbuf, llen + len, 0,
 				   (struct sockaddr *) &dst->addr,
 				   sizeof(dst->addr)) < 0) {
-				perror("sendto(CTRL_IFACE monitor)");
+				wpa_printf(MSG_ERROR,
+					   "sendto(CTRL_IFACE monitor): %s",
+					   strerror(errno));
 				dst->errors++;
 				if (dst->errors > 10) {
 					wpa_supplicant_ctrl_iface_detach(
@@ -551,7 +555,8 @@
 	res = recvfrom(sock, buf, sizeof(buf) - 1, 0,
 		       (struct sockaddr *) &from, &fromlen);
 	if (res < 0) {
-		perror("recvfrom(ctrl_iface)");
+		wpa_printf(MSG_ERROR, "recvfrom(ctrl_iface): %s",
+			   strerror(errno));
 		return;
 	}
 
@@ -634,7 +639,7 @@
 
 	priv->sock = socket(PF_INET, SOCK_DGRAM, 0);
 	if (priv->sock < 0) {
-		perror("socket(PF_INET)");
+		wpa_printf(MSG_ERROR, "socket(PF_INET): %s", strerror(errno));
 		goto fail;
 	}
 
@@ -652,7 +657,7 @@
 		if ((port - WPA_GLOBAL_CTRL_IFACE_PORT) <
 		    WPA_GLOBAL_CTRL_IFACE_PORT_LIMIT)
 			goto try_again;
-		perror("bind(AF_INET)");
+		wpa_printf(MSG_ERROR, "bind(AF_INET): %s", strerror(errno));
 		goto fail;
 	}
 
diff --git a/wpa_supplicant/ctrl_iface_unix.c b/wpa_supplicant/ctrl_iface_unix.c
index 40082e2..f49ba07 100644
--- a/wpa_supplicant/ctrl_iface_unix.c
+++ b/wpa_supplicant/ctrl_iface_unix.c
@@ -47,6 +47,7 @@
 	struct wpa_supplicant *wpa_s;
 	int sock;
 	struct dl_list ctrl_dst;
+	int android_control_socket;
 };
 
 
@@ -54,6 +55,7 @@
 	struct wpa_global *global;
 	int sock;
 	struct dl_list ctrl_dst;
+	int android_control_socket;
 };
 
 
@@ -72,7 +74,7 @@
 
 static int wpa_supplicant_ctrl_iface_attach(struct dl_list *ctrl_dst,
 					    struct sockaddr_un *from,
-					    socklen_t fromlen)
+					    socklen_t fromlen, int global)
 {
 	struct wpa_ctrl_dst *dst;
 	char addr_txt[200];
@@ -87,7 +89,8 @@
 	printf_encode(addr_txt, sizeof(addr_txt),
 		      (u8 *) from->sun_path,
 		      fromlen - offsetof(struct sockaddr_un, sun_path));
-	wpa_printf(MSG_DEBUG, "CTRL_IFACE monitor attached %s", addr_txt);
+	wpa_printf(MSG_DEBUG, "CTRL_IFACE %smonitor attached %s",
+		   global ? "global " : "", addr_txt);
 	return 0;
 }
 
@@ -172,7 +175,7 @@
 
 	if (os_strcmp(buf, "ATTACH") == 0) {
 		if (wpa_supplicant_ctrl_iface_attach(&priv->ctrl_dst, &from,
-						     fromlen))
+						     fromlen, 0))
 			reply_len = 1;
 		else {
 			new_attached = 1;
@@ -194,6 +197,13 @@
 		reply_buf = wpa_supplicant_ctrl_iface_process(wpa_s, buf,
 							      &reply_len);
 		reply = reply_buf;
+
+		/*
+		 * There could be some password/key material in the command, so
+		 * clear the buffer explicitly now that it is not needed
+		 * anymore.
+		 */
+		os_memset(buf, 0, res);
 	}
 
 	if (!reply && reply_len == 1) {
@@ -270,7 +280,7 @@
 	}
 
 	res = os_snprintf(buf, len, "%s/%s", dir, wpa_s->ifname);
-	if (res < 0 || (size_t) res >= len) {
+	if (os_snprintf_error(len, res)) {
 		os_free(pbuf);
 		os_free(buf);
 		return NULL;
@@ -292,7 +302,8 @@
 }
 
 
-static void wpa_supplicant_ctrl_iface_msg_cb(void *ctx, int level, int global,
+static void wpa_supplicant_ctrl_iface_msg_cb(void *ctx, int level,
+					     enum wpa_msg_type type,
 					     const char *txt, size_t len)
 {
 	struct wpa_supplicant *wpa_s = ctx;
@@ -300,15 +311,14 @@
 	if (wpa_s == NULL)
 		return;
 
-	if (global != 2 && wpa_s->global->ctrl_iface) {
+	if (type != WPA_MSG_NO_GLOBAL && wpa_s->global->ctrl_iface) {
 		struct ctrl_iface_global_priv *priv = wpa_s->global->ctrl_iface;
 		if (!dl_list_empty(&priv->ctrl_dst)) {
-			wpa_supplicant_ctrl_iface_send(wpa_s, global ? NULL :
-						       wpa_s->ifname,
-						       priv->sock,
-						       &priv->ctrl_dst,
-						       level, txt, len, NULL,
-						       priv);
+			wpa_supplicant_ctrl_iface_send(
+				wpa_s,
+				type == WPA_MSG_GLOBAL ? NULL : wpa_s->ifname,
+				priv->sock, &priv->ctrl_dst, level, txt, len,
+				NULL, priv);
 		}
 	}
 
@@ -340,8 +350,10 @@
 	os_snprintf(addr.sun_path, sizeof(addr.sun_path), "wpa_%s",
 		    wpa_s->conf->ctrl_interface);
 	priv->sock = android_get_control_socket(addr.sun_path);
-	if (priv->sock >= 0)
+	if (priv->sock >= 0) {
+		priv->android_control_socket = 1;
 		goto havesock;
+	}
 #endif /* ANDROID */
 	if (os_strncmp(buf, "DIR=", 4) == 0) {
 		dir = buf + 4;
@@ -556,6 +568,16 @@
 	if (priv->sock <= 0)
 		return -1;
 
+	/*
+	 * On Android, the control socket being used may be the socket
+	 * that is created when wpa_supplicant is started as a /init.*.rc
+	 * service. Such a socket is maintained as a key-value pair in
+	 * Android's environment. Closing this control socket would leave us
+	 * in a bad state with an invalid socket descriptor.
+	 */
+	if (priv->android_control_socket)
+		return priv->sock;
+
 	eloop_unregister_read_sock(priv->sock);
 	close(priv->sock);
 	priv->sock = -1;
@@ -657,7 +679,7 @@
 		return;
 
 	res = os_snprintf(levelstr, sizeof(levelstr), "<%d>", level);
-	if (res < 0 || (size_t) res >= sizeof(levelstr))
+	if (os_snprintf_error(sizeof(levelstr), res))
 		return;
 	idx = 0;
 	if (ifname) {
@@ -761,7 +783,8 @@
 		if (os_strcmp(buf, "ATTACH") == 0) {
 			/* handle ATTACH signal of first monitor interface */
 			if (!wpa_supplicant_ctrl_iface_attach(&priv->ctrl_dst,
-							      &from, fromlen)) {
+							      &from, fromlen,
+							      0)) {
 				if (sendto(priv->sock, "OK\n", 3, 0,
 					   (struct sockaddr *) &from, fromlen) <
 				    0) {
@@ -816,7 +839,7 @@
 
 	if (os_strcmp(buf, "ATTACH") == 0) {
 		if (wpa_supplicant_ctrl_iface_attach(&priv->ctrl_dst, &from,
-						     fromlen))
+						     fromlen, 1))
 			reply_len = 1;
 		else
 			reply_len = 2;
@@ -830,6 +853,13 @@
 		reply_buf = wpa_supplicant_global_ctrl_iface_process(
 			global, buf, &reply_len);
 		reply = reply_buf;
+
+		/*
+		 * There could be some password/key material in the command, so
+		 * clear the buffer explicitly now that it is not needed
+		 * anymore.
+		 */
+		os_memset(buf, 0, res);
 	}
 
 	if (!reply && reply_len == 1) {
@@ -870,6 +900,7 @@
 		}
 		wpa_printf(MSG_DEBUG, "Using Android control socket '%s'",
 			   ctrl + 9);
+		priv->android_control_socket = 1;
 		goto havesock;
 	}
 
@@ -884,6 +915,7 @@
 			wpa_printf(MSG_DEBUG,
 				   "Using Android control socket '%s'",
 				   ctrl);
+			priv->android_control_socket = 1;
 			goto havesock;
 		}
 	}
@@ -1064,6 +1096,16 @@
 	if (priv->sock <= 0)
 		return -1;
 
+	/*
+	 * On Android, the control socket being used may be the socket
+	 * that is created when wpa_supplicant is started as a /init.*.rc
+	 * service. Such a socket is maintained as a key-value pair in
+	 * Android's environment. Closing this control socket would leave us
+	 * in a bad state with an invalid socket descriptor.
+	 */
+	if (priv->android_control_socket)
+		return priv->sock;
+
 	eloop_unregister_read_sock(priv->sock);
 	close(priv->sock);
 	priv->sock = -1;
diff --git a/wpa_supplicant/dbus/dbus_common.c b/wpa_supplicant/dbus/dbus_common.c
index 5cc1505..7ef6cad 100644
--- a/wpa_supplicant/dbus/dbus_common.c
+++ b/wpa_supplicant/dbus/dbus_common.c
@@ -165,6 +165,7 @@
 static dbus_bool_t add_timeout(DBusTimeout *timeout, void *data)
 {
 	struct wpas_dbus_priv *priv = data;
+
 	if (!dbus_timeout_get_enabled(timeout))
 		return TRUE;
 
@@ -180,6 +181,7 @@
 static void remove_timeout(DBusTimeout *timeout, void *data)
 {
 	struct wpas_dbus_priv *priv = data;
+
 	eloop_cancel_timeout(process_timeout, priv, timeout);
 	dbus_timeout_set_data(timeout, NULL, NULL);
 }
@@ -244,8 +246,7 @@
 						   remove_timeout,
 						   timeout_toggled, priv,
 						   NULL)) {
-		wpa_printf(MSG_ERROR, "dbus: Failed to set callback "
-			   "functions");
+		wpa_printf(MSG_ERROR, "dbus: Failed to set callback functions");
 		return -1;
 	}
 
@@ -259,12 +260,12 @@
 
 
 static DBusHandlerResult disconnect_filter(DBusConnection *conn,
-                                           DBusMessage *message, void *data)
+					   DBusMessage *message, void *data)
 {
 	struct wpas_dbus_priv *priv = data;
 
 	if (dbus_message_is_signal(message, DBUS_INTERFACE_LOCAL,
-	                           "Disconnected")) {
+				   "Disconnected")) {
 		wpa_printf(MSG_DEBUG, "dbus: bus disconnected, terminating");
 		dbus_connection_set_exit_on_disconnect(conn, FALSE);
 		wpa_supplicant_terminate_proc(priv->global);
@@ -284,10 +285,11 @@
 	priv->con = dbus_bus_get(DBUS_BUS_SYSTEM, &error);
 	if (priv->con) {
 		dbus_connection_add_filter(priv->con, disconnect_filter, priv,
-		                           NULL);
+					   NULL);
 	} else {
-		wpa_printf(MSG_ERROR, "dbus: Could not acquire the system "
-			   "bus: %s - %s", error.name, error.message);
+		wpa_printf(MSG_ERROR,
+			   "dbus: Could not acquire the system bus: %s - %s",
+			   error.name, error.message);
 		ret = -1;
 	}
 	dbus_error_free(&error);
@@ -309,7 +311,7 @@
 	 * FIXME: is there a better solution to this problem?
 	 */
 	eloop_register_timeout(0, 50, dispatch_initial_dbus_messages,
-	                       priv->con, NULL);
+			       priv->con, NULL);
 
 	return 0;
 }
@@ -345,26 +347,14 @@
 		return NULL;
 	priv->global = global;
 
-	if (wpas_dbus_init_common(priv) < 0) {
-		wpas_dbus_deinit(priv);
-		return NULL;
-	}
-
+	if (wpas_dbus_init_common(priv) < 0 ||
 #ifdef CONFIG_CTRL_IFACE_DBUS_NEW
-	if (wpas_dbus_ctrl_iface_init(priv) < 0) {
-		wpas_dbus_deinit(priv);
-		return NULL;
-	}
+	    wpas_dbus_ctrl_iface_init(priv) < 0 ||
 #endif /* CONFIG_CTRL_IFACE_DBUS_NEW */
-
 #ifdef CONFIG_CTRL_IFACE_DBUS
-	if (wpa_supplicant_dbus_ctrl_iface_init(priv) < 0) {
-		wpas_dbus_deinit(priv);
-		return NULL;
-	}
+	    wpa_supplicant_dbus_ctrl_iface_init(priv) < 0 ||
 #endif /* CONFIG_CTRL_IFACE_DBUS */
-
-	if (wpas_dbus_init_common_finish(priv) < 0) {
+	    wpas_dbus_init_common_finish(priv) < 0) {
 		wpas_dbus_deinit(priv);
 		return NULL;
 	}
diff --git a/wpa_supplicant/dbus/dbus_dict_helpers.c b/wpa_supplicant/dbus/dbus_dict_helpers.c
index 949ce7c..a0c44eb 100644
--- a/wpa_supplicant/dbus/dbus_dict_helpers.c
+++ b/wpa_supplicant/dbus/dbus_dict_helpers.c
@@ -66,7 +66,7 @@
 
 const char * wpa_dbus_type_as_string(const int type)
 {
-	switch(type) {
+	switch (type) {
 	case DBUS_TYPE_BYTE:
 		return DBUS_TYPE_BYTE_AS_STRING;
 	case DBUS_TYPE_BOOLEAN:
@@ -106,11 +106,8 @@
 					      iter_dict_entry))
 		return FALSE;
 
-	if (!dbus_message_iter_append_basic(iter_dict_entry, DBUS_TYPE_STRING,
-					    &key))
-		return FALSE;
-
-	return TRUE;
+	return dbus_message_iter_append_basic(iter_dict_entry, DBUS_TYPE_STRING,
+					      &key);
 }
 
 
@@ -120,10 +117,8 @@
 {
 	if (!dbus_message_iter_close_container(iter_dict_entry, iter_dict_val))
 		return FALSE;
-	if (!dbus_message_iter_close_container(iter_dict, iter_dict_entry))
-		return FALSE;
 
-	return TRUE;
+	return dbus_message_iter_close_container(iter_dict, iter_dict_entry);
 }
 
 
@@ -143,22 +138,15 @@
 		return FALSE;
 
 	if (!_wpa_dbus_add_dict_entry_start(iter_dict, &iter_dict_entry,
-					    key, value_type))
-		return FALSE;
-
-	if (!dbus_message_iter_open_container(&iter_dict_entry,
+					    key, value_type) ||
+	    !dbus_message_iter_open_container(&iter_dict_entry,
 					      DBUS_TYPE_VARIANT,
-					      type_as_string, &iter_dict_val))
+					      type_as_string, &iter_dict_val) ||
+	    !dbus_message_iter_append_basic(&iter_dict_val, value_type, value))
 		return FALSE;
 
-	if (!dbus_message_iter_append_basic(&iter_dict_val, value_type, value))
-		return FALSE;
-
-	if (!_wpa_dbus_add_dict_entry_end(iter_dict, &iter_dict_entry,
-					  &iter_dict_val))
-		return FALSE;
-
-	return TRUE;
+	return _wpa_dbus_add_dict_entry_end(iter_dict, &iter_dict_entry,
+					    &iter_dict_val);
 }
 
 
@@ -170,17 +158,13 @@
 	dbus_uint32_t i;
 
 	if (!_wpa_dbus_add_dict_entry_start(iter_dict, &iter_dict_entry,
-					    key, DBUS_TYPE_ARRAY))
-		return FALSE;
-
-	if (!dbus_message_iter_open_container(&iter_dict_entry,
+					    key, DBUS_TYPE_ARRAY) ||
+	    !dbus_message_iter_open_container(&iter_dict_entry,
 					      DBUS_TYPE_VARIANT,
 					      DBUS_TYPE_ARRAY_AS_STRING
 					      DBUS_TYPE_BYTE_AS_STRING,
-					      &iter_dict_val))
-		return FALSE;
-
-	if (!dbus_message_iter_open_container(&iter_dict_val, DBUS_TYPE_ARRAY,
+					      &iter_dict_val) ||
+	    !dbus_message_iter_open_container(&iter_dict_val, DBUS_TYPE_ARRAY,
 					      DBUS_TYPE_BYTE_AS_STRING,
 					      &iter_array))
 		return FALSE;
@@ -195,11 +179,8 @@
 	if (!dbus_message_iter_close_container(&iter_dict_val, &iter_array))
 		return FALSE;
 
-	if (!_wpa_dbus_add_dict_entry_end(iter_dict, &iter_dict_entry,
-					  &iter_dict_val))
-		return FALSE;
-
-	return TRUE;
+	return _wpa_dbus_add_dict_entry_end(iter_dict, &iter_dict_entry,
+					    &iter_dict_val);
 }
 
 
@@ -428,9 +409,7 @@
 					    const char *value,
 					    const dbus_uint32_t value_len)
 {
-	if (!key)
-		return FALSE;
-	if (!value && (value_len != 0))
+	if (!key || (!value && value_len != 0))
 		return FALSE;
 	return _wpa_dbus_add_dict_entry_byte_array(iter_dict, key, value,
 						   value_len);
@@ -465,27 +444,20 @@
 	err = os_snprintf(array_type, sizeof(array_type),
 			  DBUS_TYPE_ARRAY_AS_STRING "%s",
 			  type);
-	if (err < 0 || err > (int) sizeof(array_type))
+	if (os_snprintf_error(sizeof(array_type), err))
 		return FALSE;
 
-	if (!iter_dict || !iter_dict_entry || !iter_dict_val || !iter_array)
-		return FALSE;
-
-	if (!_wpa_dbus_add_dict_entry_start(iter_dict, iter_dict_entry,
-					    key, DBUS_TYPE_ARRAY))
-		return FALSE;
-
-	if (!dbus_message_iter_open_container(iter_dict_entry,
+	if (!iter_dict || !iter_dict_entry || !iter_dict_val || !iter_array ||
+	    !_wpa_dbus_add_dict_entry_start(iter_dict, iter_dict_entry,
+					    key, DBUS_TYPE_ARRAY) ||
+	    !dbus_message_iter_open_container(iter_dict_entry,
 					      DBUS_TYPE_VARIANT,
 					      array_type,
 					      iter_dict_val))
 		return FALSE;
 
-	if (!dbus_message_iter_open_container(iter_dict_val, DBUS_TYPE_ARRAY,
-					      type, iter_array))
-		return FALSE;
-
-	return TRUE;
+	return dbus_message_iter_open_container(iter_dict_val, DBUS_TYPE_ARRAY,
+						type, iter_array);
 }
 
 
@@ -542,10 +514,8 @@
 	DBusMessageIter iter_bytes;
 	size_t i;
 
-	if (!iter_array || !value)
-		return FALSE;
-
-	if (!dbus_message_iter_open_container(iter_array, DBUS_TYPE_ARRAY,
+	if (!iter_array || !value ||
+	    !dbus_message_iter_open_container(iter_array, DBUS_TYPE_ARRAY,
 					      DBUS_TYPE_BYTE_AS_STRING,
 					      &iter_bytes))
 		return FALSE;
@@ -557,10 +527,7 @@
 			return FALSE;
 	}
 
-	if (!dbus_message_iter_close_container(iter_array, &iter_bytes))
-		return FALSE;
-
-	return TRUE;
+	return dbus_message_iter_close_container(iter_array, &iter_bytes);
 }
 
 
@@ -586,17 +553,12 @@
 				    DBusMessageIter *iter_dict_val,
 				    DBusMessageIter *iter_array)
 {
-	if (!iter_dict || !iter_dict_entry || !iter_dict_val || !iter_array)
+	if (!iter_dict || !iter_dict_entry || !iter_dict_val || !iter_array ||
+	    !dbus_message_iter_close_container(iter_dict_val, iter_array))
 		return FALSE;
 
-	if (!dbus_message_iter_close_container(iter_dict_val, iter_array))
-		return FALSE;
-
-	if (!_wpa_dbus_add_dict_entry_end(iter_dict, iter_dict_entry,
-					  iter_dict_val))
-		return FALSE;
-
-	return TRUE;
+	return _wpa_dbus_add_dict_entry_end(iter_dict, iter_dict_entry,
+					    iter_dict_val);
 }
 
 
@@ -619,12 +581,8 @@
 	DBusMessageIter iter_dict_entry, iter_dict_val, iter_array;
 	dbus_uint32_t i;
 
-	if (!key)
-		return FALSE;
-	if (!items && (num_items != 0))
-		return FALSE;
-
-	if (!wpa_dbus_dict_begin_string_array(iter_dict, key,
+	if (!key || (!items && num_items != 0) ||
+	    !wpa_dbus_dict_begin_string_array(iter_dict, key,
 					      &iter_dict_entry, &iter_dict_val,
 					      &iter_array))
 		return FALSE;
@@ -635,11 +593,8 @@
 			return FALSE;
 	}
 
-	if (!wpa_dbus_dict_end_string_array(iter_dict, &iter_dict_entry,
-					    &iter_dict_val, &iter_array))
-		return FALSE;
-
-	return TRUE;
+	return wpa_dbus_dict_end_string_array(iter_dict, &iter_dict_entry,
+					      &iter_dict_val, &iter_array);
 }
 
 
@@ -662,12 +617,9 @@
 	DBusMessageIter iter_dict_entry, iter_dict_val, iter_array;
 	dbus_uint32_t i;
 
-	if (!key)
-		return FALSE;
-	if (!items && (num_items != 0))
-		return FALSE;
-
-	if (!wpa_dbus_dict_begin_array(iter_dict, key,
+	if (!key ||
+	    (!items && num_items != 0) ||
+	    !wpa_dbus_dict_begin_array(iter_dict, key,
 				       DBUS_TYPE_ARRAY_AS_STRING
 				       DBUS_TYPE_BYTE_AS_STRING,
 				       &iter_dict_entry, &iter_dict_val,
@@ -681,11 +633,8 @@
 			return FALSE;
 	}
 
-	if (!wpa_dbus_dict_end_array(iter_dict, &iter_dict_entry,
-				     &iter_dict_val, &iter_array))
-		return FALSE;
-
-	return TRUE;
+	return wpa_dbus_dict_end_array(iter_dict, &iter_dict_entry,
+				       &iter_dict_val, &iter_array);
 }
 
 
@@ -707,16 +656,25 @@
 				    DBusMessageIter *iter_dict,
 				    DBusError *error)
 {
+	int type;
+
+	wpa_printf(MSG_MSGDUMP, "%s: start reading a dict entry", __func__);
 	if (!iter || !iter_dict) {
 		dbus_set_error_const(error, DBUS_ERROR_FAILED,
-		                     "[internal] missing message iterators");
+				     "[internal] missing message iterators");
 		return FALSE;
 	}
 
-	if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY ||
+	type = dbus_message_iter_get_arg_type(iter);
+	if (type != DBUS_TYPE_ARRAY ||
 	    dbus_message_iter_get_element_type(iter) != DBUS_TYPE_DICT_ENTRY) {
+		wpa_printf(MSG_DEBUG,
+			   "%s: unexpected message argument types (arg=%c element=%c)",
+			   __func__, type,
+			   type != DBUS_TYPE_ARRAY ? '?' :
+			   dbus_message_iter_get_element_type(iter));
 		dbus_set_error_const(error, DBUS_ERROR_INVALID_ARGS,
-		                     "unexpected message argument types");
+				     "unexpected message argument types");
 		return FALSE;
 	}
 
@@ -742,7 +700,6 @@
 	if (!buffer)
 		return FALSE;
 
-	entry->bytearray_value = buffer;
 	entry->array_len = 0;
 	while (dbus_message_iter_get_arg_type(iter) == DBUS_TYPE_BYTE) {
 		char byte;
@@ -753,21 +710,22 @@
 				BYTE_ARRAY_ITEM_SIZE);
 			if (nbuffer == NULL) {
 				os_free(buffer);
-				wpa_printf(MSG_ERROR, "dbus: _wpa_dbus_dict_"
-					   "entry_get_byte_array out of "
-					   "memory trying to retrieve the "
-					   "string array");
+				wpa_printf(MSG_ERROR,
+					   "dbus: %s out of memory trying to retrieve the string array",
+					   __func__);
 				goto done;
 			}
 			buffer = nbuffer;
 		}
-		entry->bytearray_value = buffer;
 
 		dbus_message_iter_get_basic(iter, &byte);
-		entry->bytearray_value[count] = byte;
+		buffer[count] = byte;
 		entry->array_len = ++count;
 		dbus_message_iter_next(iter);
 	}
+	entry->bytearray_value = buffer;
+	wpa_hexdump_key(MSG_MSGDUMP, "dbus: byte array contents",
+			entry->bytearray_value, entry->array_len);
 
 	/* Zero-length arrays are valid. */
 	if (entry->array_len == 0) {
@@ -790,18 +748,16 @@
 	struct wpa_dbus_dict_entry *entry)
 {
 	dbus_uint32_t count = 0;
-	dbus_bool_t success = FALSE;
 	char **buffer, **nbuffer;
 
 	entry->strarray_value = NULL;
+	entry->array_len = 0;
 	entry->array_type = DBUS_TYPE_STRING;
 
 	buffer = os_calloc(STR_ARRAY_CHUNK_SIZE, STR_ARRAY_ITEM_SIZE);
 	if (buffer == NULL)
 		return FALSE;
 
-	entry->strarray_value = buffer;
-	entry->array_len = 0;
 	while (dbus_message_iter_get_arg_type(iter) == DBUS_TYPE_STRING) {
 		const char *value;
 		char *str;
@@ -811,29 +767,31 @@
 				buffer, count + STR_ARRAY_CHUNK_SIZE,
 				STR_ARRAY_ITEM_SIZE);
 			if (nbuffer == NULL) {
-				os_free(buffer);
-				wpa_printf(MSG_ERROR, "dbus: _wpa_dbus_dict_"
-					   "entry_get_string_array out of "
-					   "memory trying to retrieve the "
-					   "string array");
-				goto done;
+				wpa_printf(MSG_ERROR,
+					   "dbus: %s out of memory trying to retrieve the string array",
+					   __func__);
+				goto fail;
 			}
 			buffer = nbuffer;
 		}
-		entry->strarray_value = buffer;
 
 		dbus_message_iter_get_basic(iter, &value);
+		wpa_printf(MSG_MSGDUMP, "%s: string_array value: %s",
+			   __func__, wpa_debug_show_keys ? value : "[omitted]");
 		str = os_strdup(value);
 		if (str == NULL) {
-			wpa_printf(MSG_ERROR, "dbus: _wpa_dbus_dict_entry_get_"
-				   "string_array out of memory trying to "
-				   "duplicate the string array");
-			goto done;
+			wpa_printf(MSG_ERROR,
+				   "dbus: %s out of memory trying to duplicate the string array",
+				   __func__);
+			goto fail;
 		}
-		entry->strarray_value[count] = str;
-		entry->array_len = ++count;
+		buffer[count++] = str;
 		dbus_message_iter_next(iter);
 	}
+	entry->strarray_value = buffer;
+	entry->array_len = count;
+	wpa_printf(MSG_MSGDUMP, "%s: string_array length %u",
+		   __func__, entry->array_len);
 
 	/* Zero-length arrays are valid. */
 	if (entry->array_len == 0) {
@@ -841,10 +799,15 @@
 		entry->strarray_value = NULL;
 	}
 
-	success = TRUE;
+	return TRUE;
 
-done:
-	return success;
+fail:
+	while (count > 0) {
+		count--;
+		os_free(buffer[count]);
+	}
+	os_free(buffer);
+	return FALSE;
 }
 
 
@@ -856,15 +819,31 @@
 {
 	struct wpa_dbus_dict_entry tmpentry;
 	size_t buflen = 0;
-	int i;
-
-	if (dbus_message_iter_get_element_type(iter) != DBUS_TYPE_BYTE)
-		return FALSE;
+	int i, type;
 
 	entry->array_type = WPAS_DBUS_TYPE_BINARRAY;
 	entry->array_len = 0;
 	entry->binarray_value = NULL;
 
+	type = dbus_message_iter_get_arg_type(iter);
+	wpa_printf(MSG_MSGDUMP, "%s: parsing binarray type %c", __func__, type);
+	if (type == DBUS_TYPE_INVALID) {
+		/* Likely an empty array of arrays */
+		return TRUE;
+	}
+	if (type != DBUS_TYPE_ARRAY) {
+		wpa_printf(MSG_DEBUG, "%s: not an array type: %c",
+			   __func__, type);
+		return FALSE;
+	}
+
+	type = dbus_message_iter_get_element_type(iter);
+	if (type != DBUS_TYPE_BYTE) {
+		wpa_printf(MSG_DEBUG, "%s: unexpected element type %c",
+			   __func__, type);
+		return FALSE;
+	}
+
 	while (dbus_message_iter_get_arg_type(iter) == DBUS_TYPE_ARRAY) {
 		DBusMessageIter iter_array;
 
@@ -884,7 +863,7 @@
 		os_memset(&tmpentry, 0, sizeof(tmpentry));
 		tmpentry.type = DBUS_TYPE_ARRAY;
 		if (_wpa_dbus_dict_entry_get_byte_array(&iter_array, &tmpentry)
-					== FALSE)
+		    == FALSE)
 			goto cleanup;
 
 		entry->binarray_value[entry->array_len] =
@@ -897,6 +876,8 @@
 		entry->array_len++;
 		dbus_message_iter_next(iter);
 	}
+	wpa_printf(MSG_MSGDUMP, "%s: binarray length %u",
+		   __func__, entry->array_len);
 
 	return TRUE;
 
@@ -917,12 +898,11 @@
 	dbus_bool_t success = FALSE;
 	DBusMessageIter iter_array;
 
-	if (!entry)
-		return FALSE;
+	wpa_printf(MSG_MSGDUMP, "%s: array_type %c", __func__, array_type);
 
 	dbus_message_iter_recurse(iter_dict_val, &iter_array);
 
- 	switch (array_type) {
+	switch (array_type) {
 	case DBUS_TYPE_BYTE:
 		success = _wpa_dbus_dict_entry_get_byte_array(&iter_array,
 							      entry);
@@ -936,6 +916,8 @@
 		success = _wpa_dbus_dict_entry_get_binarray(&iter_array, entry);
 		break;
 	default:
+		wpa_printf(MSG_MSGDUMP, "%s: unsupported array type %c",
+			   __func__, array_type);
 		break;
 	}
 
@@ -950,42 +932,72 @@
 
 	switch (entry->type) {
 	case DBUS_TYPE_OBJECT_PATH:
+		dbus_message_iter_get_basic(iter, &v);
+		wpa_printf(MSG_MSGDUMP, "%s: object path value: %s",
+			   __func__, v);
+		entry->str_value = os_strdup(v);
+		if (entry->str_value == NULL)
+			return FALSE;
+		break;
 	case DBUS_TYPE_STRING:
 		dbus_message_iter_get_basic(iter, &v);
+		wpa_printf(MSG_MSGDUMP, "%s: string value: %s",
+			   __func__, wpa_debug_show_keys ? v : "[omitted]");
 		entry->str_value = os_strdup(v);
 		if (entry->str_value == NULL)
 			return FALSE;
 		break;
 	case DBUS_TYPE_BOOLEAN:
 		dbus_message_iter_get_basic(iter, &entry->bool_value);
+		wpa_printf(MSG_MSGDUMP, "%s: boolean value: %d",
+			   __func__, entry->bool_value);
 		break;
 	case DBUS_TYPE_BYTE:
 		dbus_message_iter_get_basic(iter, &entry->byte_value);
+		wpa_printf(MSG_MSGDUMP, "%s: byte value: %d",
+			   __func__, entry->byte_value);
 		break;
 	case DBUS_TYPE_INT16:
 		dbus_message_iter_get_basic(iter, &entry->int16_value);
+		wpa_printf(MSG_MSGDUMP, "%s: int16 value: %d",
+			   __func__, entry->int16_value);
 		break;
 	case DBUS_TYPE_UINT16:
 		dbus_message_iter_get_basic(iter, &entry->uint16_value);
+		wpa_printf(MSG_MSGDUMP, "%s: uint16 value: %d",
+			   __func__, entry->uint16_value);
 		break;
 	case DBUS_TYPE_INT32:
 		dbus_message_iter_get_basic(iter, &entry->int32_value);
+		wpa_printf(MSG_MSGDUMP, "%s: int32 value: %d",
+			   __func__, entry->int32_value);
 		break;
 	case DBUS_TYPE_UINT32:
 		dbus_message_iter_get_basic(iter, &entry->uint32_value);
+		wpa_printf(MSG_MSGDUMP, "%s: uint32 value: %d",
+			   __func__, entry->uint32_value);
 		break;
 	case DBUS_TYPE_INT64:
 		dbus_message_iter_get_basic(iter, &entry->int64_value);
+		wpa_printf(MSG_MSGDUMP, "%s: int64 value: %lld",
+			   __func__, (long long int) entry->int64_value);
 		break;
 	case DBUS_TYPE_UINT64:
 		dbus_message_iter_get_basic(iter, &entry->uint64_value);
+		wpa_printf(MSG_MSGDUMP, "%s: uint64 value: %llu",
+			   __func__,
+			   (unsigned long long int) entry->uint64_value);
 		break;
 	case DBUS_TYPE_DOUBLE:
 		dbus_message_iter_get_basic(iter, &entry->double_value);
+		wpa_printf(MSG_MSGDUMP, "%s: double value: %f",
+			   __func__, entry->double_value);
 		break;
 	case DBUS_TYPE_ARRAY:
 		return _wpa_dbus_dict_entry_get_array(iter, entry);
 	default:
+		wpa_printf(MSG_MSGDUMP, "%s: unsupported type %c",
+			   __func__, entry->type);
 		return FALSE;
 	}
 
@@ -1016,26 +1028,40 @@
 	int type;
 	const char *key;
 
-	if (!iter_dict || !entry)
+	if (!iter_dict || !entry ||
+	    dbus_message_iter_get_arg_type(iter_dict) != DBUS_TYPE_DICT_ENTRY) {
+		wpa_printf(MSG_DEBUG, "%s: not a dict entry", __func__);
 		goto error;
-
-	if (dbus_message_iter_get_arg_type(iter_dict) != DBUS_TYPE_DICT_ENTRY)
-		goto error;
+	}
 
 	dbus_message_iter_recurse(iter_dict, &iter_dict_entry);
 	dbus_message_iter_get_basic(&iter_dict_entry, &key);
+	wpa_printf(MSG_MSGDUMP, "%s: dict entry key: %s", __func__, key);
 	entry->key = key;
 
-	if (!dbus_message_iter_next(&iter_dict_entry))
+	if (!dbus_message_iter_next(&iter_dict_entry)) {
+		wpa_printf(MSG_DEBUG, "%s: no variant in dict entry", __func__);
 		goto error;
+	}
 	type = dbus_message_iter_get_arg_type(&iter_dict_entry);
-	if (type != DBUS_TYPE_VARIANT)
+	if (type != DBUS_TYPE_VARIANT) {
+		wpa_printf(MSG_DEBUG,
+			   "%s: unexpected dict entry variant type: %c",
+			   __func__, type);
 		goto error;
+	}
 
 	dbus_message_iter_recurse(&iter_dict_entry, &iter_dict_val);
 	entry->type = dbus_message_iter_get_arg_type(&iter_dict_val);
-	if (!_wpa_dbus_dict_fill_value_from_variant(entry, &iter_dict_val))
+	wpa_printf(MSG_MSGDUMP, "%s: dict entry variant content type: %c",
+		   __func__, entry->type);
+	entry->array_type = DBUS_TYPE_INVALID;
+	if (!_wpa_dbus_dict_fill_value_from_variant(entry, &iter_dict_val)) {
+		wpa_printf(MSG_DEBUG,
+			   "%s: failed to fetch dict values from variant",
+			   __func__);
 		goto error;
+	}
 
 	dbus_message_iter_next(iter_dict);
 	return TRUE;
@@ -1090,6 +1116,8 @@
 			os_free(entry->bytearray_value);
 			break;
 		case DBUS_TYPE_STRING:
+			if (!entry->strarray_value)
+				break;
 			for (i = 0; i < entry->array_len; i++)
 				os_free(entry->strarray_value[i]);
 			os_free(entry->strarray_value);
diff --git a/wpa_supplicant/dbus/dbus_dict_helpers.h b/wpa_supplicant/dbus/dbus_dict_helpers.h
index 9666349..b068431 100644
--- a/wpa_supplicant/dbus/dbus_dict_helpers.h
+++ b/wpa_supplicant/dbus/dbus_dict_helpers.h
@@ -72,28 +72,28 @@
 
 /* Manual construction and addition of array elements */
 dbus_bool_t wpa_dbus_dict_begin_array(DBusMessageIter *iter_dict,
-                                      const char *key, const char *type,
-                                      DBusMessageIter *iter_dict_entry,
-                                      DBusMessageIter *iter_dict_val,
-                                      DBusMessageIter *iter_array);
+				      const char *key, const char *type,
+				      DBusMessageIter *iter_dict_entry,
+				      DBusMessageIter *iter_dict_val,
+				      DBusMessageIter *iter_array);
 
 dbus_bool_t wpa_dbus_dict_begin_string_array(DBusMessageIter *iter_dict,
-                                             const char *key,
-                                             DBusMessageIter *iter_dict_entry,
-                                             DBusMessageIter *iter_dict_val,
-                                             DBusMessageIter *iter_array);
+					     const char *key,
+					     DBusMessageIter *iter_dict_entry,
+					     DBusMessageIter *iter_dict_val,
+					     DBusMessageIter *iter_array);
 
 dbus_bool_t wpa_dbus_dict_string_array_add_element(DBusMessageIter *iter_array,
-                                             const char *elem);
+						   const char *elem);
 
 dbus_bool_t wpa_dbus_dict_bin_array_add_element(DBusMessageIter *iter_array,
 						const u8 *value,
 						size_t value_len);
 
 dbus_bool_t wpa_dbus_dict_end_array(DBusMessageIter *iter_dict,
-                                    DBusMessageIter *iter_dict_entry,
-                                    DBusMessageIter *iter_dict_val,
-                                    DBusMessageIter *iter_array);
+				    DBusMessageIter *iter_dict_entry,
+				    DBusMessageIter *iter_dict_val,
+				    DBusMessageIter *iter_array);
 
 static inline dbus_bool_t
 wpa_dbus_dict_end_string_array(DBusMessageIter *iter_dict,
@@ -120,7 +120,11 @@
  * Reading a dict from a DBusMessage
  */
 
-#define WPAS_DBUS_TYPE_BINARRAY (DBUS_NUMBER_OF_TYPES + 100)
+/*
+ * Used only in struct wpa_dbus_dict_entry::array_type internally to identify
+ * special binary array case.
+ */
+#define WPAS_DBUS_TYPE_BINARRAY ((int) '@')
 
 struct wpa_dbus_dict_entry {
 	int type;         /** the dbus type of the dict entry's value */
diff --git a/wpa_supplicant/dbus/dbus_new.c b/wpa_supplicant/dbus/dbus_new.c
index 5e58c5b..1959ea7 100644
--- a/wpa_supplicant/dbus/dbus_new.c
+++ b/wpa_supplicant/dbus/dbus_new.c
@@ -75,8 +75,7 @@
 			return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
 		}
 
-		for (wpa_s = priv->global->ifaces; wpa_s; wpa_s = wpa_s->next)
-		{
+		for (wpa_s = priv->global->ifaces; wpa_s; wpa_s = wpa_s->next) {
 			if (wpa_s->preq_notify_peer != NULL &&
 			    os_strcmp(name, wpa_s->preq_notify_peer) == 0 &&
 			    (new_owner == NULL || os_strlen(new_owner) == 0)) {
@@ -138,7 +137,7 @@
 	iface = wpa_s->global->dbus;
 
 	/* Do nothing if the control interface is not turned on */
-	if (iface == NULL)
+	if (iface == NULL || !wpa_s->dbus_new_path)
 		return;
 
 	msg = dbus_message_new_signal(WPAS_DBUS_NEW_PATH,
@@ -148,22 +147,14 @@
 
 	dbus_message_iter_init_append(msg, &iter);
 	if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH,
-					    &wpa_s->dbus_new_path))
-		goto err;
-
-	if (properties) {
-		if (!wpa_dbus_get_object_properties(
-			    iface, wpa_s->dbus_new_path,
-			    WPAS_DBUS_NEW_IFACE_INTERFACE, &iter))
-			goto err;
-	}
-
-	dbus_connection_send(iface->con, msg, NULL);
-	dbus_message_unref(msg);
-	return;
-
-err:
-	wpa_printf(MSG_ERROR, "dbus: Failed to construct signal");
+					    &wpa_s->dbus_new_path) ||
+	    (properties &&
+	     !wpa_dbus_get_object_properties(
+		     iface, wpa_s->dbus_new_path,
+		     WPAS_DBUS_NEW_IFACE_INTERFACE, &iter)))
+		wpa_printf(MSG_ERROR, "dbus: Failed to construct signal");
+	else
+		dbus_connection_send(iface->con, msg, NULL);
 	dbus_message_unref(msg);
 }
 
@@ -209,7 +200,7 @@
 	iface = wpa_s->global->dbus;
 
 	/* Do nothing if the control interface is not turned on */
-	if (iface == NULL)
+	if (iface == NULL || !wpa_s->dbus_new_path)
 		return;
 
 	msg = dbus_message_new_signal(wpa_s->dbus_new_path,
@@ -229,7 +220,7 @@
 
 
 /**
- * wpas_dbus_signal_blob - Send a BSS related event signal
+ * wpas_dbus_signal_bss - Send a BSS related event signal
  * @wpa_s: %wpa_supplicant network interface data
  * @bss_obj_path: BSS object path
  * @sig_name: signal name - BSSAdded or BSSRemoved
@@ -248,7 +239,7 @@
 	iface = wpa_s->global->dbus;
 
 	/* Do nothing if the control interface is not turned on */
-	if (iface == NULL)
+	if (iface == NULL || !wpa_s->dbus_new_path)
 		return;
 
 	msg = dbus_message_new_signal(wpa_s->dbus_new_path,
@@ -259,22 +250,14 @@
 
 	dbus_message_iter_init_append(msg, &iter);
 	if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH,
-					    &bss_obj_path))
-		goto err;
-
-	if (properties) {
-		if (!wpa_dbus_get_object_properties(iface, bss_obj_path,
-						    WPAS_DBUS_NEW_IFACE_BSS,
-						    &iter))
-			goto err;
-	}
-
-	dbus_connection_send(iface->con, msg, NULL);
-	dbus_message_unref(msg);
-	return;
-
-err:
-	wpa_printf(MSG_ERROR, "dbus: Failed to construct signal");
+					    &bss_obj_path) ||
+	    (properties &&
+	     !wpa_dbus_get_object_properties(iface, bss_obj_path,
+					     WPAS_DBUS_NEW_IFACE_BSS,
+					     &iter)))
+		wpa_printf(MSG_ERROR, "dbus: Failed to construct signal");
+	else
+		dbus_connection_send(iface->con, msg, NULL);
 	dbus_message_unref(msg);
 }
 
@@ -324,7 +307,7 @@
 	iface = wpa_s->global->dbus;
 
 	/* Do nothing if the control interface is not turned on */
-	if (iface == NULL)
+	if (iface == NULL || !wpa_s->dbus_new_path)
 		return;
 
 	msg = dbus_message_new_signal(wpa_s->dbus_new_path,
@@ -391,7 +374,7 @@
 	iface = wpa_s->global->dbus;
 
 	/* Do nothing if the control interface is not turned on */
-	if (iface == NULL)
+	if (iface == NULL || !wpa_s->dbus_new_path)
 		return;
 
 	os_snprintf(net_obj_path, WPAS_DBUS_OBJECT_PATH_MAX,
@@ -407,23 +390,14 @@
 	dbus_message_iter_init_append(msg, &iter);
 	path = net_obj_path;
 	if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH,
-					    &path))
-		goto err;
-
-	if (properties) {
-		if (!wpa_dbus_get_object_properties(
-			    iface, net_obj_path, WPAS_DBUS_NEW_IFACE_NETWORK,
-			    &iter))
-			goto err;
-	}
-
-	dbus_connection_send(iface->con, msg, NULL);
-
-	dbus_message_unref(msg);
-	return;
-
-err:
-	wpa_printf(MSG_ERROR, "dbus: Failed to construct signal");
+					    &path) ||
+	    (properties &&
+	     !wpa_dbus_get_object_properties(
+		     iface, net_obj_path, WPAS_DBUS_NEW_IFACE_NETWORK,
+		     &iter)))
+		wpa_printf(MSG_ERROR, "dbus: Failed to construct signal");
+	else
+		dbus_connection_send(iface->con, msg, NULL);
 	dbus_message_unref(msg);
 }
 
@@ -493,7 +467,7 @@
 	iface = wpa_s->global->dbus;
 
 	/* Do nothing if the control interface is not turned on */
-	if (iface == NULL)
+	if (iface == NULL || !wpa_s->dbus_new_path)
 		return;
 
 	field = wpa_supplicant_ctrl_req_to_string(rtype, default_txt, &txt);
@@ -513,19 +487,12 @@
 
 	dbus_message_iter_init_append(msg, &iter);
 	if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH,
-					    &net_ptr))
-		goto err;
-	if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &field))
-		goto err;
-	if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &txt))
-		goto err;
-
-	dbus_connection_send(iface->con, msg, NULL);
-	dbus_message_unref(msg);
-	return;
-
-err:
-	wpa_printf(MSG_ERROR, "dbus: Failed to construct signal");
+					    &net_ptr) ||
+	    !dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &field) ||
+	    !dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &txt))
+		wpa_printf(MSG_ERROR, "dbus: Failed to construct signal");
+	else
+		dbus_connection_send(iface->con, msg, NULL);
 	dbus_message_unref(msg);
 }
 
@@ -543,6 +510,9 @@
 {
 
 	char path[WPAS_DBUS_OBJECT_PATH_MAX];
+
+	if (!wpa_s->dbus_new_path)
+		return;
 	os_snprintf(path, WPAS_DBUS_OBJECT_PATH_MAX,
 		    "%s/" WPAS_DBUS_NEW_NETWORKS_PART "/%d",
 		    wpa_s->dbus_new_path, ssid->id);
@@ -555,6 +525,44 @@
 #ifdef CONFIG_WPS
 
 /**
+ * wpas_dbus_signal_wps_event_pbc_overlap - Signals PBC overlap WPS event
+ * @wpa_s: %wpa_supplicant network interface data
+ *
+ * Sends Event dbus signal with name "pbc-overlap" and empty dict as arguments
+ */
+void wpas_dbus_signal_wps_event_pbc_overlap(struct wpa_supplicant *wpa_s)
+{
+
+	DBusMessage *msg;
+	DBusMessageIter iter, dict_iter;
+	struct wpas_dbus_priv *iface;
+	char *key = "pbc-overlap";
+
+	iface = wpa_s->global->dbus;
+
+	/* Do nothing if the control interface is not turned on */
+	if (iface == NULL || !wpa_s->dbus_new_path)
+		return;
+
+	msg = dbus_message_new_signal(wpa_s->dbus_new_path,
+				      WPAS_DBUS_NEW_IFACE_WPS, "Event");
+	if (msg == NULL)
+		return;
+
+	dbus_message_iter_init_append(msg, &iter);
+
+	if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &key) ||
+	    !wpa_dbus_dict_open_write(&iter, &dict_iter) ||
+	    !wpa_dbus_dict_close_write(&iter, &dict_iter))
+		wpa_printf(MSG_ERROR, "dbus: Failed to construct signal");
+	else
+		dbus_connection_send(iface->con, msg, NULL);
+
+	dbus_message_unref(msg);
+}
+
+
+/**
  * wpas_dbus_signal_wps_event_success - Signals Success WPS event
  * @wpa_s: %wpa_supplicant network interface data
  *
@@ -571,7 +579,7 @@
 	iface = wpa_s->global->dbus;
 
 	/* Do nothing if the control interface is not turned on */
-	if (iface == NULL)
+	if (iface == NULL || !wpa_s->dbus_new_path)
 		return;
 
 	msg = dbus_message_new_signal(wpa_s->dbus_new_path,
@@ -595,6 +603,7 @@
 /**
  * wpas_dbus_signal_wps_event_fail - Signals Fail WPS event
  * @wpa_s: %wpa_supplicant network interface data
+ * @fail: WPS failure information
  *
  * Sends Event dbus signal with name "fail" and dictionary containing
  * "msg field with fail message number (int32) as arguments
@@ -611,7 +620,7 @@
 	iface = wpa_s->global->dbus;
 
 	/* Do nothing if the control interface is not turned on */
-	if (iface == NULL)
+	if (iface == NULL || !wpa_s->dbus_new_path)
 		return;
 
 	msg = dbus_message_new_signal(wpa_s->dbus_new_path,
@@ -636,6 +645,7 @@
 /**
  * wpas_dbus_signal_wps_event_m2d - Signals M2D WPS event
  * @wpa_s: %wpa_supplicant network interface data
+ * @m2d: M2D event data information
  *
  * Sends Event dbus signal with name "m2d" and dictionary containing
  * fields of wps_event_m2d structure.
@@ -652,7 +662,7 @@
 	iface = wpa_s->global->dbus;
 
 	/* Do nothing if the control interface is not turned on */
-	if (iface == NULL)
+	if (iface == NULL || !wpa_s->dbus_new_path)
 		return;
 
 	msg = dbus_message_new_signal(wpa_s->dbus_new_path,
@@ -701,6 +711,7 @@
 /**
  * wpas_dbus_signal_wps_cred - Signals new credentials
  * @wpa_s: %wpa_supplicant network interface data
+ * @cred: WPS Credential information
  *
  * Sends signal with credentials in directory argument
  */
@@ -718,7 +729,7 @@
 	iface = wpa_s->global->dbus;
 
 	/* Do nothing if the control interface is not turned on */
-	if (iface == NULL)
+	if (iface == NULL || !wpa_s->dbus_new_path)
 		return;
 
 	msg = dbus_message_new_signal(wpa_s->dbus_new_path,
@@ -749,15 +760,11 @@
 	if (cred->encr_type & WPS_ENCR_AES)
 		encr_type[et_num++] = "aes";
 
-	if (wpa_s->current_ssid) {
-		if (!wpa_dbus_dict_append_byte_array(
-			    &dict_iter, "BSSID",
-			    (const char *) wpa_s->current_ssid->bssid,
-			    ETH_ALEN))
-			goto nomem;
-	}
-
-	if (!wpa_dbus_dict_append_byte_array(&dict_iter, "SSID",
+	if ((wpa_s->current_ssid &&
+	     !wpa_dbus_dict_append_byte_array(
+		     &dict_iter, "BSSID",
+		     (const char *) wpa_s->current_ssid->bssid, ETH_ALEN)) ||
+	    !wpa_dbus_dict_append_byte_array(&dict_iter, "SSID",
 					     (const char *) cred->ssid,
 					     cred->ssid_len) ||
 	    !wpa_dbus_dict_append_string_array(&dict_iter, "AuthType",
@@ -784,6 +791,8 @@
 
 void wpas_dbus_signal_certification(struct wpa_supplicant *wpa_s,
 				    int depth, const char *subject,
+				    const char *altsubject[],
+				    int num_altsubject,
 				    const char *cert_hash,
 				    const struct wpabuf *cert)
 {
@@ -794,7 +803,7 @@
 	iface = wpa_s->global->dbus;
 
 	/* Do nothing if the control interface is not turned on */
-	if (iface == NULL)
+	if (iface == NULL || !wpa_s->dbus_new_path)
 		return;
 
 	msg = dbus_message_new_signal(wpa_s->dbus_new_path,
@@ -804,29 +813,23 @@
 		return;
 
 	dbus_message_iter_init_append(msg, &iter);
-	if (!wpa_dbus_dict_open_write(&iter, &dict_iter))
-		goto nomem;
-
-	if (!wpa_dbus_dict_append_uint32(&dict_iter, "depth", depth) ||
-	    !wpa_dbus_dict_append_string(&dict_iter, "subject", subject))
-		goto nomem;
-
-	if (cert_hash &&
-	    !wpa_dbus_dict_append_string(&dict_iter, "cert_hash", cert_hash))
-		goto nomem;
-
-	if (cert &&
-	    !wpa_dbus_dict_append_byte_array(&dict_iter, "cert",
-					     wpabuf_head(cert),
-					     wpabuf_len(cert)))
-		goto nomem;
-
-	if (!wpa_dbus_dict_close_write(&iter, &dict_iter))
-		goto nomem;
-
-	dbus_connection_send(iface->con, msg, NULL);
-
-nomem:
+	if (!wpa_dbus_dict_open_write(&iter, &dict_iter) ||
+	    !wpa_dbus_dict_append_uint32(&dict_iter, "depth", depth) ||
+	    !wpa_dbus_dict_append_string(&dict_iter, "subject", subject) ||
+	    (altsubject && num_altsubject &&
+	     !wpa_dbus_dict_append_string_array(&dict_iter, "altsubject",
+						altsubject, num_altsubject)) ||
+	    (cert_hash &&
+	     !wpa_dbus_dict_append_string(&dict_iter, "cert_hash",
+					  cert_hash)) ||
+	    (cert &&
+	     !wpa_dbus_dict_append_byte_array(&dict_iter, "cert",
+					      wpabuf_head(cert),
+					      wpabuf_len(cert))) ||
+	    !wpa_dbus_dict_close_write(&iter, &dict_iter))
+		wpa_printf(MSG_ERROR, "dbus: Failed to construct signal");
+	else
+		dbus_connection_send(iface->con, msg, NULL);
 	dbus_message_unref(msg);
 }
 
@@ -841,7 +844,7 @@
 	iface = wpa_s->global->dbus;
 
 	/* Do nothing if the control interface is not turned on */
-	if (iface == NULL)
+	if (iface == NULL || !wpa_s->dbus_new_path)
 		return;
 
 	msg = dbus_message_new_signal(wpa_s->dbus_new_path,
@@ -852,15 +855,12 @@
 
 	dbus_message_iter_init_append(msg, &iter);
 
-	if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &status)
-	    ||
+	if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &status) ||
 	    !dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING,
 					    &parameter))
-		goto nomem;
-
-	dbus_connection_send(iface->con, msg, NULL);
-
-nomem:
+		wpa_printf(MSG_ERROR, "dbus: Failed to construct signal");
+	else
+		dbus_connection_send(iface->con, msg, NULL);
 	dbus_message_unref(msg);
 }
 
@@ -887,7 +887,7 @@
 	iface = wpa_s->global->dbus;
 
 	/* Do nothing if the control interface is not turned on */
-	if (iface == NULL)
+	if (iface == NULL || !wpa_s->dbus_new_path)
 		return;
 
 	msg = dbus_message_new_signal(wpa_s->dbus_new_path,
@@ -946,49 +946,41 @@
 void wpas_dbus_signal_p2p_group_removed(struct wpa_supplicant *wpa_s,
 					const char *role)
 {
-	int error = 1;
 	DBusMessage *msg;
 	DBusMessageIter iter, dict_iter;
 	struct wpas_dbus_priv *iface = wpa_s->global->dbus;
+	struct wpa_supplicant *parent;
 
 	/* Do nothing if the control interface is not turned on */
 	if (iface == NULL)
 		return;
 
-	if (!wpa_s->dbus_groupobj_path)
+	parent = wpa_s->parent;
+	if (parent->p2p_mgmt)
+		parent = parent->parent;
+
+	if (!wpa_s->dbus_groupobj_path || !wpa_s->dbus_new_path ||
+	    !parent->dbus_new_path)
 		return;
 
-	msg = dbus_message_new_signal(wpa_s->parent->dbus_new_path,
+	msg = dbus_message_new_signal(parent->dbus_new_path,
 				      WPAS_DBUS_NEW_IFACE_P2PDEVICE,
 				      "GroupFinished");
 	if (msg == NULL)
 		return;
 
 	dbus_message_iter_init_append(msg, &iter);
-	if (!wpa_dbus_dict_open_write(&iter, &dict_iter))
-		goto nomem;
-
-	if (!wpa_dbus_dict_append_object_path(&dict_iter,
+	if (!wpa_dbus_dict_open_write(&iter, &dict_iter) ||
+	    !wpa_dbus_dict_append_object_path(&dict_iter,
 					      "interface_object",
-					      wpa_s->dbus_new_path))
-		goto nomem;
-
-	if (!wpa_dbus_dict_append_string(&dict_iter, "role", role))
-		goto nomem;
-
-	if (!wpa_dbus_dict_append_object_path(&dict_iter, "group_object",
+					      wpa_s->dbus_new_path) ||
+	    !wpa_dbus_dict_append_string(&dict_iter, "role", role) ||
+	    !wpa_dbus_dict_append_object_path(&dict_iter, "group_object",
 					      wpa_s->dbus_groupobj_path) ||
 	    !wpa_dbus_dict_close_write(&iter, &dict_iter))
-		goto nomem;
-
-	error = 0;
-	dbus_connection_send(iface->con, msg, NULL);
-
-nomem:
-	if (error > 0)
-		wpa_printf(MSG_ERROR,
-			   "dbus: Failed to construct GroupFinished");
-
+		wpa_printf(MSG_ERROR, "dbus: Failed to construct signal");
+	else
+		dbus_connection_send(iface->con, msg, NULL);
 	dbus_message_unref(msg);
 }
 
@@ -1034,6 +1026,11 @@
 	if (iface == NULL)
 		return;
 
+	if (wpa_s->p2p_mgmt)
+		wpa_s = wpa_s->parent;
+	if (!wpa_s->dbus_new_path)
+		return;
+
 	if (request || !status) {
 		if (config_methods & WPS_CONFIG_DISPLAY)
 			_signal = request ?
@@ -1048,9 +1045,10 @@
 				   "ProvisionDiscoveryPBCResponse";
 		else
 			return; /* Unknown or un-supported method */
-	} else if (!request && status)
+	} else {
 		/* Explicit check for failure response */
 		_signal = "ProvisionDiscoveryFailure";
+	}
 
 	add_pin = ((request && (config_methods & WPS_CONFIG_DISPLAY)) ||
 		   (!request && !status &&
@@ -1105,8 +1103,19 @@
 }
 
 
+/**
+ * wpas_dbus_signal_p2p_go_neg_req - Signal P2P GO Negotiation Request RX
+ * @wpa_s: %wpa_supplicant network interface data
+ * @src: Source address of the message triggering this notification
+ * @dev_passwd_id: WPS Device Password Id
+ * @go_intent: Peer's GO Intent value
+ *
+ * Sends signal to notify that a peer P2P Device is requesting group owner
+ * negotiation with us.
+ */
 void wpas_dbus_signal_p2p_go_neg_req(struct wpa_supplicant *wpa_s,
-				     const u8 *src, u16 dev_passwd_id)
+				     const u8 *src, u16 dev_passwd_id,
+				     u8 go_intent)
 {
 	DBusMessage *msg;
 	DBusMessageIter iter;
@@ -1119,6 +1128,11 @@
 	if (iface == NULL)
 		return;
 
+	if (wpa_s->p2p_mgmt)
+		wpa_s = wpa_s->parent;
+	if (!wpa_s->dbus_new_path)
+		return;
+
 	os_snprintf(peer_obj_path, WPAS_DBUS_OBJECT_PATH_MAX,
 		    "%s/" WPAS_DBUS_NEW_P2P_PEERS_PART "/" COMPACT_MACSTR,
 		    wpa_s->dbus_new_path, MAC2STR(src));
@@ -1135,7 +1149,9 @@
 	if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH,
 					    &path) ||
 	    !dbus_message_iter_append_basic(&iter, DBUS_TYPE_UINT16,
-					    &dev_passwd_id))
+					    &dev_passwd_id) ||
+	    !dbus_message_iter_append_basic(&iter, DBUS_TYPE_BYTE,
+					    &go_intent))
 		wpa_printf(MSG_ERROR, "dbus: Failed to construct signal");
 	else
 		dbus_connection_send(iface->con, msg, NULL);
@@ -1150,7 +1166,8 @@
 {
 	char group_name[3];
 
-	if (os_memcmp(ssid->ssid, P2P_WILDCARD_SSID, P2P_WILDCARD_SSID_LEN))
+	if (!wpa_s->dbus_new_path ||
+	    os_memcmp(ssid->ssid, P2P_WILDCARD_SSID, P2P_WILDCARD_SSID_LEN))
 		return -1;
 
 	os_memcpy(group_name, ssid->ssid + P2P_WILDCARD_SSID_LEN, 2);
@@ -1245,61 +1262,57 @@
 	DBusMessage *msg;
 	DBusMessageIter iter, dict_iter;
 	struct wpas_dbus_priv *iface;
+	struct wpa_supplicant *parent;
 
-	iface = wpa_s->parent->global->dbus;
+	parent = wpa_s->parent;
+	if (parent->p2p_mgmt)
+		parent = parent->parent;
+
+	iface = parent->global->dbus;
 
 	/* Do nothing if the control interface is not turned on */
-	if (iface == NULL)
+	if (iface == NULL || !parent->dbus_new_path || !wpa_s->dbus_new_path)
 		return;
 
 	if (wpa_s->dbus_groupobj_path == NULL)
 		return;
 
 	/* New interface has been created for this group */
-	msg = dbus_message_new_signal(wpa_s->parent->dbus_new_path,
+	msg = dbus_message_new_signal(parent->dbus_new_path,
 				      WPAS_DBUS_NEW_IFACE_P2PDEVICE,
 				      "GroupStarted");
 	if (msg == NULL)
 		return;
 
 	dbus_message_iter_init_append(msg, &iter);
-	if (!wpa_dbus_dict_open_write(&iter, &dict_iter))
-		goto nomem;
-
 	/*
 	 * In case the device supports creating a separate interface the
 	 * DBus client will need to know the object path for the interface
 	 * object this group was created on, so include it here.
 	 */
-	if (!wpa_dbus_dict_append_object_path(&dict_iter,
-					"interface_object",
-					wpa_s->dbus_new_path))
-		goto nomem;
-
-	if (!wpa_dbus_dict_append_string(&dict_iter, "role",
-					 client ? "client" : "GO"))
-		goto nomem;
-
-	if (!wpa_dbus_dict_append_object_path(&dict_iter, "group_object",
+	if (!wpa_dbus_dict_open_write(&iter, &dict_iter) ||
+	    !wpa_dbus_dict_append_object_path(&dict_iter,
+					      "interface_object",
+					      wpa_s->dbus_new_path) ||
+	    !wpa_dbus_dict_append_string(&dict_iter, "role",
+					 client ? "client" : "GO") ||
+	    !wpa_dbus_dict_append_object_path(&dict_iter, "group_object",
 					      wpa_s->dbus_groupobj_path) ||
-	   !wpa_dbus_dict_close_write(&iter, &dict_iter))
-		goto nomem;
-
-	dbus_connection_send(iface->con, msg, NULL);
-
-	if (client)
-		peer_groups_changed(wpa_s);
-
-nomem:
+	    !wpa_dbus_dict_close_write(&iter, &dict_iter)) {
+		wpa_printf(MSG_ERROR, "dbus: Failed to construct signal");
+	} else {
+		dbus_connection_send(iface->con, msg, NULL);
+		if (client)
+			peer_groups_changed(wpa_s);
+	}
 	dbus_message_unref(msg);
 }
 
 
 /**
- *
- * Method to emit GONegotiation Success or Failure signals based
- * on status.
- * @status: Status of the GO neg request. 0 for success, other for errors.
+ * wpas_dbus_signal_p2p_go_neg_resp - Emit GONegotiation Success/Failure signal
+ * @wpa_s: %wpa_supplicant network interface data
+ * @res: Result of the GO Neg Request
  */
 void wpas_dbus_signal_p2p_go_neg_resp(struct wpa_supplicant *wpa_s,
 				      struct p2p_go_neg_results *res)
@@ -1315,9 +1328,12 @@
 
 	iface = wpa_s->global->dbus;
 
+	if (wpa_s->p2p_mgmt)
+		wpa_s = wpa_s->parent;
+
 	os_memset(freqs, 0, sizeof(freqs));
 	/* Do nothing if the control interface is not turned on */
-	if (iface == NULL)
+	if (iface == NULL || !wpa_s->dbus_new_path)
 		return;
 
 	os_snprintf(peer_obj_path, WPAS_DBUS_OBJECT_PATH_MAX,
@@ -1333,9 +1349,8 @@
 		return;
 
 	dbus_message_iter_init_append(msg, &iter);
-	if (!wpa_dbus_dict_open_write(&iter, &dict_iter))
-		goto err;
-	if (!wpa_dbus_dict_append_object_path(&dict_iter, "peer_object",
+	if (!wpa_dbus_dict_open_write(&iter, &dict_iter) ||
+	    !wpa_dbus_dict_append_object_path(&dict_iter, "peer_object",
 					      path) ||
 	    !wpa_dbus_dict_append_int32(&dict_iter, "status", res->status))
 		goto err;
@@ -1344,15 +1359,10 @@
 		int i = 0;
 		int freq_list_num = 0;
 
-		if (res->role_go) {
-			if (!wpa_dbus_dict_append_byte_array(
-				    &dict_iter, "passphrase",
-				    (const char *) res->passphrase,
-				    sizeof(res->passphrase)))
-				goto err;
-		}
-
-		if (!wpa_dbus_dict_append_string(&dict_iter, "role_go",
+		if ((res->role_go &&
+		     !wpa_dbus_dict_append_string(&dict_iter, "passphrase",
+						  res->passphrase)) ||
+		    !wpa_dbus_dict_append_string(&dict_iter, "role_go",
 						 res->role_go ? "GO" :
 						 "client") ||
 		    !wpa_dbus_dict_append_int32(&dict_iter, "frequency",
@@ -1387,22 +1397,16 @@
 					       DBUS_TYPE_INT32_AS_STRING,
 					       &iter_dict_entry,
 					       &iter_dict_val,
-					       &iter_dict_array))
-			goto err;
-
-		if (!dbus_message_iter_append_fixed_array(&iter_dict_array,
+					       &iter_dict_array) ||
+		    !dbus_message_iter_append_fixed_array(&iter_dict_array,
 							  DBUS_TYPE_INT32,
 							  &f_array,
-							  freq_list_num))
-			goto err;
-
-		if (!wpa_dbus_dict_end_array(&dict_iter,
+							  freq_list_num) ||
+		    !wpa_dbus_dict_end_array(&dict_iter,
 					     &iter_dict_entry,
 					     &iter_dict_val,
-					     &iter_dict_array))
-			goto err;
-
-		if (!wpa_dbus_dict_append_int32(&dict_iter, "persistent_group",
+					     &iter_dict_array) ||
+		    !wpa_dbus_dict_append_int32(&dict_iter, "persistent_group",
 						res->persistent_group) ||
 		    !wpa_dbus_dict_append_uint32(&dict_iter,
 						 "peer_config_timeout",
@@ -1420,12 +1424,10 @@
 
 
 /**
- *
- * Method to emit Invitation Result signal based on status and
- * bssid
- * @status: Status of the Invite request. 0 for success, other
- * for errors
- * @bssid : Basic Service Set Identifier
+ * wpas_dbus_signal_p2p_invitation_result - Emit InvitationResult signal
+ * @wpa_s: %wpa_supplicant network interface data
+ * @status: Status of invitation process
+ * @bssid: Basic Service Set Identifier
  */
 void wpas_dbus_signal_p2p_invitation_result(struct wpa_supplicant *wpa_s,
 					    int status, const u8 *bssid)
@@ -1441,6 +1443,11 @@
 	if (iface == NULL)
 		return;
 
+	if (wpa_s->p2p_mgmt)
+		wpa_s = wpa_s->parent;
+	if (!wpa_s->dbus_new_path)
+		return;
+
 	msg = dbus_message_new_signal(wpa_s->dbus_new_path,
 				      WPAS_DBUS_NEW_IFACE_P2PDEVICE,
 				      "InvitationResult");
@@ -1449,23 +1456,16 @@
 		return;
 
 	dbus_message_iter_init_append(msg, &iter);
-	if (!wpa_dbus_dict_open_write(&iter, &dict_iter))
-		goto nomem;
-
-	if (!wpa_dbus_dict_append_int32(&dict_iter, "status", status))
-		goto nomem;
-	if (bssid) {
-		if (!wpa_dbus_dict_append_byte_array(&dict_iter, "BSSID",
-						     (const char *) bssid,
-						     ETH_ALEN))
-			goto nomem;
-	}
-	if (!wpa_dbus_dict_close_write(&iter, &dict_iter))
-		goto nomem;
-
-	dbus_connection_send(iface->con, msg, NULL);
-
-nomem:
+	if (!wpa_dbus_dict_open_write(&iter, &dict_iter) ||
+	    !wpa_dbus_dict_append_int32(&dict_iter, "status", status) ||
+	    (bssid &&
+	     !wpa_dbus_dict_append_byte_array(&dict_iter, "BSSID",
+					      (const char *) bssid,
+					      ETH_ALEN)) ||
+	    !wpa_dbus_dict_close_write(&iter, &dict_iter))
+		wpa_printf(MSG_ERROR, "dbus: Failed to construct signal");
+	else
+		dbus_connection_send(iface->con, msg, NULL);
 	dbus_message_unref(msg);
 }
 
@@ -1486,6 +1486,7 @@
 	DBusMessage *msg;
 	DBusMessageIter iter;
 	char peer_obj_path[WPAS_DBUS_OBJECT_PATH_MAX], *path;
+	struct wpa_supplicant *parent;
 
 	iface = wpa_s->global->dbus;
 
@@ -1496,10 +1497,16 @@
 	if (!wpa_s->dbus_groupobj_path)
 		return;
 
+	parent = wpa_s->parent;
+	if (parent->p2p_mgmt)
+		parent = parent->parent;
+	if (!parent->dbus_new_path)
+		return;
+
 	os_snprintf(peer_obj_path, WPAS_DBUS_OBJECT_PATH_MAX,
 			"%s/" WPAS_DBUS_NEW_P2P_PEERS_PART "/"
 			COMPACT_MACSTR,
-			wpa_s->parent->dbus_new_path, MAC2STR(peer_addr));
+			parent->dbus_new_path, MAC2STR(peer_addr));
 
 	msg = dbus_message_new_signal(wpa_s->dbus_groupobj_path,
 				      WPAS_DBUS_NEW_IFACE_P2P_GROUP,
@@ -1510,18 +1517,12 @@
 	dbus_message_iter_init_append(msg, &iter);
 	path = peer_obj_path;
 	if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH,
-					    &path))
-		goto err;
-
-	dbus_connection_send(iface->con, msg, NULL);
-	dbus_message_unref(msg);
-
-	wpas_dbus_signal_peer_groups_changed(wpa_s->parent, peer_addr);
-
-	return;
-
-err:
-	wpa_printf(MSG_ERROR, "dbus: Failed to construct signal");
+					    &path)) {
+		wpa_printf(MSG_ERROR, "dbus: Failed to construct signal");
+	} else {
+		dbus_connection_send(iface->con, msg, NULL);
+		wpas_dbus_signal_peer_groups_changed(parent, peer_addr);
+	}
 	dbus_message_unref(msg);
 }
 
@@ -1542,6 +1543,7 @@
 	DBusMessage *msg;
 	DBusMessageIter iter;
 	char peer_obj_path[WPAS_DBUS_OBJECT_PATH_MAX], *path;
+	struct wpa_supplicant *parent;
 
 	iface = wpa_s->global->dbus;
 
@@ -1552,10 +1554,16 @@
 	if (!wpa_s->dbus_groupobj_path)
 		return;
 
+	parent = wpa_s->parent;
+	if (parent->p2p_mgmt)
+		parent = parent->parent;
+	if (!parent->dbus_new_path)
+		return;
+
 	os_snprintf(peer_obj_path, WPAS_DBUS_OBJECT_PATH_MAX,
 			"%s/" WPAS_DBUS_NEW_P2P_PEERS_PART "/"
 			COMPACT_MACSTR,
-			wpa_s->dbus_groupobj_path, MAC2STR(peer_addr));
+			parent->dbus_new_path, MAC2STR(peer_addr));
 
 	msg = dbus_message_new_signal(wpa_s->dbus_groupobj_path,
 				      WPAS_DBUS_NEW_IFACE_P2P_GROUP,
@@ -1566,19 +1574,13 @@
 	dbus_message_iter_init_append(msg, &iter);
 	path = peer_obj_path;
 	if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH,
-					    &path))
-		goto err;
-
-	dbus_connection_send(iface->con, msg, NULL);
-	dbus_message_unref(msg);
-
-	wpas_dbus_signal_peer_groups_changed(wpa_s->parent, peer_addr);
-
-	return;
-
-err:
-	wpa_printf(MSG_ERROR, "dbus: Failed to construct PeerDisconnected "
-			      "signal");
+					    &path)) {
+		wpa_printf(MSG_ERROR,
+			   "dbus: Failed to construct PeerDisconnected signal");
+	} else {
+		dbus_connection_send(iface->con, msg, NULL);
+		wpas_dbus_signal_peer_groups_changed(parent, peer_addr);
+	}
 	dbus_message_unref(msg);
 }
 
@@ -1605,22 +1607,28 @@
 	DBusMessageIter iter, dict_iter;
 	struct wpas_dbus_priv *iface;
 	char peer_obj_path[WPAS_DBUS_OBJECT_PATH_MAX], *path;
+
 	iface = wpa_s->global->dbus;
 
 	/* Do nothing if the control interface is not turned on */
 	if (iface == NULL)
 		return;
 
+	if (wpa_s->p2p_mgmt)
+		wpa_s = wpa_s->parent;
+	if (!wpa_s->dbus_new_path)
+		return;
+
+	/* Check if this is a known peer */
+	if (!p2p_peer_known(wpa_s->global->p2p, sa))
+		return;
+
 	msg = dbus_message_new_signal(wpa_s->dbus_new_path,
 				      WPAS_DBUS_NEW_IFACE_P2PDEVICE,
 				      "ServiceDiscoveryRequest");
 	if (msg == NULL)
 		return;
 
-	/* Check if this is a known peer */
-	if (!p2p_peer_known(wpa_s->global->p2p, sa))
-		goto error;
-
 	os_snprintf(peer_obj_path, WPAS_DBUS_OBJECT_PATH_MAX,
 		    "%s/" WPAS_DBUS_NEW_P2P_PEERS_PART "/"
 		    COMPACT_MACSTR, wpa_s->dbus_new_path, MAC2STR(sa));
@@ -1628,11 +1636,8 @@
 	path = peer_obj_path;
 
 	dbus_message_iter_init_append(msg, &iter);
-	if (!wpa_dbus_dict_open_write(&iter, &dict_iter))
-		goto error;
-
-
-	if (!wpa_dbus_dict_append_object_path(&dict_iter, "peer_object",
+	if (!wpa_dbus_dict_open_write(&iter, &dict_iter) ||
+	    !wpa_dbus_dict_append_object_path(&dict_iter, "peer_object",
 					      path) ||
 	    !wpa_dbus_dict_append_int32(&dict_iter, "frequency", freq) ||
 	    !wpa_dbus_dict_append_int32(&dict_iter, "dialog_token",
@@ -1643,13 +1648,9 @@
 					     (const char *) tlvs,
 					     tlvs_len) ||
 	    !wpa_dbus_dict_close_write(&iter, &dict_iter))
-		goto error;
-
-	dbus_connection_send(iface->con, msg, NULL);
-	dbus_message_unref(msg);
-	return;
-error:
-	wpa_printf(MSG_ERROR, "dbus: Failed to construct signal");
+		wpa_printf(MSG_ERROR, "dbus: Failed to construct signal");
+	else
+		dbus_connection_send(iface->con, msg, NULL);
 	dbus_message_unref(msg);
 }
 
@@ -1674,21 +1675,27 @@
 	DBusMessageIter iter, dict_iter;
 	struct wpas_dbus_priv *iface;
 	char peer_obj_path[WPAS_DBUS_OBJECT_PATH_MAX], *path;
+
 	iface = wpa_s->global->dbus;
 
 	/* Do nothing if the control interface is not turned on */
 	if (iface == NULL)
 		return;
 
-	msg = dbus_message_new_signal(wpa_s->dbus_new_path,
-				      WPAS_DBUS_NEW_IFACE_P2PDEVICE,
-				"ServiceDiscoveryResponse");
-	if (msg == NULL)
+	if (wpa_s->p2p_mgmt)
+		wpa_s = wpa_s->parent;
+	if (!wpa_s->dbus_new_path)
 		return;
 
 	/* Check if this is a known peer */
 	if (!p2p_peer_known(wpa_s->global->p2p, sa))
-		goto error;
+		return;
+
+	msg = dbus_message_new_signal(wpa_s->dbus_new_path,
+				      WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+				      "ServiceDiscoveryResponse");
+	if (msg == NULL)
+		return;
 
 	os_snprintf(peer_obj_path, WPAS_DBUS_OBJECT_PATH_MAX,
 		    "%s/" WPAS_DBUS_NEW_P2P_PEERS_PART "/"
@@ -1697,10 +1704,8 @@
 	path = peer_obj_path;
 
 	dbus_message_iter_init_append(msg, &iter);
-	if (!wpa_dbus_dict_open_write(&iter, &dict_iter))
-		goto error;
-
-	if (!wpa_dbus_dict_append_object_path(&dict_iter, "peer_object",
+	if (!wpa_dbus_dict_open_write(&iter, &dict_iter) ||
+	    !wpa_dbus_dict_append_object_path(&dict_iter, "peer_object",
 					      path) ||
 	    !wpa_dbus_dict_append_uint16(&dict_iter, "update_indicator",
 					 update_indic) ||
@@ -1708,17 +1713,13 @@
 					     (const char *) tlvs,
 					     tlvs_len) ||
 	    !wpa_dbus_dict_close_write(&iter, &dict_iter))
-		goto error;
-
-
-	dbus_connection_send(iface->con, msg, NULL);
-	dbus_message_unref(msg);
-	return;
-error:
-	wpa_printf(MSG_ERROR, "dbus: Failed to construct signal");
+		wpa_printf(MSG_ERROR, "dbus: Failed to construct signal");
+	else
+		dbus_connection_send(iface->con, msg, NULL);
 	dbus_message_unref(msg);
 }
 
+
 /**
  * wpas_dbus_signal_persistent_group - Send a persistent group related
  *	event signal
@@ -1744,6 +1745,11 @@
 	if (iface == NULL)
 		return;
 
+	if (wpa_s->p2p_mgmt)
+		wpa_s = wpa_s->parent;
+	if (!wpa_s->dbus_new_path)
+		return;
+
 	os_snprintf(pgrp_obj_path, WPAS_DBUS_OBJECT_PATH_MAX,
 		    "%s/" WPAS_DBUS_NEW_PERSISTENT_GROUPS_PART "/%u",
 		    wpa_s->dbus_new_path, id);
@@ -1757,23 +1763,15 @@
 	dbus_message_iter_init_append(msg, &iter);
 	path = pgrp_obj_path;
 	if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH,
-					    &path))
-		goto err;
+					    &path) ||
+	    (properties &&
+	     !wpa_dbus_get_object_properties(
+		     iface, pgrp_obj_path,
+		     WPAS_DBUS_NEW_IFACE_PERSISTENT_GROUP, &iter)))
+		wpa_printf(MSG_ERROR, "dbus: Failed to construct signal");
+	else
+		dbus_connection_send(iface->con, msg, NULL);
 
-	if (properties) {
-		if (!wpa_dbus_get_object_properties(
-			    iface, pgrp_obj_path,
-			    WPAS_DBUS_NEW_IFACE_PERSISTENT_GROUP, &iter))
-			goto err;
-	}
-
-	dbus_connection_send(iface->con, msg, NULL);
-
-	dbus_message_unref(msg);
-	return;
-
-err:
-	wpa_printf(MSG_ERROR, "dbus: Failed to construct signal");
 	dbus_message_unref(msg);
 }
 
@@ -1813,6 +1811,7 @@
 /**
  * wpas_dbus_signal_p2p_wps_failed - Signals WpsFailed event
  * @wpa_s: %wpa_supplicant network interface data
+ * @fail: WPS failure information
  *
  * Sends Event dbus signal with name "fail" and dictionary containing
  * "msg" field with fail message number (int32) as arguments
@@ -1832,6 +1831,11 @@
 	if (iface == NULL)
 		return;
 
+	if (wpa_s->p2p_mgmt)
+		wpa_s = wpa_s->parent;
+
+	if (!wpa_s->dbus_new_path)
+		return;
 	msg = dbus_message_new_signal(wpa_s->dbus_new_path,
 				      WPAS_DBUS_NEW_IFACE_P2PDEVICE,
 				      "WpsFailed");
@@ -1853,7 +1857,7 @@
 	dbus_message_unref(msg);
 }
 
-#endif /*CONFIG_P2P*/
+#endif /* CONFIG_P2P */
 
 
 /**
@@ -1932,6 +1936,9 @@
 	char path[WPAS_DBUS_OBJECT_PATH_MAX];
 	char *prop;
 
+	if (!wpa_s->dbus_new_path)
+		return;
+
 	switch (property) {
 	case WPAS_DBUS_BSS_PROP_SIGNAL:
 		prop = "Signal";
@@ -2047,7 +2054,7 @@
 
 static const struct wpa_dbus_method_desc wpas_dbus_global_methods[] = {
 	{ "CreateInterface", WPAS_DBUS_NEW_INTERFACE,
-	  (WPADBusMethodHandler) &wpas_dbus_handler_create_interface,
+	  (WPADBusMethodHandler) wpas_dbus_handler_create_interface,
 	  {
 		  { "args", "a{sv}", ARG_IN },
 		  { "path", "o", ARG_OUT },
@@ -2055,14 +2062,14 @@
 	  }
 	},
 	{ "RemoveInterface", WPAS_DBUS_NEW_INTERFACE,
-	  (WPADBusMethodHandler) &wpas_dbus_handler_remove_interface,
+	  (WPADBusMethodHandler) wpas_dbus_handler_remove_interface,
 	  {
 		  { "path", "o", ARG_IN },
 		  END_ARGS
 	  }
 	},
 	{ "GetInterface", WPAS_DBUS_NEW_INTERFACE,
-	  (WPADBusMethodHandler) &wpas_dbus_handler_get_interface,
+	  (WPADBusMethodHandler) wpas_dbus_handler_get_interface,
 	  {
 		  { "ifname", "s", ARG_IN },
 		  { "path", "o", ARG_OUT },
@@ -2120,14 +2127,6 @@
 		  END_ARGS
 	  }
 	},
-	{ "NetworkRequest", WPAS_DBUS_NEW_IFACE_INTERFACE,
-	  {
-		  { "path", "o", ARG_OUT },
-		  { "field", "s", ARG_OUT },
-		  { "text", "s", ARG_OUT },
-		  END_ARGS
-	  }
-	},
 	/* Deprecated: use org.freedesktop.DBus.Properties.PropertiesChanged */
 	{ "PropertiesChanged", WPAS_DBUS_NEW_INTERFACE,
 	  {
@@ -2154,8 +2153,8 @@
 
 	obj_desc = os_zalloc(sizeof(struct wpa_dbus_object_desc));
 	if (!obj_desc) {
-		wpa_printf(MSG_ERROR, "Not enough memory "
-			   "to create object description");
+		wpa_printf(MSG_ERROR,
+			   "Not enough memory to create object description");
 		return -1;
 	}
 
@@ -2255,7 +2254,7 @@
 #endif /* CONFIG_P2P */
 
 	/* Do nothing if the control interface is not turned on */
-	if (wpa_s == NULL || wpa_s->global == NULL)
+	if (wpa_s == NULL || wpa_s->global == NULL || !wpa_s->dbus_new_path)
 		return 0;
 	ctrl_iface = wpa_s->global->dbus;
 	if (ctrl_iface == NULL)
@@ -2269,16 +2268,16 @@
 		   net_obj_path);
 	obj_desc = os_zalloc(sizeof(struct wpa_dbus_object_desc));
 	if (!obj_desc) {
-		wpa_printf(MSG_ERROR, "Not enough memory "
-			   "to create object description");
+		wpa_printf(MSG_ERROR,
+			   "Not enough memory to create object description");
 		goto err;
 	}
 
 	/* allocate memory for handlers arguments */
 	arg = os_zalloc(sizeof(struct network_handler_args));
 	if (!arg) {
-		wpa_printf(MSG_ERROR, "Not enough memory "
-			   "to create arguments for method");
+		wpa_printf(MSG_ERROR,
+			   "Not enough memory to create arguments for method");
 		goto err;
 	}
 
@@ -2429,7 +2428,7 @@
 	char bss_obj_path[WPAS_DBUS_OBJECT_PATH_MAX];
 
 	/* Do nothing if the control interface is not turned on */
-	if (wpa_s == NULL || wpa_s->global == NULL)
+	if (wpa_s == NULL || wpa_s->global == NULL || !wpa_s->dbus_new_path)
 		return 0;
 	ctrl_iface = wpa_s->global->dbus;
 	if (ctrl_iface == NULL)
@@ -2472,7 +2471,7 @@
 	struct bss_handler_args *arg;
 
 	/* Do nothing if the control interface is not turned on */
-	if (wpa_s == NULL || wpa_s->global == NULL)
+	if (wpa_s == NULL || wpa_s->global == NULL || !wpa_s->dbus_new_path)
 		return 0;
 	ctrl_iface = wpa_s->global->dbus;
 	if (ctrl_iface == NULL)
@@ -2484,15 +2483,15 @@
 
 	obj_desc = os_zalloc(sizeof(struct wpa_dbus_object_desc));
 	if (!obj_desc) {
-		wpa_printf(MSG_ERROR, "Not enough memory "
-			   "to create object description");
+		wpa_printf(MSG_ERROR,
+			   "Not enough memory to create object description");
 		goto err;
 	}
 
 	arg = os_zalloc(sizeof(struct bss_handler_args));
 	if (!arg) {
-		wpa_printf(MSG_ERROR, "Not enough memory "
-			   "to create arguments for handler");
+		wpa_printf(MSG_ERROR,
+			   "Not enough memory to create arguments for handler");
 		goto err;
 	}
 	arg->wpa_s = wpa_s;
@@ -2525,27 +2524,27 @@
 
 static const struct wpa_dbus_method_desc wpas_dbus_interface_methods[] = {
 	{ "Scan", WPAS_DBUS_NEW_IFACE_INTERFACE,
-	  (WPADBusMethodHandler) &wpas_dbus_handler_scan,
+	  (WPADBusMethodHandler) wpas_dbus_handler_scan,
 	  {
 		  { "args", "a{sv}", ARG_IN },
 		  END_ARGS
 	  }
 	},
 	{ "SignalPoll", WPAS_DBUS_NEW_IFACE_INTERFACE,
-	  (WPADBusMethodHandler) &wpas_dbus_handler_signal_poll,
+	  (WPADBusMethodHandler) wpas_dbus_handler_signal_poll,
 	  {
 		  { "args", "a{sv}", ARG_OUT },
 		  END_ARGS
 	  }
 	},
 	{ "Disconnect", WPAS_DBUS_NEW_IFACE_INTERFACE,
-	  (WPADBusMethodHandler) &wpas_dbus_handler_disconnect,
+	  (WPADBusMethodHandler) wpas_dbus_handler_disconnect,
 	  {
 		  END_ARGS
 	  }
 	},
 	{ "AddNetwork", WPAS_DBUS_NEW_IFACE_INTERFACE,
-	  (WPADBusMethodHandler) &wpas_dbus_handler_add_network,
+	  (WPADBusMethodHandler) wpas_dbus_handler_add_network,
 	  {
 		  { "args", "a{sv}", ARG_IN },
 		  { "path", "o", ARG_OUT },
@@ -2553,39 +2552,45 @@
 	  }
 	},
 	{ "Reassociate", WPAS_DBUS_NEW_IFACE_INTERFACE,
-	  (WPADBusMethodHandler) &wpas_dbus_handler_reassociate,
+	  (WPADBusMethodHandler) wpas_dbus_handler_reassociate,
 	  {
 		  END_ARGS
 	  }
 	},
 	{ "Reattach", WPAS_DBUS_NEW_IFACE_INTERFACE,
-	  (WPADBusMethodHandler) &wpas_dbus_handler_reattach,
+	  (WPADBusMethodHandler) wpas_dbus_handler_reattach,
+	  {
+		  END_ARGS
+	  }
+	},
+	{ "Reconnect", WPAS_DBUS_NEW_IFACE_INTERFACE,
+	  (WPADBusMethodHandler) wpas_dbus_handler_reconnect,
 	  {
 		  END_ARGS
 	  }
 	},
 	{ "RemoveNetwork", WPAS_DBUS_NEW_IFACE_INTERFACE,
-	  (WPADBusMethodHandler) &wpas_dbus_handler_remove_network,
+	  (WPADBusMethodHandler) wpas_dbus_handler_remove_network,
 	  {
 		  { "path", "o", ARG_IN },
 		  END_ARGS
 	  }
 	},
 	{ "RemoveAllNetworks", WPAS_DBUS_NEW_IFACE_INTERFACE,
-	  (WPADBusMethodHandler) &wpas_dbus_handler_remove_all_networks,
+	  (WPADBusMethodHandler) wpas_dbus_handler_remove_all_networks,
 	  {
 		  END_ARGS
 	  }
 	},
 	{ "SelectNetwork", WPAS_DBUS_NEW_IFACE_INTERFACE,
-	  (WPADBusMethodHandler) &wpas_dbus_handler_select_network,
+	  (WPADBusMethodHandler) wpas_dbus_handler_select_network,
 	  {
 		  { "path", "o", ARG_IN },
 		  END_ARGS
 	  }
 	},
 	{ "NetworkReply", WPAS_DBUS_NEW_IFACE_INTERFACE,
-	  (WPADBusMethodHandler) &wpas_dbus_handler_network_reply,
+	  (WPADBusMethodHandler) wpas_dbus_handler_network_reply,
 	  {
 		  { "path", "o", ARG_IN },
 		  { "field", "s", ARG_IN },
@@ -2595,7 +2600,7 @@
 	},
 #ifndef CONFIG_NO_CONFIG_BLOBS
 	{ "AddBlob", WPAS_DBUS_NEW_IFACE_INTERFACE,
-	  (WPADBusMethodHandler) &wpas_dbus_handler_add_blob,
+	  (WPADBusMethodHandler) wpas_dbus_handler_add_blob,
 	  {
 		  { "name", "s", ARG_IN },
 		  { "data", "ay", ARG_IN },
@@ -2603,7 +2608,7 @@
 	  }
 	},
 	{ "GetBlob", WPAS_DBUS_NEW_IFACE_INTERFACE,
-	  (WPADBusMethodHandler) &wpas_dbus_handler_get_blob,
+	  (WPADBusMethodHandler) wpas_dbus_handler_get_blob,
 	  {
 		  { "name", "s", ARG_IN },
 		  { "data", "ay", ARG_OUT },
@@ -2611,7 +2616,7 @@
 	  }
 	},
 	{ "RemoveBlob", WPAS_DBUS_NEW_IFACE_INTERFACE,
-	  (WPADBusMethodHandler) &wpas_dbus_handler_remove_blob,
+	  (WPADBusMethodHandler) wpas_dbus_handler_remove_blob,
 	  {
 		  { "name", "s", ARG_IN },
 		  END_ARGS
@@ -2620,7 +2625,7 @@
 #endif /* CONFIG_NO_CONFIG_BLOBS */
 	{ "SetPKCS11EngineAndModulePath", WPAS_DBUS_NEW_IFACE_INTERFACE,
 	  (WPADBusMethodHandler)
-	  &wpas_dbus_handler_set_pkcs11_engine_and_module_path,
+	  wpas_dbus_handler_set_pkcs11_engine_and_module_path,
 	  {
 		  { "pkcs11_engine_path", "s", ARG_IN },
 		  { "pkcs11_module_path", "s", ARG_IN },
@@ -2629,51 +2634,57 @@
 	},
 #ifdef CONFIG_WPS
 	{ "Start", WPAS_DBUS_NEW_IFACE_WPS,
-	  (WPADBusMethodHandler) &wpas_dbus_handler_wps_start,
+	  (WPADBusMethodHandler) wpas_dbus_handler_wps_start,
 	  {
 		  { "args", "a{sv}", ARG_IN },
 		  { "output", "a{sv}", ARG_OUT },
 		  END_ARGS
 	  }
 	},
+	{ "Cancel", WPAS_DBUS_NEW_IFACE_WPS,
+	  (WPADBusMethodHandler) wpas_dbus_handler_wps_cancel,
+	  {
+		  END_ARGS
+	  }
+	},
 #endif /* CONFIG_WPS */
 #ifdef CONFIG_P2P
 	{ "Find", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
-	  (WPADBusMethodHandler)wpas_dbus_handler_p2p_find,
+	  (WPADBusMethodHandler) wpas_dbus_handler_p2p_find,
 	  {
 		  { "args", "a{sv}", ARG_IN },
 		  END_ARGS
 	  }
 	},
 	{ "StopFind", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
-	  (WPADBusMethodHandler)wpas_dbus_handler_p2p_stop_find,
+	  (WPADBusMethodHandler) wpas_dbus_handler_p2p_stop_find,
 	  {
 		  END_ARGS
 	  }
 	},
 	{ "Listen", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
-	  (WPADBusMethodHandler)wpas_dbus_handler_p2p_listen,
+	  (WPADBusMethodHandler) wpas_dbus_handler_p2p_listen,
 	  {
 		  { "timeout", "i", ARG_IN },
 		  END_ARGS
 	  }
 	},
 	{ "ExtendedListen", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
-	  (WPADBusMethodHandler)wpas_dbus_handler_p2p_extendedlisten,
+	  (WPADBusMethodHandler) wpas_dbus_handler_p2p_extendedlisten,
 	  {
 		  { "args", "a{sv}", ARG_IN },
 		  END_ARGS
 	  }
 	},
 	{ "PresenceRequest", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
-	  (WPADBusMethodHandler)wpas_dbus_handler_p2p_presence_request,
+	  (WPADBusMethodHandler) wpas_dbus_handler_p2p_presence_request,
 	  {
 		  { "args", "a{sv}", ARG_IN },
 		  END_ARGS
 	  }
 	},
 	{ "ProvisionDiscoveryRequest", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
-	  (WPADBusMethodHandler)wpas_dbus_handler_p2p_prov_disc_req,
+	  (WPADBusMethodHandler) wpas_dbus_handler_p2p_prov_disc_req,
 	  {
 		  { "peer", "o", ARG_IN },
 		  { "config_method", "s", ARG_IN },
@@ -2681,7 +2692,7 @@
 	  }
 	},
 	{ "Connect", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
-	  (WPADBusMethodHandler)wpas_dbus_handler_p2p_connect,
+	  (WPADBusMethodHandler) wpas_dbus_handler_p2p_connect,
 	  {
 		  { "args", "a{sv}", ARG_IN },
 		  { "generated_pin", "s", ARG_OUT },
@@ -2689,60 +2700,73 @@
 	  }
 	},
 	{ "GroupAdd", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
-	  (WPADBusMethodHandler)wpas_dbus_handler_p2p_group_add,
+	  (WPADBusMethodHandler) wpas_dbus_handler_p2p_group_add,
 	  {
 		  { "args", "a{sv}", ARG_IN },
 		  END_ARGS
 	  }
 	},
+	{ "Cancel", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+	  (WPADBusMethodHandler) wpas_dbus_handler_p2p_cancel,
+	  {
+		  END_ARGS
+	  }
+	},
 	{ "Invite", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
-	  (WPADBusMethodHandler)wpas_dbus_handler_p2p_invite,
+	  (WPADBusMethodHandler) wpas_dbus_handler_p2p_invite,
 	  {
 		  { "args", "a{sv}", ARG_IN },
 		  END_ARGS
 	  }
 	},
 	{ "Disconnect", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
-	  (WPADBusMethodHandler)wpas_dbus_handler_p2p_disconnect,
+	  (WPADBusMethodHandler) wpas_dbus_handler_p2p_disconnect,
 	  {
 		  END_ARGS
 	  }
 	},
 	{ "RejectPeer", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
-	  (WPADBusMethodHandler)wpas_dbus_handler_p2p_rejectpeer,
+	  (WPADBusMethodHandler) wpas_dbus_handler_p2p_rejectpeer,
 	  {
 		  { "peer", "o", ARG_IN },
 		  END_ARGS
 	  }
 	},
+	{ "RemoveClient", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+	  (WPADBusMethodHandler) wpas_dbus_handler_p2p_remove_client,
+	  {
+		  { "args", "a{sv}", ARG_IN },
+		  END_ARGS
+	  }
+	},
 	{ "Flush", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
-	  (WPADBusMethodHandler)wpas_dbus_handler_p2p_flush,
+	  (WPADBusMethodHandler) wpas_dbus_handler_p2p_flush,
 	  {
 		  END_ARGS
 	  }
 	},
 	{ "AddService", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
-	  (WPADBusMethodHandler)wpas_dbus_handler_p2p_add_service,
+	  (WPADBusMethodHandler) wpas_dbus_handler_p2p_add_service,
 	  {
 		  { "args", "a{sv}", ARG_IN },
 		  END_ARGS
 	  }
 	},
 	{ "DeleteService", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
-	  (WPADBusMethodHandler)wpas_dbus_handler_p2p_delete_service,
+	  (WPADBusMethodHandler) wpas_dbus_handler_p2p_delete_service,
 	  {
 		  { "args", "a{sv}", ARG_IN },
 		  END_ARGS
 	  }
 	},
 	{ "FlushService", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
-	  (WPADBusMethodHandler)wpas_dbus_handler_p2p_flush_service,
+	  (WPADBusMethodHandler) wpas_dbus_handler_p2p_flush_service,
 	  {
 		  END_ARGS
 	  }
 	},
 	{ "ServiceDiscoveryRequest", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
-	  (WPADBusMethodHandler)wpas_dbus_handler_p2p_service_sd_req,
+	  (WPADBusMethodHandler) wpas_dbus_handler_p2p_service_sd_req,
 	  {
 		  { "args", "a{sv}", ARG_IN },
 		  { "ref", "t", ARG_OUT },
@@ -2750,27 +2774,27 @@
 	  }
 	},
 	{ "ServiceDiscoveryResponse", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
-	  (WPADBusMethodHandler)wpas_dbus_handler_p2p_service_sd_res,
+	  (WPADBusMethodHandler) wpas_dbus_handler_p2p_service_sd_res,
 	  {
 		  { "args", "a{sv}", ARG_IN },
 		  END_ARGS
 	  }
 	},
 	{ "ServiceDiscoveryCancelRequest", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
-	  (WPADBusMethodHandler)wpas_dbus_handler_p2p_service_sd_cancel_req,
+	  (WPADBusMethodHandler) wpas_dbus_handler_p2p_service_sd_cancel_req,
 	  {
 		  { "args", "t", ARG_IN },
 		  END_ARGS
 	  }
 	},
 	{ "ServiceUpdate", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
-	  (WPADBusMethodHandler)wpas_dbus_handler_p2p_service_update,
+	  (WPADBusMethodHandler) wpas_dbus_handler_p2p_service_update,
 	  {
 		  END_ARGS
 	  }
 	},
 	{ "ServiceDiscoveryExternal", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
-	  (WPADBusMethodHandler)wpas_dbus_handler_p2p_serv_disc_external,
+	  (WPADBusMethodHandler) wpas_dbus_handler_p2p_serv_disc_external,
 	  {
 		  { "arg", "i", ARG_IN },
 		  END_ARGS
@@ -2800,7 +2824,7 @@
 	},
 #endif /* CONFIG_P2P */
 	{ "FlushBSS", WPAS_DBUS_NEW_IFACE_INTERFACE,
-	  (WPADBusMethodHandler) &wpas_dbus_handler_flush_bss,
+	  (WPADBusMethodHandler) wpas_dbus_handler_flush_bss,
 	  {
 		  { "age", "u", ARG_IN },
 		  END_ARGS
@@ -2821,20 +2845,20 @@
 	},
 #endif /* CONFIG_AP */
 	{ "EAPLogoff", WPAS_DBUS_NEW_IFACE_INTERFACE,
-	  (WPADBusMethodHandler) &wpas_dbus_handler_eap_logoff,
+	  (WPADBusMethodHandler) wpas_dbus_handler_eap_logoff,
 	  {
 		  END_ARGS
 	  }
 	},
 	{ "EAPLogon", WPAS_DBUS_NEW_IFACE_INTERFACE,
-	  (WPADBusMethodHandler) &wpas_dbus_handler_eap_logon,
+	  (WPADBusMethodHandler) wpas_dbus_handler_eap_logon,
 	  {
 		  END_ARGS
 	  }
 	},
 #ifdef CONFIG_AUTOSCAN
 	{ "AutoScan", WPAS_DBUS_NEW_IFACE_INTERFACE,
-	  (WPADBusMethodHandler) &wpas_dbus_handler_autoscan,
+	  (WPADBusMethodHandler) wpas_dbus_handler_autoscan,
 	  {
 		  { "arg", "s", ARG_IN },
 		  END_ARGS
@@ -3080,12 +3104,6 @@
 	},
 #endif /* CONFIG_WPS */
 #ifdef CONFIG_P2P
-	{ "P2PStateChanged", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
-	  {
-		  { "states", "a{ss}", ARG_OUT },
-		  END_ARGS
-	  }
-	},
 	{ "DeviceFound", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
 	  {
 		  { "path", "o", ARG_OUT },
@@ -3098,6 +3116,11 @@
 		  END_ARGS
 	  }
 	},
+	{ "FindStopped", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+	  {
+		  END_ARGS
+	  }
+	},
 	{ "ProvisionDiscoveryRequestDisplayPin", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
 	  {
 		  { "peer_object", "o", ARG_OUT },
@@ -3165,6 +3188,7 @@
 	  {
 		  { "path", "o", ARG_OUT },
 		  { "dev_passwd_id", "i", ARG_OUT },
+		  { "device_go_intent", "y", ARG_OUT },
 		  END_ARGS
 	  }
 	},
@@ -3246,10 +3270,23 @@
 		  END_ARGS
 	  }
 	},
+	{ "NetworkRequest", WPAS_DBUS_NEW_IFACE_INTERFACE,
+	  {
+		  { "path", "o", ARG_OUT },
+		  { "field", "s", ARG_OUT },
+		  { "text", "s", ARG_OUT },
+		  END_ARGS
+	  }
+	},
 	{ NULL, NULL, { END_ARGS } }
 };
 
 
+/**
+ * wpas_dbus_register_interface - Register an interface with D-Bus
+ * @wpa_s: wpa_supplicant interface structure
+ * Returns: 0 on success, -1 on failure
+ */
 int wpas_dbus_register_interface(struct wpa_supplicant *wpa_s)
 {
 
@@ -3272,8 +3309,8 @@
 
 	obj_desc = os_zalloc(sizeof(struct wpa_dbus_object_desc));
 	if (!obj_desc) {
-		wpa_printf(MSG_ERROR, "Not enough memory "
-			   "to create object description");
+		wpa_printf(MSG_ERROR,
+			   "Not enough memory to create object description");
 		goto err;
 	}
 
@@ -3300,6 +3337,11 @@
 }
 
 
+/**
+ * wpas_dbus_unregister_interface - Unregister the interface from D-Bus
+ * @wpa_s: wpa_supplicant interface structure
+ * Returns: 0 on success, -1 on failure
+ */
 int wpas_dbus_unregister_interface(struct wpa_supplicant *wpa_s)
 {
 	struct wpas_dbus_priv *ctrl_iface;
@@ -3308,7 +3350,7 @@
 	if (wpa_s == NULL || wpa_s->global == NULL)
 		return 0;
 	ctrl_iface = wpa_s->global->dbus;
-	if (ctrl_iface == NULL)
+	if (ctrl_iface == NULL || wpa_s->dbus_new_path == NULL)
 		return 0;
 
 	wpa_printf(MSG_DEBUG, "dbus: Unregister interface object '%s'",
@@ -3341,6 +3383,22 @@
 	  wpas_dbus_getter_p2p_peer_device_name,
 	  NULL
 	},
+	{ "Manufacturer", WPAS_DBUS_NEW_IFACE_P2P_PEER, "s",
+	  wpas_dbus_getter_p2p_peer_manufacturer,
+	  NULL
+	},
+	{ "ModelName", WPAS_DBUS_NEW_IFACE_P2P_PEER, "s",
+	  wpas_dbus_getter_p2p_peer_modelname,
+	  NULL
+	},
+	{ "ModelNumber", WPAS_DBUS_NEW_IFACE_P2P_PEER, "s",
+	  wpas_dbus_getter_p2p_peer_modelnumber,
+	  NULL
+	},
+	{ "SerialNumber", WPAS_DBUS_NEW_IFACE_P2P_PEER, "s",
+	  wpas_dbus_getter_p2p_peer_serialnumber,
+	  NULL
+	},
 	{ "PrimaryDeviceType", WPAS_DBUS_NEW_IFACE_P2P_PEER, "ay",
 	  wpas_dbus_getter_p2p_peer_primary_device_type,
 	  NULL
@@ -3415,10 +3473,13 @@
 	DBusMessageIter iter;
 	char peer_obj_path[WPAS_DBUS_OBJECT_PATH_MAX], *path;
 
+	if (wpa_s->p2p_mgmt)
+		wpa_s = wpa_s->parent;
+
 	iface = wpa_s->global->dbus;
 
 	/* Do nothing if the control interface is not turned on */
-	if (iface == NULL)
+	if (iface == NULL || !wpa_s->dbus_new_path)
 		return;
 
 	os_snprintf(peer_obj_path, WPAS_DBUS_OBJECT_PATH_MAX,
@@ -3434,15 +3495,10 @@
 	path = peer_obj_path;
 	if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH,
 					    &path))
-		goto err;
+		wpa_printf(MSG_ERROR, "dbus: Failed to construct signal");
+	else
+		dbus_connection_send(iface->con, msg, NULL);
 
-	dbus_connection_send(iface->con, msg, NULL);
-
-	dbus_message_unref(msg);
-	return;
-
-err:
-	wpa_printf(MSG_ERROR, "dbus: Failed to construct signal");
 	dbus_message_unref(msg);
 }
 
@@ -3450,7 +3506,7 @@
 /**
  * wpas_dbus_signal_peer_found - Send a peer found signal
  * @wpa_s: %wpa_supplicant network interface data
- * @dev: peer device object
+ * @dev_addr: Peer P2P Device Address
  *
  * Notify listeners about find a p2p peer device found
  */
@@ -3465,7 +3521,7 @@
 /**
  * wpas_dbus_signal_peer_lost - Send a peer lost signal
  * @wpa_s: %wpa_supplicant network interface data
- * @dev: peer device object
+ * @dev_addr: Peer P2P Device Address
  *
  * Notify listeners about lost a p2p peer device
  */
@@ -3480,7 +3536,7 @@
 /**
  * wpas_dbus_register_peer - Register a discovered peer object with dbus
  * @wpa_s: wpa_supplicant interface structure
- * @ssid: network configuration data
+ * @dev_addr: P2P Device Address of the peer
  * Returns: 0 on success, -1 on failure
  *
  * Registers network representing object with dbus
@@ -3500,6 +3556,10 @@
 	if (ctrl_iface == NULL)
 		return 0;
 
+	wpa_s = wpa_s->parent->parent;
+	if (!wpa_s->dbus_new_path)
+		return 0;
+
 	os_snprintf(peer_obj_path, WPAS_DBUS_OBJECT_PATH_MAX,
 		    "%s/" WPAS_DBUS_NEW_P2P_PEERS_PART "/" COMPACT_MACSTR,
 		    wpa_s->dbus_new_path, MAC2STR(dev_addr));
@@ -3508,16 +3568,16 @@
 		   peer_obj_path);
 	obj_desc = os_zalloc(sizeof(struct wpa_dbus_object_desc));
 	if (!obj_desc) {
-		wpa_printf(MSG_ERROR, "Not enough memory "
-			   "to create object description");
+		wpa_printf(MSG_ERROR,
+			   "Not enough memory to create object description");
 		goto err;
 	}
 
 	/* allocate memory for handlers arguments */
 	arg = os_zalloc(sizeof(struct peer_handler_args));
 	if (!arg) {
-		wpa_printf(MSG_ERROR, "Not enough memory "
-			   "to create arguments for method");
+		wpa_printf(MSG_ERROR,
+			   "Not enough memory to create arguments for method");
 		goto err;
 	}
 
@@ -3556,9 +3616,13 @@
 	int ret;
 
 	/* Do nothing if the control interface is not turned on */
-	if (wpa_s == NULL || wpa_s->global == NULL ||
-	    wpa_s->dbus_new_path == NULL)
+	if (wpa_s == NULL || wpa_s->global == NULL)
 		return 0;
+
+	wpa_s = wpa_s->parent->parent;
+	if (!wpa_s->dbus_new_path)
+		return 0;
+
 	ctrl_iface = wpa_s->global->dbus;
 	if (ctrl_iface == NULL)
 		return 0;
@@ -3575,11 +3639,52 @@
 }
 
 
+/**
+ * wpas_dbus_signal_p2p_find_stopped - Send P2P Find stopped signal
+ * @wpa_s: %wpa_supplicant network interface data
+ *
+ * Notify listeners about P2P Find stopped
+ */
+void wpas_dbus_signal_p2p_find_stopped(struct wpa_supplicant *wpa_s)
+{
+	struct wpas_dbus_priv *iface;
+	DBusMessage *msg;
+
+	iface = wpa_s->global->dbus;
+
+	/* Do nothing if the control interface is not turned on */
+	if (iface == NULL || !wpa_s->dbus_new_path)
+		return;
+
+	msg = dbus_message_new_signal(wpa_s->dbus_new_path,
+				      WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+				      "FindStopped");
+	if (msg == NULL)
+		return;
+
+	dbus_connection_send(iface->con, msg, NULL);
+
+	dbus_message_unref(msg);
+}
+
+
+/**
+ * wpas_dbus_signal_peer_groups_changed - Send peer group change property signal
+ * @wpa_s: %wpa_supplicant network interface data
+ * @dev_addr: P2P Device Address
+ *
+ * Notify listeners about peer Groups property changes.
+ */
 void wpas_dbus_signal_peer_groups_changed(struct wpa_supplicant *wpa_s,
 					  const u8 *dev_addr)
 {
 	char peer_obj_path[WPAS_DBUS_OBJECT_PATH_MAX];
 
+	if (wpa_s->p2p_mgmt)
+		wpa_s = wpa_s->parent;
+
+	if (!wpa_s->dbus_new_path)
+		return;
 	os_snprintf(peer_obj_path, WPAS_DBUS_OBJECT_PATH_MAX,
 		    "%s/" WPAS_DBUS_NEW_P2P_PEERS_PART "/" COMPACT_MACSTR,
 		    wpa_s->dbus_new_path, MAC2STR(dev_addr));
@@ -3685,8 +3790,8 @@
 		   group_obj_path);
 	obj_desc = os_zalloc(sizeof(struct wpa_dbus_object_desc));
 	if (!obj_desc) {
-		wpa_printf(MSG_ERROR, "Not enough memory "
-			   "to create object description");
+		wpa_printf(MSG_ERROR,
+			   "Not enough memory to create object description");
 		goto err;
 	}
 
@@ -3723,6 +3828,9 @@
 	if (wpa_s == NULL || wpa_s->global == NULL)
 		return;
 
+	if (wpa_s->p2p_mgmt)
+		wpa_s = wpa_s->parent;
+
 	ctrl_iface = wpa_s->global->dbus;
 	if (ctrl_iface == NULL)
 		return;
@@ -3778,11 +3886,17 @@
 	/* Do nothing if the control interface is not turned on */
 	if (wpa_s == NULL || wpa_s->global == NULL)
 		return 0;
+	wpa_s = wpa_s->parent->parent;
+	if (!wpa_s->dbus_new_path)
+		return 0;
 
 	/* Make sure ssid is a persistent group */
 	if (ssid->disabled != 2 && !ssid->p2p_persistent_group)
 		return -1; /* should we return w/o complaining? */
 
+	if (wpa_s->p2p_mgmt)
+		wpa_s = wpa_s->parent;
+
 	ctrl_iface = wpa_s->global->dbus;
 	if (ctrl_iface == NULL)
 		return 0;
@@ -3799,8 +3913,8 @@
 		   pgrp_obj_path);
 	obj_desc = os_zalloc(sizeof(struct wpa_dbus_object_desc));
 	if (!obj_desc) {
-		wpa_printf(MSG_ERROR, "dbus: Not enough memory to create "
-			   "object description");
+		wpa_printf(MSG_ERROR,
+			   "dbus: Not enough memory to create object description");
 		goto err;
 	}
 
@@ -3811,8 +3925,8 @@
 	/* allocate memory for handlers arguments */
 	arg = os_zalloc(sizeof(struct network_handler_args));
 	if (!arg) {
-		wpa_printf(MSG_ERROR, "dbus: Not enough memory to create "
-			   "arguments for method");
+		wpa_printf(MSG_ERROR,
+			   "dbus: Not enough memory to create arguments for method");
 		goto err;
 	}
 
@@ -3859,11 +3973,13 @@
 	int ret;
 
 	/* Do nothing if the control interface is not turned on */
-	if (wpa_s == NULL || wpa_s->global == NULL ||
-	    wpa_s->dbus_new_path == NULL)
+	if (wpa_s == NULL || wpa_s->global == NULL)
 		return 0;
+
+	wpa_s = wpa_s->parent->parent;
+
 	ctrl_iface = wpa_s->global->dbus;
-	if (ctrl_iface == NULL)
+	if (ctrl_iface == NULL || !wpa_s->dbus_new_path)
 		return 0;
 
 	os_snprintf(pgrp_obj_path, WPAS_DBUS_OBJECT_PATH_MAX,
diff --git a/wpa_supplicant/dbus/dbus_new.h b/wpa_supplicant/dbus/dbus_new.h
index 5f32bbf..7503348 100644
--- a/wpa_supplicant/dbus/dbus_new.h
+++ b/wpa_supplicant/dbus/dbus_new.h
@@ -152,6 +152,7 @@
 void wpas_dbus_signal_wps_event_fail(struct wpa_supplicant *wpa_s,
 				     struct wps_event_fail *fail);
 void wpas_dbus_signal_wps_event_success(struct wpa_supplicant *wpa_s);
+void wpas_dbus_signal_wps_event_pbc_overlap(struct wpa_supplicant *wpa_s);
 int wpas_dbus_register_network(struct wpa_supplicant *wpa_s,
 			       struct wpa_ssid *ssid);
 int wpas_dbus_unregister_network(struct wpa_supplicant *wpa_s, int nid);
@@ -168,6 +169,7 @@
 void wpas_dbus_signal_debug_show_keys_changed(struct wpa_global *global);
 
 int wpas_dbus_register_peer(struct wpa_supplicant *wpa_s, const u8 *dev_addr);
+void wpas_dbus_signal_p2p_find_stopped(struct wpa_supplicant *wpa_s);
 void wpas_dbus_signal_peer_device_found(struct wpa_supplicant *wpa_s,
 					   const u8 *dev_addr);
 int wpas_dbus_unregister_peer(struct wpa_supplicant *wpa_s,
@@ -184,7 +186,8 @@
 					      u16 config_methods,
 					      unsigned int generated_pin);
 void wpas_dbus_signal_p2p_go_neg_req(struct wpa_supplicant *wpa_s,
-				     const u8 *src, u16 dev_passwd_id);
+				     const u8 *src, u16 dev_passwd_id,
+				     u8 go_intent);
 void wpas_dbus_signal_p2p_group_started(struct wpa_supplicant *wpa_s,
 					const struct wpa_ssid *ssid,
 					int client, int network_id);
@@ -215,6 +218,8 @@
 				     struct wps_event_fail *fail);
 void wpas_dbus_signal_certification(struct wpa_supplicant *wpa_s,
 				    int depth, const char *subject,
+				    const char *altsubject[],
+				    int num_altsubject,
 				    const char *cert_hash,
 				    const struct wpabuf *cert);
 void wpas_dbus_signal_preq(struct wpa_supplicant *wpa_s,
@@ -293,6 +298,11 @@
 {
 }
 
+static inline void wpas_dbus_signal_wps_event_pbc_overlap(
+	struct wpa_supplicant *wpa_s)
+{
+}
+
 static inline int wpas_dbus_register_network(struct wpa_supplicant *wpa_s,
 					     struct wpa_ssid *ssid)
 {
@@ -375,10 +385,10 @@
 {
 }
 
-static inline void wpas_dbus_signal_p2p_go_neg_req(
-				struct wpa_supplicant *wpa_s,
-				const u8 *src,
-				u16 dev_passwd_id)
+static inline void wpas_dbus_signal_p2p_go_neg_req(struct wpa_supplicant *wpa_s,
+						   const u8 *src,
+						   u16 dev_passwd_id,
+						   u8 go_intent)
 {
 }
 
@@ -458,6 +468,11 @@
 }
 
 static inline void
+wpas_dbus_signal_p2p_find_stopped(struct wpa_supplicant *wpa_s)
+{
+}
+
+static inline void
 wpas_dbus_signal_peer_device_found(struct wpa_supplicant *wpa_s,
 				   const u8 *dev_addr)
 {
@@ -484,6 +499,8 @@
 static inline void wpas_dbus_signal_certification(struct wpa_supplicant *wpa_s,
 						  int depth,
 						  const char *subject,
+						  const char *altsubject[],
+						  int num_altsubject,
 						  const char *cert_hash,
 						  const struct wpabuf *cert)
 {
diff --git a/wpa_supplicant/dbus/dbus_new_handlers.c b/wpa_supplicant/dbus/dbus_new_handlers.c
index 9f6c4a3..1c04e92 100644
--- a/wpa_supplicant/dbus/dbus_new_handlers.c
+++ b/wpa_supplicant/dbus/dbus_new_handlers.c
@@ -2,7 +2,7 @@
  * WPA Supplicant / dbus-based control interface
  * Copyright (c) 2006, Dan Williams <dcbw@redhat.com> and Red Hat, Inc.
  * Copyright (c) 2009-2010, Witold Sowa <witold.sowa@gmail.com>
- * Copyright (c) 2009, Jouni Malinen <j@w1.fi>
+ * 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.
@@ -29,13 +29,13 @@
 #include "dbus_common_i.h"
 #include "drivers/driver.h"
 
-static const char *debug_strings[] = {
+static const char * const debug_strings[] = {
 	"excessive", "msgdump", "debug", "info", "warning", "error", NULL
 };
 
 
 /**
- * wpas_dbus_error_unknown_error - Return a new InvalidArgs error message
+ * wpas_dbus_error_unknown_error - Return a new UnknownError error message
  * @message: Pointer to incoming dbus message this error refers to
  * @arg: Optional string appended to error message
  * Returns: a dbus error message
@@ -45,20 +45,6 @@
 DBusMessage * wpas_dbus_error_unknown_error(DBusMessage *message,
 					    const char *arg)
 {
-	/*
-	 * This function can be called as a result of a failure
-	 * within internal getter calls, which will call this function
-	 * with a NULL message parameter.  However, dbus_message_new_error
-	 * looks very unkindly (i.e, abort()) on a NULL message, so
-	 * in this case, we should not call it.
-	 */
-	if (message == NULL) {
-		wpa_printf(MSG_INFO, "dbus: wpas_dbus_error_unknown_error "
-			   "called with NULL message (arg=%s)",
-			   arg ? arg : "N/A");
-		return NULL;
-	}
-
 	return dbus_message_new_error(message, WPAS_DBUS_ERROR_UNKNOWN_ERROR,
 				      arg);
 }
@@ -73,9 +59,9 @@
  */
 static DBusMessage * wpas_dbus_error_iface_unknown(DBusMessage *message)
 {
-	return dbus_message_new_error(message, WPAS_DBUS_ERROR_IFACE_UNKNOWN,
-				      "wpa_supplicant knows nothing about "
-				      "this interface.");
+	return dbus_message_new_error(
+		message, WPAS_DBUS_ERROR_IFACE_UNKNOWN,
+		"wpa_supplicant knows nothing about this interface.");
 }
 
 
@@ -88,9 +74,9 @@
  */
 static DBusMessage * wpas_dbus_error_network_unknown(DBusMessage *message)
 {
-	return dbus_message_new_error(message, WPAS_DBUS_ERROR_NETWORK_UNKNOWN,
-				      "There is no such a network in this "
-				      "interface.");
+	return dbus_message_new_error(
+		message, WPAS_DBUS_ERROR_NETWORK_UNKNOWN,
+		"There is no such a network in this interface.");
 }
 
 
@@ -106,9 +92,9 @@
 {
 	DBusMessage *reply;
 
-	reply = dbus_message_new_error(message, WPAS_DBUS_ERROR_INVALID_ARGS,
-				       "Did not receive correct message "
-				       "arguments.");
+	reply = dbus_message_new_error(
+		message, WPAS_DBUS_ERROR_INVALID_ARGS,
+		"Did not receive correct message arguments.");
 	if (arg != NULL)
 		dbus_message_append_args(reply, DBUS_TYPE_STRING, &arg,
 					 DBUS_TYPE_INVALID);
@@ -125,20 +111,23 @@
  *
  * Convenience function to create and return a scan error
  */
-DBusMessage * wpas_dbus_error_scan_error(DBusMessage *message,
-					 const char *error)
+static DBusMessage * wpas_dbus_error_scan_error(DBusMessage *message,
+						const char *error)
 {
-	DBusMessage *reply;
-
-	reply = dbus_message_new_error(message,
-				       WPAS_DBUS_ERROR_IFACE_SCAN_ERROR,
-				       error);
-
-	return reply;
+	return dbus_message_new_error(message,
+				      WPAS_DBUS_ERROR_IFACE_SCAN_ERROR,
+				      error);
 }
 
 
-static const char *dont_quote[] = {
+DBusMessage * wpas_dbus_error_no_memory(DBusMessage *message)
+{
+	wpa_printf(MSG_DEBUG, "dbus: Failed to allocate memory");
+	return dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY, NULL);
+}
+
+
+static const char * const dont_quote[] = {
 	"key_mgmt", "proto", "pairwise", "auth_alg", "group", "eap",
 	"opensc_engine_path", "pkcs11_engine_path", "pkcs11_module_path",
 	"bssid", "scan_freq", "freq_list", NULL
@@ -147,6 +136,7 @@
 static dbus_bool_t should_quote_opt(const char *key)
 {
 	int i = 0;
+
 	while (dont_quote[i] != NULL) {
 		if (os_strcmp(key, dont_quote[i]) == 0)
 			return FALSE;
@@ -167,7 +157,8 @@
 	struct wpa_supplicant *wpa_s;
 
 	for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) {
-		if (os_strcmp(wpa_s->dbus_new_path, path) == 0)
+		if (wpa_s->dbus_new_path &&
+		    os_strcmp(wpa_s->dbus_new_path, path) == 0)
 			return wpa_s;
 	}
 	return NULL;
@@ -223,7 +214,7 @@
 		} else if (entry.type == DBUS_TYPE_STRING) {
 			if (should_quote_opt(entry.key)) {
 				size = os_strlen(entry.str_value);
-				if (size <= 0)
+				if (size == 0)
 					goto error;
 
 				size += 3;
@@ -233,7 +224,7 @@
 
 				ret = os_snprintf(value, size, "\"%s\"",
 						  entry.str_value);
-				if (ret < 0 || (size_t) ret != (size - 1))
+				if (os_snprintf_error(size, ret))
 					goto error;
 			} else {
 				value = os_strdup(entry.str_value);
@@ -247,7 +238,7 @@
 
 			ret = os_snprintf(value, size, "%u",
 					  entry.uint32_value);
-			if (ret <= 0)
+			if (os_snprintf_error(size, ret))
 				goto error;
 		} else if (entry.type == DBUS_TYPE_INT32) {
 			value = os_zalloc(size);
@@ -256,7 +247,7 @@
 
 			ret = os_snprintf(value, size, "%d",
 					  entry.int32_value);
-			if (ret <= 0)
+			if (os_snprintf_error(size, ret))
 				goto error;
 		} else
 			goto error;
@@ -264,6 +255,19 @@
 		if (wpa_config_set(ssid, entry.key, value, 0) < 0)
 			goto error;
 
+		if (os_strcmp(entry.key, "bssid") != 0 &&
+		    os_strcmp(entry.key, "priority") != 0)
+			wpa_sm_pmksa_cache_flush(wpa_s->wpa, ssid);
+
+		if (wpa_s->current_ssid == ssid ||
+		    wpa_s->current_ssid == NULL) {
+			/*
+			 * Invalidate the EAP session cache if anything in the
+			 * current or previously used configuration changes.
+			 */
+			eapol_sm_invalidate_cached_session(wpa_s->eapol);
+		}
+
 		if ((os_strcmp(entry.key, "psk") == 0 &&
 		     value[0] == '"' && ssid->ssid_len) ||
 		    (os_strcmp(entry.key, "ssid") == 0 && ssid->passphrase))
@@ -306,27 +310,21 @@
 
 	if (!dbus_type_is_basic(type)) {
 		dbus_set_error(error, DBUS_ERROR_FAILED,
-		               "%s: given type is not basic", __func__);
+			       "%s: given type is not basic", __func__);
 		return FALSE;
 	}
 
 	if (!dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT,
-	                                      wpa_dbus_type_as_string(type),
-	                                      &variant_iter))
-		goto error;
-
-	if (!dbus_message_iter_append_basic(&variant_iter, type, val))
-		goto error;
-
-	if (!dbus_message_iter_close_container(iter, &variant_iter))
-		goto error;
+					      wpa_dbus_type_as_string(type),
+					      &variant_iter) ||
+	    !dbus_message_iter_append_basic(&variant_iter, type, val) ||
+	    !dbus_message_iter_close_container(iter, &variant_iter)) {
+		dbus_set_error(error, DBUS_ERROR_FAILED,
+			       "%s: error constructing reply", __func__);
+		return FALSE;
+	}
 
 	return TRUE;
-
-error:
-	dbus_set_error(error, DBUS_ERROR_FAILED,
-	               "%s: error constructing reply", __func__);
-	return FALSE;
 }
 
 
@@ -389,7 +387,7 @@
 
 	if (!dbus_type_is_basic(type)) {
 		dbus_set_error(error, DBUS_ERROR_FAILED,
-		               "%s: given type is not basic", __func__);
+			       "%s: given type is not basic", __func__);
 		return FALSE;
 	}
 
@@ -397,20 +395,15 @@
 	type_str[1] = sub_type_str[0];
 
 	if (!dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT,
-					      type_str, &variant_iter)) {
-		dbus_set_error(error, DBUS_ERROR_FAILED,
-		               "%s: failed to construct message 1", __func__);
-		return FALSE;
-	}
-
-	if (!dbus_message_iter_open_container(&variant_iter, DBUS_TYPE_ARRAY,
+					      type_str, &variant_iter) ||
+	    !dbus_message_iter_open_container(&variant_iter, DBUS_TYPE_ARRAY,
 					      sub_type_str, &array_iter)) {
 		dbus_set_error(error, DBUS_ERROR_FAILED,
-		               "%s: failed to construct message 2", __func__);
+			       "%s: failed to construct message", __func__);
 		return FALSE;
 	}
 
-	switch(type) {
+	switch (type) {
 	case DBUS_TYPE_BYTE:
 	case DBUS_TYPE_BOOLEAN:
 		element_size = 1;
@@ -436,7 +429,7 @@
 		break;
 	default:
 		dbus_set_error(error, DBUS_ERROR_FAILED,
-		               "%s: unknown element type %d", __func__, type);
+			       "%s: unknown element type %d", __func__, type);
 		return FALSE;
 	}
 
@@ -450,15 +443,10 @@
 		}
 	}
 
-	if (!dbus_message_iter_close_container(&variant_iter, &array_iter)) {
+	if (!dbus_message_iter_close_container(&variant_iter, &array_iter) ||
+	    !dbus_message_iter_close_container(iter, &variant_iter)) {
 		dbus_set_error(error, DBUS_ERROR_FAILED,
-		               "%s: failed to construct message 3", __func__);
-		return FALSE;
-	}
-
-	if (!dbus_message_iter_close_container(iter, &variant_iter)) {
-		dbus_set_error(error, DBUS_ERROR_FAILED,
-		               "%s: failed to construct message 4", __func__);
+			       "%s: failed to construct message 3", __func__);
 		return FALSE;
 	}
 
@@ -501,15 +489,11 @@
 	inner_type_str[1] = sub_type_str[0];
 
 	if (!dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT,
-					      type_str, &variant_iter)) {
-		dbus_set_error(error, DBUS_ERROR_FAILED,
-			       "%s: failed to construct message 1", __func__);
-		return FALSE;
-	}
-	if (!dbus_message_iter_open_container(&variant_iter, DBUS_TYPE_ARRAY,
+					      type_str, &variant_iter) ||
+	    !dbus_message_iter_open_container(&variant_iter, DBUS_TYPE_ARRAY,
 					      inner_type_str, &array_iter)) {
 		dbus_set_error(error, DBUS_ERROR_FAILED,
-			       "%s: failed to construct message 2", __func__);
+			       "%s: failed to construct message", __func__);
 		return FALSE;
 	}
 
@@ -520,15 +504,10 @@
 
 	}
 
-	if (!dbus_message_iter_close_container(&variant_iter, &array_iter)) {
+	if (!dbus_message_iter_close_container(&variant_iter, &array_iter) ||
+	    !dbus_message_iter_close_container(iter, &variant_iter)) {
 		dbus_set_error(error, DBUS_ERROR_FAILED,
-			       "%s: failed to close message 2", __func__);
-		return FALSE;
-	}
-
-	if (!dbus_message_iter_close_container(iter, &variant_iter)) {
-		dbus_set_error(error, DBUS_ERROR_FAILED,
-			       "%s: failed to close message 1", __func__);
+			       "%s: failed to close message", __func__);
 		return FALSE;
 	}
 
@@ -566,34 +545,34 @@
 	while (wpa_dbus_dict_has_dict_entry(&iter_dict)) {
 		if (!wpa_dbus_dict_get_entry(&iter_dict, &entry))
 			goto error;
-		if (!os_strcmp(entry.key, "Driver") &&
-		    (entry.type == DBUS_TYPE_STRING)) {
+		if (os_strcmp(entry.key, "Driver") == 0 &&
+		    entry.type == DBUS_TYPE_STRING) {
 			os_free(driver);
 			driver = os_strdup(entry.str_value);
 			wpa_dbus_dict_entry_clear(&entry);
 			if (driver == NULL)
-				goto error;
-		} else if (!os_strcmp(entry.key, "Ifname") &&
-			   (entry.type == DBUS_TYPE_STRING)) {
+				goto oom;
+		} else if (os_strcmp(entry.key, "Ifname") == 0 &&
+			   entry.type == DBUS_TYPE_STRING) {
 			os_free(ifname);
 			ifname = os_strdup(entry.str_value);
 			wpa_dbus_dict_entry_clear(&entry);
 			if (ifname == NULL)
-				goto error;
-		} else if (!os_strcmp(entry.key, "ConfigFile") &&
-			   (entry.type == DBUS_TYPE_STRING)) {
+				goto oom;
+		} else if (os_strcmp(entry.key, "ConfigFile") == 0 &&
+			   entry.type == DBUS_TYPE_STRING) {
 			os_free(confname);
 			confname = os_strdup(entry.str_value);
 			wpa_dbus_dict_entry_clear(&entry);
 			if (confname == NULL)
-				goto error;
-		} else if (!os_strcmp(entry.key, "BridgeIfname") &&
-			   (entry.type == DBUS_TYPE_STRING)) {
+				goto oom;
+		} else if (os_strcmp(entry.key, "BridgeIfname") == 0 &&
+			   entry.type == DBUS_TYPE_STRING) {
 			os_free(bridge_ifname);
 			bridge_ifname = os_strdup(entry.str_value);
 			wpa_dbus_dict_entry_clear(&entry);
 			if (bridge_ifname == NULL)
-				goto error;
+				goto oom;
 		} else {
 			wpa_dbus_dict_entry_clear(&entry);
 			goto error;
@@ -608,28 +587,30 @@
 	 * an error if we already control it.
 	 */
 	if (wpa_supplicant_get_iface(global, ifname) != NULL) {
-		reply = dbus_message_new_error(message,
-					       WPAS_DBUS_ERROR_IFACE_EXISTS,
-					       "wpa_supplicant already "
-					       "controls this interface.");
+		reply = dbus_message_new_error(
+			message, WPAS_DBUS_ERROR_IFACE_EXISTS,
+			"wpa_supplicant already controls this interface.");
 	} else {
 		struct wpa_supplicant *wpa_s;
 		struct wpa_interface iface;
+
 		os_memset(&iface, 0, sizeof(iface));
 		iface.driver = driver;
 		iface.ifname = ifname;
 		iface.confname = confname;
 		iface.bridge_ifname = bridge_ifname;
 		/* Otherwise, have wpa_supplicant attach to it. */
-		if ((wpa_s = wpa_supplicant_add_iface(global, &iface))) {
+		wpa_s = wpa_supplicant_add_iface(global, &iface, NULL);
+		if (wpa_s && wpa_s->dbus_new_path) {
 			const char *path = wpa_s->dbus_new_path;
+
 			reply = dbus_message_new_method_return(message);
 			dbus_message_append_args(reply, DBUS_TYPE_OBJECT_PATH,
-			                         &path, DBUS_TYPE_INVALID);
+						 &path, DBUS_TYPE_INVALID);
 		} else {
 			reply = wpas_dbus_error_unknown_error(
-				message, "wpa_supplicant couldn't grab this "
-				"interface.");
+				message,
+				"wpa_supplicant couldn't grab this interface.");
 		}
 	}
 
@@ -643,6 +624,9 @@
 error:
 	reply = wpas_dbus_error_invalid_args(message, NULL);
 	goto out;
+oom:
+	reply = wpas_dbus_error_no_memory(message);
+	goto out;
 }
 
 
@@ -672,8 +656,8 @@
 		reply = wpas_dbus_error_iface_unknown(message);
 	else if (wpa_supplicant_remove_iface(global, wpa_s, 0)) {
 		reply = wpas_dbus_error_unknown_error(
-			message, "wpa_supplicant couldn't remove this "
-			"interface.");
+			message,
+			"wpa_supplicant couldn't remove this interface.");
 	}
 
 	return reply;
@@ -701,19 +685,17 @@
 			      DBUS_TYPE_INVALID);
 
 	wpa_s = wpa_supplicant_get_iface(global, ifname);
-	if (wpa_s == NULL)
+	if (wpa_s == NULL || wpa_s->dbus_new_path == NULL)
 		return wpas_dbus_error_iface_unknown(message);
 
 	path = wpa_s->dbus_new_path;
 	reply = dbus_message_new_method_return(message);
 	if (reply == NULL)
-		return dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY,
-					      NULL);
+		return wpas_dbus_error_no_memory(message);
 	if (!dbus_message_append_args(reply, DBUS_TYPE_OBJECT_PATH, &path,
 				      DBUS_TYPE_INVALID)) {
 		dbus_message_unref(reply);
-		return dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY,
-					      NULL);
+		return wpas_dbus_error_no_memory(message);
 	}
 
 	return reply;
@@ -756,8 +738,8 @@
  * Getter for "DebugTimestamp" property.
  */
 dbus_bool_t wpas_dbus_getter_debug_timestamp(DBusMessageIter *iter,
-                                             DBusError *error,
-                                             void *user_data)
+					     DBusError *error,
+					     void *user_data)
 {
 	return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_BOOLEAN,
 						&wpa_debug_timestamp, error);
@@ -812,8 +794,8 @@
 	if (val < 0 ||
 	    wpa_supplicant_set_debug_params(global, val, wpa_debug_timestamp,
 					    wpa_debug_show_keys)) {
-		dbus_set_error_const(error, DBUS_ERROR_FAILED, "wrong debug "
-				     "level value");
+		dbus_set_error_const(error, DBUS_ERROR_FAILED,
+				     "wrong debug level value");
 		return FALSE;
 	}
 
@@ -895,8 +877,10 @@
 	unsigned int i = 0, num = 0;
 	dbus_bool_t success;
 
-	for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next)
-		num++;
+	for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) {
+		if (wpa_s->dbus_new_path)
+			num++;
+	}
 
 	paths = os_calloc(num, sizeof(char *));
 	if (!paths) {
@@ -904,8 +888,10 @@
 		return FALSE;
 	}
 
-	for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next)
-		paths[i++] = wpa_s->dbus_new_path;
+	for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) {
+		if (wpa_s->dbus_new_path)
+			paths[i++] = wpa_s->dbus_new_path;
+	}
 
 	success = wpas_dbus_simple_array_property_getter(iter,
 							 DBUS_TYPE_OBJECT_PATH,
@@ -963,8 +949,8 @@
  * and P2P that are determined at compile time.
  */
 dbus_bool_t wpas_dbus_getter_global_capabilities(DBusMessageIter *iter,
-					         DBusError *error,
-					         void *user_data)
+						 DBusError *error,
+						 void *user_data)
 {
 	const char *capabilities[5] = { NULL, NULL, NULL, NULL, NULL };
 	size_t num_items = 0;
@@ -993,8 +979,8 @@
 				   char **type, DBusMessage **reply)
 {
 	if (dbus_message_iter_get_arg_type(var) != DBUS_TYPE_STRING) {
-		wpa_printf(MSG_DEBUG, "wpas_dbus_handler_scan[dbus]: "
-			   "Type must be a string");
+		wpa_printf(MSG_DEBUG, "%s[dbus]: Type must be a string",
+			   __func__);
 		*reply = wpas_dbus_error_invalid_args(
 			message, "Wrong Type value type. String required");
 		return -1;
@@ -1016,36 +1002,36 @@
 	int len;
 
 	if (dbus_message_iter_get_arg_type(var) != DBUS_TYPE_ARRAY) {
-		wpa_printf(MSG_DEBUG, "wpas_dbus_handler_scan[dbus]: ssids "
-			   "must be an array of arrays of bytes");
+		wpa_printf(MSG_DEBUG,
+			   "%s[dbus]: ssids must be an array of arrays of bytes",
+			   __func__);
 		*reply = wpas_dbus_error_invalid_args(
-			message, "Wrong SSIDs value type. Array of arrays of "
-			"bytes required");
+			message,
+			"Wrong SSIDs value type. Array of arrays of bytes required");
 		return -1;
 	}
 
 	dbus_message_iter_recurse(var, &array_iter);
 
 	if (dbus_message_iter_get_arg_type(&array_iter) != DBUS_TYPE_ARRAY ||
-	    dbus_message_iter_get_element_type(&array_iter) != DBUS_TYPE_BYTE)
-	{
-		wpa_printf(MSG_DEBUG, "wpas_dbus_handler_scan[dbus]: ssids "
-			   "must be an array of arrays of bytes");
+	    dbus_message_iter_get_element_type(&array_iter) != DBUS_TYPE_BYTE) {
+		wpa_printf(MSG_DEBUG,
+			   "%s[dbus]: ssids must be an array of arrays of bytes",
+			   __func__);
 		*reply = wpas_dbus_error_invalid_args(
-			message, "Wrong SSIDs value type. Array of arrays of "
-			"bytes required");
+			message,
+			"Wrong SSIDs value type. Array of arrays of bytes required");
 		return -1;
 	}
 
-	while (dbus_message_iter_get_arg_type(&array_iter) == DBUS_TYPE_ARRAY)
-	{
+	while (dbus_message_iter_get_arg_type(&array_iter) == DBUS_TYPE_ARRAY) {
 		if (ssids_num >= WPAS_MAX_SCAN_SSIDS) {
-			wpa_printf(MSG_DEBUG, "wpas_dbus_handler_scan[dbus]: "
-				   "Too many ssids specified on scan dbus "
-				   "call");
+			wpa_printf(MSG_DEBUG,
+				   "%s[dbus]: Too many ssids specified on scan dbus call",
+				   __func__);
 			*reply = wpas_dbus_error_invalid_args(
-				message, "Too many ssids specified. Specify "
-				"at most four");
+				message,
+				"Too many ssids specified. Specify at most four");
 			return -1;
 		}
 
@@ -1053,11 +1039,10 @@
 
 		dbus_message_iter_get_fixed_array(&sub_array_iter, &val, &len);
 
-		if (len > MAX_SSID_LEN) {
+		if (len > SSID_MAX_LEN) {
 			wpa_printf(MSG_DEBUG,
-				   "wpas_dbus_handler_scan[dbus]: "
-				   "SSID too long (len=%d max_len=%d)",
-				   len, MAX_SSID_LEN);
+				   "%s[dbus]: SSID too long (len=%d max_len=%d)",
+				   __func__, len, SSID_MAX_LEN);
 			*reply = wpas_dbus_error_invalid_args(
 				message, "Invalid SSID: too long");
 			return -1;
@@ -1066,12 +1051,7 @@
 		if (len != 0) {
 			ssid = os_malloc(len);
 			if (ssid == NULL) {
-				wpa_printf(MSG_DEBUG,
-					   "wpas_dbus_handler_scan[dbus]: "
-					   "out of memory. Cannot allocate "
-					   "memory for SSID");
-				*reply = dbus_message_new_error(
-					message, DBUS_ERROR_NO_MEMORY, NULL);
+				*reply = wpas_dbus_error_no_memory(message);
 				return -1;
 			}
 			os_memcpy(ssid, val, len);
@@ -1103,28 +1083,28 @@
 	int len;
 
 	if (dbus_message_iter_get_arg_type(var) != DBUS_TYPE_ARRAY) {
-		wpa_printf(MSG_DEBUG, "wpas_dbus_handler_scan[dbus]: ies must "
-			   "be an array of arrays of bytes");
+		wpa_printf(MSG_DEBUG,
+			   "%s[dbus]: ies must be an array of arrays of bytes",
+			   __func__);
 		*reply = wpas_dbus_error_invalid_args(
-			message, "Wrong IEs value type. Array of arrays of "
-			"bytes required");
+			message,
+			"Wrong IEs value type. Array of arrays of bytes required");
 		return -1;
 	}
 
 	dbus_message_iter_recurse(var, &array_iter);
 
 	if (dbus_message_iter_get_arg_type(&array_iter) != DBUS_TYPE_ARRAY ||
-	    dbus_message_iter_get_element_type(&array_iter) != DBUS_TYPE_BYTE)
-	{
-		wpa_printf(MSG_DEBUG, "wpas_dbus_handler_scan[dbus]: ies must "
-			   "be an array of arrays of bytes");
+	    dbus_message_iter_get_element_type(&array_iter) != DBUS_TYPE_BYTE) {
+		wpa_printf(MSG_DEBUG,
+			   "%s[dbus]: ies must be an array of arrays of bytes",
+			   __func__);
 		*reply = wpas_dbus_error_invalid_args(
 			message, "Wrong IEs value type. Array required");
 		return -1;
 	}
 
-	while (dbus_message_iter_get_arg_type(&array_iter) == DBUS_TYPE_ARRAY)
-	{
+	while (dbus_message_iter_get_arg_type(&array_iter) == DBUS_TYPE_ARRAY) {
 		dbus_message_iter_recurse(&array_iter, &sub_array_iter);
 
 		dbus_message_iter_get_fixed_array(&sub_array_iter, &val, &len);
@@ -1135,12 +1115,8 @@
 
 		nies = os_realloc(ies, ies_len + len);
 		if (nies == NULL) {
-			wpa_printf(MSG_DEBUG, "wpas_dbus_handler_scan[dbus]: "
-				   "out of memory. Cannot allocate memory for "
-				   "IE");
 			os_free(ies);
-			*reply = dbus_message_new_error(
-				message, DBUS_ERROR_NO_MEMORY, NULL);
+			*reply = wpas_dbus_error_no_memory(message);
 			return -1;
 		}
 		ies = nies;
@@ -1166,11 +1142,12 @@
 	int freqs_num = 0;
 
 	if (dbus_message_iter_get_arg_type(var) != DBUS_TYPE_ARRAY) {
-		wpa_printf(MSG_DEBUG, "wpas_dbus_handler_scan[dbus]: "
-			   "Channels must be an array of structs");
+		wpa_printf(MSG_DEBUG,
+			   "%s[dbus]: Channels must be an array of structs",
+			   __func__);
 		*reply = wpas_dbus_error_invalid_args(
-			message, "Wrong Channels value type. Array of structs "
-			"required");
+			message,
+			"Wrong Channels value type. Array of structs required");
 		return -1;
 	}
 
@@ -1178,11 +1155,11 @@
 
 	if (dbus_message_iter_get_arg_type(&array_iter) != DBUS_TYPE_STRUCT) {
 		wpa_printf(MSG_DEBUG,
-			   "wpas_dbus_handler_scan[dbus]: Channels must be an "
-			   "array of structs");
+			   "%s[dbus]: Channels must be an array of structs",
+			   __func__);
 		*reply = wpas_dbus_error_invalid_args(
-			message, "Wrong Channels value type. Array of structs "
-			"required");
+			message,
+			"Wrong Channels value type. Array of structs required");
 		return -1;
 	}
 
@@ -1194,14 +1171,14 @@
 
 		if (dbus_message_iter_get_arg_type(&sub_array_iter) !=
 		    DBUS_TYPE_UINT32) {
-			wpa_printf(MSG_DEBUG, "wpas_dbus_handler_scan[dbus]: "
-				   "Channel must by specified by struct of "
-				   "two UINT32s %c",
+			wpa_printf(MSG_DEBUG,
+				   "%s[dbus]: Channel must by specified by struct of two UINT32s %c",
+				   __func__,
 				   dbus_message_iter_get_arg_type(
 					   &sub_array_iter));
 			*reply = wpas_dbus_error_invalid_args(
-				message, "Wrong Channel struct. Two UINT32s "
-				"required");
+				message,
+				"Wrong Channel struct. Two UINT32s required");
 			os_free(freqs);
 			return -1;
 		}
@@ -1210,9 +1187,9 @@
 		if (!dbus_message_iter_next(&sub_array_iter) ||
 		    dbus_message_iter_get_arg_type(&sub_array_iter) !=
 		    DBUS_TYPE_UINT32) {
-			wpa_printf(MSG_DEBUG, "wpas_dbus_handler_scan[dbus]: "
-				   "Channel must by specified by struct of "
-				   "two UINT32s");
+			wpa_printf(MSG_DEBUG,
+				   "%s[dbus]: Channel must by specified by struct of two UINT32s",
+				   __func__);
 			*reply = wpas_dbus_error_invalid_args(
 				message,
 				"Wrong Channel struct. Two UINT32s required");
@@ -1232,11 +1209,7 @@
 			freqs = nfreqs;
 		}
 		if (freqs == NULL) {
-			wpa_printf(MSG_DEBUG, "wpas_dbus_handler_scan[dbus]: "
-				   "out of memory. can't allocate memory for "
-				   "freqs");
-			*reply = dbus_message_new_error(
-				message, DBUS_ERROR_NO_MEMORY, NULL);
+			*reply = wpas_dbus_error_no_memory(message);
 			return -1;
 		}
 
@@ -1251,10 +1224,7 @@
 		os_free(freqs);
 	freqs = nfreqs;
 	if (freqs == NULL) {
-		wpa_printf(MSG_DEBUG, "wpas_dbus_handler_scan[dbus]: "
-			   "out of memory. Can't allocate memory for freqs");
-		*reply = dbus_message_new_error(
-			message, DBUS_ERROR_NO_MEMORY, NULL);
+		*reply = wpas_dbus_error_no_memory(message);
 		return -1;
 	}
 	freqs[freqs_num] = 0;
@@ -1270,8 +1240,8 @@
 					 DBusMessage **reply)
 {
 	if (dbus_message_iter_get_arg_type(var) != DBUS_TYPE_BOOLEAN) {
-		wpa_printf(MSG_DEBUG, "wpas_dbus_handler_scan[dbus]: "
-			   "Type must be a boolean");
+		wpa_printf(MSG_DEBUG, "%s[dbus]: Type must be a boolean",
+			   __func__);
 		*reply = wpas_dbus_error_invalid_args(
 			message, "Wrong Type value type. Boolean required");
 		return -1;
@@ -1308,7 +1278,7 @@
 	dbus_message_iter_recurse(&iter, &dict_iter);
 
 	while (dbus_message_iter_get_arg_type(&dict_iter) ==
-			DBUS_TYPE_DICT_ENTRY) {
+	       DBUS_TYPE_DICT_ENTRY) {
 		dbus_message_iter_recurse(&dict_iter, &entry_iter);
 		dbus_message_iter_get_basic(&entry_iter, &key);
 		dbus_message_iter_next(&entry_iter);
@@ -1337,8 +1307,8 @@
 							  &reply) < 0)
 				goto out;
 		} else {
-			wpa_printf(MSG_DEBUG, "wpas_dbus_handler_scan[dbus]: "
-				   "Unknown argument %s", key);
+			wpa_printf(MSG_DEBUG, "%s[dbus]: Unknown argument %s",
+				   __func__, key);
 			reply = wpas_dbus_error_invalid_args(message, key);
 			goto out;
 		}
@@ -1347,30 +1317,42 @@
 	}
 
 	if (!type) {
-		wpa_printf(MSG_DEBUG, "wpas_dbus_handler_scan[dbus]: "
-			   "Scan type not specified");
+		wpa_printf(MSG_DEBUG, "%s[dbus]: Scan type not specified",
+			   __func__);
 		reply = wpas_dbus_error_invalid_args(message, key);
 		goto out;
 	}
 
-	if (!os_strcmp(type, "passive")) {
+	if (os_strcmp(type, "passive") == 0) {
 		if (params.num_ssids || params.extra_ies_len) {
-			wpa_printf(MSG_DEBUG, "wpas_dbus_handler_scan[dbus]: "
-				   "SSIDs or IEs specified for passive scan.");
+			wpa_printf(MSG_DEBUG,
+				   "%s[dbus]: SSIDs or IEs specified for passive scan.",
+				   __func__);
 			reply = wpas_dbus_error_invalid_args(
-				message, "You can specify only Channels in "
-				"passive scan");
+				message,
+				"You can specify only Channels in passive scan");
 			goto out;
-		} else if (params.freqs && params.freqs[0]) {
-			if (wpa_supplicant_trigger_scan(wpa_s, &params)) {
-				reply = wpas_dbus_error_scan_error(
-					message, "Scan request rejected");
-			}
 		} else {
-			wpa_s->scan_req = MANUAL_SCAN_REQ;
-			wpa_supplicant_req_scan(wpa_s, 0, 0);
+			if (wpa_s->sched_scanning) {
+				wpa_printf(MSG_DEBUG,
+					   "%s[dbus]: Stop ongoing sched_scan to allow requested scan to proceed",
+					   __func__);
+				wpa_supplicant_cancel_sched_scan(wpa_s);
+			}
+
+			if (params.freqs && params.freqs[0]) {
+				if (wpa_supplicant_trigger_scan(wpa_s,
+								&params)) {
+					reply = wpas_dbus_error_scan_error(
+						message,
+						"Scan request rejected");
+				}
+			} else {
+				wpa_s->scan_req = MANUAL_SCAN_REQ;
+				wpa_supplicant_req_scan(wpa_s, 0, 0);
+			}
 		}
-	} else if (!os_strcmp(type, "active")) {
+	} else if (os_strcmp(type, "active") == 0) {
 		if (!params.num_ssids) {
 			/* Add wildcard ssid */
 			params.num_ssids++;
@@ -1378,13 +1360,20 @@
 #ifdef CONFIG_AUTOSCAN
 		autoscan_deinit(wpa_s);
 #endif /* CONFIG_AUTOSCAN */
+		if (wpa_s->sched_scanning) {
+			wpa_printf(MSG_DEBUG,
+				   "%s[dbus]: Stop ongoing sched_scan to allow requested scan to proceed",
+				   __func__);
+			wpa_supplicant_cancel_sched_scan(wpa_s);
+		}
+
 		if (wpa_supplicant_trigger_scan(wpa_s, &params)) {
 			reply = wpas_dbus_error_scan_error(
 				message, "Scan request rejected");
 		}
 	} else {
-		wpa_printf(MSG_DEBUG, "wpas_dbus_handler_scan[dbus]: "
-			   "Unknown scan type: %s", type);
+		wpa_printf(MSG_DEBUG, "%s[dbus]: Unknown scan type: %s",
+			   __func__, type);
 		reply = wpas_dbus_error_invalid_args(message,
 						     "Wrong scan type");
 		goto out;
@@ -1433,45 +1422,30 @@
 	dbus_message_iter_init_append(reply, &iter);
 
 	if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_VARIANT,
-					      "a{sv}", &variant_iter))
-		goto nomem;
-	if (!wpa_dbus_dict_open_write(&variant_iter, &iter_dict))
-		goto nomem;
-
-	if (!wpa_dbus_dict_append_int32(&iter_dict, "rssi", si.current_signal))
-		goto nomem;
-	if (!wpa_dbus_dict_append_int32(&iter_dict, "linkspeed",
-					si.current_txrate / 1000))
-		goto nomem;
-	if (!wpa_dbus_dict_append_int32(&iter_dict, "noise", si.current_noise))
-		goto nomem;
-	if (!wpa_dbus_dict_append_uint32(&iter_dict, "frequency", si.frequency))
-		goto nomem;
-
-	if (si.chanwidth != CHAN_WIDTH_UNKNOWN) {
-		if (!wpa_dbus_dict_append_string(&iter_dict, "width",
-					channel_width_to_string(si.chanwidth)))
-			goto nomem;
-	}
-
-	if (si.center_frq1 > 0 && si.center_frq2 > 0) {
-		if (!wpa_dbus_dict_append_int32(&iter_dict, "center-frq1",
-						si.center_frq1))
-			goto nomem;
-		if (!wpa_dbus_dict_append_int32(&iter_dict, "center-frq2",
-						si.center_frq2))
-			goto nomem;
-	}
-
-	if (si.avg_signal) {
-		if (!wpa_dbus_dict_append_int32(&iter_dict, "avg-rssi",
-						si.avg_signal))
-			goto nomem;
-	}
-
-	if (!wpa_dbus_dict_close_write(&variant_iter, &iter_dict))
-		goto nomem;
-	if (!dbus_message_iter_close_container(&iter, &variant_iter))
+					      "a{sv}", &variant_iter) ||
+	    !wpa_dbus_dict_open_write(&variant_iter, &iter_dict) ||
+	    !wpa_dbus_dict_append_int32(&iter_dict, "rssi",
+					si.current_signal) ||
+	    !wpa_dbus_dict_append_int32(&iter_dict, "linkspeed",
+					si.current_txrate / 1000) ||
+	    !wpa_dbus_dict_append_int32(&iter_dict, "noise",
+					si.current_noise) ||
+	    !wpa_dbus_dict_append_uint32(&iter_dict, "frequency",
+					 si.frequency) ||
+	    (si.chanwidth != CHAN_WIDTH_UNKNOWN &&
+	     !wpa_dbus_dict_append_string(
+		     &iter_dict, "width",
+		     channel_width_to_string(si.chanwidth))) ||
+	    (si.center_frq1 > 0 && si.center_frq2 > 0 &&
+	     (!wpa_dbus_dict_append_int32(&iter_dict, "center-frq1",
+					  si.center_frq1) ||
+	      !wpa_dbus_dict_append_int32(&iter_dict, "center-frq2",
+					  si.center_frq2))) ||
+	    (si.avg_signal &&
+	     !wpa_dbus_dict_append_int32(&iter_dict, "avg-rssi",
+					 si.avg_signal)) ||
+	    !wpa_dbus_dict_close_write(&variant_iter, &iter_dict) ||
+	    !dbus_message_iter_close_container(&iter, &variant_iter))
 		goto nomem;
 
 	return reply;
@@ -1479,8 +1453,7 @@
 nomem:
 	if (reply)
 		dbus_message_unref(reply);
-	reply = dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY, NULL);
-	return reply;
+	return wpas_dbus_error_no_memory(message);
 }
 
 
@@ -1528,14 +1501,14 @@
 
 	dbus_message_iter_init(message, &iter);
 
-	ssid = wpa_config_add_network(wpa_s->conf);
+	if (wpa_s->dbus_new_path)
+		ssid = wpa_config_add_network(wpa_s->conf);
 	if (ssid == NULL) {
-		wpa_printf(MSG_ERROR, "wpas_dbus_handler_add_network[dbus]: "
-			   "can't add new interface.");
+		wpa_printf(MSG_ERROR, "%s[dbus]: can't add new interface.",
+			   __func__);
 		reply = wpas_dbus_error_unknown_error(
 			message,
-			"wpa_supplicant could not add "
-			"a network on this interface.");
+			"wpa_supplicant could not add a network on this interface.");
 		goto err;
 	}
 	wpas_notify_network_added(wpa_s, ssid);
@@ -1544,9 +1517,9 @@
 
 	dbus_error_init(&error);
 	if (!set_network_properties(wpa_s, ssid, &iter, &error)) {
-		wpa_printf(MSG_DEBUG, "wpas_dbus_handler_add_network[dbus]:"
-			   "control interface couldn't set network "
-			   "properties");
+		wpa_printf(MSG_DEBUG,
+			   "%s[dbus]: control interface couldn't set network properties",
+			   __func__);
 		reply = wpas_dbus_reply_new_from_error(message, &error,
 						       DBUS_ERROR_INVALID_ARGS,
 						       "Failed to add network");
@@ -1561,15 +1534,13 @@
 
 	reply = dbus_message_new_method_return(message);
 	if (reply == NULL) {
-		reply = dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY,
-					       NULL);
+		reply = wpas_dbus_error_no_memory(message);
 		goto err;
 	}
 	if (!dbus_message_append_args(reply, DBUS_TYPE_OBJECT_PATH, &path,
 				      DBUS_TYPE_INVALID)) {
 		dbus_message_unref(reply);
-		reply = dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY,
-					       NULL);
+		reply = wpas_dbus_error_no_memory(message);
 		goto err;
 	}
 
@@ -1630,6 +1601,30 @@
 
 
 /**
+ * wpas_dbus_handler_reconnect - Reconnect if disconnected
+ * @message: Pointer to incoming dbus message
+ * @wpa_s: wpa_supplicant structure for a network interface
+ * Returns: InterfaceDisabled DBus error message if disabled
+ * or NULL otherwise.
+ *
+ * Handler function for "Reconnect" method call of network interface.
+ */
+DBusMessage * wpas_dbus_handler_reconnect(DBusMessage *message,
+		struct wpa_supplicant *wpa_s)
+{
+	if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED) {
+		return dbus_message_new_error(message,
+					      WPAS_DBUS_ERROR_IFACE_DISABLED,
+					      "This interface is disabled");
+	}
+
+	if (wpa_s->disconnected)
+		wpas_request_connection(wpa_s);
+	return NULL;
+}
+
+
+/**
  * wpas_dbus_handler_remove_network - Remove a configured network
  * @message: Pointer to incoming dbus message
  * @wpa_s: wpa_supplicant structure for a network interface
@@ -1642,7 +1637,7 @@
 {
 	DBusMessage *reply = NULL;
 	const char *op;
-	char *iface = NULL, *net_id = NULL;
+	char *iface, *net_id;
 	int id;
 	struct wpa_ssid *ssid;
 	int was_disabled;
@@ -1652,8 +1647,10 @@
 
 	/* Extract the network ID and ensure the network */
 	/* is actually a child of this interface */
-	iface = wpas_dbus_new_decompose_object_path(op, 0, &net_id, NULL);
-	if (iface == NULL || net_id == NULL ||
+	iface = wpas_dbus_new_decompose_object_path(op,
+						    WPAS_DBUS_NEW_NETWORKS_PART,
+						    &net_id);
+	if (iface == NULL || net_id == NULL || !wpa_s->dbus_new_path ||
 	    os_strcmp(iface, wpa_s->dbus_new_path) != 0) {
 		reply = wpas_dbus_error_invalid_args(message, op);
 		goto out;
@@ -1680,25 +1677,24 @@
 		wpa_supplicant_deauthenticate(wpa_s,
 					      WLAN_REASON_DEAUTH_LEAVING);
 	else if (!was_disabled && wpa_s->sched_scanning) {
-		wpa_printf(MSG_DEBUG, "Stop ongoing sched_scan to remove "
-			   "network from filters");
+		wpa_printf(MSG_DEBUG,
+			   "Stop ongoing sched_scan to remove network from filters");
 		wpa_supplicant_cancel_sched_scan(wpa_s);
 		wpa_supplicant_req_scan(wpa_s, 0, 0);
 	}
 
 	if (wpa_config_remove_network(wpa_s->conf, id) < 0) {
 		wpa_printf(MSG_ERROR,
-			   "wpas_dbus_handler_remove_network[dbus]: "
-			   "error occurred when removing network %d", id);
+			   "%s[dbus]: error occurred when removing network %d",
+			   __func__, id);
 		reply = wpas_dbus_error_unknown_error(
-			message, "error removing the specified network on "
-			"this interface.");
+			message,
+			"error removing the specified network on is interface.");
 		goto out;
 	}
 
 out:
 	os_free(iface);
-	os_free(net_id);
 	return reply;
 }
 
@@ -1711,9 +1707,8 @@
 
 	if (wpa_config_remove_network(wpa_s->conf, ssid->id) < 0) {
 		wpa_printf(MSG_ERROR,
-			   "wpas_dbus_handler_remove_all_networks[dbus]: "
-			   "error occurred when removing network %d",
-			   ssid->id);
+			   "%s[dbus]: error occurred when removing network %d",
+			   __func__, ssid->id);
 		return;
 	}
 
@@ -1756,7 +1751,7 @@
 {
 	DBusMessage *reply = NULL;
 	const char *op;
-	char *iface = NULL, *net_id = NULL;
+	char *iface, *net_id;
 	int id;
 	struct wpa_ssid *ssid;
 
@@ -1765,8 +1760,10 @@
 
 	/* Extract the network ID and ensure the network */
 	/* is actually a child of this interface */
-	iface = wpas_dbus_new_decompose_object_path(op, 0, &net_id, NULL);
-	if (iface == NULL || net_id == NULL ||
+	iface = wpas_dbus_new_decompose_object_path(op,
+						    WPAS_DBUS_NEW_NETWORKS_PART,
+						    &net_id);
+	if (iface == NULL || net_id == NULL || !wpa_s->dbus_new_path ||
 	    os_strcmp(iface, wpa_s->dbus_new_path) != 0) {
 		reply = wpas_dbus_error_invalid_args(message, op);
 		goto out;
@@ -1790,7 +1787,6 @@
 
 out:
 	os_free(iface);
-	os_free(net_id);
 	return reply;
 }
 
@@ -1809,21 +1805,23 @@
 #ifdef IEEE8021X_EAPOL
 	DBusMessage *reply = NULL;
 	const char *op, *field, *value;
-	char *iface = NULL, *net_id = NULL;
+	char *iface, *net_id;
 	int id;
 	struct wpa_ssid *ssid;
 
 	if (!dbus_message_get_args(message, NULL,
-	                           DBUS_TYPE_OBJECT_PATH, &op,
-	                           DBUS_TYPE_STRING, &field,
-	                           DBUS_TYPE_STRING, &value,
-			           DBUS_TYPE_INVALID))
+				   DBUS_TYPE_OBJECT_PATH, &op,
+				   DBUS_TYPE_STRING, &field,
+				   DBUS_TYPE_STRING, &value,
+				   DBUS_TYPE_INVALID))
 		return wpas_dbus_error_invalid_args(message, NULL);
 
 	/* Extract the network ID and ensure the network */
 	/* is actually a child of this interface */
-	iface = wpas_dbus_new_decompose_object_path(op, 0, &net_id, NULL);
-	if (iface == NULL || net_id == NULL ||
+	iface = wpas_dbus_new_decompose_object_path(op,
+						    WPAS_DBUS_NEW_NETWORKS_PART,
+						    &net_id);
+	if (iface == NULL || net_id == NULL || !wpa_s->dbus_new_path ||
 	    os_strcmp(iface, wpa_s->dbus_new_path) != 0) {
 		reply = wpas_dbus_error_invalid_args(message, op);
 		goto out;
@@ -1852,7 +1850,6 @@
 
 out:
 	os_free(iface);
-	os_free(net_id);
 	return reply;
 #else /* IEEE8021X_EAPOL */
 	wpa_printf(MSG_DEBUG, "CTRL_IFACE: 802.1X not included");
@@ -1898,26 +1895,18 @@
 
 	blob = os_zalloc(sizeof(*blob));
 	if (!blob) {
-		reply = dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY,
-					       NULL);
+		reply = wpas_dbus_error_no_memory(message);
 		goto err;
 	}
 
 	blob->data = os_malloc(blob_len);
-	if (!blob->data) {
-		reply = dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY,
-					       NULL);
+	blob->name = os_strdup(blob_name);
+	if (!blob->data || !blob->name) {
+		reply = wpas_dbus_error_no_memory(message);
 		goto err;
 	}
 	os_memcpy(blob->data, blob_data, blob_len);
-
 	blob->len = blob_len;
-	blob->name = os_strdup(blob_name);
-	if (!blob->name) {
-		reply = dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY,
-					       NULL);
-		goto err;
-	}
 
 	wpa_config_set_blob(wpa_s->conf, blob);
 	wpas_notify_blob_added(wpa_s, blob->name);
@@ -1962,39 +1951,21 @@
 	}
 
 	reply = dbus_message_new_method_return(message);
-	if (!reply) {
-		reply = dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY,
-					       NULL);
-		goto out;
-	}
+	if (!reply)
+		return wpas_dbus_error_no_memory(message);
 
 	dbus_message_iter_init_append(reply, &iter);
 
 	if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
 					      DBUS_TYPE_BYTE_AS_STRING,
-					      &array_iter)) {
+					      &array_iter) ||
+	    !dbus_message_iter_append_fixed_array(&array_iter, DBUS_TYPE_BYTE,
+						  &(blob->data), blob->len) ||
+	    !dbus_message_iter_close_container(&iter, &array_iter)) {
 		dbus_message_unref(reply);
-		reply = dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY,
-					       NULL);
-		goto out;
+		reply = wpas_dbus_error_no_memory(message);
 	}
 
-	if (!dbus_message_iter_append_fixed_array(&array_iter, DBUS_TYPE_BYTE,
-						  &(blob->data), blob->len)) {
-		dbus_message_unref(reply);
-		reply = dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY,
-					       NULL);
-		goto out;
-	}
-
-	if (!dbus_message_iter_close_container(&iter, &array_iter)) {
-		dbus_message_unref(reply);
-		reply = dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY,
-					       NULL);
-		goto out;
-	}
-
-out:
 	return reply;
 }
 
@@ -2076,11 +2047,10 @@
 
 	if (arg != NULL && os_strlen(arg) > 0) {
 		char *tmp;
+
 		tmp = os_strdup(arg);
 		if (tmp == NULL) {
-			reply = dbus_message_new_error(message,
-						       DBUS_ERROR_NO_MEMORY,
-						       NULL);
+			reply = wpas_dbus_error_no_memory(message);
 		} else {
 			os_free(wpa_s->conf->autoscan);
 			wpa_s->conf->autoscan = tmp;
@@ -2342,15 +2312,16 @@
 						   pkcs11_module_path))
 		return dbus_message_new_error(
 			message, DBUS_ERROR_FAILED,
-			"Reinit of the EAPOL state machine with the new PKCS "
-			"#11 engine and module path failed.");
+			"Reinit of the EAPOL state machine with the new PKCS #11 engine and module path failed.");
 
-	wpa_dbus_mark_property_changed(
-		wpa_s->global->dbus, wpa_s->dbus_new_path,
-		WPAS_DBUS_NEW_IFACE_INTERFACE, "PKCS11EnginePath");
-	wpa_dbus_mark_property_changed(
-		wpa_s->global->dbus, wpa_s->dbus_new_path,
-		WPAS_DBUS_NEW_IFACE_INTERFACE, "PKCS11ModulePath");
+	if (wpa_s->dbus_new_path) {
+		wpa_dbus_mark_property_changed(
+			wpa_s->global->dbus, wpa_s->dbus_new_path,
+			WPAS_DBUS_NEW_IFACE_INTERFACE, "PKCS11EnginePath");
+		wpa_dbus_mark_property_changed(
+			wpa_s->global->dbus, wpa_s->dbus_new_path,
+			WPAS_DBUS_NEW_IFACE_INTERFACE, "PKCS11ModulePath");
+	}
 
 	return NULL;
 }
@@ -2376,10 +2347,8 @@
 	const char *scans[] = { "active", "passive", "ssid" };
 
 	if (!dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT,
-					      "a{sv}", &variant_iter))
-		goto nomem;
-
-	if (!wpa_dbus_dict_open_write(&variant_iter, &iter_dict))
+					      "a{sv}", &variant_iter) ||
+	    !wpa_dbus_dict_open_write(&variant_iter, &iter_dict))
 		goto nomem;
 
 	res = wpa_drv_get_capa(wpa_s, &capa);
@@ -2387,6 +2356,7 @@
 	/***** pairwise cipher */
 	if (res < 0) {
 		const char *args[] = {"ccmp", "tkip", "none"};
+
 		if (!wpa_dbus_dict_append_string_array(
 			    &iter_dict, "Pairwise", args,
 			    ARRAY_SIZE(args)))
@@ -2395,46 +2365,26 @@
 		if (!wpa_dbus_dict_begin_string_array(&iter_dict, "Pairwise",
 						      &iter_dict_entry,
 						      &iter_dict_val,
-						      &iter_array))
-			goto nomem;
-
-		if (capa.enc & WPA_DRIVER_CAPA_ENC_CCMP_256) {
-			if (!wpa_dbus_dict_string_array_add_element(
-				    &iter_array, "ccmp-256"))
-				goto nomem;
-		}
-
-		if (capa.enc & WPA_DRIVER_CAPA_ENC_GCMP_256) {
-			if (!wpa_dbus_dict_string_array_add_element(
-				    &iter_array, "gcmp-256"))
-				goto nomem;
-		}
-
-		if (capa.enc & WPA_DRIVER_CAPA_ENC_CCMP) {
-			if (!wpa_dbus_dict_string_array_add_element(
-				    &iter_array, "ccmp"))
-				goto nomem;
-		}
-
-		if (capa.enc & WPA_DRIVER_CAPA_ENC_GCMP) {
-			if (!wpa_dbus_dict_string_array_add_element(
-				    &iter_array, "gcmp"))
-				goto nomem;
-		}
-
-		if (capa.enc & WPA_DRIVER_CAPA_ENC_TKIP) {
-			if (!wpa_dbus_dict_string_array_add_element(
-				    &iter_array, "tkip"))
-				goto nomem;
-		}
-
-		if (capa.key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_WPA_NONE) {
-			if (!wpa_dbus_dict_string_array_add_element(
-				    &iter_array, "none"))
-				goto nomem;
-		}
-
-		if (!wpa_dbus_dict_end_string_array(&iter_dict,
+						      &iter_array) ||
+		    ((capa.enc & WPA_DRIVER_CAPA_ENC_CCMP_256) &&
+		     !wpa_dbus_dict_string_array_add_element(
+			     &iter_array, "ccmp-256")) ||
+		    ((capa.enc & WPA_DRIVER_CAPA_ENC_GCMP_256) &&
+		     !wpa_dbus_dict_string_array_add_element(
+			     &iter_array, "gcmp-256")) ||
+		    ((capa.enc & WPA_DRIVER_CAPA_ENC_CCMP) &&
+		     !wpa_dbus_dict_string_array_add_element(
+			     &iter_array, "ccmp")) ||
+		    ((capa.enc & WPA_DRIVER_CAPA_ENC_GCMP) &&
+		     !wpa_dbus_dict_string_array_add_element(
+			     &iter_array, "gcmp")) ||
+		    ((capa.enc & WPA_DRIVER_CAPA_ENC_TKIP) &&
+		     !wpa_dbus_dict_string_array_add_element(
+			     &iter_array, "tkip")) ||
+		    ((capa.key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_WPA_NONE) &&
+		     !wpa_dbus_dict_string_array_add_element(
+			     &iter_array, "none")) ||
+		    !wpa_dbus_dict_end_string_array(&iter_dict,
 						    &iter_dict_entry,
 						    &iter_dict_val,
 						    &iter_array))
@@ -2446,6 +2396,7 @@
 		const char *args[] = {
 			"ccmp", "tkip", "wep104", "wep40"
 		};
+
 		if (!wpa_dbus_dict_append_string_array(
 			    &iter_dict, "Group", args,
 			    ARRAY_SIZE(args)))
@@ -2454,52 +2405,29 @@
 		if (!wpa_dbus_dict_begin_string_array(&iter_dict, "Group",
 						      &iter_dict_entry,
 						      &iter_dict_val,
-						      &iter_array))
-			goto nomem;
-
-		if (capa.enc & WPA_DRIVER_CAPA_ENC_CCMP_256) {
-			if (!wpa_dbus_dict_string_array_add_element(
-				    &iter_array, "ccmp-256"))
-				goto nomem;
-		}
-
-		if (capa.enc & WPA_DRIVER_CAPA_ENC_GCMP_256) {
-			if (!wpa_dbus_dict_string_array_add_element(
-				    &iter_array, "gcmp-256"))
-				goto nomem;
-		}
-
-		if (capa.enc & WPA_DRIVER_CAPA_ENC_CCMP) {
-			if (!wpa_dbus_dict_string_array_add_element(
-				    &iter_array, "ccmp"))
-				goto nomem;
-		}
-
-		if (capa.enc & WPA_DRIVER_CAPA_ENC_GCMP) {
-			if (!wpa_dbus_dict_string_array_add_element(
-				    &iter_array, "gcmp"))
-				goto nomem;
-		}
-
-		if (capa.enc & WPA_DRIVER_CAPA_ENC_TKIP) {
-			if (!wpa_dbus_dict_string_array_add_element(
-				    &iter_array, "tkip"))
-				goto nomem;
-		}
-
-		if (capa.enc & WPA_DRIVER_CAPA_ENC_WEP104) {
-			if (!wpa_dbus_dict_string_array_add_element(
-				    &iter_array, "wep104"))
-				goto nomem;
-		}
-
-		if (capa.enc & WPA_DRIVER_CAPA_ENC_WEP40) {
-			if (!wpa_dbus_dict_string_array_add_element(
-				    &iter_array, "wep40"))
-				goto nomem;
-		}
-
-		if (!wpa_dbus_dict_end_string_array(&iter_dict,
+						      &iter_array) ||
+		    ((capa.enc & WPA_DRIVER_CAPA_ENC_CCMP_256) &&
+		     !wpa_dbus_dict_string_array_add_element(
+			     &iter_array, "ccmp-256")) ||
+		    ((capa.enc & WPA_DRIVER_CAPA_ENC_GCMP_256) &&
+		     !wpa_dbus_dict_string_array_add_element(
+			     &iter_array, "gcmp-256")) ||
+		    ((capa.enc & WPA_DRIVER_CAPA_ENC_CCMP) &&
+		     !wpa_dbus_dict_string_array_add_element(
+			     &iter_array, "ccmp")) ||
+		    ((capa.enc & WPA_DRIVER_CAPA_ENC_GCMP) &&
+		     !wpa_dbus_dict_string_array_add_element(
+			     &iter_array, "gcmp")) ||
+		    ((capa.enc & WPA_DRIVER_CAPA_ENC_TKIP) &&
+		     !wpa_dbus_dict_string_array_add_element(
+			     &iter_array, "tkip")) ||
+		    ((capa.enc & WPA_DRIVER_CAPA_ENC_WEP104) &&
+		     !wpa_dbus_dict_string_array_add_element(
+			     &iter_array, "wep104")) ||
+		    ((capa.enc & WPA_DRIVER_CAPA_ENC_WEP40) &&
+		     !wpa_dbus_dict_string_array_add_element(
+			     &iter_array, "wep40")) ||
+		    !wpa_dbus_dict_end_string_array(&iter_dict,
 						    &iter_dict_entry,
 						    &iter_dict_val,
 						    &iter_array))
@@ -2523,28 +2451,22 @@
 		if (!wpa_dbus_dict_begin_string_array(&iter_dict, "KeyMgmt",
 						      &iter_dict_entry,
 						      &iter_dict_val,
-						      &iter_array))
-			goto nomem;
-
-		if (!wpa_dbus_dict_string_array_add_element(&iter_array,
-							    "none"))
-			goto nomem;
-
-		if (!wpa_dbus_dict_string_array_add_element(&iter_array,
+						      &iter_array) ||
+		    !wpa_dbus_dict_string_array_add_element(&iter_array,
+							    "none") ||
+		    !wpa_dbus_dict_string_array_add_element(&iter_array,
 							    "ieee8021x"))
 			goto nomem;
 
 		if (capa.key_mgmt & (WPA_DRIVER_CAPA_KEY_MGMT_WPA |
 				     WPA_DRIVER_CAPA_KEY_MGMT_WPA2)) {
 			if (!wpa_dbus_dict_string_array_add_element(
-				    &iter_array, "wpa-eap"))
+				    &iter_array, "wpa-eap") ||
+			    ((capa.key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_FT) &&
+			     !wpa_dbus_dict_string_array_add_element(
+				     &iter_array, "wpa-ft-eap")))
 				goto nomem;
 
-			if (capa.key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_FT)
-				if (!wpa_dbus_dict_string_array_add_element(
-					    &iter_array, "wpa-ft-eap"))
-					goto nomem;
-
 /* TODO: Ensure that driver actually supports sha256 encryption. */
 #ifdef CONFIG_IEEE80211W
 			if (!wpa_dbus_dict_string_array_add_element(
@@ -2556,14 +2478,13 @@
 		if (capa.key_mgmt & (WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK |
 				     WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK)) {
 			if (!wpa_dbus_dict_string_array_add_element(
-				    &iter_array, "wpa-psk"))
+				    &iter_array, "wpa-psk") ||
+			    ((capa.key_mgmt &
+			      WPA_DRIVER_CAPA_KEY_MGMT_FT_PSK) &&
+			     !wpa_dbus_dict_string_array_add_element(
+				     &iter_array, "wpa-ft-psk")))
 				goto nomem;
 
-			if (capa.key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_FT_PSK)
-				if (!wpa_dbus_dict_string_array_add_element(
-					    &iter_array, "wpa-ft-psk"))
-					goto nomem;
-
 /* TODO: Ensure that driver actually supports sha256 encryption. */
 #ifdef CONFIG_IEEE80211W
 			if (!wpa_dbus_dict_string_array_add_element(
@@ -2572,11 +2493,10 @@
 #endif /* CONFIG_IEEE80211W */
 		}
 
-		if (capa.key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_WPA_NONE) {
-			if (!wpa_dbus_dict_string_array_add_element(
-				    &iter_array, "wpa-none"))
-				goto nomem;
-		}
+		if ((capa.key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_WPA_NONE) &&
+		    !wpa_dbus_dict_string_array_add_element(&iter_array,
+							    "wpa-none"))
+			goto nomem;
 
 
 #ifdef CONFIG_WPS
@@ -2595,6 +2515,7 @@
 	/***** WPA protocol */
 	if (res < 0) {
 		const char *args[] = { "rsn", "wpa" };
+
 		if (!wpa_dbus_dict_append_string_array(
 			    &iter_dict, "Protocol", args,
 			    ARRAY_SIZE(args)))
@@ -2603,24 +2524,16 @@
 		if (!wpa_dbus_dict_begin_string_array(&iter_dict, "Protocol",
 						      &iter_dict_entry,
 						      &iter_dict_val,
-						      &iter_array))
-			goto nomem;
-
-		if (capa.key_mgmt & (WPA_DRIVER_CAPA_KEY_MGMT_WPA2 |
-				     WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK)) {
-			if (!wpa_dbus_dict_string_array_add_element(
-				    &iter_array, "rsn"))
-				goto nomem;
-		}
-
-		if (capa.key_mgmt & (WPA_DRIVER_CAPA_KEY_MGMT_WPA |
-				     WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK)) {
-			if (!wpa_dbus_dict_string_array_add_element(
-				    &iter_array, "wpa"))
-				goto nomem;
-		}
-
-		if (!wpa_dbus_dict_end_string_array(&iter_dict,
+						      &iter_array) ||
+		    ((capa.key_mgmt & (WPA_DRIVER_CAPA_KEY_MGMT_WPA2 |
+				       WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK)) &&
+		     !wpa_dbus_dict_string_array_add_element(
+			     &iter_array, "rsn")) ||
+		    ((capa.key_mgmt & (WPA_DRIVER_CAPA_KEY_MGMT_WPA |
+				       WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK)) &&
+		     !wpa_dbus_dict_string_array_add_element(
+			     &iter_array, "wpa")) ||
+		    !wpa_dbus_dict_end_string_array(&iter_dict,
 						    &iter_dict_entry,
 						    &iter_dict_val,
 						    &iter_array))
@@ -2630,6 +2543,7 @@
 	/***** auth alg */
 	if (res < 0) {
 		const char *args[] = { "open", "shared", "leap" };
+
 		if (!wpa_dbus_dict_append_string_array(
 			    &iter_dict, "AuthAlg", args,
 			    ARRAY_SIZE(args)))
@@ -2641,25 +2555,16 @@
 						      &iter_array))
 			goto nomem;
 
-		if (capa.auth & (WPA_DRIVER_AUTH_OPEN)) {
-			if (!wpa_dbus_dict_string_array_add_element(
-				    &iter_array, "open"))
-				goto nomem;
-		}
-
-		if (capa.auth & (WPA_DRIVER_AUTH_SHARED)) {
-			if (!wpa_dbus_dict_string_array_add_element(
-				    &iter_array, "shared"))
-				goto nomem;
-		}
-
-		if (capa.auth & (WPA_DRIVER_AUTH_LEAP)) {
-			if (!wpa_dbus_dict_string_array_add_element(
-				    &iter_array, "leap"))
-				goto nomem;
-		}
-
-		if (!wpa_dbus_dict_end_string_array(&iter_dict,
+		if (((capa.auth & WPA_DRIVER_AUTH_OPEN) &&
+		     !wpa_dbus_dict_string_array_add_element(
+			     &iter_array, "open")) ||
+		    ((capa.auth & WPA_DRIVER_AUTH_SHARED) &&
+		     !wpa_dbus_dict_string_array_add_element(
+			     &iter_array, "shared")) ||
+		    ((capa.auth & WPA_DRIVER_AUTH_LEAP) &&
+		     !wpa_dbus_dict_string_array_add_element(
+			     &iter_array, "leap")) ||
+		    !wpa_dbus_dict_end_string_array(&iter_dict,
 						    &iter_dict_entry,
 						    &iter_dict_val,
 						    &iter_array))
@@ -2675,32 +2580,18 @@
 	if (!wpa_dbus_dict_begin_string_array(&iter_dict, "Modes",
 					      &iter_dict_entry,
 					      &iter_dict_val,
-					      &iter_array))
-		goto nomem;
-
-	if (!wpa_dbus_dict_string_array_add_element(
-			    &iter_array, "infrastructure"))
-		goto nomem;
-
-	if (!wpa_dbus_dict_string_array_add_element(
-			    &iter_array, "ad-hoc"))
-		goto nomem;
-
-	if (res >= 0) {
-		if (capa.flags & (WPA_DRIVER_FLAGS_AP)) {
-			if (!wpa_dbus_dict_string_array_add_element(
-				    &iter_array, "ap"))
-				goto nomem;
-		}
-
-		if (capa.flags & (WPA_DRIVER_FLAGS_P2P_CAPABLE)) {
-			if (!wpa_dbus_dict_string_array_add_element(
-				    &iter_array, "p2p"))
-				goto nomem;
-		}
-	}
-
-	if (!wpa_dbus_dict_end_string_array(&iter_dict,
+					      &iter_array) ||
+	    !wpa_dbus_dict_string_array_add_element(
+		    &iter_array, "infrastructure") ||
+	    !wpa_dbus_dict_string_array_add_element(
+		    &iter_array, "ad-hoc") ||
+	    (res >= 0 && (capa.flags & WPA_DRIVER_FLAGS_AP) &&
+	     !wpa_dbus_dict_string_array_add_element(
+		     &iter_array, "ap")) ||
+	    (res >= 0 && (capa.flags & WPA_DRIVER_FLAGS_P2P_CAPABLE) &&
+	     !wpa_dbus_dict_string_array_add_element(
+		     &iter_array, "p2p")) ||
+	    !wpa_dbus_dict_end_string_array(&iter_dict,
 					    &iter_dict_entry,
 					    &iter_dict_val,
 					    &iter_array))
@@ -2715,9 +2606,8 @@
 			goto nomem;
 	}
 
-	if (!wpa_dbus_dict_close_write(&variant_iter, &iter_dict))
-		goto nomem;
-	if (!dbus_message_iter_close_container(iter, &variant_iter))
+	if (!wpa_dbus_dict_close_write(&variant_iter, &iter_dict) ||
+	    !dbus_message_iter_close_container(iter, &variant_iter))
 		goto nomem;
 
 	return TRUE;
@@ -2778,7 +2668,7 @@
  * Getter for "scanning" property.
  */
 dbus_bool_t wpas_dbus_getter_scanning(DBusMessageIter *iter, DBusError *error,
-                                      void *user_data)
+				      void *user_data)
 {
 	struct wpa_supplicant *wpa_s = user_data;
 	dbus_bool_t scanning = wpa_s->scanning ? TRUE : FALSE;
@@ -2900,6 +2790,7 @@
 {
 	struct wpa_supplicant *wpa_s = user_data;
 	dbus_int32_t reason = wpa_s->disconnect_reason;
+
 	return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_INT32,
 						&reason, error);
 }
@@ -3154,8 +3045,8 @@
 	const char *driver;
 
 	if (wpa_s->driver == NULL || wpa_s->driver->name == NULL) {
-		wpa_printf(MSG_DEBUG, "wpas_dbus_getter_driver[dbus]: "
-			   "wpa_s has no driver set");
+		wpa_printf(MSG_DEBUG, "%s[dbus]: wpa_s has no driver set",
+			   __func__);
 		dbus_set_error(error, DBUS_ERROR_FAILED, "%s: no driver set",
 			       __func__);
 		return FALSE;
@@ -3183,7 +3074,7 @@
 	struct wpa_supplicant *wpa_s = user_data;
 	char path_buf[WPAS_DBUS_OBJECT_PATH_MAX], *bss_obj_path = path_buf;
 
-	if (wpa_s->current_bss)
+	if (wpa_s->current_bss && wpa_s->dbus_new_path)
 		os_snprintf(bss_obj_path, WPAS_DBUS_OBJECT_PATH_MAX,
 			    "%s/" WPAS_DBUS_NEW_BSSIDS_PART "/%u",
 			    wpa_s->dbus_new_path, wpa_s->current_bss->id);
@@ -3211,7 +3102,7 @@
 	struct wpa_supplicant *wpa_s = user_data;
 	char path_buf[WPAS_DBUS_OBJECT_PATH_MAX], *net_obj_path = path_buf;
 
-	if (wpa_s->current_ssid)
+	if (wpa_s->current_ssid && wpa_s->dbus_new_path)
 		os_snprintf(net_obj_path, WPAS_DBUS_OBJECT_PATH_MAX,
 			    "%s/" WPAS_DBUS_NEW_NETWORKS_PART "/%u",
 			    wpa_s->dbus_new_path, wpa_s->current_ssid->id);
@@ -3275,6 +3166,7 @@
 {
 	struct wpa_supplicant *wpa_s = user_data;
 	const char *bridge_ifname = wpa_s->bridge_ifname;
+
 	return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_STRING,
 						&bridge_ifname, error);
 }
@@ -3298,6 +3190,12 @@
 	unsigned int i = 0;
 	dbus_bool_t success = FALSE;
 
+	if (!wpa_s->dbus_new_path) {
+		dbus_set_error(error, DBUS_ERROR_FAILED,
+			       "%s: no D-Bus interface", __func__);
+		return FALSE;
+	}
+
 	paths = os_calloc(wpa_s->num_bss, sizeof(char *));
 	if (!paths) {
 		dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory");
@@ -3349,11 +3247,9 @@
 	unsigned int i = 0, num = 0;
 	dbus_bool_t success = FALSE;
 
-	if (wpa_s->conf == NULL) {
-		wpa_printf(MSG_ERROR, "%s[dbus]: An error occurred getting "
-			   "networks list.", __func__);
-		dbus_set_error(error, DBUS_ERROR_FAILED, "%s: an error "
-			       "occurred getting the networks list", __func__);
+	if (!wpa_s->dbus_new_path) {
+		dbus_set_error(error, DBUS_ERROR_FAILED,
+			       "%s: no D-Bus interface", __func__);
 		return FALSE;
 	}
 
@@ -3373,7 +3269,8 @@
 			continue;
 		paths[i] = os_zalloc(WPAS_DBUS_OBJECT_PATH_MAX);
 		if (paths[i] == NULL) {
-			dbus_set_error(error, DBUS_ERROR_NO_MEMORY, "no memory");
+			dbus_set_error(error, DBUS_ERROR_NO_MEMORY,
+				       "no memory");
 			goto out;
 		}
 
@@ -3411,16 +3308,6 @@
 	struct wpa_supplicant *wpa_s = user_data;
 	const char *pkcs11_engine_path;
 
-	if (wpa_s->conf == NULL) {
-		wpa_printf(MSG_ERROR,
-			   "wpas_dbus_getter_pkcs11_engine_path[dbus]: An "
-			   "error occurred getting the PKCS #11 engine path.");
-		dbus_set_error_const(
-			error, DBUS_ERROR_FAILED,
-			"An error occured getting the PKCS #11 engine path.");
-		return FALSE;
-	}
-
 	if (wpa_s->conf->pkcs11_engine_path == NULL)
 		pkcs11_engine_path = "";
 	else
@@ -3446,16 +3333,6 @@
 	struct wpa_supplicant *wpa_s = user_data;
 	const char *pkcs11_module_path;
 
-	if (wpa_s->conf == NULL) {
-		wpa_printf(MSG_ERROR,
-			   "wpas_dbus_getter_pkcs11_module_path[dbus]: An "
-			   "error occurred getting the PKCS #11 module path.");
-		dbus_set_error_const(
-			error, DBUS_ERROR_FAILED,
-			"An error occured getting the PKCS #11 module path.");
-		return FALSE;
-	}
-
 	if (wpa_s->conf->pkcs11_module_path == NULL)
 		pkcs11_module_path = "";
 	else
@@ -3534,7 +3411,7 @@
 
 	if (!res) {
 		wpa_printf(MSG_ERROR, "%s[dbus]: no bss with id %d found",
-		           func_name, args->id);
+			   func_name, args->id);
 		dbus_set_error(error, DBUS_ERROR_FAILED,
 			       "%s: BSS %d not found",
 			       func_name, args->id);
@@ -3775,7 +3652,7 @@
 	DBusMessageIter iter_dict, variant_iter;
 	const char *group;
 	const char *pairwise[5]; /* max 5 pairwise ciphers is supported */
-	const char *key_mgmt[7]; /* max 7 key managements may be supported */
+	const char *key_mgmt[9]; /* max 9 key managements may be supported */
 	int n;
 
 	if (!dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT,
@@ -3799,6 +3676,14 @@
 		key_mgmt[n++] = "wpa-ft-eap";
 	if (ie_data->key_mgmt & WPA_KEY_MGMT_IEEE8021X_SHA256)
 		key_mgmt[n++] = "wpa-eap-sha256";
+#ifdef CONFIG_SUITEB
+	if (ie_data->key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B)
+		key_mgmt[n++] = "wpa-eap-suite-b";
+#endif /* CONFIG_SUITEB */
+#ifdef CONFIG_SUITEB192
+	if (ie_data->key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B_192)
+		key_mgmt[n++] = "wpa-eap-suite-b-192";
+#endif /* CONFIG_SUITEB192 */
 	if (ie_data->key_mgmt & WPA_KEY_MGMT_NONE)
 		key_mgmt[n++] = "wpa-none";
 
@@ -3872,9 +3757,8 @@
 			goto nomem;
 	}
 
-	if (!wpa_dbus_dict_close_write(&variant_iter, &iter_dict))
-		goto nomem;
-	if (!dbus_message_iter_close_container(iter, &variant_iter))
+	if (!wpa_dbus_dict_close_write(&variant_iter, &iter_dict) ||
+	    !dbus_message_iter_close_container(iter, &variant_iter))
 		goto nomem;
 
 	return TRUE;
@@ -3908,12 +3792,10 @@
 
 	os_memset(&wpa_data, 0, sizeof(wpa_data));
 	ie = wpa_bss_get_vendor_ie(res, WPA_IE_VENDOR_TYPE);
-	if (ie) {
-		if (wpa_parse_wpa_ie(ie, 2 + ie[1], &wpa_data) < 0) {
-			dbus_set_error_const(error, DBUS_ERROR_FAILED,
-					     "failed to parse WPA IE");
-			return FALSE;
-		}
+	if (ie && wpa_parse_wpa_ie(ie, 2 + ie[1], &wpa_data) < 0) {
+		dbus_set_error_const(error, DBUS_ERROR_FAILED,
+				     "failed to parse WPA IE");
+		return FALSE;
 	}
 
 	return wpas_dbus_get_bss_security_prop(iter, &wpa_data, error);
@@ -3943,12 +3825,10 @@
 
 	os_memset(&wpa_data, 0, sizeof(wpa_data));
 	ie = wpa_bss_get_ie(res, WLAN_EID_RSN);
-	if (ie) {
-		if (wpa_parse_wpa_ie(ie, 2 + ie[1], &wpa_data) < 0) {
-			dbus_set_error_const(error, DBUS_ERROR_FAILED,
-					     "failed to parse RSN IE");
-			return FALSE;
-		}
+	if (ie && wpa_parse_wpa_ie(ie, 2 + ie[1], &wpa_data) < 0) {
+		dbus_set_error_const(error, DBUS_ERROR_FAILED,
+				     "failed to parse RSN IE");
+		return FALSE;
 	}
 
 	return wpas_dbus_get_bss_security_prop(iter, &wpa_data, error);
@@ -3973,6 +3853,7 @@
 	struct wpabuf *wps_ie;
 #endif /* CONFIG_WPS */
 	DBusMessageIter iter_dict, variant_iter;
+	int wps_support = 0;
 	const char *type = "";
 
 	res = get_bss_helper(args, error, __func__);
@@ -3980,28 +3861,26 @@
 		return FALSE;
 
 	if (!dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT,
-					      "a{sv}", &variant_iter))
-		goto nomem;
-
-	if (!wpa_dbus_dict_open_write(&variant_iter, &iter_dict))
+					      "a{sv}", &variant_iter) ||
+	    !wpa_dbus_dict_open_write(&variant_iter, &iter_dict))
 		goto nomem;
 
 #ifdef CONFIG_WPS
 	wps_ie = wpa_bss_get_vendor_ie_multi(res, WPS_IE_VENDOR_TYPE);
 	if (wps_ie) {
+		wps_support = 1;
 		if (wps_is_selected_pbc_registrar(wps_ie))
 			type = "pbc";
 		else if (wps_is_selected_pin_registrar(wps_ie))
 			type = "pin";
+
+		wpabuf_free(wps_ie);
 	}
 #endif /* CONFIG_WPS */
 
-	if (!wpa_dbus_dict_append_string(&iter_dict, "Type", type))
-		goto nomem;
-
-	if (!wpa_dbus_dict_close_write(&variant_iter, &iter_dict))
-		goto nomem;
-	if (!dbus_message_iter_close_container(iter, &variant_iter))
+	if ((wps_support && !wpa_dbus_dict_append_string(&iter_dict, "Type", type)) ||
+	    !wpa_dbus_dict_close_write(&variant_iter, &iter_dict) ||
+	    !dbus_message_iter_close_container(iter, &variant_iter))
 		goto nomem;
 
 	return TRUE;
@@ -4223,8 +4102,7 @@
 
 	name = os_strdup(dbus_message_get_sender(message));
 	if (!name)
-		return dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY,
-					      "out of memory");
+		return wpas_dbus_error_no_memory(message);
 
 	wpa_s->preq_notify_peer = name;
 
@@ -4288,7 +4166,7 @@
 	struct wpas_dbus_priv *priv = wpa_s->global->dbus;
 
 	/* Do nothing if the control interface is not turned on */
-	if (priv == NULL)
+	if (priv == NULL || !wpa_s->dbus_new_path)
 		return;
 
 	if (wpa_s->preq_notify_peer == NULL)
@@ -4304,28 +4182,22 @@
 
 	dbus_message_iter_init_append(msg, &iter);
 
-	if (!wpa_dbus_dict_open_write(&iter, &dict_iter))
-		goto fail;
-	if (addr && !wpa_dbus_dict_append_byte_array(&dict_iter, "addr",
-						     (const char *) addr,
-						     ETH_ALEN))
-		goto fail;
-	if (dst && !wpa_dbus_dict_append_byte_array(&dict_iter, "dst",
-						    (const char *) dst,
-						    ETH_ALEN))
-		goto fail;
-	if (bssid && !wpa_dbus_dict_append_byte_array(&dict_iter, "bssid",
-						      (const char *) bssid,
-						      ETH_ALEN))
-		goto fail;
-	if (ie && ie_len && !wpa_dbus_dict_append_byte_array(&dict_iter, "ies",
-							     (const char *) ie,
-							     ie_len))
-		goto fail;
-	if (ssi_signal && !wpa_dbus_dict_append_int32(&dict_iter, "signal",
-						      ssi_signal))
-		goto fail;
-	if (!wpa_dbus_dict_close_write(&iter, &dict_iter))
+	if (!wpa_dbus_dict_open_write(&iter, &dict_iter) ||
+	    (addr && !wpa_dbus_dict_append_byte_array(&dict_iter, "addr",
+						      (const char *) addr,
+						      ETH_ALEN)) ||
+	    (dst && !wpa_dbus_dict_append_byte_array(&dict_iter, "dst",
+						     (const char *) dst,
+						     ETH_ALEN)) ||
+	    (bssid && !wpa_dbus_dict_append_byte_array(&dict_iter, "bssid",
+						       (const char *) bssid,
+						       ETH_ALEN)) ||
+	    (ie && ie_len && !wpa_dbus_dict_append_byte_array(&dict_iter, "ies",
+							      (const char *) ie,
+							      ie_len)) ||
+	    (ssi_signal && !wpa_dbus_dict_append_int32(&dict_iter, "signal",
+						       ssi_signal)) ||
+	    !wpa_dbus_dict_close_write(&iter, &dict_iter))
 		goto fail;
 
 	dbus_connection_send(priv->con, msg, NULL);
diff --git a/wpa_supplicant/dbus/dbus_new_handlers.h b/wpa_supplicant/dbus/dbus_new_handlers.h
index f6a83cd..50f72ec 100644
--- a/wpa_supplicant/dbus/dbus_new_handlers.h
+++ b/wpa_supplicant/dbus/dbus_new_handlers.h
@@ -55,8 +55,8 @@
 					 void *user_data);
 
 dbus_bool_t wpas_dbus_getter_debug_timestamp(DBusMessageIter *iter,
-                                             DBusError *error,
-                                             void *user_data);
+					     DBusError *error,
+					     void *user_data);
 
 dbus_bool_t wpas_dbus_getter_debug_show_keys(DBusMessageIter *iter,
 					     DBusError *error,
@@ -107,6 +107,9 @@
 DBusMessage * wpas_dbus_handler_reattach(DBusMessage *message,
 					 struct wpa_supplicant *wpa_s);
 
+DBusMessage * wpas_dbus_handler_reconnect(DBusMessage *message,
+					  struct wpa_supplicant *wpa_s);
+
 DBusMessage * wpas_dbus_handler_remove_network(DBusMessage *message,
 					       struct wpa_supplicant *wpa_s);
 
@@ -291,6 +294,9 @@
 DBusMessage * wpas_dbus_handler_wps_start(DBusMessage *message,
 					  struct wpa_supplicant *wpa_s);
 
+DBusMessage * wpas_dbus_handler_wps_cancel(DBusMessage *message,
+					   struct wpa_supplicant *wpa_s);
+
 dbus_bool_t wpas_dbus_getter_process_credentials(DBusMessageIter *iter,
 	DBusError *error, void *user_data);
 
@@ -319,6 +325,7 @@
 					   const char *arg);
 DBusMessage * wpas_dbus_error_unknown_error(DBusMessage *message,
 					    const char *arg);
+DBusMessage * wpas_dbus_error_no_memory(DBusMessage *message);
 
 DBusMessage * wpas_dbus_handler_subscribe_preq(
 	DBusMessage *message, struct wpa_supplicant *wpa_s);
diff --git a/wpa_supplicant/dbus/dbus_new_handlers_p2p.c b/wpa_supplicant/dbus/dbus_new_handlers_p2p.c
index 7867f0c..e9d60df 100644
--- a/wpa_supplicant/dbus/dbus_new_handlers_p2p.c
+++ b/wpa_supplicant/dbus/dbus_new_handlers_p2p.c
@@ -35,9 +35,9 @@
  * @addr - out param must be of ETH_ALEN size
  * Returns 0 if valid (including MAC), -1 otherwise
  */
-static int parse_peer_object_path(char *peer_path, u8 addr[ETH_ALEN])
+static int parse_peer_object_path(const char *peer_path, u8 addr[ETH_ALEN])
 {
-	char *p;
+	const char *p;
 
 	if (!peer_path)
 		return -1;
@@ -57,12 +57,12 @@
  *
  * Convenience function to create and return an invalid persistent group error.
  */
-static DBusMessage * wpas_dbus_error_persistent_group_unknown(
-	DBusMessage *message)
+static DBusMessage *
+wpas_dbus_error_persistent_group_unknown(DBusMessage *message)
 {
-	return dbus_message_new_error(message, WPAS_DBUS_ERROR_NETWORK_UNKNOWN,
-				      "There is no such persistent group in "
-				      "this P2P device.");
+	return dbus_message_new_error(
+		message, WPAS_DBUS_ERROR_NETWORK_UNKNOWN,
+		"There is no such persistent group in this P2P device.");
 }
 
 
@@ -74,7 +74,7 @@
 	DBusMessageIter iter;
 	DBusMessageIter iter_dict;
 	unsigned int timeout = 0;
-	enum p2p_discovery_type type = P2P_FIND_ONLY_SOCIAL;
+	enum p2p_discovery_type type = P2P_FIND_START_WITH_FULL;
 	int num_req_dev_types = 0;
 	unsigned int i;
 	u8 *req_dev_types = NULL;
@@ -89,12 +89,12 @@
 		if (!wpa_dbus_dict_get_entry(&iter_dict, &entry))
 			goto error;
 
-		if (!os_strcmp(entry.key, "Timeout") &&
-		    (entry.type == DBUS_TYPE_INT32)) {
+		if (os_strcmp(entry.key, "Timeout") == 0 &&
+		    entry.type == DBUS_TYPE_INT32) {
 			timeout = entry.uint32_value;
 		} else if (os_strcmp(entry.key, "RequestedDeviceTypes") == 0) {
-			if ((entry.type != DBUS_TYPE_ARRAY) ||
-			    (entry.array_type != WPAS_DBUS_TYPE_BINARRAY))
+			if (entry.type != DBUS_TYPE_ARRAY ||
+			    entry.array_type != WPAS_DBUS_TYPE_BINARRAY)
 				goto error_clear;
 
 			os_free(req_dev_types);
@@ -105,20 +105,20 @@
 
 			for (i = 0; i < entry.array_len; i++) {
 				if (wpabuf_len(entry.binarray_value[i]) !=
-							WPS_DEV_TYPE_LEN)
+				    WPS_DEV_TYPE_LEN)
 					goto error_clear;
 				os_memcpy(req_dev_types + i * WPS_DEV_TYPE_LEN,
 					  wpabuf_head(entry.binarray_value[i]),
 					  WPS_DEV_TYPE_LEN);
 			}
 			num_req_dev_types = entry.array_len;
-		} else if (!os_strcmp(entry.key, "DiscoveryType") &&
-			   (entry.type == DBUS_TYPE_STRING)) {
-			if (!os_strcmp(entry.str_value, "start_with_full"))
+		} else if (os_strcmp(entry.key, "DiscoveryType") == 0 &&
+			   entry.type == DBUS_TYPE_STRING) {
+			if (os_strcmp(entry.str_value, "start_with_full") == 0)
 				type = P2P_FIND_START_WITH_FULL;
-			else if (!os_strcmp(entry.str_value, "social"))
+			else if (os_strcmp(entry.str_value, "social") == 0)
 				type = P2P_FIND_ONLY_SOCIAL;
-			else if (!os_strcmp(entry.str_value, "progressive"))
+			else if (os_strcmp(entry.str_value, "progressive") == 0)
 				type = P2P_FIND_PROGRESSIVE;
 			else
 				goto error_clear;
@@ -127,8 +127,10 @@
 		wpa_dbus_dict_entry_clear(&entry);
 	}
 
+	wpa_s = wpa_s->global->p2p_init_wpa_s;
+
 	wpas_p2p_find(wpa_s, timeout, type, num_req_dev_types, req_dev_types,
-		      NULL, 0);
+		      NULL, 0, 0, NULL, 0);
 	os_free(req_dev_types);
 	return reply;
 
@@ -144,7 +146,7 @@
 DBusMessage * wpas_dbus_handler_p2p_stop_find(DBusMessage *message,
 					      struct wpa_supplicant *wpa_s)
 {
-	wpas_p2p_stop_find(wpa_s);
+	wpas_p2p_stop_find(wpa_s->global->p2p_init_wpa_s);
 	return NULL;
 }
 
@@ -162,6 +164,8 @@
 	if (parse_peer_object_path(peer_object_path, peer_addr) < 0)
 		return wpas_dbus_error_invalid_args(message, NULL);
 
+	wpa_s = wpa_s->global->p2p_init_wpa_s;
+
 	if (wpas_p2p_reject(wpa_s, peer_addr) < 0)
 		return wpas_dbus_error_unknown_error(message,
 				"Failed to call wpas_p2p_reject method.");
@@ -177,12 +181,15 @@
 
 	if (!dbus_message_get_args(message, NULL, DBUS_TYPE_INT32, &timeout,
 				   DBUS_TYPE_INVALID))
-		return dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY,
-					      NULL);
+		return wpas_dbus_error_no_memory(message);
 
-	if (wpas_p2p_listen(wpa_s, (unsigned int)timeout))
-		return dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY,
-					      NULL);
+	wpa_s = wpa_s->global->p2p_init_wpa_s;
+
+	if (wpas_p2p_listen(wpa_s, (unsigned int) timeout)) {
+		return dbus_message_new_error(message,
+					      WPAS_DBUS_ERROR_UNKNOWN_ERROR,
+					      "Could not start P2P listen");
+	}
 
 	return NULL;
 }
@@ -206,17 +213,19 @@
 		if (!wpa_dbus_dict_get_entry(&iter_dict, &entry))
 			goto error;
 
-		if (!os_strcmp(entry.key, "period") &&
-		    (entry.type == DBUS_TYPE_INT32))
+		if (os_strcmp(entry.key, "period") == 0 &&
+		    entry.type == DBUS_TYPE_INT32)
 			period = entry.uint32_value;
-		else if (!os_strcmp(entry.key, "interval") &&
-			 (entry.type == DBUS_TYPE_INT32))
+		else if (os_strcmp(entry.key, "interval") == 0 &&
+			 entry.type == DBUS_TYPE_INT32)
 			interval = entry.uint32_value;
 		else
 			goto error_clear;
 		wpa_dbus_dict_entry_clear(&entry);
 	}
 
+	wpa_s = wpa_s->global->p2p_init_wpa_s;
+
 	if (wpas_p2p_ext_listen(wpa_s, period, interval))
 		return wpas_dbus_error_unknown_error(
 			message, "failed to initiate a p2p_ext_listen.");
@@ -248,16 +257,16 @@
 		if (!wpa_dbus_dict_get_entry(&iter_dict, &entry))
 			goto error;
 
-		if (!os_strcmp(entry.key, "duration1") &&
-		    (entry.type == DBUS_TYPE_INT32))
+		if (os_strcmp(entry.key, "duration1") == 0 &&
+		    entry.type == DBUS_TYPE_INT32)
 			dur1 = entry.uint32_value;
-		else if (!os_strcmp(entry.key, "interval1") &&
+		else if (os_strcmp(entry.key, "interval1") == 0 &&
 			 entry.type == DBUS_TYPE_INT32)
 			int1 = entry.uint32_value;
-		else if (!os_strcmp(entry.key, "duration2") &&
+		else if (os_strcmp(entry.key, "duration2") == 0 &&
 			 entry.type == DBUS_TYPE_INT32)
 			dur2 = entry.uint32_value;
-		else if (!os_strcmp(entry.key, "interval2") &&
+		else if (os_strcmp(entry.key, "interval2") == 0 &&
 			 entry.type == DBUS_TYPE_INT32)
 			int2 = entry.uint32_value;
 		else
@@ -265,6 +274,7 @@
 
 		wpa_dbus_dict_entry_clear(&entry);
 	}
+
 	if (wpas_p2p_presence_req(wpa_s, dur1, int1, dur2, int2) < 0)
 		return wpas_dbus_error_unknown_error(message,
 				"Failed to invoke presence request.");
@@ -289,7 +299,6 @@
 	int persistent_group = 0;
 	int freq = 0;
 	char *iface = NULL;
-	char *net_id_str = NULL;
 	unsigned int group_id = 0;
 	struct wpa_ssid *ssid;
 
@@ -302,15 +311,16 @@
 		if (!wpa_dbus_dict_get_entry(&iter_dict, &entry))
 			goto inv_args;
 
-		if (!os_strcmp(entry.key, "persistent") &&
-		    (entry.type == DBUS_TYPE_BOOLEAN)) {
-			persistent_group = (entry.bool_value == TRUE) ? 1 : 0;
-		} else if (!os_strcmp(entry.key, "frequency") &&
-			   (entry.type == DBUS_TYPE_INT32)) {
+		if (os_strcmp(entry.key, "persistent") == 0 &&
+		    entry.type == DBUS_TYPE_BOOLEAN) {
+			persistent_group = entry.bool_value;
+		} else if (os_strcmp(entry.key, "frequency") == 0 &&
+			   entry.type == DBUS_TYPE_INT32) {
 			freq = entry.int32_value;
 			if (freq <= 0)
 				goto inv_args_clear;
-		} else if (!os_strcmp(entry.key, "persistent_group_object") &&
+		} else if (os_strcmp(entry.key, "persistent_group_object") ==
+			   0 &&
 			   entry.type == DBUS_TYPE_OBJECT_PATH)
 			pg_object_path = os_strdup(entry.str_value);
 		else
@@ -319,16 +329,22 @@
 		wpa_dbus_dict_entry_clear(&entry);
 	}
 
+	wpa_s = wpa_s->global->p2p_init_wpa_s;
+
 	if (pg_object_path != NULL) {
+		char *net_id_str;
+
 		/*
 		 * A persistent group Object Path is defined meaning we want
 		 * to re-invoke a persistent group.
 		 */
 
-		iface = wpas_dbus_new_decompose_object_path(pg_object_path, 1,
-							    &net_id_str, NULL);
-		if (iface == NULL ||
-		    os_strcmp(iface, wpa_s->dbus_new_path) != 0) {
+		iface = wpas_dbus_new_decompose_object_path(
+			pg_object_path, WPAS_DBUS_NEW_PERSISTENT_GROUPS_PART,
+			&net_id_str);
+		if (iface == NULL || net_id_str == NULL ||
+		    !wpa_s->parent->dbus_new_path ||
+		    os_strcmp(iface, wpa_s->parent->dbus_new_path) != 0) {
 			reply =
 			    wpas_dbus_error_invalid_args(message,
 							 pg_object_path);
@@ -359,7 +375,6 @@
 
 out:
 	os_free(pg_object_path);
-	os_free(net_id_str);
 	os_free(iface);
 	return reply;
 inv_args_clear:
@@ -394,14 +409,71 @@
 				"P2P is not available for this interface");
 		}
 		dbus_set_error_const(error, DBUS_ERROR_FAILED,
-				     "P2P is not available for this "
-				     "interface");
+				     "P2P is not available for this interface");
 		return FALSE;
 	}
 	return TRUE;
 }
 
 
+DBusMessage * wpas_dbus_handler_p2p_remove_client(DBusMessage *message,
+						  struct wpa_supplicant *wpa_s)
+{
+	DBusMessageIter iter_dict;
+	DBusMessage *reply = NULL;
+	DBusMessageIter iter;
+	struct wpa_dbus_dict_entry entry;
+	char *peer_object_path = NULL;
+	char *interface_addr = NULL;
+	u8 peer_addr[ETH_ALEN];
+
+	if (!wpa_dbus_p2p_check_enabled(wpa_s, message, &reply, NULL))
+		return reply;
+
+	dbus_message_iter_init(message, &iter);
+
+	if (!wpa_dbus_dict_open_read(&iter, &iter_dict, NULL))
+		goto err;
+
+	while (wpa_dbus_dict_has_dict_entry(&iter_dict)) {
+		if (!wpa_dbus_dict_get_entry(&iter_dict, &entry))
+			goto err;
+
+		if (os_strcmp(entry.key, "peer") == 0 &&
+		    entry.type == DBUS_TYPE_OBJECT_PATH) {
+			os_free(peer_object_path);
+			peer_object_path = os_strdup(entry.str_value);
+			wpa_dbus_dict_entry_clear(&entry);
+		} else if (os_strcmp(entry.key, "iface") == 0 &&
+			   entry.type == DBUS_TYPE_STRING) {
+			os_free(interface_addr);
+			interface_addr = os_strdup(entry.str_value);
+			wpa_dbus_dict_entry_clear(&entry);
+		} else {
+			wpa_dbus_dict_entry_clear(&entry);
+			goto err;
+		}
+	}
+
+	if ((!peer_object_path && !interface_addr) ||
+	    (peer_object_path &&
+	     (parse_peer_object_path(peer_object_path, peer_addr) < 0 ||
+	      !p2p_peer_known(wpa_s->global->p2p, peer_addr))) ||
+	    (interface_addr && hwaddr_aton(interface_addr, peer_addr) < 0))
+		goto err;
+
+	wpas_p2p_remove_client(wpa_s, peer_addr, interface_addr != NULL);
+	reply = NULL;
+out:
+	os_free(peer_object_path);
+	os_free(interface_addr);
+	return reply;
+err:
+	reply = wpas_dbus_error_invalid_args(message, "Invalid address format");
+	goto out;
+}
+
+
 DBusMessage * wpas_dbus_handler_p2p_flush(DBusMessage *message,
 					  struct wpa_supplicant *wpa_s)
 {
@@ -410,6 +482,8 @@
 	if (!wpa_dbus_p2p_check_enabled(wpa_s, message, &reply, NULL))
 		return reply;
 
+	wpa_s = wpa_s->global->p2p_init_wpa_s;
+
 	os_memset(wpa_s->p2p_auth_invite, 0, ETH_ALEN);
 	wpa_s->force_long_sd = 0;
 	p2p_flush(wpa_s->global->p2p);
@@ -450,42 +524,42 @@
 		if (!wpa_dbus_dict_get_entry(&iter_dict, &entry))
 			goto inv_args;
 
-		if (!os_strcmp(entry.key, "peer") &&
-		    (entry.type == DBUS_TYPE_OBJECT_PATH)) {
+		if (os_strcmp(entry.key, "peer") == 0 &&
+		    entry.type == DBUS_TYPE_OBJECT_PATH) {
 			peer_object_path = os_strdup(entry.str_value);
-		} else if (!os_strcmp(entry.key, "persistent") &&
-			   (entry.type == DBUS_TYPE_BOOLEAN)) {
-			persistent_group = (entry.bool_value == TRUE) ? 1 : 0;
-		} else if (!os_strcmp(entry.key, "join") &&
-			   (entry.type == DBUS_TYPE_BOOLEAN)) {
-			join = (entry.bool_value == TRUE) ? 1 : 0;
-		} else if (!os_strcmp(entry.key, "authorize_only") &&
-			   (entry.type == DBUS_TYPE_BOOLEAN)) {
-			authorize_only = (entry.bool_value == TRUE) ? 1 : 0;
-		} else if (!os_strcmp(entry.key, "frequency") &&
-			   (entry.type == DBUS_TYPE_INT32)) {
+		} else if (os_strcmp(entry.key, "persistent") == 0 &&
+			   entry.type == DBUS_TYPE_BOOLEAN) {
+			persistent_group = entry.bool_value;
+		} else if (os_strcmp(entry.key, "join") == 0 &&
+			   entry.type == DBUS_TYPE_BOOLEAN) {
+			join = entry.bool_value;
+		} else if (os_strcmp(entry.key, "authorize_only") == 0 &&
+			   entry.type == DBUS_TYPE_BOOLEAN) {
+			authorize_only = entry.bool_value;
+		} else if (os_strcmp(entry.key, "frequency") == 0 &&
+			   entry.type == DBUS_TYPE_INT32) {
 			freq = entry.int32_value;
 			if (freq <= 0)
 				goto inv_args_clear;
-		} else if (!os_strcmp(entry.key, "go_intent") &&
-			   (entry.type == DBUS_TYPE_INT32)) {
+		} else if (os_strcmp(entry.key, "go_intent") == 0 &&
+			   entry.type == DBUS_TYPE_INT32) {
 			go_intent = entry.int32_value;
 			if ((go_intent < 0) || (go_intent > 15))
 				goto inv_args_clear;
-		} else if (!os_strcmp(entry.key, "wps_method") &&
-			   (entry.type == DBUS_TYPE_STRING)) {
-			if (!os_strcmp(entry.str_value, "pbc"))
+		} else if (os_strcmp(entry.key, "wps_method") == 0 &&
+			   entry.type == DBUS_TYPE_STRING) {
+			if (os_strcmp(entry.str_value, "pbc") == 0)
 				wps_method = WPS_PBC;
-			else if (!os_strcmp(entry.str_value, "pin"))
+			else if (os_strcmp(entry.str_value, "pin") == 0)
 				wps_method = WPS_PIN_DISPLAY;
-			else if (!os_strcmp(entry.str_value, "display"))
+			else if (os_strcmp(entry.str_value, "display") == 0)
 				wps_method = WPS_PIN_DISPLAY;
-			else if (!os_strcmp(entry.str_value, "keypad"))
+			else if (os_strcmp(entry.str_value, "keypad") == 0)
 				wps_method = WPS_PIN_KEYPAD;
 			else
 				goto inv_args_clear;
-		} else if (!os_strcmp(entry.key, "pin") &&
-			   (entry.type == DBUS_TYPE_STRING)) {
+		} else if (os_strcmp(entry.key, "pin") == 0 &&
+			   entry.type == DBUS_TYPE_STRING) {
 			pin = os_strdup(entry.str_value);
 		} else
 			goto inv_args_clear;
@@ -493,17 +567,19 @@
 		wpa_dbus_dict_entry_clear(&entry);
 	}
 
-	if (!peer_object_path || (wps_method == WPS_NOT_READY) ||
-	    (parse_peer_object_path(peer_object_path, addr) < 0) ||
+	if (wps_method == WPS_NOT_READY ||
+	    parse_peer_object_path(peer_object_path, addr) < 0 ||
 	    !p2p_peer_known(wpa_s->global->p2p, addr))
 		goto inv_args;
 
 	/*
 	 * Validate the wps_method specified and the pin value.
 	 */
-	if ((!pin || !pin[0]) && (wps_method == WPS_PIN_KEYPAD))
+	if ((!pin || !pin[0]) && wps_method == WPS_PIN_KEYPAD)
 		goto inv_args;
 
+	wpa_s = wpa_s->global->p2p_init_wpa_s;
+
 	new_pin = wpas_p2p_connect(wpa_s, addr, pin, wps_method,
 				   persistent_group, 0, join, authorize_only,
 				   go_intent, freq, -1, 0, 0, 0);
@@ -511,6 +587,7 @@
 	if (new_pin >= 0) {
 		char npin[9];
 		char *generated_pin;
+
 		os_snprintf(npin, sizeof(npin), "%08d", new_pin);
 		generated_pin = npin;
 		reply = dbus_message_new_method_return(message);
@@ -519,8 +596,8 @@
 	} else {
 		switch (new_pin) {
 		case -2:
-			err_msg = "connect failed due to channel "
-				"unavailability.";
+			err_msg =
+				"connect failed due to channel unavailability.";
 			iface = WPAS_DBUS_ERROR_CONNECT_CHANNEL_UNAVAILABLE;
 			break;
 
@@ -556,6 +633,26 @@
 }
 
 
+/**
+ * wpas_dbus_handler_p2p_cancel - Cancel P2P group formation
+ * @message: Pointer to incoming dbus message
+ * @wpa_s: %wpa_supplicant data structure
+ * Returns: NULL on success or DBus error on failure
+ *
+ * Handler for "Cancel" method call. Returns NULL if P2P cancel succeeds or DBus
+ * error on P2P cancel failure
+ */
+DBusMessage * wpas_dbus_handler_p2p_cancel(DBusMessage *message,
+					   struct wpa_supplicant *wpa_s)
+{
+	if (wpas_p2p_cancel(wpa_s))
+		return wpas_dbus_error_unknown_error(message,
+						     "P2P cancel failed");
+
+	return NULL;
+}
+
+
 DBusMessage * wpas_dbus_handler_p2p_invite(DBusMessage *message,
 					   struct wpa_supplicant *wpa_s)
 {
@@ -566,7 +663,6 @@
 	char *peer_object_path = NULL;
 	char *pg_object_path = NULL;
 	char *iface = NULL;
-	char *net_id_str = NULL;
 	u8 peer_addr[ETH_ALEN];
 	unsigned int group_id = 0;
 	int persistent = 0;
@@ -584,12 +680,13 @@
 		if (!wpa_dbus_dict_get_entry(&iter_dict, &entry))
 			goto err;
 
-		if (!os_strcmp(entry.key, "peer") &&
-		    (entry.type == DBUS_TYPE_OBJECT_PATH)) {
+		if (os_strcmp(entry.key, "peer") == 0 &&
+		    entry.type == DBUS_TYPE_OBJECT_PATH) {
 			peer_object_path = os_strdup(entry.str_value);
 			wpa_dbus_dict_entry_clear(&entry);
-		} else if (!os_strcmp(entry.key, "persistent_group_object") &&
-			   (entry.type == DBUS_TYPE_OBJECT_PATH)) {
+		} else if (os_strcmp(entry.key, "persistent_group_object") ==
+			   0 &&
+			   entry.type == DBUS_TYPE_OBJECT_PATH) {
 			pg_object_path = os_strdup(entry.str_value);
 			persistent = 1;
 			wpa_dbus_dict_entry_clear(&entry);
@@ -599,22 +696,26 @@
 		}
 	}
 
-	if (!peer_object_path ||
-	    (parse_peer_object_path(peer_object_path, peer_addr) < 0) ||
-	    !p2p_peer_known(wpa_s->global->p2p, peer_addr)) {
+	if (parse_peer_object_path(peer_object_path, peer_addr) < 0 ||
+	    !p2p_peer_known(wpa_s->global->p2p, peer_addr))
 		goto err;
-	}
+
+	wpa_s = wpa_s->global->p2p_init_wpa_s;
 
 	if (persistent) {
+		char *net_id_str;
 		/*
 		 * A group ID is defined meaning we want to re-invoke a
 		 * persistent group
 		 */
 
-		iface = wpas_dbus_new_decompose_object_path(pg_object_path, 1,
-							    &net_id_str, NULL);
-		if (iface == NULL ||
-		    os_strcmp(iface, wpa_s->dbus_new_path) != 0) {
+		iface = wpas_dbus_new_decompose_object_path(
+			pg_object_path,
+			WPAS_DBUS_NEW_PERSISTENT_GROUPS_PART,
+			&net_id_str);
+		if (iface == NULL || net_id_str == NULL ||
+		    !wpa_s->parent->dbus_new_path ||
+		    os_strcmp(iface, wpa_s->parent->dbus_new_path) != 0) {
 			reply = wpas_dbus_error_invalid_args(message,
 							     pg_object_path);
 			goto out;
@@ -652,6 +753,7 @@
 	}
 
 out:
+	os_free(iface);
 	os_free(pg_object_path);
 	os_free(peer_object_path);
 	return reply;
@@ -690,8 +792,10 @@
 	    os_strcmp(config_method, "pushbutton"))
 		return wpas_dbus_error_invalid_args(message, NULL);
 
+	wpa_s = wpa_s->global->p2p_init_wpa_s;
+
 	if (wpas_p2p_prov_disc(wpa_s, peer_addr, config_method,
-			       WPAS_P2P_PD_FOR_GO_NEG) < 0)
+			       WPAS_P2P_PD_FOR_GO_NEG, NULL) < 0)
 		return wpas_dbus_error_unknown_error(message,
 				"Failed to send provision discovery request");
 
@@ -719,6 +823,8 @@
 	if (!wpa_dbus_p2p_check_enabled(wpa_s, NULL, NULL, error))
 		return FALSE;
 
+	wpa_s = wpa_s->global->p2p_init_wpa_s;
+
 	if (!dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT,
 					      "a{sv}", &variant_iter) ||
 	    !wpa_dbus_dict_open_write(&variant_iter, &dict_iter))
@@ -732,8 +838,8 @@
 
 	/* Primary device type */
 	if (!wpa_dbus_dict_append_byte_array(&dict_iter, "PrimaryDeviceType",
-	    				     (char *)wpa_s->conf->device_type,
-	    				     WPS_DEV_TYPE_LEN))
+					     (char *) wpa_s->conf->device_type,
+					     WPS_DEV_TYPE_LEN))
 		goto err_no_mem;
 
 	/* Secondary device types */
@@ -768,75 +874,37 @@
 			wpa_s->conf->wps_vendor_ext[i];
 	}
 
-	if (num_vendor_extensions &&
-	    !wpa_dbus_dict_append_wpabuf_array(&dict_iter,
-					       "VendorExtension",
-					       vendor_ext,
-					       num_vendor_extensions))
-		goto err_no_mem;
-
-	/* GO Intent */
-	if (!wpa_dbus_dict_append_uint32(&dict_iter, "GOIntent",
-					 wpa_s->conf->p2p_go_intent))
-		goto err_no_mem;
-
-	/* Persistent Reconnect */
-	if (!wpa_dbus_dict_append_bool(&dict_iter, "PersistentReconnect",
-				       wpa_s->conf->persistent_reconnect))
-		goto err_no_mem;
-
-	/* Listen Reg Class */
-	if (!wpa_dbus_dict_append_uint32(&dict_iter, "ListenRegClass",
-					 wpa_s->conf->p2p_listen_reg_class))
-		goto err_no_mem;
-
-	/* Listen Channel */
-	if (!wpa_dbus_dict_append_uint32(&dict_iter, "ListenChannel",
-					 wpa_s->conf->p2p_listen_channel))
-		goto err_no_mem;
-
-	/* Oper Reg Class */
-	if (!wpa_dbus_dict_append_uint32(&dict_iter, "OperRegClass",
-					 wpa_s->conf->p2p_oper_reg_class))
-		goto err_no_mem;
-
-	/* Oper Channel */
-	if (!wpa_dbus_dict_append_uint32(&dict_iter, "OperChannel",
-					 wpa_s->conf->p2p_oper_channel))
-		goto err_no_mem;
-
-	/* SSID Postfix */
-	if (wpa_s->conf->p2p_ssid_postfix &&
-	    !wpa_dbus_dict_append_string(&dict_iter, "SsidPostfix",
-					 wpa_s->conf->p2p_ssid_postfix))
-		goto err_no_mem;
-
-	/* Intra Bss */
-	if (!wpa_dbus_dict_append_bool(&dict_iter, "IntraBss",
-				       wpa_s->conf->p2p_intra_bss))
-		goto err_no_mem;
-
-	/* Group Idle */
-	if (!wpa_dbus_dict_append_uint32(&dict_iter, "GroupIdle",
-					 wpa_s->conf->p2p_group_idle))
-		goto err_no_mem;
-
-	/* Dissasociation low ack */
-	if (!wpa_dbus_dict_append_uint32(&dict_iter, "disassoc_low_ack",
-					 wpa_s->conf->disassoc_low_ack))
-		goto err_no_mem;
-
-	/* No Group Iface */
-	if (!wpa_dbus_dict_append_bool(&dict_iter, "NoGroupIface",
-				       wpa_s->conf->p2p_no_group_iface))
-		goto err_no_mem;
-
-	/* P2P Search Delay */
-	if (!wpa_dbus_dict_append_uint32(&dict_iter, "p2p_search_delay",
-					 wpa_s->conf->p2p_search_delay))
-		goto err_no_mem;
-
-	if (!wpa_dbus_dict_close_write(&variant_iter, &dict_iter) ||
+	if ((num_vendor_extensions &&
+	     !wpa_dbus_dict_append_wpabuf_array(&dict_iter,
+						"VendorExtension",
+						vendor_ext,
+						num_vendor_extensions)) ||
+	    !wpa_dbus_dict_append_uint32(&dict_iter, "GOIntent",
+					 wpa_s->conf->p2p_go_intent) ||
+	    !wpa_dbus_dict_append_bool(&dict_iter, "PersistentReconnect",
+				       wpa_s->conf->persistent_reconnect) ||
+	    !wpa_dbus_dict_append_uint32(&dict_iter, "ListenRegClass",
+					 wpa_s->conf->p2p_listen_reg_class) ||
+	    !wpa_dbus_dict_append_uint32(&dict_iter, "ListenChannel",
+					 wpa_s->conf->p2p_listen_channel) ||
+	    !wpa_dbus_dict_append_uint32(&dict_iter, "OperRegClass",
+					 wpa_s->conf->p2p_oper_reg_class) ||
+	    !wpa_dbus_dict_append_uint32(&dict_iter, "OperChannel",
+					 wpa_s->conf->p2p_oper_channel) ||
+	    (wpa_s->conf->p2p_ssid_postfix &&
+	     !wpa_dbus_dict_append_string(&dict_iter, "SsidPostfix",
+					  wpa_s->conf->p2p_ssid_postfix)) ||
+	    !wpa_dbus_dict_append_bool(&dict_iter, "IntraBss",
+				       wpa_s->conf->p2p_intra_bss) ||
+	    !wpa_dbus_dict_append_uint32(&dict_iter, "GroupIdle",
+					 wpa_s->conf->p2p_group_idle) ||
+	    !wpa_dbus_dict_append_uint32(&dict_iter, "disassoc_low_ack",
+					 wpa_s->conf->disassoc_low_ack) ||
+	    !wpa_dbus_dict_append_bool(&dict_iter, "NoGroupIface",
+				       wpa_s->conf->p2p_no_group_iface) ||
+	    !wpa_dbus_dict_append_uint32(&dict_iter, "p2p_search_delay",
+					 wpa_s->conf->p2p_search_delay) ||
+	    !wpa_dbus_dict_close_write(&variant_iter, &dict_iter) ||
 	    !dbus_message_iter_close_container(iter, &variant_iter))
 		goto err_no_mem;
 
@@ -860,6 +928,8 @@
 	if (!wpa_dbus_p2p_check_enabled(wpa_s, NULL, NULL, error))
 		return FALSE;
 
+	wpa_s = wpa_s->global->p2p_init_wpa_s;
+
 	dbus_message_iter_recurse(iter, &variant_iter);
 	if (!wpa_dbus_dict_open_read(&variant_iter, &iter_dict, error))
 		return FALSE;
@@ -915,8 +985,8 @@
 			wpa_s->conf->changed_parameters |=
 					CFG_CHANGED_SEC_DEVICE_TYPE;
 		} else if (os_strcmp(entry.key, "VendorExtension") == 0) {
-			if ((entry.type != DBUS_TYPE_ARRAY) ||
-			    (entry.array_type != WPAS_DBUS_TYPE_BINARRAY) ||
+			if (entry.type != DBUS_TYPE_ARRAY ||
+			    entry.array_type != WPAS_DBUS_TYPE_BINARRAY ||
 			    (entry.array_len > P2P_MAX_WPS_VENDOR_EXT))
 				goto error;
 
@@ -932,30 +1002,30 @@
 				} else
 					wpa_s->conf->wps_vendor_ext[i] = NULL;
 			}
-		} else if ((os_strcmp(entry.key, "GOIntent") == 0) &&
-			   (entry.type == DBUS_TYPE_UINT32) &&
+		} else if (os_strcmp(entry.key, "GOIntent") == 0 &&
+			   entry.type == DBUS_TYPE_UINT32 &&
 			   (entry.uint32_value <= 15))
 			wpa_s->conf->p2p_go_intent = entry.uint32_value;
-		else if ((os_strcmp(entry.key, "PersistentReconnect") == 0) &&
-			 (entry.type == DBUS_TYPE_BOOLEAN))
+		else if (os_strcmp(entry.key, "PersistentReconnect") == 0 &&
+			 entry.type == DBUS_TYPE_BOOLEAN)
 			wpa_s->conf->persistent_reconnect = entry.bool_value;
-		else if ((os_strcmp(entry.key, "ListenRegClass") == 0) &&
-			 (entry.type == DBUS_TYPE_UINT32)) {
+		else if (os_strcmp(entry.key, "ListenRegClass") == 0 &&
+			 entry.type == DBUS_TYPE_UINT32) {
 			wpa_s->conf->p2p_listen_reg_class = entry.uint32_value;
 			wpa_s->conf->changed_parameters |=
 				CFG_CHANGED_P2P_LISTEN_CHANNEL;
-		} else if ((os_strcmp(entry.key, "ListenChannel") == 0) &&
-			   (entry.type == DBUS_TYPE_UINT32)) {
+		} else if (os_strcmp(entry.key, "ListenChannel") == 0 &&
+			   entry.type == DBUS_TYPE_UINT32) {
 			wpa_s->conf->p2p_listen_channel = entry.uint32_value;
 			wpa_s->conf->changed_parameters |=
 				CFG_CHANGED_P2P_LISTEN_CHANNEL;
-		} else if ((os_strcmp(entry.key, "OperRegClass") == 0) &&
-			   (entry.type == DBUS_TYPE_UINT32)) {
+		} else if (os_strcmp(entry.key, "OperRegClass") == 0 &&
+			   entry.type == DBUS_TYPE_UINT32) {
 			wpa_s->conf->p2p_oper_reg_class = entry.uint32_value;
 			wpa_s->conf->changed_parameters |=
 				CFG_CHANGED_P2P_OPER_CHANNEL;
-		} else if ((os_strcmp(entry.key, "OperChannel") == 0) &&
-			   (entry.type == DBUS_TYPE_UINT32)) {
+		} else if (os_strcmp(entry.key, "OperChannel") == 0 &&
+			   entry.type == DBUS_TYPE_UINT32) {
 			wpa_s->conf->p2p_oper_channel = entry.uint32_value;
 			wpa_s->conf->changed_parameters |=
 				CFG_CHANGED_P2P_OPER_CHANNEL;
@@ -974,13 +1044,13 @@
 
 			wpa_s->conf->changed_parameters |=
 					CFG_CHANGED_P2P_SSID_POSTFIX;
-		} else if ((os_strcmp(entry.key, "IntraBss") == 0) &&
-			   (entry.type == DBUS_TYPE_BOOLEAN)) {
+		} else if (os_strcmp(entry.key, "IntraBss") == 0 &&
+			   entry.type == DBUS_TYPE_BOOLEAN) {
 			wpa_s->conf->p2p_intra_bss = entry.bool_value;
 			wpa_s->conf->changed_parameters |=
 				CFG_CHANGED_P2P_INTRA_BSS;
-		} else if ((os_strcmp(entry.key, "GroupIdle") == 0) &&
-			   (entry.type == DBUS_TYPE_UINT32))
+		} else if (os_strcmp(entry.key, "GroupIdle") == 0 &&
+			   entry.type == DBUS_TYPE_UINT32)
 			wpa_s->conf->p2p_group_idle = entry.uint32_value;
 		else if (os_strcmp(entry.key, "disassoc_low_ack") == 0 &&
 			 entry.type == DBUS_TYPE_UINT32)
@@ -1036,7 +1106,8 @@
 
 	char **peer_obj_paths = NULL;
 
-	if (!wpa_dbus_p2p_check_enabled(wpa_s, NULL, NULL, error))
+	if (!wpa_dbus_p2p_check_enabled(wpa_s, NULL, NULL, error) ||
+	    !wpa_s->parent->parent->dbus_new_path)
 		return FALSE;
 
 	dl_list_init(&peer_objpath_list);
@@ -1057,7 +1128,8 @@
 		os_snprintf(node->path, WPAS_DBUS_OBJECT_PATH_MAX,
 			    "%s/" WPAS_DBUS_NEW_P2P_PEERS_PART
 			    "/" COMPACT_MACSTR,
-			    wpa_s->dbus_new_path, MAC2STR(addr));
+			    wpa_s->parent->parent->dbus_new_path,
+			    MAC2STR(addr));
 		dl_list_add_tail(&peer_objpath_list, &node->list);
 		num++;
 
@@ -1177,13 +1249,17 @@
 	struct wpa_supplicant *wpa_s = user_data;
 	char go_peer_obj_path[WPAS_DBUS_OBJECT_PATH_MAX], *path;
 
+	if (!wpa_s->parent->parent->dbus_new_path)
+		return FALSE;
+
 	if (wpas_get_p2p_role(wpa_s) != WPAS_P2P_ROLE_CLIENT)
 		os_snprintf(go_peer_obj_path, WPAS_DBUS_OBJECT_PATH_MAX, "/");
 	else
 		os_snprintf(go_peer_obj_path, WPAS_DBUS_OBJECT_PATH_MAX,
 			    "%s/" WPAS_DBUS_NEW_P2P_PEERS_PART "/"
 			    COMPACT_MACSTR,
-			    wpa_s->dbus_new_path, MAC2STR(wpa_s->go_dev_addr));
+			    wpa_s->parent->parent->dbus_new_path,
+			    MAC2STR(wpa_s->go_dev_addr));
 
 	path = go_peer_obj_path;
 	return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_OBJECT_PATH,
@@ -1233,6 +1309,154 @@
 }
 
 
+dbus_bool_t wpas_dbus_getter_p2p_peer_manufacturer(DBusMessageIter *iter,
+						   DBusError *error,
+						   void *user_data)
+{
+	struct peer_handler_args *peer_args = user_data;
+	const struct p2p_peer_info *info;
+	char *tmp;
+
+	if (!wpa_dbus_p2p_check_enabled(peer_args->wpa_s, NULL, NULL, error))
+		return FALSE;
+
+	/* get the peer info */
+	info = p2p_get_peer_found(peer_args->wpa_s->global->p2p,
+				  peer_args->p2p_device_addr, 0);
+	if (info == NULL) {
+		dbus_set_error(error, DBUS_ERROR_FAILED, "failed to find peer");
+		return FALSE;
+	}
+
+	tmp = os_strdup(info->manufacturer);
+	if (!tmp) {
+		dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory");
+		return FALSE;
+	}
+
+	if (!wpas_dbus_simple_property_getter(iter, DBUS_TYPE_STRING, &tmp,
+					      error)) {
+		dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory");
+		os_free(tmp);
+		return FALSE;
+	}
+
+	os_free(tmp);
+	return TRUE;
+}
+
+
+dbus_bool_t wpas_dbus_getter_p2p_peer_modelname(DBusMessageIter *iter,
+						DBusError *error,
+						void *user_data)
+{
+	struct peer_handler_args *peer_args = user_data;
+	const struct p2p_peer_info *info;
+	char *tmp;
+
+	if (!wpa_dbus_p2p_check_enabled(peer_args->wpa_s, NULL, NULL, error))
+		return FALSE;
+
+	/* get the peer info */
+	info = p2p_get_peer_found(peer_args->wpa_s->global->p2p,
+				  peer_args->p2p_device_addr, 0);
+	if (info == NULL) {
+		dbus_set_error(error, DBUS_ERROR_FAILED, "failed to find peer");
+		return FALSE;
+	}
+
+	tmp = os_strdup(info->model_name);
+	if (!tmp) {
+		dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory");
+		return FALSE;
+	}
+
+	if (!wpas_dbus_simple_property_getter(iter, DBUS_TYPE_STRING, &tmp,
+					      error)) {
+		dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory");
+		os_free(tmp);
+		return FALSE;
+	}
+
+	os_free(tmp);
+	return TRUE;
+}
+
+
+dbus_bool_t wpas_dbus_getter_p2p_peer_modelnumber(DBusMessageIter *iter,
+						  DBusError *error,
+						  void *user_data)
+{
+	struct peer_handler_args *peer_args = user_data;
+	const struct p2p_peer_info *info;
+	char *tmp;
+
+	if (!wpa_dbus_p2p_check_enabled(peer_args->wpa_s, NULL, NULL, error))
+		return FALSE;
+
+	/* get the peer info */
+	info = p2p_get_peer_found(peer_args->wpa_s->global->p2p,
+				  peer_args->p2p_device_addr, 0);
+	if (info == NULL) {
+		dbus_set_error(error, DBUS_ERROR_FAILED, "failed to find peer");
+		return FALSE;
+	}
+
+	tmp = os_strdup(info->model_number);
+	if (!tmp) {
+		dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory");
+		return FALSE;
+	}
+
+	if (!wpas_dbus_simple_property_getter(iter, DBUS_TYPE_STRING, &tmp,
+					      error)) {
+		dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory");
+		os_free(tmp);
+		return FALSE;
+	}
+
+	os_free(tmp);
+	return TRUE;
+}
+
+
+dbus_bool_t wpas_dbus_getter_p2p_peer_serialnumber(DBusMessageIter *iter,
+						   DBusError *error,
+						   void *user_data)
+{
+	struct peer_handler_args *peer_args = user_data;
+	const struct p2p_peer_info *info;
+	char *tmp;
+
+	if (!wpa_dbus_p2p_check_enabled(peer_args->wpa_s, NULL, NULL, error))
+		return FALSE;
+
+	/* get the peer info */
+	info = p2p_get_peer_found(peer_args->wpa_s->global->p2p,
+				  peer_args->p2p_device_addr, 0);
+	if (info == NULL) {
+		dbus_set_error(error, DBUS_ERROR_FAILED, "failed to find peer");
+		return FALSE;
+	}
+
+	tmp = os_strdup(info->serial_number);
+	if (!tmp) {
+		dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory");
+		return FALSE;
+	}
+
+	if (!wpas_dbus_simple_property_getter(iter, DBUS_TYPE_STRING, &tmp,
+					      error)) {
+		dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory");
+		os_free(tmp);
+		return FALSE;
+	}
+
+	os_free(tmp);
+	return TRUE;
+}
+
+
 dbus_bool_t wpas_dbus_getter_p2p_peer_primary_device_type(
 	DBusMessageIter *iter, DBusError *error, void *user_data)
 {
@@ -1260,8 +1484,8 @@
 
 
 dbus_bool_t wpas_dbus_getter_p2p_peer_config_method(DBusMessageIter *iter,
-                                                    DBusError *error,
-                                                    void *user_data)
+						    DBusError *error,
+						    void *user_data)
 {
 	struct peer_handler_args *peer_args = user_data;
 	const struct p2p_peer_info *info;
@@ -1285,8 +1509,8 @@
 
 
 dbus_bool_t wpas_dbus_getter_p2p_peer_level(DBusMessageIter *iter,
-                                            DBusError *error,
-                                            void *user_data)
+					    DBusError *error,
+					    void *user_data)
 {
 	struct peer_handler_args *peer_args = user_data;
 	const struct p2p_peer_info *info;
@@ -1310,8 +1534,8 @@
 
 
 dbus_bool_t wpas_dbus_getter_p2p_peer_device_capability(DBusMessageIter *iter,
-                                                        DBusError *error,
-                                                        void *user_data)
+							DBusError *error,
+							void *user_data)
 {
 	struct peer_handler_args *peer_args = user_data;
 	const struct p2p_peer_info *info;
@@ -1369,8 +1593,7 @@
 	info = p2p_get_peer_found(peer_args->wpa_s->global->p2p,
 				  peer_args->p2p_device_addr, 0);
 	if (info == NULL) {
-		dbus_set_error(error, DBUS_ERROR_FAILED,
-			       "failed to find peer");
+		dbus_set_error(error, DBUS_ERROR_FAILED, "failed to find peer");
 		return FALSE;
 	}
 
@@ -1378,18 +1601,13 @@
 					      DBUS_TYPE_ARRAY_AS_STRING
 					      DBUS_TYPE_ARRAY_AS_STRING
 					      DBUS_TYPE_BYTE_AS_STRING,
-					      &variant_iter)) {
-		dbus_set_error(error, DBUS_ERROR_FAILED,
-		               "%s: failed to construct message 1", __func__);
-		return FALSE;
-	}
-
-	if (!dbus_message_iter_open_container(&variant_iter, DBUS_TYPE_ARRAY,
+					      &variant_iter) ||
+	    !dbus_message_iter_open_container(&variant_iter, DBUS_TYPE_ARRAY,
 					      DBUS_TYPE_ARRAY_AS_STRING
 					      DBUS_TYPE_BYTE_AS_STRING,
 					      &array_iter)) {
 		dbus_set_error(error, DBUS_ERROR_FAILED,
-		               "%s: failed to construct message 2", __func__);
+			       "%s: failed to construct message 1", __func__);
 		return FALSE;
 	}
 
@@ -1404,29 +1622,14 @@
 			if (!dbus_message_iter_open_container(
 				    &array_iter, DBUS_TYPE_ARRAY,
 				    DBUS_TYPE_BYTE_AS_STRING,
-				    &inner_array_iter)) {
-				dbus_set_error(error, DBUS_ERROR_FAILED,
-					       "%s: failed to construct "
-					       "message 3 (%d)",
-					       __func__, i);
-				return FALSE;
-			}
-
-			if (!dbus_message_iter_append_fixed_array(
+				    &inner_array_iter) ||
+			    !dbus_message_iter_append_fixed_array(
 				    &inner_array_iter, DBUS_TYPE_BYTE,
-				    &sec_dev_type_list, WPS_DEV_TYPE_LEN)) {
-				dbus_set_error(error, DBUS_ERROR_FAILED,
-					       "%s: failed to construct "
-					       "message 4 (%d)",
-					       __func__, i);
-				return FALSE;
-			}
-
-			if (!dbus_message_iter_close_container(
+				    &sec_dev_type_list, WPS_DEV_TYPE_LEN) ||
+			    !dbus_message_iter_close_container(
 				    &array_iter, &inner_array_iter)) {
 				dbus_set_error(error, DBUS_ERROR_FAILED,
-					       "%s: failed to construct "
-					       "message 5 (%d)",
+					       "%s: failed to construct message 2 (%d)",
 					       __func__, i);
 				return FALSE;
 			}
@@ -1435,15 +1638,10 @@
 		}
 	}
 
-	if (!dbus_message_iter_close_container(&variant_iter, &array_iter)) {
+	if (!dbus_message_iter_close_container(&variant_iter, &array_iter) ||
+	    !dbus_message_iter_close_container(iter, &variant_iter)) {
 		dbus_set_error(error, DBUS_ERROR_FAILED,
-		               "%s: failed to construct message 6", __func__);
-		return FALSE;
-	}
-
-	if (!dbus_message_iter_close_container(iter, &variant_iter)) {
-		dbus_set_error(error, DBUS_ERROR_FAILED,
-		               "%s: failed to construct message 7", __func__);
+			       "%s: failed to construct message 3", __func__);
 		return FALSE;
 	}
 
@@ -1583,7 +1781,7 @@
 	struct peer_handler_args *peer_args = user_data;
 	const struct p2p_peer_info *info;
 	struct peer_group_data data;
-	struct wpa_supplicant *wpa_s_go;
+	struct wpa_supplicant *wpa_s, *wpa_s_go;
 	dbus_bool_t success = FALSE;
 
 	info = p2p_get_peer_found(peer_args->wpa_s->global->p2p,
@@ -1595,8 +1793,11 @@
 	}
 
 	os_memset(&data, 0, sizeof(data));
-	wpa_s_go = wpas_get_p2p_client_iface(peer_args->wpa_s,
-					     info->p2p_device_addr);
+
+	wpa_s = peer_args->wpa_s;
+	wpa_s = wpa_s->global->p2p_init_wpa_s;
+
+	wpa_s_go = wpas_get_p2p_client_iface(wpa_s, info->p2p_device_addr);
 	if (wpa_s_go) {
 		data.paths = os_calloc(1, sizeof(char *));
 		if (data.paths == NULL)
@@ -1651,14 +1852,9 @@
 	unsigned int i = 0, num = 0;
 	dbus_bool_t success = FALSE;
 
-	if (wpa_s->conf == NULL) {
-		wpa_printf(MSG_ERROR, "dbus: %s: "
-			   "An error occurred getting persistent groups list",
-			   __func__);
-		dbus_set_error_const(error, DBUS_ERROR_FAILED, "an error "
-				     "occurred getting persistent groups list");
+	wpa_s = wpa_s->global->p2p_init_wpa_s;
+	if (!wpa_s->parent->dbus_new_path)
 		return FALSE;
-	}
 
 	for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next)
 		if (network_is_persistent_group(ssid))
@@ -1683,7 +1879,7 @@
 		/* Construct the object path for this network. */
 		os_snprintf(paths[i++], WPAS_DBUS_OBJECT_PATH_MAX,
 			    "%s/" WPAS_DBUS_NEW_PERSISTENT_GROUPS_PART "/%d",
-			    wpa_s->dbus_new_path, ssid->id);
+			    wpa_s->parent->dbus_new_path, ssid->id);
 	}
 
 	success = wpas_dbus_simple_array_property_getter(iter,
@@ -1770,14 +1966,16 @@
 
 	dbus_message_iter_init(message, &iter);
 
-	ssid = wpa_config_add_network(wpa_s->conf);
+	wpa_s = wpa_s->global->p2p_init_wpa_s;
+	if (wpa_s->parent->dbus_new_path)
+		ssid = wpa_config_add_network(wpa_s->conf);
 	if (ssid == NULL) {
-		wpa_printf(MSG_ERROR, "dbus: %s: "
-			   "Cannot add new persistent group", __func__);
+		wpa_printf(MSG_ERROR,
+			   "dbus: %s: Cannot add new persistent group",
+			   __func__);
 		reply = wpas_dbus_error_unknown_error(
 			message,
-			"wpa_supplicant could not add "
-			"a persistent group on this interface.");
+			"wpa_supplicant could not add a persistent group on this interface.");
 		goto err;
 	}
 
@@ -1790,13 +1988,12 @@
 
 	dbus_error_init(&error);
 	if (!set_network_properties(wpa_s, ssid, &iter, &error)) {
-		wpa_printf(MSG_DEBUG, "dbus: %s: "
-			   "Control interface could not set persistent group "
-			   "properties", __func__);
-		reply = wpas_dbus_reply_new_from_error(message, &error,
-						       DBUS_ERROR_INVALID_ARGS,
-						       "Failed to set network "
-						       "properties");
+		wpa_printf(MSG_DEBUG,
+			   "dbus: %s: Control interface could not set persistent group properties",
+			   __func__);
+		reply = wpas_dbus_reply_new_from_error(
+			message, &error, DBUS_ERROR_INVALID_ARGS,
+			"Failed to set network properties");
 		dbus_error_free(&error);
 		goto err;
 	}
@@ -1804,19 +2001,17 @@
 	/* Construct the object path for this network. */
 	os_snprintf(path, WPAS_DBUS_OBJECT_PATH_MAX,
 		    "%s/" WPAS_DBUS_NEW_PERSISTENT_GROUPS_PART "/%d",
-		    wpa_s->dbus_new_path, ssid->id);
+		    wpa_s->parent->dbus_new_path, ssid->id);
 
 	reply = dbus_message_new_method_return(message);
 	if (reply == NULL) {
-		reply = dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY,
-					       NULL);
+		reply = wpas_dbus_error_no_memory(message);
 		goto err;
 	}
 	if (!dbus_message_append_args(reply, DBUS_TYPE_OBJECT_PATH, &path,
 				      DBUS_TYPE_INVALID)) {
 		dbus_message_unref(reply);
-		reply = dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY,
-					       NULL);
+		reply = wpas_dbus_error_no_memory(message);
 		goto err;
 	}
 
@@ -1846,21 +2041,25 @@
 {
 	DBusMessage *reply = NULL;
 	const char *op;
-	char *iface = NULL, *persistent_group_id = NULL;
+	char *iface = NULL, *persistent_group_id;
 	int id;
 	struct wpa_ssid *ssid;
 
 	dbus_message_get_args(message, NULL, DBUS_TYPE_OBJECT_PATH, &op,
 			      DBUS_TYPE_INVALID);
 
+	wpa_s = wpa_s->global->p2p_init_wpa_s;
+
 	/*
 	 * Extract the network ID and ensure the network is actually a child of
 	 * this interface.
 	 */
-	iface = wpas_dbus_new_decompose_object_path(op, 1,
-						    &persistent_group_id,
-						    NULL);
-	if (iface == NULL || os_strcmp(iface, wpa_s->dbus_new_path) != 0) {
+	iface = wpas_dbus_new_decompose_object_path(
+		op, WPAS_DBUS_NEW_PERSISTENT_GROUPS_PART,
+		&persistent_group_id);
+	if (iface == NULL || persistent_group_id == NULL ||
+	    !wpa_s->parent->dbus_new_path ||
+	    os_strcmp(iface, wpa_s->parent->dbus_new_path) != 0) {
 		reply = wpas_dbus_error_invalid_args(message, op);
 		goto out;
 	}
@@ -1880,19 +2079,17 @@
 	wpas_notify_persistent_group_removed(wpa_s, ssid);
 
 	if (wpa_config_remove_network(wpa_s->conf, id) < 0) {
-		wpa_printf(MSG_ERROR, "dbus: %s: "
-			   "error occurred when removing persistent group %d",
+		wpa_printf(MSG_ERROR,
+			   "dbus: %s: error occurred when removing persistent group %d",
 			   __func__, id);
 		reply = wpas_dbus_error_unknown_error(
 			message,
-			"error removing the specified persistent group on "
-			"this interface.");
+			"error removing the specified persistent group on this interface.");
 		goto out;
 	}
 
 out:
 	os_free(iface);
-	os_free(persistent_group_id);
 	return reply;
 }
 
@@ -1903,8 +2100,8 @@
 	wpas_notify_persistent_group_removed(wpa_s, ssid);
 
 	if (wpa_config_remove_network(wpa_s->conf, ssid->id) < 0) {
-		wpa_printf(MSG_ERROR, "dbus: %s: "
-			   "error occurred when removing persistent group %d",
+		wpa_printf(MSG_ERROR,
+			   "dbus: %s: error occurred when removing persistent group %d",
 			   __func__, ssid->id);
 		return;
 	}
@@ -1927,6 +2124,8 @@
 	struct wpa_ssid *ssid, *next;
 	struct wpa_config *config;
 
+	wpa_s = wpa_s->global->p2p_init_wpa_s;
+
 	config = wpa_s->conf;
 	ssid = config->ssid;
 	while (ssid) {
@@ -1956,6 +2155,9 @@
 	const u8 *addr;
 	dbus_bool_t success = FALSE;
 
+	if (!wpa_s->parent->parent->dbus_new_path)
+		return FALSE;
+
 	/* Verify correct role for this property */
 	if (wpas_get_p2p_role(wpa_s) != WPAS_P2P_ROLE_GO) {
 		return wpas_dbus_simple_array_property_getter(
@@ -1983,7 +2185,8 @@
 		os_snprintf(paths[i], WPAS_DBUS_OBJECT_PATH_MAX,
 			    "%s/" WPAS_DBUS_NEW_P2P_PEERS_PART
 			    "/" COMPACT_MACSTR,
-			    wpa_s->parent->dbus_new_path, MAC2STR(addr));
+			    wpa_s->parent->parent->dbus_new_path,
+			    MAC2STR(addr));
 		i++;
 	}
 
@@ -2012,6 +2215,7 @@
 					    DBusError *error, void *user_data)
 {
 	struct wpa_supplicant *wpa_s = user_data;
+
 	if (wpa_s->current_ssid == NULL)
 		return FALSE;
 	return wpas_dbus_simple_array_property_getter(
@@ -2072,15 +2276,14 @@
 						  void *user_data)
 {
 	struct wpa_supplicant *wpa_s = user_data;
-	u8 role = wpas_get_p2p_role(wpa_s);
-	char *p_pass = NULL;
+	char *p_pass;
+	struct wpa_ssid *ssid = wpa_s->current_ssid;
 
-	/* Verify correct role for this property */
-	if (role == WPAS_P2P_ROLE_GO) {
-		if (wpa_s->current_ssid == NULL)
-			return FALSE;
-		p_pass = wpa_s->current_ssid->passphrase;
-	} else
+	if (ssid == NULL)
+		return FALSE;
+
+	p_pass = ssid->passphrase;
+	if (!p_pass)
 		p_pass = "";
 
 	return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_STRING,
@@ -2093,20 +2296,20 @@
 					   DBusError *error, void *user_data)
 {
 	struct wpa_supplicant *wpa_s = user_data;
-	u8 role = wpas_get_p2p_role(wpa_s);
 	u8 *p_psk = NULL;
 	u8 psk_len = 0;
+	struct wpa_ssid *ssid = wpa_s->current_ssid;
 
-	/* Verify correct role for this property */
-	if (role == WPAS_P2P_ROLE_CLIENT) {
-		if (wpa_s->current_ssid == NULL)
-			return FALSE;
-		p_psk = wpa_s->current_ssid->psk;
-		psk_len = 32;
+	if (ssid == NULL)
+		return FALSE;
+
+	if (ssid->psk_set) {
+		p_psk = ssid->psk;
+		psk_len = sizeof(ssid->psk);
 	}
 
 	return wpas_dbus_simple_array_property_getter(iter, DBUS_TYPE_BYTE,
-						      &p_psk, psk_len, error);
+						      p_psk, psk_len, error);
 }
 
 
@@ -2150,7 +2353,7 @@
 						  void *user_data)
 {
 	struct wpa_supplicant *wpa_s = user_data;
-	DBusMessageIter variant_iter, iter_dict;
+	DBusMessageIter variant_iter, iter_dict, array_iter, sub;
 	struct wpa_dbus_dict_entry entry = { .type = DBUS_TYPE_STRING };
 	unsigned int i;
 	struct hostapd_data *hapd = NULL;
@@ -2162,6 +2365,82 @@
 		return FALSE;
 
 	dbus_message_iter_recurse(iter, &variant_iter);
+	if (dbus_message_iter_get_arg_type(&variant_iter) != DBUS_TYPE_ARRAY)
+		return FALSE;
+
+	/*
+	 * This is supposed to be array of bytearrays (aay), but the earlier
+	 * implementation used a dict with "WPSVendorExtensions" as the key in
+	 * this setter function which does not match the format used by the
+	 * getter function. For backwards compatibility, allow both formats to
+	 * be used in the setter.
+	 */
+	if (dbus_message_iter_get_element_type(&variant_iter) ==
+	    DBUS_TYPE_ARRAY) {
+		/* This is the proper format matching the getter */
+		struct wpabuf *vals[MAX_WPS_VENDOR_EXTENSIONS];
+
+		dbus_message_iter_recurse(&variant_iter, &array_iter);
+
+		if (dbus_message_iter_get_arg_type(&array_iter) !=
+		    DBUS_TYPE_ARRAY ||
+		    dbus_message_iter_get_element_type(&array_iter) !=
+		    DBUS_TYPE_BYTE) {
+			wpa_printf(MSG_DEBUG,
+				   "dbus: Not an array of array of bytes");
+			return FALSE;
+		}
+
+		i = 0;
+		os_memset(vals, 0, sizeof(vals));
+
+		while (dbus_message_iter_get_arg_type(&array_iter) ==
+		       DBUS_TYPE_ARRAY) {
+			char *val;
+			int len;
+
+			if (i == MAX_WPS_VENDOR_EXTENSIONS) {
+				wpa_printf(MSG_DEBUG,
+					   "dbus: Too many WPSVendorExtensions values");
+				i = MAX_WPS_VENDOR_EXTENSIONS + 1;
+				break;
+			}
+
+			dbus_message_iter_recurse(&array_iter, &sub);
+			dbus_message_iter_get_fixed_array(&sub, &val, &len);
+			wpa_hexdump(MSG_DEBUG, "dbus: WPSVendorExtentions[]",
+				    val, len);
+			vals[i] = wpabuf_alloc_copy(val, len);
+			if (vals[i] == NULL) {
+				i = MAX_WPS_VENDOR_EXTENSIONS + 1;
+				break;
+			}
+			i++;
+			dbus_message_iter_next(&array_iter);
+		}
+
+		if (i > MAX_WPS_VENDOR_EXTENSIONS) {
+			for (i = 0; i < MAX_WPS_VENDOR_EXTENSIONS; i++)
+				wpabuf_free(vals[i]);
+			return FALSE;
+		}
+
+		for (i = 0; i < MAX_WPS_VENDOR_EXTENSIONS; i++) {
+			wpabuf_free(hapd->conf->wps_vendor_ext[i]);
+			hapd->conf->wps_vendor_ext[i] = vals[i];
+		}
+
+		hostapd_update_wps(hapd);
+
+		return TRUE;
+	}
+
+	if (dbus_message_iter_get_element_type(&variant_iter) !=
+	    DBUS_TYPE_DICT_ENTRY)
+		return FALSE;
+
+	wpa_printf(MSG_DEBUG,
+		   "dbus: Try to use backwards compatibility version of WPSVendorExtensions setter");
 	if (!wpa_dbus_dict_open_read(&variant_iter, &iter_dict, error))
 		return FALSE;
 
@@ -2179,6 +2458,7 @@
 				goto error;
 
 			for (i = 0; i < MAX_WPS_VENDOR_EXTENSIONS; i++) {
+				wpabuf_free(hapd->conf->wps_vendor_ext[i]);
 				if (i < entry.array_len) {
 					hapd->conf->wps_vendor_ext[i] =
 						entry.binarray_value[i];
@@ -2227,30 +2507,31 @@
 		if (!wpa_dbus_dict_get_entry(&iter_dict, &entry))
 			goto error;
 
-		if (!os_strcmp(entry.key, "service_type") &&
-		    (entry.type == DBUS_TYPE_STRING)) {
-			if (!os_strcmp(entry.str_value, "upnp"))
+		if (os_strcmp(entry.key, "service_type") == 0 &&
+		    entry.type == DBUS_TYPE_STRING) {
+			if (os_strcmp(entry.str_value, "upnp") == 0)
 				upnp = 1;
-			else if (!os_strcmp(entry.str_value, "bonjour"))
+			else if (os_strcmp(entry.str_value, "bonjour") == 0)
 				bonjour = 1;
 			else
 				goto error_clear;
-		} else if (!os_strcmp(entry.key, "version") &&
-		           entry.type == DBUS_TYPE_INT32) {
+		} else if (os_strcmp(entry.key, "version") == 0 &&
+			   entry.type == DBUS_TYPE_INT32) {
 			version = entry.uint32_value;
-		} else if (!os_strcmp(entry.key, "service") &&
-			     (entry.type == DBUS_TYPE_STRING)) {
+		} else if (os_strcmp(entry.key, "service") == 0 &&
+			   entry.type == DBUS_TYPE_STRING) {
+			os_free(service);
 			service = os_strdup(entry.str_value);
-		} else if (!os_strcmp(entry.key, "query")) {
-			if ((entry.type != DBUS_TYPE_ARRAY) ||
-			    (entry.array_type != DBUS_TYPE_BYTE))
+		} else if (os_strcmp(entry.key, "query") == 0) {
+			if (entry.type != DBUS_TYPE_ARRAY ||
+			    entry.array_type != DBUS_TYPE_BYTE)
 				goto error_clear;
 			query = wpabuf_alloc_copy(
 				entry.bytearray_value,
 				entry.array_len);
-		} else if (!os_strcmp(entry.key, "response")) {
-			if ((entry.type != DBUS_TYPE_ARRAY) ||
-			    (entry.array_type != DBUS_TYPE_BYTE))
+		} else if (os_strcmp(entry.key, "response") == 0) {
+			if (entry.type != DBUS_TYPE_ARRAY ||
+			    entry.array_type != DBUS_TYPE_BYTE)
 				goto error_clear;
 			resp = wpabuf_alloc_copy(entry.bytearray_value,
 						 entry.array_len);
@@ -2265,8 +2546,6 @@
 		if (wpas_p2p_service_add_upnp(wpa_s, version, service) != 0)
 			goto error;
 
-		os_free(service);
-		service = NULL;
 	} else if (bonjour == 1) {
 		if (query == NULL || resp == NULL)
 			goto error;
@@ -2278,6 +2557,7 @@
 	} else
 		goto error;
 
+	os_free(service);
 	return reply;
 error_clear:
 	wpa_dbus_dict_entry_clear(&entry);
@@ -2312,11 +2592,11 @@
 		if (!wpa_dbus_dict_get_entry(&iter_dict, &entry))
 			goto error;
 
-		if (!os_strcmp(entry.key, "service_type") &&
-		    (entry.type == DBUS_TYPE_STRING)) {
-			if (!os_strcmp(entry.str_value, "upnp"))
+		if (os_strcmp(entry.key, "service_type") == 0 &&
+		    entry.type == DBUS_TYPE_STRING) {
+			if (os_strcmp(entry.str_value, "upnp") == 0)
 				upnp = 1;
-			else if (!os_strcmp(entry.str_value, "bonjour"))
+			else if (os_strcmp(entry.str_value, "bonjour") == 0)
 				bonjour = 1;
 			else
 				goto error_clear;
@@ -2327,13 +2607,14 @@
 		while (wpa_dbus_dict_has_dict_entry(&iter_dict)) {
 			if (!wpa_dbus_dict_get_entry(&iter_dict, &entry))
 				goto error;
-			if (!os_strcmp(entry.key, "version") &&
+			if (os_strcmp(entry.key, "version") == 0 &&
 			    entry.type == DBUS_TYPE_INT32)
 				version = entry.uint32_value;
-			else if (!os_strcmp(entry.key, "service") &&
-				 entry.type == DBUS_TYPE_STRING)
+			else if (os_strcmp(entry.key, "service") == 0 &&
+				 entry.type == DBUS_TYPE_STRING) {
+				os_free(service);
 				service = os_strdup(entry.str_value);
-			else
+			} else
 				goto error_clear;
 
 			wpa_dbus_dict_entry_clear(&entry);
@@ -2343,7 +2624,6 @@
 			goto error;
 
 		ret = wpas_p2p_service_del_upnp(wpa_s, version, service);
-		os_free(service);
 		if (ret != 0)
 			goto error;
 	} else if (bonjour == 1) {
@@ -2351,10 +2631,11 @@
 			if (!wpa_dbus_dict_get_entry(&iter_dict, &entry))
 				goto error;
 
-			if (!os_strcmp(entry.key, "query")) {
-				if ((entry.type != DBUS_TYPE_ARRAY) ||
-				    (entry.array_type != DBUS_TYPE_BYTE))
+			if (os_strcmp(entry.key, "query") == 0) {
+				if (entry.type != DBUS_TYPE_ARRAY ||
+				    entry.array_type != DBUS_TYPE_BYTE)
 					goto error_clear;
+				wpabuf_free(query);
 				query = wpabuf_alloc_copy(
 					entry.bytearray_value,
 					entry.array_len);
@@ -2370,14 +2651,17 @@
 		ret = wpas_p2p_service_del_bonjour(wpa_s, query);
 		if (ret != 0)
 			goto error;
-		wpabuf_free(query);
 	} else
 		goto error;
 
+	wpabuf_free(query);
+	os_free(service);
 	return reply;
 error_clear:
 	wpa_dbus_dict_entry_clear(&entry);
 error:
+	wpabuf_free(query);
+	os_free(service);
 	return wpas_dbus_error_invalid_args(message, NULL);
 }
 
@@ -2413,22 +2697,22 @@
 	while (wpa_dbus_dict_has_dict_entry(&iter_dict)) {
 		if (!wpa_dbus_dict_get_entry(&iter_dict, &entry))
 			goto error;
-		if (!os_strcmp(entry.key, "peer_object") &&
+		if (os_strcmp(entry.key, "peer_object") == 0 &&
 		    entry.type == DBUS_TYPE_OBJECT_PATH) {
 			peer_object_path = os_strdup(entry.str_value);
-		} else if (!os_strcmp(entry.key, "service_type") &&
+		} else if (os_strcmp(entry.key, "service_type") == 0 &&
 			   entry.type == DBUS_TYPE_STRING) {
-			if (!os_strcmp(entry.str_value, "upnp"))
+			if (os_strcmp(entry.str_value, "upnp") == 0)
 				upnp = 1;
 			else
 				goto error_clear;
-		} else if (!os_strcmp(entry.key, "version") &&
+		} else if (os_strcmp(entry.key, "version") == 0 &&
 			   entry.type == DBUS_TYPE_INT32) {
 			version = entry.uint32_value;
-		} else if (!os_strcmp(entry.key, "service") &&
+		} else if (os_strcmp(entry.key, "service") == 0 &&
 			   entry.type == DBUS_TYPE_STRING) {
 			service = os_strdup(entry.str_value);
-		} else if (!os_strcmp(entry.key, "tlv")) {
+		} else if (os_strcmp(entry.key, "tlv") == 0) {
 			if (entry.type != DBUS_TYPE_ARRAY ||
 			    entry.array_type != DBUS_TYPE_BYTE)
 				goto error_clear;
@@ -2506,16 +2790,17 @@
 		if (!wpa_dbus_dict_get_entry(&iter_dict, &entry))
 			goto error;
 
-		if (!os_strcmp(entry.key, "peer_object") &&
+		if (os_strcmp(entry.key, "peer_object") == 0 &&
 		    entry.type == DBUS_TYPE_OBJECT_PATH) {
 			peer_object_path = os_strdup(entry.str_value);
-		} else if (!os_strcmp(entry.key, "frequency") &&
+		} else if (os_strcmp(entry.key, "frequency") == 0 &&
 			   entry.type == DBUS_TYPE_INT32) {
 			freq = entry.uint32_value;
-		} else if (!os_strcmp(entry.key, "dialog_token") &&
-			   entry.type == DBUS_TYPE_UINT32) {
+		} else if (os_strcmp(entry.key, "dialog_token") == 0 &&
+			   (entry.type == DBUS_TYPE_UINT32 ||
+			    entry.type == DBUS_TYPE_INT32)) {
 			dlg_tok = entry.uint32_value;
-		} else if (!os_strcmp(entry.key, "tlvs")) {
+		} else if (os_strcmp(entry.key, "tlvs") == 0) {
 			if (entry.type != DBUS_TYPE_ARRAY ||
 			    entry.array_type != DBUS_TYPE_BYTE)
 				goto error_clear;
@@ -2526,12 +2811,9 @@
 
 		wpa_dbus_dict_entry_clear(&entry);
 	}
-	if (!peer_object_path ||
-	    (parse_peer_object_path(peer_object_path, addr) < 0) ||
-	    !p2p_peer_known(wpa_s->global->p2p, addr))
-		goto error;
-
-	if (tlv == NULL)
+	if (parse_peer_object_path(peer_object_path, addr) < 0 ||
+	    !p2p_peer_known(wpa_s->global->p2p, addr) ||
+	    tlv == NULL)
 		goto error;
 
 	wpas_p2p_sd_response(wpa_s, freq, addr, (u8) dlg_tok, tlv);
diff --git a/wpa_supplicant/dbus/dbus_new_handlers_p2p.h b/wpa_supplicant/dbus/dbus_new_handlers_p2p.h
index 6e67c89..2aecbbe 100644
--- a/wpa_supplicant/dbus/dbus_new_handlers_p2p.h
+++ b/wpa_supplicant/dbus/dbus_new_handlers_p2p.h
@@ -46,6 +46,9 @@
 		DBusMessage *message,
 		struct wpa_supplicant *wpa_s);
 
+DBusMessage * wpas_dbus_handler_p2p_cancel(DBusMessage *message,
+					   struct wpa_supplicant *wpa_s);
+
 DBusMessage *wpas_dbus_handler_p2p_invite(
 		DBusMessage *message,
 		struct wpa_supplicant *wpa_s);
@@ -53,6 +56,9 @@
 DBusMessage *wpas_dbus_handler_p2p_disconnect(
 	DBusMessage *message, struct wpa_supplicant *wpa_s);
 
+DBusMessage * wpas_dbus_handler_p2p_remove_client(
+	DBusMessage *message, struct wpa_supplicant *wpa_s);
+
 DBusMessage *wpas_dbus_handler_p2p_flush(
 	DBusMessage *message, struct wpa_supplicant *wpa_s);
 
@@ -109,34 +115,50 @@
  */
 
 dbus_bool_t wpas_dbus_getter_p2p_peer_device_name(DBusMessageIter *iter,
-                                                  DBusError *error,
-                                                  void *user_data);
+						  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);
+						    DBusError *error,
+						    void *user_data);
 
 dbus_bool_t wpas_dbus_getter_p2p_peer_level(DBusMessageIter *iter,
-                                            DBusError *error,
-                                            void *user_data);
+					    DBusError *error,
+					    void *user_data);
 
 dbus_bool_t wpas_dbus_getter_p2p_peer_device_capability(DBusMessageIter *iter,
-                                                        DBusError *error,
-                                                        void *user_data);
+							DBusError *error,
+							void *user_data);
 
 dbus_bool_t wpas_dbus_getter_p2p_peer_group_capability(DBusMessageIter *iter,
-                                                       DBusError *error,
-                                                       void *user_data);
+						       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);
+						       DBusError *error,
+						       void *user_data);
 
 dbus_bool_t wpas_dbus_getter_p2p_peer_ies(DBusMessageIter *iter,
 					  DBusError *error,
diff --git a/wpa_supplicant/dbus/dbus_new_handlers_wps.c b/wpa_supplicant/dbus/dbus_new_handlers_wps.c
index 8ecf7db..b2251ba 100644
--- a/wpa_supplicant/dbus/dbus_new_handlers_wps.c
+++ b/wpa_supplicant/dbus/dbus_new_handlers_wps.c
@@ -41,8 +41,8 @@
 	dbus_message_iter_recurse(entry_iter, &variant_iter);
 	if (dbus_message_iter_get_arg_type(&variant_iter) !=
 	    DBUS_TYPE_STRING) {
-		wpa_printf(MSG_DEBUG, "dbus: WPS.Start - Wrong Role type, "
-			   "string required");
+		wpa_printf(MSG_DEBUG,
+			   "dbus: WPS.Start - Wrong Role type, string required");
 		*reply = wpas_dbus_error_invalid_args(message,
 						      "Role must be a string");
 		return -1;
@@ -53,7 +53,7 @@
 	else if (os_strcmp(val, "registrar") == 0)
 		params->role = 2;
 	else {
-		wpa_printf(MSG_DEBUG, "dbus: WPS.Start - Uknown role %s", val);
+		wpa_printf(MSG_DEBUG, "dbus: WPS.Start - Unknown role %s", val);
 		*reply = wpas_dbus_error_invalid_args(message, val);
 		return -1;
 	}
@@ -70,10 +70,9 @@
 	char *val;
 
 	dbus_message_iter_recurse(entry_iter, &variant_iter);
-	if (dbus_message_iter_get_arg_type(&variant_iter) !=
-	    DBUS_TYPE_STRING) {
-		wpa_printf(MSG_DEBUG, "dbus: WPS.Start - Wrong Type type, "
-			   "string required");
+	if (dbus_message_iter_get_arg_type(&variant_iter) != DBUS_TYPE_STRING) {
+		wpa_printf(MSG_DEBUG,
+			   "dbus: WPS.Start - Wrong Type type, string required");
 		*reply = wpas_dbus_error_invalid_args(message,
 						      "Type must be a string");
 		return -1;
@@ -105,8 +104,8 @@
 	if (dbus_message_iter_get_arg_type(&variant_iter) != DBUS_TYPE_ARRAY ||
 	    dbus_message_iter_get_element_type(&variant_iter) !=
 	    DBUS_TYPE_BYTE) {
-		wpa_printf(MSG_DEBUG, "dbus: WPS.Start - Wrong Bssid type, "
-			   "byte array required");
+		wpa_printf(MSG_DEBUG,
+			   "dbus: WPS.Start - Wrong Bssid type, byte array required");
 		*reply = wpas_dbus_error_invalid_args(
 			message, "Bssid must be a byte array");
 		return -1;
@@ -114,8 +113,8 @@
 	dbus_message_iter_recurse(&variant_iter, &array_iter);
 	dbus_message_iter_get_fixed_array(&array_iter, &params->bssid, &len);
 	if (len != ETH_ALEN) {
-		wpa_printf(MSG_DEBUG, "dbus: WPS.Stsrt - Wrong Bssid length "
-			   "%d", len);
+		wpa_printf(MSG_DEBUG, "dbus: WPS.Start - Wrong Bssid length %d",
+			   len);
 		*reply = wpas_dbus_error_invalid_args(message,
 						      "Bssid is wrong length");
 		return -1;
@@ -132,10 +131,9 @@
 	DBusMessageIter variant_iter;
 
 	dbus_message_iter_recurse(entry_iter, &variant_iter);
-	if (dbus_message_iter_get_arg_type(&variant_iter) !=
-	    DBUS_TYPE_STRING) {
-		wpa_printf(MSG_DEBUG, "dbus: WPS.Start - Wrong Pin type, "
-			   "string required");
+	if (dbus_message_iter_get_arg_type(&variant_iter) != DBUS_TYPE_STRING) {
+		wpa_printf(MSG_DEBUG,
+			   "dbus: WPS.Start - Wrong Pin type, string required");
 		*reply = wpas_dbus_error_invalid_args(message,
 						      "Pin must be a string");
 		return -1;
@@ -158,8 +156,8 @@
 	if (dbus_message_iter_get_arg_type(&variant_iter) != DBUS_TYPE_ARRAY ||
 	    dbus_message_iter_get_element_type(&variant_iter) !=
 	    DBUS_TYPE_BYTE) {
-		wpa_printf(MSG_DEBUG, "dbus: WPS.Start - Wrong "
-			   "P2PDeviceAddress type, byte array required");
+		wpa_printf(MSG_DEBUG,
+			   "dbus: WPS.Start - Wrong P2PDeviceAddress type, byte array required");
 		*reply = wpas_dbus_error_invalid_args(
 			message, "P2PDeviceAddress must be a byte array");
 		return -1;
@@ -168,11 +166,11 @@
 	dbus_message_iter_get_fixed_array(&array_iter, &params->p2p_dev_addr,
 					  &len);
 	if (len != ETH_ALEN) {
-		wpa_printf(MSG_DEBUG, "dbus: WPS.Start - Wrong "
-			   "P2PDeviceAddress length %d", len);
-		*reply = wpas_dbus_error_invalid_args(message,
-						      "P2PDeviceAddress "
-						      "has wrong length");
+		wpa_printf(MSG_DEBUG,
+			   "dbus: WPS.Start - Wrong P2PDeviceAddress length %d",
+			   len);
+		*reply = wpas_dbus_error_invalid_args(
+			message, "P2PDeviceAddress has wrong length");
 		return -1;
 	}
 	return 0;
@@ -249,54 +247,54 @@
 		dbus_message_iter_next(&dict_iter);
 	}
 
+#ifdef CONFIG_AP
+	if (wpa_s->ap_iface && params.type == 1) {
+		if (params.pin == NULL) {
+			wpa_printf(MSG_DEBUG,
+				   "dbus: WPS.Start - Pin required for registrar role");
+			return wpas_dbus_error_invalid_args(
+				message, "Pin required for registrar role.");
+		}
+		ret = wpa_supplicant_ap_wps_pin(wpa_s,
+						params.bssid,
+						params.pin,
+						npin, sizeof(npin), 0);
+	} else if (wpa_s->ap_iface) {
+		ret = wpa_supplicant_ap_wps_pbc(wpa_s,
+						params.bssid,
+						params.p2p_dev_addr);
+	} else
+#endif /* CONFIG_AP */
 	if (params.role == 0) {
 		wpa_printf(MSG_DEBUG, "dbus: WPS.Start - Role not specified");
 		return wpas_dbus_error_invalid_args(message,
 						    "Role not specified");
-	} else if (params.role == 1 && params.type == 0) {
+	} else if (params.role == 2) {
+		if (params.pin == NULL) {
+			wpa_printf(MSG_DEBUG,
+				   "dbus: WPS.Start - Pin required for registrar role");
+			return wpas_dbus_error_invalid_args(
+				message, "Pin required for registrar role.");
+		}
+		ret = wpas_wps_start_reg(wpa_s, params.bssid, params.pin,
+					 NULL);
+	} else if (params.type == 0) {
 		wpa_printf(MSG_DEBUG, "dbus: WPS.Start - Type not specified");
 		return wpas_dbus_error_invalid_args(message,
 						    "Type not specified");
-	} else if (params.role == 2 && params.pin == NULL) {
-		wpa_printf(MSG_DEBUG, "dbus: WPS.Start - Pin required for "
-			   "registrar role");
-		return wpas_dbus_error_invalid_args(
-			message, "Pin required for registrar role.");
-	}
-
-	if (params.role == 2)
-		ret = wpas_wps_start_reg(wpa_s, params.bssid, params.pin,
-					 NULL);
-	else if (params.type == 1) {
-#ifdef CONFIG_AP
-		if (wpa_s->ap_iface)
-			ret = wpa_supplicant_ap_wps_pin(wpa_s,
-							params.bssid,
-							params.pin,
-							npin, sizeof(npin), 0);
-		else
-#endif /* CONFIG_AP */
-		{
-			ret = wpas_wps_start_pin(wpa_s, params.bssid,
-						 params.pin, 0,
-						 DEV_PW_DEFAULT);
-			if (ret > 0)
-				os_snprintf(npin, sizeof(npin), "%08d", ret);
-		}
+	} else if (params.type == 1) {
+		ret = wpas_wps_start_pin(wpa_s, params.bssid,
+					 params.pin, 0,
+					 DEV_PW_DEFAULT);
+		if (ret > 0)
+			os_snprintf(npin, sizeof(npin), "%08d", ret);
 	} else {
-#ifdef CONFIG_AP
-		if (wpa_s->ap_iface)
-			ret = wpa_supplicant_ap_wps_pbc(wpa_s,
-							params.bssid,
-							params.p2p_dev_addr);
-		else
-#endif /* CONFIG_AP */
 		ret = wpas_wps_start_pbc(wpa_s, params.bssid, 0);
 	}
 
 	if (ret < 0) {
-		wpa_printf(MSG_DEBUG, "dbus: WPS.Start wpas_wps_failed in "
-			   "role %s and key %s",
+		wpa_printf(MSG_DEBUG,
+			   "dbus: WPS.Start wpas_wps_failed in role %s and key %s",
 			   (params.role == 1 ? "enrollee" : "registrar"),
 			   (params.type == 0 ? "" :
 			    (params.type == 1 ? "pin" : "pbc")));
@@ -305,31 +303,16 @@
 	}
 
 	reply = dbus_message_new_method_return(message);
-	if (!reply) {
-		return dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY,
-					      NULL);
-	}
+	if (!reply)
+		return wpas_dbus_error_no_memory(message);
 
 	dbus_message_iter_init_append(reply, &iter);
-	if (!wpa_dbus_dict_open_write(&iter, &dict_iter)) {
+	if (!wpa_dbus_dict_open_write(&iter, &dict_iter) ||
+	    (os_strlen(npin) > 0 &&
+	     !wpa_dbus_dict_append_string(&dict_iter, "Pin", npin)) ||
+	    !wpa_dbus_dict_close_write(&iter, &dict_iter)) {
 		dbus_message_unref(reply);
-		return dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY,
-					      NULL);
-	}
-
-	if (os_strlen(npin) > 0) {
-		if (!wpa_dbus_dict_append_string(&dict_iter, "Pin", npin)) {
-			dbus_message_unref(reply);
-			return dbus_message_new_error(message,
-						      DBUS_ERROR_NO_MEMORY,
-						      NULL);
-		}
-	}
-
-	if (!wpa_dbus_dict_close_write(&iter, &dict_iter)) {
-		dbus_message_unref(reply);
-		return dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY,
-					      NULL);
+		return wpas_dbus_error_no_memory(message);
 	}
 
 	return reply;
@@ -337,6 +320,26 @@
 
 
 /**
+ * wpas_dbus_handler_wps_cancel - Cancel ongoing WPS configuration
+ * @message: Pointer to incoming dbus message
+ * @wpa_s: %wpa_supplicant data structure
+ * Returns: NULL on success or DBus error on failure
+ *
+ * Handler for "Cancel" method call. Returns NULL if WPS cancel successfull
+ * or DBus error on WPS cancel failure
+ */
+DBusMessage * wpas_dbus_handler_wps_cancel(DBusMessage *message,
+					   struct wpa_supplicant *wpa_s)
+{
+	if (wpas_wps_cancel(wpa_s))
+		return wpas_dbus_error_unknown_error(message,
+						     "WPS cancel failed");
+
+	return NULL;
+}
+
+
+/**
  * wpas_dbus_getter_process_credentials - Check if credentials are processed
  * @message: Pointer to incoming dbus message
  * @wpa_s: %wpa_supplicant data structure
@@ -351,7 +354,8 @@
 						 void *user_data)
 {
 	struct wpa_supplicant *wpa_s = user_data;
-	dbus_bool_t process = (wpa_s->conf->wps_cred_processing != 1);
+	dbus_bool_t process = wpa_s->conf->wps_cred_processing != 1;
+
 	return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_BOOLEAN,
 						&process, error);
 }
@@ -374,11 +378,13 @@
 	struct wpa_supplicant *wpa_s = user_data;
 	dbus_bool_t process_credentials, old_pc;
 
+	if (!wpa_s->dbus_new_path)
+		return FALSE;
 	if (!wpas_dbus_simple_property_setter(iter, error, DBUS_TYPE_BOOLEAN,
 					      &process_credentials))
 		return FALSE;
 
-	old_pc = (wpa_s->conf->wps_cred_processing != 1);
+	old_pc = wpa_s->conf->wps_cred_processing != 1;
 	wpa_s->conf->wps_cred_processing = (process_credentials ? 2 : 1);
 
 	if ((wpa_s->conf->wps_cred_processing != 1) != old_pc)
@@ -408,6 +414,8 @@
 	struct wpa_supplicant *wpa_s = user_data;
 	char *methods = wpa_s->conf->config_methods;
 
+	if (methods == NULL)
+		methods = "";
 	return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_STRING,
 						&methods, error);
 }
diff --git a/wpa_supplicant/dbus/dbus_new_helpers.c b/wpa_supplicant/dbus/dbus_new_helpers.c
index 750522d..45623f3 100644
--- a/wpa_supplicant/dbus/dbus_new_helpers.c
+++ b/wpa_supplicant/dbus/dbus_new_helpers.c
@@ -15,6 +15,7 @@
 #include "dbus_common_i.h"
 #include "dbus_new.h"
 #include "dbus_new_helpers.h"
+#include "dbus_new_handlers.h"
 #include "dbus_dict_helpers.h"
 
 
@@ -45,8 +46,13 @@
 			goto error;
 
 		/* An error getting a property fails the request entirely */
-		if (!dsc->getter(&entry_iter, error, user_data))
+		if (!dsc->getter(&entry_iter, error, user_data)) {
+			wpa_printf(MSG_INFO,
+				   "dbus: %s dbus_interface=%s dbus_property=%s getter failed",
+				   __func__, dsc->dbus_interface,
+				   dsc->dbus_property);
 			return FALSE;
+		}
 
 		if (!dbus_message_iter_close_container(dict_iter, &entry_iter))
 			goto error;
@@ -73,46 +79,36 @@
  * with properties names as keys and theirs values as values.
  */
 static DBusMessage * get_all_properties(DBusMessage *message, char *interface,
-				        struct wpa_dbus_object_desc *obj_dsc)
+					struct wpa_dbus_object_desc *obj_dsc)
 {
 	DBusMessage *reply;
 	DBusMessageIter iter, dict_iter;
 	DBusError error;
 
 	reply = dbus_message_new_method_return(message);
-	if (reply == NULL) {
-		wpa_printf(MSG_ERROR, "%s: out of memory creating dbus reply",
-			   __func__);
-		return NULL;
-	}
+	if (reply == NULL)
+		return wpas_dbus_error_no_memory(message);
 
 	dbus_message_iter_init_append(reply, &iter);
 	if (!wpa_dbus_dict_open_write(&iter, &dict_iter)) {
-		wpa_printf(MSG_ERROR, "%s: out of memory creating reply",
-			   __func__);
 		dbus_message_unref(reply);
-		reply = dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY,
-					       "out of memory");
-		return reply;
+		return wpas_dbus_error_no_memory(message);
 	}
 
 	dbus_error_init(&error);
 	if (!fill_dict_with_properties(&dict_iter, obj_dsc->properties,
-				       interface, obj_dsc->user_data, &error))
-	{
+				       interface, obj_dsc->user_data, &error)) {
 		dbus_message_unref(reply);
-		reply = wpas_dbus_reply_new_from_error(message, &error,
-						       DBUS_ERROR_INVALID_ARGS,
-						       "No readable properties"
-						       " in this interface");
+		reply = wpas_dbus_reply_new_from_error(
+			message, &error, DBUS_ERROR_INVALID_ARGS,
+			"No readable properties in this interface");
 		dbus_error_free(&error);
 		return reply;
 	}
 
 	if (!wpa_dbus_dict_close_write(&iter, &dict_iter)) {
 		dbus_message_unref(reply);
-		return dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY,
-					      "out of memory");
+		return wpas_dbus_error_no_memory(message);
 	}
 
 	return reply;
@@ -135,8 +131,9 @@
 	for (arg = method_dsc->args; arg && arg->name; arg++) {
 		if (arg->dir == ARG_IN) {
 			size_t blen = registered_sig + MAX_SIG_LEN - pos;
+
 			ret = os_snprintf(pos, blen, "%s", arg->type);
-			if (ret < 0 || (size_t) ret >= blen)
+			if (os_snprintf_error(blen, ret))
 				return 0;
 			pos += ret;
 		}
@@ -270,10 +267,13 @@
 	}
 
 	if (os_strncmp(WPA_DBUS_PROPERTIES_GET, method,
-		       WPAS_DBUS_METHOD_SIGNAL_PROP_MAX) == 0)
+		       WPAS_DBUS_METHOD_SIGNAL_PROP_MAX) == 0) {
+		wpa_printf(MSG_MSGDUMP, "%s: Get(%s)", __func__, property);
 		return properties_get(message, property_dsc,
 				      obj_dsc->user_data);
+	}
 
+	wpa_printf(MSG_MSGDUMP, "%s: Set(%s)", __func__, property);
 	return properties_set(message, property_dsc, obj_dsc->user_data);
 }
 
@@ -295,8 +295,7 @@
 	    !os_strncmp(WPA_DBUS_PROPERTIES_GETALL, method,
 			WPAS_DBUS_METHOD_SIGNAL_PROP_MAX)) {
 		/* First argument: interface name (DBUS_TYPE_STRING) */
-		if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
-		{
+		if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING) {
 			return dbus_message_new_error(message,
 						      DBUS_ERROR_INVALID_ARGS,
 						      NULL);
@@ -352,8 +351,7 @@
 					      NULL);
 	}
 
-	return method_dsc->method_handler(message,
-					  obj_dsc->user_data);
+	return method_dsc->method_handler(message, obj_dsc->user_data);
 }
 
 
@@ -388,8 +386,9 @@
 	if (!method || !path || !msg_interface)
 		return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
 
-	wpa_printf(MSG_MSGDUMP, "dbus: %s.%s (%s)",
-		   msg_interface, method, path);
+	wpa_printf(MSG_MSGDUMP, "dbus: %s.%s (%s) [%s]",
+		   msg_interface, method, path,
+		   dbus_message_get_signature(message));
 
 	/* if message is introspection method call */
 	if (!os_strncmp(WPA_DBUS_INTROSPECTION_METHOD, method,
@@ -401,8 +400,7 @@
 #else /* CONFIG_CTRL_IFACE_DBUS_INTRO */
 		reply = dbus_message_new_error(
 			message, DBUS_ERROR_UNKNOWN_METHOD,
-			"wpa_supplicant was compiled without "
-			"introspection support.");
+			"wpa_supplicant was compiled without introspection support.");
 #endif /* CONFIG_CTRL_IFACE_DBUS_INTRO */
 	} else if (!os_strncmp(WPA_DBUS_PROPERTIES_INTERFACE, msg_interface,
 			     WPAS_DBUS_INTERFACE_MAX)) {
@@ -455,6 +453,7 @@
 	free_dbus_object_desc(obj_dsc);
 }
 
+
 /**
  * wpa_dbus_ctrl_iface_init - Initialize dbus control interface
  * @application_data: Pointer to application specific data structure
@@ -482,30 +481,28 @@
 	obj_desc->path = os_strdup(dbus_path);
 
 	/* Register the message handler for the global dbus interface */
-	if (!dbus_connection_register_object_path(iface->con,
-						  dbus_path, &wpa_vtable,
-						  obj_desc)) {
-		wpa_printf(MSG_ERROR, "dbus: Could not set up message "
-			   "handler");
+	if (!dbus_connection_register_object_path(iface->con, dbus_path,
+						  &wpa_vtable, obj_desc)) {
+		wpa_printf(MSG_ERROR, "dbus: Could not set up message handler");
 		return -1;
 	}
 
 	/* Register our service with the message bus */
 	dbus_error_init(&error);
-	switch (dbus_bus_request_name(iface->con, dbus_service,
-				      0, &error)) {
+	switch (dbus_bus_request_name(iface->con, dbus_service, 0, &error)) {
 	case DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER:
 		ret = 0;
 		break;
 	case DBUS_REQUEST_NAME_REPLY_EXISTS:
 	case DBUS_REQUEST_NAME_REPLY_IN_QUEUE:
 	case DBUS_REQUEST_NAME_REPLY_ALREADY_OWNER:
-		wpa_printf(MSG_ERROR, "dbus: Could not request service name: "
-			   "already registered");
+		wpa_printf(MSG_ERROR,
+			   "dbus: Could not request service name: already registered");
 		break;
 	default:
-		wpa_printf(MSG_ERROR, "dbus: Could not request service name: "
-			   "%s %s", error.name, error.message);
+		wpa_printf(MSG_ERROR,
+			   "dbus: Could not request service name: %s %s",
+			   error.name, error.message);
 		break;
 	}
 	dbus_error_free(&error);
@@ -529,14 +526,12 @@
  *
  * Registers a new interface with dbus and assigns it a dbus object path.
  */
-int wpa_dbus_register_object_per_iface(
-	struct wpas_dbus_priv *ctrl_iface,
-	const char *path, const char *ifname,
-	struct wpa_dbus_object_desc *obj_desc)
+int wpa_dbus_register_object_per_iface(struct wpas_dbus_priv *ctrl_iface,
+				       const char *path, const char *ifname,
+				       struct wpa_dbus_object_desc *obj_desc)
 {
 	DBusConnection *con;
 	DBusError error;
-
 	DBusObjectPathVTable vtable = {
 		&free_dbus_object_desc_cb, &message_handler,
 		NULL, NULL, NULL, NULL
@@ -554,14 +549,12 @@
 	/* Register the message handler for the interface functions */
 	if (!dbus_connection_try_register_object_path(con, path, &vtable,
 						      obj_desc, &error)) {
-		if (!os_strcmp(error.name, DBUS_ERROR_OBJECT_PATH_IN_USE)) {
+		if (os_strcmp(error.name, DBUS_ERROR_OBJECT_PATH_IN_USE) == 0) {
 			wpa_printf(MSG_DEBUG, "dbus: %s", error.message);
 		} else {
-			wpa_printf(MSG_ERROR, "dbus: Could not set up message "
-				   "handler for interface %s object %s",
-				   ifname, path);
-			wpa_printf(MSG_ERROR, "dbus error: %s", error.name);
-			wpa_printf(MSG_ERROR, "dbus: %s", error.message);
+			wpa_printf(MSG_ERROR,
+				   "dbus: Could not set up message handler for interface %s object %s (error: %s message: %s)",
+				   ifname, path, error.name, error.message);
 		}
 		dbus_error_free(&error);
 		return -1;
@@ -591,8 +584,9 @@
 
 	dbus_connection_get_object_path_data(con, path, (void **) &obj_desc);
 	if (!obj_desc) {
-		wpa_printf(MSG_ERROR, "dbus: %s: Could not obtain object's "
-			   "private data: %s", __func__, path);
+		wpa_printf(MSG_ERROR,
+			   "dbus: %s: Could not obtain object's private data: %s",
+			   __func__, path);
 		return 0;
 	}
 
@@ -626,24 +620,22 @@
 
 		if (!dbus_message_iter_open_container(dict_iter,
 						      DBUS_TYPE_DICT_ENTRY,
-						      NULL, &entry_iter))
-			return FALSE;
-
-		if (!dbus_message_iter_append_basic(&entry_iter,
+						      NULL, &entry_iter) ||
+		    !dbus_message_iter_append_basic(&entry_iter,
 						    DBUS_TYPE_STRING,
 						    &dsc->dbus_property))
 			return FALSE;
 
 		dbus_error_init(&error);
 		if (!dsc->getter(&entry_iter, &error, obj_dsc->user_data)) {
-			if (dbus_error_is_set (&error)) {
-				wpa_printf(MSG_ERROR, "dbus: %s: Cannot get "
-					   "new value of property %s: (%s) %s",
-				           __func__, dsc->dbus_property,
-				           error.name, error.message);
+			if (dbus_error_is_set(&error)) {
+				wpa_printf(MSG_ERROR,
+					   "dbus: %s: Cannot get new value of property %s: (%s) %s",
+					   __func__, dsc->dbus_property,
+					   error.name, error.message);
 			} else {
-				wpa_printf(MSG_ERROR, "dbus: %s: Cannot get "
-					   "new value of property %s",
+				wpa_printf(MSG_ERROR,
+					   "dbus: %s: Cannot get new value of property %s",
 					   __func__, dsc->dbus_property);
 			}
 			dbus_error_free(&error);
@@ -673,38 +665,23 @@
 	dbus_message_iter_init_append(msg, &signal_iter);
 
 	if (!dbus_message_iter_append_basic(&signal_iter, DBUS_TYPE_STRING,
-					    &interface))
-		goto err;
+					    &interface) ||
+	    /* Changed properties dict */
+	    !dbus_message_iter_open_container(&signal_iter, DBUS_TYPE_ARRAY,
+					      "{sv}", &dict_iter) ||
+	    !put_changed_properties(obj_dsc, interface, &dict_iter, 0) ||
+	    !dbus_message_iter_close_container(&signal_iter, &dict_iter) ||
+	    /* Invalidated properties array (empty) */
+	    !dbus_message_iter_open_container(&signal_iter, DBUS_TYPE_ARRAY,
+					      "s", &dict_iter) ||
+	    !dbus_message_iter_close_container(&signal_iter, &dict_iter)) {
+		wpa_printf(MSG_DEBUG, "dbus: %s: Failed to construct signal",
+			   __func__);
+	} else {
+		dbus_connection_send(con, msg, NULL);
+	}
 
-	/* Changed properties dict */
-	if (!dbus_message_iter_open_container(&signal_iter, DBUS_TYPE_ARRAY,
-					      "{sv}", &dict_iter))
-		goto err;
-
-	if (!put_changed_properties(obj_dsc, interface, &dict_iter, 0))
-		goto err;
-
-	if (!dbus_message_iter_close_container(&signal_iter, &dict_iter))
-		goto err;
-
-	/* Invalidated properties array (empty) */
-	if (!dbus_message_iter_open_container(&signal_iter, DBUS_TYPE_ARRAY,
-					      "s", &dict_iter))
-		goto err;
-
-	if (!dbus_message_iter_close_container(&signal_iter, &dict_iter))
-		goto err;
-
-	dbus_connection_send(con, msg, NULL);
-
-out:
 	dbus_message_unref(msg);
-	return;
-
-err:
-	wpa_printf(MSG_DEBUG, "dbus: %s: Failed to construct signal",
-		   __func__);
-	goto out;
 }
 
 
@@ -722,25 +699,16 @@
 	dbus_message_iter_init_append(msg, &signal_iter);
 
 	if (!dbus_message_iter_open_container(&signal_iter, DBUS_TYPE_ARRAY,
-					      "{sv}", &dict_iter))
-		goto err;
+					      "{sv}", &dict_iter) ||
+	    !put_changed_properties(obj_dsc, interface, &dict_iter, 1) ||
+	    !dbus_message_iter_close_container(&signal_iter, &dict_iter)) {
+		wpa_printf(MSG_DEBUG, "dbus: %s: Failed to construct signal",
+			   __func__);
+	} else {
+		dbus_connection_send(con, msg, NULL);
+	}
 
-	if (!put_changed_properties(obj_dsc, interface, &dict_iter, 1))
-		goto err;
-
-	if (!dbus_message_iter_close_container(&signal_iter, &dict_iter))
-		goto err;
-
-	dbus_connection_send(con, msg, NULL);
-
-out:
 	dbus_message_unref(msg);
-	return;
-
-err:
-	wpa_printf(MSG_DEBUG, "dbus: %s: Failed to construct signal",
-		   __func__);
-	goto out;
 }
 
 
@@ -772,8 +740,9 @@
 	DBusConnection *con = eloop_ctx;
 	struct wpa_dbus_object_desc *obj_desc = timeout_ctx;
 
-	wpa_printf(MSG_DEBUG, "dbus: %s: Timeout - sending changed properties "
-		   "of object %s", __func__, obj_desc->path);
+	wpa_printf(MSG_DEBUG,
+		   "dbus: %s: Timeout - sending changed properties of object %s",
+		   __func__, obj_desc->path);
 	wpa_dbus_flush_object_changed_properties(con, obj_desc->path);
 }
 
@@ -884,8 +853,9 @@
 	dbus_connection_get_object_path_data(iface->con, path,
 					     (void **) &obj_desc);
 	if (!obj_desc) {
-		wpa_printf(MSG_ERROR, "dbus: wpa_dbus_property_changed: "
-			   "could not obtain object's private data: %s", path);
+		wpa_printf(MSG_ERROR,
+			   "dbus: wpa_dbus_property_changed: could not obtain object's private data: %s",
+			   path);
 		return;
 	}
 
@@ -898,13 +868,14 @@
 		}
 
 	if (!dsc || !dsc->dbus_property) {
-		wpa_printf(MSG_ERROR, "dbus: wpa_dbus_property_changed: "
-			   "no property %s in object %s", property, path);
+		wpa_printf(MSG_ERROR,
+			   "dbus: wpa_dbus_property_changed: no property %s in object %s",
+			   property, path);
 		return;
 	}
 
 	if (!eloop_is_timeout_registered(flush_object_timeout_handler,
-					 iface->con, obj_desc->path)) {
+					 iface->con, obj_desc)) {
 		eloop_register_timeout(0, WPA_DBUS_SEND_PROP_CHANGED_TIMEOUT,
 				       flush_object_timeout_handler,
 				       iface->con, obj_desc);
@@ -936,8 +907,9 @@
 	dbus_connection_get_object_path_data(iface->con, path,
 					     (void **) &obj_desc);
 	if (!obj_desc) {
-		wpa_printf(MSG_ERROR, "dbus: %s: could not obtain object's "
-		           "private data: %s", __func__, path);
+		wpa_printf(MSG_ERROR,
+			   "dbus: %s: could not obtain object's private data: %s",
+			   __func__, path);
 		return FALSE;
 	}
 
@@ -951,10 +923,11 @@
 	if (!fill_dict_with_properties(&dict_iter, obj_desc->properties,
 				       interface, obj_desc->user_data,
 				       &error)) {
-		wpa_printf(MSG_ERROR, "dbus: %s: failed to get object"
-		           " properties: (%s) %s", __func__,
-		           dbus_error_is_set(&error) ? error.name : "none",
-		           dbus_error_is_set(&error) ? error.message : "none");
+		wpa_printf(MSG_ERROR,
+			   "dbus: %s: failed to get object properties: (%s) %s",
+			   __func__,
+			   dbus_error_is_set(&error) ? error.name : "none",
+			   dbus_error_is_set(&error) ? error.message : "none");
 		dbus_error_free(&error);
 		return FALSE;
 	}
@@ -965,29 +938,34 @@
 /**
  * wpas_dbus_new_decompose_object_path - Decompose an interface object path into parts
  * @path: The dbus object path
- * @p2p_persistent_group: indicates whether to parse the path as a P2P
- *                        persistent group object
- * @network: (out) the configured network this object path refers to, if any
- * @bssid: (out) the scanned bssid this object path refers to, if any
- * Returns: The object path of the network interface this path refers to
+ * @sep: Separating part (e.g., "Networks" or "PersistentGroups")
+ * @item: (out) The part following the specified separator, if any
+ * Returns: The object path of the interface this path refers to
  *
- * For a given object path, decomposes the object path into object id, network,
- * and BSSID parts, if those parts exist.
+ * For a given object path, decomposes the object path into object id and
+ * requested part, if those parts exist. The caller is responsible for freeing
+ * the returned value. The *item pointer points to that allocated value and must
+ * not be freed separately.
+ *
+ * As an example, path = "/fi/w1/wpa_supplicant1/Interfaces/1/Networks/0" and
+ * sep = "Networks" would result in "/fi/w1/wpa_supplicant1/Interfaces/1"
+ * getting returned and *items set to point to "0".
  */
-char *wpas_dbus_new_decompose_object_path(const char *path,
-					   int p2p_persistent_group,
-					   char **network,
-					   char **bssid)
+char * wpas_dbus_new_decompose_object_path(const char *path, const char *sep,
+					   char **item)
 {
 	const unsigned int dev_path_prefix_len =
 		os_strlen(WPAS_DBUS_NEW_PATH_INTERFACES "/");
 	char *obj_path_only;
-	char *next_sep;
+	char *pos;
+	size_t sep_len;
 
-	/* Be a bit paranoid about path */
-	if (!path || os_strncmp(path, WPAS_DBUS_NEW_PATH_INTERFACES "/",
-				dev_path_prefix_len))
-		return NULL;
+	*item = NULL;
+
+	/* Verify that this starts with our interface prefix */
+	if (os_strncmp(path, WPAS_DBUS_NEW_PATH_INTERFACES "/",
+		       dev_path_prefix_len) != 0)
+		return NULL; /* not our path */
 
 	/* Ensure there's something at the end of the path */
 	if ((path + dev_path_prefix_len)[0] == '\0')
@@ -997,39 +975,20 @@
 	if (obj_path_only == NULL)
 		return NULL;
 
-	next_sep = os_strchr(obj_path_only + dev_path_prefix_len, '/');
-	if (next_sep != NULL) {
-		const char *net_part = os_strstr(
-			next_sep, p2p_persistent_group ?
-			WPAS_DBUS_NEW_PERSISTENT_GROUPS_PART "/" :
-			WPAS_DBUS_NEW_NETWORKS_PART "/");
-		const char *bssid_part = os_strstr(
-			next_sep, WPAS_DBUS_NEW_BSSIDS_PART "/");
+	pos = obj_path_only + dev_path_prefix_len;
+	pos = os_strchr(pos, '/');
+	if (pos == NULL)
+		return obj_path_only; /* no next item on the path */
 
-		if (network && net_part) {
-			/* Deal with a request for a configured network */
-			const char *net_name = net_part +
-				os_strlen(p2p_persistent_group ?
-					  WPAS_DBUS_NEW_PERSISTENT_GROUPS_PART
-					  "/" :
-					  WPAS_DBUS_NEW_NETWORKS_PART "/");
-			*network = NULL;
-			if (os_strlen(net_name))
-				*network = os_strdup(net_name);
-		} else if (bssid && bssid_part) {
-			/* Deal with a request for a scanned BSSID */
-			const char *bssid_name = bssid_part +
-				os_strlen(WPAS_DBUS_NEW_BSSIDS_PART "/");
-			if (os_strlen(bssid_name))
-				*bssid = os_strdup(bssid_name);
-			else
-				*bssid = NULL;
-		}
+	 /* Separate network interface prefix from the path */
+	*pos++ = '\0';
 
-		/* Cut off interface object path before "/" */
-		*next_sep = '\0';
-	}
+	sep_len = os_strlen(sep);
+	if (os_strncmp(pos, sep, sep_len) != 0 || pos[sep_len] != '/')
+		return obj_path_only; /* no match */
 
+	 /* return a pointer to the requested item */
+	*item = pos + sep_len + 1;
 	return obj_path_only;
 }
 
diff --git a/wpa_supplicant/dbus/dbus_new_helpers.h b/wpa_supplicant/dbus/dbus_new_helpers.h
index 6d31ad5..6e2c1f1 100644
--- a/wpa_supplicant/dbus/dbus_new_helpers.h
+++ b/wpa_supplicant/dbus/dbus_new_helpers.h
@@ -12,13 +12,13 @@
 
 #include <dbus/dbus.h>
 
-typedef DBusMessage * (* WPADBusMethodHandler)(DBusMessage *message,
-					       void *user_data);
-typedef void (* WPADBusArgumentFreeFunction)(void *handler_arg);
+typedef DBusMessage * (*WPADBusMethodHandler)(DBusMessage *message,
+					      void *user_data);
+typedef void (*WPADBusArgumentFreeFunction)(void *handler_arg);
 
-typedef dbus_bool_t (* WPADBusPropertyAccessor)(DBusMessageIter *iter,
-                                                DBusError *error,
-						void *user_data);
+typedef dbus_bool_t (*WPADBusPropertyAccessor)(DBusMessageIter *iter,
+					       DBusError *error,
+					       void *user_data);
 
 struct wpa_dbus_object_desc {
 	DBusConnection *connection;
@@ -137,10 +137,8 @@
 DBusMessage * wpa_dbus_introspect(DBusMessage *message,
 				  struct wpa_dbus_object_desc *obj_dsc);
 
-char *wpas_dbus_new_decompose_object_path(const char *path,
-					   int p2p_persistent_group,
-					   char **network,
-					   char **bssid);
+char * wpas_dbus_new_decompose_object_path(const char *path, const char *sep,
+					   char **item);
 
 DBusMessage *wpas_dbus_reply_new_from_error(DBusMessage *message,
 					    DBusError *error,
diff --git a/wpa_supplicant/dbus/dbus_new_introspect.c b/wpa_supplicant/dbus/dbus_new_introspect.c
index 3b090c0..6209c67 100644
--- a/wpa_supplicant/dbus/dbus_new_introspect.c
+++ b/wpa_supplicant/dbus/dbus_new_introspect.c
@@ -37,14 +37,16 @@
 	iface = os_zalloc(sizeof(struct interfaces));
 	if (!iface)
 		return NULL;
+	iface->dbus_interface = os_strdup(dbus_interface);
 	iface->xml = wpabuf_alloc(6000);
-	if (iface->xml == NULL) {
+	if (iface->dbus_interface == NULL || iface->xml == NULL) {
+		os_free(iface->dbus_interface);
+		wpabuf_free(iface->xml);
 		os_free(iface);
 		return NULL;
 	}
 	wpabuf_printf(iface->xml, "<interface name=\"%s\">", dbus_interface);
 	dl_list_add_tail(list, &iface->list);
-	iface->dbus_interface = os_strdup(dbus_interface);
 	return iface;
 }
 
@@ -96,6 +98,7 @@
 {
 	const struct wpa_dbus_method_desc *dsc;
 	struct interfaces *iface;
+
 	for (dsc = methods; dsc && dsc->dbus_method; dsc++) {
 		iface = add_interface(list, dsc->dbus_interface);
 		if (iface)
@@ -110,6 +113,7 @@
 {
 	const struct wpa_dbus_signal_desc *dsc;
 	struct interfaces *iface;
+
 	for (dsc = signals; dsc && dsc->dbus_signal; dsc++) {
 		iface = add_interface(list, dsc->dbus_interface);
 		if (iface)
@@ -124,6 +128,7 @@
 {
 	const struct wpa_dbus_property_desc *dsc;
 	struct interfaces *iface;
+
 	for (dsc = properties; dsc && dsc->dbus_property; dsc++) {
 		iface = add_interface(list, dsc->dbus_interface);
 		if (iface)
@@ -154,14 +159,14 @@
 static void add_interfaces(struct dl_list *list, struct wpabuf *xml)
 {
 	struct interfaces *iface, *n;
+
 	dl_list_for_each_safe(iface, n, list, struct interfaces, list) {
 		if (wpabuf_len(iface->xml) + 20 < wpabuf_tailroom(xml)) {
 			wpabuf_put_buf(xml, iface->xml);
 			wpabuf_put_str(xml, "</interface>");
 		} else {
-			wpa_printf(MSG_DEBUG, "dbus: Not enough room for "
-				   "add_interfaces inspect data: tailroom %u, "
-				   "add %u",
+			wpa_printf(MSG_DEBUG,
+				   "dbus: Not enough room for add_interfaces inspect data: tailroom %u, add %u",
 				   (unsigned int) wpabuf_tailroom(xml),
 				   (unsigned int) wpabuf_len(iface->xml));
 		}
@@ -229,6 +234,7 @@
 				struct wpa_dbus_object_desc *obj_dsc)
 {
 	struct dl_list ifaces;
+
 	dl_list_init(&ifaces);
 	extract_interfaces(&ifaces, obj_dsc);
 	add_interfaces(&ifaces, xml);
@@ -270,6 +276,7 @@
 	reply = dbus_message_new_method_return(message);
 	if (reply) {
 		const char *intro_str = wpabuf_head(xml);
+
 		dbus_message_append_args(reply, DBUS_TYPE_STRING, &intro_str,
 					 DBUS_TYPE_INVALID);
 	}
diff --git a/wpa_supplicant/dbus/dbus_old.c b/wpa_supplicant/dbus/dbus_old.c
index 85d8a78..88227af 100644
--- a/wpa_supplicant/dbus/dbus_old.c
+++ b/wpa_supplicant/dbus/dbus_old.c
@@ -92,9 +92,9 @@
  */
 DBusMessage * wpas_dbus_new_invalid_iface_error(DBusMessage *message)
 {
-	return dbus_message_new_error(message, WPAS_ERROR_INVALID_IFACE,
-				      "wpa_supplicant knows nothing about "
-				      "this interface.");
+	return dbus_message_new_error(
+		message, WPAS_ERROR_INVALID_IFACE,
+		"wpa_supplicant knows nothing about this interface.");
 }
 
 
@@ -216,8 +216,12 @@
 	if (!msg_interface)
 		goto out;
 
+	wpa_printf(MSG_MSGDUMP, "dbus[old/iface]: %s.%s (%s) [%s]",
+		   msg_interface, method, path,
+		   dbus_message_get_signature(message));
+
 	iface_obj_path = wpas_dbus_decompose_object_path(path, &network,
-	                                                 &bssid);
+							 &bssid);
 	if (iface_obj_path == NULL) {
 		reply = wpas_dbus_new_invalid_iface_error(message);
 		goto out;
@@ -227,7 +231,7 @@
 	 * wpa_supplicant structure it's supposed to (which is wpa_s)
 	 */
 	if (wpa_supplicant_get_iface_by_dbus_path(wpa_s->global,
-	                                          iface_obj_path) != wpa_s) {
+						  iface_obj_path) != wpa_s) {
 		reply = wpas_dbus_new_invalid_iface_error(message);
 		goto out;
 	}
@@ -235,6 +239,7 @@
 	if (network && !strcmp(msg_interface, WPAS_DBUS_IFACE_NETWORK)) {
 		/* A method for one of this interface's configured networks */
 		int nid = strtoul(network, NULL, 10);
+
 		if (errno != EINVAL)
 			reply = wpas_dispatch_network_method(message, wpa_s,
 							     nid);
@@ -275,25 +280,25 @@
 			reply = wpas_dbus_iface_remove_blobs(message, wpa_s);
 #endif /* CONFIG_NO_CONFIG_BLOBS */
 #ifdef CONFIG_WPS
-		else if (!os_strcmp(method, "wpsPbc"))
+		else if (os_strcmp(method, "wpsPbc") == 0)
 			reply = wpas_dbus_iface_wps_pbc(message, wpa_s);
-		else if (!os_strcmp(method, "wpsPin"))
+		else if (os_strcmp(method, "wpsPin") == 0)
 			reply = wpas_dbus_iface_wps_pin(message, wpa_s);
-		else if (!os_strcmp(method, "wpsReg"))
+		else if (os_strcmp(method, "wpsReg") == 0)
 			reply = wpas_dbus_iface_wps_reg(message, wpa_s);
 #endif /* CONFIG_WPS */
-		else if (!os_strcmp(method, "flush"))
+		else if (os_strcmp(method, "flush") == 0)
 			reply = wpas_dbus_iface_flush(message, wpa_s);
 	}
 
 	/* If the message was handled, send back the reply */
+out:
 	if (reply) {
 		if (!dbus_message_get_no_reply(message))
 			dbus_connection_send(connection, reply, NULL);
 		dbus_message_unref(reply);
 	}
 
-out:
 	os_free(iface_obj_path);
 	os_free(network);
 	os_free(bssid);
@@ -328,6 +333,10 @@
 	if (!method || !path || !ctrl_iface || !msg_interface)
 		return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
 
+	wpa_printf(MSG_MSGDUMP, "dbus[old]: %s.%s (%s) [%s]",
+		   msg_interface, method, path,
+		   dbus_message_get_signature(message));
+
 	/* Validate the method interface */
 	if (strcmp(msg_interface, WPAS_DBUS_INTERFACE) != 0)
 		return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
@@ -374,15 +383,15 @@
 	DBusMessage *_signal;
 
 	/* Do nothing if the control interface is not turned on */
-	if (iface == NULL)
+	if (iface == NULL || !wpa_s->dbus_path)
 		return;
 
 	_signal = dbus_message_new_signal(wpa_s->dbus_path,
 					  WPAS_DBUS_IFACE_INTERFACE,
 					  "ScanResultsAvailable");
 	if (_signal == NULL) {
-		wpa_printf(MSG_ERROR, "dbus: Not enough memory to send scan "
-			   "results signal");
+		wpa_printf(MSG_ERROR,
+			   "dbus: Not enough memory to send scan results signal");
 		return;
 	}
 	dbus_connection_send(iface->con, _signal, NULL);
@@ -426,29 +435,21 @@
 					  "StateChange");
 	if (_signal == NULL) {
 		wpa_printf(MSG_ERROR,
-		           "dbus: wpa_supplicant_dbus_notify_state_change: "
-		           "could not create dbus signal; likely out of "
-		           "memory");
+			   "dbus: %s: could not create dbus signal; likely out of memory",
+			   __func__);
 		return;
 	}
 
 	new_state_str = wpa_supplicant_state_txt(new_state);
 	old_state_str = wpa_supplicant_state_txt(old_state);
-	if (new_state_str == NULL || old_state_str == NULL) {
-		wpa_printf(MSG_ERROR,
-		           "dbus: wpa_supplicant_dbus_notify_state_change: "
-		           "Could not convert state strings");
-		goto out;
-	}
 
 	if (!dbus_message_append_args(_signal,
-	                              DBUS_TYPE_STRING, &new_state_str,
-	                              DBUS_TYPE_STRING, &old_state_str,
-	                              DBUS_TYPE_INVALID)) {
+				      DBUS_TYPE_STRING, &new_state_str,
+				      DBUS_TYPE_STRING, &old_state_str,
+				      DBUS_TYPE_INVALID)) {
 		wpa_printf(MSG_ERROR,
-		           "dbus: wpa_supplicant_dbus_notify_state_change: "
-		           "Not enough memory to construct state change "
-		           "signal");
+			   "dbus: %s: Not enough memory to construct state change signal",
+			   __func__);
 		goto out;
 	}
 
@@ -473,25 +474,25 @@
 	dbus_bool_t scanning = wpa_s->scanning ? TRUE : FALSE;
 
 	/* Do nothing if the control interface is not turned on */
-	if (iface == NULL)
+	if (iface == NULL || !wpa_s->dbus_path)
 		return;
 
 	_signal = dbus_message_new_signal(wpa_s->dbus_path,
 					  WPAS_DBUS_IFACE_INTERFACE,
 					  "Scanning");
 	if (_signal == NULL) {
-		wpa_printf(MSG_ERROR, "dbus: Not enough memory to send scan "
-			   "results signal");
+		wpa_printf(MSG_ERROR,
+			   "dbus: Not enough memory to send scan results signal");
 		return;
 	}
 
 	if (dbus_message_append_args(_signal,
-	                             DBUS_TYPE_BOOLEAN, &scanning,
-	                             DBUS_TYPE_INVALID)) {
+				     DBUS_TYPE_BOOLEAN, &scanning,
+				     DBUS_TYPE_INVALID)) {
 		dbus_connection_send(iface->con, _signal, NULL);
 	} else {
-		wpa_printf(MSG_ERROR, "dbus: Not enough memory to construct "
-			   "signal");
+		wpa_printf(MSG_ERROR,
+			   "dbus: Not enough memory to construct signal");
 	}
 	dbus_message_unref(_signal);
 }
@@ -508,7 +509,7 @@
 	if (wpa_s->global == NULL)
 		return;
 	iface = wpa_s->global->dbus;
-	if (iface == NULL)
+	if (iface == NULL || !wpa_s->dbus_path)
 		return;
 
 	_signal = dbus_message_new_signal(wpa_s->dbus_path,
@@ -516,19 +517,18 @@
 					  "WpsCred");
 	if (_signal == NULL) {
 		wpa_printf(MSG_ERROR,
-		           "dbus: wpa_supplicant_dbus_notify_wps_cred: "
-		           "Could not create dbus signal; likely out of "
-		           "memory");
+			   "dbus: %s: Could not create dbus signal; likely out of memory",
+			   __func__);
 		return;
 	}
 
 	if (!dbus_message_append_args(_signal,
-	                              DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE,
+				      DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE,
 				      &cred->cred_attr, cred->cred_attr_len,
-	                              DBUS_TYPE_INVALID)) {
+				      DBUS_TYPE_INVALID)) {
 		wpa_printf(MSG_ERROR,
-		           "dbus: wpa_supplicant_dbus_notify_wps_cred: "
-		           "Not enough memory to construct signal");
+			   "dbus: %s: Not enough memory to construct signal",
+			   __func__);
 		goto out;
 	}
 
@@ -559,7 +559,7 @@
 	if (wpa_s->global == NULL)
 		return;
 	iface = wpa_s->global->dbus;
-	if (iface == NULL)
+	if (iface == NULL || !wpa_s->dbus_path)
 		return;
 
 	_signal = dbus_message_new_signal(wpa_s->dbus_path,
@@ -567,9 +567,8 @@
 					  "Certification");
 	if (_signal == NULL) {
 		wpa_printf(MSG_ERROR,
-		           "dbus: wpa_supplicant_dbus_notify_certification: "
-		           "Could not create dbus signal; likely out of "
-		           "memory");
+			   "dbus: %s: Could not create dbus signal; likely out of memory",
+			   __func__);
 		return;
 	}
 
@@ -578,15 +577,15 @@
 	cert_hex_len = cert ? wpabuf_len(cert) : 0;
 
 	if (!dbus_message_append_args(_signal,
-				      DBUS_TYPE_INT32,&depth,
+				      DBUS_TYPE_INT32, &depth,
 				      DBUS_TYPE_STRING, &subject,
-	                              DBUS_TYPE_STRING, &hash,
-	                              DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE,
+				      DBUS_TYPE_STRING, &hash,
+				      DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE,
 				      &cert_hex, cert_hex_len,
-	                              DBUS_TYPE_INVALID)) {
+				      DBUS_TYPE_INVALID)) {
 		wpa_printf(MSG_ERROR,
-		           "dbus: wpa_supplicant_dbus_notify_certification: "
-		           "Not enough memory to construct signal");
+			   "dbus: %s: Not enough memory to construct signal",
+			   __func__);
 		goto out;
 	}
 
@@ -618,8 +617,7 @@
 	if (!dbus_connection_register_object_path(iface->con,
 						  WPAS_DBUS_PATH, &wpas_vtable,
 						  iface)) {
-		wpa_printf(MSG_ERROR, "dbus: Could not set up message "
-			   "handler");
+		wpa_printf(MSG_ERROR, "dbus: Could not set up message handler");
 		return -1;
 	}
 
@@ -633,12 +631,13 @@
 	case DBUS_REQUEST_NAME_REPLY_EXISTS:
 	case DBUS_REQUEST_NAME_REPLY_IN_QUEUE:
 	case DBUS_REQUEST_NAME_REPLY_ALREADY_OWNER:
-		wpa_printf(MSG_ERROR, "dbus: Could not request service name: "
-			   "already registered");
+		wpa_printf(MSG_ERROR,
+			   "dbus: Could not request service name: already registered");
 		break;
 	default:
-		wpa_printf(MSG_ERROR, "dbus: Could not request service name: "
-			   "%s %s", error.name, error.message);
+		wpa_printf(MSG_ERROR,
+			   "dbus: Could not request service name: %s %s",
+			   error.name, error.message);
 		break;
 	}
 	dbus_error_free(&error);
@@ -687,8 +686,9 @@
 	/* Register the message handler for the interface functions */
 	if (!dbus_connection_register_fallback(con, wpa_s->dbus_path, &vtable,
 					       wpa_s)) {
-		wpa_printf(MSG_ERROR, "dbus: Could not set up message "
-			   "handler for interface %s", wpa_s->ifname);
+		wpa_printf(MSG_ERROR,
+			   "dbus: Could not set up message handler for interface %s",
+			   wpa_s->ifname);
 		return -1;
 	}
 
@@ -712,7 +712,7 @@
 	if (wpa_s == NULL || wpa_s->global == NULL)
 		return 0;
 	ctrl_iface = wpa_s->global->dbus;
-	if (ctrl_iface == NULL)
+	if (ctrl_iface == NULL || wpa_s->dbus_path == NULL)
 		return 0;
 
 	con = ctrl_iface->con;
@@ -738,7 +738,7 @@
 	struct wpa_supplicant *wpa_s;
 
 	for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) {
-		if (strcmp(wpa_s->dbus_path, path) == 0)
+		if (wpa_s->dbus_path && strcmp(wpa_s->dbus_path, path) == 0)
 			return wpa_s;
 	}
 	return NULL;
diff --git a/wpa_supplicant/dbus/dbus_old.h b/wpa_supplicant/dbus/dbus_old.h
index e668231..451a9f8 100644
--- a/wpa_supplicant/dbus/dbus_old.h
+++ b/wpa_supplicant/dbus/dbus_old.h
@@ -82,7 +82,7 @@
 					      const struct wpabuf *cert);
 
 char * wpas_dbus_decompose_object_path(const char *path, char **network,
-                                       char **bssid);
+				       char **bssid);
 
 int wpas_dbus_register_iface(struct wpa_supplicant *wpa_s);
 int wpas_dbus_unregister_iface(struct wpa_supplicant *wpa_s);
@@ -104,7 +104,12 @@
 {
 }
 
-#define wpa_supplicant_dbus_notify_state_change(w,n,o) do { } while (0)
+static inline void
+wpa_supplicant_dbus_notify_state_change(struct wpa_supplicant *wpa_s,
+					enum wpa_states new_state,
+					enum wpa_states old_state)
+{
+}
 
 static inline void
 wpa_supplicant_dbus_notify_wps_cred(struct wpa_supplicant *wpa_s,
diff --git a/wpa_supplicant/dbus/dbus_old_handlers.c b/wpa_supplicant/dbus/dbus_old_handlers.c
index 048158f..462c713 100644
--- a/wpa_supplicant/dbus/dbus_old_handlers.c
+++ b/wpa_supplicant/dbus/dbus_old_handlers.c
@@ -37,9 +37,9 @@
 {
 	DBusMessage *reply;
 
-	reply = dbus_message_new_error(message, WPAS_ERROR_INVALID_OPTS,
-				       "Did not receive correct message "
-				       "arguments.");
+	reply = dbus_message_new_error(
+		message, WPAS_ERROR_INVALID_OPTS,
+		"Did not receive correct message arguments.");
 	if (arg != NULL)
 		dbus_message_append_args(reply, DBUS_TYPE_STRING, &arg,
 					 DBUS_TYPE_INVALID);
@@ -112,28 +112,28 @@
 			if (!wpa_dbus_dict_get_entry(&iter_dict, &entry))
 				goto error;
 			if (!strcmp(entry.key, "driver") &&
-			    (entry.type == DBUS_TYPE_STRING)) {
+			    entry.type == DBUS_TYPE_STRING) {
 				os_free(driver);
 				driver = os_strdup(entry.str_value);
 				wpa_dbus_dict_entry_clear(&entry);
 				if (driver == NULL)
 					goto error;
 			} else if (!strcmp(entry.key, "driver-params") &&
-				   (entry.type == DBUS_TYPE_STRING)) {
+				   entry.type == DBUS_TYPE_STRING) {
 				os_free(driver_param);
 				driver_param = os_strdup(entry.str_value);
 				wpa_dbus_dict_entry_clear(&entry);
 				if (driver_param == NULL)
 					goto error;
 			} else if (!strcmp(entry.key, "config-file") &&
-				   (entry.type == DBUS_TYPE_STRING)) {
+				   entry.type == DBUS_TYPE_STRING) {
 				os_free(confname);
 				confname = os_strdup(entry.str_value);
 				wpa_dbus_dict_entry_clear(&entry);
 				if (confname == NULL)
 					goto error;
 			} else if (!strcmp(entry.key, "bridge-ifname") &&
-				   (entry.type == DBUS_TYPE_STRING)) {
+				   entry.type == DBUS_TYPE_STRING) {
 				os_free(bridge_ifname);
 				bridge_ifname = os_strdup(entry.str_value);
 				wpa_dbus_dict_entry_clear(&entry);
@@ -151,13 +151,13 @@
 	 * an error if we already control it.
 	 */
 	if (wpa_supplicant_get_iface(global, ifname) != NULL) {
-		reply = dbus_message_new_error(message,
-					       WPAS_ERROR_EXISTS_ERROR,
-					       "wpa_supplicant already "
-					       "controls this interface.");
+		reply = dbus_message_new_error(
+			message, WPAS_ERROR_EXISTS_ERROR,
+			"wpa_supplicant already controls this interface.");
 	} else {
 		struct wpa_supplicant *wpa_s;
 		struct wpa_interface iface;
+
 		os_memset(&iface, 0, sizeof(iface));
 		iface.ifname = ifname;
 		iface.driver = driver;
@@ -165,17 +165,17 @@
 		iface.confname = confname;
 		iface.bridge_ifname = bridge_ifname;
 		/* Otherwise, have wpa_supplicant attach to it. */
-		if ((wpa_s = wpa_supplicant_add_iface(global, &iface))) {
+		wpa_s = wpa_supplicant_add_iface(global, &iface, NULL);
+		if (wpa_s && wpa_s->dbus_path) {
 			const char *path = wpa_s->dbus_path;
+
 			reply = dbus_message_new_method_return(message);
 			dbus_message_append_args(reply, DBUS_TYPE_OBJECT_PATH,
-			                         &path, DBUS_TYPE_INVALID);
+						 &path, DBUS_TYPE_INVALID);
 		} else {
-			reply = dbus_message_new_error(message,
-						       WPAS_ERROR_ADD_ERROR,
-						       "wpa_supplicant "
-						       "couldn't grab this "
-						       "interface.");
+			reply = dbus_message_new_error(
+				message, WPAS_ERROR_ADD_ERROR,
+				"wpa_supplicant couldn't grab this interface.");
 		}
 	}
 
@@ -226,10 +226,9 @@
 	if (!wpa_supplicant_remove_iface(global, wpa_s, 0)) {
 		reply = wpas_dbus_new_success_reply(message);
 	} else {
-		reply = dbus_message_new_error(message,
-					       WPAS_ERROR_REMOVE_ERROR,
-					       "wpa_supplicant couldn't "
-					       "remove this interface.");
+		reply = dbus_message_new_error(
+			message, WPAS_ERROR_REMOVE_ERROR,
+			"wpa_supplicant couldn't remove this interface.");
 	}
 
 out:
@@ -256,14 +255,14 @@
 	struct wpa_supplicant *wpa_s;
 
 	if (!dbus_message_get_args(message, NULL,
-	                           DBUS_TYPE_STRING, &ifname,
-	                           DBUS_TYPE_INVALID)) {
+				   DBUS_TYPE_STRING, &ifname,
+				   DBUS_TYPE_INVALID)) {
 		reply = wpas_dbus_new_invalid_opts_error(message, NULL);
 		goto out;
 	}
 
 	wpa_s = wpa_supplicant_get_iface(global, ifname);
-	if (wpa_s == NULL) {
+	if (wpa_s == NULL || !wpa_s->dbus_path) {
 		reply = wpas_dbus_new_invalid_iface_error(message);
 		goto out;
 	}
@@ -271,8 +270,8 @@
 	path = wpa_s->dbus_path;
 	reply = dbus_message_new_method_return(message);
 	dbus_message_append_args(reply,
-	                         DBUS_TYPE_OBJECT_PATH, &path,
-	                         DBUS_TYPE_INVALID);
+				 DBUS_TYPE_OBJECT_PATH, &path,
+				 DBUS_TYPE_INVALID);
 
 out:
 	return reply;
@@ -298,10 +297,10 @@
 	dbus_bool_t debug_show_keys;
 
 	if (!dbus_message_get_args(message, NULL,
-	                           DBUS_TYPE_INT32, &debug_level,
-	                           DBUS_TYPE_BOOLEAN, &debug_timestamp,
-	                           DBUS_TYPE_BOOLEAN, &debug_show_keys,
-	                           DBUS_TYPE_INVALID)) {
+				   DBUS_TYPE_INT32, &debug_level,
+				   DBUS_TYPE_BOOLEAN, &debug_timestamp,
+				   DBUS_TYPE_BOOLEAN, &debug_show_keys,
+				   DBUS_TYPE_INVALID)) {
 		return wpas_dbus_new_invalid_opts_error(message, NULL);
 	}
 
@@ -355,6 +354,11 @@
 	DBusMessageIter sub_iter;
 	struct wpa_bss *bss;
 
+	if (!wpa_s->dbus_path)
+		return dbus_message_new_error(message,
+					      WPAS_ERROR_INTERNAL_ERROR,
+					      "no D-Bus interface available");
+
 	/* Create and initialize the return message */
 	reply = dbus_message_new_method_return(message);
 	dbus_message_iter_init_append(reply, &iter);
@@ -409,84 +413,56 @@
 {
 	DBusMessage *reply;
 	DBusMessageIter iter, iter_dict;
-	const u8 *ie;
+	const u8 *wpa_ie, *rsn_ie, *wps_ie;
 
 	/* Dump the properties into a dbus message */
 	reply = dbus_message_new_method_return(message);
 
+	wpa_ie = wpa_bss_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE);
+	rsn_ie = wpa_bss_get_ie(bss, WLAN_EID_RSN);
+	wps_ie = wpa_bss_get_vendor_ie(bss, WPS_IE_VENDOR_TYPE);
+
 	dbus_message_iter_init_append(reply, &iter);
-	if (!wpa_dbus_dict_open_write(&iter, &iter_dict))
-		goto error;
-
-	if (!wpa_dbus_dict_append_byte_array(&iter_dict, "bssid",
+	if (!wpa_dbus_dict_open_write(&iter, &iter_dict) ||
+	    !wpa_dbus_dict_append_byte_array(&iter_dict, "bssid",
 					     (const char *) bss->bssid,
-					     ETH_ALEN))
-		goto error;
-
-	ie = wpa_bss_get_ie(bss, WLAN_EID_SSID);
-	if (ie) {
-		if (!wpa_dbus_dict_append_byte_array(&iter_dict, "ssid",
-						     (const char *) (ie + 2),
-						     ie[1]))
-			goto error;
+					     ETH_ALEN) ||
+	    !wpa_dbus_dict_append_byte_array(&iter_dict, "ssid",
+					     (const char *) bss->ssid,
+					     bss->ssid_len) ||
+	    (wpa_ie &&
+	     !wpa_dbus_dict_append_byte_array(&iter_dict, "wpaie",
+					      (const char *) wpa_ie,
+					      wpa_ie[1] + 2)) ||
+	    (rsn_ie &&
+	     !wpa_dbus_dict_append_byte_array(&iter_dict, "rsnie",
+					      (const char *) rsn_ie,
+					      rsn_ie[1] + 2)) ||
+	    (wps_ie &&
+	     !wpa_dbus_dict_append_byte_array(&iter_dict, "wpsie",
+					     (const char *) wps_ie,
+					      wps_ie[1] + 2)) ||
+	    (bss->freq &&
+	     !wpa_dbus_dict_append_int32(&iter_dict, "frequency", bss->freq)) ||
+	    !wpa_dbus_dict_append_uint16(&iter_dict, "capabilities",
+					 bss->caps) ||
+	    (!(bss->flags & WPA_BSS_QUAL_INVALID) &&
+	     !wpa_dbus_dict_append_int32(&iter_dict, "quality", bss->qual)) ||
+	    (!(bss->flags & WPA_BSS_NOISE_INVALID) &&
+	     !wpa_dbus_dict_append_int32(&iter_dict, "noise", bss->noise)) ||
+	    (!(bss->flags & WPA_BSS_LEVEL_INVALID) &&
+	     !wpa_dbus_dict_append_int32(&iter_dict, "level", bss->level)) ||
+	    !wpa_dbus_dict_append_int32(&iter_dict, "maxrate",
+					wpa_bss_get_max_rate(bss) * 500000) ||
+	    !wpa_dbus_dict_close_write(&iter, &iter_dict)) {
+		if (reply)
+			dbus_message_unref(reply);
+		reply = dbus_message_new_error(
+			message, WPAS_ERROR_INTERNAL_ERROR,
+			"an internal error occurred returning BSSID properties.");
 	}
 
-	ie = wpa_bss_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE);
-	if (ie) {
-		if (!wpa_dbus_dict_append_byte_array(&iter_dict, "wpaie",
-						     (const char *) ie,
-						     ie[1] + 2))
-			goto error;
-	}
-
-	ie = wpa_bss_get_ie(bss, WLAN_EID_RSN);
-	if (ie) {
-		if (!wpa_dbus_dict_append_byte_array(&iter_dict, "rsnie",
-						     (const char *) ie,
-						     ie[1] + 2))
-			goto error;
-	}
-
-	ie = wpa_bss_get_vendor_ie(bss, WPS_IE_VENDOR_TYPE);
-	if (ie) {
-		if (!wpa_dbus_dict_append_byte_array(&iter_dict, "wpsie",
-						     (const char *) ie,
-						     ie[1] + 2))
-			goto error;
-	}
-
-	if (bss->freq) {
-		if (!wpa_dbus_dict_append_int32(&iter_dict, "frequency",
-						bss->freq))
-			goto error;
-	}
-	if (!wpa_dbus_dict_append_uint16(&iter_dict, "capabilities",
-					 bss->caps))
-		goto error;
-	if (!(bss->flags & WPA_BSS_QUAL_INVALID) &&
-	    !wpa_dbus_dict_append_int32(&iter_dict, "quality", bss->qual))
-		goto error;
-	if (!(bss->flags & WPA_BSS_NOISE_INVALID) &&
-	    !wpa_dbus_dict_append_int32(&iter_dict, "noise", bss->noise))
-		goto error;
-	if (!(bss->flags & WPA_BSS_LEVEL_INVALID) &&
-	    !wpa_dbus_dict_append_int32(&iter_dict, "level", bss->level))
-		goto error;
-	if (!wpa_dbus_dict_append_int32(&iter_dict, "maxrate",
-					wpa_bss_get_max_rate(bss) * 500000))
-		goto error;
-
-	if (!wpa_dbus_dict_close_write(&iter, &iter_dict))
-		goto error;
-
 	return reply;
-
-error:
-	if (reply)
-		dbus_message_unref(reply);
-	return dbus_message_new_error(message, WPAS_ERROR_INTERNAL_ERROR,
-				      "an internal error occurred returning "
-				      "BSSID properties.");
 }
 
 
@@ -524,7 +500,7 @@
 	/* EAP methods */
 	eap_methods = eap_get_names_as_string_array(&num_items);
 	if (eap_methods) {
-		dbus_bool_t success = FALSE;
+		dbus_bool_t success;
 		size_t i = 0;
 
 		success = wpa_dbus_dict_append_string_array(
@@ -546,6 +522,7 @@
 	if (res < 0) {
 		if (!strict) {
 			const char *args[] = {"CCMP", "TKIP", "NONE"};
+
 			if (!wpa_dbus_dict_append_string_array(
 				    &iter_dict, "pairwise", args,
 				    ARRAY_SIZE(args)))
@@ -555,28 +532,17 @@
 		if (!wpa_dbus_dict_begin_string_array(&iter_dict, "pairwise",
 						      &iter_dict_entry,
 						      &iter_dict_val,
-						      &iter_array))
-			goto error;
-
-		if (capa.enc & WPA_DRIVER_CAPA_ENC_CCMP) {
-			if (!wpa_dbus_dict_string_array_add_element(
-				    &iter_array, "CCMP"))
-				goto error;
-		}
-
-		if (capa.enc & WPA_DRIVER_CAPA_ENC_TKIP) {
-			if (!wpa_dbus_dict_string_array_add_element(
-				    &iter_array, "TKIP"))
-				goto error;
-		}
-
-		if (capa.key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_WPA_NONE) {
-			if (!wpa_dbus_dict_string_array_add_element(
-				    &iter_array, "NONE"))
-				goto error;
-		}
-
-		if (!wpa_dbus_dict_end_string_array(&iter_dict,
+						      &iter_array) ||
+		    ((capa.enc & WPA_DRIVER_CAPA_ENC_CCMP) &&
+		     !wpa_dbus_dict_string_array_add_element(
+			     &iter_array, "CCMP")) ||
+		    ((capa.enc & WPA_DRIVER_CAPA_ENC_TKIP) &&
+		     !wpa_dbus_dict_string_array_add_element(
+			     &iter_array, "TKIP")) ||
+		    ((capa.key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_WPA_NONE) &&
+		     !wpa_dbus_dict_string_array_add_element(
+			     &iter_array, "NONE")) ||
+		    !wpa_dbus_dict_end_string_array(&iter_dict,
 						    &iter_dict_entry,
 						    &iter_dict_val,
 						    &iter_array))
@@ -589,6 +555,7 @@
 			const char *args[] = {
 				"CCMP", "TKIP", "WEP104", "WEP40"
 			};
+
 			if (!wpa_dbus_dict_append_string_array(
 				    &iter_dict, "group", args,
 				    ARRAY_SIZE(args)))
@@ -601,31 +568,19 @@
 						      &iter_array))
 			goto error;
 
-		if (capa.enc & WPA_DRIVER_CAPA_ENC_CCMP) {
-			if (!wpa_dbus_dict_string_array_add_element(
-				    &iter_array, "CCMP"))
-				goto error;
-		}
-
-		if (capa.enc & WPA_DRIVER_CAPA_ENC_TKIP) {
-			if (!wpa_dbus_dict_string_array_add_element(
-				    &iter_array, "TKIP"))
-				goto error;
-		}
-
-		if (capa.enc & WPA_DRIVER_CAPA_ENC_WEP104) {
-			if (!wpa_dbus_dict_string_array_add_element(
-				    &iter_array, "WEP104"))
-				goto error;
-		}
-
-		if (capa.enc & WPA_DRIVER_CAPA_ENC_WEP40) {
-			if (!wpa_dbus_dict_string_array_add_element(
-				    &iter_array, "WEP40"))
-				goto error;
-		}
-
-		if (!wpa_dbus_dict_end_string_array(&iter_dict,
+		if (((capa.enc & WPA_DRIVER_CAPA_ENC_CCMP) &&
+		     !wpa_dbus_dict_string_array_add_element(
+			     &iter_array, "CCMP")) ||
+		    ((capa.enc & WPA_DRIVER_CAPA_ENC_TKIP) &&
+		     !wpa_dbus_dict_string_array_add_element(
+			     &iter_array, "TKIP")) ||
+		    ((capa.enc & WPA_DRIVER_CAPA_ENC_WEP104) &&
+		     !wpa_dbus_dict_string_array_add_element(
+			     &iter_array, "WEP104")) ||
+		    ((capa.enc & WPA_DRIVER_CAPA_ENC_WEP40) &&
+		     !wpa_dbus_dict_string_array_add_element(
+			     &iter_array, "WEP40")) ||
+		    !wpa_dbus_dict_end_string_array(&iter_dict,
 						    &iter_dict_entry,
 						    &iter_dict_val,
 						    &iter_array))
@@ -648,38 +603,23 @@
 		if (!wpa_dbus_dict_begin_string_array(&iter_dict, "key_mgmt",
 						      &iter_dict_entry,
 						      &iter_dict_val,
-						      &iter_array))
-			goto error;
-
-		if (!wpa_dbus_dict_string_array_add_element(&iter_array,
-							    "NONE"))
-			goto error;
-
-		if (!wpa_dbus_dict_string_array_add_element(&iter_array,
-							    "IEEE8021X"))
-			goto error;
-
-		if (capa.key_mgmt & (WPA_DRIVER_CAPA_KEY_MGMT_WPA |
-				     WPA_DRIVER_CAPA_KEY_MGMT_WPA2)) {
-			if (!wpa_dbus_dict_string_array_add_element(
-				    &iter_array, "WPA-EAP"))
-				goto error;
-		}
-
-		if (capa.key_mgmt & (WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK |
-				     WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK)) {
-			if (!wpa_dbus_dict_string_array_add_element(
-				    &iter_array, "WPA-PSK"))
-				goto error;
-		}
-
-		if (capa.key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_WPA_NONE) {
-			if (!wpa_dbus_dict_string_array_add_element(
-				    &iter_array, "WPA-NONE"))
-				goto error;
-		}
-
-		if (!wpa_dbus_dict_end_string_array(&iter_dict,
+						      &iter_array) ||
+		    !wpa_dbus_dict_string_array_add_element(&iter_array,
+							    "NONE") ||
+		    !wpa_dbus_dict_string_array_add_element(&iter_array,
+							    "IEEE8021X") ||
+		    ((capa.key_mgmt & (WPA_DRIVER_CAPA_KEY_MGMT_WPA |
+				       WPA_DRIVER_CAPA_KEY_MGMT_WPA2)) &&
+		     !wpa_dbus_dict_string_array_add_element(
+			     &iter_array, "WPA-EAP")) ||
+		    ((capa.key_mgmt & (WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK |
+				       WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK)) &&
+		     !wpa_dbus_dict_string_array_add_element(
+			     &iter_array, "WPA-PSK")) ||
+		    ((capa.key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_WPA_NONE) &&
+		     !wpa_dbus_dict_string_array_add_element(
+			     &iter_array, "WPA-NONE")) ||
+		    !wpa_dbus_dict_end_string_array(&iter_dict,
 						    &iter_dict_entry,
 						    &iter_dict_val,
 						    &iter_array))
@@ -690,6 +630,7 @@
 	if (res < 0) {
 		if (!strict) {
 			const char *args[] = { "RSN", "WPA" };
+
 			if (!wpa_dbus_dict_append_string_array(
 				    &iter_dict, "proto", args,
 				    ARRAY_SIZE(args)))
@@ -699,24 +640,16 @@
 		if (!wpa_dbus_dict_begin_string_array(&iter_dict, "proto",
 						      &iter_dict_entry,
 						      &iter_dict_val,
-						      &iter_array))
-			goto error;
-
-		if (capa.key_mgmt & (WPA_DRIVER_CAPA_KEY_MGMT_WPA2 |
-				     WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK)) {
-			if (!wpa_dbus_dict_string_array_add_element(
-				    &iter_array, "RSN"))
-				goto error;
-		}
-
-		if (capa.key_mgmt & (WPA_DRIVER_CAPA_KEY_MGMT_WPA |
-				     WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK)) {
-			if (!wpa_dbus_dict_string_array_add_element(
-				    &iter_array, "WPA"))
-				goto error;
-		}
-
-		if (!wpa_dbus_dict_end_string_array(&iter_dict,
+						      &iter_array) ||
+		    ((capa.key_mgmt & (WPA_DRIVER_CAPA_KEY_MGMT_WPA2 |
+				       WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK)) &&
+		     !wpa_dbus_dict_string_array_add_element(
+			     &iter_array, "RSN")) ||
+		    ((capa.key_mgmt & (WPA_DRIVER_CAPA_KEY_MGMT_WPA |
+				       WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK)) &&
+		     !wpa_dbus_dict_string_array_add_element(
+			     &iter_array, "WPA")) ||
+		    !wpa_dbus_dict_end_string_array(&iter_dict,
 						    &iter_dict_entry,
 						    &iter_dict_val,
 						    &iter_array))
@@ -727,6 +660,7 @@
 	if (res < 0) {
 		if (!strict) {
 			const char *args[] = { "OPEN", "SHARED", "LEAP" };
+
 			if (!wpa_dbus_dict_append_string_array(
 				    &iter_dict, "auth_alg", args,
 				    ARRAY_SIZE(args)))
@@ -736,28 +670,17 @@
 		if (!wpa_dbus_dict_begin_string_array(&iter_dict, "auth_alg",
 						      &iter_dict_entry,
 						      &iter_dict_val,
-						      &iter_array))
-			goto error;
-
-		if (capa.auth & (WPA_DRIVER_AUTH_OPEN)) {
-			if (!wpa_dbus_dict_string_array_add_element(
-				    &iter_array, "OPEN"))
-				goto error;
-		}
-
-		if (capa.auth & (WPA_DRIVER_AUTH_SHARED)) {
-			if (!wpa_dbus_dict_string_array_add_element(
-				    &iter_array, "SHARED"))
-				goto error;
-		}
-
-		if (capa.auth & (WPA_DRIVER_AUTH_LEAP)) {
-			if (!wpa_dbus_dict_string_array_add_element(
-				    &iter_array, "LEAP"))
-				goto error;
-		}
-
-		if (!wpa_dbus_dict_end_string_array(&iter_dict,
+						      &iter_array) ||
+		    ((capa.auth & WPA_DRIVER_AUTH_OPEN) &&
+		     !wpa_dbus_dict_string_array_add_element(
+			     &iter_array, "OPEN")) ||
+		    ((capa.auth & WPA_DRIVER_AUTH_SHARED) &&
+		     !wpa_dbus_dict_string_array_add_element(
+			     &iter_array, "SHARED")) ||
+		    ((capa.auth & WPA_DRIVER_AUTH_LEAP) &&
+		     !wpa_dbus_dict_string_array_add_element(
+			     &iter_array, "LEAP")) ||
+		    !wpa_dbus_dict_end_string_array(&iter_dict,
 						    &iter_dict_entry,
 						    &iter_dict_val,
 						    &iter_array))
@@ -772,9 +695,9 @@
 error:
 	if (reply)
 		dbus_message_unref(reply);
-	return dbus_message_new_error(message, WPAS_ERROR_INTERNAL_ERROR,
-				      "an internal error occurred returning "
-				      "interface capabilities.");
+	return dbus_message_new_error(
+		message, WPAS_ERROR_INTERNAL_ERROR,
+		"an internal error occurred returning interface capabilities.");
 }
 
 
@@ -790,15 +713,15 @@
 					  struct wpa_supplicant *wpa_s)
 {
 	DBusMessage *reply = NULL;
-	struct wpa_ssid *ssid;
+	struct wpa_ssid *ssid = NULL;
 	char path_buf[WPAS_DBUS_OBJECT_PATH_MAX], *path = path_buf;
 
-	ssid = wpa_config_add_network(wpa_s->conf);
+	if (wpa_s->dbus_path)
+		ssid = wpa_config_add_network(wpa_s->conf);
 	if (ssid == NULL) {
-		reply = dbus_message_new_error(message,
-					       WPAS_ERROR_ADD_NETWORK_ERROR,
-					       "wpa_supplicant could not add "
-					       "a network on this interface.");
+		reply = dbus_message_new_error(
+			message, WPAS_ERROR_ADD_NETWORK_ERROR,
+			"wpa_supplicant could not add a network on this interface.");
 		goto out;
 	}
 	wpas_notify_network_added(wpa_s, ssid);
@@ -838,21 +761,21 @@
 	struct wpa_ssid *ssid;
 
 	if (!dbus_message_get_args(message, NULL,
-	                           DBUS_TYPE_OBJECT_PATH, &op,
-	                           DBUS_TYPE_INVALID)) {
+				   DBUS_TYPE_OBJECT_PATH, &op,
+				   DBUS_TYPE_INVALID)) {
 		reply = wpas_dbus_new_invalid_opts_error(message, NULL);
 		goto out;
 	}
 
 	/* Extract the network ID */
 	iface = wpas_dbus_decompose_object_path(op, &net_id, NULL);
-	if (iface == NULL) {
+	if (iface == NULL || net_id == NULL) {
 		reply = wpas_dbus_new_invalid_network_error(message);
 		goto out;
 	}
 
 	/* Ensure the network is actually a child of this interface */
-	if (os_strcmp(iface, wpa_s->dbus_path) != 0) {
+	if (!wpa_s->dbus_path || os_strcmp(iface, wpa_s->dbus_path) != 0) {
 		reply = wpas_dbus_new_invalid_network_error(message);
 		goto out;
 	}
@@ -866,17 +789,17 @@
 
 	wpas_notify_network_removed(wpa_s, ssid);
 
-	if (wpa_config_remove_network(wpa_s->conf, id) < 0) {
-		reply = dbus_message_new_error(message,
-					       WPAS_ERROR_REMOVE_NETWORK_ERROR,
-					       "error removing the specified "
-					       "on this interface.");
-		goto out;
-	}
-
 	if (ssid == wpa_s->current_ssid)
 		wpa_supplicant_deauthenticate(wpa_s,
 					      WLAN_REASON_DEAUTH_LEAVING);
+
+	if (wpa_config_remove_network(wpa_s->conf, id) < 0) {
+		reply = dbus_message_new_error(
+			message, WPAS_ERROR_REMOVE_NETWORK_ERROR,
+			"error removing the specified on this interface.");
+		goto out;
+	}
+
 	reply = wpas_dbus_new_success_reply(message);
 
 out:
@@ -886,7 +809,7 @@
 }
 
 
-static const char *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
@@ -896,8 +819,9 @@
 static dbus_bool_t should_quote_opt(const char *key)
 {
 	int i = 0;
+
 	while (dont_quote[i] != NULL) {
-		if (strcmp(key, dont_quote[i]) == 0)
+		if (os_strcmp(key, dont_quote[i]) == 0)
 			return FALSE;
 		i++;
 	}
@@ -960,7 +884,7 @@
 			if (should_quote_opt(entry.key)) {
 				size = os_strlen(entry.str_value);
 				/* Zero-length option check */
-				if (size <= 0)
+				if (size == 0)
 					goto error;
 				size += 3;  /* For quotes and terminator */
 				value = os_zalloc(size);
@@ -968,7 +892,7 @@
 					goto error;
 				ret = os_snprintf(value, size, "\"%s\"",
 						  entry.str_value);
-				if (ret < 0 || (size_t) ret != (size - 1))
+				if (os_snprintf_error(size, ret))
 					goto error;
 			} else {
 				value = os_strdup(entry.str_value);
@@ -981,7 +905,7 @@
 				goto error;
 			ret = os_snprintf(value, size, "%u",
 					  entry.uint32_value);
-			if (ret <= 0)
+			if (os_snprintf_error(size, ret))
 				goto error;
 		} else if (entry.type == DBUS_TYPE_INT32) {
 			value = os_zalloc(size);
@@ -989,7 +913,7 @@
 				goto error;
 			ret = os_snprintf(value, size, "%d",
 					  entry.int32_value);
-			if (ret <= 0)
+			if (os_snprintf_error(size, ret))
 				goto error;
 		} else
 			goto error;
@@ -1102,7 +1026,8 @@
 			goto out;
 		}
 		/* Ensure the object path really points to this interface */
-		if (os_strcmp(iface_obj_path, wpa_s->dbus_path) != 0) {
+		if (network == NULL || !wpa_s->dbus_path ||
+		    os_strcmp(iface_obj_path, wpa_s->dbus_path) != 0) {
 			reply = wpas_dbus_new_invalid_network_error(message);
 			goto out;
 		}
@@ -1212,28 +1137,30 @@
 		if (!wpa_dbus_dict_get_entry(&iter_dict, &entry))
 			goto error;
 		if (!strcmp(entry.key, "opensc_engine_path") &&
-		    (entry.type == DBUS_TYPE_STRING)) {
+		    entry.type == DBUS_TYPE_STRING) {
 			os_free(opensc_engine_path);
 			opensc_engine_path = os_strdup(entry.str_value);
+			wpa_dbus_dict_entry_clear(&entry);
 			if (opensc_engine_path == NULL)
 				goto error;
 		} else if (!strcmp(entry.key, "pkcs11_engine_path") &&
-			   (entry.type == DBUS_TYPE_STRING)) {
+			   entry.type == DBUS_TYPE_STRING) {
 			os_free(pkcs11_engine_path);
 			pkcs11_engine_path = os_strdup(entry.str_value);
+			wpa_dbus_dict_entry_clear(&entry);
 			if (pkcs11_engine_path == NULL)
 				goto error;
 		} else if (!strcmp(entry.key, "pkcs11_module_path") &&
-				 (entry.type == DBUS_TYPE_STRING)) {
+				 entry.type == DBUS_TYPE_STRING) {
 			os_free(pkcs11_module_path);
 			pkcs11_module_path = os_strdup(entry.str_value);
+			wpa_dbus_dict_entry_clear(&entry);
 			if (pkcs11_module_path == NULL)
 				goto error;
 		} else {
 			wpa_dbus_dict_entry_clear(&entry);
 			goto error;
 		}
-		wpa_dbus_dict_entry_clear(&entry);
 	}
 
 	os_free(wpa_s->conf->opensc_engine_path);
@@ -1304,8 +1231,8 @@
 		dbus_message_append_args(reply, DBUS_TYPE_BOOLEAN, &scanning,
 					 DBUS_TYPE_INVALID);
 	} else {
-		wpa_printf(MSG_ERROR, "dbus: Not enough memory to return "
-			   "scanning state");
+		wpa_printf(MSG_ERROR,
+			   "dbus: Not enough memory to return scanning state");
 	}
 
 	return reply;
@@ -1378,7 +1305,7 @@
 		blob->len = entry.array_len;
 		os_memcpy(blob->data, (u8 *) entry.bytearray_value,
 				entry.array_len);
-		if (blob->name == NULL || blob->data == NULL) {
+		if (blob->name == NULL) {
 			wpa_config_free_blob(blob);
 			reply = dbus_message_new_error(
 				message, WPAS_ERROR_ADD_ERROR,
@@ -1417,8 +1344,8 @@
 
 	dbus_message_iter_init(message, &iter);
 
-	if ((dbus_message_iter_get_arg_type (&iter) != DBUS_TYPE_ARRAY) ||
-	    (dbus_message_iter_get_element_type (&iter) != DBUS_TYPE_STRING))
+	if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
+	    dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_STRING)
 		return wpas_dbus_new_invalid_opts_error(message, NULL);
 
 	dbus_message_iter_recurse(&iter, &array);
@@ -1428,8 +1355,7 @@
 		dbus_message_iter_get_basic(&array, &name);
 		if (!os_strlen(name))
 			err_msg = "Invalid blob name.";
-
-		if (wpa_config_remove_blob(wpa_s->conf, name) != 0)
+		else if (wpa_config_remove_blob(wpa_s->conf, name) != 0)
 			err_msg = "Error removing blob.";
 		else
 			wpas_notify_blob_removed(wpa_s, name);
diff --git a/wpa_supplicant/dbus/dbus_old_handlers.h b/wpa_supplicant/dbus/dbus_old_handlers.h
index 825bc6d..e60ad06 100644
--- a/wpa_supplicant/dbus/dbus_old_handlers.h
+++ b/wpa_supplicant/dbus/dbus_old_handlers.h
@@ -58,13 +58,13 @@
 					      struct wpa_ssid *ssid);
 
 DBusMessage * wpas_dbus_iface_select_network(DBusMessage *message,
-                                             struct wpa_supplicant *wpa_s);
+					     struct wpa_supplicant *wpa_s);
 
 DBusMessage * wpas_dbus_iface_disconnect(DBusMessage *message,
 					 struct wpa_supplicant *wpa_s);
 
 DBusMessage * wpas_dbus_iface_set_ap_scan(DBusMessage *message,
-                                          struct wpa_supplicant *wpa_s);
+					  struct wpa_supplicant *wpa_s);
 
 DBusMessage * wpas_dbus_iface_set_smartcard_modules(
 	DBusMessage *message, struct wpa_supplicant *wpa_s);
@@ -76,7 +76,7 @@
 					   struct wpa_supplicant *wpa_s);
 
 DBusMessage * wpas_dbus_iface_set_blobs(DBusMessage *message,
-				        struct wpa_supplicant *wpa_s);
+					struct wpa_supplicant *wpa_s);
 
 DBusMessage * wpas_dbus_iface_remove_blobs(DBusMessage *message,
 					   struct wpa_supplicant *wpa_s);
diff --git a/wpa_supplicant/dbus/dbus_old_handlers_wps.c b/wpa_supplicant/dbus/dbus_old_handlers_wps.c
index bb79382..5309a53 100644
--- a/wpa_supplicant/dbus/dbus_old_handlers_wps.c
+++ b/wpa_supplicant/dbus/dbus_old_handlers_wps.c
@@ -36,7 +36,7 @@
 				   DBUS_TYPE_INVALID))
 		return wpas_dbus_new_invalid_opts_error(message, NULL);
 
-	if (!os_strcmp(arg_bssid, "any"))
+	if (os_strcmp(arg_bssid, "any") == 0)
 		ret = wpas_wps_start_pbc(wpa_s, NULL, 0);
 	else if (!hwaddr_aton(arg_bssid, bssid))
 		ret = wpas_wps_start_pbc(wpa_s, bssid, 0);
@@ -46,10 +46,9 @@
 	}
 
 	if (ret < 0) {
-		return dbus_message_new_error(message,
-					      WPAS_ERROR_WPS_PBC_ERROR,
-					      "Could not start PBC "
-					      "negotiation");
+		return dbus_message_new_error(
+			message, WPAS_ERROR_WPS_PBC_ERROR,
+			"Could not start PBC negotiation");
 	}
 
 	return wpas_dbus_new_success_reply(message);
@@ -73,12 +72,13 @@
 	char *pin = NULL;
 	u8 bssid[ETH_ALEN], *_bssid = NULL;
 	int ret = 0;
+	char npin[9];
 
 	if (!dbus_message_get_args(message, NULL, DBUS_TYPE_STRING, &arg_bssid,
 				   DBUS_TYPE_STRING, &pin, DBUS_TYPE_INVALID))
 		return wpas_dbus_new_invalid_opts_error(message, NULL);
 
-	if (!os_strcmp(arg_bssid, "any"))
+	if (os_strcmp(arg_bssid, "any") == 0)
 		_bssid = NULL;
 	else if (!hwaddr_aton(arg_bssid, bssid))
 		_bssid = bssid;
@@ -104,15 +104,12 @@
 	if (reply == NULL)
 		return NULL;
 
-	if (ret == 0) {
-		dbus_message_append_args(reply, DBUS_TYPE_STRING, &pin,
-					 DBUS_TYPE_INVALID);
-	} else {
-		char npin[9];
+	if (ret > 0) {
 		os_snprintf(npin, sizeof(npin), "%08d", ret);
-		dbus_message_append_args(reply, DBUS_TYPE_STRING, &npin,
-					 DBUS_TYPE_INVALID);
+		pin = npin;
 	}
+	dbus_message_append_args(reply, DBUS_TYPE_STRING, &pin,
+				 DBUS_TYPE_INVALID);
 	return reply;
 }
 
@@ -138,9 +135,7 @@
 				   DBUS_TYPE_STRING, &pin, DBUS_TYPE_INVALID))
 		return wpas_dbus_new_invalid_opts_error(message, NULL);
 
-	if (!os_strcmp(arg_bssid, "any"))
-		ret = wpas_wps_start_reg(wpa_s, NULL, pin, NULL);
-	else if (!hwaddr_aton(arg_bssid, bssid))
+	if (!hwaddr_aton(arg_bssid, bssid))
 		ret = wpas_wps_start_reg(wpa_s, bssid, pin, NULL);
 	else {
 		return wpas_dbus_new_invalid_opts_error(message,
@@ -149,7 +144,7 @@
 
 	if (ret < 0) {
 		return dbus_message_new_error(message,
-					      WPAS_ERROR_WPS_PBC_ERROR,
+					      WPAS_ERROR_WPS_REG_ERROR,
 					      "Could not request credentials");
 	}
 
diff --git a/wpa_supplicant/defconfig b/wpa_supplicant/defconfig
index 94c94b1..7f627fd 100644
--- a/wpa_supplicant/defconfig
+++ b/wpa_supplicant/defconfig
@@ -67,9 +67,6 @@
 # wpa_supplicant.
 # CONFIG_USE_NDISUIO=y
 
-# Driver interface for development testing
-#CONFIG_DRIVER_TEST=y
-
 # Driver interface for wired Ethernet drivers
 CONFIG_DRIVER_WIRED=y
 
diff --git a/wpa_supplicant/doc/docbook/eapol_test.sgml b/wpa_supplicant/doc/docbook/eapol_test.sgml
index fec174b..e9af6d9 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-2014,
+    <para>wpa_supplicant is copyright (c) 2003-2015,
     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 860b5a0..afb8c3b 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-2014,
+    <para>wpa_supplicant is copyright (c) 2003-2015,
     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 142e1ab..47947c1 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-2014,
+    <para>wpa_supplicant is copyright (c) 2003-2015,
     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 f6ef8f1..5f7b49d 100644
--- a/wpa_supplicant/doc/docbook/wpa_gui.sgml
+++ b/wpa_supplicant/doc/docbook/wpa_gui.sgml
@@ -16,7 +16,9 @@
       <command>wpa_gui</command>
       <arg>-p <replaceable>path to ctrl sockets</replaceable></arg>
       <arg>-i <replaceable>ifname</replaceable></arg>
+      <arg>-m <replaceable>seconds</replaceable></arg>
       <arg>-t</arg>
+      <arg>-q</arg>
     </cmdsynopsis>
   </refsynopsisdiv>
 
@@ -51,12 +53,27 @@
       </varlistentry>
 
       <varlistentry>
+	<term>-m seconds</term>
+
+	<listitem><para>Set the update interval in seconds for the signal
+	strength meter. This value must be a positive integer, otherwise
+	meter is not enabled (default behavior).</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
 	<term>-t</term>
 
         <listitem><para>Start program in the system tray only (if the window
 	manager supports it). By default the main status window is
 	shown.</para></listitem>
       </varlistentry>
+
+      <varlistentry>
+	<term>-q</term>
+
+        <listitem><para>Run program in the quiet mode - do not display tray
+	icon pop-up messages.</para></listitem>
+      </varlistentry>
     </variablelist>
   </refsect1>
   <refsect1>
@@ -74,7 +91,7 @@
   </refsect1>
   <refsect1>
     <title>Legal</title>
-    <para>wpa_supplicant is copyright (c) 2003-2014,
+    <para>wpa_supplicant is copyright (c) 2003-2015,
     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 3b4360b..b381e40 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-2014,
+    <para>wpa_supplicant is copyright (c) 2003-2015,
     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 9c114cc..d13a5db 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-2014,
+    <para>wpa_supplicant is copyright (c) 2003-2015,
     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 182060d..46c21b5 100644
--- a/wpa_supplicant/doc/docbook/wpa_supplicant.sgml
+++ b/wpa_supplicant/doc/docbook/wpa_supplicant.sgml
@@ -469,7 +469,7 @@
 	  <para>Enable DBus control interface. If enabled, interface
 	  definitions may be omitted. (This is only available
 	  if <command>wpa_supplicant</command> was built with
-	  the <literal>CONFIG_DBUS</literal> option.)</para>0
+	  the <literal>CONFIG_DBUS</literal> option.)</para>
 	</listitem>
       </varlistentry>
 
@@ -736,7 +736,7 @@
   </refsect1>
   <refsect1>
     <title>Legal</title>
-    <para>wpa_supplicant is copyright (c) 2003-2014,
+    <para>wpa_supplicant is copyright (c) 2003-2015,
     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 649de9b..1fcb180 100644
--- a/wpa_supplicant/driver_i.h
+++ b/wpa_supplicant/driver_i.h
@@ -1,6 +1,6 @@
 /*
  * wpa_supplicant - Internal driver interface wrappers
- * Copyright (c) 2003-2009, 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.
@@ -65,9 +65,35 @@
 	return -1;
 }
 
+static inline int wpa_drv_init_mesh(struct wpa_supplicant *wpa_s)
+{
+	if (wpa_s->driver->init_mesh)
+		return wpa_s->driver->init_mesh(wpa_s->drv_priv);
+	return -1;
+}
+
+static inline int wpa_drv_join_mesh(struct wpa_supplicant *wpa_s,
+				    struct wpa_driver_mesh_join_params *params)
+{
+	if (wpa_s->driver->join_mesh)
+		return wpa_s->driver->join_mesh(wpa_s->drv_priv, params);
+	return -1;
+}
+
+static inline int wpa_drv_leave_mesh(struct wpa_supplicant *wpa_s)
+{
+	if (wpa_s->driver->leave_mesh)
+		return wpa_s->driver->leave_mesh(wpa_s->drv_priv);
+	return -1;
+}
+
 static inline int wpa_drv_scan(struct wpa_supplicant *wpa_s,
 			       struct wpa_driver_scan_params *params)
 {
+#ifdef CONFIG_TESTING_OPTIONS
+	if (wpa_s->test_failure == WPAS_TEST_FAILURE_SCAN_TRIGGER)
+		return -EBUSY;
+#endif /* CONFIG_TESTING_OPTIONS */
 	if (wpa_s->driver->scan2)
 		return wpa_s->driver->scan2(wpa_s->drv_priv, params);
 	return -1;
@@ -222,16 +248,6 @@
 	return NULL;
 }
 
-static inline int wpa_drv_send_eapol(struct wpa_supplicant *wpa_s,
-				     const u8 *dst, u16 proto,
-				     const u8 *data, size_t data_len)
-{
-	if (wpa_s->driver->send_eapol)
-		return wpa_s->driver->send_eapol(wpa_s->drv_priv, dst, proto,
-						 data, data_len);
-	return -1;
-}
-
 static inline int wpa_drv_set_operstate(struct wpa_supplicant *wpa_s,
 					int state)
 {
@@ -270,11 +286,13 @@
 }
 
 static inline int wpa_drv_send_mlme(struct wpa_supplicant *wpa_s,
-				    const u8 *data, size_t data_len, int noack)
+				    const u8 *data, size_t data_len, int noack,
+				    unsigned int freq)
 {
 	if (wpa_s->driver->send_mlme)
 		return wpa_s->driver->send_mlme(wpa_s->drv_priv,
-						data, data_len, noack);
+						data, data_len, noack,
+						freq);
 	return -1;
 }
 
@@ -288,16 +306,6 @@
 	return -1;
 }
 
-static inline int wpa_drv_send_ft_action(struct wpa_supplicant *wpa_s,
-					 u8 action, const u8 *target_ap,
-					 const u8 *ies, size_t ies_len)
-{
-	if (wpa_s->driver->send_ft_action)
-		return wpa_s->driver->send_ft_action(wpa_s->drv_priv, action,
-						     target_ap, ies, ies_len);
-	return -1;
-}
-
 static inline int wpa_drv_set_ap(struct wpa_supplicant *wpa_s,
 				 struct wpa_driver_ap_params *params)
 {
@@ -497,13 +505,6 @@
 					    proberesp, assocresp);
 }
 
-static inline int wpa_drv_shared_freq(struct wpa_supplicant *wpa_s)
-{
-	if (!wpa_s->driver->shared_freq)
-		return -1;
-	return wpa_s->driver->shared_freq(wpa_s->drv_priv);
-}
-
 static inline int wpa_drv_get_noa(struct wpa_supplicant *wpa_s,
 				  u8 *buf, size_t buf_len)
 {
@@ -563,12 +564,14 @@
 #endif /* ANDROID */
 
 static inline void wpa_drv_set_rekey_info(struct wpa_supplicant *wpa_s,
-					  const u8 *kek, const u8 *kck,
+					  const u8 *kek, size_t kek_len,
+					  const u8 *kck, size_t kck_len,
 					  const u8 *replay_ctr)
 {
 	if (!wpa_s->driver->set_rekey_info)
 		return;
-	wpa_s->driver->set_rekey_info(wpa_s->drv_priv, kek, kck, replay_ctr);
+	wpa_s->driver->set_rekey_info(wpa_s->drv_priv, kek, kek_len,
+				      kck, kck_len, replay_ctr);
 }
 
 static inline int wpa_drv_radio_disable(struct wpa_supplicant *wpa_s,
@@ -587,6 +590,45 @@
 	return wpa_s->driver->switch_channel(wpa_s->drv_priv, settings);
 }
 
+static inline int wpa_drv_add_ts(struct wpa_supplicant *wpa_s, u8 tsid,
+				 const u8 *address, u8 user_priority,
+				 u16 admitted_time)
+{
+	if (!wpa_s->driver->add_tx_ts)
+		return -1;
+	return wpa_s->driver->add_tx_ts(wpa_s->drv_priv, tsid, address,
+					user_priority, admitted_time);
+}
+
+static inline int wpa_drv_del_ts(struct wpa_supplicant *wpa_s, u8 tid,
+				 const u8 *address)
+{
+	if (!wpa_s->driver->del_tx_ts)
+		return -1;
+	return wpa_s->driver->del_tx_ts(wpa_s->drv_priv, tid, address);
+}
+
+static inline int wpa_drv_tdls_enable_channel_switch(
+	struct wpa_supplicant *wpa_s, const u8 *addr, u8 oper_class,
+	const struct hostapd_freq_params *freq_params)
+{
+	if (!wpa_s->driver->tdls_enable_channel_switch)
+		return -1;
+	return wpa_s->driver->tdls_enable_channel_switch(wpa_s->drv_priv, addr,
+							 oper_class,
+							 freq_params);
+}
+
+static inline int
+wpa_drv_tdls_disable_channel_switch(struct wpa_supplicant *wpa_s,
+				    const u8 *addr)
+{
+	if (!wpa_s->driver->tdls_disable_channel_switch)
+		return -1;
+	return wpa_s->driver->tdls_disable_channel_switch(wpa_s->drv_priv,
+							  addr);
+}
+
 static inline int wpa_drv_wnm_oper(struct wpa_supplicant *wpa_s,
 				   enum wnm_oper oper, const u8 *peer,
 				   u8 *buf, u16 *buf_len)
diff --git a/wpa_supplicant/eapol_test.c b/wpa_supplicant/eapol_test.c
index e19782f..bde7508 100644
--- a/wpa_supplicant/eapol_test.c
+++ b/wpa_supplicant/eapol_test.c
@@ -30,7 +30,7 @@
 #include "wpas_glue.h"
 
 
-struct wpa_driver_ops *wpa_drivers[] = { NULL };
+const struct wpa_driver_ops *const wpa_drivers[] = { NULL };
 
 
 struct extra_radius_attr {
@@ -461,7 +461,7 @@
 	len = os_snprintf(buf, buflen,
 			  WPA_CTRL_REQ "%s-%d:%s needed for SSID ",
 			  field_name, ssid->id, txt);
-	if (len < 0 || (size_t) len >= buflen) {
+	if (os_snprintf_error(buflen, len)) {
 		os_free(buf);
 		return;
 	}
@@ -480,6 +480,7 @@
 
 
 static void eapol_test_cert_cb(void *ctx, int depth, const char *subject,
+			       const char *altsubject[], int num_altsubject,
 			       const char *cert_hash,
 			       const struct wpabuf *cert)
 {
@@ -509,6 +510,14 @@
 			eapol_test_write_cert(e->server_cert_file,
 					      subject, cert);
 	}
+
+	if (altsubject) {
+		int i;
+
+		for (i = 0; i < num_altsubject; i++)
+			wpa_msg(e->wpa_s, MSG_INFO, WPA_EVENT_EAP_PEER_ALT
+				"depth=%d %s", depth, altsubject[i]);
+	}
 }
 
 
@@ -568,6 +577,7 @@
 	ctx->opensc_engine_path = wpa_s->conf->opensc_engine_path;
 	ctx->pkcs11_engine_path = wpa_s->conf->pkcs11_engine_path;
 	ctx->pkcs11_module_path = wpa_s->conf->pkcs11_module_path;
+	ctx->openssl_ciphers = wpa_s->conf->openssl_ciphers;
 	ctx->eap_param_needed = eapol_test_eap_param_needed;
 	ctx->cert_cb = eapol_test_cert_cb;
 	ctx->cert_in_cb = 1;
@@ -928,9 +938,12 @@
 		*pos++ = a[3];
 	}
 #else /* CONFIG_NATIVE_WINDOWS or CONFIG_ANSI_C_EXTRA */
-	inet_aton(authsrv, &as->addr.u.v4);
+	if (hostapd_parse_ip_addr(authsrv, &as->addr) < 0) {
+		wpa_printf(MSG_ERROR, "Invalid IP address '%s'",
+			   authsrv);
+		assert(0);
+	}
 #endif /* CONFIG_NATIVE_WINDOWS or CONFIG_ANSI_C_EXTRA */
-	as->addr.af = AF_INET;
 	as->port = port;
 	as->shared_secret = (u8 *) os_strdup(secret);
 	as->shared_secret_len = os_strlen(secret);
diff --git a/wpa_supplicant/events.c b/wpa_supplicant/events.c
index 985fa6e..2dbc39f 100644
--- a/wpa_supplicant/events.c
+++ b/wpa_supplicant/events.c
@@ -1,6 +1,6 @@
 /*
  * WPA Supplicant - Driver event processing
- * Copyright (c) 2003-2014, 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.
@@ -42,6 +42,9 @@
 #include "scan.h"
 #include "offchannel.h"
 #include "interworking.h"
+#include "mesh.h"
+#include "mesh_mpm.h"
+#include "wmm_ac.h"
 
 
 #ifndef CONFIG_NO_SCAN_PROCESSING
@@ -68,6 +71,59 @@
 }
 
 
+/**
+ * wpas_reenabled_network_time - Time until first network is re-enabled
+ * @wpa_s: Pointer to wpa_supplicant data
+ * Returns: If all enabled networks are temporarily disabled, returns the time
+ *	(in sec) until the first network is re-enabled. Otherwise returns 0.
+ *
+ * This function is used in case all enabled networks are temporarily disabled,
+ * in which case it returns the time (in sec) that the first network will be
+ * re-enabled. The function assumes that at least one network is enabled.
+ */
+static int wpas_reenabled_network_time(struct wpa_supplicant *wpa_s)
+{
+	struct wpa_ssid *ssid;
+	int disabled_for, res = 0;
+
+#ifdef CONFIG_INTERWORKING
+	if (wpa_s->conf->auto_interworking && wpa_s->conf->interworking &&
+	    wpa_s->conf->cred)
+		return 0;
+#endif /* CONFIG_INTERWORKING */
+
+	for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) {
+		if (ssid->disabled)
+			continue;
+
+		disabled_for = wpas_temp_disabled(wpa_s, ssid);
+		if (!disabled_for)
+			return 0;
+
+		if (!res || disabled_for < res)
+			res = disabled_for;
+	}
+
+	return res;
+}
+
+
+void wpas_network_reenabled(void *eloop_ctx, void *timeout_ctx)
+{
+	struct wpa_supplicant *wpa_s = eloop_ctx;
+
+	if (wpa_s->disconnected || wpa_s->wpa_state != WPA_SCANNING)
+		return;
+
+	wpa_dbg(wpa_s, MSG_DEBUG,
+		"Try to associate due to network getting re-enabled");
+	if (wpa_supplicant_fast_associate(wpa_s) != 1) {
+		wpa_supplicant_cancel_sched_scan(wpa_s);
+		wpa_supplicant_req_scan(wpa_s, 0, 0);
+	}
+}
+
+
 static struct wpa_bss * wpa_supplicant_get_new_bss(
 	struct wpa_supplicant *wpa_s, const u8 *bssid)
 {
@@ -102,11 +158,32 @@
 static int wpa_supplicant_select_config(struct wpa_supplicant *wpa_s)
 {
 	struct wpa_ssid *ssid, *old_ssid;
+	u8 drv_ssid[SSID_MAX_LEN];
+	size_t drv_ssid_len;
 	int res;
 
 	if (wpa_s->conf->ap_scan == 1 && wpa_s->current_ssid) {
 		wpa_supplicant_update_current_bss(wpa_s);
-		return 0;
+
+		if (wpa_s->current_ssid->ssid_len == 0)
+			return 0; /* current profile still in use */
+		res = wpa_drv_get_ssid(wpa_s, drv_ssid);
+		if (res < 0) {
+			wpa_msg(wpa_s, MSG_INFO,
+				"Failed to read SSID from driver");
+			return 0; /* try to use current profile */
+		}
+		drv_ssid_len = res;
+
+		if (drv_ssid_len == wpa_s->current_ssid->ssid_len &&
+		    os_memcmp(drv_ssid, wpa_s->current_ssid->ssid,
+			      drv_ssid_len) == 0)
+			return 0; /* current profile still in use */
+
+		wpa_msg(wpa_s, MSG_DEBUG,
+			"Driver-initiated BSS selection changed the SSID to %s",
+			wpa_ssid_txt(drv_ssid, drv_ssid_len));
+		/* continue selecting a new network profile */
 	}
 
 	wpa_dbg(wpa_s, MSG_DEBUG, "Select network based on association "
@@ -172,6 +249,15 @@
 		wpa_s->countermeasures = 0;
 		wpa_drv_set_countermeasures(wpa_s, 0);
 		wpa_msg(wpa_s, MSG_INFO, "WPA: TKIP countermeasures stopped");
+
+		/*
+		 * It is possible that the device is sched scanning, which means
+		 * that a connection attempt will be done only when we receive
+		 * scan results. However, in this case, it would be preferable
+		 * to scan and connect immediately, so cancel the sched_scan and
+		 * issue a regular scan flow.
+		 */
+		wpa_supplicant_cancel_sched_scan(wpa_s);
 		wpa_supplicant_req_scan(wpa_s, 0, 0);
 	}
 }
@@ -199,20 +285,12 @@
 	bssid_changed = !is_zero_ether_addr(wpa_s->bssid);
 	os_memset(wpa_s->bssid, 0, ETH_ALEN);
 	os_memset(wpa_s->pending_bssid, 0, ETH_ALEN);
-#ifdef CONFIG_SME
-	wpa_s->sme.prev_bssid_set = 0;
-#endif /* CONFIG_SME */
+	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;
-#ifdef CONFIG_IEEE80211R
-#ifdef CONFIG_SME
-	if (wpa_s->sme.ft_ies)
-		sme_update_ft_ies(wpa_s, NULL, NULL, 0);
-#endif /* CONFIG_SME */
-#endif /* CONFIG_IEEE80211R */
 
 	if (bssid_changed)
 		wpas_notify_bssid_changed(wpa_s);
@@ -225,6 +303,8 @@
 	wpa_s->current_ssid = NULL;
 	eapol_sm_notify_config(wpa_s->eapol, NULL, NULL);
 	wpa_s->key_mgmt = 0;
+
+	wpas_rrm_reset(wpa_s);
 }
 
 
@@ -243,7 +323,7 @@
 						    ie.pmkid + i * PMKID_LEN,
 						    NULL, NULL, 0);
 		if (pmksa_set == 0) {
-			eapol_sm_notify_pmkid_attempt(wpa_s->eapol, 1);
+			eapol_sm_notify_pmkid_attempt(wpa_s->eapol);
 			break;
 		}
 	}
@@ -479,8 +559,7 @@
 
 #ifdef CONFIG_IEEE80211W
 		if (!(ie.capabilities & WPA_CAPABILITY_MFPC) &&
-		    (ssid->ieee80211w == MGMT_FRAME_PROTECTION_DEFAULT ?
-		     wpa_s->conf->pmf : ssid->ieee80211w) ==
+		    wpas_get_ssid_pmf(wpa_s, ssid) ==
 		    MGMT_FRAME_PROTECTION_REQUIRED) {
 			wpa_dbg(wpa_s, MSG_DEBUG, "   skip RSN IE - no mgmt "
 				"frame protection");
@@ -582,42 +661,6 @@
 }
 
 
-int ht_supported(const struct hostapd_hw_modes *mode)
-{
-	if (!(mode->flags & HOSTAPD_MODE_FLAG_HT_INFO_KNOWN)) {
-		/*
-		 * The driver did not indicate whether it supports HT. Assume
-		 * it does to avoid connection issues.
-		 */
-		return 1;
-	}
-
-	/*
-	 * IEEE Std 802.11n-2009 20.1.1:
-	 * An HT non-AP STA shall support all EQM rates for one spatial stream.
-	 */
-	return mode->mcs_set[0] == 0xff;
-}
-
-
-int vht_supported(const struct hostapd_hw_modes *mode)
-{
-	if (!(mode->flags & HOSTAPD_MODE_FLAG_VHT_INFO_KNOWN)) {
-		/*
-		 * The driver did not indicate whether it supports VHT. Assume
-		 * it does to avoid connection issues.
-		 */
-		return 1;
-	}
-
-	/*
-	 * A VHT non-AP STA shall support MCS 0-7 for one spatial stream.
-	 * TODO: Verify if this complies with the standard
-	 */
-	return (mode->vht_mcs_set[0] & 0x3) != 3;
-}
-
-
 static int rate_match(struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
 {
 	const struct hostapd_hw_modes *mode = NULL, *modes;
@@ -709,9 +752,10 @@
 				 * order to join a BSS all required rates
 				 * have to be supported by the hardware.
 				 */
-				wpa_dbg(wpa_s, MSG_DEBUG, "   hardware does "
-					"not support required rate %d.%d Mbps",
-					r / 10, r % 10);
+				wpa_dbg(wpa_s, MSG_DEBUG,
+					"   hardware does not support required rate %d.%d Mbps (freq=%d mode==%d num_rates=%d)",
+					r / 10, r % 10,
+					bss->freq, mode->mode, mode->num_rates);
 				return 0;
 			}
 		}
@@ -737,6 +781,33 @@
 }
 
 
+static int match_mac_mask(const u8 *addr_a, const u8 *addr_b, const u8 *mask)
+{
+	size_t i;
+
+	for (i = 0; i < ETH_ALEN; i++) {
+		if ((addr_a[i] & mask[i]) != (addr_b[i] & mask[i]))
+			return 0;
+	}
+	return 1;
+}
+
+
+static int addr_in_list(const u8 *addr, const u8 *list, size_t num)
+{
+	size_t i;
+
+	for (i = 0; i < num; i++) {
+		const u8 *a = list + i * ETH_ALEN * 2;
+		const u8 *m = a + ETH_ALEN;
+
+		if (match_mac_mask(a, addr, m))
+			return 1;
+	}
+	return 0;
+}
+
+
 static struct wpa_ssid * wpa_scan_res_match(struct wpa_supplicant *wpa_s,
 					    int i, struct wpa_bss *bss,
 					    struct wpa_ssid *group,
@@ -861,6 +932,24 @@
 			continue;
 		}
 
+		/* check blacklist */
+		if (ssid->num_bssid_blacklist &&
+		    addr_in_list(bss->bssid, ssid->bssid_blacklist,
+				 ssid->num_bssid_blacklist)) {
+			wpa_dbg(wpa_s, MSG_DEBUG,
+				"   skip - BSSID blacklisted");
+			continue;
+		}
+
+		/* if there is a whitelist, only accept those APs */
+		if (ssid->num_bssid_whitelist &&
+		    !addr_in_list(bss->bssid, ssid->bssid_whitelist,
+				  ssid->num_bssid_whitelist)) {
+			wpa_dbg(wpa_s, MSG_DEBUG,
+				"   skip - BSSID not in whitelist");
+			continue;
+		}
+
 		if (!wpa_supplicant_ssid_bss_match(wpa_s, ssid, bss))
 			continue;
 
@@ -996,14 +1085,13 @@
 	struct wpa_bss *selected = NULL;
 	int prio;
 	struct wpa_ssid *next_ssid = NULL;
+	struct wpa_ssid *ssid;
 
 	if (wpa_s->last_scan_res == NULL ||
 	    wpa_s->last_scan_res_used == 0)
 		return NULL; /* no scan results from last update */
 
 	if (wpa_s->next_ssid) {
-		struct wpa_ssid *ssid;
-
 		/* check that next_ssid is still valid */
 		for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) {
 			if (ssid == wpa_s->next_ssid)
@@ -1039,6 +1127,27 @@
 			break;
 	}
 
+	ssid = *selected_ssid;
+	if (selected && ssid && ssid->mem_only_psk && !ssid->psk_set &&
+	    !ssid->passphrase && !ssid->ext_psk) {
+		const char *field_name, *txt = NULL;
+
+		wpa_dbg(wpa_s, MSG_DEBUG,
+			"PSK/passphrase not yet available for the selected network");
+
+		wpas_notify_network_request(wpa_s, ssid,
+					    WPA_CTRL_REQ_PSK_PASSPHRASE, NULL);
+
+		field_name = wpa_supplicant_ctrl_req_to_string(
+			WPA_CTRL_REQ_PSK_PASSPHRASE, NULL, &txt);
+		if (field_name == NULL)
+			return NULL;
+
+		wpas_send_ctrl_req(wpa_s, ssid, field_name, txt);
+
+		selected = NULL;
+	}
+
 	return selected;
 }
 
@@ -1070,6 +1179,7 @@
 	if (wpas_wps_scan_pbc_overlap(wpa_s, selected, ssid)) {
 		wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_OVERLAP
 			"PBC session overlap");
+		wpas_notify_wps_event_pbc_overlap(wpa_s);
 #ifdef CONFIG_P2P
 		if (wpa_s->p2p_group_interface == P2P_GROUP_INTERFACE_CLIENT ||
 		    wpa_s->p2p_in_provisioning) {
@@ -1136,7 +1246,8 @@
 			if (wpas_network_disabled(wpa_s, ssid))
 				continue;
 			if (ssid->mode == IEEE80211_MODE_IBSS ||
-			    ssid->mode == IEEE80211_MODE_AP)
+			    ssid->mode == IEEE80211_MODE_AP ||
+			    ssid->mode == IEEE80211_MODE_MESH)
 				return ssid;
 		}
 	}
@@ -1176,7 +1287,9 @@
 				       struct wpa_ssid *ssid)
 {
 	struct wpa_bss *current_bss = NULL;
+#ifndef CONFIG_NO_ROAMING
 	int min_diff;
+#endif /* CONFIG_NO_ROAMING */
 
 	if (wpa_s->reassociate)
 		return 1; /* explicit request to reassociate */
@@ -1208,10 +1321,14 @@
 
 #ifndef CONFIG_NO_ROAMING
 	wpa_dbg(wpa_s, MSG_DEBUG, "Considering within-ESS reassociation");
-	wpa_dbg(wpa_s, MSG_DEBUG, "Current BSS: " MACSTR " level=%d",
-		MAC2STR(current_bss->bssid), current_bss->level);
-	wpa_dbg(wpa_s, MSG_DEBUG, "Selected BSS: " MACSTR " level=%d",
-		MAC2STR(selected->bssid), selected->level);
+	wpa_dbg(wpa_s, MSG_DEBUG, "Current BSS: " MACSTR
+		" level=%d snr=%d est_throughput=%u",
+		MAC2STR(current_bss->bssid), current_bss->level,
+		current_bss->snr, current_bss->est_throughput);
+	wpa_dbg(wpa_s, MSG_DEBUG, "Selected BSS: " MACSTR
+		" level=%d snr=%d est_throughput=%u",
+		MAC2STR(selected->bssid), selected->level,
+		selected->snr, selected->est_throughput);
 
 	if (wpa_s->current_ssid->bssid_set &&
 	    os_memcmp(selected->bssid, wpa_s->current_ssid->bssid, ETH_ALEN) ==
@@ -1221,6 +1338,12 @@
 		return 1;
 	}
 
+	if (selected->est_throughput > current_bss->est_throughput + 5000) {
+		wpa_dbg(wpa_s, MSG_DEBUG,
+			"Allow reassociation - selected BSS has better estimated throughput");
+		return 1;
+	}
+
 	if (current_bss->level < 0 && current_bss->level > selected->level) {
 		wpa_dbg(wpa_s, MSG_DEBUG, "Skip roam - Current BSS has better "
 			"signal level");
@@ -1306,7 +1429,7 @@
 #endif /* CONFIG_NO_RANDOM_POOL */
 
 	if (own_request && wpa_s->scan_res_handler &&
-	    (wpa_s->own_scan_running || !wpa_s->external_scan_running)) {
+	    (wpa_s->own_scan_running || !wpa_s->radio->external_scan_running)) {
 		void (*scan_res_handler)(struct wpa_supplicant *wpa_s,
 					 struct wpa_scan_results *scan_res);
 
@@ -1327,7 +1450,7 @@
 	}
 
 	wpa_dbg(wpa_s, MSG_DEBUG, "New scan results available (own=%u ext=%u)",
-		wpa_s->own_scan_running, wpa_s->external_scan_running);
+		wpa_s->own_scan_running, wpa_s->radio->external_scan_running);
 	if (wpa_s->last_scan_req == MANUAL_SCAN_REQ &&
 	    wpa_s->manual_scan_use_id && wpa_s->own_scan_running) {
 		wpa_msg_ctrl(wpa_s, MSG_INFO, WPA_EVENT_SCAN_RESULTS "id=%u",
@@ -1340,12 +1463,15 @@
 
 	wpas_notify_scan_done(wpa_s, 1);
 
-	if (!wpa_s->own_scan_running && wpa_s->external_scan_running) {
+	if (!wpa_s->own_scan_running && wpa_s->radio->external_scan_running) {
 		wpa_dbg(wpa_s, MSG_DEBUG, "Do not use results from externally requested scan operation for network selection");
 		wpa_scan_results_free(scan_res);
 		return 0;
 	}
 
+	if (wnm_scan_process(wpa_s, 1) > 0)
+		goto scan_work_done;
+
 	if (sme_proc_obss_scan(wpa_s) > 0)
 		goto scan_work_done;
 
@@ -1392,6 +1518,20 @@
 {
 	struct wpa_bss *selected;
 	struct wpa_ssid *ssid = NULL;
+	int time_to_reenable = wpas_reenabled_network_time(wpa_s);
+
+	if (time_to_reenable > 0) {
+		wpa_dbg(wpa_s, MSG_DEBUG,
+			"Postpone network selection by %d seconds since all networks are disabled",
+			time_to_reenable);
+		eloop_cancel_timeout(wpas_network_reenabled, wpa_s, NULL);
+		eloop_register_timeout(time_to_reenable, 0,
+				       wpas_network_reenabled, wpa_s, NULL);
+		return 0;
+	}
+
+	if (wpa_s->p2p_mgmt)
+		return 0; /* no normal connection on p2p_mgmt interface */
 
 	selected = wpa_supplicant_pick_network(wpa_s, &ssid);
 
@@ -1416,6 +1556,13 @@
 		 */
 		return 1;
 	} else {
+#ifdef CONFIG_MESH
+		if (wpa_s->ifmsh) {
+			wpa_msg(wpa_s, MSG_INFO,
+				"Avoiding join because we already joined a mesh group");
+			return 0;
+		}
+#endif /* CONFIG_MESH */
 		wpa_dbg(wpa_s, MSG_DEBUG, "No suitable network found");
 		ssid = wpa_supplicant_pick_new_network(wpa_s);
 		if (ssid) {
@@ -1432,7 +1579,12 @@
 			int timeout_sec = wpa_s->scan_interval;
 			int timeout_usec = 0;
 #ifdef CONFIG_P2P
-			if (wpas_p2p_scan_no_go_seen(wpa_s) == 1)
+			int res;
+
+			res = wpas_p2p_scan_no_go_seen(wpa_s);
+			if (res == 2)
+				return 2;
+			if (res == 1)
 				return 0;
 
 			if (wpa_s->p2p_in_provisioning ||
@@ -1476,18 +1628,30 @@
 			if (wpa_supplicant_req_sched_scan(wpa_s))
 				wpa_supplicant_req_new_scan(wpa_s, timeout_sec,
 							    timeout_usec);
+
+			wpa_msg_ctrl(wpa_s, MSG_INFO,
+				     WPA_EVENT_NETWORK_NOT_FOUND);
 		}
 	}
 	return 0;
 }
 
 
-static void wpa_supplicant_event_scan_results(struct wpa_supplicant *wpa_s,
-					      union wpa_event_data *data)
+static int wpa_supplicant_event_scan_results(struct wpa_supplicant *wpa_s,
+					     union wpa_event_data *data)
 {
 	struct wpa_supplicant *ifs;
+	int res;
 
-	if (_wpa_supplicant_event_scan_results(wpa_s, data, 1) != 0) {
+	res = _wpa_supplicant_event_scan_results(wpa_s, data, 1);
+	if (res == 2) {
+		/*
+		 * Interface may have been removed, so must not dereference
+		 * wpa_s after this.
+		 */
+		return 1;
+	}
+	if (res != 0) {
 		/*
 		 * If no scan results could be fetched, then no need to
 		 * notify those interfaces that did not actually request
@@ -1495,7 +1659,7 @@
 		 * interface, do not notify other interfaces to avoid concurrent
 		 * operations during a connection attempt.
 		 */
-		return;
+		return 0;
 	}
 
 	/*
@@ -1510,6 +1674,8 @@
 			_wpa_supplicant_event_scan_results(ifs, data, 0);
 		}
 	}
+
+	return 0;
 }
 
 #endif /* CONFIG_NO_SCAN_PROCESSING */
@@ -1522,7 +1688,7 @@
 #else /* CONFIG_NO_SCAN_PROCESSING */
 	struct os_reltime now;
 
-	if (wpa_s->last_scan_res_used <= 0)
+	if (wpa_s->last_scan_res_used == 0)
 		return -1;
 
 	os_get_reltime(&now);
@@ -1891,6 +2057,8 @@
 	}
 #endif /* CONFIG_AP */
 
+	eloop_cancel_timeout(wpas_network_reenabled, wpa_s, NULL);
+
 	ft_completed = wpa_ft_is_completed(wpa_s->wpa);
 	if (data && wpa_supplicant_event_associnfo(wpa_s, data) < 0)
 		return;
@@ -2067,6 +2235,15 @@
 #endif /* CONFIG_IBSS_RSN */
 
 	wpas_wps_notify_assoc(wpa_s, bssid);
+
+	if (data) {
+		wmm_ac_notify_assoc(wpa_s, data->assoc_info.resp_ies,
+				    data->assoc_info.resp_ies_len,
+				    &data->assoc_info.wmm_params);
+
+		if (wpa_s->reassoc_same_bss)
+			wmm_ac_restore_tspecs(wpa_s);
+	}
 }
 
 
@@ -2383,6 +2560,21 @@
 			wpa_msg(wpa_s, MSG_INFO, "Failed to initialize the "
 				"driver after interface was added");
 		}
+
+#ifdef CONFIG_P2P
+		if (!wpa_s->global->p2p &&
+		    !wpa_s->global->p2p_disabled &&
+		    !wpa_s->conf->p2p_disabled &&
+		    (wpa_s->drv_flags &
+		     WPA_DRIVER_FLAGS_DEDICATED_P2P_DEVICE) &&
+		    wpas_p2p_add_p2pdev_interface(
+			    wpa_s, wpa_s->global->params.conf_p2p_dev) < 0) {
+			wpa_printf(MSG_INFO,
+				   "P2P: Failed to enable P2P Device interface");
+			/* Try to continue without. P2P will be disabled. */
+		}
+#endif /* CONFIG_P2P */
+
 		break;
 	case EVENT_INTERFACE_REMOVED:
 		wpa_dbg(wpa_s, MSG_DEBUG, "Configured interface was removed");
@@ -2391,6 +2583,21 @@
 		wpa_supplicant_set_state(wpa_s, WPA_INTERFACE_DISABLED);
 		l2_packet_deinit(wpa_s->l2);
 		wpa_s->l2 = NULL;
+
+#ifdef CONFIG_P2P
+		if (wpa_s->global->p2p &&
+		    wpa_s->global->p2p_init_wpa_s->parent == wpa_s &&
+		    (wpa_s->drv_flags &
+		     WPA_DRIVER_FLAGS_DEDICATED_P2P_DEVICE)) {
+			wpa_dbg(wpa_s, MSG_DEBUG,
+				"Removing P2P Device interface");
+			wpa_supplicant_remove_iface(
+				wpa_s->global, wpa_s->global->p2p_init_wpa_s,
+				0);
+			wpa_s->global->p2p_init_wpa_s = NULL;
+		}
+#endif /* CONFIG_P2P */
+
 #ifdef CONFIG_TERMINATE_ONLASTIF
 		/* check if last interface */
 		if (!any_interfaces(wpa_s->global->ifaces))
@@ -2435,6 +2642,10 @@
 			wpa_drv_tdls_oper(wpa_s, TDLS_TEARDOWN,
 					  data->tdls.peer);
 		break;
+	case TDLS_REQUEST_DISCOVER:
+			wpa_tdls_send_discovery_request(wpa_s->wpa,
+							data->tdls.peer);
+		break;
 	}
 }
 #endif /* CONFIG_TDLS */
@@ -2612,6 +2823,9 @@
 	}
 #endif /* CONFIG_AP */
 
+	if (!locally_generated)
+		wpa_s->own_disconnect_req = 0;
+
 	wpa_supplicant_event_disassoc(wpa_s, reason_code, locally_generated);
 
 	if (((reason_code == WLAN_REASON_IEEE_802_1X_AUTH_FAILED ||
@@ -2797,11 +3011,20 @@
 				ifs, &ifs->hw.num_modes, &ifs->hw.flags);
 		}
 	}
+
+	/* Restart sched_scan with updated channel list */
+	if (wpa_s->sched_scanning) {
+		wpa_dbg(wpa_s, MSG_DEBUG,
+			"Channel list changed restart sched scan.");
+		wpa_supplicant_cancel_sched_scan(wpa_s);
+		wpa_supplicant_req_scan(wpa_s, 0, 0);
+	}
 }
 
 
 static void wpas_event_rx_mgmt_action(struct wpa_supplicant *wpa_s,
-				      const u8 *frame, size_t len, int freq)
+				      const u8 *frame, size_t len, int freq,
+				      int rssi)
 {
 	const struct ieee80211_mgmt *mgmt;
 	const u8 *payload;
@@ -2820,6 +3043,11 @@
 		" Category=%u DataLen=%d freq=%d MHz",
 		MAC2STR(mgmt->sa), category, (int) plen, freq);
 
+	if (category == WLAN_ACTION_WMM) {
+		wmm_ac_rx_action(wpa_s, mgmt->da, mgmt->sa, payload, plen);
+		return;
+	}
+
 #ifdef CONFIG_IEEE80211R
 	if (category == WLAN_ACTION_FT) {
 		ft_rx_action(wpa_s, payload, plen);
@@ -2877,8 +3105,24 @@
 	}
 #endif /* CONFIG_INTERWORKING */
 
+	if (category == WLAN_ACTION_RADIO_MEASUREMENT &&
+	    payload[0] == WLAN_RRM_NEIGHBOR_REPORT_RESPONSE) {
+		wpas_rrm_process_neighbor_rep(wpa_s, payload + 1, plen - 1);
+		return;
+	}
+
+	if (category == WLAN_ACTION_RADIO_MEASUREMENT &&
+	    payload[0] == WLAN_RRM_LINK_MEASUREMENT_REQUEST) {
+		wpas_rrm_handle_link_measurement_request(wpa_s, mgmt->sa,
+							 payload + 1, plen - 1,
+							 rssi);
+		return;
+	}
+
 	wpas_p2p_rx_action(wpa_s, mgmt->da, mgmt->sa, mgmt->bssid,
 			   category, payload, plen, freq);
+	if (wpa_s->ifmsh)
+		mesh_mpm_action_rx(wpa_s, mgmt, len);
 }
 
 
@@ -2934,10 +3178,31 @@
 }
 
 
+static void wpa_supplicant_event_assoc_auth(struct wpa_supplicant *wpa_s,
+					    union wpa_event_data *data)
+{
+	wpa_dbg(wpa_s, MSG_DEBUG,
+		"Connection authorized by device, previous state %d",
+		wpa_s->wpa_state);
+	if (wpa_s->wpa_state == WPA_ASSOCIATED) {
+		wpa_supplicant_cancel_auth_timeout(wpa_s);
+		wpa_supplicant_set_state(wpa_s, WPA_COMPLETED);
+		eapol_sm_notify_portValid(wpa_s->eapol, TRUE);
+		eapol_sm_notify_eap_success(wpa_s->eapol, TRUE);
+	}
+	wpa_sm_set_rx_replay_ctr(wpa_s->wpa, data->assoc_info.key_replay_ctr);
+	wpa_sm_set_ptk_kck_kek(wpa_s->wpa, data->assoc_info.ptk_kck,
+			       data->assoc_info.ptk_kck_len,
+			       data->assoc_info.ptk_kek,
+			       data->assoc_info.ptk_kek_len);
+}
+
+
 void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
 			  union wpa_event_data *data)
 {
 	struct wpa_supplicant *wpa_s = ctx;
+	int resched;
 
 	if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED &&
 	    event != EVENT_INTERFACE_ENABLED &&
@@ -2974,6 +3239,8 @@
 		break;
 	case EVENT_ASSOC:
 		wpa_supplicant_event_assoc(wpa_s, data);
+		if (data && data->assoc_info.authorized)
+			wpa_supplicant_event_assoc_auth(wpa_s, data);
 		break;
 	case EVENT_DISASSOC:
 		wpas_event_disassoc(wpa_s,
@@ -3009,7 +3276,7 @@
 			}
 		} else {
 			wpa_dbg(wpa_s, MSG_DEBUG, "External program started a scan");
-			wpa_s->external_scan_running = 1;
+			wpa_s->radio->external_scan_running = 1;
 			wpa_msg_ctrl(wpa_s, MSG_INFO, WPA_EVENT_SCAN_STARTED);
 		}
 		break;
@@ -3023,9 +3290,10 @@
 			wpa_dbg(wpa_s, MSG_DEBUG, "Scan completed in %ld.%06ld seconds",
 				diff.sec, diff.usec);
 		}
-		wpa_supplicant_event_scan_results(wpa_s, data);
+		if (wpa_supplicant_event_scan_results(wpa_s, data))
+			break; /* interface may have been removed */
 		wpa_s->own_scan_running = 0;
-		wpa_s->external_scan_running = 0;
+		wpa_s->radio->external_scan_running = 0;
 		radio_work_check_next(wpa_s);
 		break;
 #endif /* CONFIG_NO_SCAN_PROCESSING */
@@ -3084,10 +3352,24 @@
 		}
 		break;
 	case EVENT_AUTH_TIMED_OUT:
+		/* It is possible to get this event from earlier connection */
+		if (wpa_s->current_ssid &&
+		    wpa_s->current_ssid->mode == WPAS_MODE_MESH) {
+			wpa_dbg(wpa_s, MSG_DEBUG,
+				"Ignore AUTH_TIMED_OUT in mesh configuration");
+			break;
+		}
 		if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME)
 			sme_event_auth_timed_out(wpa_s, data);
 		break;
 	case EVENT_ASSOC_TIMED_OUT:
+		/* It is possible to get this event from earlier connection */
+		if (wpa_s->current_ssid &&
+		    wpa_s->current_ssid->mode == WPAS_MODE_MESH) {
+			wpa_dbg(wpa_s, MSG_DEBUG,
+				"Ignore ASSOC_TIMED_OUT in mesh configuration");
+			break;
+		}
 		if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME)
 			sme_event_assoc_timed_out(wpa_s, data);
 		break;
@@ -3182,6 +3464,29 @@
 				  data->ch_switch.cf1,
 				  data->ch_switch.cf2);
 		break;
+#ifdef NEED_AP_MLME
+	case EVENT_DFS_RADAR_DETECTED:
+		if (data)
+			wpas_event_dfs_radar_detected(wpa_s, &data->dfs_event);
+		break;
+	case EVENT_DFS_CAC_STARTED:
+		if (data)
+			wpas_event_dfs_cac_started(wpa_s, &data->dfs_event);
+		break;
+	case EVENT_DFS_CAC_FINISHED:
+		if (data)
+			wpas_event_dfs_cac_finished(wpa_s, &data->dfs_event);
+		break;
+	case EVENT_DFS_CAC_ABORTED:
+		if (data)
+			wpas_event_dfs_cac_aborted(wpa_s, &data->dfs_event);
+		break;
+	case EVENT_DFS_NOP_FINISHED:
+		if (data)
+			wpas_event_dfs_cac_nop_finished(wpa_s,
+							&data->dfs_event);
+		break;
+#endif /* NEED_AP_MLME */
 #endif /* CONFIG_AP */
 	case EVENT_RX_MGMT: {
 		u16 fc, stype;
@@ -3223,12 +3528,15 @@
 				wpas_p2p_probe_req_rx(
 					wpa_s, src, mgmt->da,
 					mgmt->bssid, ie, ie_len,
+					data->rx_mgmt.freq,
 					data->rx_mgmt.ssi_signal);
 				break;
 			}
 #endif /* CONFIG_P2P */
 #ifdef CONFIG_IBSS_RSN
-			if (stype == WLAN_FC_STYPE_AUTH &&
+			if (wpa_s->current_ssid &&
+			    wpa_s->current_ssid->mode == WPAS_MODE_IBSS &&
+			    stype == WLAN_FC_STYPE_AUTH &&
 			    data->rx_mgmt.frame_len >= 30) {
 				wpa_supplicant_event_ibss_auth(wpa_s, data);
 				break;
@@ -3239,7 +3547,13 @@
 				wpas_event_rx_mgmt_action(
 					wpa_s, data->rx_mgmt.frame,
 					data->rx_mgmt.frame_len,
-					data->rx_mgmt.freq);
+					data->rx_mgmt.freq,
+					data->rx_mgmt.ssi_signal);
+				break;
+			}
+
+			if (wpa_s->ifmsh) {
+				mesh_mpm_mgmt_rx(wpa_s, &data->rx_mgmt);
 				break;
 			}
 
@@ -3286,6 +3600,7 @@
 				      data->rx_probe_req.bssid,
 				      data->rx_probe_req.ie,
 				      data->rx_probe_req.ie_len,
+				      0,
 				      data->rx_probe_req.ssi_signal);
 		break;
 	case EVENT_REMAIN_ON_CHANNEL:
@@ -3318,6 +3633,8 @@
 			data->signal_change.current_signal,
 			data->signal_change.current_noise,
 			data->signal_change.current_txrate);
+		wpa_bss_update_level(wpa_s->current_bss,
+				     data->signal_change.current_signal);
 		bgscan_notify_signal_change(
 			wpa_s, data->signal_change.above_threshold,
 			data->signal_change.current_signal,
@@ -3338,6 +3655,7 @@
 			if (!wpa_s->ap_iface) {
 				wpa_supplicant_set_state(wpa_s,
 							 WPA_DISCONNECTED);
+				wpa_s->scan_req = NORMAL_SCAN_REQ;
 				wpa_supplicant_req_scan(wpa_s, 0, 0);
 			} else
 				wpa_supplicant_set_state(wpa_s,
@@ -3355,11 +3673,24 @@
 		    (wpa_s->current_ssid && wpa_s->current_ssid->p2p_group &&
 		     wpa_s->current_ssid->mode == WPAS_MODE_P2P_GO)) {
 			/*
+			 * Mark interface disabled if this happens to end up not
+			 * being removed as a separate P2P group interface.
+			 */
+			wpa_supplicant_set_state(wpa_s, WPA_INTERFACE_DISABLED);
+			/*
 			 * The interface was externally disabled. Remove
 			 * it assuming an external entity will start a
 			 * new session if needed.
 			 */
-			wpas_p2p_disconnect(wpa_s);
+			if (wpa_s->current_ssid &&
+			    wpa_s->current_ssid->p2p_group)
+				wpas_p2p_interface_unavailable(wpa_s);
+			else
+				wpas_p2p_disconnect(wpa_s);
+			/*
+			 * wpa_s instance may have been freed, so must not use
+			 * it here anymore.
+			 */
 			break;
 		}
 		if (wpa_s->p2p_scan_work && wpa_s->global->p2p &&
@@ -3440,6 +3771,7 @@
 	case EVENT_SCHED_SCAN_STOPPED:
 		wpa_s->pno = 0;
 		wpa_s->sched_scanning = 0;
+		resched = wpa_s->scanning;
 		wpa_supplicant_notify_scanning(wpa_s, 0);
 
 		if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED)
@@ -3454,6 +3786,8 @@
 		} else if (wpa_s->pno_sched_pending) {
 			wpa_s->pno_sched_pending = 0;
 			wpas_start_pno(wpa_s);
+		} else if (resched) {
+			wpa_supplicant_req_scan(wpa_s, 0, 0);
 		}
 
 		break;
@@ -3475,6 +3809,15 @@
 			data->connect_failed_reason.code);
 #endif /* CONFIG_AP */
 		break;
+	case EVENT_NEW_PEER_CANDIDATE:
+#ifdef CONFIG_MESH
+		if (!wpa_s->ifmsh || !data)
+			break;
+		wpa_mesh_notify_peer(wpa_s, data->mesh_peer.peer,
+				     data->mesh_peer.ies,
+				     data->mesh_peer.ie_len);
+#endif /* CONFIG_MESH */
+		break;
 	default:
 		wpa_msg(wpa_s, MSG_INFO, "Unknown event %d", event);
 		break;
diff --git a/wpa_supplicant/examples/wps-ap-cli b/wpa_supplicant/examples/wps-ap-cli
index 7c6b0aa..cc2cff2 100755
--- a/wpa_supplicant/examples/wps-ap-cli
+++ b/wpa_supplicant/examples/wps-ap-cli
@@ -14,11 +14,13 @@
 enter_pin()
 {
 	echo "Enter a PIN from a station to be enrolled to the network."
-	read -p "Enrollee PIN: " pin
+	echo -n "Enrollee PIN: "
+	read pin
 	cpin=`$CLI wps_check_pin "$pin" | tail -1`
 	if [ "$cpin" = "FAIL-CHECKSUM" ]; then
 		echo "Checksum digit is not valid"
-		read -p "Do you want to use this PIN (y/n)? " resp
+		echo -n "Do you want to use this PIN (y/n)? "
+		read resp
 		case "$resp" in
 			y*)
 				cpin=`echo "$pin" | sed "s/[^1234567890]//g"`
@@ -50,7 +52,8 @@
 	echo "3: Show current configuration"
 	echo "0: Exit wps-ap-cli"
 
-	read -p "Command: " cmd
+	echo -n "Command: "
+	read cmd
 
 	case "$cmd" in
 		1)
diff --git a/wpa_supplicant/gas_query.c b/wpa_supplicant/gas_query.c
index 3a89674..10ecce7 100644
--- a/wpa_supplicant/gas_query.c
+++ b/wpa_supplicant/gas_query.c
@@ -442,6 +442,7 @@
 	u16 comeback_delay, resp_len;
 	const u8 *pos, *adv_proto;
 	int prot, pmf;
+	unsigned int left;
 
 	if (gas == NULL || len < 4)
 		return -1;
@@ -543,17 +544,17 @@
 	resp_len = WPA_GET_LE16(pos);
 	pos += 2;
 
-	if (pos + resp_len > data + len) {
+	left = data + len - pos;
+	if (resp_len > left) {
 		wpa_printf(MSG_DEBUG, "GAS: Truncated Query Response in "
 			   "response from " MACSTR, MAC2STR(sa));
 		return 0;
 	}
 
-	if (pos + resp_len < data + len) {
+	if (resp_len < left) {
 		wpa_printf(MSG_DEBUG, "GAS: Ignore %u octets of extra data "
 			   "after Query Response from " MACSTR,
-			   (unsigned int) (data + len - pos - resp_len),
-			   MAC2STR(sa));
+			   left - resp_len, MAC2STR(sa));
 	}
 
 	if (action == WLAN_PA_GAS_COMEBACK_RESP)
diff --git a/wpa_supplicant/hs20_supplicant.c b/wpa_supplicant/hs20_supplicant.c
index 257aa6d..a1afc85 100644
--- a/wpa_supplicant/hs20_supplicant.c
+++ b/wpa_supplicant/hs20_supplicant.c
@@ -7,6 +7,7 @@
  */
 
 #include "includes.h"
+#include <sys/stat.h>
 
 #include "common.h"
 #include "eloop.h"
@@ -45,7 +46,7 @@
 
 struct osu_provider {
 	u8 bssid[ETH_ALEN];
-	u8 osu_ssid[32];
+	u8 osu_ssid[SSID_MAX_LEN];
 	u8 osu_ssid_len;
 	char server_uri[256];
 	u32 osu_methods; /* bit 0 = OMA-DM, bit 1 = SOAP-XML SPP */
@@ -187,14 +188,16 @@
 	struct wpa_bss *bss;
 	int res;
 
-	freq = wpa_s->assoc_freq;
 	bss = wpa_bss_get_bssid(wpa_s, dst);
-	if (bss) {
-		wpa_bss_anqp_unshare_alloc(bss);
-		freq = bss->freq;
-	}
-	if (freq <= 0)
+	if (!bss) {
+		wpa_printf(MSG_WARNING,
+			   "ANQP: Cannot send query to unknown BSS "
+			   MACSTR, MAC2STR(dst));
 		return -1;
+	}
+
+	wpa_bss_anqp_unshare_alloc(bss);
+	freq = bss->freq;
 
 	wpa_printf(MSG_DEBUG, "HS20: ANQP Query Request to " MACSTR " for "
 		   "subtypes 0x%x", MAC2STR(dst), stypes);
@@ -216,6 +219,30 @@
 }
 
 
+static void hs20_set_osu_access_permission(const char *osu_dir,
+					   const char *fname)
+{
+	struct stat statbuf;
+
+	/* Get OSU directory information */
+	if (stat(osu_dir, &statbuf) < 0) {
+		wpa_printf(MSG_WARNING, "Cannot stat the OSU directory %s",
+			   osu_dir);
+		return;
+	}
+
+	if (chmod(fname, statbuf.st_mode) < 0) {
+		wpa_printf(MSG_WARNING,
+			   "Cannot change the permissions for %s", fname);
+		return;
+	}
+
+	if (chown(fname, statbuf.st_uid, statbuf.st_gid) < 0) {
+		wpa_printf(MSG_WARNING, "Cannot change the ownership for %s",
+			   fname);
+	}
+}
+
 static int hs20_process_icon_binary_file(struct wpa_supplicant *wpa_s,
 					 const u8 *sa, const u8 *pos,
 					 size_t slen)
@@ -278,6 +305,9 @@
 	f = fopen(fname, "wb");
 	if (f == NULL)
 		return -1;
+
+	hs20_set_osu_access_permission(wpa_s->conf->osu_dir, fname);
+
 	if (fwrite(pos, slen, 1, f) != 1) {
 		fclose(f);
 		unlink(fname);
@@ -327,11 +357,11 @@
 
 
 void hs20_parse_rx_hs20_anqp_resp(struct wpa_supplicant *wpa_s,
-				  const u8 *sa, const u8 *data, size_t slen)
+				  struct wpa_bss *bss, const u8 *sa,
+				  const u8 *data, size_t slen)
 {
 	const u8 *pos = data;
 	u8 subtype;
-	struct wpa_bss *bss = wpa_bss_get_bssid(wpa_s, sa);
 	struct wpa_bss_anqp *anqp = NULL;
 	int ret;
 
@@ -352,6 +382,11 @@
 		wpa_msg(wpa_s, MSG_INFO, "RX-HS20-ANQP " MACSTR
 			" HS Capability List", MAC2STR(sa));
 		wpa_hexdump_ascii(MSG_DEBUG, "HS Capability List", pos, slen);
+		if (anqp) {
+			wpabuf_free(anqp->hs20_capability_list);
+			anqp->hs20_capability_list =
+				wpabuf_alloc_copy(pos, slen);
+		}
 		break;
 	case HS20_STYPE_OPERATOR_FRIENDLY_NAME:
 		wpa_msg(wpa_s, MSG_INFO, "RX-HS20-ANQP " MACSTR
@@ -479,6 +514,9 @@
 		hs20_free_osu_prov(wpa_s);
 		return;
 	}
+
+	hs20_set_osu_access_permission(wpa_s->conf->osu_dir, fname);
+
 	for (i = 0; i < wpa_s->osu_prov_count; i++) {
 		struct osu_provider *osu = &wpa_s->osu_prov[i];
 		if (i > 0)
@@ -562,6 +600,7 @@
 	const u8 *end = pos + len;
 	u16 len2;
 	const u8 *pos2;
+	u8 uri_len, osu_method_len, osu_nai_len;
 
 	wpa_hexdump(MSG_DEBUG, "HS 2.0: Parsing OSU Provider", pos, len);
 	prov = os_realloc_array(wpa_s->osu_prov,
@@ -585,7 +624,7 @@
 	}
 	len2 = WPA_GET_LE16(pos);
 	pos += 2;
-	if (pos + len2 > end) {
+	if (len2 > end - pos) {
 		wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for OSU "
 			   "Friendly Name Duples");
 		return;
@@ -607,22 +646,34 @@
 	}
 
 	/* OSU Server URI */
-	if (pos + 1 > end || pos + 1 + pos[0] > end) {
+	if (pos + 1 > end) {
+		wpa_printf(MSG_DEBUG,
+			   "HS 2.0: Not enough room for OSU Server URI length");
+		return;
+	}
+	uri_len = *pos++;
+	if (uri_len > end - pos) {
 		wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for OSU Server "
 			   "URI");
 		return;
 	}
-	os_memcpy(prov->server_uri, pos + 1, pos[0]);
-	pos += 1 + pos[0];
+	os_memcpy(prov->server_uri, pos, uri_len);
+	pos += uri_len;
 
 	/* OSU Method list */
-	if (pos + 1 > end || pos + 1 + pos[0] > end) {
+	if (pos + 1 > end) {
+		wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for OSU Method "
+			   "list length");
+		return;
+	}
+	osu_method_len = pos[0];
+	if (osu_method_len > end - pos - 1) {
 		wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for OSU Method "
 			   "list");
 		return;
 	}
 	pos2 = pos + 1;
-	pos += 1 + pos[0];
+	pos += 1 + osu_method_len;
 	while (pos2 < pos) {
 		if (*pos2 < 32)
 			prov->osu_methods |= BIT(*pos2);
@@ -637,7 +688,7 @@
 	}
 	len2 = WPA_GET_LE16(pos);
 	pos += 2;
-	if (pos + len2 > end) {
+	if (len2 > end - pos) {
 		wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for Icons "
 			   "Available");
 		return;
@@ -648,6 +699,8 @@
 	/* Icons Available */
 	while (pos2 < pos) {
 		struct osu_icon *icon = &prov->icon[prov->icon_count];
+		u8 flen;
+
 		if (pos2 + 2 + 2 + 3 + 1 + 1 > pos) {
 			wpa_printf(MSG_DEBUG, "HS 2.0: Invalid Icon Metadata");
 			break;
@@ -660,31 +713,43 @@
 		os_memcpy(icon->lang, pos2, 3);
 		pos2 += 3;
 
-		if (pos2 + 1 + pos2[0] > pos) {
+		flen = pos2[0];
+		if (flen > pos - pos2 - 1) {
 			wpa_printf(MSG_DEBUG, "HS 2.0: Not room for Icon Type");
 			break;
 		}
-		os_memcpy(icon->icon_type, pos2 + 1, pos2[0]);
-		pos2 += 1 + pos2[0];
+		os_memcpy(icon->icon_type, pos2 + 1, flen);
+		pos2 += 1 + flen;
 
-		if (pos2 + 1 + pos2[0] > pos) {
+		if (pos2 + 1 > pos) {
+			wpa_printf(MSG_DEBUG, "HS 2.0: Not room for Icon "
+				   "Filename length");
+			break;
+		}
+		flen = pos2[0];
+		if (flen > pos - pos2 - 1) {
 			wpa_printf(MSG_DEBUG, "HS 2.0: Not room for Icon "
 				   "Filename");
 			break;
 		}
-		os_memcpy(icon->filename, pos2 + 1, pos2[0]);
-		pos2 += 1 + pos2[0];
+		os_memcpy(icon->filename, pos2 + 1, flen);
+		pos2 += 1 + flen;
 
 		prov->icon_count++;
 	}
 
 	/* OSU_NAI */
-	if (pos + 1 > end || pos + 1 + pos[0] > end) {
+	if (pos + 1 > end) {
 		wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for OSU_NAI");
 		return;
 	}
-	os_memcpy(prov->osu_nai, pos + 1, pos[0]);
-	pos += 1 + pos[0];
+	osu_nai_len = pos[0];
+	if (osu_nai_len > end - pos - 1) {
+		wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for OSU_NAI");
+		return;
+	}
+	os_memcpy(prov->osu_nai, pos + 1, osu_nai_len);
+	pos += 1 + osu_nai_len;
 
 	/* OSU Service Description Length */
 	if (pos + 2 > end) {
@@ -694,7 +759,7 @@
 	}
 	len2 = WPA_GET_LE16(pos);
 	pos += 2;
-	if (pos + len2 > end) {
+	if (len2 > end - pos) {
 		wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for OSU "
 			   "Service Description Duples");
 		return;
@@ -705,15 +770,18 @@
 	/* OSU Service Description Duples */
 	while (pos2 + 4 <= pos && prov->serv_desc_count < OSU_MAX_ITEMS) {
 		struct osu_lang_string *f;
-		if (pos2 + 1 + pos2[0] > pos || pos2[0] < 3) {
+		u8 descr_len;
+
+		descr_len = pos2[0];
+		if (descr_len > pos - pos2 - 1 || descr_len < 3) {
 			wpa_printf(MSG_DEBUG, "Invalid OSU Service "
 				   "Description");
 			break;
 		}
 		f = &prov->serv_desc[prov->serv_desc_count++];
 		os_memcpy(f->lang, pos2 + 1, 3);
-		os_memcpy(f->text, pos2 + 1 + 3, pos2[0] - 3);
-		pos2 += 1 + pos2[0];
+		os_memcpy(f->text, pos2 + 1 + 3, descr_len - 3);
+		pos2 += 1 + descr_len;
 	}
 
 	wpa_printf(MSG_DEBUG, "HS 2.0: Added OSU Provider through " MACSTR,
@@ -756,7 +824,7 @@
 			continue;
 		}
 		osu_ssid_len = *pos++;
-		if (osu_ssid_len > 32) {
+		if (osu_ssid_len > SSID_MAX_LEN) {
 			wpa_printf(MSG_DEBUG, "HS 2.0: Invalid OSU SSID "
 				   "Length %u", osu_ssid_len);
 			continue;
@@ -778,7 +846,7 @@
 			num_providers--;
 			len = WPA_GET_LE16(pos);
 			pos += 2;
-			if (pos + len > end)
+			if (len > (unsigned int) (end - pos))
 				break;
 			hs20_osu_add_prov(wpa_s, bss, osu_ssid,
 					  osu_ssid_len, pos, len);
@@ -801,6 +869,10 @@
 				      struct wpa_scan_results *scan_res)
 {
 	wpa_printf(MSG_DEBUG, "OSU provisioning fetch scan completed");
+	if (!wpa_s->fetch_osu_waiting_scan) {
+		wpa_printf(MSG_DEBUG, "OSU fetch have been canceled");
+		return;
+	}
 	wpa_s->network_select = 0;
 	wpa_s->fetch_all_anqp = 1;
 	wpa_s->fetch_osu_info = 1;
@@ -849,6 +921,7 @@
 
 void hs20_start_osu_scan(struct wpa_supplicant *wpa_s)
 {
+	wpa_s->fetch_osu_waiting_scan = 1;
 	wpa_s->num_osu_scans++;
 	wpa_s->scan_req = MANUAL_SCAN_REQ;
 	wpa_s->scan_res_handler = hs20_osu_scan_res_handler;
@@ -860,6 +933,7 @@
 {
 	wpa_printf(MSG_DEBUG, "Cancel OSU fetch");
 	interworking_stop_fetch_anqp(wpa_s);
+	wpa_s->fetch_osu_waiting_scan = 0;
 	wpa_s->network_select = 0;
 	wpa_s->fetch_osu_info = 0;
 	wpa_s->fetch_osu_icon_in_progress = 0;
diff --git a/wpa_supplicant/hs20_supplicant.h b/wpa_supplicant/hs20_supplicant.h
index 06739f5..85b5120 100644
--- a/wpa_supplicant/hs20_supplicant.h
+++ b/wpa_supplicant/hs20_supplicant.h
@@ -17,7 +17,8 @@
 void hs20_put_anqp_req(u32 stypes, const u8 *payload, size_t payload_len,
 		       struct wpabuf *buf);
 void hs20_parse_rx_hs20_anqp_resp(struct wpa_supplicant *wpa_s,
-				  const u8 *sa, const u8 *data, size_t slen);
+				  struct wpa_bss *bss, const u8 *sa,
+				  const u8 *data, size_t slen);
 int is_hs20_network(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid,
 		    struct wpa_bss *bss);
 int hs20_get_pps_mo_id(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid);
diff --git a/wpa_supplicant/ibss_rsn.c b/wpa_supplicant/ibss_rsn.c
index 3083dd8..6299191 100644
--- a/wpa_supplicant/ibss_rsn.c
+++ b/wpa_supplicant/ibss_rsn.c
@@ -72,7 +72,7 @@
 	if (wpa_s->l2)
 		return l2_packet_send(wpa_s->l2, dest, proto, buf, len);
 
-	return wpa_drv_send_eapol(wpa_s, dest, proto, buf, len);
+	return -1;
 }
 
 
@@ -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);
+	wpa_sm_set_pmk(peer->supp, psk, PMK_LEN, NULL);
 
 	peer->supp_ie_len = sizeof(peer->supp_ie);
 	if (wpa_sm_set_assoc_wpa_ie_default(peer->supp, peer->supp_ie,
@@ -283,7 +283,7 @@
 		return l2_packet_send(wpa_s->l2, addr, ETH_P_EAPOL, data,
 				      data_len);
 
-	return wpa_drv_send_eapol(wpa_s, addr, ETH_P_EAPOL, data, data_len);
+	return -1;
 }
 
 
@@ -571,6 +571,9 @@
 	struct ibss_rsn_peer *peer;
 	int res;
 
+	if (!ibss_rsn)
+		return -1;
+
 	/* if the peer already exists, exit immediately */
 	peer = ibss_rsn_get_peer(ibss_rsn, addr);
 	if (peer)
diff --git a/wpa_supplicant/interworking.c b/wpa_supplicant/interworking.c
index 19b6e38..fd47c17 100644
--- a/wpa_supplicant/interworking.c
+++ b/wpa_supplicant/interworking.c
@@ -73,17 +73,23 @@
 
 static void interworking_reconnect(struct wpa_supplicant *wpa_s)
 {
+	unsigned int tried;
+
 	if (wpa_s->wpa_state >= WPA_AUTHENTICATING) {
 		wpa_supplicant_cancel_sched_scan(wpa_s);
+		wpa_s->own_disconnect_req = 1;
 		wpa_supplicant_deauthenticate(wpa_s,
 					      WLAN_REASON_DEAUTH_LEAVING);
 	}
 	wpa_s->disconnected = 0;
 	wpa_s->reassociate = 1;
+	tried = wpa_s->interworking_fast_assoc_tried;
+	wpa_s->interworking_fast_assoc_tried = 1;
 
-	if (wpa_supplicant_fast_associate(wpa_s) >= 0)
+	if (!tried && wpa_supplicant_fast_associate(wpa_s) >= 0)
 		return;
 
+	wpa_s->interworking_fast_assoc_tried = 0;
 	wpa_supplicant_req_scan(wpa_s, 0, 0);
 }
 
@@ -245,8 +251,8 @@
 	struct wpabuf *extra = NULL;
 	int all = wpa_s->fetch_all_anqp;
 
-	wpa_printf(MSG_DEBUG, "Interworking: ANQP Query Request to " MACSTR,
-		   MAC2STR(bss->bssid));
+	wpa_msg(wpa_s, MSG_DEBUG, "Interworking: ANQP Query Request to " MACSTR,
+		MAC2STR(bss->bssid));
 	wpa_s->interworking_gas_bss = bss;
 
 	info_ids[num_info_ids++] = ANQP_CAPABILITY_LIST;
@@ -307,14 +313,14 @@
 	res = gas_query_req(wpa_s->gas, bss->bssid, bss->freq, buf,
 			    interworking_anqp_resp_cb, wpa_s);
 	if (res < 0) {
-		wpa_printf(MSG_DEBUG, "ANQP: Failed to send Query Request");
+		wpa_msg(wpa_s, MSG_DEBUG, "ANQP: Failed to send Query Request");
 		wpabuf_free(buf);
 		ret = -1;
 		eloop_register_timeout(0, 0, interworking_continue_anqp, wpa_s,
 				       NULL);
 	} else
-		wpa_printf(MSG_DEBUG, "ANQP: Query started with dialog token "
-			   "%u", res);
+		wpa_msg(wpa_s, MSG_DEBUG,
+			"ANQP: Query started with dialog token %u", res);
 
 	return ret;
 }
@@ -508,20 +514,25 @@
 	struct nai_realm *realm;
 	const u8 *pos, *end;
 	u16 i, num;
+	size_t left;
 
-	if (anqp == NULL || wpabuf_len(anqp) < 2)
+	if (anqp == NULL)
+		return NULL;
+	left = wpabuf_len(anqp);
+	if (left < 2)
 		return NULL;
 
 	pos = wpabuf_head_u8(anqp);
-	end = pos + wpabuf_len(anqp);
+	end = pos + left;
 	num = WPA_GET_LE16(pos);
 	wpa_printf(MSG_DEBUG, "NAI Realm Count: %u", num);
 	pos += 2;
+	left -= 2;
 
-	if (num * 5 > end - pos) {
+	if (num > left / 5) {
 		wpa_printf(MSG_DEBUG, "Invalid NAI Realm Count %u - not "
 			   "enough data (%u octets) for that many realms",
-			   num, (unsigned int) (end - pos));
+			   num, (unsigned int) left);
 		return NULL;
 	}
 
@@ -577,56 +588,69 @@
 }
 
 
-static int nai_realm_cred_username(struct nai_realm_eap *eap)
+static int nai_realm_cred_username(struct wpa_supplicant *wpa_s,
+				   struct nai_realm_eap *eap)
 {
-	if (eap_get_name(EAP_VENDOR_IETF, eap->method) == NULL)
+	if (eap_get_name(EAP_VENDOR_IETF, eap->method) == NULL) {
+		wpa_msg(wpa_s, MSG_DEBUG,
+			"nai-realm-cred-username: EAP method not supported: %d",
+			eap->method);
 		return 0; /* method not supported */
+	}
 
 	if (eap->method != EAP_TYPE_TTLS && eap->method != EAP_TYPE_PEAP &&
 	    eap->method != EAP_TYPE_FAST) {
 		/* Only tunneled methods with username/password supported */
+		wpa_msg(wpa_s, MSG_DEBUG,
+			"nai-realm-cred-username: Method: %d is not TTLS, PEAP, or FAST",
+			eap->method);
 		return 0;
 	}
 
 	if (eap->method == EAP_TYPE_PEAP || eap->method == EAP_TYPE_FAST) {
 		if (eap->inner_method &&
-		    eap_get_name(EAP_VENDOR_IETF, eap->inner_method) == NULL)
+		    eap_get_name(EAP_VENDOR_IETF, eap->inner_method) == NULL) {
+			wpa_msg(wpa_s, MSG_DEBUG,
+				"nai-realm-cred-username: PEAP/FAST: Inner method not supported: %d",
+				eap->inner_method);
 			return 0;
+		}
 		if (!eap->inner_method &&
-		    eap_get_name(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2) == NULL)
+		    eap_get_name(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2) == NULL) {
+			wpa_msg(wpa_s, MSG_DEBUG,
+				"nai-realm-cred-username: MSCHAPv2 not supported");
 			return 0;
+		}
 	}
 
 	if (eap->method == EAP_TYPE_TTLS) {
 		if (eap->inner_method == 0 && eap->inner_non_eap == 0)
 			return 1; /* Assume TTLS/MSCHAPv2 is used */
 		if (eap->inner_method &&
-		    eap_get_name(EAP_VENDOR_IETF, eap->inner_method) == NULL)
+		    eap_get_name(EAP_VENDOR_IETF, eap->inner_method) == NULL) {
+			wpa_msg(wpa_s, MSG_DEBUG,
+				"nai-realm-cred-username: TTLS, but inner not supported: %d",
+				eap->inner_method);
 			return 0;
+		}
 		if (eap->inner_non_eap &&
 		    eap->inner_non_eap != NAI_REALM_INNER_NON_EAP_PAP &&
 		    eap->inner_non_eap != NAI_REALM_INNER_NON_EAP_CHAP &&
 		    eap->inner_non_eap != NAI_REALM_INNER_NON_EAP_MSCHAP &&
-		    eap->inner_non_eap != NAI_REALM_INNER_NON_EAP_MSCHAPV2)
+		    eap->inner_non_eap != NAI_REALM_INNER_NON_EAP_MSCHAPV2) {
+			wpa_msg(wpa_s, MSG_DEBUG,
+				"nai-realm-cred-username: TTLS, inner-non-eap not supported: %d",
+				eap->inner_non_eap);
 			return 0;
+		}
 	}
 
 	if (eap->inner_method &&
 	    eap->inner_method != EAP_TYPE_GTC &&
-	    eap->inner_method != EAP_TYPE_MSCHAPV2)
-		return 0;
-
-	return 1;
-}
-
-
-static int nai_realm_cred_cert(struct nai_realm_eap *eap)
-{
-	if (eap_get_name(EAP_VENDOR_IETF, eap->method) == NULL)
-		return 0; /* method not supported */
-
-	if (eap->method != EAP_TYPE_TLS) {
-		/* Only EAP-TLS supported for credential authentication */
+	    eap->inner_method != EAP_TYPE_MSCHAPV2) {
+		wpa_msg(wpa_s, MSG_DEBUG,
+			"nai-realm-cred-username: inner-method not GTC or MSCHAPv2: %d",
+			eap->inner_method);
 		return 0;
 	}
 
@@ -634,27 +658,55 @@
 }
 
 
-static struct nai_realm_eap * nai_realm_find_eap(struct wpa_cred *cred,
+static int nai_realm_cred_cert(struct wpa_supplicant *wpa_s,
+			       struct nai_realm_eap *eap)
+{
+	if (eap_get_name(EAP_VENDOR_IETF, eap->method) == NULL) {
+		wpa_msg(wpa_s, MSG_DEBUG,
+			"nai-realm-cred-cert: Method not supported: %d",
+			eap->method);
+		return 0; /* method not supported */
+	}
+
+	if (eap->method != EAP_TYPE_TLS) {
+		/* Only EAP-TLS supported for credential authentication */
+		wpa_msg(wpa_s, MSG_DEBUG,
+			"nai-realm-cred-cert: Method not TLS: %d",
+			eap->method);
+		return 0;
+	}
+
+	return 1;
+}
+
+
+static struct nai_realm_eap * nai_realm_find_eap(struct wpa_supplicant *wpa_s,
+						 struct wpa_cred *cred,
 						 struct nai_realm *realm)
 {
 	u8 e;
 
-	if (cred == NULL ||
-	    cred->username == NULL ||
+	if (cred->username == NULL ||
 	    cred->username[0] == '\0' ||
 	    ((cred->password == NULL ||
 	      cred->password[0] == '\0') &&
 	     (cred->private_key == NULL ||
-	      cred->private_key[0] == '\0')))
+	      cred->private_key[0] == '\0'))) {
+		wpa_msg(wpa_s, MSG_DEBUG,
+			"nai-realm-find-eap: incomplete cred info: username: %s  password: %s private_key: %s",
+			cred->username ? cred->username : "NULL",
+			cred->password ? cred->password : "NULL",
+			cred->private_key ? cred->private_key : "NULL");
 		return NULL;
+	}
 
 	for (e = 0; e < realm->eap_count; e++) {
 		struct nai_realm_eap *eap = &realm->eap[e];
 		if (cred->password && cred->password[0] &&
-		    nai_realm_cred_username(eap))
+		    nai_realm_cred_username(wpa_s, eap))
 			return eap;
 		if (cred->private_key && cred->private_key[0] &&
-		    nai_realm_cred_cert(eap))
+		    nai_realm_cred_cert(wpa_s, eap))
 			return eap;
 	}
 
@@ -864,6 +916,7 @@
 	if (ssid == wpa_s->current_ssid) {
 		wpa_sm_set_config(wpa_s->wpa, NULL);
 		eapol_sm_notify_config(wpa_s->eapol, NULL, NULL);
+		wpa_s->own_disconnect_req = 1;
 		wpa_supplicant_deauthenticate(wpa_s,
 					      WLAN_REASON_DEAUTH_LEAVING);
 	}
@@ -904,7 +957,7 @@
 
 static int interworking_connect_3gpp(struct wpa_supplicant *wpa_s,
 				     struct wpa_cred *cred,
-				     struct wpa_bss *bss)
+				     struct wpa_bss *bss, int only_add)
 {
 #ifdef INTERWORKING_3GPP
 	struct wpa_ssid *ssid;
@@ -915,13 +968,13 @@
 	if (bss->anqp == NULL || bss->anqp->anqp_3gpp == NULL)
 		return -1;
 
-	wpa_printf(MSG_DEBUG, "Interworking: Connect with " MACSTR " (3GPP)",
-		   MAC2STR(bss->bssid));
+	wpa_msg(wpa_s, MSG_DEBUG, "Interworking: Connect with " MACSTR
+		" (3GPP)", MAC2STR(bss->bssid));
 
 	if (already_connected(wpa_s, cred, bss)) {
 		wpa_msg(wpa_s, MSG_INFO, INTERWORKING_ALREADY_CONNECTED MACSTR,
 			MAC2STR(bss->bssid));
-		return 0;
+		return wpa_s->current_ssid->id;
 	}
 
 	remove_duplicate_network(wpa_s, cred, bss);
@@ -973,13 +1026,13 @@
 		break;
 	}
 	if (res < 0) {
-		wpa_printf(MSG_DEBUG, "Selected EAP method (%d) not supported",
-			   eap_type);
+		wpa_msg(wpa_s, MSG_DEBUG,
+			"Selected EAP method (%d) not supported", eap_type);
 		goto fail;
 	}
 
 	if (!cred->pcsc && set_root_nai(ssid, cred->imsi, prefix) < 0) {
-		wpa_printf(MSG_DEBUG, "Failed to set Root NAI");
+		wpa_msg(wpa_s, MSG_DEBUG, "Failed to set Root NAI");
 		goto fail;
 	}
 
@@ -998,9 +1051,10 @@
 
 	wpa_s->next_ssid = ssid;
 	wpa_config_update_prio_list(wpa_s->conf);
-	interworking_reconnect(wpa_s);
+	if (!only_add)
+		interworking_reconnect(wpa_s);
 
-	return 0;
+	return ssid->id;
 
 fail:
 	wpas_notify_network_removed(wpa_s, ssid);
@@ -1448,17 +1502,17 @@
 
 static int interworking_connect_roaming_consortium(
 	struct wpa_supplicant *wpa_s, struct wpa_cred *cred,
-	struct wpa_bss *bss)
+	struct wpa_bss *bss, int only_add)
 {
 	struct wpa_ssid *ssid;
 
-	wpa_printf(MSG_DEBUG, "Interworking: Connect with " MACSTR " based on "
-		   "roaming consortium match", MAC2STR(bss->bssid));
+	wpa_msg(wpa_s, MSG_DEBUG, "Interworking: Connect with " MACSTR
+		" based on roaming consortium match", MAC2STR(bss->bssid));
 
 	if (already_connected(wpa_s, cred, bss)) {
 		wpa_msg(wpa_s, MSG_INFO, INTERWORKING_ALREADY_CONNECTED MACSTR,
 			MAC2STR(bss->bssid));
-		return 0;
+		return wpa_s->current_ssid->id;
 	}
 
 	remove_duplicate_network(wpa_s, cred, bss);
@@ -1481,8 +1535,8 @@
 		goto fail;
 
 	if (cred->eap_method == NULL) {
-		wpa_printf(MSG_DEBUG, "Interworking: No EAP method set for "
-			   "credential using roaming consortium");
+		wpa_msg(wpa_s, MSG_DEBUG,
+			"Interworking: No EAP method set for credential using roaming consortium");
 		goto fail;
 	}
 
@@ -1494,9 +1548,10 @@
 
 	wpa_s->next_ssid = ssid;
 	wpa_config_update_prio_list(wpa_s->conf);
-	interworking_reconnect(wpa_s);
+	if (!only_add)
+		interworking_reconnect(wpa_s);
 
-	return 0;
+	return ssid->id;
 
 fail:
 	wpas_notify_network_removed(wpa_s, ssid);
@@ -1506,7 +1561,8 @@
 
 
 static int interworking_connect_helper(struct wpa_supplicant *wpa_s,
-				       struct wpa_bss *bss, int allow_excluded)
+				       struct wpa_bss *bss, int allow_excluded,
+				       int only_add)
 {
 	struct wpa_cred *cred, *cred_rc, *cred_3gpp;
 	struct wpa_ssid *ssid;
@@ -1521,8 +1577,9 @@
 		return -1;
 	if (disallowed_bssid(wpa_s, bss->bssid) ||
 	    disallowed_ssid(wpa_s, bss->ssid, bss->ssid_len)) {
-		wpa_printf(MSG_DEBUG, "Interworking: Reject connection to disallowed BSS "
-			   MACSTR, MAC2STR(bss->bssid));
+		wpa_msg(wpa_s, MSG_DEBUG,
+			"Interworking: Reject connection to disallowed BSS "
+			MACSTR, MAC2STR(bss->bssid));
 		return -1;
 	}
 
@@ -1535,27 +1592,26 @@
 		 * We currently support only HS 2.0 networks and those are
 		 * required to use WPA2-Enterprise.
 		 */
-		wpa_printf(MSG_DEBUG, "Interworking: Network does not use "
-			   "RSN");
+		wpa_msg(wpa_s, MSG_DEBUG,
+			"Interworking: Network does not use RSN");
 		return -1;
 	}
 
 	cred_rc = interworking_credentials_available_roaming_consortium(
 		wpa_s, bss, 0, excl);
 	if (cred_rc) {
-		wpa_printf(MSG_DEBUG, "Interworking: Highest roaming "
-			   "consortium matching credential priority %d "
-			   "sp_priority %d",
-			   cred_rc->priority, cred_rc->sp_priority);
+		wpa_msg(wpa_s, MSG_DEBUG,
+			"Interworking: Highest roaming consortium matching credential priority %d sp_priority %d",
+			cred_rc->priority, cred_rc->sp_priority);
 		if (allow_excluded && excl && !(*excl))
 			excl = NULL;
 	}
 
 	cred = interworking_credentials_available_realm(wpa_s, bss, 0, excl);
 	if (cred) {
-		wpa_printf(MSG_DEBUG, "Interworking: Highest NAI Realm list "
-			   "matching credential priority %d sp_priority %d",
-			   cred->priority, cred->sp_priority);
+		wpa_msg(wpa_s, MSG_DEBUG,
+			"Interworking: Highest NAI Realm list matching credential priority %d sp_priority %d",
+			cred->priority, cred->sp_priority);
 		if (allow_excluded && excl && !(*excl))
 			excl = NULL;
 	}
@@ -1563,22 +1619,22 @@
 	cred_3gpp = interworking_credentials_available_3gpp(wpa_s, bss, 0,
 							    excl);
 	if (cred_3gpp) {
-		wpa_printf(MSG_DEBUG, "Interworking: Highest 3GPP matching "
-			   "credential priority %d sp_priority %d",
-			   cred_3gpp->priority, cred_3gpp->sp_priority);
+		wpa_msg(wpa_s, MSG_DEBUG,
+			"Interworking: Highest 3GPP matching credential priority %d sp_priority %d",
+			cred_3gpp->priority, cred_3gpp->sp_priority);
 		if (allow_excluded && excl && !(*excl))
 			excl = NULL;
 	}
 
 	if (!cred_rc && !cred && !cred_3gpp) {
-		wpa_printf(MSG_DEBUG, "Interworking: No full credential matches - consider options without BW(etc.) limits");
+		wpa_msg(wpa_s, MSG_DEBUG,
+			"Interworking: No full credential matches - consider options without BW(etc.) limits");
 		cred_rc = interworking_credentials_available_roaming_consortium(
 			wpa_s, bss, 1, excl);
 		if (cred_rc) {
-			wpa_printf(MSG_DEBUG, "Interworking: Highest roaming "
-				   "consortium matching credential priority %d "
-				   "sp_priority %d (ignore BW)",
-				   cred_rc->priority, cred_rc->sp_priority);
+			wpa_msg(wpa_s, MSG_DEBUG,
+				"Interworking: Highest roaming consortium matching credential priority %d sp_priority %d (ignore BW)",
+				cred_rc->priority, cred_rc->sp_priority);
 			if (allow_excluded && excl && !(*excl))
 				excl = NULL;
 		}
@@ -1586,10 +1642,9 @@
 		cred = interworking_credentials_available_realm(wpa_s, bss, 1,
 								excl);
 		if (cred) {
-			wpa_printf(MSG_DEBUG, "Interworking: Highest NAI Realm "
-				   "list matching credential priority %d "
-				   "sp_priority %d (ignore BW)",
-				   cred->priority, cred->sp_priority);
+			wpa_msg(wpa_s, MSG_DEBUG,
+				"Interworking: Highest NAI Realm list matching credential priority %d sp_priority %d (ignore BW)",
+				cred->priority, cred->sp_priority);
 			if (allow_excluded && excl && !(*excl))
 				excl = NULL;
 		}
@@ -1597,10 +1652,9 @@
 		cred_3gpp = interworking_credentials_available_3gpp(wpa_s, bss,
 								    1, excl);
 		if (cred_3gpp) {
-			wpa_printf(MSG_DEBUG, "Interworking: Highest 3GPP "
-				   "matching credential priority %d "
-				   "sp_priority %d (ignore BW)",
-				   cred_3gpp->priority, cred_3gpp->sp_priority);
+			wpa_msg(wpa_s, MSG_DEBUG,
+				"Interworking: Highest 3GPP matching credential priority %d sp_priority %d (ignore BW)",
+				cred_3gpp->priority, cred_3gpp->sp_priority);
 			if (allow_excluded && excl && !(*excl))
 				excl = NULL;
 		}
@@ -1610,45 +1664,48 @@
 	    (cred == NULL || cred_prio_cmp(cred_rc, cred) >= 0) &&
 	    (cred_3gpp == NULL || cred_prio_cmp(cred_rc, cred_3gpp) >= 0))
 		return interworking_connect_roaming_consortium(wpa_s, cred_rc,
-							       bss);
+							       bss, only_add);
 
 	if (cred_3gpp &&
 	    (cred == NULL || cred_prio_cmp(cred_3gpp, cred) >= 0)) {
-		return interworking_connect_3gpp(wpa_s, cred_3gpp, bss);
+		return interworking_connect_3gpp(wpa_s, cred_3gpp, bss,
+						 only_add);
 	}
 
 	if (cred == NULL) {
-		wpa_printf(MSG_DEBUG, "Interworking: No matching credentials "
-			   "found for " MACSTR, MAC2STR(bss->bssid));
+		wpa_msg(wpa_s, MSG_DEBUG,
+			"Interworking: No matching credentials found for "
+			MACSTR, MAC2STR(bss->bssid));
 		return -1;
 	}
 
 	realm = nai_realm_parse(bss->anqp ? bss->anqp->nai_realm : NULL,
 				&count);
 	if (realm == NULL) {
-		wpa_printf(MSG_DEBUG, "Interworking: Could not parse NAI "
-			   "Realm list from " MACSTR, MAC2STR(bss->bssid));
+		wpa_msg(wpa_s, MSG_DEBUG,
+			"Interworking: Could not parse NAI Realm list from "
+			MACSTR, MAC2STR(bss->bssid));
 		return -1;
 	}
 
 	for (i = 0; i < count; i++) {
 		if (!nai_realm_match(&realm[i], cred->realm))
 			continue;
-		eap = nai_realm_find_eap(cred, &realm[i]);
+		eap = nai_realm_find_eap(wpa_s, cred, &realm[i]);
 		if (eap)
 			break;
 	}
 
 	if (!eap) {
-		wpa_printf(MSG_DEBUG, "Interworking: No matching credentials "
-			   "and EAP method found for " MACSTR,
-			   MAC2STR(bss->bssid));
+		wpa_msg(wpa_s, MSG_DEBUG,
+			"Interworking: No matching credentials and EAP method found for "
+			MACSTR, MAC2STR(bss->bssid));
 		nai_realm_free(realm, count);
 		return -1;
 	}
 
-	wpa_printf(MSG_DEBUG, "Interworking: Connect with " MACSTR,
-		   MAC2STR(bss->bssid));
+	wpa_msg(wpa_s, MSG_DEBUG, "Interworking: Connect with " MACSTR,
+		MAC2STR(bss->bssid));
 
 	if (already_connected(wpa_s, cred, bss)) {
 		wpa_msg(wpa_s, MSG_INFO, INTERWORKING_ALREADY_CONNECTED MACSTR,
@@ -1750,9 +1807,10 @@
 
 	wpa_s->next_ssid = ssid;
 	wpa_config_update_prio_list(wpa_s->conf);
-	interworking_reconnect(wpa_s);
+	if (!only_add)
+		interworking_reconnect(wpa_s);
 
-	return 0;
+	return ssid->id;
 
 fail:
 	wpas_notify_network_removed(wpa_s, ssid);
@@ -1762,9 +1820,10 @@
 }
 
 
-int interworking_connect(struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
+int interworking_connect(struct wpa_supplicant *wpa_s, struct wpa_bss *bss,
+			 int only_add)
 {
-	return interworking_connect_helper(wpa_s, bss, 1);
+	return interworking_connect_helper(wpa_s, bss, 1, only_add);
 }
 
 
@@ -1803,22 +1862,29 @@
 	int ret;
 	int is_excluded = 0;
 
-	if (bss->anqp == NULL || bss->anqp->anqp_3gpp == NULL)
+	if (bss->anqp == NULL || bss->anqp->anqp_3gpp == NULL) {
+		wpa_msg(wpa_s, MSG_DEBUG,
+			"interworking-avail-3gpp: not avail, anqp: %p  anqp_3gpp: %p",
+			bss->anqp, bss->anqp ? bss->anqp->anqp_3gpp : NULL);
 		return NULL;
+	}
 
 #ifdef CONFIG_EAP_PROXY
 	if (!wpa_s->imsi[0]) {
 		size_t len;
-		wpa_printf(MSG_DEBUG, "Interworking: IMSI not available - try to read again through eap_proxy");
+		wpa_msg(wpa_s, MSG_DEBUG,
+			"Interworking: IMSI not available - try to read again through eap_proxy");
 		wpa_s->mnc_len = eapol_sm_get_eap_proxy_imsi(wpa_s->eapol,
 							     wpa_s->imsi,
 							     &len);
 		if (wpa_s->mnc_len > 0) {
 			wpa_s->imsi[len] = '\0';
-			wpa_printf(MSG_DEBUG, "eap_proxy: IMSI %s (MNC length %d)",
-				   wpa_s->imsi, wpa_s->mnc_len);
+			wpa_msg(wpa_s, MSG_DEBUG,
+				"eap_proxy: IMSI %s (MNC length %d)",
+				wpa_s->imsi, wpa_s->mnc_len);
 		} else {
-			wpa_printf(MSG_DEBUG, "eap_proxy: IMSI not available");
+			wpa_msg(wpa_s, MSG_DEBUG,
+				"eap_proxy: IMSI not available");
 		}
 	}
 #endif /* CONFIG_EAP_PROXY */
@@ -1869,10 +1935,12 @@
 #if defined(PCSC_FUNCS) || defined(CONFIG_EAP_PROXY)
 	compare:
 #endif /* PCSC_FUNCS || CONFIG_EAP_PROXY */
-		wpa_printf(MSG_DEBUG, "Interworking: Parsing 3GPP info from "
-			   MACSTR, MAC2STR(bss->bssid));
+		wpa_msg(wpa_s, MSG_DEBUG,
+			"Interworking: Parsing 3GPP info from " MACSTR,
+			MAC2STR(bss->bssid));
 		ret = plmn_id_match(bss->anqp->anqp_3gpp, imsi, mnc_len);
-		wpa_printf(MSG_DEBUG, "PLMN match %sfound", ret ? "" : "not ");
+		wpa_msg(wpa_s, MSG_DEBUG, "PLMN match %sfound",
+			ret ? "" : "not ");
 		if (ret) {
 			if (cred_no_required_oi_match(cred, bss))
 				continue;
@@ -1924,12 +1992,13 @@
 	if (wpa_s->conf->cred == NULL)
 		return NULL;
 
-	wpa_printf(MSG_DEBUG, "Interworking: Parsing NAI Realm list from "
-		   MACSTR, MAC2STR(bss->bssid));
+	wpa_msg(wpa_s, MSG_DEBUG, "Interworking: Parsing NAI Realm list from "
+		MACSTR, MAC2STR(bss->bssid));
 	realm = nai_realm_parse(bss->anqp->nai_realm, &count);
 	if (realm == NULL) {
-		wpa_printf(MSG_DEBUG, "Interworking: Could not parse NAI "
-			   "Realm list from " MACSTR, MAC2STR(bss->bssid));
+		wpa_msg(wpa_s, MSG_DEBUG,
+			"Interworking: Could not parse NAI Realm list from "
+			MACSTR, MAC2STR(bss->bssid));
 		return NULL;
 	}
 
@@ -1940,7 +2009,7 @@
 		for (i = 0; i < count; i++) {
 			if (!nai_realm_match(&realm[i], cred->realm))
 				continue;
-			if (nai_realm_find_eap(cred, &realm[i])) {
+			if (nai_realm_find_eap(wpa_s, cred, &realm[i])) {
 				if (cred_no_required_oi_match(cred, bss))
 					continue;
 				if (!ignore_bw &&
@@ -1968,6 +2037,9 @@
 					}
 				}
 				break;
+			} else {
+				wpa_msg(wpa_s, MSG_DEBUG,
+					"Interworking: realm-find-eap returned false");
 			}
 		}
 	}
@@ -1986,7 +2058,7 @@
 	int *excluded)
 {
 	struct wpa_cred *cred, *cred2;
-	int excluded1, excluded2;
+	int excluded1, excluded2 = 0;
 
 	if (disallowed_bssid(wpa_s, bss->bssid) ||
 	    disallowed_ssid(wpa_s, bss->ssid, bss->ssid_len)) {
@@ -2108,8 +2180,9 @@
 		realm = os_strchr(nai, '@');
 		if (realm)
 			realm++;
-		wpa_printf(MSG_DEBUG, "Interworking: Search for match "
-			   "with SIM/USIM domain %s", realm);
+		wpa_msg(wpa_s, MSG_DEBUG,
+			"Interworking: Search for match with SIM/USIM domain %s",
+			realm);
 		if (realm &&
 		    domain_name_list_contains(domain_names, realm, 1))
 			return 1;
@@ -2122,8 +2195,9 @@
 		return ret;
 
 	for (i = 0; i < cred->num_domain; i++) {
-		wpa_printf(MSG_DEBUG, "Interworking: Search for match with "
-			   "home SP FQDN %s", cred->domain[i]);
+		wpa_msg(wpa_s, MSG_DEBUG,
+			"Interworking: Search for match with home SP FQDN %s",
+			cred->domain[i]);
 		if (domain_name_list_contains(domain_names, cred->domain[i], 1))
 			return 1;
 	}
@@ -2299,14 +2373,16 @@
 							  &excluded);
 		if (!cred)
 			continue;
+
 		if (!wpa_bss_get_ie(bss, WLAN_EID_RSN)) {
 			/*
 			 * We currently support only HS 2.0 networks and those
 			 * are required to use WPA2-Enterprise.
 			 */
-			wpa_printf(MSG_DEBUG, "Interworking: Credential match "
-				   "with " MACSTR " but network does not use "
-				   "RSN", MAC2STR(bss->bssid));
+			wpa_msg(wpa_s, MSG_DEBUG,
+				"Interworking: Credential match with " MACSTR
+				" but network does not use RSN",
+				MAC2STR(bss->bssid));
 			continue;
 		}
 		if (!excluded)
@@ -2397,8 +2473,8 @@
 		 * have matching APs.
 		 */
 		if (interworking_find_network_match(wpa_s)) {
-			wpa_printf(MSG_DEBUG, "Interworking: Possible BSS "
-				   "match for enabled network configurations");
+			wpa_msg(wpa_s, MSG_DEBUG,
+				"Interworking: Possible BSS match for enabled network configurations");
 			if (wpa_s->auto_select) {
 				interworking_reconnect(wpa_s);
 				return;
@@ -2406,8 +2482,8 @@
 		}
 
 		if (wpa_s->auto_network_select) {
-			wpa_printf(MSG_DEBUG, "Interworking: Continue "
-				   "scanning after ANQP fetch");
+			wpa_msg(wpa_s, MSG_DEBUG,
+				"Interworking: Continue scanning after ANQP fetch");
 			wpa_supplicant_req_scan(wpa_s, wpa_s->scan_interval,
 						0);
 			return;
@@ -2415,6 +2491,8 @@
 
 		wpa_msg(wpa_s, MSG_INFO, INTERWORKING_NO_MATCH "No network "
 			"with matching credentials found");
+		if (wpa_s->wpa_state == WPA_SCANNING)
+			wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED);
 	}
 
 	if (selected) {
@@ -2427,7 +2505,7 @@
 			   MAC2STR(selected->bssid));
 		wpa_msg(wpa_s, MSG_INFO, INTERWORKING_SELECTED MACSTR,
 			MAC2STR(selected->bssid));
-		interworking_connect(wpa_s, selected);
+		interworking_connect(wpa_s, selected, 0);
 	}
 }
 
@@ -2458,9 +2536,10 @@
 		    os_memcmp(bss->ssid, other->ssid, bss->ssid_len) != 0)
 			continue;
 
-		wpa_printf(MSG_DEBUG, "Interworking: Share ANQP data with "
-			   "already fetched BSSID " MACSTR " and " MACSTR,
-			   MAC2STR(other->bssid), MAC2STR(bss->bssid));
+		wpa_msg(wpa_s, MSG_DEBUG,
+			"Interworking: Share ANQP data with already fetched BSSID "
+			MACSTR " and " MACSTR,
+			MAC2STR(other->bssid), MAC2STR(bss->bssid));
 		other->anqp->users++;
 		return other->anqp;
 	}
@@ -2525,6 +2604,7 @@
 	if (found == 0) {
 		if (wpa_s->fetch_osu_info) {
 			if (wpa_s->num_prov_found == 0 &&
+			    wpa_s->fetch_osu_waiting_scan &&
 			    wpa_s->num_osu_scans < 3) {
 				wpa_printf(MSG_DEBUG, "HS 2.0: No OSU providers seen - try to scan again");
 				hs20_start_osu_scan(wpa_s);
@@ -2550,7 +2630,12 @@
 		bss->flags &= ~WPA_BSS_ANQP_FETCH_TRIED;
 
 	wpa_s->fetch_anqp_in_progress = 1;
-	interworking_next_anqp_fetch(wpa_s);
+
+	/*
+	 * Start actual ANQP operation from eloop call to make sure the loop
+	 * does not end up using excessive recursion.
+	 */
+	eloop_register_timeout(0, 0, interworking_continue_anqp, wpa_s, NULL);
 }
 
 
@@ -2588,17 +2673,20 @@
 	struct wpa_bss *bss;
 	int res;
 
-	freq = wpa_s->assoc_freq;
 	bss = wpa_bss_get_bssid(wpa_s, dst);
-	if (bss) {
-		wpa_bss_anqp_unshare_alloc(bss);
-		freq = bss->freq;
-	}
-	if (freq <= 0)
+	if (!bss) {
+		wpa_printf(MSG_WARNING,
+			   "ANQP: Cannot send query to unknown BSS "
+			   MACSTR, MAC2STR(dst));
 		return -1;
+	}
 
-	wpa_printf(MSG_DEBUG, "ANQP: Query Request to " MACSTR " for %u id(s)",
-		   MAC2STR(dst), (unsigned int) num_ids);
+	wpa_bss_anqp_unshare_alloc(bss);
+	freq = bss->freq;
+
+	wpa_msg(wpa_s, MSG_DEBUG,
+		"ANQP: Query Request to " MACSTR " for %u id(s)",
+		MAC2STR(dst), (unsigned int) num_ids);
 
 #ifdef CONFIG_HS20
 	if (subtypes != 0) {
@@ -2616,12 +2704,13 @@
 
 	res = gas_query_req(wpa_s->gas, dst, freq, buf, anqp_resp_cb, wpa_s);
 	if (res < 0) {
-		wpa_printf(MSG_DEBUG, "ANQP: Failed to send Query Request");
+		wpa_msg(wpa_s, MSG_DEBUG, "ANQP: Failed to send Query Request");
 		wpabuf_free(buf);
 		ret = -1;
-	} else
-		wpa_printf(MSG_DEBUG, "ANQP: Query started with dialog token "
-			   "%u", res);
+	} else {
+		wpa_msg(wpa_s, MSG_DEBUG,
+			"ANQP: Query started with dialog token %u", res);
+	}
 
 	return ret;
 }
@@ -2645,6 +2734,12 @@
 	case ANQP_CAPABILITY_LIST:
 		wpa_msg(wpa_s, MSG_INFO, "RX-ANQP " MACSTR
 			" ANQP Capability list", MAC2STR(sa));
+		wpa_hexdump_ascii(MSG_DEBUG, "ANQP: Capability list",
+				  pos, slen);
+		if (anqp) {
+			wpabuf_free(anqp->capability_list);
+			anqp->capability_list = wpabuf_alloc_copy(pos, slen);
+		}
 		break;
 	case ANQP_VENUE_NAME:
 		wpa_msg(wpa_s, MSG_INFO, "RX-ANQP " MACSTR
@@ -2733,26 +2828,27 @@
 
 			switch (type) {
 			case HS20_ANQP_OUI_TYPE:
-				hs20_parse_rx_hs20_anqp_resp(wpa_s, sa, pos,
-							     slen);
+				hs20_parse_rx_hs20_anqp_resp(wpa_s, bss, sa,
+							     pos, slen);
 				break;
 			default:
-				wpa_printf(MSG_DEBUG, "HS20: Unsupported ANQP "
-					   "vendor type %u", type);
+				wpa_msg(wpa_s, MSG_DEBUG,
+					"HS20: Unsupported ANQP vendor type %u",
+					type);
 				break;
 			}
 			break;
 #endif /* CONFIG_HS20 */
 		default:
-			wpa_printf(MSG_DEBUG, "Interworking: Unsupported "
-				   "vendor-specific ANQP OUI %06x",
-				   WPA_GET_BE24(pos));
+			wpa_msg(wpa_s, MSG_DEBUG,
+				"Interworking: Unsupported vendor-specific ANQP OUI %06x",
+				WPA_GET_BE24(pos));
 			return;
 		}
 		break;
 	default:
-		wpa_printf(MSG_DEBUG, "Interworking: Unsupported ANQP Info ID "
-			   "%u", info_id);
+		wpa_msg(wpa_s, MSG_DEBUG,
+			"Interworking: Unsupported ANQP Info ID %u", info_id);
 		break;
 	}
 }
@@ -2769,6 +2865,7 @@
 	u16 info_id;
 	u16 slen;
 	struct wpa_bss *bss = NULL, *tmp;
+	const char *anqp_result = "SUCCESS";
 
 	wpa_printf(MSG_DEBUG, "Interworking: anqp_resp_cb dst=" MACSTR
 		   " dialog_token=%u result=%d status_code=%u",
@@ -2776,17 +2873,19 @@
 	if (result != GAS_QUERY_SUCCESS) {
 		if (wpa_s->fetch_osu_icon_in_progress)
 			hs20_icon_fetch_failed(wpa_s);
-		return;
+		anqp_result = "FAILURE";
+		goto out;
 	}
 
 	pos = wpabuf_head(adv_proto);
 	if (wpabuf_len(adv_proto) < 4 || pos[0] != WLAN_EID_ADV_PROTO ||
 	    pos[1] < 2 || pos[3] != ACCESS_NETWORK_QUERY_PROTOCOL) {
-		wpa_printf(MSG_DEBUG, "ANQP: Unexpected Advertisement "
-			   "Protocol in response");
+		wpa_msg(wpa_s, MSG_DEBUG,
+			"ANQP: Unexpected Advertisement Protocol in response");
 		if (wpa_s->fetch_osu_icon_in_progress)
 			hs20_icon_fetch_failed(wpa_s);
-		return;
+		anqp_result = "INVALID_FRAME";
+		goto out;
 	}
 
 	/*
@@ -2808,33 +2907,43 @@
 	end = pos + wpabuf_len(resp);
 
 	while (pos < end) {
-		if (pos + 4 > end) {
-			wpa_printf(MSG_DEBUG, "ANQP: Invalid element");
-			break;
+		unsigned int left = end - pos;
+
+		if (left < 4) {
+			wpa_msg(wpa_s, MSG_DEBUG, "ANQP: Invalid element");
+			anqp_result = "INVALID_FRAME";
+			goto out_parse_done;
 		}
 		info_id = WPA_GET_LE16(pos);
 		pos += 2;
 		slen = WPA_GET_LE16(pos);
 		pos += 2;
-		if (pos + slen > end) {
-			wpa_printf(MSG_DEBUG, "ANQP: Invalid element length "
-				   "for Info ID %u", info_id);
-			break;
+		left -= 4;
+		if (left < slen) {
+			wpa_msg(wpa_s, MSG_DEBUG,
+				"ANQP: Invalid element length for Info ID %u",
+				info_id);
+			anqp_result = "INVALID_FRAME";
+			goto out_parse_done;
 		}
 		interworking_parse_rx_anqp_resp(wpa_s, bss, dst, info_id, pos,
 						slen);
 		pos += slen;
 	}
 
+out_parse_done:
 	hs20_notify_parse_done(wpa_s);
+out:
+	wpa_msg(wpa_s, MSG_INFO, ANQP_QUERY_DONE "addr=" MACSTR " result=%s",
+		MAC2STR(dst), anqp_result);
 }
 
 
 static void interworking_scan_res_handler(struct wpa_supplicant *wpa_s,
 					  struct wpa_scan_results *scan_res)
 {
-	wpa_printf(MSG_DEBUG, "Interworking: Scan results available - start "
-		   "ANQP fetch");
+	wpa_msg(wpa_s, MSG_DEBUG,
+		"Interworking: Scan results available - start ANQP fetch");
 	interworking_start_fetch_anqp(wpa_s);
 }
 
@@ -2848,8 +2957,8 @@
 	wpa_s->auto_select = !!auto_select;
 	wpa_s->fetch_all_anqp = 0;
 	wpa_s->fetch_osu_info = 0;
-	wpa_printf(MSG_DEBUG, "Interworking: Start scan for network "
-		   "selection");
+	wpa_msg(wpa_s, MSG_DEBUG,
+		"Interworking: Start scan for network selection");
 	wpa_s->scan_res_handler = interworking_scan_res_handler;
 	wpa_s->normal_scans = 0;
 	wpa_s->scan_req = MANUAL_SCAN_REQ;
@@ -2910,8 +3019,8 @@
 	if (freq <= 0)
 		return -1;
 
-	wpa_printf(MSG_DEBUG, "GAS request to " MACSTR " (freq %d MHz)",
-		   MAC2STR(dst), freq);
+	wpa_msg(wpa_s, MSG_DEBUG, "GAS request to " MACSTR " (freq %d MHz)",
+		MAC2STR(dst), freq);
 	wpa_hexdump_buf(MSG_DEBUG, "Advertisement Protocol ID", adv_proto);
 	wpa_hexdump_buf(MSG_DEBUG, "GAS Query", query);
 
@@ -2937,12 +3046,12 @@
 
 	res = gas_query_req(wpa_s->gas, dst, freq, buf, gas_resp_cb, wpa_s);
 	if (res < 0) {
-		wpa_printf(MSG_DEBUG, "GAS: Failed to send Query Request");
+		wpa_msg(wpa_s, MSG_DEBUG, "GAS: Failed to send Query Request");
 		wpabuf_free(buf);
 		ret = -1;
 	} else
-		wpa_printf(MSG_DEBUG, "GAS: Query started with dialog token "
-			   "%u", res);
+		wpa_msg(wpa_s, MSG_DEBUG,
+			"GAS: Query started with dialog token %u", res);
 
 	return ret;
 }
diff --git a/wpa_supplicant/interworking.h b/wpa_supplicant/interworking.h
index 38ef745..3743dc0 100644
--- a/wpa_supplicant/interworking.h
+++ b/wpa_supplicant/interworking.h
@@ -24,7 +24,8 @@
 void interworking_stop_fetch_anqp(struct wpa_supplicant *wpa_s);
 int interworking_select(struct wpa_supplicant *wpa_s, int auto_select,
 			int *freqs);
-int interworking_connect(struct wpa_supplicant *wpa_s, struct wpa_bss *bss);
+int interworking_connect(struct wpa_supplicant *wpa_s, struct wpa_bss *bss,
+			 int only_add);
 void interworking_start_fetch_anqp(struct wpa_supplicant *wpa_s);
 int interworking_home_sp_cred(struct wpa_supplicant *wpa_s,
 			      struct wpa_cred *cred,
diff --git a/wpa_supplicant/main.c b/wpa_supplicant/main.c
index e596468..1c93306 100644
--- a/wpa_supplicant/main.c
+++ b/wpa_supplicant/main.c
@@ -237,7 +237,7 @@
 			goto out;
 #ifdef CONFIG_P2P
 		case 'm':
-			iface->conf_p2p_dev = optarg;
+			params.conf_p2p_dev = optarg;
 			break;
 #endif /* CONFIG_P2P */
 		case 'o':
@@ -288,7 +288,7 @@
 			if (iface == NULL)
 				goto out;
 			ifaces = iface;
-			iface = &ifaces[iface_count - 1]; 
+			iface = &ifaces[iface_count - 1];
 			os_memset(iface, 0, sizeof(*iface));
 			break;
 		default:
@@ -322,19 +322,11 @@
 			exitcode = -1;
 			break;
 		}
-		wpa_s = wpa_supplicant_add_iface(global, &ifaces[i]);
+		wpa_s = wpa_supplicant_add_iface(global, &ifaces[i], NULL);
 		if (wpa_s == NULL) {
 			exitcode = -1;
 			break;
 		}
-#ifdef CONFIG_P2P
-		if (wpa_s->global->p2p == NULL &&
-		    (wpa_s->drv_flags &
-		     WPA_DRIVER_FLAGS_DEDICATED_P2P_DEVICE) &&
-		    wpas_p2p_add_p2pdev_interface(wpa_s, iface->conf_p2p_dev) <
-		    0)
-			exitcode = -1;
-#endif /* CONFIG_P2P */
 	}
 
 	if (exitcode == 0)
diff --git a/wpa_supplicant/main_none.c b/wpa_supplicant/main_none.c
index 010c30a..4d3caf2 100644
--- a/wpa_supplicant/main_none.c
+++ b/wpa_supplicant/main_none.c
@@ -28,7 +28,7 @@
 	memset(&iface, 0, sizeof(iface));
 	/* TODO: set interface parameters */
 
-	if (wpa_supplicant_add_iface(global, &iface) == NULL)
+	if (wpa_supplicant_add_iface(global, &iface, NULL) == NULL)
 		exitcode = -1;
 
 	if (exitcode == 0)
diff --git a/wpa_supplicant/main_winmain.c b/wpa_supplicant/main_winmain.c
index 93a68f1..e1dded0 100644
--- a/wpa_supplicant/main_winmain.c
+++ b/wpa_supplicant/main_winmain.c
@@ -61,7 +61,7 @@
 			exitcode = -1;
 			break;
 		}
-		if (wpa_supplicant_add_iface(global, &ifaces[i]) == NULL)
+		if (wpa_supplicant_add_iface(global, &ifaces[i], NULL) == NULL)
 			exitcode = -1;
 	}
 
diff --git a/wpa_supplicant/main_winsvc.c b/wpa_supplicant/main_winsvc.c
index 0b7d5ce..9950aa9 100644
--- a/wpa_supplicant/main_winsvc.c
+++ b/wpa_supplicant/main_winsvc.c
@@ -119,7 +119,7 @@
 
 	RegCloseKey(hk);
 
-	if (wpa_supplicant_add_iface(global, &iface) == NULL) {
+	if (wpa_supplicant_add_iface(global, &iface, NULL) == NULL) {
 		if (skip_on_error)
 			wpa_printf(MSG_DEBUG, "Skipped interface '%s' due to "
 				   "initialization failure", iface.ifname);
diff --git a/wpa_supplicant/mesh.c b/wpa_supplicant/mesh.c
new file mode 100644
index 0000000..ca012e2
--- /dev/null
+++ b/wpa_supplicant/mesh.c
@@ -0,0 +1,541 @@
+/*
+ * WPA Supplicant - Basic mesh mode routines
+ * Copyright (c) 2013-2014, cozybit, Inc.  All rights reserved.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "utils/uuid.h"
+#include "common/ieee802_11_defs.h"
+#include "common/wpa_ctrl.h"
+#include "ap/sta_info.h"
+#include "ap/hostapd.h"
+#include "ap/ieee802_11.h"
+#include "config_ssid.h"
+#include "config.h"
+#include "wpa_supplicant_i.h"
+#include "driver_i.h"
+#include "notify.h"
+#include "ap.h"
+#include "mesh_mpm.h"
+#include "mesh_rsn.h"
+#include "mesh.h"
+
+
+static void wpa_supplicant_mesh_deinit(struct wpa_supplicant *wpa_s)
+{
+	wpa_supplicant_mesh_iface_deinit(wpa_s, wpa_s->ifmsh);
+	wpa_s->ifmsh = NULL;
+	wpa_s->current_ssid = NULL;
+	os_free(wpa_s->mesh_rsn);
+	wpa_s->mesh_rsn = NULL;
+	/* TODO: leave mesh (stop beacon). This will happen on link down
+	 * anyway, so it's not urgent */
+}
+
+
+void wpa_supplicant_mesh_iface_deinit(struct wpa_supplicant *wpa_s,
+				      struct hostapd_iface *ifmsh)
+{
+	if (!ifmsh)
+		return;
+
+	if (ifmsh->mconf) {
+		mesh_mpm_deinit(wpa_s, ifmsh);
+		if (ifmsh->mconf->ies) {
+			ifmsh->mconf->ies = NULL;
+			/* We cannot free this struct
+			 * because wpa_authenticator on
+			 * hostapd side is also using it
+			 * for now just set to NULL and
+			 * let hostapd code free it.
+			 */
+		}
+		os_free(ifmsh->mconf);
+		ifmsh->mconf = NULL;
+	}
+
+	/* take care of shared data */
+	hostapd_interface_deinit(ifmsh);
+	hostapd_interface_free(ifmsh);
+}
+
+
+static struct mesh_conf * mesh_config_create(struct wpa_ssid *ssid)
+{
+	struct mesh_conf *conf;
+
+	conf = os_zalloc(sizeof(struct mesh_conf));
+	if (!conf)
+		return NULL;
+
+	os_memcpy(conf->meshid, ssid->ssid, ssid->ssid_len);
+	conf->meshid_len = ssid->ssid_len;
+
+	if (ssid->key_mgmt & WPA_KEY_MGMT_SAE)
+		conf->security |= MESH_CONF_SEC_AUTH |
+			MESH_CONF_SEC_AMPE;
+	else
+		conf->security |= MESH_CONF_SEC_NONE;
+
+	/* defaults */
+	conf->mesh_pp_id = MESH_PATH_PROTOCOL_HWMP;
+	conf->mesh_pm_id = MESH_PATH_METRIC_AIRTIME;
+	conf->mesh_cc_id = 0;
+	conf->mesh_sp_id = MESH_SYNC_METHOD_NEIGHBOR_OFFSET;
+	conf->mesh_auth_id = (conf->security & MESH_CONF_SEC_AUTH) ? 1 : 0;
+	conf->dot11MeshMaxRetries = ssid->dot11MeshMaxRetries;
+	conf->dot11MeshRetryTimeout = ssid->dot11MeshRetryTimeout;
+	conf->dot11MeshConfirmTimeout = ssid->dot11MeshConfirmTimeout;
+	conf->dot11MeshHoldingTimeout = ssid->dot11MeshHoldingTimeout;
+
+	return conf;
+}
+
+
+static void wpas_mesh_copy_groups(struct hostapd_data *bss,
+				  struct wpa_supplicant *wpa_s)
+{
+	int num_groups;
+	size_t groups_size;
+
+	for (num_groups = 0; wpa_s->conf->sae_groups[num_groups] > 0;
+	     num_groups++)
+		;
+
+	groups_size = (num_groups + 1) * sizeof(wpa_s->conf->sae_groups[0]);
+	bss->conf->sae_groups = os_malloc(groups_size);
+	if (bss->conf->sae_groups)
+		os_memcpy(bss->conf->sae_groups, wpa_s->conf->sae_groups,
+			  groups_size);
+}
+
+
+static int wpa_supplicant_mesh_init(struct wpa_supplicant *wpa_s,
+				    struct wpa_ssid *ssid)
+{
+	struct hostapd_iface *ifmsh;
+	struct hostapd_data *bss;
+	struct hostapd_config *conf;
+	struct mesh_conf *mconf;
+	int basic_rates_erp[] = { 10, 20, 55, 60, 110, 120, 240, -1 };
+	static int default_groups[] = { 19, 20, 21, 25, 26, -1 };
+	size_t len;
+	int rate_len;
+
+	if (!wpa_s->conf->user_mpm) {
+		/* not much for us to do here */
+		wpa_msg(wpa_s, MSG_WARNING,
+			"user_mpm is not enabled in configuration");
+		return 0;
+	}
+
+	wpa_s->ifmsh = ifmsh = os_zalloc(sizeof(*wpa_s->ifmsh));
+	if (!ifmsh)
+		return -ENOMEM;
+
+	ifmsh->drv_flags = wpa_s->drv_flags;
+	ifmsh->num_bss = 1;
+	ifmsh->bss = os_calloc(wpa_s->ifmsh->num_bss,
+			       sizeof(struct hostapd_data *));
+	if (!ifmsh->bss)
+		goto out_free;
+
+	ifmsh->bss[0] = bss = os_zalloc(sizeof(struct hostapd_data));
+	if (!bss)
+		goto out_free;
+
+	os_memcpy(bss->own_addr, wpa_s->own_addr, ETH_ALEN);
+	bss->driver = wpa_s->driver;
+	bss->drv_priv = wpa_s->drv_priv;
+	bss->iface = ifmsh;
+	bss->mesh_sta_free_cb = mesh_mpm_free_sta;
+	wpa_s->assoc_freq = ssid->frequency;
+	wpa_s->current_ssid = ssid;
+
+	/* setup an AP config for auth processing */
+	conf = hostapd_config_defaults();
+	if (!conf)
+		goto out_free;
+
+	bss->conf = *conf->bss;
+	bss->conf->start_disabled = 1;
+	bss->conf->mesh = MESH_ENABLED;
+	bss->conf->ap_max_inactivity = wpa_s->conf->mesh_max_inactivity;
+	bss->iconf = conf;
+	ifmsh->conf = conf;
+
+	ifmsh->bss[0]->max_plinks = wpa_s->conf->max_peer_links;
+	os_strlcpy(bss->conf->iface, wpa_s->ifname, sizeof(bss->conf->iface));
+
+	mconf = mesh_config_create(ssid);
+	if (!mconf)
+		goto out_free;
+	ifmsh->mconf = mconf;
+
+	/* need conf->hw_mode for supported rates. */
+	if (ssid->frequency == 0) {
+		conf->hw_mode = HOSTAPD_MODE_IEEE80211G;
+		conf->channel = 1;
+	} else {
+		conf->hw_mode = ieee80211_freq_to_chan(ssid->frequency,
+						       &conf->channel);
+	}
+	if (conf->hw_mode == NUM_HOSTAPD_MODES) {
+		wpa_printf(MSG_ERROR, "Unsupported mesh mode frequency: %d MHz",
+			   ssid->frequency);
+		goto out_free;
+	}
+
+	if (ssid->mesh_basic_rates == NULL) {
+		/*
+		 * XXX: Hack! This is so an MPM which correctly sets the ERP
+		 * mandatory rates as BSSBasicRateSet doesn't reject us. We
+		 * could add a new hw_mode HOSTAPD_MODE_IEEE80211G_ERP, but
+		 * this is way easier. This also makes our BSSBasicRateSet
+		 * advertised in beacons match the one in peering frames, sigh.
+		 */
+		if (conf->hw_mode == HOSTAPD_MODE_IEEE80211G) {
+			conf->basic_rates = os_malloc(sizeof(basic_rates_erp));
+			if (!conf->basic_rates)
+				goto out_free;
+			os_memcpy(conf->basic_rates, basic_rates_erp,
+				  sizeof(basic_rates_erp));
+		}
+	} else {
+		rate_len = 0;
+		while (1) {
+			if (ssid->mesh_basic_rates[rate_len] < 1)
+				break;
+			rate_len++;
+		}
+		conf->basic_rates = os_calloc(rate_len + 1, sizeof(int));
+		if (conf->basic_rates == NULL)
+			goto out_free;
+		os_memcpy(conf->basic_rates, ssid->mesh_basic_rates,
+			  rate_len * sizeof(int));
+		conf->basic_rates[rate_len] = -1;
+	}
+
+	if (hostapd_setup_interface(ifmsh)) {
+		wpa_printf(MSG_ERROR,
+			   "Failed to initialize hostapd interface for mesh");
+		return -1;
+	}
+
+	if (wpa_drv_init_mesh(wpa_s)) {
+		wpa_msg(wpa_s, MSG_ERROR, "Failed to init mesh in driver");
+		return -1;
+	}
+
+	if (mconf->security != MESH_CONF_SEC_NONE) {
+		if (ssid->passphrase == NULL) {
+			wpa_printf(MSG_ERROR,
+				   "mesh: Passphrase for SAE not configured");
+			goto out_free;
+		}
+
+		bss->conf->wpa = ssid->proto;
+		bss->conf->wpa_key_mgmt = ssid->key_mgmt;
+
+		if (wpa_s->conf->sae_groups &&
+		    wpa_s->conf->sae_groups[0] > 0) {
+			wpas_mesh_copy_groups(bss, wpa_s);
+		} else {
+			bss->conf->sae_groups =
+				os_malloc(sizeof(default_groups));
+			if (!bss->conf->sae_groups)
+				goto out_free;
+			os_memcpy(bss->conf->sae_groups, default_groups,
+				  sizeof(default_groups));
+		}
+
+		len = os_strlen(ssid->passphrase);
+		bss->conf->ssid.wpa_passphrase =
+			dup_binstr(ssid->passphrase, len);
+
+		wpa_s->mesh_rsn = mesh_rsn_auth_init(wpa_s, mconf);
+		if (!wpa_s->mesh_rsn)
+			goto out_free;
+	}
+
+	wpa_supplicant_conf_ap_ht(wpa_s, ssid, conf);
+
+	return 0;
+out_free:
+	wpa_supplicant_mesh_deinit(wpa_s);
+	return -ENOMEM;
+}
+
+
+void wpa_mesh_notify_peer(struct wpa_supplicant *wpa_s, const u8 *addr,
+			  const u8 *ies, size_t ie_len)
+{
+	struct ieee802_11_elems elems;
+
+	wpa_msg(wpa_s, MSG_INFO,
+		"new peer notification for " MACSTR, MAC2STR(addr));
+
+	if (ieee802_11_parse_elems(ies, ie_len, &elems, 0) == ParseFailed) {
+		wpa_msg(wpa_s, MSG_INFO, "Could not parse beacon from " MACSTR,
+			MAC2STR(addr));
+		return;
+	}
+	wpa_mesh_new_mesh_peer(wpa_s, addr, &elems);
+}
+
+
+void wpa_supplicant_mesh_add_scan_ie(struct wpa_supplicant *wpa_s,
+				     struct wpabuf **extra_ie)
+{
+	/* EID + 0-length (wildcard) mesh-id */
+	size_t ielen = 2;
+
+	if (wpabuf_resize(extra_ie, ielen) == 0) {
+		wpabuf_put_u8(*extra_ie, WLAN_EID_MESH_ID);
+		wpabuf_put_u8(*extra_ie, 0);
+	}
+}
+
+
+int wpa_supplicant_join_mesh(struct wpa_supplicant *wpa_s,
+			     struct wpa_ssid *ssid)
+{
+	struct wpa_driver_mesh_join_params params;
+	int ret = 0;
+
+	if (!ssid || !ssid->ssid || !ssid->ssid_len || !ssid->frequency) {
+		ret = -ENOENT;
+		goto out;
+	}
+
+	wpa_supplicant_mesh_deinit(wpa_s);
+
+	os_memset(&params, 0, sizeof(params));
+	params.meshid = ssid->ssid;
+	params.meshid_len = ssid->ssid_len;
+	ibss_mesh_setup_freq(wpa_s, ssid, &params.freq);
+	wpa_s->mesh_ht_enabled = !!params.freq.ht_enabled;
+	if (ssid->beacon_int > 0)
+		params.beacon_int = ssid->beacon_int;
+	else if (wpa_s->conf->beacon_int > 0)
+		params.beacon_int = wpa_s->conf->beacon_int;
+	params.max_peer_links = wpa_s->conf->max_peer_links;
+
+	if (ssid->key_mgmt & WPA_KEY_MGMT_SAE) {
+		params.flags |= WPA_DRIVER_MESH_FLAG_SAE_AUTH;
+		params.flags |= WPA_DRIVER_MESH_FLAG_AMPE;
+		wpa_s->conf->user_mpm = 1;
+	}
+
+	if (wpa_s->conf->user_mpm) {
+		params.flags |= WPA_DRIVER_MESH_FLAG_USER_MPM;
+		params.conf.flags &= ~WPA_DRIVER_MESH_CONF_FLAG_AUTO_PLINKS;
+	} else {
+		params.flags |= WPA_DRIVER_MESH_FLAG_DRIVER_MPM;
+		params.conf.flags |= WPA_DRIVER_MESH_CONF_FLAG_AUTO_PLINKS;
+	}
+	params.conf.peer_link_timeout = wpa_s->conf->mesh_max_inactivity;
+
+	if (wpa_supplicant_mesh_init(wpa_s, ssid)) {
+		wpa_msg(wpa_s, MSG_ERROR, "Failed to init mesh");
+		wpa_drv_leave_mesh(wpa_s);
+		ret = -1;
+		goto out;
+	}
+
+	if (wpa_s->ifmsh) {
+		params.ies = wpa_s->ifmsh->mconf->ies;
+		params.ie_len = wpa_s->ifmsh->mconf->ie_len;
+		params.basic_rates = wpa_s->ifmsh->basic_rates;
+	}
+
+	wpa_msg(wpa_s, MSG_INFO, "joining mesh %s",
+		wpa_ssid_txt(ssid->ssid, ssid->ssid_len));
+	ret = wpa_drv_join_mesh(wpa_s, &params);
+	if (ret)
+		wpa_msg(wpa_s, MSG_ERROR, "mesh join error=%d\n", ret);
+
+	/* hostapd sets the interface down until we associate */
+	wpa_drv_set_operstate(wpa_s, 1);
+
+out:
+	return ret;
+}
+
+
+int wpa_supplicant_leave_mesh(struct wpa_supplicant *wpa_s)
+{
+	int ret = 0;
+
+	wpa_msg(wpa_s, MSG_INFO, "leaving mesh");
+
+	/* Need to send peering close messages first */
+	wpa_supplicant_mesh_deinit(wpa_s);
+
+	ret = wpa_drv_leave_mesh(wpa_s);
+	if (ret)
+		wpa_msg(wpa_s, MSG_ERROR, "mesh leave error=%d", ret);
+
+	wpa_drv_set_operstate(wpa_s, 1);
+
+	return ret;
+}
+
+
+static int mesh_attr_text(const u8 *ies, size_t ies_len, char *buf, char *end)
+{
+	struct ieee802_11_elems elems;
+	char *mesh_id, *pos = buf;
+	u8 *bss_basic_rate_set;
+	int bss_basic_rate_set_len, ret, i;
+
+	if (ieee802_11_parse_elems(ies, ies_len, &elems, 0) == ParseFailed)
+		return -1;
+
+	if (elems.mesh_id_len < 1)
+		return 0;
+
+	mesh_id = os_malloc(elems.mesh_id_len + 1);
+	if (mesh_id == NULL)
+		return -1;
+
+	os_memcpy(mesh_id, elems.mesh_id, elems.mesh_id_len);
+	mesh_id[elems.mesh_id_len] = '\0';
+	ret = os_snprintf(pos, end - pos, "mesh_id=%s\n", mesh_id);
+	os_free(mesh_id);
+	if (os_snprintf_error(end - pos, ret))
+		return pos - buf;
+	pos += ret;
+
+	if (elems.mesh_config_len > 6) {
+		ret = os_snprintf(pos, end - pos,
+				  "active_path_selection_protocol_id=0x%02x\n"
+				  "active_path_selection_metric_id=0x%02x\n"
+				  "congestion_control_mode_id=0x%02x\n"
+				  "synchronization_method_id=0x%02x\n"
+				  "authentication_protocol_id=0x%02x\n"
+				  "mesh_formation_info=0x%02x\n"
+				  "mesh_capability=0x%02x\n",
+				  elems.mesh_config[0], elems.mesh_config[1],
+				  elems.mesh_config[2], elems.mesh_config[3],
+				  elems.mesh_config[4], elems.mesh_config[5],
+				  elems.mesh_config[6]);
+		if (os_snprintf_error(end - pos, ret))
+			return pos - buf;
+		pos += ret;
+	}
+
+	bss_basic_rate_set = os_malloc(elems.supp_rates_len +
+		elems.ext_supp_rates_len);
+	if (bss_basic_rate_set == NULL)
+		return -1;
+
+	bss_basic_rate_set_len = 0;
+	for (i = 0; i < elems.supp_rates_len; i++) {
+		if (elems.supp_rates[i] & 0x80) {
+			bss_basic_rate_set[bss_basic_rate_set_len++] =
+				(elems.supp_rates[i] & 0x7f) * 5;
+		}
+	}
+	for (i = 0; i < elems.ext_supp_rates_len; i++) {
+		if (elems.ext_supp_rates[i] & 0x80) {
+			bss_basic_rate_set[bss_basic_rate_set_len++] =
+				(elems.ext_supp_rates[i] & 0x7f) * 5;
+		}
+	}
+	if (bss_basic_rate_set_len > 0) {
+		ret = os_snprintf(pos, end - pos, "bss_basic_rate_set=%d",
+				  bss_basic_rate_set[0]);
+		if (os_snprintf_error(end - pos, ret))
+			goto fail;
+		pos += ret;
+
+		for (i = 1; i < bss_basic_rate_set_len; i++) {
+			ret = os_snprintf(pos, end - pos, " %d",
+					  bss_basic_rate_set[i]);
+			if (os_snprintf_error(end - pos, ret))
+				goto fail;
+			pos += ret;
+		}
+
+		ret = os_snprintf(pos, end - pos, "\n");
+		if (os_snprintf_error(end - pos, ret))
+			goto fail;
+		pos += ret;
+	}
+fail:
+	os_free(bss_basic_rate_set);
+
+	return pos - buf;
+}
+
+
+int wpas_mesh_scan_result_text(const u8 *ies, size_t ies_len, char *buf,
+			       char *end)
+{
+	return mesh_attr_text(ies, ies_len, buf, end);
+}
+
+
+static int wpas_mesh_get_ifname(struct wpa_supplicant *wpa_s, char *ifname,
+				size_t len)
+{
+	char *ifname_ptr = wpa_s->ifname;
+	int res;
+
+	res = os_snprintf(ifname, len, "mesh-%s-%d", ifname_ptr,
+			  wpa_s->mesh_if_idx);
+	if (os_snprintf_error(len, res) ||
+	    (os_strlen(ifname) >= IFNAMSIZ &&
+	     os_strlen(wpa_s->ifname) < IFNAMSIZ)) {
+		/* Try to avoid going over the IFNAMSIZ length limit */
+		res = os_snprintf(ifname, len, "mesh-%d", wpa_s->mesh_if_idx);
+		if (os_snprintf_error(len, res))
+			return -1;
+	}
+	wpa_s->mesh_if_idx++;
+	return 0;
+}
+
+
+int wpas_mesh_add_interface(struct wpa_supplicant *wpa_s, char *ifname,
+			    size_t len)
+{
+	struct wpa_interface iface;
+	struct wpa_supplicant *mesh_wpa_s;
+	u8 addr[ETH_ALEN];
+
+	if (ifname[0] == '\0' && wpas_mesh_get_ifname(wpa_s, ifname, len) < 0)
+		return -1;
+
+	if (wpa_drv_if_add(wpa_s, WPA_IF_MESH, ifname, NULL, NULL, NULL, addr,
+			   NULL) < 0) {
+		wpa_printf(MSG_ERROR,
+			   "mesh: Failed to create new mesh interface");
+		return -1;
+	}
+	wpa_printf(MSG_INFO, "mesh: Created virtual interface %s addr "
+		   MACSTR, ifname, MAC2STR(addr));
+
+	os_memset(&iface, 0, sizeof(iface));
+	iface.ifname = ifname;
+	iface.driver = wpa_s->driver->name;
+	iface.driver_param = wpa_s->conf->driver_param;
+	iface.ctrl_interface = wpa_s->conf->ctrl_interface;
+
+	mesh_wpa_s = wpa_supplicant_add_iface(wpa_s->global, &iface, wpa_s);
+	if (!mesh_wpa_s) {
+		wpa_printf(MSG_ERROR,
+			   "mesh: Failed to create new wpa_supplicant interface");
+		wpa_supplicant_remove_iface(wpa_s->global, wpa_s, 0);
+		return -1;
+	}
+	mesh_wpa_s->mesh_if_created = 1;
+	return 0;
+}
diff --git a/wpa_supplicant/mesh.h b/wpa_supplicant/mesh.h
new file mode 100644
index 0000000..3cb7f1b
--- /dev/null
+++ b/wpa_supplicant/mesh.h
@@ -0,0 +1,44 @@
+/*
+ * WPA Supplicant - Basic mesh mode routines
+ * Copyright (c) 2013-2014, cozybit, Inc.  All rights reserved.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef MESH_H
+#define MESH_H
+
+int wpa_supplicant_join_mesh(struct wpa_supplicant *wpa_s,
+			     struct wpa_ssid *ssid);
+int wpa_supplicant_leave_mesh(struct wpa_supplicant *wpa_s);
+void wpa_supplicant_mesh_iface_deinit(struct wpa_supplicant *wpa_s,
+				      struct hostapd_iface *ifmsh);
+int wpas_mesh_scan_result_text(const u8 *ies, size_t ies_len, char *buf,
+			       char *end);
+int wpas_mesh_add_interface(struct wpa_supplicant *wpa_s, char *ifname,
+			    size_t len);
+
+#ifdef CONFIG_MESH
+
+void wpa_mesh_notify_peer(struct wpa_supplicant *wpa_s, const u8 *addr,
+			  const u8 *ies, size_t ie_len);
+void wpa_supplicant_mesh_add_scan_ie(struct wpa_supplicant *wpa_s,
+				     struct wpabuf **extra_ie);
+
+#else /* CONFIG_MESH */
+
+static inline void wpa_mesh_notify_peer(struct wpa_supplicant *wpa_s,
+					const u8 *addr,
+					const u8 *ies, size_t ie_len)
+{
+}
+
+static inline void wpa_supplicant_mesh_add_scan_ie(struct wpa_supplicant *wpa_s,
+						   struct wpabuf **extra_ie)
+{
+}
+
+#endif /* CONFIG_MESH */
+
+#endif /* MESH_H */
diff --git a/wpa_supplicant/mesh_mpm.c b/wpa_supplicant/mesh_mpm.c
new file mode 100644
index 0000000..b29b5ff
--- /dev/null
+++ b/wpa_supplicant/mesh_mpm.c
@@ -0,0 +1,1058 @@
+/*
+ * WPA Supplicant - Basic mesh peer management
+ * Copyright (c) 2013-2014, cozybit, Inc.  All rights reserved.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "common/ieee802_11_defs.h"
+#include "ap/hostapd.h"
+#include "ap/sta_info.h"
+#include "ap/ieee802_11.h"
+#include "wpa_supplicant_i.h"
+#include "driver_i.h"
+#include "mesh_mpm.h"
+#include "mesh_rsn.h"
+
+struct mesh_peer_mgmt_ie {
+	const u8 *proto_id;
+	const u8 *llid;
+	const u8 *plid;
+	const u8 *reason;
+	const u8 *pmk;
+};
+
+static void plink_timer(void *eloop_ctx, void *user_data);
+
+
+enum plink_event {
+	PLINK_UNDEFINED,
+	OPN_ACPT,
+	OPN_RJCT,
+	OPN_IGNR,
+	CNF_ACPT,
+	CNF_RJCT,
+	CNF_IGNR,
+	CLS_ACPT,
+	CLS_IGNR
+};
+
+static const char * const mplstate[] = {
+	[PLINK_LISTEN] = "LISTEN",
+	[PLINK_OPEN_SENT] = "OPEN_SENT",
+	[PLINK_OPEN_RCVD] = "OPEN_RCVD",
+	[PLINK_CNF_RCVD] = "CNF_RCVD",
+	[PLINK_ESTAB] = "ESTAB",
+	[PLINK_HOLDING] = "HOLDING",
+	[PLINK_BLOCKED] = "BLOCKED"
+};
+
+static const char * const mplevent[] = {
+	[PLINK_UNDEFINED] = "UNDEFINED",
+	[OPN_ACPT] = "OPN_ACPT",
+	[OPN_RJCT] = "OPN_RJCT",
+	[OPN_IGNR] = "OPN_IGNR",
+	[CNF_ACPT] = "CNF_ACPT",
+	[CNF_RJCT] = "CNF_RJCT",
+	[CNF_IGNR] = "CNF_IGNR",
+	[CLS_ACPT] = "CLS_ACPT",
+	[CLS_IGNR] = "CLS_IGNR"
+};
+
+
+static int mesh_mpm_parse_peer_mgmt(struct wpa_supplicant *wpa_s,
+				    u8 action_field,
+				    const u8 *ie, size_t len,
+				    struct mesh_peer_mgmt_ie *mpm_ie)
+{
+	os_memset(mpm_ie, 0, sizeof(*mpm_ie));
+
+	/* remove optional PMK at end */
+	if (len >= 16) {
+		len -= 16;
+		mpm_ie->pmk = ie + len - 16;
+	}
+
+	if ((action_field == PLINK_OPEN && len != 4) ||
+	    (action_field == PLINK_CONFIRM && len != 6) ||
+	    (action_field == PLINK_CLOSE && len != 6 && len != 8)) {
+		wpa_msg(wpa_s, MSG_DEBUG, "MPM: Invalid peer mgmt ie");
+		return -1;
+	}
+
+	/* required fields */
+	if (len < 4)
+		return -1;
+	mpm_ie->proto_id = ie;
+	mpm_ie->llid = ie + 2;
+	ie += 4;
+	len -= 4;
+
+	/* close reason is always present at end for close */
+	if (action_field == PLINK_CLOSE) {
+		if (len < 2)
+			return -1;
+		mpm_ie->reason = ie + len - 2;
+		len -= 2;
+	}
+
+	/* plid, present for confirm, and possibly close */
+	if (len)
+		mpm_ie->plid = ie;
+
+	return 0;
+}
+
+
+static int plink_free_count(struct hostapd_data *hapd)
+{
+	if (hapd->max_plinks > hapd->num_plinks)
+		return hapd->max_plinks - hapd->num_plinks;
+	return 0;
+}
+
+
+static u16 copy_supp_rates(struct wpa_supplicant *wpa_s,
+			   struct sta_info *sta,
+			   struct ieee802_11_elems *elems)
+{
+	if (!elems->supp_rates) {
+		wpa_msg(wpa_s, MSG_ERROR, "no supported rates from " MACSTR,
+			MAC2STR(sta->addr));
+		return WLAN_STATUS_UNSPECIFIED_FAILURE;
+	}
+
+	if (elems->supp_rates_len + elems->ext_supp_rates_len >
+	    sizeof(sta->supported_rates)) {
+		wpa_msg(wpa_s, MSG_ERROR,
+			"Invalid supported rates element length " MACSTR
+			" %d+%d", MAC2STR(sta->addr), elems->supp_rates_len,
+			elems->ext_supp_rates_len);
+		return WLAN_STATUS_UNSPECIFIED_FAILURE;
+	}
+
+	sta->supported_rates_len = merge_byte_arrays(
+		sta->supported_rates, sizeof(sta->supported_rates),
+		elems->supp_rates, elems->supp_rates_len,
+		elems->ext_supp_rates, elems->ext_supp_rates_len);
+
+	return WLAN_STATUS_SUCCESS;
+}
+
+
+/* return true if elems from a neighbor match this MBSS */
+static Boolean matches_local(struct wpa_supplicant *wpa_s,
+			     struct ieee802_11_elems *elems)
+{
+	struct mesh_conf *mconf = wpa_s->ifmsh->mconf;
+
+	if (elems->mesh_config_len < 5)
+		return FALSE;
+
+	return (mconf->meshid_len == elems->mesh_id_len &&
+		os_memcmp(mconf->meshid, elems->mesh_id,
+			  elems->mesh_id_len) == 0 &&
+		mconf->mesh_pp_id == elems->mesh_config[0] &&
+		mconf->mesh_pm_id == elems->mesh_config[1] &&
+		mconf->mesh_cc_id == elems->mesh_config[2] &&
+		mconf->mesh_sp_id == elems->mesh_config[3] &&
+		mconf->mesh_auth_id == elems->mesh_config[4]);
+}
+
+
+/* check if local link id is already used with another peer */
+static Boolean llid_in_use(struct wpa_supplicant *wpa_s, u16 llid)
+{
+	struct sta_info *sta;
+	struct hostapd_data *hapd = wpa_s->ifmsh->bss[0];
+
+	for (sta = hapd->sta_list; sta; sta = sta->next) {
+		if (sta->my_lid == llid)
+			return TRUE;
+	}
+
+	return FALSE;
+}
+
+
+/* generate an llid for a link and set to initial state */
+static void mesh_mpm_init_link(struct wpa_supplicant *wpa_s,
+			       struct sta_info *sta)
+{
+	u16 llid;
+
+	do {
+		if (os_get_random((u8 *) &llid, sizeof(llid)) < 0)
+			continue;
+	} while (!llid || llid_in_use(wpa_s, llid));
+
+	sta->my_lid = llid;
+	sta->peer_lid = 0;
+
+	/*
+	 * We do not use wpa_mesh_set_plink_state() here because there is no
+	 * entry in kernel yet.
+	 */
+	sta->plink_state = PLINK_LISTEN;
+}
+
+
+static void mesh_mpm_send_plink_action(struct wpa_supplicant *wpa_s,
+				       struct sta_info *sta,
+				       enum plink_action_field type,
+				       u16 close_reason)
+{
+	struct wpabuf *buf;
+	struct hostapd_iface *ifmsh = wpa_s->ifmsh;
+	struct hostapd_data *bss = ifmsh->bss[0];
+	struct mesh_conf *conf = ifmsh->mconf;
+	u8 supp_rates[2 + 2 + 32];
+#ifdef CONFIG_IEEE80211N
+	u8 ht_capa_oper[2 + 26 + 2 + 22];
+#endif /* CONFIG_IEEE80211N */
+	u8 *pos, *cat;
+	u8 ie_len, add_plid = 0;
+	int ret;
+	int ampe = conf->security & MESH_CONF_SEC_AMPE;
+	size_t buf_len;
+
+	if (!sta)
+		return;
+
+	buf_len = 2 +      /* capability info */
+		  2 +      /* AID */
+		  2 + 8 +  /* supported rates */
+		  2 + (32 - 8) +
+		  2 + 32 + /* mesh ID */
+		  2 + 7 +  /* mesh config */
+		  2 + 23 + /* peering management */
+		  2 + 96 + /* AMPE */
+		  2 + 16;  /* MIC */
+#ifdef CONFIG_IEEE80211N
+	if (type != PLINK_CLOSE && wpa_s->mesh_ht_enabled) {
+		buf_len += 2 + 26 + /* HT capabilities */
+			   2 + 22;  /* HT operation */
+	}
+#endif /* CONFIG_IEEE80211N */
+	buf = wpabuf_alloc(buf_len);
+	if (!buf)
+		return;
+
+	cat = wpabuf_mhead_u8(buf);
+	wpabuf_put_u8(buf, WLAN_ACTION_SELF_PROTECTED);
+	wpabuf_put_u8(buf, type);
+
+	if (type != PLINK_CLOSE) {
+		u8 info;
+
+		/* capability info */
+		wpabuf_put_le16(buf, ampe ? IEEE80211_CAP_PRIVACY : 0);
+
+		/* aid */
+		if (type == PLINK_CONFIRM)
+			wpabuf_put_le16(buf, sta->peer_lid);
+
+		/* IE: supp + ext. supp rates */
+		pos = hostapd_eid_supp_rates(bss, supp_rates);
+		pos = hostapd_eid_ext_supp_rates(bss, pos);
+		wpabuf_put_data(buf, supp_rates, pos - supp_rates);
+
+		/* IE: Mesh ID */
+		wpabuf_put_u8(buf, WLAN_EID_MESH_ID);
+		wpabuf_put_u8(buf, conf->meshid_len);
+		wpabuf_put_data(buf, conf->meshid, conf->meshid_len);
+
+		/* IE: mesh conf */
+		wpabuf_put_u8(buf, WLAN_EID_MESH_CONFIG);
+		wpabuf_put_u8(buf, 7);
+		wpabuf_put_u8(buf, conf->mesh_pp_id);
+		wpabuf_put_u8(buf, conf->mesh_pm_id);
+		wpabuf_put_u8(buf, conf->mesh_cc_id);
+		wpabuf_put_u8(buf, conf->mesh_sp_id);
+		wpabuf_put_u8(buf, conf->mesh_auth_id);
+		info = (bss->num_plinks > 63 ? 63 : bss->num_plinks) << 1;
+		/* TODO: Add Connected to Mesh Gate/AS subfields */
+		wpabuf_put_u8(buf, info);
+		/* always forwarding & accepting plinks for now */
+		wpabuf_put_u8(buf, 0x1 | 0x8);
+	} else {	/* Peer closing frame */
+		/* IE: Mesh ID */
+		wpabuf_put_u8(buf, WLAN_EID_MESH_ID);
+		wpabuf_put_u8(buf, conf->meshid_len);
+		wpabuf_put_data(buf, conf->meshid, conf->meshid_len);
+	}
+
+	/* IE: Mesh Peering Management element */
+	ie_len = 4;
+	if (ampe)
+		ie_len += PMKID_LEN;
+	switch (type) {
+	case PLINK_OPEN:
+		break;
+	case PLINK_CONFIRM:
+		ie_len += 2;
+		add_plid = 1;
+		break;
+	case PLINK_CLOSE:
+		ie_len += 2;
+		add_plid = 1;
+		ie_len += 2; /* reason code */
+		break;
+	}
+
+	wpabuf_put_u8(buf, WLAN_EID_PEER_MGMT);
+	wpabuf_put_u8(buf, ie_len);
+	/* peering protocol */
+	if (ampe)
+		wpabuf_put_le16(buf, 1);
+	else
+		wpabuf_put_le16(buf, 0);
+	wpabuf_put_le16(buf, sta->my_lid);
+	if (add_plid)
+		wpabuf_put_le16(buf, sta->peer_lid);
+	if (type == PLINK_CLOSE)
+		wpabuf_put_le16(buf, close_reason);
+	if (ampe) {
+		if (sta->sae == NULL) {
+			wpa_msg(wpa_s, MSG_INFO, "Mesh MPM: no SAE session");
+			goto fail;
+		}
+		mesh_rsn_get_pmkid(wpa_s->mesh_rsn, sta,
+				   wpabuf_put(buf, PMKID_LEN));
+	}
+
+#ifdef CONFIG_IEEE80211N
+	if (type != PLINK_CLOSE && wpa_s->mesh_ht_enabled) {
+		pos = hostapd_eid_ht_capabilities(bss, ht_capa_oper);
+		pos = hostapd_eid_ht_operation(bss, pos);
+		wpabuf_put_data(buf, ht_capa_oper, pos - ht_capa_oper);
+	}
+#endif /* CONFIG_IEEE80211N */
+
+	if (ampe && mesh_rsn_protect_frame(wpa_s->mesh_rsn, sta, cat, buf)) {
+		wpa_msg(wpa_s, MSG_INFO,
+			"Mesh MPM: failed to add AMPE and MIC IE");
+		goto fail;
+	}
+
+	ret = wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0,
+				  sta->addr, wpa_s->own_addr, wpa_s->own_addr,
+				  wpabuf_head(buf), wpabuf_len(buf), 0);
+	if (ret < 0)
+		wpa_msg(wpa_s, MSG_INFO,
+			"Mesh MPM: failed to send peering frame");
+
+fail:
+	wpabuf_free(buf);
+}
+
+
+/* configure peering state in ours and driver's station entry */
+void wpa_mesh_set_plink_state(struct wpa_supplicant *wpa_s,
+			      struct sta_info *sta,
+			      enum mesh_plink_state state)
+{
+	struct hostapd_sta_add_params params;
+	int ret;
+
+	sta->plink_state = state;
+
+	os_memset(&params, 0, sizeof(params));
+	params.addr = sta->addr;
+	params.plink_state = state;
+	params.set = 1;
+
+	wpa_msg(wpa_s, MSG_DEBUG, "MPM set " MACSTR " into %s",
+		MAC2STR(sta->addr), mplstate[state]);
+	ret = wpa_drv_sta_add(wpa_s, &params);
+	if (ret) {
+		wpa_msg(wpa_s, MSG_ERROR, "Driver failed to set " MACSTR
+			": %d", MAC2STR(sta->addr), ret);
+	}
+}
+
+
+static void mesh_mpm_fsm_restart(struct wpa_supplicant *wpa_s,
+				 struct sta_info *sta)
+{
+	struct hostapd_data *hapd = wpa_s->ifmsh->bss[0];
+
+	eloop_cancel_timeout(plink_timer, wpa_s, sta);
+
+	ap_free_sta(hapd, sta);
+}
+
+
+static void plink_timer(void *eloop_ctx, void *user_data)
+{
+	struct wpa_supplicant *wpa_s = eloop_ctx;
+	struct sta_info *sta = user_data;
+	u16 reason = 0;
+	struct mesh_conf *conf = wpa_s->ifmsh->mconf;
+
+	switch (sta->plink_state) {
+	case PLINK_OPEN_RCVD:
+	case PLINK_OPEN_SENT:
+		/* retry timer */
+		if (sta->mpm_retries < conf->dot11MeshMaxRetries) {
+			eloop_register_timeout(
+				conf->dot11MeshRetryTimeout / 1000,
+				(conf->dot11MeshRetryTimeout % 1000) * 1000,
+				plink_timer, wpa_s, sta);
+			mesh_mpm_send_plink_action(wpa_s, sta, PLINK_OPEN, 0);
+			sta->mpm_retries++;
+			break;
+		}
+		reason = WLAN_REASON_MESH_MAX_RETRIES;
+		/* fall through on else */
+
+	case PLINK_CNF_RCVD:
+		/* confirm timer */
+		if (!reason)
+			reason = WLAN_REASON_MESH_CONFIRM_TIMEOUT;
+		wpa_mesh_set_plink_state(wpa_s, sta, PLINK_HOLDING);
+		eloop_register_timeout(conf->dot11MeshHoldingTimeout / 1000,
+			(conf->dot11MeshHoldingTimeout % 1000) * 1000,
+			plink_timer, wpa_s, sta);
+		mesh_mpm_send_plink_action(wpa_s, sta, PLINK_CLOSE, reason);
+		break;
+	case PLINK_HOLDING:
+		/* holding timer */
+		mesh_mpm_fsm_restart(wpa_s, sta);
+		break;
+	default:
+		break;
+	}
+}
+
+
+/* initiate peering with station */
+static void
+mesh_mpm_plink_open(struct wpa_supplicant *wpa_s, struct sta_info *sta,
+		    enum mesh_plink_state next_state)
+{
+	struct mesh_conf *conf = wpa_s->ifmsh->mconf;
+
+	eloop_cancel_timeout(plink_timer, wpa_s, sta);
+	eloop_register_timeout(conf->dot11MeshRetryTimeout / 1000,
+			       (conf->dot11MeshRetryTimeout % 1000) * 1000,
+			       plink_timer, wpa_s, sta);
+	mesh_mpm_send_plink_action(wpa_s, sta, PLINK_OPEN, 0);
+	wpa_mesh_set_plink_state(wpa_s, sta, next_state);
+}
+
+
+int mesh_mpm_plink_close(struct hostapd_data *hapd,
+			 struct sta_info *sta, void *ctx)
+{
+	struct wpa_supplicant *wpa_s = ctx;
+	int reason = WLAN_REASON_MESH_PEERING_CANCELLED;
+
+	if (sta) {
+		wpa_mesh_set_plink_state(wpa_s, sta, PLINK_HOLDING);
+		mesh_mpm_send_plink_action(wpa_s, sta, PLINK_CLOSE, reason);
+		wpa_printf(MSG_DEBUG, "MPM closing plink sta=" MACSTR,
+			   MAC2STR(sta->addr));
+		eloop_cancel_timeout(plink_timer, wpa_s, sta);
+		return 0;
+	}
+
+	return 1;
+}
+
+
+void mesh_mpm_deinit(struct wpa_supplicant *wpa_s, struct hostapd_iface *ifmsh)
+{
+	struct hostapd_data *hapd = ifmsh->bss[0];
+
+	/* notify peers we're leaving */
+	ap_for_each_sta(hapd, mesh_mpm_plink_close, wpa_s);
+
+	hapd->num_plinks = 0;
+	hostapd_free_stas(hapd);
+}
+
+
+/* for mesh_rsn to indicate this peer has completed authentication, and we're
+ * ready to start AMPE */
+void mesh_mpm_auth_peer(struct wpa_supplicant *wpa_s, const u8 *addr)
+{
+	struct hostapd_data *data = wpa_s->ifmsh->bss[0];
+	struct hostapd_sta_add_params params;
+	struct sta_info *sta;
+	int ret;
+
+	sta = ap_get_sta(data, addr);
+	if (!sta) {
+		wpa_msg(wpa_s, MSG_DEBUG, "no such mesh peer");
+		return;
+	}
+
+	/* TODO: Should do nothing if this STA is already authenticated, but
+	 * the AP code already sets this flag. */
+	sta->flags |= WLAN_STA_AUTH;
+
+	mesh_rsn_init_ampe_sta(wpa_s, sta);
+
+	os_memset(&params, 0, sizeof(params));
+	params.addr = sta->addr;
+	params.flags = WPA_STA_AUTHENTICATED | WPA_STA_AUTHORIZED;
+	params.set = 1;
+
+	wpa_msg(wpa_s, MSG_DEBUG, "MPM authenticating " MACSTR,
+		MAC2STR(sta->addr));
+	ret = wpa_drv_sta_add(wpa_s, &params);
+	if (ret) {
+		wpa_msg(wpa_s, MSG_ERROR,
+			"Driver failed to set " MACSTR ": %d",
+			MAC2STR(sta->addr), ret);
+	}
+
+	if (!sta->my_lid)
+		mesh_mpm_init_link(wpa_s, sta);
+
+	mesh_mpm_plink_open(wpa_s, sta, PLINK_OPEN_SENT);
+}
+
+/*
+ * Initialize a sta_info structure for a peer and upload it into the driver
+ * in preparation for beginning authentication or peering. This is done when a
+ * Beacon (secure or open mesh) or a peering open frame (for open mesh) is
+ * received from the peer for the first time.
+ */
+static struct sta_info * mesh_mpm_add_peer(struct wpa_supplicant *wpa_s,
+					   const u8 *addr,
+					   struct ieee802_11_elems *elems)
+{
+	struct hostapd_sta_add_params params;
+	struct mesh_conf *conf = wpa_s->ifmsh->mconf;
+	struct hostapd_data *data = wpa_s->ifmsh->bss[0];
+	struct sta_info *sta;
+	int ret;
+
+	sta = ap_get_sta(data, addr);
+	if (!sta) {
+		sta = ap_sta_add(data, addr);
+		if (!sta)
+			return NULL;
+	}
+
+	/* initialize sta */
+	if (copy_supp_rates(wpa_s, sta, elems)) {
+		ap_free_sta(data, sta);
+		return NULL;
+	}
+
+	mesh_mpm_init_link(wpa_s, sta);
+
+#ifdef CONFIG_IEEE80211N
+	copy_sta_ht_capab(data, sta, elems->ht_capabilities);
+	update_ht_state(data, sta);
+#endif /* CONFIG_IEEE80211N */
+
+	/* insert into driver */
+	os_memset(&params, 0, sizeof(params));
+	params.supp_rates = sta->supported_rates;
+	params.supp_rates_len = sta->supported_rates_len;
+	params.addr = addr;
+	params.plink_state = sta->plink_state;
+	params.aid = sta->peer_lid;
+	params.listen_interval = 100;
+	params.ht_capabilities = sta->ht_capabilities;
+	params.flags |= WPA_STA_WMM;
+	params.flags_mask |= WPA_STA_AUTHENTICATED;
+	if (conf->security == MESH_CONF_SEC_NONE) {
+		params.flags |= WPA_STA_AUTHORIZED;
+		params.flags |= WPA_STA_AUTHENTICATED;
+	} else {
+		sta->flags |= WLAN_STA_MFP;
+		params.flags |= WPA_STA_MFP;
+	}
+
+	ret = wpa_drv_sta_add(wpa_s, &params);
+	if (ret) {
+		wpa_msg(wpa_s, MSG_ERROR,
+			"Driver failed to insert " MACSTR ": %d",
+			MAC2STR(addr), ret);
+		ap_free_sta(data, sta);
+		return NULL;
+	}
+
+	return sta;
+}
+
+
+void wpa_mesh_new_mesh_peer(struct wpa_supplicant *wpa_s, const u8 *addr,
+			    struct ieee802_11_elems *elems)
+{
+	struct mesh_conf *conf = wpa_s->ifmsh->mconf;
+	struct hostapd_data *data = wpa_s->ifmsh->bss[0];
+	struct sta_info *sta;
+	struct wpa_ssid *ssid = wpa_s->current_ssid;
+
+	sta = mesh_mpm_add_peer(wpa_s, addr, elems);
+	if (!sta)
+		return;
+
+	if (ssid && ssid->no_auto_peer) {
+		wpa_msg(wpa_s, MSG_INFO, "will not initiate new peer link with "
+			MACSTR " because of no_auto_peer", MAC2STR(addr));
+		if (data->mesh_pending_auth) {
+			struct os_reltime age;
+			const struct ieee80211_mgmt *mgmt;
+			struct hostapd_frame_info fi;
+
+			mgmt = wpabuf_head(data->mesh_pending_auth);
+			os_reltime_age(&data->mesh_pending_auth_time, &age);
+			if (age.sec < 2 &&
+			    os_memcmp(mgmt->sa, addr, ETH_ALEN) == 0) {
+				wpa_printf(MSG_DEBUG,
+					   "mesh: Process pending Authentication frame from %u.%06u seconds ago",
+					   (unsigned int) age.sec,
+					   (unsigned int) age.usec);
+				os_memset(&fi, 0, sizeof(fi));
+				ieee802_11_mgmt(
+					data,
+					wpabuf_head(data->mesh_pending_auth),
+					wpabuf_len(data->mesh_pending_auth),
+					&fi);
+			}
+			wpabuf_free(data->mesh_pending_auth);
+			data->mesh_pending_auth = NULL;
+		}
+		return;
+	}
+
+	if (conf->security == MESH_CONF_SEC_NONE)
+		mesh_mpm_plink_open(wpa_s, sta, PLINK_OPEN_SENT);
+	else
+		mesh_rsn_auth_sae_sta(wpa_s, sta);
+}
+
+
+void mesh_mpm_mgmt_rx(struct wpa_supplicant *wpa_s, struct rx_mgmt *rx_mgmt)
+{
+	struct hostapd_frame_info fi;
+
+	os_memset(&fi, 0, sizeof(fi));
+	fi.datarate = rx_mgmt->datarate;
+	fi.ssi_signal = rx_mgmt->ssi_signal;
+	ieee802_11_mgmt(wpa_s->ifmsh->bss[0], rx_mgmt->frame,
+			rx_mgmt->frame_len, &fi);
+}
+
+
+static void mesh_mpm_plink_estab(struct wpa_supplicant *wpa_s,
+				 struct sta_info *sta)
+{
+	struct hostapd_data *hapd = wpa_s->ifmsh->bss[0];
+	struct mesh_conf *conf = wpa_s->ifmsh->mconf;
+	u8 seq[6] = {};
+
+	wpa_msg(wpa_s, MSG_INFO, "mesh plink with " MACSTR " established",
+		MAC2STR(sta->addr));
+
+	if (conf->security & MESH_CONF_SEC_AMPE) {
+		wpa_drv_set_key(wpa_s, WPA_ALG_CCMP, sta->addr, 0, 0,
+				seq, sizeof(seq), sta->mtk, sizeof(sta->mtk));
+		wpa_drv_set_key(wpa_s, WPA_ALG_CCMP, sta->addr, 1, 0,
+				seq, sizeof(seq),
+				sta->mgtk, sizeof(sta->mgtk));
+		wpa_drv_set_key(wpa_s, WPA_ALG_IGTK, sta->addr, 4, 0,
+				seq, sizeof(seq),
+				sta->mgtk, sizeof(sta->mgtk));
+
+		wpa_hexdump_key(MSG_DEBUG, "mtk:", sta->mtk, sizeof(sta->mtk));
+		wpa_hexdump_key(MSG_DEBUG, "mgtk:",
+				sta->mgtk, sizeof(sta->mgtk));
+	}
+
+	wpa_mesh_set_plink_state(wpa_s, sta, PLINK_ESTAB);
+	hapd->num_plinks++;
+
+	sta->flags |= WLAN_STA_ASSOC;
+
+	eloop_cancel_timeout(plink_timer, wpa_s, sta);
+
+	/* Send ctrl event */
+	wpa_msg_ctrl(wpa_s, MSG_INFO, MESH_PEER_CONNECTED MACSTR,
+		     MAC2STR(sta->addr));
+}
+
+
+static void mesh_mpm_fsm(struct wpa_supplicant *wpa_s, struct sta_info *sta,
+			 enum plink_event event)
+{
+	struct hostapd_data *hapd = wpa_s->ifmsh->bss[0];
+	struct mesh_conf *conf = wpa_s->ifmsh->mconf;
+	u16 reason = 0;
+
+	wpa_msg(wpa_s, MSG_DEBUG, "MPM " MACSTR " state %s event %s",
+		MAC2STR(sta->addr), mplstate[sta->plink_state],
+		mplevent[event]);
+
+	switch (sta->plink_state) {
+	case PLINK_LISTEN:
+		switch (event) {
+		case CLS_ACPT:
+			mesh_mpm_fsm_restart(wpa_s, sta);
+			break;
+		case OPN_ACPT:
+			mesh_mpm_plink_open(wpa_s, sta, PLINK_OPEN_RCVD);
+			mesh_mpm_send_plink_action(wpa_s, sta, PLINK_CONFIRM,
+						   0);
+			break;
+		default:
+			break;
+		}
+		break;
+	case PLINK_OPEN_SENT:
+		switch (event) {
+		case OPN_RJCT:
+		case CNF_RJCT:
+			reason = WLAN_REASON_MESH_CONFIG_POLICY_VIOLATION;
+			/* fall-through */
+		case CLS_ACPT:
+			wpa_mesh_set_plink_state(wpa_s, sta, PLINK_HOLDING);
+			if (!reason)
+				reason = WLAN_REASON_MESH_CLOSE_RCVD;
+			eloop_register_timeout(
+				conf->dot11MeshHoldingTimeout / 1000,
+				(conf->dot11MeshHoldingTimeout % 1000) * 1000,
+				plink_timer, wpa_s, sta);
+			mesh_mpm_send_plink_action(wpa_s, sta,
+						   PLINK_CLOSE, reason);
+			break;
+		case OPN_ACPT:
+			/* retry timer is left untouched */
+			wpa_mesh_set_plink_state(wpa_s, sta, PLINK_OPEN_RCVD);
+			mesh_mpm_send_plink_action(wpa_s, sta,
+						   PLINK_CONFIRM, 0);
+			break;
+		case CNF_ACPT:
+			wpa_mesh_set_plink_state(wpa_s, sta, PLINK_CNF_RCVD);
+			eloop_register_timeout(
+				conf->dot11MeshConfirmTimeout / 1000,
+				(conf->dot11MeshConfirmTimeout % 1000) * 1000,
+				plink_timer, wpa_s, sta);
+			break;
+		default:
+			break;
+		}
+		break;
+	case PLINK_OPEN_RCVD:
+		switch (event) {
+		case OPN_RJCT:
+		case CNF_RJCT:
+			reason = WLAN_REASON_MESH_CONFIG_POLICY_VIOLATION;
+			/* fall-through */
+		case CLS_ACPT:
+			wpa_mesh_set_plink_state(wpa_s, sta, PLINK_HOLDING);
+			if (!reason)
+				reason = WLAN_REASON_MESH_CLOSE_RCVD;
+			eloop_register_timeout(
+				conf->dot11MeshHoldingTimeout / 1000,
+				(conf->dot11MeshHoldingTimeout % 1000) * 1000,
+				plink_timer, wpa_s, sta);
+			sta->mpm_close_reason = reason;
+			mesh_mpm_send_plink_action(wpa_s, sta,
+						   PLINK_CLOSE, reason);
+			break;
+		case OPN_ACPT:
+			mesh_mpm_send_plink_action(wpa_s, sta,
+						   PLINK_CONFIRM, 0);
+			break;
+		case CNF_ACPT:
+			if (conf->security & MESH_CONF_SEC_AMPE)
+				mesh_rsn_derive_mtk(wpa_s, sta);
+			mesh_mpm_plink_estab(wpa_s, sta);
+			break;
+		default:
+			break;
+		}
+		break;
+	case PLINK_CNF_RCVD:
+		switch (event) {
+		case OPN_RJCT:
+		case CNF_RJCT:
+			reason = WLAN_REASON_MESH_CONFIG_POLICY_VIOLATION;
+			/* fall-through */
+		case CLS_ACPT:
+			wpa_mesh_set_plink_state(wpa_s, sta, PLINK_HOLDING);
+			if (!reason)
+				reason = WLAN_REASON_MESH_CLOSE_RCVD;
+			eloop_register_timeout(
+				conf->dot11MeshHoldingTimeout / 1000,
+				(conf->dot11MeshHoldingTimeout % 1000) * 1000,
+				plink_timer, wpa_s, sta);
+			sta->mpm_close_reason = reason;
+			mesh_mpm_send_plink_action(wpa_s, sta,
+						   PLINK_CLOSE, reason);
+			break;
+		case OPN_ACPT:
+			mesh_mpm_plink_estab(wpa_s, sta);
+			mesh_mpm_send_plink_action(wpa_s, sta,
+						   PLINK_CONFIRM, 0);
+			break;
+		default:
+			break;
+		}
+		break;
+	case PLINK_ESTAB:
+		switch (event) {
+		case CLS_ACPT:
+			wpa_mesh_set_plink_state(wpa_s, sta, PLINK_HOLDING);
+			reason = WLAN_REASON_MESH_CLOSE_RCVD;
+
+			eloop_register_timeout(
+				conf->dot11MeshHoldingTimeout / 1000,
+				(conf->dot11MeshHoldingTimeout % 1000) * 1000,
+				plink_timer, wpa_s, sta);
+			sta->mpm_close_reason = reason;
+
+			wpa_msg(wpa_s, MSG_INFO, "mesh plink with " MACSTR
+				" closed with reason %d",
+				MAC2STR(sta->addr), reason);
+
+			wpa_msg_ctrl(wpa_s, MSG_INFO,
+				     MESH_PEER_DISCONNECTED MACSTR,
+				     MAC2STR(sta->addr));
+
+			hapd->num_plinks--;
+
+			mesh_mpm_send_plink_action(wpa_s, sta,
+						   PLINK_CLOSE, reason);
+			break;
+		case OPN_ACPT:
+			mesh_mpm_send_plink_action(wpa_s, sta,
+						   PLINK_CONFIRM, 0);
+			break;
+		default:
+			break;
+		}
+		break;
+	case PLINK_HOLDING:
+		switch (event) {
+		case CLS_ACPT:
+			mesh_mpm_fsm_restart(wpa_s, sta);
+			break;
+		case OPN_ACPT:
+		case CNF_ACPT:
+		case OPN_RJCT:
+		case CNF_RJCT:
+			reason = sta->mpm_close_reason;
+			mesh_mpm_send_plink_action(wpa_s, sta,
+						   PLINK_CLOSE, reason);
+			break;
+		default:
+			break;
+		}
+		break;
+	default:
+		wpa_msg(wpa_s, MSG_DEBUG,
+			"Unsupported MPM event %s for state %s",
+			mplevent[event], mplstate[sta->plink_state]);
+		break;
+	}
+}
+
+
+void mesh_mpm_action_rx(struct wpa_supplicant *wpa_s,
+			const struct ieee80211_mgmt *mgmt, size_t len)
+{
+	u8 action_field;
+	struct hostapd_data *hapd = wpa_s->ifmsh->bss[0];
+	struct mesh_conf *mconf = wpa_s->ifmsh->mconf;
+	struct sta_info *sta;
+	u16 plid = 0, llid = 0;
+	enum plink_event event;
+	struct ieee802_11_elems elems;
+	struct mesh_peer_mgmt_ie peer_mgmt_ie;
+	const u8 *ies;
+	size_t ie_len;
+	int ret;
+
+	if (mgmt->u.action.category != WLAN_ACTION_SELF_PROTECTED)
+		return;
+
+	action_field = mgmt->u.action.u.slf_prot_action.action;
+	if (action_field != PLINK_OPEN &&
+	    action_field != PLINK_CONFIRM &&
+	    action_field != PLINK_CLOSE)
+		return;
+
+	ies = mgmt->u.action.u.slf_prot_action.variable;
+	ie_len = (const u8 *) mgmt + len -
+		mgmt->u.action.u.slf_prot_action.variable;
+
+	/* at least expect mesh id and peering mgmt */
+	if (ie_len < 2 + 2) {
+		wpa_printf(MSG_DEBUG,
+			   "MPM: Ignore too short action frame %u ie_len %u",
+			   action_field, (unsigned int) ie_len);
+		return;
+	}
+	wpa_printf(MSG_DEBUG, "MPM: Received PLINK action %u", action_field);
+
+	if (action_field == PLINK_OPEN || action_field == PLINK_CONFIRM) {
+		wpa_printf(MSG_DEBUG, "MPM: Capability 0x%x",
+			   WPA_GET_LE16(ies));
+		ies += 2;	/* capability */
+		ie_len -= 2;
+	}
+	if (action_field == PLINK_CONFIRM) {
+		wpa_printf(MSG_DEBUG, "MPM: AID 0x%x", WPA_GET_LE16(ies));
+		ies += 2;	/* aid */
+		ie_len -= 2;
+	}
+
+	/* check for mesh peering, mesh id and mesh config IEs */
+	if (ieee802_11_parse_elems(ies, ie_len, &elems, 0) == ParseFailed) {
+		wpa_printf(MSG_DEBUG, "MPM: Failed to parse PLINK IEs");
+		return;
+	}
+	if (!elems.peer_mgmt) {
+		wpa_printf(MSG_DEBUG,
+			   "MPM: No Mesh Peering Management element");
+		return;
+	}
+	if (action_field != PLINK_CLOSE) {
+		if (!elems.mesh_id || !elems.mesh_config) {
+			wpa_printf(MSG_DEBUG,
+				   "MPM: No Mesh ID or Mesh Configuration element");
+			return;
+		}
+
+		if (!matches_local(wpa_s, &elems)) {
+			wpa_printf(MSG_DEBUG,
+				   "MPM: Mesh ID or Mesh Configuration element do not match local MBSS");
+			return;
+		}
+	}
+
+	ret = mesh_mpm_parse_peer_mgmt(wpa_s, action_field,
+				       elems.peer_mgmt,
+				       elems.peer_mgmt_len,
+				       &peer_mgmt_ie);
+	if (ret) {
+		wpa_printf(MSG_DEBUG, "MPM: Mesh parsing rejected frame");
+		return;
+	}
+
+	/* the sender's llid is our plid and vice-versa */
+	plid = WPA_GET_LE16(peer_mgmt_ie.llid);
+	if (peer_mgmt_ie.plid)
+		llid = WPA_GET_LE16(peer_mgmt_ie.plid);
+	wpa_printf(MSG_DEBUG, "MPM: plid=0x%x llid=0x%x", plid, llid);
+
+	sta = ap_get_sta(hapd, mgmt->sa);
+
+	/*
+	 * If this is an open frame from an unknown STA, and this is an
+	 * open mesh, then go ahead and add the peer before proceeding.
+	 */
+	if (!sta && action_field == PLINK_OPEN &&
+	    !(mconf->security & MESH_CONF_SEC_AMPE))
+		sta = mesh_mpm_add_peer(wpa_s, mgmt->sa, &elems);
+
+	if (!sta) {
+		wpa_printf(MSG_DEBUG, "MPM: No STA entry for peer");
+		return;
+	}
+
+#ifdef CONFIG_SAE
+	/* peer is in sae_accepted? */
+	if (sta->sae && sta->sae->state != SAE_ACCEPTED) {
+		wpa_printf(MSG_DEBUG, "MPM: SAE not yet accepted for peer");
+		return;
+	}
+#endif /* CONFIG_SAE */
+
+	if (!sta->my_lid)
+		mesh_mpm_init_link(wpa_s, sta);
+
+	if ((mconf->security & MESH_CONF_SEC_AMPE) &&
+	    mesh_rsn_process_ampe(wpa_s, sta, &elems,
+				  &mgmt->u.action.category,
+				  ies, ie_len)) {
+		wpa_printf(MSG_DEBUG, "MPM: RSN process rejected frame");
+		return;
+	}
+
+	if (sta->plink_state == PLINK_BLOCKED) {
+		wpa_printf(MSG_DEBUG, "MPM: PLINK_BLOCKED");
+		return;
+	}
+
+	/* Now we will figure out the appropriate event... */
+	switch (action_field) {
+	case PLINK_OPEN:
+		if (plink_free_count(hapd) == 0) {
+			event = OPN_IGNR;
+			wpa_printf(MSG_INFO,
+				   "MPM: Peer link num over quota(%d)",
+				   hapd->max_plinks);
+		} else if (sta->peer_lid && sta->peer_lid != plid) {
+			event = OPN_IGNR;
+		} else {
+			sta->peer_lid = plid;
+			event = OPN_ACPT;
+		}
+		break;
+	case PLINK_CONFIRM:
+		if (plink_free_count(hapd) == 0) {
+			event = CNF_IGNR;
+			wpa_printf(MSG_INFO,
+				   "MPM: Peer link num over quota(%d)",
+				   hapd->max_plinks);
+		} else if (sta->my_lid != llid ||
+			   (sta->peer_lid && sta->peer_lid != plid)) {
+			event = CNF_IGNR;
+		} else {
+			if (!sta->peer_lid)
+				sta->peer_lid = plid;
+			event = CNF_ACPT;
+		}
+		break;
+	case PLINK_CLOSE:
+		if (sta->plink_state == PLINK_ESTAB)
+			/* Do not check for llid or plid. This does not
+			 * follow the standard but since multiple plinks
+			 * per cand are not supported, it is necessary in
+			 * order to avoid a livelock when MP A sees an
+			 * establish peer link to MP B but MP B does not
+			 * see it. This can be caused by a timeout in
+			 * B's peer link establishment or B being
+			 * restarted.
+			 */
+			event = CLS_ACPT;
+		else if (sta->peer_lid != plid)
+			event = CLS_IGNR;
+		else if (peer_mgmt_ie.plid && sta->my_lid != llid)
+			event = CLS_IGNR;
+		else
+			event = CLS_ACPT;
+		break;
+	default:
+		/*
+		 * This cannot be hit due to the action_field check above, but
+		 * compilers may not be able to figure that out and can warn
+		 * about uninitialized event below.
+		 */
+		return;
+	}
+	mesh_mpm_fsm(wpa_s, sta, event);
+}
+
+
+/* called by ap_free_sta */
+void mesh_mpm_free_sta(struct sta_info *sta)
+{
+	eloop_cancel_timeout(plink_timer, ELOOP_ALL_CTX, sta);
+	eloop_cancel_timeout(mesh_auth_timer, ELOOP_ALL_CTX, sta);
+}
diff --git a/wpa_supplicant/mesh_mpm.h b/wpa_supplicant/mesh_mpm.h
new file mode 100644
index 0000000..7ebaef0
--- /dev/null
+++ b/wpa_supplicant/mesh_mpm.h
@@ -0,0 +1,43 @@
+/*
+ * WPA Supplicant - Basic mesh peer management
+ * Copyright (c) 2013-2014, cozybit, Inc.  All rights reserved.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef MESH_MPM_H
+#define MESH_MPM_H
+
+/* notify MPM of new mesh peer to be inserted in MPM and driver */
+void wpa_mesh_new_mesh_peer(struct wpa_supplicant *wpa_s, const u8 *addr,
+			    struct ieee802_11_elems *elems);
+void mesh_mpm_deinit(struct wpa_supplicant *wpa_s, struct hostapd_iface *ifmsh);
+void mesh_mpm_auth_peer(struct wpa_supplicant *wpa_s, const u8 *addr);
+void mesh_mpm_free_sta(struct sta_info *sta);
+void wpa_mesh_set_plink_state(struct wpa_supplicant *wpa_s,
+			      struct sta_info *sta,
+			      enum mesh_plink_state state);
+
+#ifdef CONFIG_MESH
+
+void mesh_mpm_action_rx(struct wpa_supplicant *wpa_s,
+			const struct ieee80211_mgmt *mgmt, size_t len);
+void mesh_mpm_mgmt_rx(struct wpa_supplicant *wpa_s, struct rx_mgmt *rx_mgmt);
+
+#else /* CONFIG_MESH */
+
+static inline void mesh_mpm_action_rx(struct wpa_supplicant *wpa_s,
+				      const struct ieee80211_mgmt *mgmt,
+				      size_t len)
+{
+}
+
+static inline void mesh_mpm_mgmt_rx(struct wpa_supplicant *wpa_s,
+				    struct rx_mgmt *rx_mgmt)
+{
+}
+
+#endif /* CONFIG_MESH */
+
+#endif /* MESH_MPM_H */
diff --git a/wpa_supplicant/mesh_rsn.c b/wpa_supplicant/mesh_rsn.c
new file mode 100644
index 0000000..936002d
--- /dev/null
+++ b/wpa_supplicant/mesh_rsn.c
@@ -0,0 +1,574 @@
+/*
+ * WPA Supplicant - Mesh RSN routines
+ * Copyright (c) 2013-2014, cozybit, Inc.  All rights reserved.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "crypto/sha256.h"
+#include "crypto/random.h"
+#include "crypto/aes.h"
+#include "crypto/aes_siv.h"
+#include "rsn_supp/wpa.h"
+#include "ap/hostapd.h"
+#include "ap/wpa_auth.h"
+#include "ap/sta_info.h"
+#include "ap/ieee802_11.h"
+#include "wpa_supplicant_i.h"
+#include "driver_i.h"
+#include "wpas_glue.h"
+#include "mesh_mpm.h"
+#include "mesh_rsn.h"
+
+#define MESH_AUTH_TIMEOUT 10
+#define MESH_AUTH_RETRY 3
+#define MESH_AUTH_BLOCK_DURATION 3600
+
+void mesh_auth_timer(void *eloop_ctx, void *user_data)
+{
+	struct wpa_supplicant *wpa_s = eloop_ctx;
+	struct sta_info *sta = user_data;
+
+	if (sta->sae->state != SAE_ACCEPTED) {
+		wpa_printf(MSG_DEBUG, "AUTH: Re-authenticate with " MACSTR
+			   " (attempt %d) ",
+			   MAC2STR(sta->addr), sta->sae_auth_retry);
+		wpa_msg(wpa_s, MSG_INFO, MESH_SAE_AUTH_FAILURE "addr=" MACSTR,
+			MAC2STR(sta->addr));
+		if (sta->sae_auth_retry < MESH_AUTH_RETRY) {
+			mesh_rsn_auth_sae_sta(wpa_s, sta);
+		} else {
+			if (sta->sae_auth_retry > MESH_AUTH_RETRY) {
+				ap_free_sta(wpa_s->ifmsh->bss[0], sta);
+				return;
+			}
+
+			/* block the STA if exceeded the number of attempts */
+			wpa_mesh_set_plink_state(wpa_s, sta, PLINK_BLOCKED);
+			sta->sae->state = SAE_NOTHING;
+			if (wpa_s->mesh_auth_block_duration <
+			    MESH_AUTH_BLOCK_DURATION)
+				wpa_s->mesh_auth_block_duration += 60;
+			eloop_register_timeout(wpa_s->mesh_auth_block_duration,
+					       0, mesh_auth_timer, wpa_s, sta);
+			wpa_msg(wpa_s, MSG_INFO, MESH_SAE_AUTH_BLOCKED "addr="
+				MACSTR " duration=%d",
+				MAC2STR(sta->addr),
+				wpa_s->mesh_auth_block_duration);
+		}
+		sta->sae_auth_retry++;
+	}
+}
+
+
+static void auth_logger(void *ctx, const u8 *addr, logger_level level,
+			const char *txt)
+{
+	if (addr)
+		wpa_printf(MSG_DEBUG, "AUTH: " MACSTR " - %s",
+			   MAC2STR(addr), txt);
+	else
+		wpa_printf(MSG_DEBUG, "AUTH: %s", txt);
+}
+
+
+static const u8 *auth_get_psk(void *ctx, const u8 *addr,
+			      const u8 *p2p_dev_addr, const u8 *prev_psk)
+{
+	struct mesh_rsn *mesh_rsn = ctx;
+	struct hostapd_data *hapd = mesh_rsn->wpa_s->ifmsh->bss[0];
+	struct sta_info *sta = ap_get_sta(hapd, addr);
+
+	wpa_printf(MSG_DEBUG, "AUTH: %s (addr=" MACSTR " prev_psk=%p)",
+		   __func__, MAC2STR(addr), prev_psk);
+
+	if (sta && sta->auth_alg == WLAN_AUTH_SAE) {
+		if (!sta->sae || prev_psk)
+			return NULL;
+		return sta->sae->pmk;
+	}
+
+	return NULL;
+}
+
+
+static int auth_set_key(void *ctx, int vlan_id, enum wpa_alg alg,
+			const u8 *addr, int idx, u8 *key, size_t key_len)
+{
+	struct mesh_rsn *mesh_rsn = ctx;
+	u8 seq[6];
+
+	os_memset(seq, 0, sizeof(seq));
+
+	if (addr) {
+		wpa_printf(MSG_DEBUG, "AUTH: %s(alg=%d addr=" MACSTR
+			   " key_idx=%d)",
+			   __func__, alg, MAC2STR(addr), idx);
+	} else {
+		wpa_printf(MSG_DEBUG, "AUTH: %s(alg=%d key_idx=%d)",
+			   __func__, alg, idx);
+	}
+	wpa_hexdump_key(MSG_DEBUG, "AUTH: set_key - key", key, key_len);
+
+	return wpa_drv_set_key(mesh_rsn->wpa_s, alg, addr, idx,
+			       1, seq, 6, key, key_len);
+}
+
+
+static int auth_start_ampe(void *ctx, const u8 *addr)
+{
+	struct mesh_rsn *mesh_rsn = ctx;
+	struct hostapd_data *hapd;
+	struct sta_info *sta;
+
+	if (mesh_rsn->wpa_s->current_ssid->mode != WPAS_MODE_MESH)
+		return -1;
+
+	hapd = mesh_rsn->wpa_s->ifmsh->bss[0];
+	sta = ap_get_sta(hapd, addr);
+	if (sta)
+		eloop_cancel_timeout(mesh_auth_timer, mesh_rsn->wpa_s, sta);
+
+	mesh_mpm_auth_peer(mesh_rsn->wpa_s, addr);
+	return 0;
+}
+
+
+static int __mesh_rsn_auth_init(struct mesh_rsn *rsn, const u8 *addr)
+{
+	struct wpa_auth_config conf;
+	struct wpa_auth_callbacks cb;
+	u8 seq[6] = {};
+
+	wpa_printf(MSG_DEBUG, "AUTH: Initializing group state machine");
+
+	os_memset(&conf, 0, sizeof(conf));
+	conf.wpa = 2;
+	conf.wpa_key_mgmt = WPA_KEY_MGMT_SAE;
+	conf.wpa_pairwise = WPA_CIPHER_CCMP;
+	conf.rsn_pairwise = WPA_CIPHER_CCMP;
+	conf.wpa_group = WPA_CIPHER_CCMP;
+	conf.eapol_version = 0;
+	conf.wpa_group_rekey = -1;
+
+	os_memset(&cb, 0, sizeof(cb));
+	cb.ctx = rsn;
+	cb.logger = auth_logger;
+	cb.get_psk = auth_get_psk;
+	cb.set_key = auth_set_key;
+	cb.start_ampe = auth_start_ampe;
+
+	rsn->auth = wpa_init(addr, &conf, &cb);
+	if (rsn->auth == NULL) {
+		wpa_printf(MSG_DEBUG, "AUTH: wpa_init() failed");
+		return -1;
+	}
+
+	/* TODO: support rekeying */
+	if (random_get_bytes(rsn->mgtk, 16) < 0) {
+		wpa_deinit(rsn->auth);
+		return -1;
+	}
+
+	/* group mgmt */
+	wpa_drv_set_key(rsn->wpa_s, WPA_ALG_IGTK, NULL, 4, 1,
+			seq, sizeof(seq), rsn->mgtk, sizeof(rsn->mgtk));
+
+	/* group privacy / data frames */
+	wpa_drv_set_key(rsn->wpa_s, WPA_ALG_CCMP, NULL, 1, 1,
+			seq, sizeof(seq), rsn->mgtk, sizeof(rsn->mgtk));
+
+	return 0;
+}
+
+
+static void mesh_rsn_deinit(struct mesh_rsn *rsn)
+{
+	os_memset(rsn->mgtk, 0, sizeof(rsn->mgtk));
+	wpa_deinit(rsn->auth);
+}
+
+
+struct mesh_rsn *mesh_rsn_auth_init(struct wpa_supplicant *wpa_s,
+				    struct mesh_conf *conf)
+{
+	struct mesh_rsn *mesh_rsn;
+	struct hostapd_data *bss = wpa_s->ifmsh->bss[0];
+	const u8 *ie;
+	size_t ie_len;
+
+	mesh_rsn = os_zalloc(sizeof(*mesh_rsn));
+	if (mesh_rsn == NULL)
+		return NULL;
+	mesh_rsn->wpa_s = wpa_s;
+
+	if (__mesh_rsn_auth_init(mesh_rsn, wpa_s->own_addr) < 0) {
+		mesh_rsn_deinit(mesh_rsn);
+		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;
+
+	wpa_supplicant_rsn_supp_set_config(wpa_s, wpa_s->current_ssid);
+
+	return mesh_rsn;
+}
+
+
+static int index_within_array(const int *array, int idx)
+{
+	int i;
+
+	for (i = 0; i < idx; i++) {
+		if (array[i] == -1)
+			return 0;
+	}
+
+	return 1;
+}
+
+
+static int mesh_rsn_sae_group(struct wpa_supplicant *wpa_s,
+			      struct sae_data *sae)
+{
+	int *groups = wpa_s->ifmsh->bss[0]->conf->sae_groups;
+
+	/* Configuration may have changed, so validate current index */
+	if (!index_within_array(groups, wpa_s->mesh_rsn->sae_group_index))
+		return -1;
+
+	for (;;) {
+		int group = groups[wpa_s->mesh_rsn->sae_group_index];
+
+		if (group <= 0)
+			break;
+		if (sae_set_group(sae, group) == 0) {
+			wpa_dbg(wpa_s, MSG_DEBUG, "SME: Selected SAE group %d",
+				sae->group);
+			return 0;
+		}
+		wpa_s->mesh_rsn->sae_group_index++;
+	}
+
+	return -1;
+}
+
+
+static int mesh_rsn_build_sae_commit(struct wpa_supplicant *wpa_s,
+				     struct wpa_ssid *ssid,
+				     struct sta_info *sta)
+{
+	if (ssid->passphrase == NULL) {
+		wpa_msg(wpa_s, MSG_DEBUG, "SAE: No password available");
+		return -1;
+	}
+
+	if (mesh_rsn_sae_group(wpa_s, sta->sae) < 0) {
+		wpa_msg(wpa_s, MSG_DEBUG, "SAE: Failed to select group");
+		return -1;
+	}
+
+	return sae_prepare_commit(wpa_s->own_addr, sta->addr,
+				  (u8 *) ssid->passphrase,
+				  os_strlen(ssid->passphrase), sta->sae);
+}
+
+
+/* initiate new SAE authentication with sta */
+int mesh_rsn_auth_sae_sta(struct wpa_supplicant *wpa_s,
+			  struct sta_info *sta)
+{
+	struct hostapd_data *hapd = wpa_s->ifmsh->bss[0];
+	struct wpa_ssid *ssid = wpa_s->current_ssid;
+	unsigned int rnd;
+	int ret;
+
+	if (!ssid) {
+		wpa_msg(wpa_s, MSG_DEBUG,
+			"AUTH: No current_ssid known to initiate new SAE");
+		return -1;
+	}
+
+	if (!sta->sae) {
+		sta->sae = os_zalloc(sizeof(*sta->sae));
+		if (sta->sae == NULL)
+			return -1;
+	}
+
+	if (mesh_rsn_build_sae_commit(wpa_s, ssid, sta))
+		return -1;
+
+	wpa_msg(wpa_s, MSG_DEBUG,
+		"AUTH: started authentication with SAE peer: " MACSTR,
+		MAC2STR(sta->addr));
+
+	wpa_supplicant_set_state(wpa_s, WPA_AUTHENTICATING);
+	ret = auth_sae_init_committed(hapd, sta);
+	if (ret)
+		return ret;
+
+	eloop_cancel_timeout(mesh_auth_timer, wpa_s, sta);
+	rnd = rand() % MESH_AUTH_TIMEOUT;
+	eloop_register_timeout(MESH_AUTH_TIMEOUT + rnd, 0, mesh_auth_timer,
+			       wpa_s, sta);
+	return 0;
+}
+
+
+void mesh_rsn_get_pmkid(struct mesh_rsn *rsn, struct sta_info *sta, u8 *pmkid)
+{
+	/* don't expect wpa auth to cache the pmkid for now */
+	rsn_pmkid(sta->sae->pmk, PMK_LEN, rsn->wpa_s->own_addr,
+		  sta->addr, pmkid,
+		  wpa_key_mgmt_sha256(wpa_auth_sta_key_mgmt(sta->wpa_sm)));
+}
+
+
+static void
+mesh_rsn_derive_aek(struct mesh_rsn *rsn, struct sta_info *sta)
+{
+	u8 *myaddr = rsn->wpa_s->own_addr;
+	u8 *peer = sta->addr;
+	u8 *addr1 = peer, *addr2 = myaddr;
+	u8 context[AES_BLOCK_SIZE];
+
+	/* SAE */
+	RSN_SELECTOR_PUT(context, wpa_cipher_to_suite(0, WPA_CIPHER_GCMP));
+
+	if (os_memcmp(myaddr, peer, ETH_ALEN) < 0) {
+		addr1 = myaddr;
+		addr2 = peer;
+	}
+	os_memcpy(context + 4, addr1, ETH_ALEN);
+	os_memcpy(context + 10, addr2, ETH_ALEN);
+
+	sha256_prf(sta->sae->pmk, sizeof(sta->sae->pmk), "AEK Derivation",
+		   context, sizeof(context), sta->aek, sizeof(sta->aek));
+}
+
+
+/* derive mesh temporal key from pmk */
+int mesh_rsn_derive_mtk(struct wpa_supplicant *wpa_s, struct sta_info *sta)
+{
+	u8 *ptr;
+	u8 *min, *max;
+	u16 min_lid, max_lid;
+	size_t nonce_len = sizeof(sta->my_nonce);
+	size_t lid_len = sizeof(sta->my_lid);
+	u8 *myaddr = wpa_s->own_addr;
+	u8 *peer = sta->addr;
+	/* 2 nonces, 2 linkids, akm suite, 2 mac addrs */
+	u8 context[64 + 4 + 4 + 12];
+
+	ptr = context;
+	if (os_memcmp(sta->my_nonce, sta->peer_nonce, nonce_len) < 0) {
+		min = sta->my_nonce;
+		max = sta->peer_nonce;
+	} else {
+		min = sta->peer_nonce;
+		max = sta->my_nonce;
+	}
+	os_memcpy(ptr, min, nonce_len);
+	os_memcpy(ptr + nonce_len, max, nonce_len);
+	ptr += 2 * nonce_len;
+
+	if (sta->my_lid < sta->peer_lid) {
+		min_lid = host_to_le16(sta->my_lid);
+		max_lid = host_to_le16(sta->peer_lid);
+	} else {
+		min_lid = host_to_le16(sta->peer_lid);
+		max_lid = host_to_le16(sta->my_lid);
+	}
+	os_memcpy(ptr, &min_lid, lid_len);
+	os_memcpy(ptr + lid_len, &max_lid, lid_len);
+	ptr += 2 * lid_len;
+
+	/* SAE */
+	RSN_SELECTOR_PUT(ptr, wpa_cipher_to_suite(0, WPA_CIPHER_GCMP));
+	ptr += 4;
+
+	if (os_memcmp(myaddr, peer, ETH_ALEN) < 0) {
+		min = myaddr;
+		max = peer;
+	} else {
+		min = peer;
+		max = myaddr;
+	}
+	os_memcpy(ptr, min, ETH_ALEN);
+	os_memcpy(ptr + ETH_ALEN, max, ETH_ALEN);
+
+	sha256_prf(sta->sae->pmk, sizeof(sta->sae->pmk),
+		   "Temporal Key Derivation", context, sizeof(context),
+		   sta->mtk, sizeof(sta->mtk));
+	return 0;
+}
+
+
+void mesh_rsn_init_ampe_sta(struct wpa_supplicant *wpa_s, struct sta_info *sta)
+{
+	if (random_get_bytes(sta->my_nonce, 32) < 0) {
+		wpa_printf(MSG_INFO, "mesh: Failed to derive random nonce");
+		/* TODO: How to handle this more cleanly? */
+	}
+	os_memset(sta->peer_nonce, 0, 32);
+	mesh_rsn_derive_aek(wpa_s->mesh_rsn, sta);
+}
+
+
+/* insert AMPE and encrypted MIC at @ie.
+ * @mesh_rsn: mesh RSN context
+ * @sta: STA we're sending to
+ * @cat: pointer to category code in frame header.
+ * @buf: wpabuf to add encrypted AMPE and MIC to.
+ * */
+int mesh_rsn_protect_frame(struct mesh_rsn *rsn, struct sta_info *sta,
+			   const u8 *cat, struct wpabuf *buf)
+{
+	struct ieee80211_ampe_ie *ampe;
+	u8 const *ie = wpabuf_head_u8(buf) + wpabuf_len(buf);
+	u8 *ampe_ie = NULL, *mic_ie = NULL, *mic_payload;
+	const u8 *aad[] = { rsn->wpa_s->own_addr, sta->addr, cat };
+	const size_t aad_len[] = { ETH_ALEN, ETH_ALEN, ie - cat };
+	int ret = 0;
+
+	if (AES_BLOCK_SIZE + 2 + sizeof(*ampe) + 2 > wpabuf_tailroom(buf)) {
+		wpa_printf(MSG_ERROR, "protect frame: buffer too small");
+		return -EINVAL;
+	}
+
+	ampe_ie = os_zalloc(2 + sizeof(*ampe));
+	if (!ampe_ie) {
+		wpa_printf(MSG_ERROR, "protect frame: out of memory");
+		return -ENOMEM;
+	}
+
+	mic_ie = os_zalloc(2 + AES_BLOCK_SIZE);
+	if (!mic_ie) {
+		wpa_printf(MSG_ERROR, "protect frame: out of memory");
+		ret = -ENOMEM;
+		goto free;
+	}
+
+	/*  IE: AMPE */
+	ampe_ie[0] = WLAN_EID_AMPE;
+	ampe_ie[1] = sizeof(*ampe);
+	ampe = (struct ieee80211_ampe_ie *) (ampe_ie + 2);
+
+	RSN_SELECTOR_PUT(ampe->selected_pairwise_suite,
+		     wpa_cipher_to_suite(WPA_PROTO_RSN, WPA_CIPHER_CCMP));
+	os_memcpy(ampe->local_nonce, sta->my_nonce, 32);
+	os_memcpy(ampe->peer_nonce, sta->peer_nonce, 32);
+	/* incomplete: see 13.5.4 */
+	/* TODO: static mgtk for now since we don't support rekeying! */
+	os_memcpy(ampe->mgtk, rsn->mgtk, 16);
+	/*  TODO: Populate Key RSC */
+	/*  expire in 13 decades or so */
+	os_memset(ampe->key_expiration, 0xff, 4);
+
+	/* IE: MIC */
+	mic_ie[0] = WLAN_EID_MIC;
+	mic_ie[1] = AES_BLOCK_SIZE;
+	wpabuf_put_data(buf, mic_ie, 2);
+	/* MIC field is output ciphertext */
+
+	/* encrypt after MIC */
+	mic_payload = (u8 *) wpabuf_put(buf, 2 + sizeof(*ampe) +
+					AES_BLOCK_SIZE);
+
+	if (aes_siv_encrypt(sta->aek, ampe_ie, 2 + sizeof(*ampe), 3,
+			    aad, aad_len, mic_payload)) {
+		wpa_printf(MSG_ERROR, "protect frame: failed to encrypt");
+		ret = -ENOMEM;
+		goto free;
+	}
+
+free:
+	os_free(ampe_ie);
+	os_free(mic_ie);
+
+	return ret;
+}
+
+
+int mesh_rsn_process_ampe(struct wpa_supplicant *wpa_s, struct sta_info *sta,
+			  struct ieee802_11_elems *elems, const u8 *cat,
+			  const u8 *start, size_t elems_len)
+{
+	int ret = 0;
+	struct ieee80211_ampe_ie *ampe;
+	u8 null_nonce[32] = {};
+	u8 ampe_eid;
+	u8 ampe_ie_len;
+	u8 *ampe_buf, *crypt = NULL;
+	size_t crypt_len;
+	const u8 *aad[] = { sta->addr, wpa_s->own_addr, cat };
+	const size_t aad_len[] = { ETH_ALEN, ETH_ALEN,
+				   (elems->mic - 2) - cat };
+
+	if (!elems->mic || elems->mic_len < AES_BLOCK_SIZE) {
+		wpa_msg(wpa_s, MSG_DEBUG, "Mesh RSN: missing mic ie");
+		return -1;
+	}
+
+	ampe_buf = (u8 *) elems->mic + elems->mic_len;
+	if ((int) elems_len < ampe_buf - start)
+		return -1;
+
+	crypt_len = elems_len - (elems->mic - start);
+	if (crypt_len < 2) {
+		wpa_msg(wpa_s, MSG_DEBUG, "Mesh RSN: missing ampe ie");
+		return -1;
+	}
+
+	/* crypt is modified by siv_decrypt */
+	crypt = os_zalloc(crypt_len);
+	if (!crypt) {
+		wpa_printf(MSG_ERROR, "Mesh RSN: out of memory");
+		ret = -ENOMEM;
+		goto free;
+	}
+
+	os_memcpy(crypt, elems->mic, crypt_len);
+
+	if (aes_siv_decrypt(sta->aek, crypt, crypt_len, 3,
+			    aad, aad_len, ampe_buf)) {
+		wpa_printf(MSG_ERROR, "Mesh RSN: frame verification failed!");
+		ret = -1;
+		goto free;
+	}
+
+	ampe_eid = *ampe_buf++;
+	ampe_ie_len = *ampe_buf++;
+
+	if (ampe_eid != WLAN_EID_AMPE ||
+	    ampe_ie_len < sizeof(struct ieee80211_ampe_ie)) {
+		wpa_msg(wpa_s, MSG_DEBUG, "Mesh RSN: invalid ampe ie");
+		ret = -1;
+		goto free;
+	}
+
+	ampe = (struct ieee80211_ampe_ie *) ampe_buf;
+	if (os_memcmp(ampe->peer_nonce, null_nonce, 32) != 0 &&
+	    os_memcmp(ampe->peer_nonce, sta->my_nonce, 32) != 0) {
+		wpa_msg(wpa_s, MSG_DEBUG, "Mesh RSN: invalid peer nonce");
+		ret = -1;
+		goto free;
+	}
+	os_memcpy(sta->peer_nonce, ampe->local_nonce,
+		  sizeof(ampe->local_nonce));
+	os_memcpy(sta->mgtk, ampe->mgtk, sizeof(ampe->mgtk));
+
+	/* todo parse mgtk expiration */
+free:
+	os_free(crypt);
+	return ret;
+}
diff --git a/wpa_supplicant/mesh_rsn.h b/wpa_supplicant/mesh_rsn.h
new file mode 100644
index 0000000..b1471b2
--- /dev/null
+++ b/wpa_supplicant/mesh_rsn.h
@@ -0,0 +1,36 @@
+/*
+ * WPA Supplicant - Mesh RSN routines
+ * Copyright (c) 2013-2014, cozybit, Inc.  All rights reserved.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef MESH_RSN_H
+#define MESH_RSN_H
+
+struct mesh_rsn {
+	struct wpa_supplicant *wpa_s;
+	struct wpa_authenticator *auth;
+	u8 mgtk[16];
+#ifdef CONFIG_SAE
+	struct wpabuf *sae_token;
+	int sae_group_index;
+#endif /* CONFIG_SAE */
+};
+
+struct mesh_rsn * mesh_rsn_auth_init(struct wpa_supplicant *wpa_s,
+				     struct mesh_conf *conf);
+int mesh_rsn_auth_sae_sta(struct wpa_supplicant *wpa_s, struct sta_info *sta);
+int mesh_rsn_derive_mtk(struct wpa_supplicant *wpa_s, struct sta_info *sta);
+void mesh_rsn_get_pmkid(struct mesh_rsn *rsn, struct sta_info *sta, u8 *pmkid);
+void mesh_rsn_init_ampe_sta(struct wpa_supplicant *wpa_s,
+			    struct sta_info *sta);
+int mesh_rsn_protect_frame(struct mesh_rsn *rsn, struct sta_info *sta,
+			   const u8 *cat, struct wpabuf *buf);
+int mesh_rsn_process_ampe(struct wpa_supplicant *wpa_s, struct sta_info *sta,
+			  struct ieee802_11_elems *elems, const u8 *cat,
+			  const u8 *start, size_t elems_len);
+void mesh_auth_timer(void *eloop_ctx, void *user_data);
+
+#endif /* MESH_RSN_H */
diff --git a/wpa_supplicant/notify.c b/wpa_supplicant/notify.c
index 617ce84..822db74 100644
--- a/wpa_supplicant/notify.c
+++ b/wpa_supplicant/notify.c
@@ -48,6 +48,9 @@
 
 int wpas_notify_iface_added(struct wpa_supplicant *wpa_s)
 {
+	if (wpa_s->p2p_mgmt)
+		return 0;
+
 	if (wpas_dbus_register_iface(wpa_s))
 		return -1;
 
@@ -60,6 +63,9 @@
 
 void wpas_notify_iface_removed(struct wpa_supplicant *wpa_s)
 {
+	if (wpa_s->p2p_mgmt)
+		return;
+
 	/* unregister interface in old DBus ctrl iface */
 	wpas_dbus_unregister_iface(wpa_s);
 
@@ -72,6 +78,9 @@
 			       enum wpa_states new_state,
 			       enum wpa_states old_state)
 {
+	if (wpa_s->p2p_mgmt)
+		return;
+
 	/* notify the old DBus API */
 	wpa_supplicant_dbus_notify_state_change(wpa_s, new_state,
 						old_state);
@@ -101,30 +110,45 @@
 
 void wpas_notify_disconnect_reason(struct wpa_supplicant *wpa_s)
 {
+	if (wpa_s->p2p_mgmt)
+		return;
+
 	wpas_dbus_signal_prop_changed(wpa_s, WPAS_DBUS_PROP_DISCONNECT_REASON);
 }
 
 
 void wpas_notify_network_changed(struct wpa_supplicant *wpa_s)
 {
+	if (wpa_s->p2p_mgmt)
+		return;
+
 	wpas_dbus_signal_prop_changed(wpa_s, WPAS_DBUS_PROP_CURRENT_NETWORK);
 }
 
 
 void wpas_notify_ap_scan_changed(struct wpa_supplicant *wpa_s)
 {
+	if (wpa_s->p2p_mgmt)
+		return;
+
 	wpas_dbus_signal_prop_changed(wpa_s, WPAS_DBUS_PROP_AP_SCAN);
 }
 
 
 void wpas_notify_bssid_changed(struct wpa_supplicant *wpa_s)
 {
+	if (wpa_s->p2p_mgmt)
+		return;
+
 	wpas_dbus_signal_prop_changed(wpa_s, WPAS_DBUS_PROP_CURRENT_BSS);
 }
 
 
 void wpas_notify_auth_changed(struct wpa_supplicant *wpa_s)
 {
+	if (wpa_s->p2p_mgmt)
+		return;
+
 	wpas_dbus_signal_prop_changed(wpa_s, WPAS_DBUS_PROP_CURRENT_AUTH_MODE);
 }
 
@@ -132,6 +156,9 @@
 void wpas_notify_network_enabled_changed(struct wpa_supplicant *wpa_s,
 					 struct wpa_ssid *ssid)
 {
+	if (wpa_s->p2p_mgmt)
+		return;
+
 	wpas_dbus_signal_network_enabled_changed(wpa_s, ssid);
 }
 
@@ -139,6 +166,9 @@
 void wpas_notify_network_selected(struct wpa_supplicant *wpa_s,
 				  struct wpa_ssid *ssid)
 {
+	if (wpa_s->p2p_mgmt)
+		return;
+
 	wpas_dbus_signal_network_selected(wpa_s, ssid->id);
 }
 
@@ -148,12 +178,18 @@
 				 enum wpa_ctrl_req_type rtype,
 				 const char *default_txt)
 {
+	if (wpa_s->p2p_mgmt)
+		return;
+
 	wpas_dbus_signal_network_request(wpa_s, ssid, rtype, default_txt);
 }
 
 
 void wpas_notify_scanning(struct wpa_supplicant *wpa_s)
 {
+	if (wpa_s->p2p_mgmt)
+		return;
+
 	/* notify the old DBus API */
 	wpa_supplicant_dbus_notify_scanning(wpa_s);
 
@@ -164,12 +200,18 @@
 
 void wpas_notify_scan_done(struct wpa_supplicant *wpa_s, int success)
 {
+	if (wpa_s->p2p_mgmt)
+		return;
+
 	wpas_dbus_signal_scan_done(wpa_s, success);
 }
 
 
 void wpas_notify_scan_results(struct wpa_supplicant *wpa_s)
 {
+	if (wpa_s->p2p_mgmt)
+		return;
+
 	/* notify the old DBus API */
 	wpa_supplicant_dbus_notify_scan_results(wpa_s);
 
@@ -180,6 +222,9 @@
 void wpas_notify_wps_credential(struct wpa_supplicant *wpa_s,
 				const struct wps_credential *cred)
 {
+	if (wpa_s->p2p_mgmt)
+		return;
+
 #ifdef CONFIG_WPS
 	/* notify the old DBus API */
 	wpa_supplicant_dbus_notify_wps_cred(wpa_s, cred);
@@ -192,6 +237,9 @@
 void wpas_notify_wps_event_m2d(struct wpa_supplicant *wpa_s,
 			       struct wps_event_m2d *m2d)
 {
+	if (wpa_s->p2p_mgmt)
+		return;
+
 #ifdef CONFIG_WPS
 	wpas_dbus_signal_wps_event_m2d(wpa_s, m2d);
 #endif /* CONFIG_WPS */
@@ -201,6 +249,9 @@
 void wpas_notify_wps_event_fail(struct wpa_supplicant *wpa_s,
 				struct wps_event_fail *fail)
 {
+	if (wpa_s->p2p_mgmt)
+		return;
+
 #ifdef CONFIG_WPS
 	wpas_dbus_signal_wps_event_fail(wpa_s, fail);
 #endif /* CONFIG_WPS */
@@ -209,15 +260,31 @@
 
 void wpas_notify_wps_event_success(struct wpa_supplicant *wpa_s)
 {
+	if (wpa_s->p2p_mgmt)
+		return;
+
 #ifdef CONFIG_WPS
 	wpas_dbus_signal_wps_event_success(wpa_s);
 #endif /* CONFIG_WPS */
 }
 
+void wpas_notify_wps_event_pbc_overlap(struct wpa_supplicant *wpa_s)
+{
+	if (wpa_s->p2p_mgmt)
+		return;
+
+#ifdef CONFIG_WPS
+	wpas_dbus_signal_wps_event_pbc_overlap(wpa_s);
+#endif /* CONFIG_WPS */
+}
+
 
 void wpas_notify_network_added(struct wpa_supplicant *wpa_s,
 			       struct wpa_ssid *ssid)
 {
+	if (wpa_s->p2p_mgmt)
+		return;
+
 	/*
 	 * Networks objects created during any P2P activities should not be
 	 * exposed out. They might/will confuse certain non-P2P aware
@@ -254,8 +321,12 @@
 		wpa_s->next_ssid = NULL;
 	if (wpa_s->wpa)
 		wpa_sm_pmksa_cache_flush(wpa_s->wpa, ssid);
-	if (!ssid->p2p_group && wpa_s->global->p2p_group_formation != wpa_s)
+	if (!ssid->p2p_group && wpa_s->global->p2p_group_formation != wpa_s &&
+	    !wpa_s->p2p_mgmt)
 		wpas_dbus_unregister_network(wpa_s, ssid->id);
+	if (network_is_persistent_group(ssid))
+		wpas_notify_persistent_group_removed(wpa_s, ssid);
+
 	wpas_p2p_network_removed(wpa_s, ssid);
 }
 
@@ -263,6 +334,9 @@
 void wpas_notify_bss_added(struct wpa_supplicant *wpa_s,
 			   u8 bssid[], unsigned int id)
 {
+	if (wpa_s->p2p_mgmt)
+		return;
+
 	wpas_dbus_register_bss(wpa_s, bssid, id);
 	wpa_msg_ctrl(wpa_s, MSG_INFO, WPA_EVENT_BSS_ADDED "%u " MACSTR,
 		     id, MAC2STR(bssid));
@@ -272,6 +346,9 @@
 void wpas_notify_bss_removed(struct wpa_supplicant *wpa_s,
 			     u8 bssid[], unsigned int id)
 {
+	if (wpa_s->p2p_mgmt)
+		return;
+
 	wpas_dbus_unregister_bss(wpa_s, bssid, id);
 	wpa_msg_ctrl(wpa_s, MSG_INFO, WPA_EVENT_BSS_REMOVED "%u " MACSTR,
 		     id, MAC2STR(bssid));
@@ -281,6 +358,9 @@
 void wpas_notify_bss_freq_changed(struct wpa_supplicant *wpa_s,
 				  unsigned int id)
 {
+	if (wpa_s->p2p_mgmt)
+		return;
+
 	wpas_dbus_bss_signal_prop_changed(wpa_s, WPAS_DBUS_BSS_PROP_FREQ, id);
 }
 
@@ -288,6 +368,9 @@
 void wpas_notify_bss_signal_changed(struct wpa_supplicant *wpa_s,
 				    unsigned int id)
 {
+	if (wpa_s->p2p_mgmt)
+		return;
+
 	wpas_dbus_bss_signal_prop_changed(wpa_s, WPAS_DBUS_BSS_PROP_SIGNAL,
 					  id);
 }
@@ -296,6 +379,9 @@
 void wpas_notify_bss_privacy_changed(struct wpa_supplicant *wpa_s,
 				     unsigned int id)
 {
+	if (wpa_s->p2p_mgmt)
+		return;
+
 	wpas_dbus_bss_signal_prop_changed(wpa_s, WPAS_DBUS_BSS_PROP_PRIVACY,
 					  id);
 }
@@ -304,6 +390,9 @@
 void wpas_notify_bss_mode_changed(struct wpa_supplicant *wpa_s,
 				  unsigned int id)
 {
+	if (wpa_s->p2p_mgmt)
+		return;
+
 	wpas_dbus_bss_signal_prop_changed(wpa_s, WPAS_DBUS_BSS_PROP_MODE, id);
 }
 
@@ -311,6 +400,9 @@
 void wpas_notify_bss_wpaie_changed(struct wpa_supplicant *wpa_s,
 				   unsigned int id)
 {
+	if (wpa_s->p2p_mgmt)
+		return;
+
 	wpas_dbus_bss_signal_prop_changed(wpa_s, WPAS_DBUS_BSS_PROP_WPA, id);
 }
 
@@ -318,6 +410,9 @@
 void wpas_notify_bss_rsnie_changed(struct wpa_supplicant *wpa_s,
 				   unsigned int id)
 {
+	if (wpa_s->p2p_mgmt)
+		return;
+
 	wpas_dbus_bss_signal_prop_changed(wpa_s, WPAS_DBUS_BSS_PROP_RSN, id);
 }
 
@@ -325,6 +420,9 @@
 void wpas_notify_bss_wps_changed(struct wpa_supplicant *wpa_s,
 				 unsigned int id)
 {
+	if (wpa_s->p2p_mgmt)
+		return;
+
 #ifdef CONFIG_WPS
 	wpas_dbus_bss_signal_prop_changed(wpa_s, WPAS_DBUS_BSS_PROP_WPS, id);
 #endif /* CONFIG_WPS */
@@ -334,6 +432,9 @@
 void wpas_notify_bss_ies_changed(struct wpa_supplicant *wpa_s,
 				   unsigned int id)
 {
+	if (wpa_s->p2p_mgmt)
+		return;
+
 	wpas_dbus_bss_signal_prop_changed(wpa_s, WPAS_DBUS_BSS_PROP_IES, id);
 }
 
@@ -341,24 +442,36 @@
 void wpas_notify_bss_rates_changed(struct wpa_supplicant *wpa_s,
 				   unsigned int id)
 {
+	if (wpa_s->p2p_mgmt)
+		return;
+
 	wpas_dbus_bss_signal_prop_changed(wpa_s, WPAS_DBUS_BSS_PROP_RATES, id);
 }
 
 
 void wpas_notify_bss_seen(struct wpa_supplicant *wpa_s, unsigned int id)
 {
+	if (wpa_s->p2p_mgmt)
+		return;
+
 	wpas_dbus_bss_signal_prop_changed(wpa_s, WPAS_DBUS_BSS_PROP_AGE, id);
 }
 
 
 void wpas_notify_blob_added(struct wpa_supplicant *wpa_s, const char *name)
 {
+	if (wpa_s->p2p_mgmt)
+		return;
+
 	wpas_dbus_signal_blob_added(wpa_s, name);
 }
 
 
 void wpas_notify_blob_removed(struct wpa_supplicant *wpa_s, const char *name)
 {
+	if (wpa_s->p2p_mgmt)
+		return;
+
 	wpas_dbus_signal_blob_removed(wpa_s, name);
 }
 
@@ -417,6 +530,13 @@
 
 #ifdef CONFIG_P2P
 
+void wpas_notify_p2p_find_stopped(struct wpa_supplicant *wpa_s)
+{
+	/* Notify P2P find has stopped */
+	wpas_dbus_signal_p2p_find_stopped(wpa_s);
+}
+
+
 void wpas_notify_p2p_device_found(struct wpa_supplicant *wpa_s,
 				  const u8 *dev_addr, int new_device)
 {
@@ -451,9 +571,9 @@
 
 
 void wpas_notify_p2p_go_neg_req(struct wpa_supplicant *wpa_s,
-				const u8 *src, u16 dev_passwd_id)
+				const u8 *src, u16 dev_passwd_id, u8 go_intent)
 {
-	wpas_dbus_signal_p2p_go_neg_req(wpa_s, src, dev_passwd_id);
+	wpas_dbus_signal_p2p_go_neg_req(wpa_s, src, dev_passwd_id, go_intent);
 }
 
 
@@ -546,7 +666,8 @@
 	 * Create 'peer-joined' signal on group object -- will also
 	 * check P2P itself.
 	 */
-	wpas_dbus_signal_p2p_peer_joined(wpa_s, p2p_dev_addr);
+	if (p2p_dev_addr)
+		wpas_dbus_signal_p2p_peer_joined(wpa_s, p2p_dev_addr);
 #endif /* CONFIG_P2P */
 
 	/* Notify listeners a new station has been authorized */
@@ -563,7 +684,8 @@
 	 * Create 'peer-disconnected' signal on group object if this
 	 * is a P2P group.
 	 */
-	wpas_dbus_signal_p2p_peer_disconnected(wpa_s, p2p_dev_addr);
+	if (p2p_dev_addr)
+		wpas_dbus_signal_p2p_peer_disconnected(wpa_s, p2p_dev_addr);
 #endif /* CONFIG_P2P */
 
 	/* Notify listeners a station has been deauthorized */
@@ -583,13 +705,13 @@
 
 
 void wpas_notify_certification(struct wpa_supplicant *wpa_s, int depth,
-			       const char *subject, const char *cert_hash,
+			       const char *subject, const char *altsubject[],
+			       int num_altsubject, const char *cert_hash,
 			       const struct wpabuf *cert)
 {
 	wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_EAP_PEER_CERT
 		"depth=%d subject='%s'%s%s",
-		depth, subject,
-		cert_hash ? " hash=" : "",
+		depth, subject, cert_hash ? " hash=" : "",
 		cert_hash ? cert_hash : "");
 
 	if (cert) {
@@ -607,11 +729,20 @@
 		}
 	}
 
+	if (altsubject) {
+		int i;
+
+		for (i = 0; i < num_altsubject; i++)
+			wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_EAP_PEER_ALT
+				"depth=%d %s", depth, altsubject[i]);
+	}
+
 	/* notify the old DBus API */
 	wpa_supplicant_dbus_notify_certification(wpa_s, depth, subject,
 						 cert_hash, cert);
 	/* notify the new DBus API */
-	wpas_dbus_signal_certification(wpa_s, depth, subject, cert_hash, cert);
+	wpas_dbus_signal_certification(wpa_s, depth, subject, altsubject,
+				       num_altsubject, cert_hash, cert);
 }
 
 
@@ -648,3 +779,24 @@
 	wpa_drv_roaming(wpa_s, !ssid->bssid_set,
 			ssid->bssid_set ? ssid->bssid : NULL);
 }
+
+
+void wpas_notify_network_type_changed(struct wpa_supplicant *wpa_s,
+				      struct wpa_ssid *ssid)
+{
+#ifdef CONFIG_P2P
+	if (ssid->disabled == 2) {
+		/* Changed from normal network profile to persistent group */
+		ssid->disabled = 0;
+		wpas_dbus_unregister_network(wpa_s, ssid->id);
+		ssid->disabled = 2;
+		ssid->p2p_persistent_group = 1;
+		wpas_dbus_register_persistent_group(wpa_s, ssid);
+	} else {
+		/* Changed from persistent group to normal network profile */
+		wpas_dbus_unregister_persistent_group(wpa_s, ssid->id);
+		ssid->p2p_persistent_group = 0;
+		wpas_dbus_register_network(wpa_s, ssid);
+	}
+#endif /* CONFIG_P2P */
+}
diff --git a/wpa_supplicant/notify.h b/wpa_supplicant/notify.h
index 7feb530..1aeec47 100644
--- a/wpa_supplicant/notify.h
+++ b/wpa_supplicant/notify.h
@@ -45,6 +45,7 @@
 void wpas_notify_wps_event_fail(struct wpa_supplicant *wpa_s,
 				struct wps_event_fail *fail);
 void wpas_notify_wps_event_success(struct wpa_supplicant *wpa_s);
+void wpas_notify_wps_event_pbc_overlap(struct wpa_supplicant *wpa_s);
 void wpas_notify_network_added(struct wpa_supplicant *wpa_s,
 			       struct wpa_ssid *ssid);
 void wpas_notify_network_removed(struct wpa_supplicant *wpa_s,
@@ -84,6 +85,7 @@
 void wpas_notify_sta_authorized(struct wpa_supplicant *wpa_s,
 				const u8 *mac_addr, int authorized,
 				const u8 *p2p_dev_addr);
+void wpas_notify_p2p_find_stopped(struct wpa_supplicant *wpa_s);
 void wpas_notify_p2p_device_found(struct wpa_supplicant *wpa_s,
 				  const u8 *dev_addr, int new_device);
 void wpas_notify_p2p_device_lost(struct wpa_supplicant *wpa_s,
@@ -92,7 +94,7 @@
 				   const struct wpa_ssid *ssid,
 				   const char *role);
 void wpas_notify_p2p_go_neg_req(struct wpa_supplicant *wpa_s,
-				const u8 *src, u16 dev_passwd_id);
+				const u8 *src, u16 dev_passwd_id, u8 go_intent);
 void wpas_notify_p2p_go_neg_completed(struct wpa_supplicant *wpa_s,
 				      struct p2p_go_neg_results *res);
 void wpas_notify_p2p_invitation_result(struct wpa_supplicant *wpa_s,
@@ -121,7 +123,8 @@
 				struct wps_event_fail *fail);
 
 void wpas_notify_certification(struct wpa_supplicant *wpa_s, int depth,
-			       const char *subject, const char *cert_hash,
+			       const char *subject, const char *altsubject[],
+			       int num_altsubject, const char *cert_hash,
 			       const struct wpabuf *cert);
 void wpas_notify_preq(struct wpa_supplicant *wpa_s,
 		      const u8 *addr, const u8 *dst, const u8 *bssid,
@@ -130,5 +133,7 @@
 			    const char *parameter);
 void wpas_notify_network_bssid_set_changed(struct wpa_supplicant *wpa_s,
 					   struct wpa_ssid *ssid);
+void wpas_notify_network_type_changed(struct wpa_supplicant *wpa_s,
+				      struct wpa_ssid *ssid);
 
 #endif /* NOTIFY_H */
diff --git a/wpa_supplicant/offchannel.c b/wpa_supplicant/offchannel.c
index 17689c5..63af83a 100644
--- a/wpa_supplicant/offchannel.c
+++ b/wpa_supplicant/offchannel.c
@@ -31,8 +31,7 @@
 	 */
 	iface = wpa_s->global->ifaces;
 	while (iface) {
-		if (os_memcmp(wpa_s->pending_action_src,
-			      iface->own_addr, ETH_ALEN) == 0)
+		if (os_memcmp(src, iface->own_addr, ETH_ALEN) == 0)
 			break;
 		iface = iface->next;
 	}
@@ -85,6 +84,7 @@
 			   wpa_s->off_channel_freq,
 			   iface->assoc_freq);
 		if (without_roc && wpa_s->off_channel_freq == 0) {
+			unsigned int duration = 200;
 			/*
 			 * We may get here if wpas_send_action() found us to be
 			 * on the correct channel, but remain-on-channel cancel
@@ -92,9 +92,18 @@
 			 */
 			wpa_printf(MSG_DEBUG, "Off-channel: Schedule "
 				   "remain-on-channel to send Action frame");
+#ifdef CONFIG_TESTING_OPTIONS
+			if (wpa_s->extra_roc_dur) {
+				wpa_printf(MSG_DEBUG,
+					   "TESTING: Increase ROC duration %u -> %u",
+					   duration,
+					   duration + wpa_s->extra_roc_dur);
+				duration += wpa_s->extra_roc_dur;
+	}
+#endif /* CONFIG_TESTING_OPTIONS */
 			if (wpa_drv_remain_on_channel(
-				    wpa_s, wpa_s->pending_action_freq, 200) <
-			    0) {
+				    wpa_s, wpa_s->pending_action_freq,
+				    duration) < 0) {
 				wpa_printf(MSG_DEBUG, "Off-channel: Failed to "
 					   "request driver to remain on "
 					   "channel (%u MHz) for Action Frame "
@@ -190,11 +199,13 @@
 			data, data_len, result);
 	}
 
+#ifdef CONFIG_P2P
 	if (wpa_s->p2p_long_listen > 0) {
 		/* Continue the listen */
 		wpa_printf(MSG_DEBUG, "P2P: Continuing long Listen state");
 		wpas_p2p_listen_start(wpa_s, wpa_s->p2p_long_listen);
 	}
+#endif /* CONFIG_P2P */
 }
 
 
@@ -262,8 +273,7 @@
 		struct wpa_supplicant *iface;
 		int ret;
 
-		iface = wpas_get_tx_interface(wpa_s,
-					      wpa_s->pending_action_src);
+		iface = wpas_get_tx_interface(wpa_s, src);
 		wpa_s->action_tx_wait_time = wait_time;
 
 		ret = wpa_drv_send_action(
@@ -315,6 +325,13 @@
 		wait_time = wpa_s->max_remain_on_chan;
 	else if (wait_time == 0)
 		wait_time = 20;
+#ifdef CONFIG_TESTING_OPTIONS
+	if (wpa_s->extra_roc_dur) {
+		wpa_printf(MSG_DEBUG, "TESTING: Increase ROC duration %u -> %u",
+			   wait_time, wait_time + wpa_s->extra_roc_dur);
+		wait_time += wpa_s->extra_roc_dur;
+	}
+#endif /* CONFIG_TESTING_OPTIONS */
 	if (wpa_drv_remain_on_channel(wpa_s, freq, wait_time) < 0) {
 		wpa_printf(MSG_DEBUG, "Off-channel: Failed to request driver "
 			   "to remain on channel (%u MHz) for Action "
@@ -337,15 +354,18 @@
  */
 void offchannel_send_action_done(struct wpa_supplicant *wpa_s)
 {
-	wpa_printf(MSG_DEBUG, "Off-channel: Action frame sequence done "
-		   "notification");
+	wpa_printf(MSG_DEBUG,
+		   "Off-channel: Action frame sequence done notification: pending_action_tx=%p drv_offchan_tx=%d action_tx_wait_time=%d off_channel_freq=%d roc_waiting_drv_freq=%d",
+		   wpa_s->pending_action_tx,
+		   !!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_OFFCHANNEL_TX),
+		   wpa_s->action_tx_wait_time, wpa_s->off_channel_freq,
+		   wpa_s->roc_waiting_drv_freq);
 	wpabuf_free(wpa_s->pending_action_tx);
 	wpa_s->pending_action_tx = NULL;
 	if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_OFFCHANNEL_TX &&
 	    wpa_s->action_tx_wait_time)
 		wpa_drv_send_action_cancel_wait(wpa_s);
-
-	if (wpa_s->off_channel_freq || wpa_s->roc_waiting_drv_freq) {
+	else if (wpa_s->off_channel_freq || wpa_s->roc_waiting_drv_freq) {
 		wpa_drv_cancel_remain_on_channel(wpa_s);
 		wpa_s->off_channel_freq = 0;
 		wpa_s->roc_waiting_drv_freq = 0;
diff --git a/wpa_supplicant/p2p_supplicant.c b/wpa_supplicant/p2p_supplicant.c
index 640154c..b9ebd38 100644
--- a/wpa_supplicant/p2p_supplicant.c
+++ b/wpa_supplicant/p2p_supplicant.c
@@ -22,6 +22,7 @@
 #include "ap/ap_drv_ops.h"
 #include "ap/wps_hostapd.h"
 #include "ap/p2p_hostapd.h"
+#include "ap/dfs.h"
 #include "eapol_supp/eapol_supp_sm.h"
 #include "rsn_supp/wpa.h"
 #include "wpa_supplicant_i.h"
@@ -117,12 +118,16 @@
 static void wpas_p2p_group_formation_timeout(void *eloop_ctx,
 					     void *timeout_ctx);
 static void wpas_p2p_group_freq_conflict(void *eloop_ctx, void *timeout_ctx);
-static void wpas_p2p_fallback_to_go_neg(struct wpa_supplicant *wpa_s,
-					int group_added);
-static int wpas_p2p_stop_find_oper(struct wpa_supplicant *wpa_s);
+static int wpas_p2p_fallback_to_go_neg(struct wpa_supplicant *wpa_s,
+				       int group_added);
+static void wpas_p2p_stop_find_oper(struct wpa_supplicant *wpa_s);
 static void wpas_stop_listen(void *ctx);
 static void wpas_p2p_psk_failure_removal(void *eloop_ctx, void *timeout_ctx);
 static void wpas_p2p_group_deinit(struct wpa_supplicant *wpa_s);
+static int wpas_p2p_add_group_interface(struct wpa_supplicant *wpa_s,
+					enum wpa_driver_if_type type);
+static void wpas_p2p_group_formation_failed(struct wpa_supplicant *wpa_s,
+					    int already_deleted);
 
 
 /*
@@ -188,7 +193,11 @@
 {
 	if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
 		return;
-	if (wpa_s->parent->conf->p2p_ignore_shared_freq &&
+
+	/* Use the wpa_s used to control the P2P Device operation */
+	wpa_s = wpa_s->global->p2p_init_wpa_s;
+
+	if (wpa_s->conf->p2p_ignore_shared_freq &&
 	    freq > 0 && wpa_s->num_multichan_concurrent > 1 &&
 	    wpas_p2p_num_unused_channels(wpa_s) > 0) {
 		wpa_printf(MSG_DEBUG, "P2P: Ignore own channel preference %d MHz due to p2p_ignore_shared_freq=1 configuration",
@@ -269,9 +278,11 @@
 	work->ctx = NULL;
 	if (ret) {
 		radio_work_done(work);
+		p2p_notify_scan_trigger_status(wpa_s->global->p2p, ret);
 		return;
 	}
 
+	p2p_notify_scan_trigger_status(wpa_s->global->p2p, ret);
 	os_get_reltime(&wpa_s->scan_trigger_time);
 	wpa_s->scan_res_handler = wpas_p2p_scan_res_handler;
 	wpa_s->own_scan_requested = 1;
@@ -279,6 +290,22 @@
 }
 
 
+static int wpas_p2p_search_social_channel(struct wpa_supplicant *wpa_s,
+					  int freq)
+{
+	if (wpa_s->global->p2p_24ghz_social_channels &&
+	    (freq == 2412 || freq == 2437 || freq == 2462)) {
+		/*
+		 * Search all social channels regardless of whether these have
+		 * been disabled for P2P operating channel use to avoid missing
+		 * peers.
+		 */
+		return 1;
+	}
+	return p2p_supported_freq(wpa_s->global->p2p, freq);
+}
+
+
 static int wpas_p2p_scan(void *ctx, enum p2p_scan_type type, int freq,
 			 unsigned int num_req_dev_types,
 			 const u8 *req_dev_types, const u8 *dev_id, u16 pw_id)
@@ -348,8 +375,8 @@
 		if (params->freqs == NULL)
 			goto fail;
 		for (i = 0; i < ARRAY_SIZE(social_channels_freq); i++) {
-			if (p2p_supported_freq(wpa_s->global->p2p,
-					       social_channels_freq[i]))
+			if (wpas_p2p_search_social_channel(
+				    wpa_s, social_channels_freq[i]))
 				params->freqs[num_channels++] =
 					social_channels_freq[i];
 		}
@@ -357,14 +384,21 @@
 		break;
 	case P2P_SCAN_FULL:
 		break;
+	case P2P_SCAN_SPECIFIC:
+		params->freqs = os_calloc(2, sizeof(int));
+		if (params->freqs == NULL)
+			goto fail;
+		params->freqs[0] = freq;
+		params->freqs[1] = 0;
+		break;
 	case P2P_SCAN_SOCIAL_PLUS_ONE:
 		params->freqs = os_calloc(ARRAY_SIZE(social_channels_freq) + 2,
 					  sizeof(int));
 		if (params->freqs == NULL)
 			goto fail;
 		for (i = 0; i < ARRAY_SIZE(social_channels_freq); i++) {
-			if (p2p_supported_freq(wpa_s->global->p2p,
-					       social_channels_freq[i]))
+			if (wpas_p2p_search_social_channel(
+				    wpa_s, social_channels_freq[i]))
 				params->freqs[num_channels++] =
 					social_channels_freq[i];
 		}
@@ -426,6 +460,318 @@
 }
 
 
+static void run_wpas_p2p_disconnect(void *eloop_ctx, void *timeout_ctx)
+{
+	struct wpa_supplicant *wpa_s = eloop_ctx;
+	wpa_printf(MSG_DEBUG,
+		   "P2P: Complete previously requested removal of %s",
+		   wpa_s->ifname);
+	wpas_p2p_disconnect(wpa_s);
+}
+
+
+static int wpas_p2p_disconnect_safely(struct wpa_supplicant *wpa_s,
+				      struct wpa_supplicant *calling_wpa_s)
+{
+	if (calling_wpa_s == wpa_s && wpa_s &&
+	    wpa_s->p2p_group_interface != NOT_P2P_GROUP_INTERFACE) {
+		/*
+		 * The calling wpa_s instance is going to be removed. Do that
+		 * from an eloop callback to keep the instance available until
+		 * the caller has returned. This my be needed, e.g., to provide
+		 * control interface responses on the per-interface socket.
+		 */
+		if (eloop_register_timeout(0, 0, run_wpas_p2p_disconnect,
+					   wpa_s, NULL) < 0)
+			return -1;
+		return 0;
+	}
+
+	return wpas_p2p_disconnect(wpa_s);
+}
+
+
+/* Determine total number of clients in active groups where we are the GO */
+static unsigned int p2p_group_go_member_count(struct wpa_supplicant *wpa_s)
+{
+	unsigned int count = 0;
+	struct wpa_ssid *s;
+
+	for (wpa_s = wpa_s->global->ifaces; wpa_s; wpa_s = wpa_s->next) {
+		for (s = wpa_s->conf->ssid; s; s = s->next) {
+			wpa_printf(MSG_DEBUG,
+				   "P2P: sup:%p ssid:%p disabled:%d p2p:%d mode:%d",
+				   wpa_s, s, s->disabled, s->p2p_group,
+				   s->mode);
+			if (!s->disabled && s->p2p_group &&
+			    s->mode == WPAS_MODE_P2P_GO) {
+				count += p2p_get_group_num_members(
+					wpa_s->p2p_group);
+			}
+		}
+	}
+
+	return count;
+}
+
+
+/* Find an interface for a P2P group where we are the GO */
+static struct wpa_supplicant *
+wpas_p2p_get_go_group(struct wpa_supplicant *wpa_s)
+{
+	struct wpa_supplicant *save = NULL;
+	struct wpa_ssid *s;
+
+	if (!wpa_s)
+		return NULL;
+
+	for (wpa_s = wpa_s->global->ifaces; wpa_s; wpa_s = wpa_s->next) {
+		for (s = wpa_s->conf->ssid; s; s = s->next) {
+			if (s->disabled || !s->p2p_group ||
+			    s->mode != WPAS_MODE_P2P_GO)
+				continue;
+
+			/* Prefer a group with connected clients */
+			if (p2p_get_group_num_members(wpa_s->p2p_group))
+				return wpa_s;
+			save = wpa_s;
+		}
+	}
+
+	/* No group with connected clients, so pick the one without (if any) */
+	return save;
+}
+
+
+/* Find an active P2P group where we are the GO */
+static struct wpa_ssid * wpas_p2p_group_go_ssid(struct wpa_supplicant *wpa_s,
+						u8 *bssid)
+{
+	struct wpa_ssid *s, *empty = NULL;
+
+	if (!wpa_s)
+		return 0;
+
+	for (wpa_s = wpa_s->global->ifaces; wpa_s; wpa_s = wpa_s->next) {
+		for (s = wpa_s->conf->ssid; s; s = s->next) {
+			if (s->disabled || !s->p2p_group ||
+			    s->mode != WPAS_MODE_P2P_GO)
+				continue;
+
+			os_memcpy(bssid, wpa_s->own_addr, ETH_ALEN);
+			if (p2p_get_group_num_members(wpa_s->p2p_group))
+				return s;
+			empty = s;
+		}
+	}
+
+	return empty;
+}
+
+
+/* Find a persistent group where we are the GO */
+static struct wpa_ssid *
+wpas_p2p_get_persistent_go(struct wpa_supplicant *wpa_s)
+{
+	struct wpa_ssid *s;
+
+	for (s = wpa_s->conf->ssid; s; s = s->next) {
+		if (s->disabled == 2 && s->mode == WPAS_MODE_P2P_GO)
+			return s;
+	}
+
+	return NULL;
+}
+
+
+static u8 p2ps_group_capability(void *ctx, u8 incoming, u8 role)
+{
+	struct wpa_supplicant *wpa_s = ctx, *tmp_wpa_s;
+	struct wpa_ssid *s;
+	u8 conncap = P2PS_SETUP_NONE;
+	unsigned int owned_members = 0;
+	unsigned int owner = 0;
+	unsigned int client = 0;
+	struct wpa_supplicant *go_wpa_s;
+	struct wpa_ssid *persistent_go;
+	int p2p_no_group_iface;
+
+	wpa_printf(MSG_DEBUG, "P2P: Conncap - in:%d role:%d", incoming, role);
+
+	/*
+	 * For non-concurrent capable devices:
+	 * If persistent_go, then no new.
+	 * If GO, then no client.
+	 * If client, then no GO.
+	 */
+	go_wpa_s = wpas_p2p_get_go_group(wpa_s);
+	persistent_go = wpas_p2p_get_persistent_go(wpa_s);
+	p2p_no_group_iface = wpa_s->conf->p2p_no_group_iface;
+
+	wpa_printf(MSG_DEBUG, "P2P: GO(iface)=%p persistent(ssid)=%p",
+		   go_wpa_s, persistent_go);
+
+	for (tmp_wpa_s = wpa_s->global->ifaces; tmp_wpa_s;
+	     tmp_wpa_s = tmp_wpa_s->next) {
+		for (s = tmp_wpa_s->conf->ssid; s; s = s->next) {
+			wpa_printf(MSG_DEBUG,
+				   "P2P: sup:%p ssid:%p disabled:%d p2p:%d mode:%d",
+				   tmp_wpa_s, s, s->disabled,
+				   s->p2p_group, s->mode);
+			if (!s->disabled && s->p2p_group) {
+				if (s->mode == WPAS_MODE_P2P_GO) {
+					owned_members +=
+						p2p_get_group_num_members(
+							tmp_wpa_s->p2p_group);
+					owner++;
+				} else
+					client++;
+			}
+		}
+	}
+
+	/* If not concurrent, restrict our choices */
+	if (p2p_no_group_iface) {
+		wpa_printf(MSG_DEBUG, "P2P: p2p_no_group_iface");
+
+		if (client)
+			return P2PS_SETUP_NONE;
+
+		if (go_wpa_s) {
+			if (role == P2PS_SETUP_CLIENT ||
+			    incoming == P2PS_SETUP_GROUP_OWNER ||
+			    p2p_client_limit_reached(go_wpa_s->p2p_group))
+				return P2PS_SETUP_NONE;
+
+			return P2PS_SETUP_GROUP_OWNER;
+		}
+
+		if (persistent_go) {
+			if (role == P2PS_SETUP_NONE || role == P2PS_SETUP_NEW) {
+				if (!incoming)
+					return P2PS_SETUP_GROUP_OWNER |
+						P2PS_SETUP_CLIENT;
+				if (incoming == P2PS_SETUP_NEW) {
+					u8 r;
+
+					if (os_get_random(&r, sizeof(r)) < 0 ||
+					    (r & 1))
+						return P2PS_SETUP_CLIENT;
+					return P2PS_SETUP_GROUP_OWNER;
+				}
+			}
+		}
+	}
+
+	/* If a required role has been specified, handle it here */
+	if (role && role != P2PS_SETUP_NEW) {
+		switch (incoming) {
+		case P2PS_SETUP_NONE:
+		case P2PS_SETUP_NEW:
+		case P2PS_SETUP_GROUP_OWNER | P2PS_SETUP_CLIENT:
+		case P2PS_SETUP_GROUP_OWNER | P2PS_SETUP_NEW:
+			conncap = role;
+			goto grp_owner;
+
+		case P2PS_SETUP_GROUP_OWNER:
+			/*
+			 * Must be a complimentary role - cannot be a client to
+			 * more than one peer.
+			 */
+			if (incoming == role || client)
+				return P2PS_SETUP_NONE;
+
+			return P2PS_SETUP_CLIENT;
+
+		case P2PS_SETUP_CLIENT:
+			/* Must be a complimentary role */
+			if (incoming != role) {
+				conncap = P2PS_SETUP_GROUP_OWNER;
+				goto grp_owner;
+			}
+
+		default:
+			return P2PS_SETUP_NONE;
+		}
+	}
+
+	/*
+	 * For now, we only will support ownership of one group, and being a
+	 * client of one group. Therefore, if we have either an existing GO
+	 * group, or an existing client group, we will not do a new GO
+	 * negotiation, but rather try to re-use the existing groups.
+	 */
+	switch (incoming) {
+	case P2PS_SETUP_NONE:
+	case P2PS_SETUP_NEW:
+		if (client)
+			conncap = P2PS_SETUP_GROUP_OWNER;
+		else if (!owned_members)
+			conncap = P2PS_SETUP_NEW;
+		else if (incoming == P2PS_SETUP_NONE)
+			conncap = P2PS_SETUP_GROUP_OWNER | P2PS_SETUP_CLIENT;
+		else
+			conncap = P2PS_SETUP_CLIENT;
+		break;
+
+	case P2PS_SETUP_CLIENT:
+		conncap = P2PS_SETUP_GROUP_OWNER;
+		break;
+
+	case P2PS_SETUP_GROUP_OWNER:
+		if (!client)
+			conncap = P2PS_SETUP_CLIENT;
+		break;
+
+	case P2PS_SETUP_GROUP_OWNER | P2PS_SETUP_NEW:
+	case P2PS_SETUP_GROUP_OWNER | P2PS_SETUP_CLIENT:
+		if (client)
+			conncap = P2PS_SETUP_GROUP_OWNER;
+		else {
+			u8 r;
+
+			if (os_get_random(&r, sizeof(r)) < 0 ||
+			    (r & 1))
+				conncap = P2PS_SETUP_CLIENT;
+			else
+				conncap = P2PS_SETUP_GROUP_OWNER;
+		}
+		break;
+
+	default:
+		return P2PS_SETUP_NONE;
+	}
+
+grp_owner:
+	if ((conncap & P2PS_SETUP_GROUP_OWNER) ||
+	    (!incoming && (conncap & P2PS_SETUP_NEW))) {
+		if (go_wpa_s && p2p_client_limit_reached(go_wpa_s->p2p_group))
+			conncap &= ~P2PS_SETUP_GROUP_OWNER;
+		wpa_printf(MSG_DEBUG, "P2P: GOs:%d members:%d conncap:%d",
+			   owner, owned_members, conncap);
+
+		s = wpas_p2p_get_persistent_go(wpa_s);
+
+		if (!s && !owner && p2p_no_group_iface) {
+			p2p_set_intended_addr(wpa_s->global->p2p,
+					      wpa_s->own_addr);
+		} else if (!s && !owner) {
+			if (wpas_p2p_add_group_interface(wpa_s,
+							 WPA_IF_P2P_GO) < 0) {
+				wpa_printf(MSG_ERROR,
+					   "P2P: Failed to allocate a new interface for the group");
+				return P2PS_SETUP_NONE;
+			}
+			wpa_s->global->pending_group_iface_for_p2ps = 1;
+			p2p_set_intended_addr(wpa_s->global->p2p,
+					      wpa_s->pending_interface_addr);
+		}
+	}
+
+	return conncap;
+}
+
+
 static int wpas_p2p_group_delete(struct wpa_supplicant *wpa_s,
 				 enum p2p_group_removal_reason removal_reason)
 {
@@ -467,8 +813,17 @@
 	if (removal_reason != P2P_GROUP_REMOVAL_SILENT && ssid)
 		wpas_notify_p2p_group_removed(wpa_s, ssid, gtype);
 
-	if (os_strcmp(gtype, "client") == 0)
+	if (os_strcmp(gtype, "client") == 0) {
 		wpa_supplicant_deauthenticate(wpa_s, WLAN_REASON_DEAUTH_LEAVING);
+		if (eloop_is_timeout_registered(wpas_p2p_psk_failure_removal,
+						wpa_s, NULL)) {
+			wpa_printf(MSG_DEBUG,
+				   "P2P: PSK failure removal was scheduled, so use PSK failure as reason for group removal");
+			removal_reason = P2P_GROUP_REMOVAL_PSK_FAILURE;
+			eloop_cancel_timeout(wpas_p2p_psk_failure_removal,
+					     wpa_s, NULL);
+		}
+	}
 
 	if (wpa_s->cross_connect_in_use) {
 		wpa_s->cross_connect_in_use = 0;
@@ -536,6 +891,7 @@
 		global = wpa_s->global;
 		ifname = os_strdup(wpa_s->ifname);
 		type = wpas_p2p_if_type(wpa_s->p2p_group_interface);
+		eloop_cancel_timeout(run_wpas_p2p_disconnect, wpa_s, NULL);
 		wpa_supplicant_remove_iface(wpa_s->global, wpa_s, 0);
 		wpa_s = global->ifaces;
 		if (wpa_s && ifname)
@@ -553,6 +909,10 @@
 	os_free(wpa_s->go_params);
 	wpa_s->go_params = NULL;
 
+	os_free(wpa_s->p2p_group_common_freqs);
+	wpa_s->p2p_group_common_freqs = NULL;
+	wpa_s->p2p_group_common_freqs_num = 0;
+
 	wpa_s->waiting_presence_resp = 0;
 
 	wpa_printf(MSG_DEBUG, "P2P: Remove temporary group network");
@@ -755,13 +1115,14 @@
 	u8 *n;
 	size_t i;
 	int found = 0;
+	struct wpa_supplicant *p2p_wpa_s = wpa_s->global->p2p_init_wpa_s;
 
 	ssid = wpa_s->current_ssid;
 	if (ssid == NULL || ssid->mode != WPAS_MODE_P2P_GO ||
 	    !ssid->p2p_persistent_group)
 		return;
 
-	for (s = wpa_s->parent->conf->ssid; s; s = s->next) {
+	for (s = p2p_wpa_s->conf->ssid; s; s = s->next) {
 		if (s->disabled != 2 || s->mode != WPAS_MODE_P2P_GO)
 			continue;
 
@@ -774,7 +1135,7 @@
 		return;
 
 	for (i = 0; s->p2p_client_list && i < s->num_p2p_clients; i++) {
-		if (os_memcmp(s->p2p_client_list + i * ETH_ALEN, addr,
+		if (os_memcmp(s->p2p_client_list + i * 2 * ETH_ALEN, addr,
 			      ETH_ALEN) != 0)
 			continue;
 
@@ -782,36 +1143,46 @@
 			return; /* already the most recent entry */
 
 		/* move the entry to mark it most recent */
-		os_memmove(s->p2p_client_list + i * ETH_ALEN,
-			   s->p2p_client_list + (i + 1) * ETH_ALEN,
-			   (s->num_p2p_clients - i - 1) * ETH_ALEN);
+		os_memmove(s->p2p_client_list + i * 2 * ETH_ALEN,
+			   s->p2p_client_list + (i + 1) * 2 * ETH_ALEN,
+			   (s->num_p2p_clients - i - 1) * 2 * ETH_ALEN);
 		os_memcpy(s->p2p_client_list +
-			  (s->num_p2p_clients - 1) * ETH_ALEN, addr, ETH_ALEN);
+			  (s->num_p2p_clients - 1) * 2 * ETH_ALEN, addr,
+			  ETH_ALEN);
+		os_memset(s->p2p_client_list +
+			  (s->num_p2p_clients - 1) * 2 * ETH_ALEN + ETH_ALEN,
+			  0xff, ETH_ALEN);
 		found = 1;
 		break;
 	}
 
 	if (!found && s->num_p2p_clients < P2P_MAX_STORED_CLIENTS) {
 		n = os_realloc_array(s->p2p_client_list,
-				     s->num_p2p_clients + 1, ETH_ALEN);
+				     s->num_p2p_clients + 1, 2 * ETH_ALEN);
 		if (n == NULL)
 			return;
-		os_memcpy(n + s->num_p2p_clients * ETH_ALEN, addr, ETH_ALEN);
+		os_memcpy(n + s->num_p2p_clients * 2 * ETH_ALEN, addr,
+			  ETH_ALEN);
+		os_memset(n + s->num_p2p_clients * 2 * ETH_ALEN + ETH_ALEN,
+			  0xff, ETH_ALEN);
 		s->p2p_client_list = n;
 		s->num_p2p_clients++;
 	} else if (!found && s->p2p_client_list) {
 		/* Not enough room for an additional entry - drop the oldest
 		 * entry */
 		os_memmove(s->p2p_client_list,
-			   s->p2p_client_list + ETH_ALEN,
-			   (s->num_p2p_clients - 1) * ETH_ALEN);
+			   s->p2p_client_list + 2 * ETH_ALEN,
+			   (s->num_p2p_clients - 1) * 2 * ETH_ALEN);
 		os_memcpy(s->p2p_client_list +
-			  (s->num_p2p_clients - 1) * ETH_ALEN,
+			  (s->num_p2p_clients - 1) * 2 * ETH_ALEN,
 			  addr, ETH_ALEN);
+		os_memset(s->p2p_client_list +
+			  (s->num_p2p_clients - 1) * 2 * ETH_ALEN + ETH_ALEN,
+			  0xff, ETH_ALEN);
 	}
 
-	if (wpa_s->parent->conf->update_config &&
-	    wpa_config_write(wpa_s->parent->confname, wpa_s->parent->conf))
+	if (p2p_wpa_s->conf->update_config &&
+	    wpa_config_write(p2p_wpa_s->confname, p2p_wpa_s->conf))
 		wpa_printf(MSG_DEBUG, "P2P: Failed to update configuration");
 }
 
@@ -862,7 +1233,7 @@
 
 
 static void wpas_group_formation_completed(struct wpa_supplicant *wpa_s,
-					   int success)
+					   int success, int already_deleted)
 {
 	struct wpa_ssid *ssid;
 	int client;
@@ -882,10 +1253,13 @@
 		wpa_s->p2p_in_provisioning = 0;
 	}
 	wpa_s->p2p_in_invitation = 0;
+	wpa_s->group_formation_reported = 1;
 
 	if (!success) {
 		wpa_msg_global(wpa_s->parent, MSG_INFO,
 			       P2P_EVENT_GROUP_FORMATION_FAILURE);
+		if (already_deleted)
+			return;
 		wpas_p2p_group_delete(wpa_s,
 				      P2P_GROUP_REMOVAL_FORMATION_FAILED);
 		return;
@@ -1044,6 +1418,9 @@
 		wpa_s->pending_pd_before_join = 0;
 		wpa_dbg(wpa_s, MSG_DEBUG, "P2P: No ACK for PD Req "
 			"during p2p_connect-auto");
+		wpa_msg_global(wpa_s->parent, MSG_INFO,
+			       P2P_EVENT_FALLBACK_TO_GO_NEG
+			       "reason=no-ACK-to-PD-Req");
 		wpas_p2p_fallback_to_go_neg(wpa_s, 0);
 		return;
 	}
@@ -1169,6 +1546,7 @@
 static void wpas_start_wps_enrollee(struct wpa_supplicant *wpa_s,
 				    struct p2p_go_neg_results *res)
 {
+	wpa_s->group_formation_reported = 0;
 	wpa_printf(MSG_DEBUG, "P2P: Start WPS Enrollee for peer " MACSTR
 		   " dev_addr " MACSTR " wps_method %d",
 		   MAC2STR(res->peer_interface_addr),
@@ -1193,6 +1571,8 @@
 #endif /* CONFIG_WPS_NFC */
 	} else {
 		u16 dev_pw_id = DEV_PW_DEFAULT;
+		if (wpa_s->p2p_wps_method == WPS_P2PS)
+			dev_pw_id = DEV_PW_P2PS_DEFAULT;
 		if (wpa_s->p2p_wps_method == WPS_PIN_KEYPAD)
 			dev_pw_id = DEV_PW_REGISTRAR_SPECIFIED;
 		wpas_wps_start_pin(wpa_s, res->peer_interface_addr,
@@ -1239,6 +1619,50 @@
 }
 
 
+static void p2p_go_dump_common_freqs(struct wpa_supplicant *wpa_s)
+{
+	unsigned int i;
+
+	wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Common group frequencies (len=%u):",
+		wpa_s->p2p_group_common_freqs_num);
+
+	for (i = 0; i < wpa_s->p2p_group_common_freqs_num; i++)
+		wpa_dbg(wpa_s, MSG_DEBUG, "freq[%u]: %d",
+			i, wpa_s->p2p_group_common_freqs[i]);
+}
+
+
+static void p2p_go_save_group_common_freqs(struct wpa_supplicant *wpa_s,
+					   struct p2p_go_neg_results *params)
+{
+	unsigned int i, len = int_array_len(wpa_s->go_params->freq_list);
+
+	wpa_s->p2p_group_common_freqs_num = 0;
+	os_free(wpa_s->p2p_group_common_freqs);
+	wpa_s->p2p_group_common_freqs = os_calloc(len, sizeof(int));
+	if (!wpa_s->p2p_group_common_freqs)
+		return;
+
+	for (i = 0; i < len; i++) {
+		if (!wpa_s->go_params->freq_list[i])
+			break;
+		wpa_s->p2p_group_common_freqs[i] =
+			wpa_s->go_params->freq_list[i];
+	}
+	wpa_s->p2p_group_common_freqs_num = i;
+}
+
+
+static void p2p_config_write(struct wpa_supplicant *wpa_s)
+{
+#ifndef CONFIG_NO_CONFIG_WRITE
+	if (wpa_s->parent->conf->update_config &&
+	    wpa_config_write(wpa_s->parent->confname, wpa_s->parent->conf))
+		wpa_printf(MSG_DEBUG, "P2P: Failed to update configuration");
+#endif /* CONFIG_NO_CONFIG_WRITE */
+}
+
+
 static void p2p_go_configured(void *ctx, void *data)
 {
 	struct wpa_supplicant *wpa_s = ctx;
@@ -1246,6 +1670,9 @@
 	struct wpa_ssid *ssid;
 	int network_id = -1;
 
+	p2p_go_save_group_common_freqs(wpa_s, params);
+	p2p_go_dump_common_freqs(wpa_s);
+
 	ssid = wpa_s->current_ssid;
 	if (ssid && ssid->mode == WPAS_MODE_P2P_GO) {
 		wpa_printf(MSG_DEBUG, "P2P: Group setup without provisioning");
@@ -1257,6 +1684,17 @@
 				       params->passphrase,
 				       wpa_s->global->p2p_dev_addr,
 				       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;
+		}
 
 		os_get_reltime(&wpa_s->global->p2p_go_wait_client);
 		if (params->persistent_group) {
@@ -1340,6 +1778,8 @@
 	}
 
 	wpa_s->show_group_started = 0;
+	wpa_s->p2p_go_group_formation_completed = 0;
+	wpa_s->group_formation_reported = 0;
 
 	wpa_config_set_network_defaults(ssid);
 	ssid->temporary = 1;
@@ -1359,6 +1799,15 @@
 	ssid->key_mgmt = WPA_KEY_MGMT_PSK;
 	ssid->proto = WPA_PROTO_RSN;
 	ssid->pairwise_cipher = WPA_CIPHER_CCMP;
+	ssid->group_cipher = WPA_CIPHER_CCMP;
+	if (params->freq > 56160) {
+		/*
+		 * Enable GCMP instead of CCMP as pairwise_cipher and
+		 * group_cipher in 60 GHz.
+		 */
+		ssid->pairwise_cipher = WPA_CIPHER_GCMP;
+		ssid->group_cipher = WPA_CIPHER_GCMP;
+	}
 	if (os_strlen(params->passphrase) > 0) {
 		ssid->passphrase = os_strdup(params->passphrase);
 		if (ssid->passphrase == NULL) {
@@ -1420,13 +1869,16 @@
 	d->ignore_old_scan_res = s->ignore_old_scan_res;
 	d->beacon_int = s->beacon_int;
 	d->dtim_period = s->dtim_period;
+	d->p2p_go_ctwindow = s->p2p_go_ctwindow;
 	d->disassoc_low_ack = s->disassoc_low_ack;
 	d->disable_scan_offload = s->disable_scan_offload;
+	d->passive_scan = s->passive_scan;
 
 	if (s->wps_nfc_dh_privkey && s->wps_nfc_dh_pubkey) {
 		d->wps_nfc_dh_privkey = wpabuf_dup(s->wps_nfc_dh_privkey);
 		d->wps_nfc_dh_pubkey = wpabuf_dup(s->wps_nfc_dh_pubkey);
 	}
+	d->p2p_cli_probe = s->p2p_cli_probe;
 }
 
 
@@ -1443,8 +1895,12 @@
 	os_snprintf(ifname, len, "p2p-%s-%d", ifname_ptr, wpa_s->p2p_group_idx);
 	if (os_strlen(ifname) >= IFNAMSIZ &&
 	    os_strlen(wpa_s->ifname) < IFNAMSIZ) {
+		int res;
+
 		/* Try to avoid going over the IFNAMSIZ length limit */
-		os_snprintf(ifname, len, "p2p-%d", wpa_s->p2p_group_idx);
+		res = os_snprintf(ifname, len, "p2p-%d", wpa_s->p2p_group_idx);
+		if (os_snprintf_error(len, res) && len)
+			ifname[len - 1] = '\0';
 	}
 }
 
@@ -1510,6 +1966,7 @@
 			  wpa_s->pending_interface_name);
 	os_memset(wpa_s->pending_interface_addr, 0, ETH_ALEN);
 	wpa_s->pending_interface_name[0] = '\0';
+	wpa_s->global->pending_group_iface_for_p2ps = 0;
 }
 
 
@@ -1544,17 +2001,17 @@
 	else
 		iface.ctrl_interface = wpa_s->conf->ctrl_interface;
 	iface.driver_param = wpa_s->conf->driver_param;
-	group_wpa_s = wpa_supplicant_add_iface(wpa_s->global, &iface);
+	group_wpa_s = wpa_supplicant_add_iface(wpa_s->global, &iface, wpa_s);
 	if (group_wpa_s == NULL) {
 		wpa_printf(MSG_ERROR, "P2P: Failed to create new "
 			   "wpa_supplicant interface");
 		return NULL;
 	}
 	wpa_s->pending_interface_name[0] = '\0';
-	group_wpa_s->parent = wpa_s;
 	group_wpa_s->p2p_group_interface = go ? P2P_GROUP_INTERFACE_GO :
 		P2P_GROUP_INTERFACE_CLIENT;
 	wpa_s->global->p2p_group_formation = group_wpa_s;
+	wpa_s->global->pending_group_iface_for_p2ps = 0;
 
 	wpas_p2p_clone_config(group_wpa_s, wpa_s);
 
@@ -1567,17 +2024,18 @@
 {
 	struct wpa_supplicant *wpa_s = eloop_ctx;
 	wpa_printf(MSG_DEBUG, "P2P: Group Formation timed out");
-	wpas_p2p_group_formation_failed(wpa_s);
+	wpas_p2p_group_formation_failed(wpa_s, 0);
 }
 
 
-void wpas_p2p_group_formation_failed(struct wpa_supplicant *wpa_s)
+static void wpas_p2p_group_formation_failed(struct wpa_supplicant *wpa_s,
+					    int already_deleted)
 {
 	eloop_cancel_timeout(wpas_p2p_group_formation_timeout,
 			     wpa_s->parent, NULL);
 	if (wpa_s->global->p2p)
 		p2p_group_formation_failed(wpa_s->global->p2p);
-	wpas_group_formation_completed(wpa_s, 0);
+	wpas_group_formation_completed(wpa_s, 0, already_deleted);
 }
 
 
@@ -1658,7 +2116,7 @@
 			wpas_p2p_remove_pending_group_interface(wpa_s);
 			eloop_cancel_timeout(wpas_p2p_long_listen_timeout,
 					     wpa_s, NULL);
-			wpas_p2p_group_formation_failed(wpa_s);
+			wpas_p2p_group_formation_failed(wpa_s, 1);
 			return;
 		}
 		if (group_wpa_s != wpa_s) {
@@ -1694,13 +2152,15 @@
 }
 
 
-static void wpas_go_neg_req_rx(void *ctx, const u8 *src, u16 dev_passwd_id)
+static void wpas_go_neg_req_rx(void *ctx, const u8 *src, u16 dev_passwd_id,
+			       u8 go_intent)
 {
 	struct wpa_supplicant *wpa_s = ctx;
 	wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_GO_NEG_REQUEST MACSTR
-		       " dev_passwd_id=%u", MAC2STR(src), dev_passwd_id);
+		       " dev_passwd_id=%u go_intent=%u", MAC2STR(src),
+		       dev_passwd_id, go_intent);
 
-	wpas_notify_p2p_go_neg_req(wpa_s, src, dev_passwd_id);
+	wpas_notify_p2p_go_neg_req(wpa_s, src, dev_passwd_id, go_intent);
 }
 
 
@@ -1718,10 +2178,56 @@
 						    WFD_SUBELEM_DEVICE_INFO);
 #endif /* CONFIG_WIFI_DISPLAY */
 
+	if (info->p2ps_instance) {
+		char str[256];
+		const u8 *buf = wpabuf_head(info->p2ps_instance);
+		size_t len = wpabuf_len(info->p2ps_instance);
+
+		while (len) {
+			u32 id;
+			u16 methods;
+			u8 str_len;
+
+			if (len < 4 + 2 + 1)
+				break;
+			id = WPA_GET_LE32(buf);
+			buf += sizeof(u32);
+			methods = WPA_GET_BE16(buf);
+			buf += sizeof(u16);
+			str_len = *buf++;
+			if (str_len > len - 4 - 2 - 1)
+				break;
+			os_memcpy(str, buf, str_len);
+			str[str_len] = '\0';
+			buf += str_len;
+			len -= str_len + sizeof(u32) + sizeof(u16) + sizeof(u8);
+
+			wpa_msg_global(wpa_s, MSG_INFO,
+				       P2P_EVENT_DEVICE_FOUND MACSTR
+				       " p2p_dev_addr=" MACSTR
+				       " pri_dev_type=%s name='%s'"
+				       " config_methods=0x%x"
+				       " dev_capab=0x%x"
+				       " group_capab=0x%x"
+				       " adv_id=%x asp_svc=%s%s",
+				       MAC2STR(addr),
+				       MAC2STR(info->p2p_device_addr),
+				       wps_dev_type_bin2str(
+					       info->pri_dev_type,
+					       devtype, sizeof(devtype)),
+				       info->device_name, methods,
+				       info->dev_capab, info->group_capab,
+				       id, str,
+				       info->vendor_elems ?
+				       " vendor_elems=1" : "");
+		}
+		goto done;
+	}
+
 	wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_DEVICE_FOUND MACSTR
 		       " p2p_dev_addr=" MACSTR
 		       " pri_dev_type=%s name='%s' config_methods=0x%x "
-		       "dev_capab=0x%x group_capab=0x%x%s%s%s",
+		       "dev_capab=0x%x group_capab=0x%x%s%s%s new=%d",
 		       MAC2STR(addr), MAC2STR(info->p2p_device_addr),
 		       wps_dev_type_bin2str(info->pri_dev_type, devtype,
 					    sizeof(devtype)),
@@ -1729,8 +2235,10 @@
 		       info->dev_capab, info->group_capab,
 		       wfd_dev_info_hex ? " wfd_dev_info=0x" : "",
 		       wfd_dev_info_hex ? wfd_dev_info_hex : "",
-		       info->vendor_elems ? " vendor_elems=1" : "");
+		       info->vendor_elems ? " vendor_elems=1" : "",
+		       new_device);
 
+done:
 	os_free(wfd_dev_info_hex);
 #endif /* CONFIG_NO_STDOUT_DEBUG */
 
@@ -1753,6 +2261,7 @@
 {
 	struct wpa_supplicant *wpa_s = ctx;
 	wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_FIND_STOPPED);
+	wpas_notify_p2p_find_stopped(wpa_s);
 }
 
 
@@ -1790,6 +2299,7 @@
 {
 	struct wpa_supplicant *wpa_s = work->wpa_s;
 	struct wpas_p2p_listen_work *lwork = work->ctx;
+	unsigned int duration;
 
 	if (deinit) {
 		if (work->started) {
@@ -1814,8 +2324,16 @@
 	wpa_s->pending_listen_freq = lwork->freq;
 	wpa_s->pending_listen_duration = lwork->duration;
 
-	if (wpa_drv_remain_on_channel(wpa_s, lwork->freq, lwork->duration) < 0)
-	{
+	duration = lwork->duration;
+#ifdef CONFIG_TESTING_OPTIONS
+	if (wpa_s->extra_roc_dur) {
+		wpa_printf(MSG_DEBUG, "TESTING: Increase ROC duration %u -> %u",
+			   duration, duration + wpa_s->extra_roc_dur);
+		duration += wpa_s->extra_roc_dur;
+	}
+#endif /* CONFIG_TESTING_OPTIONS */
+
+	if (wpa_drv_remain_on_channel(wpa_s, lwork->freq, duration) < 0) {
 		wpa_printf(MSG_DEBUG, "P2P: Failed to request the driver "
 			   "to remain on channel (%u MHz) for Listen "
 			   "state", lwork->freq);
@@ -1872,926 +2390,24 @@
 		wpa_s->roc_waiting_drv_freq = 0;
 	}
 	wpa_drv_set_ap_wps_ie(wpa_s, NULL, NULL, NULL);
-	wpa_drv_probe_req_report(wpa_s, 0);
+
+	/*
+	 * Don't cancel Probe Request RX reporting for a connected P2P Client
+	 * handling Probe Request frames.
+	 */
+	if (!wpa_s->p2p_cli_probe)
+		wpa_drv_probe_req_report(wpa_s, 0);
+
 	wpas_p2p_listen_work_done(wpa_s);
 }
 
 
-static int wpas_send_probe_resp(void *ctx, const struct wpabuf *buf)
+static int wpas_send_probe_resp(void *ctx, const struct wpabuf *buf,
+				unsigned int freq)
 {
 	struct wpa_supplicant *wpa_s = ctx;
-	return wpa_drv_send_mlme(wpa_s, wpabuf_head(buf), wpabuf_len(buf), 1);
-}
-
-
-/*
- * DNS Header section is used only to calculate compression pointers, so the
- * contents of this data does not matter, but the length needs to be reserved
- * in the virtual packet.
- */
-#define DNS_HEADER_LEN 12
-
-/*
- * 27-octet in-memory packet from P2P specification containing two implied
- * queries for _tcp.lcoal. PTR IN and _udp.local. PTR IN
- */
-#define P2P_SD_IN_MEMORY_LEN 27
-
-static int p2p_sd_dns_uncompress_label(char **upos, char *uend, u8 *start,
-				       u8 **spos, const u8 *end)
-{
-	while (*spos < end) {
-		u8 val = ((*spos)[0] & 0xc0) >> 6;
-		int len;
-
-		if (val == 1 || val == 2) {
-			/* These are reserved values in RFC 1035 */
-			wpa_printf(MSG_DEBUG, "P2P: Invalid domain name "
-				   "sequence starting with 0x%x", val);
-			return -1;
-		}
-
-		if (val == 3) {
-			u16 offset;
-			u8 *spos_tmp;
-
-			/* Offset */
-			if (*spos + 2 > end) {
-				wpa_printf(MSG_DEBUG, "P2P: No room for full "
-					   "DNS offset field");
-				return -1;
-			}
-
-			offset = (((*spos)[0] & 0x3f) << 8) | (*spos)[1];
-			if (offset >= *spos - start) {
-				wpa_printf(MSG_DEBUG, "P2P: Invalid DNS "
-					   "pointer offset %u", offset);
-				return -1;
-			}
-
-			(*spos) += 2;
-			spos_tmp = start + offset;
-			return p2p_sd_dns_uncompress_label(upos, uend, start,
-							   &spos_tmp,
-							   *spos - 2);
-		}
-
-		/* Label */
-		len = (*spos)[0] & 0x3f;
-		if (len == 0)
-			return 0;
-
-		(*spos)++;
-		if (*spos + len > end) {
-			wpa_printf(MSG_DEBUG, "P2P: Invalid domain name "
-				   "sequence - no room for label with length "
-				   "%u", len);
-			return -1;
-		}
-
-		if (*upos + len + 2 > uend)
-			return -2;
-
-		os_memcpy(*upos, *spos, len);
-		*spos += len;
-		*upos += len;
-		(*upos)[0] = '.';
-		(*upos)++;
-		(*upos)[0] = '\0';
-	}
-
-	return 0;
-}
-
-
-/* Uncompress domain names per RFC 1035 using the P2P SD in-memory packet.
- * Returns -1 on parsing error (invalid input sequence), -2 if output buffer is
- * not large enough */
-static int p2p_sd_dns_uncompress(char *buf, size_t buf_len, const u8 *msg,
-				 size_t msg_len, size_t offset)
-{
-	/* 27-octet in-memory packet from P2P specification */
-	const char *prefix = "\x04_tcp\x05local\x00\x00\x0C\x00\x01"
-		"\x04_udp\xC0\x11\x00\x0C\x00\x01";
-	u8 *tmp, *end, *spos;
-	char *upos, *uend;
-	int ret = 0;
-
-	if (buf_len < 2)
-		return -1;
-	if (offset > msg_len)
-		return -1;
-
-	tmp = os_malloc(DNS_HEADER_LEN + P2P_SD_IN_MEMORY_LEN + msg_len);
-	if (tmp == NULL)
-		return -1;
-	spos = tmp + DNS_HEADER_LEN + P2P_SD_IN_MEMORY_LEN;
-	end = spos + msg_len;
-	spos += offset;
-
-	os_memset(tmp, 0, DNS_HEADER_LEN);
-	os_memcpy(tmp + DNS_HEADER_LEN, prefix, P2P_SD_IN_MEMORY_LEN);
-	os_memcpy(tmp + DNS_HEADER_LEN + P2P_SD_IN_MEMORY_LEN, msg, msg_len);
-
-	upos = buf;
-	uend = buf + buf_len;
-
-	ret = p2p_sd_dns_uncompress_label(&upos, uend, tmp, &spos, end);
-	if (ret) {
-		os_free(tmp);
-		return ret;
-	}
-
-	if (upos == buf) {
-		upos[0] = '.';
-		upos[1] = '\0';
-	} else if (upos[-1] == '.')
-		upos[-1] = '\0';
-
-	os_free(tmp);
-	return 0;
-}
-
-
-static struct p2p_srv_bonjour *
-wpas_p2p_service_get_bonjour(struct wpa_supplicant *wpa_s,
-			     const struct wpabuf *query)
-{
-	struct p2p_srv_bonjour *bsrv;
-	size_t len;
-
-	len = wpabuf_len(query);
-	dl_list_for_each(bsrv, &wpa_s->global->p2p_srv_bonjour,
-			 struct p2p_srv_bonjour, list) {
-		if (len == wpabuf_len(bsrv->query) &&
-		    os_memcmp(wpabuf_head(query), wpabuf_head(bsrv->query),
-			      len) == 0)
-			return bsrv;
-	}
-	return NULL;
-}
-
-
-static struct p2p_srv_upnp *
-wpas_p2p_service_get_upnp(struct wpa_supplicant *wpa_s, u8 version,
-			  const char *service)
-{
-	struct p2p_srv_upnp *usrv;
-
-	dl_list_for_each(usrv, &wpa_s->global->p2p_srv_upnp,
-			 struct p2p_srv_upnp, list) {
-		if (version == usrv->version &&
-		    os_strcmp(service, usrv->service) == 0)
-			return usrv;
-	}
-	return NULL;
-}
-
-
-static void wpas_sd_add_proto_not_avail(struct wpabuf *resp, u8 srv_proto,
-					u8 srv_trans_id)
-{
-	u8 *len_pos;
-
-	if (wpabuf_tailroom(resp) < 5)
-		return;
-
-	/* Length (to be filled) */
-	len_pos = wpabuf_put(resp, 2);
-	wpabuf_put_u8(resp, srv_proto);
-	wpabuf_put_u8(resp, srv_trans_id);
-	/* Status Code */
-	wpabuf_put_u8(resp, P2P_SD_PROTO_NOT_AVAILABLE);
-	/* Response Data: empty */
-	WPA_PUT_LE16(len_pos, (u8 *) wpabuf_put(resp, 0) - len_pos - 2);
-}
-
-
-static void wpas_sd_all_bonjour(struct wpa_supplicant *wpa_s,
-				struct wpabuf *resp, u8 srv_trans_id)
-{
-	struct p2p_srv_bonjour *bsrv;
-	u8 *len_pos;
-
-	wpa_printf(MSG_DEBUG, "P2P: SD Request for all Bonjour services");
-
-	if (dl_list_empty(&wpa_s->global->p2p_srv_bonjour)) {
-		wpa_printf(MSG_DEBUG, "P2P: Bonjour protocol not available");
-		return;
-	}
-
-	dl_list_for_each(bsrv, &wpa_s->global->p2p_srv_bonjour,
-			 struct p2p_srv_bonjour, list) {
-		if (wpabuf_tailroom(resp) <
-		    5 + wpabuf_len(bsrv->query) + wpabuf_len(bsrv->resp))
-			return;
-		/* Length (to be filled) */
-		len_pos = wpabuf_put(resp, 2);
-		wpabuf_put_u8(resp, P2P_SERV_BONJOUR);
-		wpabuf_put_u8(resp, srv_trans_id);
-		/* Status Code */
-		wpabuf_put_u8(resp, P2P_SD_SUCCESS);
-		wpa_hexdump_ascii(MSG_DEBUG, "P2P: Matching Bonjour service",
-				  wpabuf_head(bsrv->resp),
-				  wpabuf_len(bsrv->resp));
-		/* Response Data */
-		wpabuf_put_buf(resp, bsrv->query); /* Key */
-		wpabuf_put_buf(resp, bsrv->resp); /* Value */
-		WPA_PUT_LE16(len_pos, (u8 *) wpabuf_put(resp, 0) - len_pos -
-			     2);
-	}
-}
-
-
-static int match_bonjour_query(struct p2p_srv_bonjour *bsrv, const u8 *query,
-			       size_t query_len)
-{
-	char str_rx[256], str_srv[256];
-
-	if (query_len < 3 || wpabuf_len(bsrv->query) < 3)
-		return 0; /* Too short to include DNS Type and Version */
-	if (os_memcmp(query + query_len - 3,
-		      wpabuf_head_u8(bsrv->query) + wpabuf_len(bsrv->query) - 3,
-		      3) != 0)
-		return 0; /* Mismatch in DNS Type or Version */
-	if (query_len == wpabuf_len(bsrv->query) &&
-	    os_memcmp(query, wpabuf_head(bsrv->query), query_len - 3) == 0)
-		return 1; /* Binary match */
-
-	if (p2p_sd_dns_uncompress(str_rx, sizeof(str_rx), query, query_len - 3,
-				  0))
-		return 0; /* Failed to uncompress query */
-	if (p2p_sd_dns_uncompress(str_srv, sizeof(str_srv),
-				  wpabuf_head(bsrv->query),
-				  wpabuf_len(bsrv->query) - 3, 0))
-		return 0; /* Failed to uncompress service */
-
-	return os_strcmp(str_rx, str_srv) == 0;
-}
-
-
-static void wpas_sd_req_bonjour(struct wpa_supplicant *wpa_s,
-				struct wpabuf *resp, u8 srv_trans_id,
-				const u8 *query, size_t query_len)
-{
-	struct p2p_srv_bonjour *bsrv;
-	u8 *len_pos;
-	int matches = 0;
-
-	wpa_hexdump_ascii(MSG_DEBUG, "P2P: SD Request for Bonjour",
-			  query, query_len);
-	if (dl_list_empty(&wpa_s->global->p2p_srv_bonjour)) {
-		wpa_printf(MSG_DEBUG, "P2P: Bonjour protocol not available");
-		wpas_sd_add_proto_not_avail(resp, P2P_SERV_BONJOUR,
-					    srv_trans_id);
-		return;
-	}
-
-	if (query_len == 0) {
-		wpas_sd_all_bonjour(wpa_s, resp, srv_trans_id);
-		return;
-	}
-
-	dl_list_for_each(bsrv, &wpa_s->global->p2p_srv_bonjour,
-			 struct p2p_srv_bonjour, list) {
-		if (!match_bonjour_query(bsrv, query, query_len))
-			continue;
-
-		if (wpabuf_tailroom(resp) <
-		    5 + query_len + wpabuf_len(bsrv->resp))
-			return;
-
-		matches++;
-
-		/* Length (to be filled) */
-		len_pos = wpabuf_put(resp, 2);
-		wpabuf_put_u8(resp, P2P_SERV_BONJOUR);
-		wpabuf_put_u8(resp, srv_trans_id);
-
-		/* Status Code */
-		wpabuf_put_u8(resp, P2P_SD_SUCCESS);
-		wpa_hexdump_ascii(MSG_DEBUG, "P2P: Matching Bonjour service",
-				  wpabuf_head(bsrv->resp),
-				  wpabuf_len(bsrv->resp));
-
-		/* Response Data */
-		wpabuf_put_data(resp, query, query_len); /* Key */
-		wpabuf_put_buf(resp, bsrv->resp); /* Value */
-
-		WPA_PUT_LE16(len_pos, (u8 *) wpabuf_put(resp, 0) - len_pos - 2);
-	}
-
-	if (matches == 0) {
-		wpa_printf(MSG_DEBUG, "P2P: Requested Bonjour service not "
-			   "available");
-		if (wpabuf_tailroom(resp) < 5)
-			return;
-
-		/* Length (to be filled) */
-		len_pos = wpabuf_put(resp, 2);
-		wpabuf_put_u8(resp, P2P_SERV_BONJOUR);
-		wpabuf_put_u8(resp, srv_trans_id);
-
-		/* Status Code */
-		wpabuf_put_u8(resp, P2P_SD_REQUESTED_INFO_NOT_AVAILABLE);
-		/* Response Data: empty */
-		WPA_PUT_LE16(len_pos, (u8 *) wpabuf_put(resp, 0) - len_pos -
-			     2);
-	}
-}
-
-
-static void wpas_sd_all_upnp(struct wpa_supplicant *wpa_s,
-			     struct wpabuf *resp, u8 srv_trans_id)
-{
-	struct p2p_srv_upnp *usrv;
-	u8 *len_pos;
-
-	wpa_printf(MSG_DEBUG, "P2P: SD Request for all UPnP services");
-
-	if (dl_list_empty(&wpa_s->global->p2p_srv_upnp)) {
-		wpa_printf(MSG_DEBUG, "P2P: UPnP protocol not available");
-		return;
-	}
-
-	dl_list_for_each(usrv, &wpa_s->global->p2p_srv_upnp,
-			 struct p2p_srv_upnp, list) {
-		if (wpabuf_tailroom(resp) < 5 + 1 + os_strlen(usrv->service))
-			return;
-
-		/* Length (to be filled) */
-		len_pos = wpabuf_put(resp, 2);
-		wpabuf_put_u8(resp, P2P_SERV_UPNP);
-		wpabuf_put_u8(resp, srv_trans_id);
-
-		/* Status Code */
-		wpabuf_put_u8(resp, P2P_SD_SUCCESS);
-		/* Response Data */
-		wpabuf_put_u8(resp, usrv->version);
-		wpa_printf(MSG_DEBUG, "P2P: Matching UPnP Service: %s",
-			   usrv->service);
-		wpabuf_put_str(resp, usrv->service);
-		WPA_PUT_LE16(len_pos, (u8 *) wpabuf_put(resp, 0) - len_pos -
-			     2);
-	}
-}
-
-
-static void wpas_sd_req_upnp(struct wpa_supplicant *wpa_s,
-			     struct wpabuf *resp, u8 srv_trans_id,
-			     const u8 *query, size_t query_len)
-{
-	struct p2p_srv_upnp *usrv;
-	u8 *len_pos;
-	u8 version;
-	char *str;
-	int count = 0;
-
-	wpa_hexdump_ascii(MSG_DEBUG, "P2P: SD Request for UPnP",
-			  query, query_len);
-
-	if (dl_list_empty(&wpa_s->global->p2p_srv_upnp)) {
-		wpa_printf(MSG_DEBUG, "P2P: UPnP protocol not available");
-		wpas_sd_add_proto_not_avail(resp, P2P_SERV_UPNP,
-					    srv_trans_id);
-		return;
-	}
-
-	if (query_len == 0) {
-		wpas_sd_all_upnp(wpa_s, resp, srv_trans_id);
-		return;
-	}
-
-	if (wpabuf_tailroom(resp) < 5)
-		return;
-
-	/* Length (to be filled) */
-	len_pos = wpabuf_put(resp, 2);
-	wpabuf_put_u8(resp, P2P_SERV_UPNP);
-	wpabuf_put_u8(resp, srv_trans_id);
-
-	version = query[0];
-	str = os_malloc(query_len);
-	if (str == NULL)
-		return;
-	os_memcpy(str, query + 1, query_len - 1);
-	str[query_len - 1] = '\0';
-
-	dl_list_for_each(usrv, &wpa_s->global->p2p_srv_upnp,
-			 struct p2p_srv_upnp, list) {
-		if (version != usrv->version)
-			continue;
-
-		if (os_strcmp(str, "ssdp:all") != 0 &&
-		    os_strstr(usrv->service, str) == NULL)
-			continue;
-
-		if (wpabuf_tailroom(resp) < 2)
-			break;
-		if (count == 0) {
-			/* Status Code */
-			wpabuf_put_u8(resp, P2P_SD_SUCCESS);
-			/* Response Data */
-			wpabuf_put_u8(resp, version);
-		} else
-			wpabuf_put_u8(resp, ',');
-
-		count++;
-
-		wpa_printf(MSG_DEBUG, "P2P: Matching UPnP Service: %s",
-			   usrv->service);
-		if (wpabuf_tailroom(resp) < os_strlen(usrv->service))
-			break;
-		wpabuf_put_str(resp, usrv->service);
-	}
-	os_free(str);
-
-	if (count == 0) {
-		wpa_printf(MSG_DEBUG, "P2P: Requested UPnP service not "
-			   "available");
-		/* Status Code */
-		wpabuf_put_u8(resp, P2P_SD_REQUESTED_INFO_NOT_AVAILABLE);
-		/* Response Data: empty */
-	}
-
-	WPA_PUT_LE16(len_pos, (u8 *) wpabuf_put(resp, 0) - len_pos - 2);
-}
-
-
-#ifdef CONFIG_WIFI_DISPLAY
-static void wpas_sd_req_wfd(struct wpa_supplicant *wpa_s,
-			    struct wpabuf *resp, u8 srv_trans_id,
-			    const u8 *query, size_t query_len)
-{
-	const u8 *pos;
-	u8 role;
-	u8 *len_pos;
-
-	wpa_hexdump(MSG_DEBUG, "P2P: SD Request for WFD", query, query_len);
-
-	if (!wpa_s->global->wifi_display) {
-		wpa_printf(MSG_DEBUG, "P2P: WFD protocol not available");
-		wpas_sd_add_proto_not_avail(resp, P2P_SERV_WIFI_DISPLAY,
-					    srv_trans_id);
-		return;
-	}
-
-	if (query_len < 1) {
-		wpa_printf(MSG_DEBUG, "P2P: Missing WFD Requested Device "
-			   "Role");
-		return;
-	}
-
-	if (wpabuf_tailroom(resp) < 5)
-		return;
-
-	pos = query;
-	role = *pos++;
-	wpa_printf(MSG_DEBUG, "P2P: WSD for device role 0x%x", role);
-
-	/* TODO: role specific handling */
-
-	/* Length (to be filled) */
-	len_pos = wpabuf_put(resp, 2);
-	wpabuf_put_u8(resp, P2P_SERV_WIFI_DISPLAY);
-	wpabuf_put_u8(resp, srv_trans_id);
-	wpabuf_put_u8(resp, P2P_SD_SUCCESS); /* Status Code */
-
-	while (pos < query + query_len) {
-		if (*pos < MAX_WFD_SUBELEMS &&
-		    wpa_s->global->wfd_subelem[*pos] &&
-		    wpabuf_tailroom(resp) >=
-		    wpabuf_len(wpa_s->global->wfd_subelem[*pos])) {
-			wpa_printf(MSG_DEBUG, "P2P: Add WSD response "
-				   "subelement %u", *pos);
-			wpabuf_put_buf(resp, wpa_s->global->wfd_subelem[*pos]);
-		}
-		pos++;
-	}
-
-	WPA_PUT_LE16(len_pos, (u8 *) wpabuf_put(resp, 0) - len_pos - 2);
-}
-#endif /* CONFIG_WIFI_DISPLAY */
-
-
-static void wpas_sd_request(void *ctx, int freq, const u8 *sa, u8 dialog_token,
-			    u16 update_indic, const u8 *tlvs, size_t tlvs_len)
-{
-	struct wpa_supplicant *wpa_s = ctx;
-	const u8 *pos = tlvs;
-	const u8 *end = tlvs + tlvs_len;
-	const u8 *tlv_end;
-	u16 slen;
-	struct wpabuf *resp;
-	u8 srv_proto, srv_trans_id;
-	size_t buf_len;
-	char *buf;
-
-	wpa_hexdump(MSG_MSGDUMP, "P2P: Service Discovery Request TLVs",
-		    tlvs, tlvs_len);
-	buf_len = 2 * tlvs_len + 1;
-	buf = os_malloc(buf_len);
-	if (buf) {
-		wpa_snprintf_hex(buf, buf_len, tlvs, tlvs_len);
-		wpa_msg_ctrl(wpa_s, MSG_INFO, P2P_EVENT_SERV_DISC_REQ "%d "
-			     MACSTR " %u %u %s",
-			     freq, MAC2STR(sa), dialog_token, update_indic,
-			     buf);
-		os_free(buf);
-	}
-
-	if (wpa_s->p2p_sd_over_ctrl_iface) {
-		wpas_notify_p2p_sd_request(wpa_s, freq, sa, dialog_token,
-					   update_indic, tlvs, tlvs_len);
-		return; /* to be processed by an external program */
-	}
-
-	resp = wpabuf_alloc(10000);
-	if (resp == NULL)
-		return;
-
-	while (pos + 1 < end) {
-		wpa_printf(MSG_DEBUG, "P2P: Service Request TLV");
-		slen = WPA_GET_LE16(pos);
-		pos += 2;
-		if (pos + slen > end || slen < 2) {
-			wpa_printf(MSG_DEBUG, "P2P: Unexpected Query Data "
-				   "length");
-			wpabuf_free(resp);
-			return;
-		}
-		tlv_end = pos + slen;
-
-		srv_proto = *pos++;
-		wpa_printf(MSG_DEBUG, "P2P: Service Protocol Type %u",
-			   srv_proto);
-		srv_trans_id = *pos++;
-		wpa_printf(MSG_DEBUG, "P2P: Service Transaction ID %u",
-			   srv_trans_id);
-
-		wpa_hexdump(MSG_MSGDUMP, "P2P: Query Data",
-			    pos, tlv_end - pos);
-
-
-		if (wpa_s->force_long_sd) {
-			wpa_printf(MSG_DEBUG, "P2P: SD test - force long "
-				   "response");
-			wpas_sd_all_bonjour(wpa_s, resp, srv_trans_id);
-			wpas_sd_all_upnp(wpa_s, resp, srv_trans_id);
-			goto done;
-		}
-
-		switch (srv_proto) {
-		case P2P_SERV_ALL_SERVICES:
-			wpa_printf(MSG_DEBUG, "P2P: Service Discovery Request "
-				   "for all services");
-			if (dl_list_empty(&wpa_s->global->p2p_srv_upnp) &&
-			    dl_list_empty(&wpa_s->global->p2p_srv_bonjour)) {
-				wpa_printf(MSG_DEBUG, "P2P: No service "
-					   "discovery protocols available");
-				wpas_sd_add_proto_not_avail(
-					resp, P2P_SERV_ALL_SERVICES,
-					srv_trans_id);
-				break;
-			}
-			wpas_sd_all_bonjour(wpa_s, resp, srv_trans_id);
-			wpas_sd_all_upnp(wpa_s, resp, srv_trans_id);
-			break;
-		case P2P_SERV_BONJOUR:
-			wpas_sd_req_bonjour(wpa_s, resp, srv_trans_id,
-					    pos, tlv_end - pos);
-			break;
-		case P2P_SERV_UPNP:
-			wpas_sd_req_upnp(wpa_s, resp, srv_trans_id,
-					 pos, tlv_end - pos);
-			break;
-#ifdef CONFIG_WIFI_DISPLAY
-		case P2P_SERV_WIFI_DISPLAY:
-			wpas_sd_req_wfd(wpa_s, resp, srv_trans_id,
-					pos, tlv_end - pos);
-			break;
-#endif /* CONFIG_WIFI_DISPLAY */
-		default:
-			wpa_printf(MSG_DEBUG, "P2P: Unavailable service "
-				   "protocol %u", srv_proto);
-			wpas_sd_add_proto_not_avail(resp, srv_proto,
-						    srv_trans_id);
-			break;
-		}
-
-		pos = tlv_end;
-	}
-
-done:
-	wpas_notify_p2p_sd_request(wpa_s, freq, sa, dialog_token,
-				   update_indic, tlvs, tlvs_len);
-
-	wpas_p2p_sd_response(wpa_s, freq, sa, dialog_token, resp);
-
-	wpabuf_free(resp);
-}
-
-
-static void wpas_sd_response(void *ctx, const u8 *sa, u16 update_indic,
-			     const u8 *tlvs, size_t tlvs_len)
-{
-	struct wpa_supplicant *wpa_s = ctx;
-	const u8 *pos = tlvs;
-	const u8 *end = tlvs + tlvs_len;
-	const u8 *tlv_end;
-	u16 slen;
-	size_t buf_len;
-	char *buf;
-
-	wpa_hexdump(MSG_MSGDUMP, "P2P: Service Discovery Response TLVs",
-		    tlvs, tlvs_len);
-	if (tlvs_len > 1500) {
-		/* TODO: better way for handling this */
-		wpa_msg_ctrl(wpa_s, MSG_INFO,
-			     P2P_EVENT_SERV_DISC_RESP MACSTR
-			     " %u <long response: %u bytes>",
-			     MAC2STR(sa), update_indic,
-			     (unsigned int) tlvs_len);
-	} else {
-		buf_len = 2 * tlvs_len + 1;
-		buf = os_malloc(buf_len);
-		if (buf) {
-			wpa_snprintf_hex(buf, buf_len, tlvs, tlvs_len);
-			wpa_msg_ctrl(wpa_s, MSG_INFO,
-				     P2P_EVENT_SERV_DISC_RESP MACSTR " %u %s",
-				     MAC2STR(sa), update_indic, buf);
-			os_free(buf);
-		}
-	}
-
-	while (pos < end) {
-		u8 srv_proto, srv_trans_id, status;
-
-		wpa_printf(MSG_DEBUG, "P2P: Service Response TLV");
-		slen = WPA_GET_LE16(pos);
-		pos += 2;
-		if (pos + slen > end || slen < 3) {
-			wpa_printf(MSG_DEBUG, "P2P: Unexpected Response Data "
-				   "length");
-			return;
-		}
-		tlv_end = pos + slen;
-
-		srv_proto = *pos++;
-		wpa_printf(MSG_DEBUG, "P2P: Service Protocol Type %u",
-			   srv_proto);
-		srv_trans_id = *pos++;
-		wpa_printf(MSG_DEBUG, "P2P: Service Transaction ID %u",
-			   srv_trans_id);
-		status = *pos++;
-		wpa_printf(MSG_DEBUG, "P2P: Status Code ID %u",
-			   status);
-
-		wpa_hexdump(MSG_MSGDUMP, "P2P: Response Data",
-			    pos, tlv_end - pos);
-
-		pos = tlv_end;
-	}
-
-	wpas_notify_p2p_sd_response(wpa_s, sa, update_indic, tlvs, tlvs_len);
-}
-
-
-u64 wpas_p2p_sd_request(struct wpa_supplicant *wpa_s, const u8 *dst,
-			const struct wpabuf *tlvs)
-{
-	if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
-		return 0;
-	return (uintptr_t) p2p_sd_request(wpa_s->global->p2p, dst, tlvs);
-}
-
-
-u64 wpas_p2p_sd_request_upnp(struct wpa_supplicant *wpa_s, const u8 *dst,
-			     u8 version, const char *query)
-{
-	struct wpabuf *tlvs;
-	u64 ret;
-
-	tlvs = wpabuf_alloc(2 + 1 + 1 + 1 + os_strlen(query));
-	if (tlvs == NULL)
-		return 0;
-	wpabuf_put_le16(tlvs, 1 + 1 + 1 + os_strlen(query));
-	wpabuf_put_u8(tlvs, P2P_SERV_UPNP); /* Service Protocol Type */
-	wpabuf_put_u8(tlvs, 1); /* Service Transaction ID */
-	wpabuf_put_u8(tlvs, version);
-	wpabuf_put_str(tlvs, query);
-	ret = wpas_p2p_sd_request(wpa_s, dst, tlvs);
-	wpabuf_free(tlvs);
-	return ret;
-}
-
-
-#ifdef CONFIG_WIFI_DISPLAY
-
-static u64 wpas_p2p_sd_request_wfd(struct wpa_supplicant *wpa_s, const u8 *dst,
-				   const struct wpabuf *tlvs)
-{
-	if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
-		return 0;
-	return (uintptr_t) p2p_sd_request_wfd(wpa_s->global->p2p, dst, tlvs);
-}
-
-
-#define MAX_WFD_SD_SUBELEMS 20
-
-static void wfd_add_sd_req_role(struct wpabuf *tlvs, u8 id, u8 role,
-				const char *subelems)
-{
-	u8 *len;
-	const char *pos;
-	int val;
-	int count = 0;
-
-	len = wpabuf_put(tlvs, 2);
-	wpabuf_put_u8(tlvs, P2P_SERV_WIFI_DISPLAY); /* Service Protocol Type */
-	wpabuf_put_u8(tlvs, id); /* Service Transaction ID */
-
-	wpabuf_put_u8(tlvs, role);
-
-	pos = subelems;
-	while (*pos) {
-		val = atoi(pos);
-		if (val >= 0 && val < 256) {
-			wpabuf_put_u8(tlvs, val);
-			count++;
-			if (count == MAX_WFD_SD_SUBELEMS)
-				break;
-		}
-		pos = os_strchr(pos + 1, ',');
-		if (pos == NULL)
-			break;
-		pos++;
-	}
-
-	WPA_PUT_LE16(len, (u8 *) wpabuf_put(tlvs, 0) - len - 2);
-}
-
-
-u64 wpas_p2p_sd_request_wifi_display(struct wpa_supplicant *wpa_s,
-				     const u8 *dst, const char *role)
-{
-	struct wpabuf *tlvs;
-	u64 ret;
-	const char *subelems;
-	u8 id = 1;
-
-	subelems = os_strchr(role, ' ');
-	if (subelems == NULL)
-		return 0;
-	subelems++;
-
-	tlvs = wpabuf_alloc(4 * (2 + 1 + 1 + 1 + MAX_WFD_SD_SUBELEMS));
-	if (tlvs == NULL)
-		return 0;
-
-	if (os_strstr(role, "[source]"))
-		wfd_add_sd_req_role(tlvs, id++, 0x00, subelems);
-	if (os_strstr(role, "[pri-sink]"))
-		wfd_add_sd_req_role(tlvs, id++, 0x01, subelems);
-	if (os_strstr(role, "[sec-sink]"))
-		wfd_add_sd_req_role(tlvs, id++, 0x02, subelems);
-	if (os_strstr(role, "[source+sink]"))
-		wfd_add_sd_req_role(tlvs, id++, 0x03, subelems);
-
-	ret = wpas_p2p_sd_request_wfd(wpa_s, dst, tlvs);
-	wpabuf_free(tlvs);
-	return ret;
-}
-
-#endif /* CONFIG_WIFI_DISPLAY */
-
-
-int wpas_p2p_sd_cancel_request(struct wpa_supplicant *wpa_s, u64 req)
-{
-	if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
-		return -1;
-	return p2p_sd_cancel_request(wpa_s->global->p2p,
-				     (void *) (uintptr_t) req);
-}
-
-
-void wpas_p2p_sd_response(struct wpa_supplicant *wpa_s, int freq,
-			  const u8 *dst, u8 dialog_token,
-			  const struct wpabuf *resp_tlvs)
-{
-	if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
-		return;
-	p2p_sd_response(wpa_s->global->p2p, freq, dst, dialog_token,
-			resp_tlvs);
-}
-
-
-void wpas_p2p_sd_service_update(struct wpa_supplicant *wpa_s)
-{
-	if (wpa_s->global->p2p)
-		p2p_sd_service_update(wpa_s->global->p2p);
-}
-
-
-static void wpas_p2p_srv_bonjour_free(struct p2p_srv_bonjour *bsrv)
-{
-	dl_list_del(&bsrv->list);
-	wpabuf_free(bsrv->query);
-	wpabuf_free(bsrv->resp);
-	os_free(bsrv);
-}
-
-
-static void wpas_p2p_srv_upnp_free(struct p2p_srv_upnp *usrv)
-{
-	dl_list_del(&usrv->list);
-	os_free(usrv->service);
-	os_free(usrv);
-}
-
-
-void wpas_p2p_service_flush(struct wpa_supplicant *wpa_s)
-{
-	struct p2p_srv_bonjour *bsrv, *bn;
-	struct p2p_srv_upnp *usrv, *un;
-
-	dl_list_for_each_safe(bsrv, bn, &wpa_s->global->p2p_srv_bonjour,
-			      struct p2p_srv_bonjour, list)
-		wpas_p2p_srv_bonjour_free(bsrv);
-
-	dl_list_for_each_safe(usrv, un, &wpa_s->global->p2p_srv_upnp,
-			      struct p2p_srv_upnp, list)
-		wpas_p2p_srv_upnp_free(usrv);
-
-	wpas_p2p_sd_service_update(wpa_s);
-}
-
-
-int wpas_p2p_service_add_bonjour(struct wpa_supplicant *wpa_s,
-				 struct wpabuf *query, struct wpabuf *resp)
-{
-	struct p2p_srv_bonjour *bsrv;
-
-	bsrv = os_zalloc(sizeof(*bsrv));
-	if (bsrv == NULL)
-		return -1;
-	bsrv->query = query;
-	bsrv->resp = resp;
-	dl_list_add(&wpa_s->global->p2p_srv_bonjour, &bsrv->list);
-
-	wpas_p2p_sd_service_update(wpa_s);
-	return 0;
-}
-
-
-int wpas_p2p_service_del_bonjour(struct wpa_supplicant *wpa_s,
-				 const struct wpabuf *query)
-{
-	struct p2p_srv_bonjour *bsrv;
-
-	bsrv = wpas_p2p_service_get_bonjour(wpa_s, query);
-	if (bsrv == NULL)
-		return -1;
-	wpas_p2p_srv_bonjour_free(bsrv);
-	wpas_p2p_sd_service_update(wpa_s);
-	return 0;
-}
-
-
-int wpas_p2p_service_add_upnp(struct wpa_supplicant *wpa_s, u8 version,
-			      const char *service)
-{
-	struct p2p_srv_upnp *usrv;
-
-	if (wpas_p2p_service_get_upnp(wpa_s, version, service))
-		return 0; /* Already listed */
-	usrv = os_zalloc(sizeof(*usrv));
-	if (usrv == NULL)
-		return -1;
-	usrv->version = version;
-	usrv->service = os_strdup(service);
-	if (usrv->service == NULL) {
-		os_free(usrv);
-		return -1;
-	}
-	dl_list_add(&wpa_s->global->p2p_srv_upnp, &usrv->list);
-
-	wpas_p2p_sd_service_update(wpa_s);
-	return 0;
-}
-
-
-int wpas_p2p_service_del_upnp(struct wpa_supplicant *wpa_s, u8 version,
-			      const char *service)
-{
-	struct p2p_srv_upnp *usrv;
-
-	usrv = wpas_p2p_service_get_upnp(wpa_s, version, service);
-	if (usrv == NULL)
-		return -1;
-	wpas_p2p_srv_upnp_free(usrv);
-	wpas_p2p_sd_service_update(wpa_s);
-	return 0;
+	return wpa_drv_send_mlme(wpa_s, wpabuf_head(buf), wpabuf_len(buf), 1,
+				 freq);
 }
 
 
@@ -2824,6 +2440,7 @@
 	u8 empty_dev_type[8];
 	unsigned int generated_pin = 0;
 	struct wpa_supplicant *group = NULL;
+	int res;
 
 	if (group_id) {
 		for (group = wpa_s->global->ifaces; group; group = group->next)
@@ -2842,15 +2459,17 @@
 		os_memset(empty_dev_type, 0, sizeof(empty_dev_type));
 		pri_dev_type = empty_dev_type;
 	}
-	os_snprintf(params, sizeof(params), " p2p_dev_addr=" MACSTR
-		    " pri_dev_type=%s name='%s' config_methods=0x%x "
-		    "dev_capab=0x%x group_capab=0x%x%s%s",
-		    MAC2STR(dev_addr),
-		    wps_dev_type_bin2str(pri_dev_type, devtype,
-					 sizeof(devtype)),
-		    dev_name, supp_config_methods, dev_capab, group_capab,
-		    group ? " group=" : "",
-		    group ? group->ifname : "");
+	res = os_snprintf(params, sizeof(params), " p2p_dev_addr=" MACSTR
+			  " pri_dev_type=%s name='%s' config_methods=0x%x "
+			  "dev_capab=0x%x group_capab=0x%x%s%s",
+			  MAC2STR(dev_addr),
+			  wps_dev_type_bin2str(pri_dev_type, devtype,
+					       sizeof(devtype)),
+			  dev_name, supp_config_methods, dev_capab, group_capab,
+			  group ? " group=" : "",
+			  group ? group->ifname : "");
+	if (os_snprintf_error(sizeof(params), res))
+		wpa_printf(MSG_DEBUG, "P2P: PD Request event truncated");
 	params[sizeof(params) - 1] = '\0';
 
 	if (config_methods & WPS_CONFIG_DISPLAY) {
@@ -2886,10 +2505,14 @@
 	}
 
 	if (wpa_s->pending_pd_use == AUTO_PD_JOIN ||
-	    wpa_s->pending_pd_use == AUTO_PD_GO_NEG)
-		os_snprintf(params, sizeof(params), " peer_go=%d",
-			    wpa_s->pending_pd_use == AUTO_PD_JOIN);
-	else
+	    wpa_s->pending_pd_use == AUTO_PD_GO_NEG) {
+		int res;
+
+		res = os_snprintf(params, sizeof(params), " peer_go=%d",
+				  wpa_s->pending_pd_use == AUTO_PD_JOIN);
+		if (os_snprintf_error(sizeof(params), res))
+			params[sizeof(params) - 1] = '\0';
+	} else
 		params[0] = '\0';
 
 	if (config_methods & WPS_CONFIG_DISPLAY)
@@ -2909,13 +2532,18 @@
 
 
 static void wpas_prov_disc_fail(void *ctx, const u8 *peer,
-				enum p2p_prov_disc_status status)
+				enum p2p_prov_disc_status status,
+				u32 adv_id, const u8 *adv_mac,
+				const char *deferred_session_resp)
 {
 	struct wpa_supplicant *wpa_s = ctx;
 
 	if (wpa_s->p2p_fallback_to_go_neg) {
 		wpa_dbg(wpa_s, MSG_DEBUG, "P2P: PD for p2p_connect-auto "
 			"failed - fall back to GO Negotiation");
+		wpa_msg_global(wpa_s->parent, MSG_INFO,
+			       P2P_EVENT_FALLBACK_TO_GO_NEG
+			       "reason=PD-failed");
 		wpas_p2p_fallback_to_go_neg(wpa_s, 0);
 		return;
 	}
@@ -2929,9 +2557,21 @@
 		return;
 	}
 
-	wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_PROV_DISC_FAILURE
-		       " p2p_dev_addr=" MACSTR " status=%d",
-		       MAC2STR(peer), status);
+	if (adv_id && adv_mac && deferred_session_resp) {
+		wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_PROV_DISC_FAILURE
+			       " p2p_dev_addr=" MACSTR " status=%d adv_id=%x"
+			       " deferred_session_resp='%s'",
+			       MAC2STR(peer), status, adv_id,
+			       deferred_session_resp);
+	} else if (adv_id && adv_mac) {
+		wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_PROV_DISC_FAILURE
+			       " p2p_dev_addr=" MACSTR " status=%d adv_id=%x",
+			       MAC2STR(peer), status, adv_id);
+	} else {
+		wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_PROV_DISC_FAILURE
+			       " p2p_dev_addr=" MACSTR " status=%d",
+			       MAC2STR(peer), status);
+	}
 
 	wpas_notify_p2p_provision_discovery(wpa_s, peer, 0 /* response */,
 					    status, 0, 0);
@@ -3013,14 +2653,14 @@
 		}
 
 #ifdef CONFIG_WPS_NFC
-		if (dev_pw_id >= 0 && wpa_s->parent->p2p_nfc_tag_enabled &&
-		    dev_pw_id == wpa_s->parent->p2p_oob_dev_pw_id) {
+		if (dev_pw_id >= 0 && wpa_s->p2p_nfc_tag_enabled &&
+		    dev_pw_id == wpa_s->p2p_oob_dev_pw_id) {
 			wpa_printf(MSG_DEBUG, "P2P: Accept invitation based on local enabled NFC Tag");
-			wpa_s->parent->p2p_wps_method = WPS_NFC;
-			wpa_s->parent->pending_join_wps_method = WPS_NFC;
-			os_memcpy(wpa_s->parent->pending_join_dev_addr,
+			wpa_s->p2p_wps_method = WPS_NFC;
+			wpa_s->pending_join_wps_method = WPS_NFC;
+			os_memcpy(wpa_s->pending_join_dev_addr,
 				  go_dev_addr, ETH_ALEN);
-			os_memcpy(wpa_s->parent->pending_join_iface_addr,
+			os_memcpy(wpa_s->pending_join_iface_addr,
 				  bssid, ETH_ALEN);
 			goto accept_inv;
 		}
@@ -3208,12 +2848,13 @@
 					const u8 *peer, int inv)
 {
 	size_t i;
+	struct wpa_supplicant *p2p_wpa_s = wpa_s->global->p2p_init_wpa_s;
 
 	if (ssid == NULL)
 		return;
 
 	for (i = 0; ssid->p2p_client_list && i < ssid->num_p2p_clients; i++) {
-		if (os_memcmp(ssid->p2p_client_list + i * ETH_ALEN, peer,
+		if (os_memcmp(ssid->p2p_client_list + i * 2 * ETH_ALEN, peer,
 			      ETH_ALEN) == 0)
 			break;
 	}
@@ -3233,12 +2874,12 @@
 		   "group %d client list%s",
 		   MAC2STR(peer), ssid->id,
 		   inv ? " due to invitation result" : "");
-	os_memmove(ssid->p2p_client_list + i * ETH_ALEN,
-		   ssid->p2p_client_list + (i + 1) * ETH_ALEN,
-		   (ssid->num_p2p_clients - i - 1) * ETH_ALEN);
+	os_memmove(ssid->p2p_client_list + i * 2 * ETH_ALEN,
+		   ssid->p2p_client_list + (i + 1) * 2 * ETH_ALEN,
+		   (ssid->num_p2p_clients - i - 1) * 2 * ETH_ALEN);
 	ssid->num_p2p_clients--;
-	if (wpa_s->parent->conf->update_config &&
-	    wpa_config_write(wpa_s->parent->confname, wpa_s->parent->conf))
+	if (p2p_wpa_s->conf->update_config &&
+	    wpa_config_write(p2p_wpa_s->confname, p2p_wpa_s->conf))
 		wpa_printf(MSG_DEBUG, "P2P: Failed to update configuration");
 }
 
@@ -3369,6 +3010,8 @@
 {
 	int i, cla = 0;
 
+	wpa_s->global->p2p_24ghz_social_channels = 1;
+
 	os_memset(cli_chan, 0, sizeof(*cli_chan));
 
 	wpa_printf(MSG_DEBUG, "P2P: Enable operating classes for 2.4 GHz "
@@ -3439,7 +3082,7 @@
 
 
 enum chan_allowed {
-	NOT_ALLOWED, PASSIVE_ONLY, ALLOWED
+	NOT_ALLOWED, NO_IR, ALLOWED
 };
 
 static int has_channel(struct wpa_global *global,
@@ -3461,10 +3104,8 @@
 			    (HOSTAPD_CHAN_DISABLED |
 			     HOSTAPD_CHAN_RADAR))
 				return NOT_ALLOWED;
-			if (mode->channels[i].flag &
-			    (HOSTAPD_CHAN_PASSIVE_SCAN |
-			     HOSTAPD_CHAN_NO_IBSS))
-				return PASSIVE_ONLY;
+			if (mode->channels[i].flag & HOSTAPD_CHAN_NO_IR)
+				return NO_IR;
 			return ALLOWED;
 		}
 	}
@@ -3482,7 +3123,7 @@
 	enum { BW20, BW40PLUS, BW40MINUS, BW80, BW2160 } bw;
 };
 
-static struct p2p_oper_class_map op_class[] = {
+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 },
@@ -3490,6 +3131,7 @@
 #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 },
@@ -3553,8 +3195,8 @@
 		res = has_channel(wpa_s->global, mode, adj_chan, &flags);
 		if (res == NOT_ALLOWED)
 			return NOT_ALLOWED;
-		if (res == PASSIVE_ONLY)
-			ret = PASSIVE_ONLY;
+		if (res == NO_IR)
+			ret = NO_IR;
 
 		if (i == 0 && !(flags & HOSTAPD_CHAN_VHT_10_70))
 			return NOT_ALLOWED;
@@ -3592,8 +3234,8 @@
 
 	if (res == NOT_ALLOWED || res2 == NOT_ALLOWED)
 		return NOT_ALLOWED;
-	if (res == PASSIVE_ONLY || res2 == PASSIVE_ONLY)
-		return PASSIVE_ONLY;
+	if (res == NO_IR || res2 == NO_IR)
+		return NO_IR;
 	return res;
 }
 
@@ -3615,13 +3257,15 @@
 	cla = cli_cla = 0;
 
 	for (op = 0; op_class[op].op_class; op++) {
-		struct p2p_oper_class_map *o = &op_class[op];
+		const struct p2p_oper_class_map *o = &op_class[op];
 		u8 ch;
 		struct p2p_reg_class *reg = NULL, *cli_reg = NULL;
 
 		mode = get_mode(wpa_s->hw.modes, wpa_s->hw.num_modes, o->mode);
 		if (mode == NULL)
 			continue;
+		if (mode->mode == HOSTAPD_MODE_IEEE80211G)
+			wpa_s->global->p2p_24ghz_social_channels = 1;
 		for (ch = o->min_chan; ch <= o->max_chan; ch += o->inc) {
 			enum chan_allowed res;
 			res = wpas_p2p_verify_channel(wpa_s, mode, ch, o->bw);
@@ -3635,7 +3279,7 @@
 				}
 				reg->channel[reg->channels] = ch;
 				reg->channels++;
-			} else if (res == PASSIVE_ONLY &&
+			} else if (res == NO_IR &&
 				   wpa_s->conf->p2p_add_cli_chan) {
 				if (cli_reg == NULL) {
 					wpa_printf(MSG_DEBUG, "P2P: Add operating class %u (client only)",
@@ -3672,12 +3316,13 @@
 	enum chan_allowed ret;
 
 	for (op = 0; op_class[op].op_class; op++) {
-		struct p2p_oper_class_map *o = &op_class[op];
+		const struct p2p_oper_class_map *o = &op_class[op];
 		u8 ch;
 
 		for (ch = o->min_chan; ch <= o->max_chan; ch += o->inc) {
 			if (o->mode != HOSTAPD_MODE_IEEE80211A ||
-			    o->bw == BW20 || ch != channel)
+			    (o->bw != BW40PLUS && o->bw != BW40MINUS) ||
+			    ch != channel)
 				continue;
 			ret = wpas_p2p_verify_channel(wpa_s, mode, ch, o->bw);
 			if (ret == ALLOWED)
@@ -3795,8 +3440,10 @@
 	char force_name[100];
 	int ret;
 
-	os_snprintf(ifname, sizeof(ifname), P2P_MGMT_DEVICE_PREFIX "%s",
-		    wpa_s->ifname);
+	ret = os_snprintf(ifname, sizeof(ifname), P2P_MGMT_DEVICE_PREFIX "%s",
+			  wpa_s->ifname);
+	if (os_snprintf_error(sizeof(ifname), ret))
+		return -1;
 	force_name[0] = '\0';
 	wpa_s->pending_interface_type = WPA_IF_P2P_DEVICE;
 	ret = wpa_drv_if_add(wpa_s, WPA_IF_P2P_DEVICE, ifname, NULL, NULL,
@@ -3825,14 +3472,12 @@
 		iface.confname = wpa_s->confname;
 		iface.ctrl_interface = wpa_s->conf->ctrl_interface;
 	}
-	iface.conf_p2p_dev = NULL;
 
-	p2pdev_wpa_s = wpa_supplicant_add_iface(wpa_s->global, &iface);
+	p2pdev_wpa_s = wpa_supplicant_add_iface(wpa_s->global, &iface, wpa_s);
 	if (!p2pdev_wpa_s) {
 		wpa_printf(MSG_DEBUG, "P2P: Failed to add P2P Device interface");
 		return -1;
 	}
-	p2pdev_wpa_s->parent = wpa_s;
 
 	wpa_s->pending_interface_name[0] = '\0';
 	return 0;
@@ -3861,6 +3506,337 @@
 }
 
 
+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)
+{
+	struct wpa_supplicant *wpa_s = ctx;
+	struct wpa_ssid *s;
+
+	s = wpas_p2p_get_persistent(wpa_s, addr, ssid, ssid_len);
+	if (s) {
+		os_memcpy(ret_ssid, s->ssid, s->ssid_len);
+		*ret_ssid_len = s->ssid_len;
+		os_memcpy(go_dev_addr, s->bssid, ETH_ALEN);
+		return 1;
+	}
+
+	return 0;
+}
+
+
+static int wpas_get_go_info(void *ctx, u8 *intended_addr,
+			    u8 *ssid, size_t *ssid_len, int *group_iface)
+{
+	struct wpa_supplicant *wpa_s = ctx;
+	struct wpa_ssid *s;
+	u8 bssid[ETH_ALEN];
+
+	s = wpas_p2p_group_go_ssid(wpa_s, bssid);
+	if (!s) {
+		s = wpas_p2p_get_persistent_go(wpa_s);
+		if (s)
+			os_memcpy(bssid, s->bssid, ETH_ALEN);
+	}
+
+	*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;
+
+	return 1;
+}
+
+
+static int wpas_remove_stale_groups(void *ctx, const u8 *peer, const u8 *go,
+				    const u8 *ssid, size_t ssid_len)
+{
+	struct wpa_supplicant *wpa_s = ctx;
+	struct wpa_ssid *s;
+	int save_config = 0;
+	size_t i;
+
+	/* Start with our first choice of Persistent Groups */
+	while ((s = wpas_p2p_get_persistent(wpa_s, peer, NULL, 0))) {
+		if (go && ssid && ssid_len &&
+		    s->ssid_len == ssid_len &&
+		    os_memcmp(go, s->bssid, ETH_ALEN) == 0 &&
+		    os_memcmp(ssid, s->ssid, ssid_len) == 0)
+			break;
+
+		/* Remove stale persistent group */
+		if (s->mode != WPAS_MODE_P2P_GO || s->num_p2p_clients <= 1) {
+			wpa_config_remove_network(wpa_s->conf, s->id);
+			save_config = 1;
+			continue;
+		}
+
+		for (i = 0; i < s->num_p2p_clients; i++) {
+			if (os_memcmp(s->p2p_client_list + i * 2 * ETH_ALEN,
+				      peer, ETH_ALEN) != 0)
+				continue;
+
+			os_memmove(s->p2p_client_list + i * 2 * ETH_ALEN,
+				   s->p2p_client_list + (i + 1) * 2 * ETH_ALEN,
+				   (s->num_p2p_clients - i - 1) * 2 * ETH_ALEN);
+			break;
+		}
+		s->num_p2p_clients--;
+		save_config = 1;
+	}
+
+	if (save_config)
+		p2p_config_write(wpa_s);
+
+	/* Return TRUE if valid SSID remains */
+	return s != NULL;
+}
+
+
+static void wpas_p2ps_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)
+{
+	struct wpa_supplicant *wpa_s = ctx;
+	u8 mac[ETH_ALEN];
+	struct wpa_ssid *persistent_go, *stale, *s;
+	int save_config = 0;
+	struct wpa_supplicant *go_wpa_s;
+
+	if (!dev)
+		return;
+
+	os_memset(mac, 0, ETH_ALEN);
+	if (!adv_mac)
+		adv_mac = mac;
+	if (!ses_mac)
+		ses_mac = mac;
+	if (!grp_mac)
+		grp_mac = mac;
+
+	if (prov_start) {
+		if (session_info == NULL) {
+			wpa_msg_global(wpa_s, MSG_INFO,
+				       P2P_EVENT_P2PS_PROVISION_START MACSTR
+				       " adv_id=%x conncap=%x"
+				       " adv_mac=" MACSTR
+				       " session=%x mac=" MACSTR
+				       " dev_passwd_id=%d",
+				       MAC2STR(dev), adv_id, conncap,
+				       MAC2STR(adv_mac),
+				       ses_id, MAC2STR(ses_mac),
+				       passwd_id);
+		} 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'",
+				       MAC2STR(dev), adv_id, conncap,
+				       MAC2STR(adv_mac),
+				       ses_id, MAC2STR(ses_mac),
+				       passwd_id, session_info);
+		}
+		return;
+	}
+
+	go_wpa_s = wpas_p2p_get_go_group(wpa_s);
+	persistent_go = wpas_p2p_get_persistent_go(wpa_s);
+
+	if (status && status != P2P_SC_SUCCESS_DEFERRED) {
+		if (go_wpa_s && !p2p_group_go_member_count(wpa_s))
+			wpas_p2p_group_remove(wpa_s, go_wpa_s->ifname);
+
+		if (persistent_go && !persistent_go->num_p2p_clients) {
+			/* remove empty persistent GO */
+			wpa_config_remove_network(wpa_s->conf,
+						  persistent_go->id);
+		}
+
+		wpa_msg_global(wpa_s, MSG_INFO,
+			       P2P_EVENT_P2PS_PROVISION_DONE MACSTR
+			       " status=%d"
+			       " adv_id=%x adv_mac=" MACSTR
+			       " session=%x mac=" MACSTR,
+			       MAC2STR(dev), status,
+			       adv_id, MAC2STR(adv_mac),
+			       ses_id, MAC2STR(ses_mac));
+		return;
+	}
+
+	/* Clean up stale persistent groups with this device */
+	s = wpas_p2p_get_persistent(wpa_s, dev, persist_ssid,
+				    persist_ssid_size);
+	for (;;) {
+		stale = wpas_p2p_get_persistent(wpa_s, dev, NULL, 0);
+		if (!stale)
+			break;
+
+		if (s && s->ssid_len == stale->ssid_len &&
+		    os_memcmp(stale->bssid, s->bssid, ETH_ALEN) == 0 &&
+		    os_memcmp(stale->ssid, s->ssid, s->ssid_len) == 0)
+			break;
+
+		/* Remove stale persistent group */
+		if (stale->mode != WPAS_MODE_P2P_GO ||
+		    stale->num_p2p_clients <= 1) {
+			wpa_config_remove_network(wpa_s->conf, stale->id);
+		} else {
+			size_t i;
+
+			for (i = 0; i < stale->num_p2p_clients; i++) {
+				if (os_memcmp(stale->p2p_client_list +
+					      i * ETH_ALEN,
+					      dev, ETH_ALEN) == 0) {
+					os_memmove(stale->p2p_client_list +
+						   i * ETH_ALEN,
+						   stale->p2p_client_list +
+						   (i + 1) * ETH_ALEN,
+						   (stale->num_p2p_clients -
+						    i - 1) * ETH_ALEN);
+					break;
+				}
+			}
+			stale->num_p2p_clients--;
+		}
+		save_config = 1;
+	}
+
+	if (save_config)
+		p2p_config_write(wpa_s);
+
+	if (s) {
+		if (go_wpa_s && !p2p_group_go_member_count(wpa_s))
+			wpas_p2p_group_remove(wpa_s, go_wpa_s->ifname);
+
+		if (persistent_go && s != persistent_go &&
+		    !persistent_go->num_p2p_clients) {
+			/* remove empty persistent GO */
+			wpa_config_remove_network(wpa_s->conf,
+						  persistent_go->id);
+			/* Save config */
+		}
+
+		wpa_msg_global(wpa_s, MSG_INFO,
+			       P2P_EVENT_P2PS_PROVISION_DONE MACSTR
+			       " status=%d"
+			       " adv_id=%x adv_mac=" MACSTR
+			       " session=%x mac=" MACSTR
+			       " persist=%d",
+			       MAC2STR(dev), status,
+			       adv_id, MAC2STR(adv_mac),
+			       ses_id, MAC2STR(ses_mac), s->id);
+		return;
+	}
+
+	if (conncap == P2PS_SETUP_GROUP_OWNER) {
+		const char *go_ifname = NULL;
+		if (!go_wpa_s) {
+			wpa_s->global->pending_p2ps_group = 1;
+
+			if (wpa_s->conf->p2p_no_group_iface)
+				go_ifname = wpa_s->ifname;
+			else if (wpa_s->pending_interface_name[0])
+				go_ifname = wpa_s->pending_interface_name;
+
+			if (!go_ifname) {
+				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);
+				return;
+			}
+
+			/* If PD Resp complete, start up the GO */
+			if (response_done && persistent_go) {
+				wpas_p2p_group_add_persistent(
+					wpa_s, persistent_go,
+					0, 0, 0, 0, 0, NULL,
+					persistent_go->mode ==
+					WPAS_MODE_P2P_GO ?
+					P2P_MAX_INITIAL_CONN_WAIT_GO_REINVOKE :
+					0);
+			} else if (response_done) {
+				wpas_p2p_group_add(wpa_s, 1, 0, 0, 0);
+			}
+
+			if (passwd_id == DEV_PW_P2PS_DEFAULT) {
+				os_memcpy(wpa_s->p2ps_join_addr, dev, ETH_ALEN);
+				wpa_s->p2ps_join_addr_valid = 1;
+				wpa_dbg(wpa_s, MSG_DEBUG,
+					"P2PS: Saving PIN for " MACSTR,
+					MAC2STR(dev));
+			}
+		} else if (passwd_id == DEV_PW_P2PS_DEFAULT) {
+			go_ifname = go_wpa_s->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);
+
+			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));
+		}
+
+		wpa_msg_global(wpa_s, MSG_INFO,
+			       P2P_EVENT_P2PS_PROVISION_DONE MACSTR
+			       " status=%d conncap=%x"
+			       " adv_id=%x adv_mac=" MACSTR
+			       " session=%x mac=" MACSTR
+			       " dev_passwd_id=%d go=%s",
+			       MAC2STR(dev), status, conncap,
+			       adv_id, MAC2STR(adv_mac),
+			       ses_id, MAC2STR(ses_mac),
+			       passwd_id, go_ifname);
+		return;
+	}
+
+	if (go_wpa_s && !p2p_group_go_member_count(wpa_s))
+		wpas_p2p_group_remove(wpa_s, go_wpa_s->ifname);
+
+	if (persistent_go && !persistent_go->num_p2p_clients) {
+		/* remove empty persistent GO */
+		wpa_config_remove_network(wpa_s->conf, persistent_go->id);
+	}
+
+	if (conncap == P2PS_SETUP_CLIENT) {
+		wpa_msg_global(wpa_s, MSG_INFO,
+			       P2P_EVENT_P2PS_PROVISION_DONE MACSTR
+			       " status=%d conncap=%x"
+			       " adv_id=%x adv_mac=" MACSTR
+			       " session=%x mac=" MACSTR
+			       " dev_passwd_id=%d join=" MACSTR,
+			       MAC2STR(dev), status, conncap,
+			       adv_id, MAC2STR(adv_mac),
+			       ses_id, MAC2STR(ses_mac),
+			       passwd_id, MAC2STR(grp_mac));
+	} 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",
+			       MAC2STR(dev), status, conncap,
+			       adv_id, MAC2STR(adv_mac),
+			       ses_id, MAC2STR(ses_mac),
+			       passwd_id);
+	}
+}
+
+
 static int _wpas_p2p_in_progress(void *ctx)
 {
 	struct wpa_supplicant *wpa_s = ctx;
@@ -3868,6 +3844,33 @@
 }
 
 
+static int wpas_prov_disc_resp_cb(void *ctx)
+{
+	struct wpa_supplicant *wpa_s = ctx;
+	struct wpa_ssid *persistent_go;
+
+	if (!wpa_s->global->pending_p2ps_group)
+		return 0;
+
+	wpa_s->global->pending_p2ps_group = 0;
+
+	if (wpas_p2p_get_go_group(wpa_s))
+		return 0;
+	persistent_go = wpas_p2p_get_persistent_go(wpa_s);
+
+	if (persistent_go) {
+		wpas_p2p_group_add_persistent(
+			wpa_s, persistent_go, 0, 0, 0, 0, 0, NULL,
+			persistent_go->mode == WPAS_MODE_P2P_GO ?
+			P2P_MAX_INITIAL_CONN_WAIT_GO_REINVOKE : 0);
+	} else {
+		wpas_p2p_group_add(wpa_s, 1, 0, 0, 0);
+	}
+
+	return 1;
+}
+
+
 /**
  * wpas_p2p_init - Initialize P2P module for %wpa_supplicant
  * @global: Pointer to global data from wpa_supplicant_init()
@@ -3915,6 +3918,12 @@
 	p2p.presence_resp = wpas_presence_resp;
 	p2p.is_concurrent_session_active = wpas_is_concurrent_session_active;
 	p2p.is_p2p_in_progress = _wpas_p2p_in_progress;
+	p2p.get_persistent_group = wpas_get_persistent_group;
+	p2p.get_go_info = wpas_get_go_info;
+	p2p.remove_stale_groups = wpas_remove_stale_groups;
+	p2p.p2ps_prov_complete = wpas_p2ps_prov_complete;
+	p2p.prov_disc_resp_cb = wpas_prov_disc_resp_cb;
+	p2p.p2ps_group_capability = p2ps_group_capability;
 
 	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);
@@ -3946,9 +3955,9 @@
 		 */
 		if (p2p_config_get_random_social(&p2p, &p2p.reg_class,
 						 &p2p.channel) != 0) {
-			wpa_printf(MSG_ERROR,
-				   "P2P: Failed to select random social channel as listen channel");
-			return -1;
+			wpa_printf(MSG_INFO,
+				   "P2P: No social channels supported by the driver - do not enable P2P");
+			return 0;
 		}
 		p2p.channel_forced = 0;
 	}
@@ -4079,6 +4088,10 @@
 	wpabuf_free(wpa_s->p2p_oob_dev_pw);
 	wpa_s->p2p_oob_dev_pw = NULL;
 
+	os_free(wpa_s->p2p_group_common_freqs);
+	wpa_s->p2p_group_common_freqs = NULL;
+	wpa_s->p2p_group_common_freqs_num = 0;
+
 	/* TODO: remove group interface from the driver if this wpa_s instance
 	 * is on top of a P2P group interface */
 }
@@ -4330,7 +4343,7 @@
 		wpa_printf(MSG_DEBUG, "P2P: Auto PD with " MACSTR " join=%d",
 			   MAC2STR(wpa_s->pending_join_dev_addr), join);
 		if (p2p_prov_disc_req(wpa_s->global->p2p,
-				      wpa_s->pending_join_dev_addr,
+				      wpa_s->pending_join_dev_addr, NULL,
 				      wpa_s->pending_pd_config_methods, join,
 				      0, wpa_s->user_initiated_pd) < 0) {
 			wpa_s->p2p_auto_pd = 0;
@@ -4348,6 +4361,9 @@
 		if (join < 0) {
 			wpa_printf(MSG_DEBUG, "P2P: Peer was not found to be "
 				   "running a GO -> use GO Negotiation");
+			wpa_msg_global(wpa_s->parent, MSG_INFO,
+				       P2P_EVENT_FALLBACK_TO_GO_NEG
+				       "reason=peer-not-running-GO");
 			wpas_p2p_connect(wpa_s, wpa_s->pending_join_dev_addr,
 					 wpa_s->p2p_pin, wpa_s->p2p_wps_method,
 					 wpa_s->p2p_persistent_group, 0, 0, 0,
@@ -4363,8 +4379,11 @@
 		wpa_printf(MSG_DEBUG, "P2P: Peer was found running GO%s -> "
 			   "try to join the group", join ? "" :
 			   " in older scan");
-		if (!join)
+		if (!join) {
+			wpa_msg_global(wpa_s->parent, MSG_INFO,
+				       P2P_EVENT_FALLBACK_TO_GO_NEG_ENABLED);
 			wpa_s->p2p_fallback_to_go_neg = 1;
+		}
 	}
 
 	freq = p2p_get_oper_freq(wpa_s->global->p2p,
@@ -4407,10 +4426,25 @@
 					       wpa_s->pending_join_iface_addr);
 	}
 	if (bss) {
+		u8 dev_addr[ETH_ALEN];
+
 		freq = bss->freq;
 		wpa_printf(MSG_DEBUG, "P2P: Target GO operating frequency "
 			   "from BSS table: %d MHz (SSID %s)", freq,
 			   wpa_ssid_txt(bss->ssid, bss->ssid_len));
+		if (p2p_parse_dev_addr((const u8 *) (bss + 1), bss->ie_len,
+				       dev_addr) == 0 &&
+		    os_memcmp(wpa_s->pending_join_dev_addr,
+			      wpa_s->pending_join_iface_addr, ETH_ALEN) == 0 &&
+		    os_memcmp(dev_addr, wpa_s->pending_join_dev_addr,
+			      ETH_ALEN) != 0) {
+			wpa_printf(MSG_DEBUG,
+				   "P2P: Update target GO device address based on BSS entry: " MACSTR " (was " MACSTR ")",
+				   MAC2STR(dev_addr),
+				   MAC2STR(wpa_s->pending_join_dev_addr));
+			os_memcpy(wpa_s->pending_join_dev_addr, dev_addr,
+				  ETH_ALEN);
+		}
 	}
 	if (freq > 0) {
 		u16 method;
@@ -4459,7 +4493,8 @@
 		}
 
 		if (p2p_prov_disc_req(wpa_s->global->p2p,
-				      wpa_s->pending_join_dev_addr, method, 1,
+				      wpa_s->pending_join_dev_addr,
+				      NULL, method, 1,
 				      freq, wpa_s->user_initiated_pd) < 0) {
 			wpa_printf(MSG_DEBUG, "P2P: Failed to send Provision "
 				   "Discovery Request before joining an "
@@ -4711,11 +4746,22 @@
 		else
 			ret = p2p_supported_freq_cli(wpa_s->global->p2p, freq);
 		if (!ret) {
-			wpa_printf(MSG_DEBUG, "P2P: The forced channel "
-				   "(%u MHz) is not supported for P2P uses",
-				   freq);
-			res = -3;
-			goto exit_free;
+			if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD) &&
+			    ieee80211_is_dfs(freq)) {
+				/*
+				 * If freq is a DFS channel and DFS is offloaded
+				 * to the driver, allow P2P GO to use it.
+				 */
+				wpa_printf(MSG_DEBUG,
+					   "P2P: The forced channel for GO (%u MHz) is DFS, and DFS is offloaded to the driver",
+					   freq);
+			} else {
+				wpa_printf(MSG_DEBUG,
+					   "P2P: The forced channel (%u MHz) is not supported for P2P uses",
+					   freq);
+				res = -3;
+				goto exit_free;
+			}
 		}
 
 		for (i = 0; i < num; i++) {
@@ -4816,6 +4862,7 @@
 	wpa_s->global->add_psk = NULL;
 
 	wpa_s->global->p2p_fail_on_wps_complete = 0;
+	wpa_s->global->pending_p2ps_group = 0;
 
 	if (go_intent < 0)
 		go_intent = wpa_s->conf->p2p_go_intent;
@@ -4837,8 +4884,10 @@
 		os_strlcpy(wpa_s->p2p_pin, pin, sizeof(wpa_s->p2p_pin));
 	else if (wps_method == WPS_PIN_DISPLAY) {
 		ret = wps_generate_pin();
-		os_snprintf(wpa_s->p2p_pin, sizeof(wpa_s->p2p_pin), "%08d",
-			    ret);
+		res = os_snprintf(wpa_s->p2p_pin, sizeof(wpa_s->p2p_pin),
+				  "%08d", ret);
+		if (os_snprintf_error(sizeof(wpa_s->p2p_pin), res))
+			wpa_s->p2p_pin[sizeof(wpa_s->p2p_pin) - 1] = '\0';
 		wpa_printf(MSG_DEBUG, "P2P: Randomly generated PIN: %s",
 			   wpa_s->p2p_pin);
 	} else
@@ -4932,7 +4981,11 @@
 {
 	if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
 		return;
-	if (wpa_s->off_channel_freq == wpa_s->pending_listen_freq) {
+	wpa_printf(MSG_DEBUG, "P2P: remain-on-channel callback (off_channel_freq=%u pending_listen_freq=%d roc_waiting_drv_freq=%d freq=%u duration=%u)",
+		   wpa_s->off_channel_freq, wpa_s->pending_listen_freq,
+		   wpa_s->roc_waiting_drv_freq, freq, duration);
+	if (wpa_s->off_channel_freq &&
+	    wpa_s->off_channel_freq == wpa_s->pending_listen_freq) {
 		p2p_listen_cb(wpa_s->global->p2p, wpa_s->pending_listen_freq,
 			      wpa_s->pending_listen_duration);
 		wpa_s->pending_listen_freq = 0;
@@ -5007,6 +5060,7 @@
 int wpas_p2p_group_remove(struct wpa_supplicant *wpa_s, const char *ifname)
 {
 	struct wpa_global *global = wpa_s->global;
+	struct wpa_supplicant *calling_wpa_s = wpa_s;
 
 	if (os_strcmp(ifname, "*") == 0) {
 		struct wpa_supplicant *prev;
@@ -5018,7 +5072,7 @@
 			    NOT_P2P_GROUP_INTERFACE ||
 			    (prev->current_ssid &&
 			     prev->current_ssid->p2p_group))
-				wpas_p2p_disconnect(prev);
+				wpas_p2p_disconnect_safely(prev, calling_wpa_s);
 		}
 		return 0;
 	}
@@ -5028,7 +5082,7 @@
 			break;
 	}
 
-	return wpas_p2p_disconnect(wpa_s);
+	return wpas_p2p_disconnect_safely(wpa_s, calling_wpa_s);
 }
 
 
@@ -5078,6 +5132,17 @@
 	}
 
 	if (freq > 0 && !p2p_supported_freq_go(wpa_s->global->p2p, freq)) {
+		if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD) &&
+		    ieee80211_is_dfs(freq)) {
+			/*
+			 * If freq is a DFS channel and DFS is offloaded to the
+			 * driver, allow P2P GO to use it.
+			 */
+			wpa_printf(MSG_DEBUG, "P2P: "
+				   "%s: The forced channel for GO (%u MHz) is DFS, and DFS is offloaded",
+				   __func__, freq);
+			return freq;
+		}
 		wpa_printf(MSG_DEBUG, "P2P: The forced channel for GO "
 			   "(%u MHz) is not supported for P2P uses",
 			   freq);
@@ -5115,6 +5180,24 @@
 			goto out;
 	}
 
+	/* try all channels in operating class 115 */
+	for (i = 0; i < 4; i++) {
+		params->freq = 5180 + i * 20;
+		if (!wpas_p2p_disallowed_freq(wpa_s->global, params->freq) &&
+		    freq_included(channels, params->freq) &&
+		    p2p_supported_freq(wpa_s->global->p2p, params->freq))
+			goto out;
+	}
+
+	/* try all channels in operating class 124 */
+	for (i = 0; i < 4; i++) {
+		params->freq = 5745 + i * 20;
+		if (!wpas_p2p_disallowed_freq(wpa_s->global, params->freq) &&
+		    freq_included(channels, params->freq) &&
+		    p2p_supported_freq(wpa_s->global->p2p, params->freq))
+			goto out;
+	}
+
 	/* try social channel class 180 channel 2 */
 	params->freq = 58320 + 1 * 2160;
 	if (!wpas_p2p_disallowed_freq(wpa_s->global, params->freq) &&
@@ -5131,7 +5214,7 @@
 			goto out;
 	}
 
-	wpa_printf(MSG_DEBUG, "P2P: No 2.4 and 60 GHz channel allowed");
+	wpa_printf(MSG_DEBUG, "P2P: No 2.4, 5, or 60 GHz channel allowed");
 	return -1;
 out:
 	wpa_printf(MSG_DEBUG, "P2P: Set GO freq %d MHz (no preference known)",
@@ -5174,6 +5257,7 @@
 		    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,
@@ -5341,10 +5425,21 @@
 		return -1;
 	if (params.freq &&
 	    !p2p_supported_freq_go(wpa_s->global->p2p, params.freq)) {
-		wpa_printf(MSG_DEBUG, "P2P: The selected channel for GO "
-			   "(%u MHz) is not supported for P2P uses",
-			   params.freq);
-		return -1;
+		if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD) &&
+		    ieee80211_is_dfs(params.freq)) {
+			/*
+			 * If freq is a DFS channel and DFS is offloaded to the
+			 * driver, allow P2P GO to use it.
+			 */
+			wpa_printf(MSG_DEBUG,
+				   "P2P: %s: The forced channel for GO (%u MHz) is DFS, and DFS is offloaded to driver",
+				__func__, params.freq);
+		} else {
+			wpa_printf(MSG_DEBUG,
+				   "P2P: The selected channel for GO (%u MHz) is not supported for P2P uses",
+				   params.freq);
+			return -1;
+		}
 	}
 	p2p_go_params(wpa_s->global->p2p, &params);
 	params.persistent_group = persistent_group;
@@ -5427,6 +5522,23 @@
 	    go == (ssid->mode == WPAS_MODE_P2P_GO)) {
 		wpa_printf(MSG_DEBUG, "P2P: Requested persistent group is "
 			   "already running");
+		if (go == 0 &&
+		    eloop_cancel_timeout(wpas_p2p_group_formation_timeout,
+					 wpa_s->parent, NULL)) {
+			/*
+			 * This can happen if Invitation Response frame was lost
+			 * and the peer (GO of a persistent group) tries to
+			 * invite us again. Reschedule the timeout to avoid
+			 * terminating the wait for the connection too early
+			 * since we now know that the peer is still trying to
+			 * invite us instead of having already started the GO.
+			 */
+			wpa_printf(MSG_DEBUG,
+				   "P2P: Reschedule group formation timeout since peer is still trying to invite us");
+			eloop_register_timeout(P2P_MAX_INITIAL_CONN_WAIT, 0,
+					       wpas_p2p_group_formation_timeout,
+					       wpa_s->parent, NULL);
+		}
 		return 0;
 	}
 
@@ -5438,21 +5550,37 @@
 
 	wpa_s->p2p_fallback_to_go_neg = 0;
 
-	if (force_freq > 0) {
-		freq = wpas_p2p_select_go_freq(wpa_s, force_freq);
-		if (freq < 0)
-			return -1;
-	} else {
-		freq = wpas_p2p_select_go_freq(wpa_s, neg_freq);
-		if (freq < 0 || (freq > 0 && !freq_included(channels, freq)))
-			freq = 0;
-	}
+	if (ssid->mode == WPAS_MODE_P2P_GO) {
+		if (force_freq > 0) {
+			freq = wpas_p2p_select_go_freq(wpa_s, force_freq);
+			if (freq < 0)
+				return -1;
+		} else {
+			freq = wpas_p2p_select_go_freq(wpa_s, neg_freq);
+			if (freq < 0 ||
+			    (freq > 0 && !freq_included(channels, freq)))
+				freq = 0;
+		}
+	} else if (ssid->mode == WPAS_MODE_INFRA) {
+		freq = neg_freq;
+		if (freq <= 0 || !freq_included(channels, freq)) {
+			struct os_reltime now;
+			struct wpa_bss *bss =
+				wpa_bss_get_p2p_dev_addr(wpa_s, ssid->bssid);
 
-	if (ssid->mode == WPAS_MODE_INFRA)
+			os_get_reltime(&now);
+			if (bss &&
+			    !os_reltime_expired(&now, &bss->last_update, 5) &&
+			    freq_included(channels, bss->freq))
+				freq = bss->freq;
+			else
+				freq = 0;
+		}
+
 		return wpas_start_p2p_client(wpa_s, ssid, addr_allocated, freq);
-
-	if (ssid->mode != WPAS_MODE_P2P_GO)
+	} else {
 		return -1;
+	}
 
 	if (wpas_p2p_init_go_params(wpa_s, &params, freq, ht40, vht, channels))
 		return -1;
@@ -5478,6 +5606,8 @@
 	if (wpa_s == NULL)
 		return -1;
 
+	p2p_channels_to_freqs(channels, params.freq_list, P2P_MAX_CHANNELS);
+
 	wpa_s->p2p_first_connection_timeout = connection_timeout;
 	wpas_start_wps_go(wpa_s, &params, 0);
 
@@ -5623,7 +5753,7 @@
 	}
 	if (wpa_s->global->p2p)
 		p2p_wps_success_cb(wpa_s->global->p2p, peer_addr);
-	wpas_group_formation_completed(wpa_s, 1);
+	wpas_group_formation_completed(wpa_s, 1, 0);
 }
 
 
@@ -5673,13 +5803,25 @@
 
 int wpas_p2p_prov_disc(struct wpa_supplicant *wpa_s, const u8 *peer_addr,
 		       const char *config_method,
-		       enum wpas_p2p_prov_disc_use use)
+		       enum wpas_p2p_prov_disc_use use,
+		       struct p2ps_provision *p2ps_prov)
 {
 	u16 config_methods;
 
+	wpa_s->global->pending_p2ps_group = 0;
 	wpa_s->p2p_fallback_to_go_neg = 0;
 	wpa_s->pending_pd_use = NORMAL_PD;
-	if (os_strncmp(config_method, "display", 7) == 0)
+	if (p2ps_prov && use == WPAS_P2P_PD_FOR_ASP) {
+		p2ps_prov->conncap = p2ps_group_capability(
+			wpa_s, P2PS_SETUP_NONE, p2ps_prov->role);
+		wpa_printf(MSG_DEBUG,
+			   "P2P: %s conncap: %d - ASP parsed: %x %x %d %s",
+			   __func__, p2ps_prov->conncap,
+			   p2ps_prov->adv_id, p2ps_prov->conncap,
+			   p2ps_prov->status, p2ps_prov->info);
+
+		config_methods = 0;
+	} else if (os_strncmp(config_method, "display", 7) == 0)
 		config_methods = WPS_CONFIG_DISPLAY;
 	else if (os_strncmp(config_method, "keypad", 6) == 0)
 		config_methods = WPS_CONFIG_KEYPAD;
@@ -5688,6 +5830,7 @@
 		config_methods = WPS_CONFIG_PUSHBUTTON;
 	else {
 		wpa_printf(MSG_DEBUG, "P2P: Unknown config method");
+		os_free(p2ps_prov);
 		return -1;
 	}
 
@@ -5708,10 +5851,12 @@
 		return 0;
 	}
 
-	if (wpa_s->global->p2p == NULL || wpa_s->global->p2p_disabled)
+	if (wpa_s->global->p2p == NULL || wpa_s->global->p2p_disabled) {
+		os_free(p2ps_prov);
 		return -1;
+	}
 
-	return p2p_prov_disc_req(wpa_s->global->p2p, peer_addr,
+	return p2p_prov_disc_req(wpa_s->global->p2p, peer_addr, p2ps_prov,
 				 config_methods, use == WPAS_P2P_PD_FOR_JOIN,
 				 0, 1);
 }
@@ -5740,7 +5885,8 @@
 int wpas_p2p_find(struct wpa_supplicant *wpa_s, unsigned int timeout,
 		  enum p2p_discovery_type type,
 		  unsigned int num_req_dev_types, const u8 *req_dev_types,
-		  const u8 *dev_id, unsigned int search_delay)
+		  const u8 *dev_id, unsigned int search_delay,
+		  u8 seek_cnt, const char **seek_string, int freq)
 {
 	wpas_p2p_clear_pending_action_tx(wpa_s);
 	wpa_s->p2p_long_listen = 0;
@@ -5753,11 +5899,33 @@
 
 	return p2p_find(wpa_s->global->p2p, timeout, type,
 			num_req_dev_types, req_dev_types, dev_id,
-			search_delay);
+			search_delay, seek_cnt, seek_string, freq);
 }
 
 
-static int wpas_p2p_stop_find_oper(struct wpa_supplicant *wpa_s)
+static void wpas_p2p_scan_res_ignore_search(struct wpa_supplicant *wpa_s,
+					    struct wpa_scan_results *scan_res)
+{
+	wpa_printf(MSG_DEBUG, "P2P: Ignore scan results");
+
+	if (wpa_s->p2p_scan_work) {
+		struct wpa_radio_work *work = wpa_s->p2p_scan_work;
+		wpa_s->p2p_scan_work = NULL;
+		radio_work_done(work);
+	}
+
+	if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
+		return;
+
+	/*
+	 * Indicate that results have been processed so that the P2P module can
+	 * continue pending tasks.
+	 */
+	p2p_scan_res_handled(wpa_s->global->p2p);
+}
+
+
+static void wpas_p2p_stop_find_oper(struct wpa_supplicant *wpa_s)
 {
 	wpas_p2p_clear_pending_action_tx(wpa_s);
 	wpa_s->p2p_long_listen = 0;
@@ -5767,15 +5935,19 @@
 	if (wpa_s->global->p2p)
 		p2p_stop_find(wpa_s->global->p2p);
 
-	return 0;
+	if (wpa_s->scan_res_handler == wpas_p2p_scan_res_handler) {
+		wpa_printf(MSG_DEBUG,
+			   "P2P: Do not consider the scan results after stop_find");
+		wpa_s->scan_res_handler = wpas_p2p_scan_res_ignore_search;
+	}
 }
 
 
 void wpas_p2p_stop_find(struct wpa_supplicant *wpa_s)
 {
-	if (wpas_p2p_stop_find_oper(wpa_s) > 0)
-		return;
-	wpas_p2p_remove_pending_group_interface(wpa_s);
+	wpas_p2p_stop_find_oper(wpa_s);
+	if (!wpa_s->global->pending_group_iface_for_p2ps)
+		wpas_p2p_remove_pending_group_interface(wpa_s);
 }
 
 
@@ -5835,7 +6007,11 @@
 
 	if (wpa_s->global->p2p_disabled)
 		return -1;
-	if (wpa_s->conf->p2p_disabled)
+	/*
+	 * Advertize mandatory cross connection capability even on
+	 * p2p_disabled=1 interface when associating with a P2P Manager WLAN AP.
+	 */
+	if (wpa_s->conf->p2p_disabled && p2p_group)
 		return -1;
 	if (wpa_s->global->p2p == NULL)
 		return -1;
@@ -5853,7 +6029,8 @@
 
 int wpas_p2p_probe_req_rx(struct wpa_supplicant *wpa_s, const u8 *addr,
 			  const u8 *dst, const u8 *bssid,
-			  const u8 *ie, size_t ie_len, int ssi_signal)
+			  const u8 *ie, size_t ie_len,
+			  unsigned int rx_freq, int ssi_signal)
 {
 	if (wpa_s->global->p2p_disabled)
 		return 0;
@@ -5861,7 +6038,7 @@
 		return 0;
 
 	switch (p2p_probe_req_rx(wpa_s->global->p2p, addr, dst, bssid,
-				 ie, ie_len)) {
+				 ie, ie_len, rx_freq)) {
 	case P2P_PREQ_NOT_P2P:
 		wpas_notify_preq(wpa_s, addr, dst, bssid, ie, ie_len,
 				 ssi_signal);
@@ -5985,6 +6162,12 @@
 		pref_freq = 0;
 	}
 
+	/*
+	 * Stop any find/listen operations before invitation and possibly
+	 * connection establishment.
+	 */
+	wpas_p2p_stop_find_oper(wpa_s);
+
 	return p2p_invite(wpa_s->global->p2p, peer_addr, role, bssid,
 			  ssid->ssid, ssid->ssid_len, force_freq, go_dev_addr,
 			  1, pref_freq, -1);
@@ -6100,11 +6283,16 @@
 
 	ip_addr[0] = '\0';
 	if (wpa_sm_get_p2p_ip_addr(wpa_s->wpa, ip) == 0) {
-		os_snprintf(ip_addr, sizeof(ip_addr), " ip_addr=%u.%u.%u.%u "
-			    "ip_mask=%u.%u.%u.%u go_ip_addr=%u.%u.%u.%u",
-			    ip[0], ip[1], ip[2], ip[3],
-			    ip[4], ip[5], ip[6], ip[7],
-			    ip[8], ip[9], ip[10], ip[11]);
+		int res;
+
+		res = os_snprintf(ip_addr, sizeof(ip_addr),
+				  " ip_addr=%u.%u.%u.%u "
+				  "ip_mask=%u.%u.%u.%u go_ip_addr=%u.%u.%u.%u",
+				  ip[0], ip[1], ip[2], ip[3],
+				  ip[4], ip[5], ip[6], ip[7],
+				  ip[8], ip[9], ip[10], ip[11]);
+		if (os_snprintf_error(sizeof(ip_addr), res))
+			ip_addr[0] = '\0';
 	}
 
 	wpas_p2p_group_started(wpa_s, 0, ssid, freq,
@@ -6546,7 +6734,8 @@
 		if (iface->drv_flags &
 		    WPA_DRIVER_FLAGS_P2P_DEDICATED_INTERFACE)
 			continue;
-		if (iface->drv_flags & WPA_DRIVER_FLAGS_P2P_CAPABLE)
+		if ((iface->drv_flags & WPA_DRIVER_FLAGS_P2P_CAPABLE) &&
+		    iface != wpa_s->parent)
 			continue;
 
 		wpa_s->cross_connect_enabled = 1;
@@ -6581,7 +6770,7 @@
 		   "session overlap");
 	if (wpa_s != wpa_s->parent)
 		wpa_msg_ctrl(wpa_s->parent, MSG_INFO, WPS_EVENT_OVERLAP);
-	wpas_p2p_group_formation_failed(wpa_s);
+	wpas_p2p_group_formation_failed(wpa_s, 0);
 	return 1;
 }
 
@@ -6691,7 +6880,7 @@
 			eloop_cancel_timeout(wpas_p2p_group_formation_timeout,
 					     wpa_s->parent, NULL);
 			if (wpa_s->p2p_in_provisioning) {
-				wpas_group_formation_completed(wpa_s, 0);
+				wpas_group_formation_completed(wpa_s, 0, 0);
 				break;
 			}
 			wpas_p2p_group_delete(wpa_s,
@@ -6701,7 +6890,7 @@
 			wpa_printf(MSG_DEBUG, "P2P: Interface %s in invitation found - cancelling",
 				   wpa_s->ifname);
 			found = 1;
-			wpas_p2p_group_formation_failed(wpa_s);
+			wpas_p2p_group_formation_failed(wpa_s, 0);
 		}
 	}
 
@@ -6859,7 +7048,7 @@
 		if (s->mode != WPAS_MODE_P2P_GO || s->p2p_client_list == NULL)
 			continue;
 		for (i = 0; i < s->num_p2p_clients; i++) {
-			if (os_memcmp(s->p2p_client_list + i * ETH_ALEN,
+			if (os_memcmp(s->p2p_client_list + i * 2 * ETH_ALEN,
 				      addr, ETH_ALEN) == 0)
 				return s; /* peer is P2P client in persistent
 					   * group */
@@ -6885,6 +7074,20 @@
 		 * provisioning step.
 		 */
 		wpa_printf(MSG_DEBUG, "P2P: Canceled P2P group formation timeout on data connection");
+
+		if (!wpa_s->p2p_go_group_formation_completed &&
+		    !wpa_s->group_formation_reported) {
+			/*
+			 * GO has not yet notified group formation success since
+			 * the WPS step was not completed cleanly. Do that
+			 * notification now since the P2P Client was able to
+			 * connect and as such, must have received the
+			 * credential from the WPS step.
+			 */
+			if (wpa_s->global->p2p)
+				p2p_wps_success_cb(wpa_s->global->p2p, addr);
+			wpas_group_formation_completed(wpa_s, 1, 0);
+		}
 	}
 	if (!wpa_s->p2p_go_group_formation_completed) {
 		wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Marking group formation completed on GO on first data connection");
@@ -6900,16 +7103,18 @@
 }
 
 
-static void wpas_p2p_fallback_to_go_neg(struct wpa_supplicant *wpa_s,
-					int group_added)
+static int wpas_p2p_fallback_to_go_neg(struct wpa_supplicant *wpa_s,
+				       int group_added)
 {
 	struct wpa_supplicant *group = wpa_s;
+	int ret = 0;
+
 	if (wpa_s->global->p2p_group_formation)
 		group = wpa_s->global->p2p_group_formation;
-	wpa_s = wpa_s->parent;
+	wpa_s = wpa_s->global->p2p_init_wpa_s;
 	offchannel_send_action_done(wpa_s);
 	if (group_added)
-		wpas_p2p_group_delete(group, P2P_GROUP_REMOVAL_SILENT);
+		ret = wpas_p2p_group_delete(group, P2P_GROUP_REMOVAL_SILENT);
 	wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Fall back to GO Negotiation");
 	wpas_p2p_connect(wpa_s, wpa_s->pending_join_dev_addr, wpa_s->p2p_pin,
 			 wpa_s->p2p_wps_method, wpa_s->p2p_persistent_group, 0,
@@ -6918,11 +7123,14 @@
 			 wpa_s->p2p_pd_before_go_neg,
 			 wpa_s->p2p_go_ht40,
 			 wpa_s->p2p_go_vht);
+	return ret;
 }
 
 
 int wpas_p2p_scan_no_go_seen(struct wpa_supplicant *wpa_s)
 {
+	int res;
+
 	if (!wpa_s->p2p_fallback_to_go_neg ||
 	    wpa_s->p2p_in_provisioning <= 5)
 		return 0;
@@ -6932,9 +7140,11 @@
 
 	wpa_dbg(wpa_s, MSG_DEBUG, "P2P: GO not found for p2p_connect-auto - "
 		"fallback to GO Negotiation");
-	wpas_p2p_fallback_to_go_neg(wpa_s, 1);
+	wpa_msg_global(wpa_s->parent, MSG_INFO, P2P_EVENT_FALLBACK_TO_GO_NEG
+		       "reason=GO-not-found");
+	res = wpas_p2p_fallback_to_go_neg(wpa_s, 1);
 
-	return 1;
+	return res == 1 ? 2 : 1;
 }
 
 
@@ -7156,16 +7366,17 @@
 {
 	struct wpa_ssid *s;
 	struct wpa_supplicant *w;
+	struct wpa_supplicant *p2p_wpa_s = wpa_s->global->p2p_init_wpa_s;
 
 	wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Remove client " MACSTR, MAC2STR(peer));
 
 	/* Remove from any persistent group */
-	for (s = wpa_s->parent->conf->ssid; s; s = s->next) {
+	for (s = p2p_wpa_s->conf->ssid; s; s = s->next) {
 		if (s->disabled != 2 || s->mode != WPAS_MODE_P2P_GO)
 			continue;
 		if (!iface_addr)
-			wpas_remove_persistent_peer(wpa_s, s, peer, 0);
-		wpas_p2p_remove_psk(wpa_s->parent, s, peer, iface_addr);
+			wpas_remove_persistent_peer(p2p_wpa_s, s, peer, 0);
+		wpas_p2p_remove_psk(p2p_wpa_s, s, peer, iface_addr);
 	}
 
 	/* Remove from any operating group */
@@ -7573,7 +7784,7 @@
 	}
 	len = WPA_GET_BE16(pos);
 	pos += 2;
-	if (pos + len > end) {
+	if (len > end - pos) {
 		wpa_printf(MSG_DEBUG, "P2P: Not enough data for WSC "
 			   "attributes");
 		return -1;
@@ -7589,7 +7800,7 @@
 	}
 	len = WPA_GET_BE16(pos);
 	pos += 2;
-	if (pos + len > end) {
+	if (len > end - pos) {
 		wpa_printf(MSG_DEBUG, "P2P: Not enough data for P2P "
 			   "attributes");
 		return -1;
@@ -7883,7 +8094,7 @@
 
 	if (cand) {
 		wpa_dbg(wpa_s, MSG_DEBUG,
-			"P2P: Update Listen channel to %u baased on operating channel",
+			"P2P: Update Listen channel to %u based on operating channel",
 			cand);
 		p2p_set_listen_channel(wpa_s->global->p2p, 81, cand, 0);
 	}
@@ -7921,8 +8132,6 @@
 
 void wpas_p2p_deinit_iface(struct wpa_supplicant *wpa_s)
 {
-	if (wpa_s == wpa_s->parent)
-		wpas_p2p_group_remove(wpa_s, "*");
 	if (wpa_s == wpa_s->global->p2p_init_wpa_s && wpa_s->global->p2p) {
 		wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Disable P2P since removing "
 			"the management interface is being removed");
diff --git a/wpa_supplicant/p2p_supplicant.h b/wpa_supplicant/p2p_supplicant.h
index 8e23c18..1df34d0 100644
--- a/wpa_supplicant/p2p_supplicant.h
+++ b/wpa_supplicant/p2p_supplicant.h
@@ -15,6 +15,7 @@
 struct p2p_peer_info;
 struct p2p_channels;
 struct wps_event_fail;
+struct p2ps_provision;
 
 int wpas_p2p_add_p2pdev_interface(struct wpa_supplicant *wpa_s,
 				  const char *conf_p2p_dev);
@@ -29,7 +30,6 @@
 		     int pd, int ht40, int vht);
 int wpas_p2p_handle_frequency_conflicts(struct wpa_supplicant *wpa_s,
                                           int freq, struct wpa_ssid *ssid);
-int wpas_p2p_group_remove(struct wpa_supplicant *wpa_s, const char *ifname);
 int wpas_p2p_group_add(struct wpa_supplicant *wpa_s, int persistent_group,
 		       int freq, int ht40, int vht);
 int wpas_p2p_group_add_persistent(struct wpa_supplicant *wpa_s,
@@ -42,11 +42,13 @@
 enum wpas_p2p_prov_disc_use {
 	WPAS_P2P_PD_FOR_GO_NEG,
 	WPAS_P2P_PD_FOR_JOIN,
-	WPAS_P2P_PD_AUTO
+	WPAS_P2P_PD_AUTO,
+	WPAS_P2P_PD_FOR_ASP
 };
 int wpas_p2p_prov_disc(struct wpa_supplicant *wpa_s, const u8 *peer_addr,
 		       const char *config_method,
-		       enum wpas_p2p_prov_disc_use use);
+		       enum wpas_p2p_prov_disc_use use,
+		       struct p2ps_provision *p2ps_prov);
 void wpas_send_action_tx_status(struct wpa_supplicant *wpa_s, const u8 *dst,
 				const u8 *data, size_t data_len,
 				enum p2p_send_action_result result);
@@ -56,16 +58,18 @@
 int wpas_p2p_find(struct wpa_supplicant *wpa_s, unsigned int timeout,
 		  enum p2p_discovery_type type,
 		  unsigned int num_req_dev_types, const u8 *req_dev_types,
-		  const u8 *dev_id, unsigned int search_delay);
+		  const u8 *dev_id, unsigned int search_delay,
+		  u8 seek_cnt, const char **seek_string, int freq);
 void wpas_p2p_stop_find(struct wpa_supplicant *wpa_s);
 int wpas_p2p_listen(struct wpa_supplicant *wpa_s, unsigned int timeout);
 int wpas_p2p_listen_start(struct wpa_supplicant *wpa_s, unsigned int timeout);
 int wpas_p2p_assoc_req_ie(struct wpa_supplicant *wpa_s, struct wpa_bss *bss,
 			  u8 *buf, size_t len, int p2p_group);
 void wpas_p2p_scan_ie(struct wpa_supplicant *wpa_s, struct wpabuf *ies);
-void wpas_p2p_group_formation_failed(struct wpa_supplicant *wpa_s);
 u64 wpas_p2p_sd_request(struct wpa_supplicant *wpa_s, const u8 *dst,
 			const struct wpabuf *tlvs);
+u64 wpas_p2p_sd_request_asp(struct wpa_supplicant *wpa_s, const u8 *dst, u8 id,
+			    const char *svc_str, const char *info_substr);
 u64 wpas_p2p_sd_request_upnp(struct wpa_supplicant *wpa_s, const u8 *dst,
 			     u8 version, const char *query);
 u64 wpas_p2p_sd_request_wifi_display(struct wpa_supplicant *wpa_s,
@@ -84,6 +88,16 @@
 			      const char *service);
 int wpas_p2p_service_del_upnp(struct wpa_supplicant *wpa_s, u8 version,
 			      const char *service);
+int wpas_p2p_service_add_asp(struct wpa_supplicant *wpa_s, int auto_accept,
+			     u32 adv_id, const char *adv_str, u8 svc_state,
+			     u16 config_methods, const char *svc_info);
+int wpas_p2p_service_del_asp(struct wpa_supplicant *wpa_s, u32 adv_id);
+void wpas_p2p_service_flush_asp(struct wpa_supplicant *wpa_s);
+int wpas_p2p_service_p2ps_id_exists(struct wpa_supplicant *wpa_s, u32 adv_id);
+void wpas_sd_request(void *ctx, int freq, const u8 *sa, u8 dialog_token,
+		     u16 update_indic, const u8 *tlvs, size_t tlvs_len);
+void wpas_sd_response(void *ctx, const u8 *sa, u16 update_indic,
+		      const u8 *tlvs, size_t tlvs_len);
 int wpas_p2p_reject(struct wpa_supplicant *wpa_s, const u8 *addr);
 int wpas_p2p_invite(struct wpa_supplicant *wpa_s, const u8 *peer_addr,
 		    struct wpa_ssid *ssid, const u8 *go_dev_addr, int freq,
@@ -143,7 +157,7 @@
 int wpas_p2p_probe_req_rx(struct wpa_supplicant *wpa_s, const u8 *addr,
 			  const u8 *dst, const u8 *bssid,
 			  const u8 *ie, size_t ie_len,
-			  int ssi_signal);
+			  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);
@@ -171,6 +185,7 @@
 int wpas_p2p_wps_eapol_cb(struct wpa_supplicant *wpa_s);
 void wpas_p2p_wps_failed(struct wpa_supplicant *wpa_s,
 			 struct wps_event_fail *fail);
+int wpas_p2p_group_remove(struct wpa_supplicant *wpa_s, const char *ifname);
 
 #else /* CONFIG_P2P */
 
@@ -196,7 +211,7 @@
 					const u8 *addr,
 					const u8 *dst, const u8 *bssid,
 					const u8 *ie, size_t ie_len,
-					int ssi_signal)
+					unsigned int rx_freq, int ssi_signal)
 {
 	return 0;
 }
@@ -294,6 +309,12 @@
 {
 }
 
+static inline int wpas_p2p_group_remove(struct wpa_supplicant *wpa_s,
+					const char *ifname)
+{
+	return 0;
+}
+
 #endif /* CONFIG_P2P */
 
 #endif /* P2P_SUPPLICANT_H */
diff --git a/wpa_supplicant/p2p_supplicant_sd.c b/wpa_supplicant/p2p_supplicant_sd.c
new file mode 100644
index 0000000..f4aa3e0
--- /dev/null
+++ b/wpa_supplicant/p2p_supplicant_sd.c
@@ -0,0 +1,1272 @@
+/*
+ * wpa_supplicant - P2P service discovery
+ * Copyright (c) 2009-2010, Atheros Communications
+ * Copyright (c) 2010-2014, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "p2p/p2p.h"
+#include "wpa_supplicant_i.h"
+#include "notify.h"
+#include "p2p_supplicant.h"
+
+
+/*
+ * DNS Header section is used only to calculate compression pointers, so the
+ * contents of this data does not matter, but the length needs to be reserved
+ * in the virtual packet.
+ */
+#define DNS_HEADER_LEN 12
+
+/*
+ * 27-octet in-memory packet from P2P specification containing two implied
+ * queries for _tcp.lcoal. PTR IN and _udp.local. PTR IN
+ */
+#define P2P_SD_IN_MEMORY_LEN 27
+
+static int p2p_sd_dns_uncompress_label(char **upos, char *uend, u8 *start,
+				       u8 **spos, const u8 *end)
+{
+	while (*spos < end) {
+		u8 val = ((*spos)[0] & 0xc0) >> 6;
+		int len;
+
+		if (val == 1 || val == 2) {
+			/* These are reserved values in RFC 1035 */
+			wpa_printf(MSG_DEBUG, "P2P: Invalid domain name "
+				   "sequence starting with 0x%x", val);
+			return -1;
+		}
+
+		if (val == 3) {
+			u16 offset;
+			u8 *spos_tmp;
+
+			/* Offset */
+			if (*spos + 2 > end) {
+				wpa_printf(MSG_DEBUG, "P2P: No room for full "
+					   "DNS offset field");
+				return -1;
+			}
+
+			offset = (((*spos)[0] & 0x3f) << 8) | (*spos)[1];
+			if (offset >= *spos - start) {
+				wpa_printf(MSG_DEBUG, "P2P: Invalid DNS "
+					   "pointer offset %u", offset);
+				return -1;
+			}
+
+			(*spos) += 2;
+			spos_tmp = start + offset;
+			return p2p_sd_dns_uncompress_label(upos, uend, start,
+							   &spos_tmp,
+							   *spos - 2);
+		}
+
+		/* Label */
+		len = (*spos)[0] & 0x3f;
+		if (len == 0)
+			return 0;
+
+		(*spos)++;
+		if (*spos + len > end) {
+			wpa_printf(MSG_DEBUG, "P2P: Invalid domain name "
+				   "sequence - no room for label with length "
+				   "%u", len);
+			return -1;
+		}
+
+		if (*upos + len + 2 > uend)
+			return -2;
+
+		os_memcpy(*upos, *spos, len);
+		*spos += len;
+		*upos += len;
+		(*upos)[0] = '.';
+		(*upos)++;
+		(*upos)[0] = '\0';
+	}
+
+	return 0;
+}
+
+
+/* Uncompress domain names per RFC 1035 using the P2P SD in-memory packet.
+ * Returns -1 on parsing error (invalid input sequence), -2 if output buffer is
+ * not large enough */
+static int p2p_sd_dns_uncompress(char *buf, size_t buf_len, const u8 *msg,
+				 size_t msg_len, size_t offset)
+{
+	/* 27-octet in-memory packet from P2P specification */
+	const char *prefix = "\x04_tcp\x05local\x00\x00\x0C\x00\x01"
+		"\x04_udp\xC0\x11\x00\x0C\x00\x01";
+	u8 *tmp, *end, *spos;
+	char *upos, *uend;
+	int ret = 0;
+
+	if (buf_len < 2)
+		return -1;
+	if (offset > msg_len)
+		return -1;
+
+	tmp = os_malloc(DNS_HEADER_LEN + P2P_SD_IN_MEMORY_LEN + msg_len);
+	if (tmp == NULL)
+		return -1;
+	spos = tmp + DNS_HEADER_LEN + P2P_SD_IN_MEMORY_LEN;
+	end = spos + msg_len;
+	spos += offset;
+
+	os_memset(tmp, 0, DNS_HEADER_LEN);
+	os_memcpy(tmp + DNS_HEADER_LEN, prefix, P2P_SD_IN_MEMORY_LEN);
+	os_memcpy(tmp + DNS_HEADER_LEN + P2P_SD_IN_MEMORY_LEN, msg, msg_len);
+
+	upos = buf;
+	uend = buf + buf_len;
+
+	ret = p2p_sd_dns_uncompress_label(&upos, uend, tmp, &spos, end);
+	if (ret) {
+		os_free(tmp);
+		return ret;
+	}
+
+	if (upos == buf) {
+		upos[0] = '.';
+		upos[1] = '\0';
+	} else if (upos[-1] == '.')
+		upos[-1] = '\0';
+
+	os_free(tmp);
+	return 0;
+}
+
+
+static struct p2p_srv_bonjour *
+wpas_p2p_service_get_bonjour(struct wpa_supplicant *wpa_s,
+			     const struct wpabuf *query)
+{
+	struct p2p_srv_bonjour *bsrv;
+	size_t len;
+
+	len = wpabuf_len(query);
+	dl_list_for_each(bsrv, &wpa_s->global->p2p_srv_bonjour,
+			 struct p2p_srv_bonjour, list) {
+		if (len == wpabuf_len(bsrv->query) &&
+		    os_memcmp(wpabuf_head(query), wpabuf_head(bsrv->query),
+			      len) == 0)
+			return bsrv;
+	}
+	return NULL;
+}
+
+
+static struct p2p_srv_upnp *
+wpas_p2p_service_get_upnp(struct wpa_supplicant *wpa_s, u8 version,
+			  const char *service)
+{
+	struct p2p_srv_upnp *usrv;
+
+	dl_list_for_each(usrv, &wpa_s->global->p2p_srv_upnp,
+			 struct p2p_srv_upnp, list) {
+		if (version == usrv->version &&
+		    os_strcmp(service, usrv->service) == 0)
+			return usrv;
+	}
+	return NULL;
+}
+
+
+static void wpas_sd_add_empty(struct wpabuf *resp, u8 srv_proto,
+			      u8 srv_trans_id, u8 status)
+{
+	u8 *len_pos;
+
+	if (wpabuf_tailroom(resp) < 5)
+		return;
+
+	/* Length (to be filled) */
+	len_pos = wpabuf_put(resp, 2);
+	wpabuf_put_u8(resp, srv_proto);
+	wpabuf_put_u8(resp, srv_trans_id);
+	/* Status Code */
+	wpabuf_put_u8(resp, status);
+	/* Response Data: empty */
+	WPA_PUT_LE16(len_pos, (u8 *) wpabuf_put(resp, 0) - len_pos - 2);
+}
+
+
+static void wpas_sd_add_proto_not_avail(struct wpabuf *resp, u8 srv_proto,
+					u8 srv_trans_id)
+{
+	wpas_sd_add_empty(resp, srv_proto, srv_trans_id,
+			  P2P_SD_PROTO_NOT_AVAILABLE);
+}
+
+
+static void wpas_sd_add_bad_request(struct wpabuf *resp, u8 srv_proto,
+				    u8 srv_trans_id)
+{
+	wpas_sd_add_empty(resp, srv_proto, srv_trans_id, P2P_SD_BAD_REQUEST);
+}
+
+
+static void wpas_sd_add_not_found(struct wpabuf *resp, u8 srv_proto,
+				  u8 srv_trans_id)
+{
+	wpas_sd_add_empty(resp, srv_proto, srv_trans_id,
+			  P2P_SD_REQUESTED_INFO_NOT_AVAILABLE);
+}
+
+
+static void wpas_sd_all_bonjour(struct wpa_supplicant *wpa_s,
+				struct wpabuf *resp, u8 srv_trans_id)
+{
+	struct p2p_srv_bonjour *bsrv;
+	u8 *len_pos;
+
+	wpa_printf(MSG_DEBUG, "P2P: SD Request for all Bonjour services");
+
+	if (dl_list_empty(&wpa_s->global->p2p_srv_bonjour)) {
+		wpa_printf(MSG_DEBUG, "P2P: Bonjour protocol not available");
+		return;
+	}
+
+	dl_list_for_each(bsrv, &wpa_s->global->p2p_srv_bonjour,
+			 struct p2p_srv_bonjour, list) {
+		if (wpabuf_tailroom(resp) <
+		    5 + wpabuf_len(bsrv->query) + wpabuf_len(bsrv->resp))
+			return;
+		/* Length (to be filled) */
+		len_pos = wpabuf_put(resp, 2);
+		wpabuf_put_u8(resp, P2P_SERV_BONJOUR);
+		wpabuf_put_u8(resp, srv_trans_id);
+		/* Status Code */
+		wpabuf_put_u8(resp, P2P_SD_SUCCESS);
+		wpa_hexdump_ascii(MSG_DEBUG, "P2P: Matching Bonjour service",
+				  wpabuf_head(bsrv->resp),
+				  wpabuf_len(bsrv->resp));
+		/* Response Data */
+		wpabuf_put_buf(resp, bsrv->query); /* Key */
+		wpabuf_put_buf(resp, bsrv->resp); /* Value */
+		WPA_PUT_LE16(len_pos, (u8 *) wpabuf_put(resp, 0) - len_pos -
+			     2);
+	}
+}
+
+
+static int match_bonjour_query(struct p2p_srv_bonjour *bsrv, const u8 *query,
+			       size_t query_len)
+{
+	char str_rx[256], str_srv[256];
+
+	if (query_len < 3 || wpabuf_len(bsrv->query) < 3)
+		return 0; /* Too short to include DNS Type and Version */
+	if (os_memcmp(query + query_len - 3,
+		      wpabuf_head_u8(bsrv->query) + wpabuf_len(bsrv->query) - 3,
+		      3) != 0)
+		return 0; /* Mismatch in DNS Type or Version */
+	if (query_len == wpabuf_len(bsrv->query) &&
+	    os_memcmp(query, wpabuf_head(bsrv->query), query_len - 3) == 0)
+		return 1; /* Binary match */
+
+	if (p2p_sd_dns_uncompress(str_rx, sizeof(str_rx), query, query_len - 3,
+				  0))
+		return 0; /* Failed to uncompress query */
+	if (p2p_sd_dns_uncompress(str_srv, sizeof(str_srv),
+				  wpabuf_head(bsrv->query),
+				  wpabuf_len(bsrv->query) - 3, 0))
+		return 0; /* Failed to uncompress service */
+
+	return os_strcmp(str_rx, str_srv) == 0;
+}
+
+
+static void wpas_sd_req_bonjour(struct wpa_supplicant *wpa_s,
+				struct wpabuf *resp, u8 srv_trans_id,
+				const u8 *query, size_t query_len)
+{
+	struct p2p_srv_bonjour *bsrv;
+	u8 *len_pos;
+	int matches = 0;
+
+	wpa_hexdump_ascii(MSG_DEBUG, "P2P: SD Request for Bonjour",
+			  query, query_len);
+	if (dl_list_empty(&wpa_s->global->p2p_srv_bonjour)) {
+		wpa_printf(MSG_DEBUG, "P2P: Bonjour protocol not available");
+		wpas_sd_add_proto_not_avail(resp, P2P_SERV_BONJOUR,
+					    srv_trans_id);
+		return;
+	}
+
+	if (query_len == 0) {
+		wpas_sd_all_bonjour(wpa_s, resp, srv_trans_id);
+		return;
+	}
+
+	dl_list_for_each(bsrv, &wpa_s->global->p2p_srv_bonjour,
+			 struct p2p_srv_bonjour, list) {
+		if (!match_bonjour_query(bsrv, query, query_len))
+			continue;
+
+		if (wpabuf_tailroom(resp) <
+		    5 + query_len + wpabuf_len(bsrv->resp))
+			return;
+
+		matches++;
+
+		/* Length (to be filled) */
+		len_pos = wpabuf_put(resp, 2);
+		wpabuf_put_u8(resp, P2P_SERV_BONJOUR);
+		wpabuf_put_u8(resp, srv_trans_id);
+
+		/* Status Code */
+		wpabuf_put_u8(resp, P2P_SD_SUCCESS);
+		wpa_hexdump_ascii(MSG_DEBUG, "P2P: Matching Bonjour service",
+				  wpabuf_head(bsrv->resp),
+				  wpabuf_len(bsrv->resp));
+
+		/* Response Data */
+		wpabuf_put_data(resp, query, query_len); /* Key */
+		wpabuf_put_buf(resp, bsrv->resp); /* Value */
+
+		WPA_PUT_LE16(len_pos, (u8 *) wpabuf_put(resp, 0) - len_pos - 2);
+	}
+
+	if (matches == 0) {
+		wpa_printf(MSG_DEBUG, "P2P: Requested Bonjour service not "
+			   "available");
+		if (wpabuf_tailroom(resp) < 5)
+			return;
+
+		/* Length (to be filled) */
+		len_pos = wpabuf_put(resp, 2);
+		wpabuf_put_u8(resp, P2P_SERV_BONJOUR);
+		wpabuf_put_u8(resp, srv_trans_id);
+
+		/* Status Code */
+		wpabuf_put_u8(resp, P2P_SD_REQUESTED_INFO_NOT_AVAILABLE);
+		/* Response Data: empty */
+		WPA_PUT_LE16(len_pos, (u8 *) wpabuf_put(resp, 0) - len_pos -
+			     2);
+	}
+}
+
+
+static void wpas_sd_all_upnp(struct wpa_supplicant *wpa_s,
+			     struct wpabuf *resp, u8 srv_trans_id)
+{
+	struct p2p_srv_upnp *usrv;
+	u8 *len_pos;
+
+	wpa_printf(MSG_DEBUG, "P2P: SD Request for all UPnP services");
+
+	if (dl_list_empty(&wpa_s->global->p2p_srv_upnp)) {
+		wpa_printf(MSG_DEBUG, "P2P: UPnP protocol not available");
+		return;
+	}
+
+	dl_list_for_each(usrv, &wpa_s->global->p2p_srv_upnp,
+			 struct p2p_srv_upnp, list) {
+		if (wpabuf_tailroom(resp) < 5 + 1 + os_strlen(usrv->service))
+			return;
+
+		/* Length (to be filled) */
+		len_pos = wpabuf_put(resp, 2);
+		wpabuf_put_u8(resp, P2P_SERV_UPNP);
+		wpabuf_put_u8(resp, srv_trans_id);
+
+		/* Status Code */
+		wpabuf_put_u8(resp, P2P_SD_SUCCESS);
+		/* Response Data */
+		wpabuf_put_u8(resp, usrv->version);
+		wpa_printf(MSG_DEBUG, "P2P: Matching UPnP Service: %s",
+			   usrv->service);
+		wpabuf_put_str(resp, usrv->service);
+		WPA_PUT_LE16(len_pos, (u8 *) wpabuf_put(resp, 0) - len_pos -
+			     2);
+	}
+}
+
+
+static void wpas_sd_req_upnp(struct wpa_supplicant *wpa_s,
+			     struct wpabuf *resp, u8 srv_trans_id,
+			     const u8 *query, size_t query_len)
+{
+	struct p2p_srv_upnp *usrv;
+	u8 *len_pos;
+	u8 version;
+	char *str;
+	int count = 0;
+
+	wpa_hexdump_ascii(MSG_DEBUG, "P2P: SD Request for UPnP",
+			  query, query_len);
+
+	if (dl_list_empty(&wpa_s->global->p2p_srv_upnp)) {
+		wpa_printf(MSG_DEBUG, "P2P: UPnP protocol not available");
+		wpas_sd_add_proto_not_avail(resp, P2P_SERV_UPNP,
+					    srv_trans_id);
+		return;
+	}
+
+	if (query_len == 0) {
+		wpas_sd_all_upnp(wpa_s, resp, srv_trans_id);
+		return;
+	}
+
+	if (wpabuf_tailroom(resp) < 5)
+		return;
+
+	/* Length (to be filled) */
+	len_pos = wpabuf_put(resp, 2);
+	wpabuf_put_u8(resp, P2P_SERV_UPNP);
+	wpabuf_put_u8(resp, srv_trans_id);
+
+	version = query[0];
+	str = os_malloc(query_len);
+	if (str == NULL)
+		return;
+	os_memcpy(str, query + 1, query_len - 1);
+	str[query_len - 1] = '\0';
+
+	dl_list_for_each(usrv, &wpa_s->global->p2p_srv_upnp,
+			 struct p2p_srv_upnp, list) {
+		if (version != usrv->version)
+			continue;
+
+		if (os_strcmp(str, "ssdp:all") != 0 &&
+		    os_strstr(usrv->service, str) == NULL)
+			continue;
+
+		if (wpabuf_tailroom(resp) < 2)
+			break;
+		if (count == 0) {
+			/* Status Code */
+			wpabuf_put_u8(resp, P2P_SD_SUCCESS);
+			/* Response Data */
+			wpabuf_put_u8(resp, version);
+		} else
+			wpabuf_put_u8(resp, ',');
+
+		count++;
+
+		wpa_printf(MSG_DEBUG, "P2P: Matching UPnP Service: %s",
+			   usrv->service);
+		if (wpabuf_tailroom(resp) < os_strlen(usrv->service))
+			break;
+		wpabuf_put_str(resp, usrv->service);
+	}
+	os_free(str);
+
+	if (count == 0) {
+		wpa_printf(MSG_DEBUG, "P2P: Requested UPnP service not "
+			   "available");
+		/* Status Code */
+		wpabuf_put_u8(resp, P2P_SD_REQUESTED_INFO_NOT_AVAILABLE);
+		/* Response Data: empty */
+	}
+
+	WPA_PUT_LE16(len_pos, (u8 *) wpabuf_put(resp, 0) - len_pos - 2);
+}
+
+
+#ifdef CONFIG_WIFI_DISPLAY
+static void wpas_sd_req_wfd(struct wpa_supplicant *wpa_s,
+			    struct wpabuf *resp, u8 srv_trans_id,
+			    const u8 *query, size_t query_len)
+{
+	const u8 *pos;
+	u8 role;
+	u8 *len_pos;
+
+	wpa_hexdump(MSG_DEBUG, "P2P: SD Request for WFD", query, query_len);
+
+	if (!wpa_s->global->wifi_display) {
+		wpa_printf(MSG_DEBUG, "P2P: WFD protocol not available");
+		wpas_sd_add_proto_not_avail(resp, P2P_SERV_WIFI_DISPLAY,
+					    srv_trans_id);
+		return;
+	}
+
+	if (query_len < 1) {
+		wpa_printf(MSG_DEBUG, "P2P: Missing WFD Requested Device "
+			   "Role");
+		return;
+	}
+
+	if (wpabuf_tailroom(resp) < 5)
+		return;
+
+	pos = query;
+	role = *pos++;
+	wpa_printf(MSG_DEBUG, "P2P: WSD for device role 0x%x", role);
+
+	/* TODO: role specific handling */
+
+	/* Length (to be filled) */
+	len_pos = wpabuf_put(resp, 2);
+	wpabuf_put_u8(resp, P2P_SERV_WIFI_DISPLAY);
+	wpabuf_put_u8(resp, srv_trans_id);
+	wpabuf_put_u8(resp, P2P_SD_SUCCESS); /* Status Code */
+
+	while (pos < query + query_len) {
+		if (*pos < MAX_WFD_SUBELEMS &&
+		    wpa_s->global->wfd_subelem[*pos] &&
+		    wpabuf_tailroom(resp) >=
+		    wpabuf_len(wpa_s->global->wfd_subelem[*pos])) {
+			wpa_printf(MSG_DEBUG, "P2P: Add WSD response "
+				   "subelement %u", *pos);
+			wpabuf_put_buf(resp, wpa_s->global->wfd_subelem[*pos]);
+		}
+		pos++;
+	}
+
+	WPA_PUT_LE16(len_pos, (u8 *) wpabuf_put(resp, 0) - len_pos - 2);
+}
+#endif /* CONFIG_WIFI_DISPLAY */
+
+
+static int find_p2ps_substr(struct p2ps_advertisement *adv_data,
+			    const u8 *needle, size_t needle_len)
+{
+	const u8 *haystack = (const u8 *) adv_data->svc_info;
+	size_t haystack_len, i;
+
+	/* Allow search term to be empty */
+	if (!needle || !needle_len)
+		return 1;
+
+	if (!haystack)
+		return 0;
+
+	haystack_len = os_strlen(adv_data->svc_info);
+	for (i = 0; i < haystack_len; i++) {
+		if (haystack_len - i < needle_len)
+			break;
+		if (os_memcmp(haystack + i, needle, needle_len) == 0)
+			return 1;
+	}
+
+	return 0;
+}
+
+
+static void wpas_sd_req_asp(struct wpa_supplicant *wpa_s,
+			    struct wpabuf *resp, u8 srv_trans_id,
+			    const u8 *query, size_t query_len)
+{
+	struct p2ps_advertisement *adv_data;
+	const u8 *svc = &query[1];
+	const u8 *info = NULL;
+	size_t svc_len = query[0];
+	size_t info_len = 0;
+	int prefix = 0;
+	u8 *count_pos = NULL;
+	u8 *len_pos = NULL;
+
+	wpa_hexdump(MSG_DEBUG, "P2P: SD Request for ASP", query, query_len);
+
+	if (!wpa_s->global->p2p) {
+		wpa_printf(MSG_DEBUG, "P2P: ASP protocol not available");
+		wpas_sd_add_proto_not_avail(resp, P2P_SERV_P2PS, srv_trans_id);
+		return;
+	}
+
+	/* Info block is optional */
+	if (svc_len + 1 < query_len) {
+		info = &svc[svc_len];
+		info_len = *info++;
+	}
+
+	/* Range check length of svc string and info block */
+	if (svc_len + (info_len ? info_len + 2 : 1) > query_len) {
+		wpa_printf(MSG_DEBUG, "P2P: ASP bad request");
+		wpas_sd_add_bad_request(resp, P2P_SERV_P2PS, srv_trans_id);
+		return;
+	}
+
+	/* Detect and correct for prefix search */
+	if (svc_len && svc[svc_len - 1] == '*') {
+		prefix = 1;
+		svc_len--;
+	}
+
+	for (adv_data = p2p_get_p2ps_adv_list(wpa_s->global->p2p);
+	     adv_data; adv_data = adv_data->next) {
+		/* If not a prefix match, reject length mismatches */
+		if (!prefix && svc_len != os_strlen(adv_data->svc_name))
+			continue;
+
+		/* Search each service for request */
+		if (os_memcmp(adv_data->svc_name, svc, svc_len) == 0 &&
+		    find_p2ps_substr(adv_data, info, info_len)) {
+			size_t len = os_strlen(adv_data->svc_name);
+			size_t svc_info_len = 0;
+
+			if (adv_data->svc_info)
+				svc_info_len = os_strlen(adv_data->svc_info);
+
+			if (len > 0xff || svc_info_len > 0xffff)
+				return;
+
+			/* Length & Count to be filled as we go */
+			if (!len_pos && !count_pos) {
+				if (wpabuf_tailroom(resp) <
+				    len + svc_info_len + 16)
+					return;
+
+				len_pos = wpabuf_put(resp, 2);
+				wpabuf_put_u8(resp, P2P_SERV_P2PS);
+				wpabuf_put_u8(resp, srv_trans_id);
+				/* Status Code */
+				wpabuf_put_u8(resp, P2P_SD_SUCCESS);
+				count_pos = wpabuf_put(resp, 1);
+				*count_pos = 0;
+			} else if (wpabuf_tailroom(resp) <
+				   len + svc_info_len + 10)
+				return;
+
+			if (svc_info_len) {
+				wpa_printf(MSG_DEBUG,
+					   "P2P: Add Svc: %s info: %s",
+					   adv_data->svc_name,
+					   adv_data->svc_info);
+			} else {
+				wpa_printf(MSG_DEBUG, "P2P: Add Svc: %s",
+					   adv_data->svc_name);
+			}
+
+			/* Advertisement ID */
+			wpabuf_put_le32(resp, adv_data->id);
+
+			/* Config Methods */
+			wpabuf_put_be16(resp, adv_data->config_methods);
+
+			/* Service Name */
+			wpabuf_put_u8(resp, (u8) len);
+			wpabuf_put_data(resp, adv_data->svc_name, len);
+
+			/* Service State */
+			wpabuf_put_u8(resp, adv_data->state);
+
+			/* Service Information */
+			wpabuf_put_le16(resp, (u16) svc_info_len);
+			wpabuf_put_data(resp, adv_data->svc_info, svc_info_len);
+
+			/* Update length and count */
+			(*count_pos)++;
+			WPA_PUT_LE16(len_pos,
+				     (u8 *) wpabuf_put(resp, 0) - len_pos - 2);
+		}
+	}
+
+	/* Return error if no matching svc found */
+	if (count_pos == NULL) {
+		wpa_printf(MSG_DEBUG, "P2P: ASP service not found");
+		wpas_sd_add_not_found(resp, P2P_SERV_P2PS, srv_trans_id);
+	}
+}
+
+
+static void wpas_sd_all_asp(struct wpa_supplicant *wpa_s,
+			    struct wpabuf *resp, u8 srv_trans_id)
+{
+	/* Query data to add all P2PS advertisements:
+	 *  - Service name length: 1
+	 *  - Service name: '*'
+	 *  - Service Information Request Length: 0
+	 */
+	const u8 q[] = { 1, (const u8) '*', 0 };
+
+	if (p2p_get_p2ps_adv_list(wpa_s->global->p2p))
+		wpas_sd_req_asp(wpa_s, resp, srv_trans_id, q, sizeof(q));
+}
+
+
+void wpas_sd_request(void *ctx, int freq, const u8 *sa, u8 dialog_token,
+		     u16 update_indic, const u8 *tlvs, size_t tlvs_len)
+{
+	struct wpa_supplicant *wpa_s = ctx;
+	const u8 *pos = tlvs;
+	const u8 *end = tlvs + tlvs_len;
+	const u8 *tlv_end;
+	u16 slen;
+	struct wpabuf *resp;
+	u8 srv_proto, srv_trans_id;
+	size_t buf_len;
+	char *buf;
+
+	wpa_hexdump(MSG_MSGDUMP, "P2P: Service Discovery Request TLVs",
+		    tlvs, tlvs_len);
+	buf_len = 2 * tlvs_len + 1;
+	buf = os_malloc(buf_len);
+	if (buf) {
+		wpa_snprintf_hex(buf, buf_len, tlvs, tlvs_len);
+		wpa_msg_ctrl(wpa_s, MSG_INFO, P2P_EVENT_SERV_DISC_REQ "%d "
+			     MACSTR " %u %u %s",
+			     freq, MAC2STR(sa), dialog_token, update_indic,
+			     buf);
+		os_free(buf);
+	}
+
+	if (wpa_s->p2p_sd_over_ctrl_iface) {
+		wpas_notify_p2p_sd_request(wpa_s, freq, sa, dialog_token,
+					   update_indic, tlvs, tlvs_len);
+		return; /* to be processed by an external program */
+	}
+
+	resp = wpabuf_alloc(10000);
+	if (resp == NULL)
+		return;
+
+	while (pos + 1 < end) {
+		wpa_printf(MSG_DEBUG, "P2P: Service Request TLV");
+		slen = WPA_GET_LE16(pos);
+		pos += 2;
+		if (pos + slen > end || slen < 2) {
+			wpa_printf(MSG_DEBUG, "P2P: Unexpected Query Data "
+				   "length");
+			wpabuf_free(resp);
+			return;
+		}
+		tlv_end = pos + slen;
+
+		srv_proto = *pos++;
+		wpa_printf(MSG_DEBUG, "P2P: Service Protocol Type %u",
+			   srv_proto);
+		srv_trans_id = *pos++;
+		wpa_printf(MSG_DEBUG, "P2P: Service Transaction ID %u",
+			   srv_trans_id);
+
+		wpa_hexdump(MSG_MSGDUMP, "P2P: Query Data",
+			    pos, tlv_end - pos);
+
+
+		if (wpa_s->force_long_sd) {
+			wpa_printf(MSG_DEBUG, "P2P: SD test - force long "
+				   "response");
+			wpas_sd_all_bonjour(wpa_s, resp, srv_trans_id);
+			wpas_sd_all_upnp(wpa_s, resp, srv_trans_id);
+			wpas_sd_all_asp(wpa_s, resp, srv_trans_id);
+			goto done;
+		}
+
+		switch (srv_proto) {
+		case P2P_SERV_ALL_SERVICES:
+			wpa_printf(MSG_DEBUG, "P2P: Service Discovery Request "
+				   "for all services");
+			if (dl_list_empty(&wpa_s->global->p2p_srv_upnp) &&
+			    dl_list_empty(&wpa_s->global->p2p_srv_bonjour) &&
+			    !p2p_get_p2ps_adv_list(wpa_s->global->p2p)) {
+				wpa_printf(MSG_DEBUG, "P2P: No service "
+					   "discovery protocols available");
+				wpas_sd_add_proto_not_avail(
+					resp, P2P_SERV_ALL_SERVICES,
+					srv_trans_id);
+				break;
+			}
+			wpas_sd_all_bonjour(wpa_s, resp, srv_trans_id);
+			wpas_sd_all_upnp(wpa_s, resp, srv_trans_id);
+			wpas_sd_all_asp(wpa_s, resp, srv_trans_id);
+			break;
+		case P2P_SERV_BONJOUR:
+			wpas_sd_req_bonjour(wpa_s, resp, srv_trans_id,
+					    pos, tlv_end - pos);
+			break;
+		case P2P_SERV_UPNP:
+			wpas_sd_req_upnp(wpa_s, resp, srv_trans_id,
+					 pos, tlv_end - pos);
+			break;
+#ifdef CONFIG_WIFI_DISPLAY
+		case P2P_SERV_WIFI_DISPLAY:
+			wpas_sd_req_wfd(wpa_s, resp, srv_trans_id,
+					pos, tlv_end - pos);
+			break;
+#endif /* CONFIG_WIFI_DISPLAY */
+		case P2P_SERV_P2PS:
+			wpas_sd_req_asp(wpa_s, resp, srv_trans_id,
+					pos, tlv_end - pos);
+			break;
+		default:
+			wpa_printf(MSG_DEBUG, "P2P: Unavailable service "
+				   "protocol %u", srv_proto);
+			wpas_sd_add_proto_not_avail(resp, srv_proto,
+						    srv_trans_id);
+			break;
+		}
+
+		pos = tlv_end;
+	}
+
+done:
+	wpas_notify_p2p_sd_request(wpa_s, freq, sa, dialog_token,
+				   update_indic, tlvs, tlvs_len);
+
+	wpas_p2p_sd_response(wpa_s, freq, sa, dialog_token, resp);
+
+	wpabuf_free(resp);
+}
+
+
+static void wpas_sd_p2ps_serv_response(struct wpa_supplicant *wpa_s,
+				       const u8 *sa, u8 srv_trans_id,
+				       const u8 *pos, const u8 *tlv_end)
+{
+	u8 left = *pos++;
+	u32 adv_id;
+	u8 svc_status;
+	u16 config_methods;
+	char svc_str[256];
+
+	while (left-- && pos < tlv_end) {
+		char *buf = NULL;
+		size_t buf_len;
+		u8 svc_len;
+
+		/* Sanity check fixed length+svc_str */
+		if (pos + 6 >= tlv_end)
+			break;
+		svc_len = pos[6];
+		if (pos + svc_len + 10 > tlv_end)
+			break;
+
+		/* Advertisement ID */
+		adv_id = WPA_GET_LE32(pos);
+		pos += sizeof(u32);
+
+		/* Config Methods */
+		config_methods = WPA_GET_BE16(pos);
+		pos += sizeof(u16);
+
+		/* Service Name */
+		pos++; /* svc_len */
+		os_memcpy(svc_str, pos, svc_len);
+		svc_str[svc_len] = '\0';
+		pos += svc_len;
+
+		/* Service Status */
+		svc_status = *pos++;
+
+		/* Service Information Length */
+		buf_len = WPA_GET_LE16(pos);
+		pos += sizeof(u16);
+
+		/* Sanity check buffer length */
+		if (buf_len > (unsigned int) (tlv_end - pos))
+			break;
+
+		if (buf_len) {
+			buf = os_zalloc(2 * buf_len + 1);
+			if (buf) {
+				utf8_escape((const char *) pos, buf_len, buf,
+					    2 * buf_len + 1);
+			}
+		}
+
+		pos += buf_len;
+
+		if (buf) {
+			wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_SERV_ASP_RESP
+				       MACSTR " %x %x %x %x %s '%s'",
+				       MAC2STR(sa), srv_trans_id, adv_id,
+				       svc_status, config_methods, svc_str,
+				       buf);
+			os_free(buf);
+		} else {
+			wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_SERV_ASP_RESP
+				       MACSTR " %x %x %x %x %s",
+				       MAC2STR(sa), srv_trans_id, adv_id,
+				       svc_status, config_methods, svc_str);
+		}
+	}
+}
+
+
+void wpas_sd_response(void *ctx, const u8 *sa, u16 update_indic,
+		      const u8 *tlvs, size_t tlvs_len)
+{
+	struct wpa_supplicant *wpa_s = ctx;
+	const u8 *pos = tlvs;
+	const u8 *end = tlvs + tlvs_len;
+	const u8 *tlv_end;
+	u16 slen;
+	size_t buf_len;
+	char *buf;
+
+	wpa_hexdump(MSG_MSGDUMP, "P2P: Service Discovery Response TLVs",
+		    tlvs, tlvs_len);
+	if (tlvs_len > 1500) {
+		/* TODO: better way for handling this */
+		wpa_msg_ctrl(wpa_s, MSG_INFO,
+			     P2P_EVENT_SERV_DISC_RESP MACSTR
+			     " %u <long response: %u bytes>",
+			     MAC2STR(sa), update_indic,
+			     (unsigned int) tlvs_len);
+	} else {
+		buf_len = 2 * tlvs_len + 1;
+		buf = os_malloc(buf_len);
+		if (buf) {
+			wpa_snprintf_hex(buf, buf_len, tlvs, tlvs_len);
+			wpa_msg_ctrl(wpa_s, MSG_INFO,
+				     P2P_EVENT_SERV_DISC_RESP MACSTR " %u %s",
+				     MAC2STR(sa), update_indic, buf);
+			os_free(buf);
+		}
+	}
+
+	while (pos < end) {
+		u8 srv_proto, srv_trans_id, status;
+
+		wpa_printf(MSG_DEBUG, "P2P: Service Response TLV");
+		slen = WPA_GET_LE16(pos);
+		pos += 2;
+		if (pos + slen > end || slen < 3) {
+			wpa_printf(MSG_DEBUG, "P2P: Unexpected Response Data "
+				   "length");
+			return;
+		}
+		tlv_end = pos + slen;
+
+		srv_proto = *pos++;
+		wpa_printf(MSG_DEBUG, "P2P: Service Protocol Type %u",
+			   srv_proto);
+		srv_trans_id = *pos++;
+		wpa_printf(MSG_DEBUG, "P2P: Service Transaction ID %u",
+			   srv_trans_id);
+		status = *pos++;
+		wpa_printf(MSG_DEBUG, "P2P: Status Code ID %u",
+			   status);
+
+		wpa_hexdump(MSG_MSGDUMP, "P2P: Response Data",
+			    pos, tlv_end - pos);
+
+		if (srv_proto == P2P_SERV_P2PS && pos < tlv_end) {
+			wpas_sd_p2ps_serv_response(wpa_s, sa, srv_trans_id,
+						   pos, tlv_end);
+		}
+
+		pos = tlv_end;
+	}
+
+	wpas_notify_p2p_sd_response(wpa_s, sa, update_indic, tlvs, tlvs_len);
+}
+
+
+u64 wpas_p2p_sd_request(struct wpa_supplicant *wpa_s, const u8 *dst,
+			const struct wpabuf *tlvs)
+{
+	if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
+		return 0;
+	return (uintptr_t) p2p_sd_request(wpa_s->global->p2p, dst, tlvs);
+}
+
+
+u64 wpas_p2p_sd_request_upnp(struct wpa_supplicant *wpa_s, const u8 *dst,
+			     u8 version, const char *query)
+{
+	struct wpabuf *tlvs;
+	u64 ret;
+
+	tlvs = wpabuf_alloc(2 + 1 + 1 + 1 + os_strlen(query));
+	if (tlvs == NULL)
+		return 0;
+	wpabuf_put_le16(tlvs, 1 + 1 + 1 + os_strlen(query));
+	wpabuf_put_u8(tlvs, P2P_SERV_UPNP); /* Service Protocol Type */
+	wpabuf_put_u8(tlvs, 1); /* Service Transaction ID */
+	wpabuf_put_u8(tlvs, version);
+	wpabuf_put_str(tlvs, query);
+	ret = wpas_p2p_sd_request(wpa_s, dst, tlvs);
+	wpabuf_free(tlvs);
+	return ret;
+}
+
+
+u64 wpas_p2p_sd_request_asp(struct wpa_supplicant *wpa_s, const u8 *dst, u8 id,
+			    const char *svc_str, const char *info_substr)
+{
+	struct wpabuf *tlvs;
+	size_t plen, svc_len, substr_len = 0;
+	u64 ret;
+
+	svc_len = os_strlen(svc_str);
+	if (info_substr)
+		substr_len = os_strlen(info_substr);
+
+	if (svc_len > 0xff || substr_len > 0xff)
+		return 0;
+
+	plen = 1 + 1 + 1 + svc_len + 1 + substr_len;
+	tlvs = wpabuf_alloc(2 + plen);
+	if (tlvs == NULL)
+		return 0;
+
+	wpabuf_put_le16(tlvs, plen);
+	wpabuf_put_u8(tlvs, P2P_SERV_P2PS);
+	wpabuf_put_u8(tlvs, id); /* Service Transaction ID */
+	wpabuf_put_u8(tlvs, (u8) svc_len); /* Service String Length */
+	wpabuf_put_data(tlvs, svc_str, svc_len);
+	wpabuf_put_u8(tlvs, (u8) substr_len); /* Info Substring Length */
+	wpabuf_put_data(tlvs, info_substr, substr_len);
+	ret = wpas_p2p_sd_request(wpa_s, dst, tlvs);
+	wpabuf_free(tlvs);
+
+	return ret;
+}
+
+
+#ifdef CONFIG_WIFI_DISPLAY
+
+static u64 wpas_p2p_sd_request_wfd(struct wpa_supplicant *wpa_s, const u8 *dst,
+				   const struct wpabuf *tlvs)
+{
+	if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
+		return 0;
+	return (uintptr_t) p2p_sd_request_wfd(wpa_s->global->p2p, dst, tlvs);
+}
+
+
+#define MAX_WFD_SD_SUBELEMS 20
+
+static void wfd_add_sd_req_role(struct wpabuf *tlvs, u8 id, u8 role,
+				const char *subelems)
+{
+	u8 *len;
+	const char *pos;
+	int val;
+	int count = 0;
+
+	len = wpabuf_put(tlvs, 2);
+	wpabuf_put_u8(tlvs, P2P_SERV_WIFI_DISPLAY); /* Service Protocol Type */
+	wpabuf_put_u8(tlvs, id); /* Service Transaction ID */
+
+	wpabuf_put_u8(tlvs, role);
+
+	pos = subelems;
+	while (*pos) {
+		val = atoi(pos);
+		if (val >= 0 && val < 256) {
+			wpabuf_put_u8(tlvs, val);
+			count++;
+			if (count == MAX_WFD_SD_SUBELEMS)
+				break;
+		}
+		pos = os_strchr(pos + 1, ',');
+		if (pos == NULL)
+			break;
+		pos++;
+	}
+
+	WPA_PUT_LE16(len, (u8 *) wpabuf_put(tlvs, 0) - len - 2);
+}
+
+
+u64 wpas_p2p_sd_request_wifi_display(struct wpa_supplicant *wpa_s,
+				     const u8 *dst, const char *role)
+{
+	struct wpabuf *tlvs;
+	u64 ret;
+	const char *subelems;
+	u8 id = 1;
+
+	subelems = os_strchr(role, ' ');
+	if (subelems == NULL)
+		return 0;
+	subelems++;
+
+	tlvs = wpabuf_alloc(4 * (2 + 1 + 1 + 1 + MAX_WFD_SD_SUBELEMS));
+	if (tlvs == NULL)
+		return 0;
+
+	if (os_strstr(role, "[source]"))
+		wfd_add_sd_req_role(tlvs, id++, 0x00, subelems);
+	if (os_strstr(role, "[pri-sink]"))
+		wfd_add_sd_req_role(tlvs, id++, 0x01, subelems);
+	if (os_strstr(role, "[sec-sink]"))
+		wfd_add_sd_req_role(tlvs, id++, 0x02, subelems);
+	if (os_strstr(role, "[source+sink]"))
+		wfd_add_sd_req_role(tlvs, id++, 0x03, subelems);
+
+	ret = wpas_p2p_sd_request_wfd(wpa_s, dst, tlvs);
+	wpabuf_free(tlvs);
+	return ret;
+}
+
+#endif /* CONFIG_WIFI_DISPLAY */
+
+
+int wpas_p2p_sd_cancel_request(struct wpa_supplicant *wpa_s, u64 req)
+{
+	if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
+		return -1;
+	return p2p_sd_cancel_request(wpa_s->global->p2p,
+				     (void *) (uintptr_t) req);
+}
+
+
+void wpas_p2p_sd_response(struct wpa_supplicant *wpa_s, int freq,
+			  const u8 *dst, u8 dialog_token,
+			  const struct wpabuf *resp_tlvs)
+{
+	if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
+		return;
+	p2p_sd_response(wpa_s->global->p2p, freq, dst, dialog_token,
+			resp_tlvs);
+}
+
+
+void wpas_p2p_sd_service_update(struct wpa_supplicant *wpa_s)
+{
+	if (wpa_s->global->p2p)
+		p2p_sd_service_update(wpa_s->global->p2p);
+}
+
+
+static void wpas_p2p_srv_bonjour_free(struct p2p_srv_bonjour *bsrv)
+{
+	dl_list_del(&bsrv->list);
+	wpabuf_free(bsrv->query);
+	wpabuf_free(bsrv->resp);
+	os_free(bsrv);
+}
+
+
+static void wpas_p2p_srv_upnp_free(struct p2p_srv_upnp *usrv)
+{
+	dl_list_del(&usrv->list);
+	os_free(usrv->service);
+	os_free(usrv);
+}
+
+
+void wpas_p2p_service_flush(struct wpa_supplicant *wpa_s)
+{
+	struct p2p_srv_bonjour *bsrv, *bn;
+	struct p2p_srv_upnp *usrv, *un;
+
+	dl_list_for_each_safe(bsrv, bn, &wpa_s->global->p2p_srv_bonjour,
+			      struct p2p_srv_bonjour, list)
+		wpas_p2p_srv_bonjour_free(bsrv);
+
+	dl_list_for_each_safe(usrv, un, &wpa_s->global->p2p_srv_upnp,
+			      struct p2p_srv_upnp, list)
+		wpas_p2p_srv_upnp_free(usrv);
+
+	wpas_p2p_service_flush_asp(wpa_s);
+	wpas_p2p_sd_service_update(wpa_s);
+}
+
+
+int wpas_p2p_service_p2ps_id_exists(struct wpa_supplicant *wpa_s, u32 adv_id)
+{
+	if (adv_id == 0)
+		return 1;
+
+	if (p2p_service_p2ps_id(wpa_s->global->p2p, adv_id))
+		return 1;
+
+	return 0;
+}
+
+
+int wpas_p2p_service_del_asp(struct wpa_supplicant *wpa_s, u32 adv_id)
+{
+	int ret;
+
+	ret = p2p_service_del_asp(wpa_s->global->p2p, adv_id);
+	if (ret == 0)
+		wpas_p2p_sd_service_update(wpa_s);
+	return ret;
+}
+
+
+int wpas_p2p_service_add_asp(struct wpa_supplicant *wpa_s,
+			     int auto_accept, u32 adv_id,
+			     const char *adv_str, u8 svc_state,
+			     u16 config_methods, const char *svc_info)
+{
+	int ret;
+
+	ret = p2p_service_add_asp(wpa_s->global->p2p, auto_accept, adv_id,
+				  adv_str, svc_state, config_methods,
+				  svc_info);
+	if (ret == 0)
+		wpas_p2p_sd_service_update(wpa_s);
+	return ret;
+}
+
+
+void wpas_p2p_service_flush_asp(struct wpa_supplicant *wpa_s)
+{
+	p2p_service_flush_asp(wpa_s->global->p2p);
+}
+
+
+int wpas_p2p_service_add_bonjour(struct wpa_supplicant *wpa_s,
+				 struct wpabuf *query, struct wpabuf *resp)
+{
+	struct p2p_srv_bonjour *bsrv;
+
+	bsrv = os_zalloc(sizeof(*bsrv));
+	if (bsrv == NULL)
+		return -1;
+	bsrv->query = query;
+	bsrv->resp = resp;
+	dl_list_add(&wpa_s->global->p2p_srv_bonjour, &bsrv->list);
+
+	wpas_p2p_sd_service_update(wpa_s);
+	return 0;
+}
+
+
+int wpas_p2p_service_del_bonjour(struct wpa_supplicant *wpa_s,
+				 const struct wpabuf *query)
+{
+	struct p2p_srv_bonjour *bsrv;
+
+	bsrv = wpas_p2p_service_get_bonjour(wpa_s, query);
+	if (bsrv == NULL)
+		return -1;
+	wpas_p2p_srv_bonjour_free(bsrv);
+	wpas_p2p_sd_service_update(wpa_s);
+	return 0;
+}
+
+
+int wpas_p2p_service_add_upnp(struct wpa_supplicant *wpa_s, u8 version,
+			      const char *service)
+{
+	struct p2p_srv_upnp *usrv;
+
+	if (wpas_p2p_service_get_upnp(wpa_s, version, service))
+		return 0; /* Already listed */
+	usrv = os_zalloc(sizeof(*usrv));
+	if (usrv == NULL)
+		return -1;
+	usrv->version = version;
+	usrv->service = os_strdup(service);
+	if (usrv->service == NULL) {
+		os_free(usrv);
+		return -1;
+	}
+	dl_list_add(&wpa_s->global->p2p_srv_upnp, &usrv->list);
+
+	wpas_p2p_sd_service_update(wpa_s);
+	return 0;
+}
+
+
+int wpas_p2p_service_del_upnp(struct wpa_supplicant *wpa_s, u8 version,
+			      const char *service)
+{
+	struct p2p_srv_upnp *usrv;
+
+	usrv = wpas_p2p_service_get_upnp(wpa_s, version, service);
+	if (usrv == NULL)
+		return -1;
+	wpas_p2p_srv_upnp_free(usrv);
+	wpas_p2p_sd_service_update(wpa_s);
+	return 0;
+}
diff --git a/wpa_supplicant/preauth_test.c b/wpa_supplicant/preauth_test.c
index ed57085..f4bba98 100644
--- a/wpa_supplicant/preauth_test.c
+++ b/wpa_supplicant/preauth_test.c
@@ -27,7 +27,7 @@
 #include "drivers/driver.h"
 
 
-struct wpa_driver_ops *wpa_drivers[] = { NULL };
+const struct wpa_driver_ops *const wpa_drivers[] = { NULL };
 
 
 struct preauth_test_data {
diff --git a/wpa_supplicant/scan.c b/wpa_supplicant/scan.c
index debceb9..e81465c 100644
--- a/wpa_supplicant/scan.c
+++ b/wpa_supplicant/scan.c
@@ -22,6 +22,7 @@
 #include "notify.h"
 #include "bss.h"
 #include "scan.h"
+#include "mesh.h"
 
 
 static void wpa_supplicant_gen_assoc_event(struct wpa_supplicant *wpa_s)
@@ -95,6 +96,10 @@
 {
 	struct wpa_ssid *ssid = wpa_s->conf->ssid;
 	int count = 0, disabled = 0;
+
+	if (wpa_s->p2p_mgmt)
+		return 0; /* no normal network profiles on p2p_mgmt interface */
+
 	while (ssid) {
 		if (!wpas_network_disabled(wpa_s, ssid))
 			count++;
@@ -167,15 +172,34 @@
 
 	wpa_supplicant_notify_scanning(wpa_s, 1);
 
-	if (wpa_s->clear_driver_scan_cache)
+	if (wpa_s->clear_driver_scan_cache) {
+		wpa_printf(MSG_DEBUG,
+			   "Request driver to clear scan cache due to local BSS flush");
 		params->only_new_results = 1;
+	}
 	ret = wpa_drv_scan(wpa_s, params);
 	wpa_scan_free_params(params);
 	work->ctx = NULL;
 	if (ret) {
+		int retry = wpa_s->last_scan_req != MANUAL_SCAN_REQ;
+
+		if (wpa_s->disconnected)
+			retry = 0;
+
 		wpa_supplicant_notify_scanning(wpa_s, 0);
 		wpas_notify_scan_done(wpa_s, 0);
+		if (wpa_s->wpa_state == WPA_SCANNING)
+			wpa_supplicant_set_state(wpa_s,
+						 wpa_s->scan_prev_wpa_state);
+		wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_SCAN_FAILED "ret=%d%s",
+			ret, retry ? " retry=1" : "");
 		radio_work_done(work);
+
+		if (retry) {
+			/* Restore scan_req since we will try to scan again */
+			wpa_s->scan_req = wpa_s->last_scan_req;
+			wpa_supplicant_req_scan(wpa_s, 1, 0);
+		}
 		return;
 	}
 
@@ -291,7 +315,7 @@
 	}
 	if (count == 0)
 		return NULL;
-	ssids = os_zalloc(count * sizeof(struct wpa_driver_scan_filter));
+	ssids = os_calloc(count, sizeof(struct wpa_driver_scan_filter));
 	if (ssids == NULL)
 		return NULL;
 
@@ -319,7 +343,7 @@
 			wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Scan only GO "
 				"preferred frequency %d MHz",
 				wpa_s->go_params->freq);
-			params->freqs = os_zalloc(2 * sizeof(int));
+			params->freqs = os_calloc(2, sizeof(int));
 			if (params->freqs)
 				params->freqs[0] = wpa_s->go_params->freq;
 		} else if (wpa_s->p2p_in_provisioning < 8 &&
@@ -343,7 +367,7 @@
 		    wpa_s->p2p_invite_go_freq > 0) {
 			wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Scan only GO preferred frequency %d MHz during invitation",
 				wpa_s->p2p_invite_go_freq);
-			params->freqs = os_zalloc(2 * sizeof(int));
+			params->freqs = os_calloc(2, sizeof(int));
 			if (params->freqs)
 				params->freqs[0] = wpa_s->p2p_invite_go_freq;
 		}
@@ -369,7 +393,7 @@
 		 */
 		wpa_dbg(wpa_s, MSG_DEBUG, "WPS: Scan only frequency %u MHz "
 			"that was used during provisioning", wpa_s->wps_freq);
-		params->freqs = os_zalloc(2 * sizeof(int));
+		params->freqs = os_calloc(2, sizeof(int));
 		if (params->freqs)
 			params->freqs[0] = wpa_s->wps_freq;
 		wpa_s->after_wps--;
@@ -381,7 +405,7 @@
 		/* Optimize provisioning scan based on already known channel */
 		wpa_dbg(wpa_s, MSG_DEBUG, "WPS: Scan only frequency %u MHz",
 			wpa_s->wps_freq);
-		params->freqs = os_zalloc(2 * sizeof(int));
+		params->freqs = os_calloc(2, sizeof(int));
 		if (params->freqs)
 			params->freqs[0] = wpa_s->wps_freq;
 		wpa_s->known_wps_freq = 0; /* only do this once */
@@ -394,22 +418,6 @@
 static void wpas_add_interworking_elements(struct wpa_supplicant *wpa_s,
 					   struct wpabuf *buf)
 {
-	if (wpa_s->conf->interworking == 0)
-		return;
-
-	wpabuf_put_u8(buf, WLAN_EID_EXT_CAPAB);
-	wpabuf_put_u8(buf, 6);
-	wpabuf_put_u8(buf, 0x00);
-	wpabuf_put_u8(buf, 0x00);
-	wpabuf_put_u8(buf, 0x00);
-	wpabuf_put_u8(buf, 0x80); /* Bit 31 - Interworking */
-	wpabuf_put_u8(buf, 0x00);
-#ifdef CONFIG_HS20
-	wpabuf_put_u8(buf, 0x40); /* Bit 46 - WNM-Notification */
-#else /* CONFIG_HS20 */
-	wpabuf_put_u8(buf, 0x00);
-#endif /* CONFIG_HS20 */
-
 	wpabuf_put_u8(buf, WLAN_EID_INTERWORKING);
 	wpabuf_put_u8(buf, is_zero_ether_addr(wpa_s->conf->hessid) ? 1 :
 		      1 + ETH_ALEN);
@@ -424,11 +432,19 @@
 static struct wpabuf * wpa_supplicant_extra_ies(struct wpa_supplicant *wpa_s)
 {
 	struct wpabuf *extra_ie = NULL;
+	u8 ext_capab[18];
+	int ext_capab_len;
 #ifdef CONFIG_WPS
 	int wps = 0;
 	enum wps_request_type req_type = WPS_REQ_ENROLLEE_INFO;
 #endif /* CONFIG_WPS */
 
+	ext_capab_len = wpas_build_ext_capab(wpa_s, ext_capab,
+					     sizeof(ext_capab));
+	if (ext_capab_len > 0 &&
+	    wpabuf_resize(&extra_ie, ext_capab_len) == 0)
+		wpabuf_put_data(extra_ie, ext_capab, ext_capab_len);
+
 #ifdef CONFIG_INTERWORKING
 	if (wpa_s->conf->interworking &&
 	    wpabuf_resize(&extra_ie, 100) == 0)
@@ -460,6 +476,8 @@
 	}
 #endif /* CONFIG_P2P */
 
+	wpa_supplicant_mesh_add_scan_ie(wpa_s, &extra_ie);
+
 #endif /* CONFIG_WPS */
 
 #ifdef CONFIG_HS20
@@ -528,7 +546,7 @@
 		return;
 	}
 
-	params->freqs = os_zalloc((mode->num_channels + 1) * sizeof(int));
+	params->freqs = os_calloc(mode->num_channels + 1, sizeof(int));
 	if (params->freqs == NULL)
 		return;
 	for (count = 0, i = 0; i < mode->num_channels; i++) {
@@ -596,16 +614,47 @@
 }
 
 
+static int wpa_set_ssids_from_scan_req(struct wpa_supplicant *wpa_s,
+				       struct wpa_driver_scan_params *params,
+				       size_t max_ssids)
+{
+	unsigned int i;
+
+	if (wpa_s->ssids_from_scan_req == NULL ||
+	    wpa_s->num_ssids_from_scan_req == 0)
+		return 0;
+
+	if (wpa_s->num_ssids_from_scan_req > max_ssids) {
+		wpa_s->num_ssids_from_scan_req = max_ssids;
+		wpa_printf(MSG_DEBUG, "Over max scan SSIDs from scan req: %u",
+			   (unsigned int) max_ssids);
+	}
+
+	for (i = 0; i < wpa_s->num_ssids_from_scan_req; i++) {
+		params->ssids[i].ssid = wpa_s->ssids_from_scan_req[i].ssid;
+		params->ssids[i].ssid_len =
+			wpa_s->ssids_from_scan_req[i].ssid_len;
+		wpa_hexdump_ascii(MSG_DEBUG, "specific SSID",
+				  params->ssids[i].ssid,
+				  params->ssids[i].ssid_len);
+	}
+
+	params->num_ssids = wpa_s->num_ssids_from_scan_req;
+	wpa_s->num_ssids_from_scan_req = 0;
+	return 1;
+}
+
+
 static void wpa_supplicant_scan(void *eloop_ctx, void *timeout_ctx)
 {
 	struct wpa_supplicant *wpa_s = eloop_ctx;
 	struct wpa_ssid *ssid;
-	int ret;
+	int ret, p2p_in_prog;
 	struct wpabuf *extra_ie = NULL;
 	struct wpa_driver_scan_params params;
 	struct wpa_driver_scan_params *scan_params;
 	size_t max_ssids;
-	enum wpa_states prev_state;
+	int connect_without_scan = 0;
 
 	if (wpa_s->pno || wpa_s->pno_sched_pending) {
 		wpa_dbg(wpa_s, MSG_DEBUG, "Skip scan - PNO is in progress");
@@ -653,7 +702,20 @@
 		return;
 	}
 
-	if (wpas_p2p_in_progress(wpa_s)) {
+	ssid = NULL;
+	if (wpa_s->scan_req != MANUAL_SCAN_REQ &&
+	    wpa_s->connect_without_scan) {
+		connect_without_scan = 1;
+		for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) {
+			if (ssid == wpa_s->connect_without_scan)
+				break;
+		}
+	}
+
+	p2p_in_prog = wpas_p2p_in_progress(wpa_s);
+	if (p2p_in_prog && p2p_in_prog != 2 &&
+	    (!ssid ||
+	     (ssid->mode != WPAS_MODE_AP && ssid->mode != WPAS_MODE_P2P_GO))) {
 		wpa_dbg(wpa_s, MSG_DEBUG, "Delay station mode scan while P2P operation is in progress");
 		wpa_supplicant_req_scan(wpa_s, 5, 0);
 		return;
@@ -670,9 +732,19 @@
 	wpa_s->last_scan_req = wpa_s->scan_req;
 	wpa_s->scan_req = NORMAL_SCAN_REQ;
 
+	if (connect_without_scan) {
+		wpa_s->connect_without_scan = NULL;
+		if (ssid) {
+			wpa_printf(MSG_DEBUG, "Start a pre-selected network "
+				   "without scan step");
+			wpa_supplicant_associate(wpa_s, NULL, ssid);
+			return;
+		}
+	}
+
 	os_memset(&params, 0, sizeof(params));
 
-	prev_state = wpa_s->wpa_state;
+	wpa_s->scan_prev_wpa_state = wpa_s->wpa_state;
 	if (wpa_s->wpa_state == WPA_DISCONNECTED ||
 	    wpa_s->wpa_state == WPA_INACTIVE)
 		wpa_supplicant_set_state(wpa_s, WPA_SCANNING);
@@ -685,24 +757,15 @@
 		goto scan;
 	}
 
-	if (wpa_s->last_scan_req != MANUAL_SCAN_REQ &&
-	    wpa_s->connect_without_scan) {
-		for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) {
-			if (ssid == wpa_s->connect_without_scan)
-				break;
-		}
-		wpa_s->connect_without_scan = NULL;
-		if (ssid) {
-			wpa_printf(MSG_DEBUG, "Start a pre-selected network "
-				   "without scan step");
-			wpa_supplicant_associate(wpa_s, NULL, ssid);
-			return;
-		}
+	if (wpa_s->last_scan_req == MANUAL_SCAN_REQ &&
+	    wpa_set_ssids_from_scan_req(wpa_s, &params, max_ssids)) {
+		wpa_printf(MSG_DEBUG, "Use specific SSIDs from SCAN command");
+		goto ssid_list_set;
 	}
 
 #ifdef CONFIG_P2P
 	if ((wpa_s->p2p_in_provisioning || wpa_s->show_group_started) &&
-	    wpa_s->go_params) {
+	    wpa_s->go_params && !wpa_s->conf->passive_scan) {
 		wpa_printf(MSG_DEBUG, "P2P: Use specific SSID for scan during P2P group formation (p2p_in_provisioning=%d show_group_started=%d)",
 			   wpa_s->p2p_in_provisioning,
 			   wpa_s->show_group_started);
@@ -810,7 +873,9 @@
 		    wpa_s->last_scan_req == MANUAL_SCAN_REQ)
 			wpa_set_scan_ssids(wpa_s, &params, max_ssids);
 
-		for (tssid = wpa_s->conf->ssid; tssid; tssid = tssid->next) {
+		for (tssid = wpa_s->conf->ssid;
+		     wpa_s->last_scan_req != MANUAL_SCAN_REQ && tssid;
+		     tssid = tssid->next) {
 			if (wpas_network_disabled(wpa_s, tssid))
 				continue;
 			if ((params.freqs || !freqs_set) && tssid->scan_freq) {
@@ -854,22 +919,26 @@
 	} else if (wpa_s->last_scan_req == MANUAL_SCAN_REQ &&
 		   wpa_s->manual_scan_passive && params.num_ssids == 0) {
 		wpa_dbg(wpa_s, MSG_DEBUG, "Use passive scan based on manual request");
+	} else if (wpa_s->conf->passive_scan) {
+		wpa_dbg(wpa_s, MSG_DEBUG,
+			"Use passive scan based on configuration");
 	} else {
 		wpa_s->prev_scan_ssid = WILDCARD_SSID_SCAN;
 		params.num_ssids++;
 		wpa_dbg(wpa_s, MSG_DEBUG, "Starting AP scan for wildcard "
 			"SSID");
 	}
-#ifdef CONFIG_P2P
-ssid_list_set:
-#endif /* CONFIG_P2P */
 
+ssid_list_set:
 	wpa_supplicant_optimize_freqs(wpa_s, &params);
 	extra_ie = wpa_supplicant_extra_ies(wpa_s);
 
 	if (wpa_s->last_scan_req == MANUAL_SCAN_REQ &&
-	    wpa_s->manual_scan_only_new)
+	    wpa_s->manual_scan_only_new) {
+		wpa_printf(MSG_DEBUG,
+			   "Request driver to clear scan cache due to manual only_new=1 scan");
 		params.only_new_results = 1;
+	}
 
 	if (wpa_s->last_scan_req == MANUAL_SCAN_REQ && params.freqs == NULL &&
 	    wpa_s->manual_scan_freqs) {
@@ -930,6 +999,14 @@
 	}
 #endif /* CONFIG_P2P */
 
+	if (wpa_s->mac_addr_rand_enable & MAC_ADDR_RAND_SCAN) {
+		params.mac_addr_rand = 1;
+		if (wpa_s->mac_addr_scan) {
+			params.mac_addr = wpa_s->mac_addr_scan;
+			params.mac_addr_mask = wpa_s->mac_addr_scan + ETH_ALEN;
+		}
+	}
+
 	scan_params = &params;
 
 scan:
@@ -979,13 +1056,17 @@
 
 	if (ret) {
 		wpa_msg(wpa_s, MSG_WARNING, "Failed to initiate AP scan");
-		if (prev_state != wpa_s->wpa_state)
-			wpa_supplicant_set_state(wpa_s, prev_state);
+		if (wpa_s->scan_prev_wpa_state != wpa_s->wpa_state)
+			wpa_supplicant_set_state(wpa_s,
+						 wpa_s->scan_prev_wpa_state);
 		/* Restore scan_req since we will try to scan again */
 		wpa_s->scan_req = wpa_s->last_scan_req;
 		wpa_supplicant_req_scan(wpa_s, 1, 0);
 	} else {
 		wpa_s->scan_for_connection = 0;
+#ifdef CONFIG_INTERWORKING
+		wpa_s->interworking_fast_assoc_tried = 0;
+#endif /* CONFIG_INTERWORKING */
 	}
 }
 
@@ -1024,8 +1105,17 @@
  */
 void wpa_supplicant_req_scan(struct wpa_supplicant *wpa_s, int sec, int usec)
 {
-	int res = eloop_deplete_timeout(sec, usec, wpa_supplicant_scan, wpa_s,
-					NULL);
+	int res;
+
+	if (wpa_s->p2p_mgmt) {
+		wpa_dbg(wpa_s, MSG_DEBUG,
+			"Ignore scan request (%d.%06d sec) on p2p_mgmt interface",
+			sec, usec);
+		return;
+	}
+
+	res = eloop_deplete_timeout(sec, usec, wpa_supplicant_scan, wpa_s,
+				    NULL);
 	if (res == 1) {
 		wpa_dbg(wpa_s, MSG_DEBUG, "Rescheduling scan request: %d.%06d sec",
 			sec, usec);
@@ -1145,7 +1235,7 @@
 	os_memset(&params, 0, sizeof(params));
 
 	/* If we can't allocate space for the filters, we just don't filter */
-	params.filter_ssids = os_zalloc(wpa_s->max_match_sets *
+	params.filter_ssids = os_calloc(wpa_s->max_match_sets,
 					sizeof(struct wpa_driver_scan_filter));
 
 	prev_state = wpa_s->wpa_state;
@@ -1273,6 +1363,15 @@
 
 	wpa_setband_scan_freqs(wpa_s, scan_params);
 
+	if (wpa_s->mac_addr_rand_enable & MAC_ADDR_RAND_SCHED_SCAN) {
+		params.mac_addr_rand = 1;
+		if (wpa_s->mac_addr_sched_scan) {
+			params.mac_addr = wpa_s->mac_addr_sched_scan;
+			params.mac_addr_mask = wpa_s->mac_addr_sched_scan +
+				ETH_ALEN;
+		}
+	}
+
 	ret = wpa_supplicant_start_sched_scan(wpa_s, scan_params,
 					      wpa_s->sched_scan_interval);
 	wpabuf_free(extra_ie);
@@ -1543,18 +1642,19 @@
  */
 #define GREAT_SNR 30
 
+#define IS_5GHZ(n) (n > 4000)
+
 /* Compare function for sorting scan results. Return >0 if @b is considered
  * better. */
 static int wpa_scan_result_compar(const void *a, const void *b)
 {
-#define IS_5GHZ(n) (n > 4000)
 #define MIN(a,b) a < b ? a : b
 	struct wpa_scan_res **_wa = (void *) a;
 	struct wpa_scan_res **_wb = (void *) b;
 	struct wpa_scan_res *wa = *_wa;
 	struct wpa_scan_res *wb = *_wb;
-	int wpa_a, wpa_b, maxrate_a, maxrate_b;
-	int snr_a, snr_b;
+	int wpa_a, wpa_b;
+	int snr_a, snr_b, snr_a_full, snr_b_full;
 
 	/* WPA/WPA2 support preferred */
 	wpa_a = wpa_scan_get_vendor_ie(wa, WPA_IE_VENDOR_TYPE) != NULL ||
@@ -1575,37 +1675,34 @@
 	    (wb->caps & IEEE80211_CAP_PRIVACY) == 0)
 		return -1;
 
-	if ((wa->flags & wb->flags & WPA_SCAN_LEVEL_DBM) &&
-	    !((wa->flags | wb->flags) & WPA_SCAN_NOISE_INVALID)) {
-		snr_a = MIN(wa->level - wa->noise, GREAT_SNR);
-		snr_b = MIN(wb->level - wb->noise, GREAT_SNR);
+	if (wa->flags & wb->flags & WPA_SCAN_LEVEL_DBM) {
+		snr_a_full = wa->snr;
+		snr_a = MIN(wa->snr, GREAT_SNR);
+		snr_b_full = wb->snr;
+		snr_b = MIN(wa->snr, GREAT_SNR);
 	} else {
-		/* Not suitable information to calculate SNR, so use level */
-		snr_a = wa->level;
-		snr_b = wb->level;
+		/* Level is not in dBm, so we can't calculate
+		 * SNR. Just use raw level (units unknown). */
+		snr_a = snr_a_full = wa->level;
+		snr_b = snr_b_full = wb->level;
 	}
 
-	/* best/max rate preferred if SNR close enough */
-        if ((snr_a && snr_b && abs(snr_b - snr_a) < 5) ||
+	/* if SNR is close, decide by max rate or frequency band */
+	if ((snr_a && snr_b && abs(snr_b - snr_a) < 5) ||
 	    (wa->qual && wb->qual && abs(wb->qual - wa->qual) < 10)) {
-		maxrate_a = wpa_scan_get_max_rate(wa);
-		maxrate_b = wpa_scan_get_max_rate(wb);
-		if (maxrate_a != maxrate_b)
-			return maxrate_b - maxrate_a;
+		if (wa->est_throughput != wb->est_throughput)
+			return wb->est_throughput - wa->est_throughput;
 		if (IS_5GHZ(wa->freq) ^ IS_5GHZ(wb->freq))
 			return IS_5GHZ(wa->freq) ? -1 : 1;
 	}
 
-	/* use freq for channel preference */
-
 	/* all things being equal, use SNR; if SNRs are
 	 * identical, use quality values since some drivers may only report
 	 * that value and leave the signal level zero */
-	if (snr_b == snr_a)
+	if (snr_b_full == snr_a_full)
 		return wb->qual - wa->qual;
-	return snr_b - snr_a;
+	return snr_b_full - snr_a_full;
 #undef MIN
-#undef IS_5GHZ
 }
 
 
@@ -1670,21 +1767,22 @@
 	for (i = 0; i < scan_res->num; i++) {
 		struct wpa_scan_res *r = scan_res->res[i];
 		u8 *pos;
-		if ((r->flags & (WPA_SCAN_LEVEL_DBM | WPA_SCAN_NOISE_INVALID))
-		    == WPA_SCAN_LEVEL_DBM) {
-			int snr = r->level - r->noise;
+		if (r->flags & WPA_SCAN_LEVEL_DBM) {
+			int noise_valid = !(r->flags & WPA_SCAN_NOISE_INVALID);
+
 			wpa_printf(MSG_EXCESSIVE, MACSTR " freq=%d qual=%d "
-				   "noise=%d level=%d snr=%d%s flags=0x%x "
-				   "age=%u",
+				   "noise=%d%s level=%d snr=%d%s flags=0x%x age=%u est=%u",
 				   MAC2STR(r->bssid), r->freq, r->qual,
-				   r->noise, r->level, snr,
-				   snr >= GREAT_SNR ? "*" : "", r->flags,
-				   r->age);
+				   r->noise, noise_valid ? "" : "~", r->level,
+				   r->snr, r->snr >= GREAT_SNR ? "*" : "",
+				   r->flags,
+				   r->age, r->est_throughput);
 		} else {
 			wpa_printf(MSG_EXCESSIVE, MACSTR " freq=%d qual=%d "
-				   "noise=%d level=%d flags=0x%x age=%u",
+				   "noise=%d level=%d flags=0x%x age=%u est=%u",
 				   MAC2STR(r->bssid), r->freq, r->qual,
-				   r->noise, r->level, r->flags, r->age);
+				   r->noise, r->level, r->flags, r->age,
+				   r->est_throughput);
 		}
 		pos = (u8 *) (r + 1);
 		if (r->ie_len)
@@ -1751,6 +1849,188 @@
 }
 
 
+/*
+ * Noise floor values to use when we have signal strength
+ * measurements, but no noise floor measurments. These values were
+ * measured in an office environment with many APs.
+ */
+#define DEFAULT_NOISE_FLOOR_2GHZ (-89)
+#define DEFAULT_NOISE_FLOOR_5GHZ (-92)
+
+static void scan_snr(struct wpa_scan_res *res)
+{
+	if (res->flags & WPA_SCAN_NOISE_INVALID) {
+		res->noise = IS_5GHZ(res->freq) ?
+			DEFAULT_NOISE_FLOOR_5GHZ :
+			DEFAULT_NOISE_FLOOR_2GHZ;
+	}
+
+	if (res->flags & WPA_SCAN_LEVEL_DBM) {
+		res->snr = res->level - res->noise;
+	} else {
+		/* Level is not in dBm, so we can't calculate
+		 * SNR. Just use raw level (units unknown). */
+		res->snr = res->level;
+	}
+}
+
+
+static unsigned int max_ht20_rate(int snr)
+{
+	if (snr < 6)
+		return 6500; /* HT20 MCS0 */
+	if (snr < 8)
+		return 13000; /* HT20 MCS1 */
+	if (snr < 13)
+		return 19500; /* HT20 MCS2 */
+	if (snr < 17)
+		return 26000; /* HT20 MCS3 */
+	if (snr < 20)
+		return 39000; /* HT20 MCS4 */
+	if (snr < 23)
+		return 52000; /* HT20 MCS5 */
+	if (snr < 24)
+		return 58500; /* HT20 MCS6 */
+	return 65000; /* HT20 MCS7 */
+}
+
+
+static unsigned int max_ht40_rate(int snr)
+{
+	if (snr < 3)
+		return 13500; /* HT40 MCS0 */
+	if (snr < 6)
+		return 27000; /* HT40 MCS1 */
+	if (snr < 10)
+		return 40500; /* HT40 MCS2 */
+	if (snr < 15)
+		return 54000; /* HT40 MCS3 */
+	if (snr < 17)
+		return 81000; /* HT40 MCS4 */
+	if (snr < 22)
+		return 108000; /* HT40 MCS5 */
+	if (snr < 24)
+		return 121500; /* HT40 MCS6 */
+	return 135000; /* HT40 MCS7 */
+}
+
+
+static unsigned int max_vht80_rate(int snr)
+{
+	if (snr < 1)
+		return 0;
+	if (snr < 2)
+		return 29300; /* VHT80 MCS0 */
+	if (snr < 5)
+		return 58500; /* VHT80 MCS1 */
+	if (snr < 9)
+		return 87800; /* VHT80 MCS2 */
+	if (snr < 11)
+		return 117000; /* VHT80 MCS3 */
+	if (snr < 15)
+		return 175500; /* VHT80 MCS4 */
+	if (snr < 16)
+		return 234000; /* VHT80 MCS5 */
+	if (snr < 18)
+		return 263300; /* VHT80 MCS6 */
+	if (snr < 20)
+		return 292500; /* VHT80 MCS7 */
+	if (snr < 22)
+		return 351000; /* VHT80 MCS8 */
+	return 390000; /* VHT80 MCS9 */
+}
+
+
+static void scan_est_throughput(struct wpa_supplicant *wpa_s,
+				struct wpa_scan_res *res)
+{
+	enum local_hw_capab capab = wpa_s->hw_capab;
+	int rate; /* max legacy rate in 500 kb/s units */
+	const u8 *ie;
+	unsigned int est, tmp;
+	int snr = res->snr;
+
+	if (res->est_throughput)
+		return;
+
+	/* Get maximum legacy rate */
+	rate = wpa_scan_get_max_rate(res);
+
+	/* Limit based on estimated SNR */
+	if (rate > 1 * 2 && snr < 1)
+		rate = 1 * 2;
+	else if (rate > 2 * 2 && snr < 4)
+		rate = 2 * 2;
+	else if (rate > 6 * 2 && snr < 5)
+		rate = 6 * 2;
+	else if (rate > 9 * 2 && snr < 6)
+		rate = 9 * 2;
+	else if (rate > 12 * 2 && snr < 7)
+		rate = 12 * 2;
+	else if (rate > 18 * 2 && snr < 10)
+		rate = 18 * 2;
+	else if (rate > 24 * 2 && snr < 11)
+		rate = 24 * 2;
+	else if (rate > 36 * 2 && snr < 15)
+		rate = 36 * 2;
+	else if (rate > 48 * 2 && snr < 19)
+		rate = 48 * 2;
+	else if (rate > 54 * 2 && snr < 21)
+		rate = 54 * 2;
+	est = rate * 500;
+
+	if (capab == CAPAB_HT || capab == CAPAB_HT40 || capab == CAPAB_VHT) {
+		ie = wpa_scan_get_ie(res, WLAN_EID_HT_CAP);
+		if (ie) {
+			tmp = max_ht20_rate(snr);
+			if (tmp > est)
+				est = tmp;
+		}
+	}
+
+	if (capab == CAPAB_HT40 || capab == CAPAB_VHT) {
+		ie = wpa_scan_get_ie(res, WLAN_EID_HT_OPERATION);
+		if (ie && ie[1] >= 2 &&
+		    (ie[3] & HT_INFO_HT_PARAM_SECONDARY_CHNL_OFF_MASK)) {
+			tmp = max_ht40_rate(snr);
+			if (tmp > est)
+				est = tmp;
+		}
+	}
+
+	if (capab == CAPAB_VHT) {
+		/* Use +1 to assume VHT is always faster than HT */
+		ie = wpa_scan_get_ie(res, WLAN_EID_VHT_CAP);
+		if (ie) {
+			tmp = max_ht20_rate(snr) + 1;
+			if (tmp > est)
+				est = tmp;
+
+			ie = wpa_scan_get_ie(res, WLAN_EID_HT_OPERATION);
+			if (ie && ie[1] >= 2 &&
+			    (ie[3] &
+			     HT_INFO_HT_PARAM_SECONDARY_CHNL_OFF_MASK)) {
+				tmp = max_ht40_rate(snr) + 1;
+				if (tmp > est)
+					est = tmp;
+			}
+
+			ie = wpa_scan_get_ie(res, WLAN_EID_VHT_OPERATION);
+			if (ie && ie[1] >= 1 &&
+			    (ie[2] & VHT_OPMODE_CHANNEL_WIDTH_MASK)) {
+				tmp = max_vht80_rate(snr) + 1;
+				if (tmp > est)
+					est = tmp;
+			}
+		}
+	}
+
+	/* TODO: channel utilization and AP load (e.g., from AP Beacon) */
+
+	res->est_throughput = est;
+}
+
+
 /**
  * wpa_supplicant_get_scan_results - Get scan results
  * @wpa_s: Pointer to wpa_supplicant data
@@ -1784,6 +2064,13 @@
 	}
 	filter_scan_res(wpa_s, scan_res);
 
+	for (i = 0; i < scan_res->num; i++) {
+		struct wpa_scan_res *scan_res_item = scan_res->res[i];
+
+		scan_snr(scan_res_item);
+		scan_est_throughput(wpa_s, scan_res_item);
+	}
+
 #ifdef CONFIG_WPS
 	if (wpas_wps_searching(wpa_s)) {
 		wpa_dbg(wpa_s, MSG_DEBUG, "WPS: Order scan results with WPS "
@@ -1918,6 +2205,23 @@
 	params->only_new_results = src->only_new_results;
 	params->low_priority = src->low_priority;
 
+	if (src->mac_addr_rand) {
+		params->mac_addr_rand = src->mac_addr_rand;
+
+		if (src->mac_addr && src->mac_addr_mask) {
+			u8 *mac_addr;
+
+			mac_addr = os_malloc(2 * ETH_ALEN);
+			if (!mac_addr)
+				goto failed;
+
+			os_memcpy(mac_addr, src->mac_addr, ETH_ALEN);
+			os_memcpy(mac_addr + ETH_ALEN, src->mac_addr_mask,
+				  ETH_ALEN);
+			params->mac_addr = mac_addr;
+			params->mac_addr_mask = mac_addr + ETH_ALEN;
+		}
+	}
 	return params;
 
 failed:
@@ -1938,13 +2242,20 @@
 	os_free((u8 *) params->extra_ies);
 	os_free(params->freqs);
 	os_free(params->filter_ssids);
+
+	/*
+	 * Note: params->mac_addr_mask points to same memory allocation and
+	 * must not be freed separately.
+	 */
+	os_free((u8 *) params->mac_addr);
+
 	os_free(params);
 }
 
 
 int wpas_start_pno(struct wpa_supplicant *wpa_s)
 {
-	int ret, interval;
+	int ret, interval, prio;
 	size_t i, num_ssid, num_match_ssid;
 	struct wpa_ssid *ssid;
 	struct wpa_driver_scan_params params;
@@ -2009,8 +2320,10 @@
 					sizeof(struct wpa_driver_scan_filter));
 	if (params.filter_ssids == NULL)
 		return -1;
+
 	i = 0;
-	ssid = wpa_s->conf->ssid;
+	prio = 0;
+	ssid = wpa_s->conf->pssid[prio];
 	while (ssid) {
 		if (!wpas_network_disabled(wpa_s, ssid)) {
 			if (ssid->scan_ssid && params.num_ssids < num_ssid) {
@@ -2028,7 +2341,12 @@
 			if (i == num_match_ssid)
 				break;
 		}
-		ssid = ssid->next;
+		if (ssid->pnext)
+			ssid = ssid->pnext;
+		else if (prio + 1 == wpa_s->conf->num_prio)
+			break;
+		else
+			ssid = wpa_s->conf->pssid[++prio];
 	}
 
 	if (wpa_s->conf->filter_rssi)
@@ -2042,6 +2360,14 @@
 		params.freqs = wpa_s->manual_sched_scan_freqs;
 	}
 
+	if (wpa_s->mac_addr_rand_enable & MAC_ADDR_RAND_PNO) {
+		params.mac_addr_rand = 1;
+		if (wpa_s->mac_addr_pno) {
+			params.mac_addr = wpa_s->mac_addr_pno;
+			params.mac_addr_mask = wpa_s->mac_addr_pno + ETH_ALEN;
+		}
+	}
+
 	ret = wpa_supplicant_start_sched_scan(wpa_s, &params, interval);
 	os_free(params.filter_ssids);
 	if (ret == 0)
@@ -2069,3 +2395,61 @@
 
 	return ret;
 }
+
+
+void wpas_mac_addr_rand_scan_clear(struct wpa_supplicant *wpa_s,
+				    unsigned int type)
+{
+	type &= MAC_ADDR_RAND_ALL;
+	wpa_s->mac_addr_rand_enable &= ~type;
+
+	if (type & MAC_ADDR_RAND_SCAN) {
+		os_free(wpa_s->mac_addr_scan);
+		wpa_s->mac_addr_scan = NULL;
+	}
+
+	if (type & MAC_ADDR_RAND_SCHED_SCAN) {
+		os_free(wpa_s->mac_addr_sched_scan);
+		wpa_s->mac_addr_sched_scan = NULL;
+	}
+
+	if (type & MAC_ADDR_RAND_PNO) {
+		os_free(wpa_s->mac_addr_pno);
+		wpa_s->mac_addr_pno = NULL;
+	}
+}
+
+
+int wpas_mac_addr_rand_scan_set(struct wpa_supplicant *wpa_s,
+				unsigned int type, const u8 *addr,
+				const u8 *mask)
+{
+	u8 *tmp = NULL;
+
+	wpas_mac_addr_rand_scan_clear(wpa_s, type);
+
+	if (addr) {
+		tmp = os_malloc(2 * ETH_ALEN);
+		if (!tmp)
+			return -1;
+		os_memcpy(tmp, addr, ETH_ALEN);
+		os_memcpy(tmp + ETH_ALEN, mask, ETH_ALEN);
+	}
+
+	if (type == MAC_ADDR_RAND_SCAN) {
+		wpa_s->mac_addr_scan = tmp;
+	} else if (type == MAC_ADDR_RAND_SCHED_SCAN) {
+		wpa_s->mac_addr_sched_scan = tmp;
+	} else if (type == MAC_ADDR_RAND_PNO) {
+		wpa_s->mac_addr_pno = tmp;
+	} else {
+		wpa_printf(MSG_INFO,
+			   "scan: Invalid MAC randomization type=0x%x",
+			   type);
+		os_free(tmp);
+		return -1;
+	}
+
+	wpa_s->mac_addr_rand_enable |= type;
+	return 0;
+}
diff --git a/wpa_supplicant/scan.h b/wpa_supplicant/scan.h
index 946d2b3..7650f5a 100644
--- a/wpa_supplicant/scan.h
+++ b/wpa_supplicant/scan.h
@@ -49,4 +49,10 @@
 int wpas_start_pno(struct wpa_supplicant *wpa_s);
 int wpas_stop_pno(struct wpa_supplicant *wpa_s);
 
+void wpas_mac_addr_rand_scan_clear(struct wpa_supplicant *wpa_s,
+				   unsigned int type);
+int wpas_mac_addr_rand_scan_set(struct wpa_supplicant *wpa_s,
+				unsigned int type, const u8 *addr,
+				const u8 *mask);
+
 #endif /* SCAN_H */
diff --git a/wpa_supplicant/sme.c b/wpa_supplicant/sme.c
index e616319..a472feb 100644
--- a/wpa_supplicant/sme.c
+++ b/wpa_supplicant/sme.c
@@ -67,7 +67,7 @@
 
 	for (;;) {
 		int group = groups[wpa_s->sme.sae_group_index];
-		if (group < 0)
+		if (group <= 0)
 			break;
 		if (sae_set_group(&wpa_s->sme.sae, group) == 0) {
 			wpa_dbg(wpa_s, MSG_DEBUG, "SME: Selected SAE group %d",
@@ -137,6 +137,60 @@
 #endif /* CONFIG_SAE */
 
 
+/**
+ * sme_auth_handle_rrm - Handle RRM aspects of current authentication attempt
+ * @wpa_s: Pointer to wpa_supplicant data
+ * @bss: Pointer to the bss which is the target of authentication attempt
+ */
+static void sme_auth_handle_rrm(struct wpa_supplicant *wpa_s,
+				struct wpa_bss *bss)
+{
+	const u8 rrm_ie_len = 5;
+	u8 *pos;
+	const u8 *rrm_ie;
+
+	wpa_s->rrm.rrm_used = 0;
+
+	wpa_printf(MSG_DEBUG,
+		   "RRM: Determining whether RRM can be used - device support: 0x%x",
+		   wpa_s->drv_rrm_flags);
+
+	rrm_ie = wpa_bss_get_ie(bss, WLAN_EID_RRM_ENABLED_CAPABILITIES);
+	if (!rrm_ie || !(bss->caps & IEEE80211_CAP_RRM)) {
+		wpa_printf(MSG_DEBUG, "RRM: No RRM in network");
+		return;
+	}
+
+	if (!(wpa_s->drv_rrm_flags &
+	      WPA_DRIVER_FLAGS_DS_PARAM_SET_IE_IN_PROBES) ||
+	    !(wpa_s->drv_rrm_flags & WPA_DRIVER_FLAGS_QUIET)) {
+		wpa_printf(MSG_DEBUG,
+			   "RRM: Insufficient RRM support in driver - do not use RRM");
+		return;
+	}
+
+	if (sizeof(wpa_s->sme.assoc_req_ie) <
+	    wpa_s->sme.assoc_req_ie_len + rrm_ie_len + 2) {
+		wpa_printf(MSG_INFO,
+			   "RRM: Unable to use RRM, no room for RRM IE");
+		return;
+	}
+
+	wpa_printf(MSG_DEBUG, "RRM: Adding RRM IE to Association Request");
+	pos = wpa_s->sme.assoc_req_ie + wpa_s->sme.assoc_req_ie_len;
+	os_memset(pos, 0, 2 + rrm_ie_len);
+	*pos++ = WLAN_EID_RRM_ENABLED_CAPABILITIES;
+	*pos++ = rrm_ie_len;
+
+	/* Set supported capabilites flags */
+	if (wpa_s->drv_rrm_flags & WPA_DRIVER_FLAGS_TX_POWER_INSERTION)
+		*pos |= WLAN_RRM_CAPS_LINK_MEASUREMENT;
+
+	wpa_s->sme.assoc_req_ie_len += rrm_ie_len + 2;
+	wpa_s->rrm.rrm_used = 1;
+}
+
+
 static void sme_send_authentication(struct wpa_supplicant *wpa_s,
 				    struct wpa_bss *bss, struct wpa_ssid *ssid,
 				    int start)
@@ -153,6 +207,7 @@
 	struct wpabuf *resp = NULL;
 	u8 ext_capab[18];
 	int ext_capab_len;
+	int skip_auth;
 
 	if (bss == NULL) {
 		wpa_msg(wpa_s, MSG_ERROR, "SME: No scan result available for "
@@ -161,6 +216,8 @@
 		return;
 	}
 
+	skip_auth = wpa_s->conf->reassoc_same_bss_optim &&
+		wpa_s->reassoc_same_bss;
 	wpa_s->current_bss = bss;
 
 	os_memset(&params, 0, sizeof(params));
@@ -199,17 +256,22 @@
 			"0x%x", params.auth_alg);
 	}
 #ifdef CONFIG_SAE
+	wpa_s->sme.sae_pmksa_caching = 0;
 	if (wpa_key_mgmt_sae(ssid->key_mgmt)) {
 		const u8 *rsn;
 		struct wpa_ie_data ied;
 
 		rsn = wpa_bss_get_ie(bss, WLAN_EID_RSN);
-		if (rsn &&
-		    wpa_parse_wpa_ie(rsn, 2 + rsn[1], &ied) == 0) {
-			if (wpa_key_mgmt_sae(ied.key_mgmt)) {
-				wpa_dbg(wpa_s, MSG_DEBUG, "Using SAE auth_alg");
-				params.auth_alg = WPA_AUTH_ALG_SAE;
-			}
+		if (!rsn) {
+			wpa_dbg(wpa_s, MSG_DEBUG,
+				"SAE enabled, but target BSS does not advertise RSN");
+		} else if (wpa_parse_wpa_ie(rsn, 2 + rsn[1], &ied) == 0 &&
+			   wpa_key_mgmt_sae(ied.key_mgmt)) {
+			wpa_dbg(wpa_s, MSG_DEBUG, "Using SAE auth_alg");
+			params.auth_alg = WPA_AUTH_ALG_SAE;
+		} else {
+			wpa_dbg(wpa_s, MSG_DEBUG,
+				"SAE enabled, but target BSS does not advertise SAE AKM for RSN");
 		}
 	}
 #endif /* CONFIG_SAE */
@@ -238,7 +300,7 @@
 		if (pmksa_cache_set_current(wpa_s->wpa, NULL, bss->bssid,
 					    wpa_s->current_ssid,
 					    try_opportunistic) == 0)
-			eapol_sm_notify_pmkid_attempt(wpa_s->eapol, 1);
+			eapol_sm_notify_pmkid_attempt(wpa_s->eapol);
 		wpa_s->sme.assoc_req_ie_len = sizeof(wpa_s->sme.assoc_req_ie);
 		if (wpa_supplicant_set_suites(wpa_s, bss, ssid,
 					      wpa_s->sme.assoc_req_ie,
@@ -325,8 +387,7 @@
 #endif /* CONFIG_IEEE80211R */
 
 #ifdef CONFIG_IEEE80211W
-	wpa_s->sme.mfp = ssid->ieee80211w == MGMT_FRAME_PROTECTION_DEFAULT ?
-		wpa_s->conf->pmf : ssid->ieee80211w;
+	wpa_s->sme.mfp = wpas_get_ssid_pmf(wpa_s, ssid);
 	if (wpa_s->sme.mfp != NO_MGMT_FRAME_PROTECTION) {
 		const u8 *rsn = wpa_bss_get_ie(bss, WLAN_EID_RSN);
 		struct wpa_ie_data _ie;
@@ -390,15 +451,40 @@
 		os_memcpy(pos, ext_capab, ext_capab_len);
 	}
 
+	if (wpa_s->vendor_elem[VENDOR_ELEM_ASSOC_REQ]) {
+		struct wpabuf *buf = wpa_s->vendor_elem[VENDOR_ELEM_ASSOC_REQ];
+		size_t len;
+
+		len = sizeof(wpa_s->sme.assoc_req_ie) -
+			wpa_s->sme.assoc_req_ie_len;
+		if (wpabuf_len(buf) <= len) {
+			os_memcpy(wpa_s->sme.assoc_req_ie +
+				  wpa_s->sme.assoc_req_ie_len,
+				  wpabuf_head(buf), wpabuf_len(buf));
+			wpa_s->sme.assoc_req_ie_len += wpabuf_len(buf);
+		}
+	}
+
+	sme_auth_handle_rrm(wpa_s, bss);
+
 #ifdef CONFIG_SAE
-	if (params.auth_alg == WPA_AUTH_ALG_SAE) {
+	if (!skip_auth && params.auth_alg == WPA_AUTH_ALG_SAE &&
+	    pmksa_cache_set_current(wpa_s->wpa, NULL, bss->bssid, ssid, 0) == 0)
+	{
+		wpa_dbg(wpa_s, MSG_DEBUG,
+			"PMKSA cache entry found - try to use PMKSA caching instead of new SAE authentication");
+		params.auth_alg = WPA_AUTH_ALG_OPEN;
+		wpa_s->sme.sae_pmksa_caching = 1;
+	}
+
+	if (!skip_auth && params.auth_alg == WPA_AUTH_ALG_SAE) {
 		if (start)
 			resp = sme_auth_build_sae_commit(wpa_s, ssid,
 							 bss->bssid);
 		else
 			resp = sme_auth_build_sae_confirm(wpa_s);
 		if (resp == NULL) {
-			wpas_connect_work_done(wpa_s);
+			wpas_connection_failed(wpa_s, bss->bssid);
 			return;
 		}
 		params.sae_data = wpabuf_head(resp);
@@ -449,6 +535,15 @@
 	}
 #endif /* CONFIG_P2P */
 
+	if (skip_auth) {
+		wpa_msg(wpa_s, MSG_DEBUG,
+			"SME: Skip authentication step on reassoc-to-same-BSS");
+		wpabuf_free(resp);
+		sme_associate(wpa_s, ssid->mode, bss->bssid, WLAN_AUTH_OPEN);
+		return;
+	}
+
+
 	wpa_s->sme.auth_alg = params.auth_alg;
 	if (wpa_drv_authenticate(wpa_s, &params) < 0) {
 		wpa_msg(wpa_s, MSG_INFO, "SME: Authentication request to the "
@@ -545,6 +640,8 @@
 static int sme_sae_auth(struct wpa_supplicant *wpa_s, u16 auth_transaction,
 			u16 status_code, const u8 *data, size_t len)
 {
+	int *groups;
+
 	wpa_dbg(wpa_s, MSG_DEBUG, "SME: SAE authentication transaction %u "
 		"status code %u", auth_transaction, status_code);
 
@@ -552,10 +649,32 @@
 	    status_code == WLAN_STATUS_ANTI_CLOGGING_TOKEN_REQ &&
 	    wpa_s->sme.sae.state == SAE_COMMITTED &&
 	    wpa_s->current_bss && wpa_s->current_ssid) {
-		wpa_dbg(wpa_s, MSG_DEBUG, "SME: SAE anti-clogging token "
-			"requested");
+		int default_groups[] = { 19, 20, 21, 25, 26, 0 };
+		u16 group;
+
+		groups = wpa_s->conf->sae_groups;
+		if (!groups || groups[0] <= 0)
+			groups = default_groups;
+
+		if (len < sizeof(le16)) {
+			wpa_dbg(wpa_s, MSG_DEBUG,
+				"SME: Too short SAE anti-clogging token request");
+			return -1;
+		}
+		group = WPA_GET_LE16(data);
+		wpa_dbg(wpa_s, MSG_DEBUG,
+			"SME: SAE anti-clogging token requested (group %u)",
+			group);
+		if (sae_group_allowed(&wpa_s->sme.sae, groups, group) !=
+		    WLAN_STATUS_SUCCESS) {
+			wpa_dbg(wpa_s, MSG_ERROR,
+				"SME: SAE group %u of anti-clogging request is invalid",
+				group);
+			return -1;
+		}
 		wpabuf_free(wpa_s->sme.sae_token);
-		wpa_s->sme.sae_token = wpabuf_alloc_copy(data, len);
+		wpa_s->sme.sae_token = wpabuf_alloc_copy(data + sizeof(le16),
+							 len - sizeof(le16));
 		sme_send_authentication(wpa_s, wpa_s->current_bss,
 					wpa_s->current_ssid, 1);
 		return 0;
@@ -579,7 +698,9 @@
 		return -1;
 
 	if (auth_transaction == 1) {
-		int *groups = wpa_s->conf->sae_groups;
+		u16 res;
+
+		groups = wpa_s->conf->sae_groups;
 
 		wpa_dbg(wpa_s, MSG_DEBUG, "SME SAE commit");
 		if (wpa_s->current_bss == NULL ||
@@ -589,8 +710,14 @@
 			return -1;
 		if (groups && groups[0] <= 0)
 			groups = NULL;
-		if (sae_parse_commit(&wpa_s->sme.sae, data, len, NULL, NULL,
-				     groups) != WLAN_STATUS_SUCCESS)
+		res = sae_parse_commit(&wpa_s->sme.sae, data, len, NULL, NULL,
+				       groups);
+		if (res == SAE_SILENTLY_DISCARD) {
+			wpa_printf(MSG_DEBUG,
+				   "SAE: Drop commit message due to reflection attack");
+			return 0;
+		}
+		if (res != WLAN_STATUS_SUCCESS)
 			return -1;
 
 		if (sae_process_commit(&wpa_s->sme.sae) < 0) {
@@ -668,7 +795,8 @@
 
 		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_sm_set_pmk(wpa_s->wpa, wpa_s->sme.sae.pmk, PMK_LEN,
+			       wpa_s->pending_bssid);
 	}
 #endif /* CONFIG_SAE */
 
@@ -711,12 +839,20 @@
 
 #ifdef CONFIG_IEEE80211R
 	if (data->auth.auth_type == WLAN_AUTH_FT) {
-		union wpa_event_data edata;
-		os_memset(&edata, 0, sizeof(edata));
-		edata.ft_ies.ies = data->auth.ies;
-		edata.ft_ies.ies_len = data->auth.ies_len;
-		os_memcpy(edata.ft_ies.target_ap, data->auth.peer, ETH_ALEN);
-		wpa_supplicant_event(wpa_s, EVENT_FT_RESPONSE, &edata);
+		if (wpa_ft_process_response(wpa_s->wpa, data->auth.ies,
+					    data->auth.ies_len, 0,
+					    data->auth.peer, NULL, 0) < 0) {
+			wpa_dbg(wpa_s, MSG_DEBUG,
+				"SME: FT Authentication response processing failed");
+			wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_DISCONNECTED "bssid="
+				MACSTR
+				" reason=%d locally_generated=1",
+				MAC2STR(wpa_s->pending_bssid),
+				WLAN_REASON_DEAUTH_LEAVING);
+			wpas_connection_failed(wpa_s, wpa_s->pending_bssid);
+			wpa_supplicant_mark_disassoc(wpa_s);
+			return;
+		}
 	}
 #endif /* CONFIG_IEEE80211R */
 
@@ -775,6 +911,7 @@
 #endif /* CONFIG_IEEE80211R */
 	params.mode = mode;
 	params.mgmt_frame_protection = wpa_s->sme.mfp;
+	params.rrm_used = wpa_s->rrm.rrm_used;
 	if (wpa_s->sme.prev_bssid_set)
 		params.prev_bssid = wpa_s->sme.prev_bssid;
 
@@ -882,6 +1019,27 @@
 
 	eloop_cancel_timeout(sme_assoc_timer, wpa_s, NULL);
 
+#ifdef CONFIG_SAE
+	if (wpa_s->sme.sae_pmksa_caching && wpa_s->current_ssid &&
+	    wpa_key_mgmt_sae(wpa_s->current_ssid->key_mgmt)) {
+		wpa_dbg(wpa_s, MSG_DEBUG,
+			"PMKSA caching attempt rejected - drop PMKSA cache entry and fall back to SAE authentication");
+		wpa_sm_aborted_cached(wpa_s->wpa);
+		wpa_sm_pmksa_cache_flush(wpa_s->wpa, wpa_s->current_ssid);
+		if (wpa_s->current_bss) {
+			struct wpa_bss *bss = wpa_s->current_bss;
+			struct wpa_ssid *ssid = wpa_s->current_ssid;
+
+			wpa_drv_deauthenticate(wpa_s, wpa_s->pending_bssid,
+					       WLAN_REASON_DEAUTH_LEAVING);
+			wpas_connect_work_done(wpa_s);
+			wpa_supplicant_mark_disassoc(wpa_s);
+			wpa_supplicant_connect(wpa_s, bss, ssid);
+			return;
+		}
+	}
+#endif /* CONFIG_SAE */
+
 	/*
 	 * For now, unconditionally terminate the previous authentication. In
 	 * theory, this should not be needed, but mac80211 gets quite confused
@@ -982,6 +1140,21 @@
 }
 
 
+void sme_clear_on_disassoc(struct wpa_supplicant *wpa_s)
+{
+	wpa_s->sme.prev_bssid_set = 0;
+#ifdef CONFIG_SAE
+	wpabuf_free(wpa_s->sme.sae_token);
+	wpa_s->sme.sae_token = NULL;
+	sae_clear_data(&wpa_s->sme.sae);
+#endif /* CONFIG_SAE */
+#ifdef CONFIG_IEEE80211R
+	if (wpa_s->sme.ft_ies)
+		sme_update_ft_ies(wpa_s, NULL, NULL, 0);
+#endif /* CONFIG_IEEE80211R */
+}
+
+
 void sme_deinit(struct wpa_supplicant *wpa_s)
 {
 	os_free(wpa_s->sme.ft_ies);
@@ -990,11 +1163,7 @@
 #ifdef CONFIG_IEEE80211W
 	sme_stop_sa_query(wpa_s);
 #endif /* CONFIG_IEEE80211W */
-#ifdef CONFIG_SAE
-	wpabuf_free(wpa_s->sme.sae_token);
-	wpa_s->sme.sae_token = NULL;
-	sae_clear_data(&wpa_s->sme.sae);
-#endif /* CONFIG_SAE */
+	sme_clear_on_disassoc(wpa_s);
 
 	eloop_cancel_timeout(sme_assoc_timer, wpa_s, NULL);
 	eloop_cancel_timeout(sme_auth_timer, wpa_s, NULL);
@@ -1136,28 +1305,72 @@
 }
 
 
-static void wpa_setband_scan_freqs_list(struct wpa_supplicant *wpa_s,
-					enum hostapd_hw_mode band,
-					struct wpa_driver_scan_params *params)
+static void wpa_obss_scan_freqs_list(struct wpa_supplicant *wpa_s,
+				     struct wpa_driver_scan_params *params)
 {
-	/* Include only supported channels for the specified band */
+	/* Include only affected channels */
 	struct hostapd_hw_modes *mode;
 	int count, i;
+	int start, end;
 
-	mode = get_mode(wpa_s->hw.modes, wpa_s->hw.num_modes, band);
+	mode = get_mode(wpa_s->hw.modes, wpa_s->hw.num_modes,
+			HOSTAPD_MODE_IEEE80211G);
 	if (mode == NULL) {
 		/* No channels supported in this band - use empty list */
 		params->freqs = os_zalloc(sizeof(int));
 		return;
 	}
 
+	if (wpa_s->sme.ht_sec_chan == HT_SEC_CHAN_UNKNOWN &&
+	    wpa_s->current_bss) {
+		const u8 *ie;
+
+		ie = wpa_bss_get_ie(wpa_s->current_bss, WLAN_EID_HT_OPERATION);
+		if (ie && ie[1] >= 2) {
+			u8 o;
+
+			o = ie[3] & HT_INFO_HT_PARAM_SECONDARY_CHNL_OFF_MASK;
+			if (o == HT_INFO_HT_PARAM_SECONDARY_CHNL_ABOVE)
+				wpa_s->sme.ht_sec_chan = HT_SEC_CHAN_ABOVE;
+			else if (o == HT_INFO_HT_PARAM_SECONDARY_CHNL_BELOW)
+				wpa_s->sme.ht_sec_chan = HT_SEC_CHAN_BELOW;
+		}
+	}
+
+	start = wpa_s->assoc_freq - 10;
+	end = wpa_s->assoc_freq + 10;
+	switch (wpa_s->sme.ht_sec_chan) {
+	case HT_SEC_CHAN_UNKNOWN:
+		/* HT40+ possible on channels 1..9 */
+		if (wpa_s->assoc_freq <= 2452)
+			start -= 20;
+		/* HT40- possible on channels 5-13 */
+		if (wpa_s->assoc_freq >= 2432)
+			end += 20;
+		break;
+	case HT_SEC_CHAN_ABOVE:
+		end += 20;
+		break;
+	case HT_SEC_CHAN_BELOW:
+		start -= 20;
+		break;
+	}
+	wpa_printf(MSG_DEBUG,
+		   "OBSS: assoc_freq %d possible affected range %d-%d",
+		   wpa_s->assoc_freq, start, end);
+
 	params->freqs = os_calloc(mode->num_channels + 1, sizeof(int));
 	if (params->freqs == NULL)
 		return;
 	for (count = 0, i = 0; i < mode->num_channels; i++) {
+		int freq;
+
 		if (mode->channels[i].flag & HOSTAPD_CHAN_DISABLED)
 			continue;
-		params->freqs[count++] = mode->channels[i].freq;
+		freq = mode->channels[i].freq;
+		if (freq - 10 >= end || freq + 10 <= start)
+			continue; /* not affected */
+		params->freqs[count++] = freq;
 	}
 }
 
@@ -1173,7 +1386,7 @@
 	}
 
 	os_memset(&params, 0, sizeof(params));
-	wpa_setband_scan_freqs_list(wpa_s, HOSTAPD_MODE_IEEE80211G, &params);
+	wpa_obss_scan_freqs_list(wpa_s, &params);
 	params.low_priority = 1;
 	wpa_printf(MSG_DEBUG, "SME OBSS: Request an OBSS scan");
 
@@ -1198,6 +1411,7 @@
 
 	eloop_cancel_timeout(sme_obss_scan_timeout, wpa_s, NULL);
 	wpa_s->sme.sched_obss_scan = 0;
+	wpa_s->sme.ht_sec_chan = HT_SEC_CHAN_UNKNOWN;
 	if (!enable)
 		return;
 
@@ -1360,9 +1574,7 @@
 	if (wpa_s->wpa_state != WPA_COMPLETED)
 		return;
 	ssid = wpa_s->current_ssid;
-	if (ssid == NULL ||
-	    (ssid->ieee80211w == MGMT_FRAME_PROTECTION_DEFAULT ?
-	     wpa_s->conf->pmf : ssid->ieee80211w) == NO_MGMT_FRAME_PROTECTION)
+	if (wpas_get_ssid_pmf(wpa_s, ssid) == NO_MGMT_FRAME_PROTECTION)
 		return;
 	if (os_memcmp(sa, wpa_s->bssid, ETH_ALEN) != 0)
 		return;
diff --git a/wpa_supplicant/sme.h b/wpa_supplicant/sme.h
index 04404c1..fd5c3b4 100644
--- a/wpa_supplicant/sme.h
+++ b/wpa_supplicant/sme.h
@@ -33,6 +33,7 @@
 void sme_state_changed(struct wpa_supplicant *wpa_s);
 void sme_disassoc_while_authenticating(struct wpa_supplicant *wpa_s,
 				       const u8 *prev_pending_bssid);
+void sme_clear_on_disassoc(struct wpa_supplicant *wpa_s);
 void sme_deinit(struct wpa_supplicant *wpa_s);
 
 int sme_proc_obss_scan(struct wpa_supplicant *wpa_s);
@@ -94,6 +95,10 @@
 {
 }
 
+static inline void sme_clear_on_disassoc(struct wpa_supplicant *wpa_s)
+{
+}
+
 static inline void sme_deinit(struct wpa_supplicant *wpa_s)
 {
 }
diff --git a/wpa_supplicant/todo.txt b/wpa_supplicant/todo.txt
index b84cccc..4c9f98e 100644
--- a/wpa_supplicant/todo.txt
+++ b/wpa_supplicant/todo.txt
@@ -5,8 +5,6 @@
   authentication has been completed (cache scard data based on serial#(?)
   and try to optimize next connection if the same card is present for next
   auth)
-- on disconnect event, could try to associate with another AP if one is
-  present in scan results; would need to update scan results periodically..
 - if driver/hw is not WPA2 capable, must remove WPA_PROTO_RSN flag from
   ssid->proto fields to avoid detecting downgrade attacks when the driver
   is not reporting RSN IE, but msg 3/4 has one
@@ -24,14 +22,12 @@
   RFC 3748 Sect. 4.2
 - test compilation with gcc -W options (more warnings?)
   (Done once; number of unused function arguments still present)
-- add proper support for using dot11RSNAConfigSATimeout
-- ctrl_iface: get/set/remove blob
+- ctrl_iface: get/remove blob
 - use doc/docbook/*.sgml and docbook2{txt,html,pdf} to replace README and
   web pages including the same information.. i.e., have this information only
   in one page; how to build a PDF file with all the SGML included?
 - EAP-POTP/RSA SecurID profile (RFC 4793)
 - document wpa_gui build and consider adding it to 'make install'
-- test madwifi with pairwise=TKIP group=WEP104
 - consider merging hostapd and wpa_supplicant PMKSA cache implementations
 - consider redesigning pending EAP requests (identity/password/otp from
   ctrl_iface) by moving the retrying of the previous request into EAP
@@ -57,14 +53,11 @@
 - try to work around race in configuring PTK and sending msg 4/4 (some NDIS
   drivers with ndiswrapper end up not being able to complete 4-way handshake
   in some cases; extra delay before setting the key seems to help)
-- add wpa_secure_memzero() macro and secure implementation (volatile u8*) to
-  clear memory; this would be used to clear temporary buffers containing
-  private data (e.g., keys); the macro can be defined to NOP in order to save
-  space (i.e., no code should depend on the macro doing something)
 - make sure that TLS session cache is not shared between EAP types or if it
   is, that the cache entries are bound to only one EAP type; e.g., cache entry
   created with EAP-TLS must not be allowed to do fast re-auth with EAP-TTLS
-- consider moving eap_tls_build_ack() call into eap_tls_process_helper()
+- consider moving eap_peer_tls_build_ack() call into
+  eap_peer_tls_process_helper()
   (it seems to be called always if helper returns 1)
   * could need to modify eap_{ttls,peap,fast}_decrypt to do same
 - add support for fetching full user cert chain from Windows certificate
diff --git a/wpa_supplicant/wifi_display.c b/wpa_supplicant/wifi_display.c
index 6dc41de..c363b21 100644
--- a/wpa_supplicant/wifi_display.c
+++ b/wpa_supplicant/wifi_display.c
@@ -233,15 +233,31 @@
 	if (pos == NULL)
 		return -1;
 	*pos++ = '\0';
-	subelem = atoi(cmd);
-	if (subelem < 0 || subelem >= MAX_WFD_SUBELEMS)
-		return -1;
 
 	len = os_strlen(pos);
 	if (len & 1)
 		return -1;
 	len /= 2;
 
+	if (os_strcmp(cmd, "all") == 0) {
+		int res;
+
+		e = wpabuf_alloc(len);
+		if (e == NULL)
+			return -1;
+		if (hexstr2bin(pos, wpabuf_put(e, len), len) < 0) {
+			wpabuf_free(e);
+			return -1;
+		}
+		res = wifi_display_subelem_set_from_ies(global, e);
+		wpabuf_free(e);
+		return res;
+	}
+
+	subelem = atoi(cmd);
+	if (subelem < 0 || subelem >= MAX_WFD_SUBELEMS)
+		return -1;
+
 	if (len == 0) {
 		/* Clear subelement */
 		e = NULL;
@@ -271,7 +287,7 @@
 {
 	int subelements[MAX_WFD_SUBELEMS] = {};
 	const u8 *pos, *end;
-	int len, subelem;
+	unsigned int len, subelem;
 	struct wpabuf *e;
 
 	wpa_printf(MSG_DEBUG, "WFD IEs set: %p - %lu",
@@ -292,7 +308,7 @@
 		wpa_printf(MSG_DEBUG, "WFD Sub-Element ID %d - len %d",
 			   *pos, len - 3);
 
-		if (pos + len > end)
+		if (len > (unsigned int) (end - pos))
 			break;
 
 		subelem = *pos;
@@ -325,6 +341,19 @@
 {
 	int subelem;
 
+	if (os_strcmp(cmd, "all") == 0) {
+		struct wpabuf *ie;
+		int res;
+
+		ie = wifi_display_get_wfd_ie(global);
+		if (ie == NULL)
+			return 0;
+		res = wpa_snprintf_hex(buf, buflen, wpabuf_head(ie),
+				       wpabuf_len(ie));
+		wpabuf_free(ie);
+		return res;
+	}
+
 	subelem = atoi(cmd);
 	if (subelem < 0 || subelem >= MAX_WFD_SUBELEMS)
 		return -1;
diff --git a/wpa_supplicant/wmm_ac.c b/wpa_supplicant/wmm_ac.c
new file mode 100644
index 0000000..5625d36
--- /dev/null
+++ b/wpa_supplicant/wmm_ac.c
@@ -0,0 +1,995 @@
+/*
+ * Wi-Fi Multimedia Admission Control (WMM-AC)
+ * Copyright(c) 2014, Intel Mobile Communication GmbH.
+ * Copyright(c) 2014, Intel Corporation. All rights reserved.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "utils/common.h"
+#include "utils/list.h"
+#include "utils/eloop.h"
+#include "common/ieee802_11_common.h"
+#include "wpa_supplicant_i.h"
+#include "bss.h"
+#include "driver_i.h"
+#include "wmm_ac.h"
+
+static void wmm_ac_addts_req_timeout(void *eloop_ctx, void *timeout_ctx);
+
+static const enum wmm_ac up_to_ac[8] = {
+	WMM_AC_BK,
+	WMM_AC_BE,
+	WMM_AC_BE,
+	WMM_AC_BK,
+	WMM_AC_VI,
+	WMM_AC_VI,
+	WMM_AC_VO,
+	WMM_AC_VO
+};
+
+
+static inline u8 wmm_ac_get_tsid(const struct wmm_tspec_element *tspec)
+{
+	return (tspec->ts_info[0] >> 1) & 0x0f;
+}
+
+
+static u8 wmm_ac_get_direction(const struct wmm_tspec_element *tspec)
+{
+	return (tspec->ts_info[0] >> 5) & 0x03;
+}
+
+
+static u8 wmm_ac_get_user_priority(const struct wmm_tspec_element *tspec)
+{
+	return (tspec->ts_info[1] >> 3) & 0x07;
+}
+
+
+static u8 wmm_ac_direction_to_idx(u8 direction)
+{
+	switch (direction) {
+	case WMM_AC_DIR_UPLINK:
+		return TS_DIR_IDX_UPLINK;
+	case WMM_AC_DIR_DOWNLINK:
+		return TS_DIR_IDX_DOWNLINK;
+	case WMM_AC_DIR_BIDIRECTIONAL:
+		return TS_DIR_IDX_BIDI;
+	default:
+		wpa_printf(MSG_ERROR, "Invalid direction: %d", direction);
+		return WMM_AC_DIR_UPLINK;
+	}
+}
+
+
+static int wmm_ac_add_ts(struct wpa_supplicant *wpa_s, const u8 *addr,
+			 const struct wmm_tspec_element *tspec)
+{
+	struct wmm_tspec_element *_tspec;
+	int ret;
+	u16 admitted_time = le_to_host16(tspec->medium_time);
+	u8 up = wmm_ac_get_user_priority(tspec);
+	u8 ac = up_to_ac[up];
+	u8 dir = wmm_ac_get_direction(tspec);
+	u8 tsid = wmm_ac_get_tsid(tspec);
+	enum ts_dir_idx idx = wmm_ac_direction_to_idx(dir);
+
+	/* should have been verified before, but double-check here */
+	if (wpa_s->tspecs[ac][idx]) {
+		wpa_printf(MSG_ERROR,
+			   "WMM AC: tspec (ac=%d, dir=%d) already exists!",
+			   ac, dir);
+		return -1;
+	}
+
+	/* copy tspec */
+	_tspec = os_malloc(sizeof(*_tspec));
+	if (!_tspec)
+		return -1;
+
+	/* store the admitted TSPEC */
+	os_memcpy(_tspec, tspec, sizeof(*_tspec));
+
+	if (dir != WMM_AC_DIR_DOWNLINK) {
+		ret = wpa_drv_add_ts(wpa_s, tsid, addr, up, admitted_time);
+		wpa_printf(MSG_DEBUG,
+			   "WMM AC: Add TS: addr=" MACSTR
+			   " TSID=%u admitted time=%u, ret=%d",
+			   MAC2STR(addr), tsid, admitted_time, ret);
+		if (ret < 0) {
+			os_free(_tspec);
+			return -1;
+		}
+	}
+
+	wpa_s->tspecs[ac][idx] = _tspec;
+
+	wpa_printf(MSG_DEBUG, "Traffic stream was created successfully");
+
+	wpa_msg(wpa_s, MSG_INFO, WMM_AC_EVENT_TSPEC_ADDED
+		"tsid=%d addr=" MACSTR " admitted_time=%d",
+		tsid, MAC2STR(addr), admitted_time);
+
+	return 0;
+}
+
+
+static void wmm_ac_del_ts_idx(struct wpa_supplicant *wpa_s, u8 ac,
+			      enum ts_dir_idx dir)
+{
+	struct wmm_tspec_element *tspec = wpa_s->tspecs[ac][dir];
+	u8 tsid;
+
+	if (!tspec)
+		return;
+
+	tsid = wmm_ac_get_tsid(tspec);
+	wpa_printf(MSG_DEBUG, "WMM AC: Del TS ac=%d tsid=%d", ac, tsid);
+
+	/* update the driver in case of uplink/bidi */
+	if (wmm_ac_get_direction(tspec) != WMM_AC_DIR_DOWNLINK)
+		wpa_drv_del_ts(wpa_s, tsid, wpa_s->bssid);
+
+	wpa_msg(wpa_s, MSG_INFO, WMM_AC_EVENT_TSPEC_REMOVED
+		"tsid=%d addr=" MACSTR, tsid, MAC2STR(wpa_s->bssid));
+
+	os_free(wpa_s->tspecs[ac][dir]);
+	wpa_s->tspecs[ac][dir] = NULL;
+}
+
+
+static void wmm_ac_del_req(struct wpa_supplicant *wpa_s, int failed)
+{
+	struct wmm_ac_addts_request *req = wpa_s->addts_request;
+
+	if (!req)
+		return;
+
+	if (failed)
+		wpa_msg(wpa_s, MSG_INFO, WMM_AC_EVENT_TSPEC_REQ_FAILED
+			"tsid=%u", wmm_ac_get_tsid(&req->tspec));
+
+	eloop_cancel_timeout(wmm_ac_addts_req_timeout, wpa_s, req);
+	wpa_s->addts_request = NULL;
+	os_free(req);
+}
+
+
+static void wmm_ac_addts_req_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+	struct wpa_supplicant *wpa_s = eloop_ctx;
+	struct wmm_ac_addts_request *addts_req = timeout_ctx;
+
+	wpa_printf(MSG_DEBUG,
+		   "Timeout getting ADDTS response (tsid=%d up=%d)",
+		   wmm_ac_get_tsid(&addts_req->tspec),
+		   wmm_ac_get_user_priority(&addts_req->tspec));
+
+	wmm_ac_del_req(wpa_s, 1);
+}
+
+
+static int wmm_ac_send_addts_request(struct wpa_supplicant *wpa_s,
+				     const struct wmm_ac_addts_request *req)
+{
+	struct wpabuf *buf;
+	int ret;
+
+	wpa_printf(MSG_DEBUG, "Sending ADDTS Request to " MACSTR,
+		   MAC2STR(req->address));
+
+	/* category + action code + dialog token + status + sizeof(tspec) */
+	buf = wpabuf_alloc(4 + sizeof(req->tspec));
+	if (!buf) {
+		wpa_printf(MSG_ERROR, "WMM AC: Allocation error");
+		return -1;
+	}
+
+	wpabuf_put_u8(buf, WLAN_ACTION_WMM);
+	wpabuf_put_u8(buf, WMM_ACTION_CODE_ADDTS_REQ);
+	wpabuf_put_u8(buf, req->dialog_token);
+	wpabuf_put_u8(buf, 0); /* status code */
+	wpabuf_put_data(buf, &req->tspec, sizeof(req->tspec));
+
+	ret = wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, req->address,
+				wpa_s->own_addr, wpa_s->bssid,
+				wpabuf_head(buf), wpabuf_len(buf), 0);
+	if (ret) {
+		wpa_printf(MSG_WARNING,
+			   "WMM AC: Failed to send ADDTS Request");
+	}
+
+	wpabuf_free(buf);
+	return ret;
+}
+
+
+static int wmm_ac_send_delts(struct wpa_supplicant *wpa_s,
+			     const struct wmm_tspec_element *tspec,
+			     const u8 *address)
+{
+	struct wpabuf *buf;
+	int ret;
+
+	/* category + action code + dialog token + status + sizeof(tspec) */
+	buf = wpabuf_alloc(4 + sizeof(*tspec));
+	if (!buf)
+		return -1;
+
+	wpa_printf(MSG_DEBUG, "Sending DELTS to " MACSTR, MAC2STR(address));
+
+	/* category + action code + dialog token + status + sizeof(tspec) */
+	wpabuf_put_u8(buf, WLAN_ACTION_WMM);
+	wpabuf_put_u8(buf, WMM_ACTION_CODE_DELTS);
+	wpabuf_put_u8(buf, 0); /* Dialog Token (not used) */
+	wpabuf_put_u8(buf, 0); /* Status Code (not used) */
+	wpabuf_put_data(buf, tspec, sizeof(*tspec));
+
+	ret = wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, address,
+				  wpa_s->own_addr, wpa_s->bssid,
+				  wpabuf_head(buf), wpabuf_len(buf), 0);
+	if (ret)
+		wpa_printf(MSG_WARNING, "Failed to send DELTS frame");
+
+	wpabuf_free(buf);
+	return ret;
+}
+
+
+/* return the AC using the given TSPEC tid */
+static int wmm_ac_find_tsid(struct wpa_supplicant *wpa_s, u8 tsid,
+			    enum ts_dir_idx *dir)
+{
+	int ac;
+	enum ts_dir_idx idx;
+
+	for (ac = 0; ac < WMM_AC_NUM; ac++) {
+		for (idx = 0; idx < TS_DIR_IDX_COUNT; idx++) {
+			if (wpa_s->tspecs[ac][idx] &&
+			    wmm_ac_get_tsid(wpa_s->tspecs[ac][idx]) == tsid) {
+				if (dir)
+					*dir = idx;
+				return ac;
+			}
+		}
+	}
+
+	return -1;
+}
+
+
+static struct wmm_ac_addts_request *
+wmm_ac_build_addts_req(struct wpa_supplicant *wpa_s,
+		       const struct wmm_ac_ts_setup_params *params,
+		       const u8 *address)
+{
+	struct wmm_ac_addts_request *addts_req;
+	struct wmm_tspec_element *tspec;
+	u8 ac = up_to_ac[params->user_priority];
+	u8 uapsd = wpa_s->wmm_ac_assoc_info->ac_params[ac].uapsd;
+
+	addts_req = os_zalloc(sizeof(*addts_req));
+	if (!addts_req)
+		return NULL;
+
+	tspec = &addts_req->tspec;
+	os_memcpy(addts_req->address, address, ETH_ALEN);
+
+	/* The dialog token cannot be zero */
+	if (++wpa_s->wmm_ac_last_dialog_token == 0)
+		wpa_s->wmm_ac_last_dialog_token++;
+
+	addts_req->dialog_token = wpa_s->wmm_ac_last_dialog_token;
+	tspec->eid = WLAN_EID_VENDOR_SPECIFIC;
+	tspec->length = sizeof(*tspec) - 2; /* reduce eid and length */
+	tspec->oui[0] = 0x00;
+	tspec->oui[1] = 0x50;
+	tspec->oui[2] = 0xf2;
+	tspec->oui_type = WMM_OUI_TYPE;
+	tspec->oui_subtype = WMM_OUI_SUBTYPE_TSPEC_ELEMENT;
+	tspec->version = WMM_VERSION;
+
+	tspec->ts_info[0] = params->tsid << 1;
+	tspec->ts_info[0] |= params->direction << 5;
+	tspec->ts_info[0] |= WMM_AC_ACCESS_POLICY_EDCA << 7;
+	tspec->ts_info[1] = uapsd << 2;
+	tspec->ts_info[1] |= params->user_priority << 3;
+	tspec->ts_info[2] = 0;
+
+	tspec->nominal_msdu_size = host_to_le16(params->nominal_msdu_size);
+	if (params->fixed_nominal_msdu)
+		tspec->nominal_msdu_size |=
+			host_to_le16(WMM_AC_FIXED_MSDU_SIZE);
+
+	tspec->mean_data_rate = host_to_le32(params->mean_data_rate);
+	tspec->minimum_phy_rate = host_to_le32(params->minimum_phy_rate);
+	tspec->surplus_bandwidth_allowance =
+		host_to_le16(params->surplus_bandwidth_allowance);
+
+	return addts_req;
+}
+
+
+static int param_in_range(const char *name, long value,
+			  long min_val, long max_val)
+{
+	if (value < min_val || (max_val >= 0 && value > max_val)) {
+		wpa_printf(MSG_DEBUG,
+			   "WMM AC: param %s (%ld) is out of range (%ld-%ld)",
+			   name, value, min_val, max_val);
+		return 0;
+	}
+
+	return 1;
+}
+
+
+static int wmm_ac_should_replace_ts(struct wpa_supplicant *wpa_s,
+				    u8 tsid, u8 ac, u8 dir)
+{
+	enum ts_dir_idx idx;
+	int cur_ac, existing_ts = 0, replace_ts = 0;
+
+	cur_ac = wmm_ac_find_tsid(wpa_s, tsid, &idx);
+	if (cur_ac >= 0) {
+		if (cur_ac != ac) {
+			wpa_printf(MSG_DEBUG,
+				   "WMM AC: TSID %i already exists on different ac (%d)",
+				   tsid, cur_ac);
+			return -1;
+		}
+
+		/* same tsid - this tspec will replace the current one */
+		replace_ts |= BIT(idx);
+	}
+
+	for (idx = 0; idx < TS_DIR_IDX_COUNT; idx++) {
+		if (wpa_s->tspecs[ac][idx])
+			existing_ts |= BIT(idx);
+	}
+
+	switch (dir) {
+	case WMM_AC_DIR_UPLINK:
+		/* replace existing uplink/bidi tspecs */
+		replace_ts |= existing_ts & (BIT(TS_DIR_IDX_UPLINK) |
+					     BIT(TS_DIR_IDX_BIDI));
+		break;
+	case WMM_AC_DIR_DOWNLINK:
+		/* replace existing downlink/bidi tspecs */
+		replace_ts |= existing_ts & (BIT(TS_DIR_IDX_DOWNLINK) |
+					     BIT(TS_DIR_IDX_BIDI));
+		break;
+	case WMM_AC_DIR_BIDIRECTIONAL:
+		/* replace all existing tspecs */
+		replace_ts |= existing_ts;
+		break;
+	default:
+		return -1;
+	}
+
+	return replace_ts;
+}
+
+
+static int wmm_ac_ts_req_is_valid(struct wpa_supplicant *wpa_s,
+				  const struct wmm_ac_ts_setup_params *params)
+{
+	enum wmm_ac req_ac;
+
+#define PARAM_IN_RANGE(field, min_value, max_value) \
+	param_in_range(#field, params->field, min_value, max_value)
+
+	if (!PARAM_IN_RANGE(tsid, 0, WMM_AC_MAX_TID) ||
+	    !PARAM_IN_RANGE(user_priority, 0, WMM_AC_MAX_USER_PRIORITY) ||
+	    !PARAM_IN_RANGE(nominal_msdu_size, 1, WMM_AC_MAX_NOMINAL_MSDU) ||
+	    !PARAM_IN_RANGE(mean_data_rate, 1, -1) ||
+	    !PARAM_IN_RANGE(minimum_phy_rate, 1, -1) ||
+	    !PARAM_IN_RANGE(surplus_bandwidth_allowance, WMM_AC_MIN_SBA_UNITY,
+			    -1))
+		return 0;
+#undef PARAM_IN_RANGE
+
+	if (!(params->direction == WMM_TSPEC_DIRECTION_UPLINK ||
+	      params->direction == WMM_TSPEC_DIRECTION_DOWNLINK ||
+	      params->direction == WMM_TSPEC_DIRECTION_BI_DIRECTIONAL)) {
+		wpa_printf(MSG_DEBUG, "WMM AC: invalid TS direction: %d",
+			   params->direction);
+		return 0;
+	}
+
+	req_ac = up_to_ac[params->user_priority];
+
+	/* Requested accesss category must have acm */
+	if (!wpa_s->wmm_ac_assoc_info->ac_params[req_ac].acm) {
+		wpa_printf(MSG_DEBUG, "WMM AC: AC %d is not ACM", req_ac);
+		return 0;
+	}
+
+	if (wmm_ac_should_replace_ts(wpa_s, params->tsid, req_ac,
+				     params->direction) < 0)
+		return 0;
+
+	return 1;
+}
+
+
+static struct wmm_ac_assoc_data *
+wmm_ac_process_param_elem(struct wpa_supplicant *wpa_s, const u8 *ies,
+			  size_t ies_len)
+{
+	struct ieee802_11_elems elems;
+	struct wmm_parameter_element *wmm_params;
+	struct wmm_ac_assoc_data *assoc_data;
+	int i;
+
+	/* Parsing WMM Parameter Element */
+	if (ieee802_11_parse_elems(ies, ies_len, &elems, 1) == ParseFailed) {
+		wpa_printf(MSG_DEBUG, "WMM AC: could not parse assoc ies");
+		return NULL;
+	}
+
+	if (!elems.wmm) {
+		wpa_printf(MSG_DEBUG, "WMM AC: No WMM IE");
+		return NULL;
+	}
+
+	if (elems.wmm_len != sizeof(*wmm_params)) {
+		wpa_printf(MSG_DEBUG, "WMM AC: Invalid WMM ie length");
+		return NULL;
+	}
+
+	wmm_params = (struct wmm_parameter_element *)(elems.wmm);
+
+	assoc_data = os_zalloc(sizeof(*assoc_data));
+	if (!assoc_data)
+		return NULL;
+
+	for (i = 0; i < WMM_AC_NUM; i++)
+		assoc_data->ac_params[i].acm =
+			!!(wmm_params->ac[i].aci_aifsn & WMM_AC_ACM);
+
+	wpa_printf(MSG_DEBUG,
+		   "WMM AC: AC mandatory: AC_BE=%u AC_BK=%u AC_VI=%u AC_VO=%u",
+		   assoc_data->ac_params[WMM_AC_BE].acm,
+		   assoc_data->ac_params[WMM_AC_BK].acm,
+		   assoc_data->ac_params[WMM_AC_VI].acm,
+		   assoc_data->ac_params[WMM_AC_VO].acm);
+
+	return assoc_data;
+}
+
+
+static int wmm_ac_init(struct wpa_supplicant *wpa_s, const u8 *ies,
+		       size_t ies_len, const struct wmm_params *wmm_params)
+{
+	struct wmm_ac_assoc_data *assoc_data;
+	u8 ac;
+
+	if (wpa_s->wmm_ac_assoc_info) {
+		wpa_printf(MSG_ERROR, "WMM AC: Already initialized");
+		return -1;
+	}
+
+	if (!ies) {
+		wpa_printf(MSG_ERROR, "WMM AC: Missing IEs");
+		return -1;
+	}
+
+	if (!(wmm_params->info_bitmap & WMM_PARAMS_UAPSD_QUEUES_INFO)) {
+		wpa_printf(MSG_DEBUG, "WMM AC: Missing U-APSD configuration");
+		return -1;
+	}
+
+	os_memset(wpa_s->tspecs, 0, sizeof(wpa_s->tspecs));
+	wpa_s->wmm_ac_last_dialog_token = 0;
+	wpa_s->addts_request = NULL;
+
+	assoc_data = wmm_ac_process_param_elem(wpa_s, ies, ies_len);
+	if (!assoc_data)
+		return -1;
+
+	wpa_printf(MSG_DEBUG, "WMM AC: U-APSD queues=0x%x",
+		   wmm_params->uapsd_queues);
+
+	for (ac = 0; ac < WMM_AC_NUM; ac++) {
+		assoc_data->ac_params[ac].uapsd =
+			!!(wmm_params->uapsd_queues & BIT(ac));
+	}
+
+	wpa_s->wmm_ac_assoc_info = assoc_data;
+	return 0;
+}
+
+
+static void wmm_ac_del_ts(struct wpa_supplicant *wpa_s, u8 ac, int dir_bitmap)
+{
+	enum ts_dir_idx idx;
+
+	for (idx = 0; idx < TS_DIR_IDX_COUNT; idx++) {
+		if (!(dir_bitmap & BIT(idx)))
+			continue;
+
+		wmm_ac_del_ts_idx(wpa_s, ac, idx);
+	}
+}
+
+
+static void wmm_ac_deinit(struct wpa_supplicant *wpa_s)
+{
+	int i;
+
+	for (i = 0; i < WMM_AC_NUM; i++)
+		wmm_ac_del_ts(wpa_s, i, TS_DIR_IDX_ALL);
+
+	/* delete pending add_ts requset */
+	wmm_ac_del_req(wpa_s, 1);
+
+	os_free(wpa_s->wmm_ac_assoc_info);
+	wpa_s->wmm_ac_assoc_info = NULL;
+}
+
+
+void wmm_ac_notify_assoc(struct wpa_supplicant *wpa_s, const u8 *ies,
+			 size_t ies_len, const struct wmm_params *wmm_params)
+{
+	if (wmm_ac_init(wpa_s, ies, ies_len, wmm_params))
+		return;
+
+	wpa_printf(MSG_DEBUG,
+		   "WMM AC: Valid WMM association, WMM AC is enabled");
+}
+
+
+void wmm_ac_notify_disassoc(struct wpa_supplicant *wpa_s)
+{
+	if (!wpa_s->wmm_ac_assoc_info)
+		return;
+
+	wmm_ac_deinit(wpa_s);
+	wpa_printf(MSG_DEBUG, "WMM AC: WMM AC is disabled");
+}
+
+
+int wpas_wmm_ac_delts(struct wpa_supplicant *wpa_s, u8 tsid)
+{
+	struct wmm_tspec_element tspec;
+	int ac;
+	enum ts_dir_idx dir;
+
+	if (!wpa_s->wmm_ac_assoc_info) {
+		wpa_printf(MSG_DEBUG,
+			   "WMM AC: Failed to delete TS, WMM AC is disabled");
+		return -1;
+	}
+
+	ac = wmm_ac_find_tsid(wpa_s, tsid, &dir);
+	if (ac < 0) {
+		wpa_printf(MSG_DEBUG, "WMM AC: TS does not exist");
+		return -1;
+	}
+
+	tspec = *wpa_s->tspecs[ac][dir];
+
+	wmm_ac_del_ts_idx(wpa_s, ac, dir);
+
+	wmm_ac_send_delts(wpa_s, &tspec, wpa_s->bssid);
+
+	return 0;
+}
+
+
+int wpas_wmm_ac_addts(struct wpa_supplicant *wpa_s,
+		      struct wmm_ac_ts_setup_params *params)
+{
+	struct wmm_ac_addts_request *addts_req;
+
+	if (!wpa_s->wmm_ac_assoc_info) {
+		wpa_printf(MSG_DEBUG,
+			   "WMM AC: Cannot add TS - missing assoc data");
+		return -1;
+	}
+
+	if (wpa_s->addts_request) {
+		wpa_printf(MSG_DEBUG,
+			   "WMM AC: can't add TS - ADDTS request is already pending");
+		return -1;
+	}
+
+	/*
+	 * we can setup downlink TS even without driver support.
+	 * however, we need driver support for the other directions.
+	 */
+	if (params->direction != WMM_AC_DIR_DOWNLINK &&
+	    !wpa_s->wmm_ac_supported) {
+		wpa_printf(MSG_DEBUG,
+			   "Cannot set uplink/bidi TS without driver support");
+		return -1;
+	}
+
+	if (!wmm_ac_ts_req_is_valid(wpa_s, params))
+		return -1;
+
+	wpa_printf(MSG_DEBUG, "WMM AC: TS setup request (addr=" MACSTR
+		   " tsid=%u user priority=%u direction=%d)",
+		   MAC2STR(wpa_s->bssid), params->tsid,
+		   params->user_priority, params->direction);
+
+	addts_req = wmm_ac_build_addts_req(wpa_s, params, wpa_s->bssid);
+	if (!addts_req)
+		return -1;
+
+	if (wmm_ac_send_addts_request(wpa_s, addts_req))
+		goto err;
+
+	/* save as pending and set ADDTS resp timeout to 1 second */
+	wpa_s->addts_request = addts_req;
+	eloop_register_timeout(1, 0, wmm_ac_addts_req_timeout,
+			       wpa_s, addts_req);
+	return 0;
+err:
+	os_free(addts_req);
+	return -1;
+}
+
+
+static void wmm_ac_handle_delts(struct wpa_supplicant *wpa_s, const u8 *sa,
+				const struct wmm_tspec_element *tspec)
+{
+	int ac;
+	u8 tsid;
+	enum ts_dir_idx idx;
+
+	tsid = wmm_ac_get_tsid(tspec);
+
+	wpa_printf(MSG_DEBUG,
+		   "WMM AC: DELTS frame has been received TSID=%u addr="
+		   MACSTR, tsid, MAC2STR(sa));
+
+	ac = wmm_ac_find_tsid(wpa_s, tsid, &idx);
+	if (ac < 0) {
+		wpa_printf(MSG_DEBUG,
+			   "WMM AC: Ignoring DELTS frame - TSID does not exist");
+		return;
+	}
+
+	wmm_ac_del_ts_idx(wpa_s, ac, idx);
+
+	wpa_printf(MSG_DEBUG,
+		   "TS was deleted successfully (tsid=%u address=" MACSTR ")",
+		   tsid, MAC2STR(sa));
+}
+
+
+static void wmm_ac_handle_addts_resp(struct wpa_supplicant *wpa_s, const u8 *sa,
+		const u8 resp_dialog_token, const u8 status_code,
+		const struct wmm_tspec_element *tspec)
+{
+	struct wmm_ac_addts_request *req = wpa_s->addts_request;
+	u8 ac, tsid, up, dir;
+	int replace_tspecs;
+
+	tsid = wmm_ac_get_tsid(tspec);
+	dir = wmm_ac_get_direction(tspec);
+	up = wmm_ac_get_user_priority(tspec);
+	ac = up_to_ac[up];
+
+	/* make sure we have a matching addts request */
+	if (!req || req->dialog_token != resp_dialog_token) {
+		wpa_printf(MSG_DEBUG,
+			   "WMM AC: no req with dialog=%u, ignoring frame",
+			   resp_dialog_token);
+		return;
+	}
+
+	/* make sure the params are the same */
+	if (os_memcmp(req->address, sa, ETH_ALEN) != 0 ||
+	    tsid != wmm_ac_get_tsid(&req->tspec) ||
+	    up != wmm_ac_get_user_priority(&req->tspec) ||
+	    dir != wmm_ac_get_direction(&req->tspec)) {
+		wpa_printf(MSG_DEBUG,
+			   "WMM AC: ADDTS params do not match, ignoring frame");
+		return;
+	}
+
+	/* delete pending request */
+	wmm_ac_del_req(wpa_s, 0);
+
+	wpa_printf(MSG_DEBUG,
+		   "ADDTS response status=%d tsid=%u up=%u direction=%u",
+		   status_code, tsid, up, dir);
+
+	if (status_code != WMM_ADDTS_STATUS_ADMISSION_ACCEPTED) {
+		wpa_printf(MSG_INFO, "WMM AC: ADDTS request was rejected");
+		goto err_msg;
+	}
+
+	replace_tspecs = wmm_ac_should_replace_ts(wpa_s, tsid, ac, dir);
+	if (replace_tspecs < 0)
+		goto err_delts;
+
+	wpa_printf(MSG_DEBUG, "ts idx replace bitmap: 0x%x", replace_tspecs);
+
+	/* when replacing tspecs - delete first */
+	wmm_ac_del_ts(wpa_s, ac, replace_tspecs);
+
+	/* Creating a new traffic stream */
+	wpa_printf(MSG_DEBUG,
+		   "WMM AC: adding a new TS with TSID=%u address="MACSTR
+		   " medium time=%u access category=%d dir=%d ",
+		   tsid, MAC2STR(sa),
+		   le_to_host16(tspec->medium_time), ac, dir);
+
+	if (wmm_ac_add_ts(wpa_s, sa, tspec))
+		goto err_delts;
+
+	return;
+
+err_delts:
+	/* ask the ap to delete the tspec */
+	wmm_ac_send_delts(wpa_s, tspec, sa);
+err_msg:
+	wpa_msg(wpa_s, MSG_INFO, WMM_AC_EVENT_TSPEC_REQ_FAILED "tsid=%u",
+		tsid);
+}
+
+
+void wmm_ac_rx_action(struct wpa_supplicant *wpa_s, const u8 *da,
+			const u8 *sa, const u8 *data, size_t len)
+{
+	u8 action;
+	u8 dialog_token;
+	u8 status_code;
+	struct ieee802_11_elems elems;
+	struct wmm_tspec_element *tspec;
+
+	if (wpa_s->wmm_ac_assoc_info == NULL) {
+		wpa_printf(MSG_DEBUG,
+			   "WMM AC: WMM AC is disabled, ignoring action frame");
+		return;
+	}
+
+	action = data[0];
+
+	if (action != WMM_ACTION_CODE_ADDTS_RESP &&
+	    action != WMM_ACTION_CODE_DELTS) {
+		wpa_printf(MSG_DEBUG,
+			   "WMM AC: Unknown action (%d), ignoring action frame",
+			   action);
+		return;
+	}
+
+	/* WMM AC action frame */
+	if (os_memcmp(da, wpa_s->own_addr, ETH_ALEN) != 0) {
+		wpa_printf(MSG_DEBUG, "WMM AC: frame destination addr="MACSTR
+			   " is other than ours, ignoring frame", MAC2STR(da));
+		return;
+	}
+
+	if (os_memcmp(sa, wpa_s->bssid, ETH_ALEN) != 0) {
+		wpa_printf(MSG_DEBUG, "WMM AC: ignore frame with sa " MACSTR
+			   " different other than our bssid", MAC2STR(da));
+		return;
+	}
+
+	if (len < 2 + sizeof(struct wmm_tspec_element)) {
+		wpa_printf(MSG_DEBUG,
+			   "WMM AC: Short ADDTS response ignored (len=%lu)",
+			   (unsigned long) len);
+		return;
+	}
+
+	data++;
+	len--;
+	dialog_token = data[0];
+	status_code = data[1];
+
+	if (ieee802_11_parse_elems(data + 2, len - 2, &elems, 1) != ParseOK) {
+		wpa_printf(MSG_DEBUG,
+			   "WMM AC: Could not parse WMM AC action from " MACSTR,
+			   MAC2STR(sa));
+		return;
+	}
+
+	/* the struct also contains the type and value, so decrease it */
+	if (elems.wmm_tspec_len != sizeof(struct wmm_tspec_element) - 2) {
+		wpa_printf(MSG_DEBUG, "WMM AC: missing or wrong length TSPEC");
+		return;
+	}
+
+	tspec = (struct wmm_tspec_element *)(elems.wmm_tspec - 2);
+
+	wpa_printf(MSG_DEBUG, "WMM AC: RX WMM AC Action from " MACSTR,
+		   MAC2STR(sa));
+	wpa_hexdump(MSG_MSGDUMP, "WMM AC: WMM AC Action content", data, len);
+
+	switch (action) {
+	case WMM_ACTION_CODE_ADDTS_RESP:
+		wmm_ac_handle_addts_resp(wpa_s, sa, dialog_token, status_code,
+					 tspec);
+		break;
+	case WMM_ACTION_CODE_DELTS:
+		wmm_ac_handle_delts(wpa_s, sa, tspec);
+		break;
+	default:
+		break;
+	}
+}
+
+
+static const char * get_ac_str(u8 ac)
+{
+	switch (ac) {
+	case WMM_AC_BE:
+		return "BE";
+	case WMM_AC_BK:
+		return "BK";
+	case WMM_AC_VI:
+		return "VI";
+	case WMM_AC_VO:
+		return "VO";
+	default:
+		return "N/A";
+	}
+}
+
+
+static const char * get_direction_str(u8 direction)
+{
+	switch (direction) {
+	case WMM_AC_DIR_DOWNLINK:
+		return "Downlink";
+	case WMM_AC_DIR_UPLINK:
+		return "Uplink";
+	case WMM_AC_DIR_BIDIRECTIONAL:
+		return "Bi-directional";
+	default:
+		return "N/A";
+	}
+}
+
+
+int wpas_wmm_ac_status(struct wpa_supplicant *wpa_s, char *buf, size_t buflen)
+{
+	struct wmm_ac_assoc_data *assoc_info = wpa_s->wmm_ac_assoc_info;
+	enum ts_dir_idx idx;
+	int pos = 0;
+	u8 ac, up;
+
+	if (!assoc_info) {
+		return wpa_scnprintf(buf, buflen - pos,
+				     "Not associated to a WMM AP, WMM AC is Disabled\n");
+	}
+
+	pos += wpa_scnprintf(buf + pos, buflen - pos, "WMM AC is Enabled\n");
+
+	for (ac = 0; ac < WMM_AC_NUM; ac++) {
+		int ts_count = 0;
+
+		pos += wpa_scnprintf(buf + pos, buflen - pos,
+				     "%s: acm=%d uapsd=%d\n",
+				     get_ac_str(ac),
+				     assoc_info->ac_params[ac].acm,
+				     assoc_info->ac_params[ac].uapsd);
+
+		for (idx = 0; idx < TS_DIR_IDX_COUNT; idx++) {
+			struct wmm_tspec_element *tspec;
+			u8 dir, tsid;
+			const char *dir_str;
+
+			tspec = wpa_s->tspecs[ac][idx];
+			if (!tspec)
+				continue;
+
+			ts_count++;
+
+			dir = wmm_ac_get_direction(tspec);
+			dir_str = get_direction_str(dir);
+			tsid = wmm_ac_get_tsid(tspec);
+			up = wmm_ac_get_user_priority(tspec);
+
+			pos += wpa_scnprintf(buf + pos, buflen - pos,
+					     "\tTSID=%u UP=%u\n"
+					     "\tAddress = "MACSTR"\n"
+					     "\tWMM AC dir = %s\n"
+					     "\tTotal admitted time = %u\n\n",
+					     tsid, up,
+					     MAC2STR(wpa_s->bssid),
+					     dir_str,
+					     le_to_host16(tspec->medium_time));
+		}
+
+		if (!ts_count) {
+			pos += wpa_scnprintf(buf + pos, buflen - pos,
+					     "\t(No Traffic Stream)\n\n");
+		}
+	}
+
+	return pos;
+}
+
+
+static u8 wmm_ac_get_tspecs_count(struct wpa_supplicant *wpa_s)
+{
+	int ac, dir, tspecs_count = 0;
+
+	for (ac = 0; ac < WMM_AC_NUM; ac++) {
+		for (dir = 0; dir < TS_DIR_IDX_COUNT; dir++) {
+			if (wpa_s->tspecs[ac][dir])
+				tspecs_count++;
+		}
+	}
+
+	return tspecs_count;
+}
+
+
+void wmm_ac_save_tspecs(struct wpa_supplicant *wpa_s)
+{
+	int ac, dir, tspecs_count;
+
+	wpa_printf(MSG_DEBUG, "WMM AC: Save last configured tspecs");
+
+	if (!wpa_s->wmm_ac_assoc_info)
+		return;
+
+	tspecs_count = wmm_ac_get_tspecs_count(wpa_s);
+	if (!tspecs_count) {
+		wpa_printf(MSG_DEBUG, "WMM AC: No configured TSPECs");
+		return;
+	}
+
+	wpa_printf(MSG_DEBUG, "WMM AC: Saving tspecs");
+
+	wmm_ac_clear_saved_tspecs(wpa_s);
+	wpa_s->last_tspecs = os_calloc(tspecs_count,
+				       sizeof(*wpa_s->last_tspecs));
+	if (!wpa_s->last_tspecs) {
+		wpa_printf(MSG_ERROR, "WMM AC: Failed to save tspecs!");
+		return;
+	}
+
+	for (ac = 0; ac < WMM_AC_NUM; ac++) {
+		for (dir = 0; dir < TS_DIR_IDX_COUNT; dir++) {
+			if (!wpa_s->tspecs[ac][dir])
+				continue;
+
+			wpa_s->last_tspecs[wpa_s->last_tspecs_count++] =
+				*wpa_s->tspecs[ac][dir];
+		}
+	}
+
+	wpa_printf(MSG_DEBUG, "WMM AC: Successfully saved %d TSPECs",
+		   wpa_s->last_tspecs_count);
+}
+
+
+void wmm_ac_clear_saved_tspecs(struct wpa_supplicant *wpa_s)
+{
+	if (wpa_s->last_tspecs) {
+		wpa_printf(MSG_DEBUG, "WMM AC: Clear saved tspecs");
+		os_free(wpa_s->last_tspecs);
+		wpa_s->last_tspecs = NULL;
+		wpa_s->last_tspecs_count = 0;
+	}
+}
+
+
+int wmm_ac_restore_tspecs(struct wpa_supplicant *wpa_s)
+{
+	unsigned int i;
+
+	if (!wpa_s->wmm_ac_assoc_info || !wpa_s->last_tspecs_count)
+		return 0;
+
+	wpa_printf(MSG_DEBUG, "WMM AC: Restore %u saved tspecs",
+		   wpa_s->last_tspecs_count);
+
+	for (i = 0; i < wpa_s->last_tspecs_count; i++)
+		wmm_ac_add_ts(wpa_s, wpa_s->bssid, &wpa_s->last_tspecs[i]);
+
+	return 0;
+}
diff --git a/wpa_supplicant/wmm_ac.h b/wpa_supplicant/wmm_ac.h
new file mode 100644
index 0000000..5171b16
--- /dev/null
+++ b/wpa_supplicant/wmm_ac.h
@@ -0,0 +1,176 @@
+/*
+ * Wi-Fi Multimedia Admission Control (WMM-AC)
+ * Copyright(c) 2014, Intel Mobile Communication GmbH.
+ * Copyright(c) 2014, Intel Corporation. All rights reserved.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef WMM_AC_H
+#define WMM_AC_H
+
+#include "common/ieee802_11_defs.h"
+#include "drivers/driver.h"
+
+struct wpa_supplicant;
+
+#define WMM_AC_ACCESS_POLICY_EDCA 1
+#define WMM_AC_FIXED_MSDU_SIZE BIT(15)
+
+#define WMM_AC_MAX_TID 7
+#define WMM_AC_MAX_USER_PRIORITY 7
+#define WMM_AC_MIN_SBA_UNITY 0x2000
+#define WMM_AC_MAX_NOMINAL_MSDU 32767
+
+/**
+ * struct wmm_ac_assoc_data - WMM Admission Control Association Data
+ *
+ * This struct will store any relevant WMM association data needed by WMM AC.
+ * In case there is a valid WMM association, an instance of this struct will be
+ * created. In case there is no instance of this struct, the station is not
+ * associated to a valid WMM BSS and hence, WMM AC will not be used.
+ */
+struct wmm_ac_assoc_data {
+	struct {
+		/*
+		 * acm - Admission Control Mandatory
+		 * In case an access category is ACM, the traffic will have
+		 * to be admitted by WMM-AC's admission mechanism before use.
+		 */
+		unsigned int acm:1;
+
+		/*
+		 * uapsd_queues - Unscheduled Automatic Power Save Delivery
+		 *		  queues.
+		 * Indicates whether ACs are configured for U-APSD (or legacy
+		 * PS). Storing this value is necessary in order to set the
+		 * Power Save Bit (PSB) in ADDTS request Action frames (if not
+		 * given).
+		 */
+		unsigned int uapsd:1;
+	} ac_params[WMM_AC_NUM];
+};
+
+/**
+ * wmm_ac_dir - WMM Admission Control Direction
+ */
+enum wmm_ac_dir {
+	WMM_AC_DIR_UPLINK = 0,
+	WMM_AC_DIR_DOWNLINK = 1,
+	WMM_AC_DIR_BIDIRECTIONAL = 3
+};
+
+/**
+ * ts_dir_idx - indices of internally saved tspecs
+ *
+ * we can have multiple tspecs (downlink + uplink) per ac.
+ * save them in array, and use the enum to directly access
+ * the respective tspec slot (according to the direction).
+ */
+enum ts_dir_idx {
+	TS_DIR_IDX_UPLINK,
+	TS_DIR_IDX_DOWNLINK,
+	TS_DIR_IDX_BIDI,
+
+	TS_DIR_IDX_COUNT
+};
+#define TS_DIR_IDX_ALL (BIT(TS_DIR_IDX_COUNT) - 1)
+
+/**
+ * struct wmm_ac_addts_request - ADDTS Request Information
+ *
+ * The last sent ADDTS request(s) will be saved as element(s) of this struct in
+ * order to be compared with the received ADDTS response in ADDTS response
+ * action frame handling and should be stored until that point.
+ * In case a new traffic stream will be created/replaced/updated, only its
+ * relevant traffic stream information will be stored as a wmm_ac_ts struct.
+ */
+struct wmm_ac_addts_request {
+	/*
+	 * dialog token - Used to link the recived ADDTS response with this
+	 * saved ADDTS request when ADDTS response is being handled
+	 */
+	u8 dialog_token;
+
+	/*
+	 * address - The alleged traffic stream's receiver/transmitter address
+	 * Address and TID are used to identify the TS (TID is contained in
+	 * TSPEC)
+	 */
+	u8 address[ETH_ALEN];
+
+	/*
+	 * tspec - Traffic Stream Specification, will be used to compare the
+	 * sent TSPEC in ADDTS request to the received TSPEC in ADDTS response
+	 * and act accordingly in ADDTS response handling
+	 */
+	struct wmm_tspec_element tspec;
+};
+
+
+/**
+ * struct wmm_ac_ts_setup_params - TS setup parameters
+ *
+ * This struct holds parameters which should be provided
+ * to wmm_ac_ts_setup in order to setup a traffic stream
+ */
+struct wmm_ac_ts_setup_params {
+	/*
+	 * tsid - Traffic ID
+	 * TID and address are used to identify the TS
+	 */
+	int tsid;
+
+	/*
+	 * direction - Traffic Stream's direction
+	 */
+	enum wmm_ac_dir direction;
+
+	/*
+	 * user_priority - Traffic Stream's user priority
+	 */
+	int user_priority;
+
+	/*
+	 * nominal_msdu_size - Nominal MAC service data unit size
+	 */
+	int nominal_msdu_size;
+
+	/*
+	 * fixed_nominal_msdu - Whether the size is fixed
+	 * 0 = Nominal MSDU size is not fixed
+	 * 1 = Nominal MSDU size is fixed
+	 */
+	int fixed_nominal_msdu;
+
+	/*
+	 * surplus_bandwidth_allowance - Specifies excess time allocation
+	 */
+	int mean_data_rate;
+
+	/*
+	 * minimum_phy_rate - Specifies the minimum supported PHY rate in bps
+	 */
+	int minimum_phy_rate;
+
+	/*
+	 * surplus_bandwidth_allowance - Specifies excess time allocation
+	 */
+	int surplus_bandwidth_allowance;
+};
+
+void wmm_ac_notify_assoc(struct wpa_supplicant *wpa_s, const u8 *ies,
+			 size_t ies_len, const struct wmm_params *wmm_params);
+void wmm_ac_notify_disassoc(struct wpa_supplicant *wpa_s);
+int wpas_wmm_ac_addts(struct wpa_supplicant *wpa_s,
+		      struct wmm_ac_ts_setup_params *params);
+int wpas_wmm_ac_delts(struct wpa_supplicant *wpa_s, u8 tsid);
+void wmm_ac_rx_action(struct wpa_supplicant *wpa_s, const u8 *da,
+			const u8 *sa, const u8 *data, size_t len);
+int wpas_wmm_ac_status(struct wpa_supplicant *wpa_s, char *buf, size_t buflen);
+void wmm_ac_save_tspecs(struct wpa_supplicant *wpa_s);
+void wmm_ac_clear_saved_tspecs(struct wpa_supplicant *wpa_s);
+int wmm_ac_restore_tspecs(struct wpa_supplicant *wpa_s);
+
+#endif /* WMM_AC_H */
diff --git a/wpa_supplicant/wnm_sta.c b/wpa_supplicant/wnm_sta.c
index e08480a..7d79499 100644
--- a/wpa_supplicant/wnm_sta.c
+++ b/wpa_supplicant/wnm_sta.c
@@ -10,6 +10,7 @@
 
 #include "utils/common.h"
 #include "common/ieee802_11_defs.h"
+#include "common/ieee802_11_common.h"
 #include "common/wpa_ctrl.h"
 #include "rsn_supp/wpa.h"
 #include "wpa_supplicant_i.h"
@@ -250,6 +251,7 @@
 	/* multiple TFS Resp IE (assuming consecutive) */
 	u8 *tfsresp_ie_start = NULL;
 	u8 *tfsresp_ie_end = NULL;
+	size_t left;
 
 	if (len < 3)
 		return;
@@ -257,11 +259,12 @@
 
 	wpa_printf(MSG_DEBUG, "WNM-Sleep Mode Response token=%u key_len_total=%d",
 		   frm[0], key_len_total);
-	pos += 3 + key_len_total;
-	if (pos > frm + len) {
+	left = len - 3;
+	if (key_len_total > left) {
 		wpa_printf(MSG_INFO, "WNM: Too short frame for Key Data field");
 		return;
 	}
+	pos += 3 + key_len_total;
 	while (pos - frm < len) {
 		u8 ie_len = *(pos + 1);
 		if (pos + 2 + ie_len > frm + len) {
@@ -315,13 +318,7 @@
 	int i;
 
 	for (i = 0; i < wpa_s->wnm_num_neighbor_report; i++) {
-		os_free(wpa_s->wnm_neighbor_report_elements[i].tsf_info);
-		os_free(wpa_s->wnm_neighbor_report_elements[i].con_coun_str);
-		os_free(wpa_s->wnm_neighbor_report_elements[i].bss_tran_can);
-		os_free(wpa_s->wnm_neighbor_report_elements[i].bss_term_dur);
-		os_free(wpa_s->wnm_neighbor_report_elements[i].bearing);
 		os_free(wpa_s->wnm_neighbor_report_elements[i].meas_pilot);
-		os_free(wpa_s->wnm_neighbor_report_elements[i].rrm_cap);
 		os_free(wpa_s->wnm_neighbor_report_elements[i].mul_bssid);
 	}
 
@@ -340,12 +337,9 @@
 			wpa_printf(MSG_DEBUG, "WNM: Too short TSF");
 			break;
 		}
-		os_free(rep->tsf_info);
-		rep->tsf_info = os_zalloc(sizeof(struct tsf_info));
-		if (rep->tsf_info == NULL)
-			break;
-		os_memcpy(rep->tsf_info->tsf_offset, pos, 2);
-		os_memcpy(rep->tsf_info->beacon_interval, pos + 2, 2);
+		rep->tsf_offset = WPA_GET_LE16(pos);
+		rep->beacon_int = WPA_GET_LE16(pos + 2);
+		rep->tsf_present = 1;
 		break;
 	case WNM_NEIGHBOR_CONDENSED_COUNTRY_STRING:
 		if (elen < 2) {
@@ -353,12 +347,8 @@
 				   "country string");
 			break;
 		}
-		os_free(rep->con_coun_str);
-		rep->con_coun_str =
-			os_zalloc(sizeof(struct condensed_country_string));
-		if (rep->con_coun_str == NULL)
-			break;
-		os_memcpy(rep->con_coun_str->country_string, pos, 2);
+		os_memcpy(rep->country, pos, 2);
+		rep->country_present = 1;
 		break;
 	case WNM_NEIGHBOR_BSS_TRANSITION_CANDIDATE:
 		if (elen < 1) {
@@ -366,25 +356,13 @@
 				   "candidate");
 			break;
 		}
-		os_free(rep->bss_tran_can);
-		rep->bss_tran_can =
-			os_zalloc(sizeof(struct bss_transition_candidate));
-		if (rep->bss_tran_can == NULL)
-			break;
-		rep->bss_tran_can->preference = pos[0];
+		rep->preference = pos[0];
+		rep->preference_present = 1;
 		break;
 	case WNM_NEIGHBOR_BSS_TERMINATION_DURATION:
-		if (elen < 10) {
-			wpa_printf(MSG_DEBUG, "WNM: Too short BSS termination "
-				   "duration");
-			break;
-		}
-		os_free(rep->bss_term_dur);
-		rep->bss_term_dur =
-			os_zalloc(sizeof(struct bss_termination_duration));
-		if (rep->bss_term_dur == NULL)
-			break;
-		os_memcpy(rep->bss_term_dur->duration, pos, 10);
+		rep->bss_term_tsf = WPA_GET_LE64(pos);
+		rep->bss_term_dur = WPA_GET_LE16(pos + 8);
+		rep->bss_term_present = 1;
 		break;
 	case WNM_NEIGHBOR_BEARING:
 		if (elen < 8) {
@@ -392,11 +370,10 @@
 				   "bearing");
 			break;
 		}
-		os_free(rep->bearing);
-		rep->bearing = os_zalloc(sizeof(struct bearing));
-		if (rep->bearing == NULL)
-			break;
-		os_memcpy(rep->bearing->bearing, pos, 8);
+		rep->bearing = WPA_GET_LE16(pos);
+		rep->distance = WPA_GET_LE32(pos + 2);
+		rep->rel_height = WPA_GET_LE16(pos + 2 + 4);
+		rep->bearing_present = 1;
 		break;
 	case WNM_NEIGHBOR_MEASUREMENT_PILOT:
 		if (elen < 1) {
@@ -418,12 +395,8 @@
 				   "capabilities");
 			break;
 		}
-		os_free(rep->rrm_cap);
-		rep->rrm_cap =
-			os_zalloc(sizeof(struct rrm_enabled_capabilities));
-		if (rep->rrm_cap == NULL)
-			break;
-		os_memcpy(rep->rrm_cap->capabilities, pos, 5);
+		os_memcpy(rep->rm_capab, pos, 5);
+		rep->rm_capab_present = 1;
 		break;
 	case WNM_NEIGHBOR_MULTIPLE_BSSID:
 		if (elen < 1) {
@@ -442,6 +415,22 @@
 }
 
 
+static int wnm_nei_get_chan(struct wpa_supplicant *wpa_s, u8 op_class, u8 chan)
+{
+	struct wpa_bss *bss = wpa_s->current_bss;
+	const char *country = NULL;
+
+	if (bss) {
+		const u8 *elem = wpa_bss_get_ie(bss, WLAN_EID_COUNTRY);
+
+		if (elem && elem[1] >= 2)
+			country = (const char *) (elem + 2);
+	}
+
+	return ieee80211_chan_to_freq(country, op_class, chan);
+}
+
+
 static void wnm_parse_neighbor_report(struct wpa_supplicant *wpa_s,
 				      const u8 *pos, u8 len,
 				      struct neighbor_report *rep)
@@ -454,7 +443,7 @@
 	}
 
 	os_memcpy(rep->bssid, pos, ETH_ALEN);
-	os_memcpy(rep->bssid_information, pos + ETH_ALEN, 4);
+	rep->bssid_info = WPA_GET_LE32(pos + ETH_ALEN);
 	rep->regulatory_class = *(pos + 10);
 	rep->channel_number = *(pos + 11);
 	rep->phy_type = *(pos + 12);
@@ -478,47 +467,78 @@
 		left -= elen;
 		pos += elen;
 	}
+
+	rep->freq = wnm_nei_get_chan(wpa_s, rep->regulatory_class,
+				     rep->channel_number);
 }
 
 
-static int compare_scan_neighbor_results(struct wpa_supplicant *wpa_s,
-					 struct wpa_scan_results *scan_res,
-					 struct neighbor_report *neigh_rep,
-					 u8 num_neigh_rep, u8 *bssid_to_connect)
+static struct wpa_bss *
+compare_scan_neighbor_results(struct wpa_supplicant *wpa_s)
 {
 
-	u8 i, j;
+	u8 i;
+	struct wpa_bss *bss = wpa_s->current_bss;
+	struct wpa_bss *target;
 
-	if (scan_res == NULL || num_neigh_rep == 0 || !wpa_s->current_bss)
+	if (!bss)
 		return 0;
 
 	wpa_printf(MSG_DEBUG, "WNM: Current BSS " MACSTR " RSSI %d",
-		   MAC2STR(wpa_s->bssid), wpa_s->current_bss->level);
+		   MAC2STR(wpa_s->bssid), bss->level);
 
-	for (i = 0; i < num_neigh_rep; i++) {
-		for (j = 0; j < scan_res->num; j++) {
-			/* Check for a better RSSI AP */
-			if (os_memcmp(scan_res->res[j]->bssid,
-				      neigh_rep[i].bssid, ETH_ALEN) == 0 &&
-			    scan_res->res[j]->level >
-			    wpa_s->current_bss->level) {
-				/* Got a BSSID with better RSSI value */
-				os_memcpy(bssid_to_connect, neigh_rep[i].bssid,
-					  ETH_ALEN);
-				wpa_printf(MSG_DEBUG, "Found a BSS " MACSTR
-					   " with better scan RSSI %d",
-					   MAC2STR(scan_res->res[j]->bssid),
-					   scan_res->res[j]->level);
-				return 1;
-			}
-			wpa_printf(MSG_DEBUG, "scan_res[%d] " MACSTR
-				   " RSSI %d", j,
-				   MAC2STR(scan_res->res[j]->bssid),
-				   scan_res->res[j]->level);
+	for (i = 0; i < wpa_s->wnm_num_neighbor_report; i++) {
+		struct neighbor_report *nei;
+
+		nei = &wpa_s->wnm_neighbor_report_elements[i];
+		if (nei->preference_present && nei->preference == 0) {
+			wpa_printf(MSG_DEBUG, "Skip excluded BSS " MACSTR,
+				   MAC2STR(nei->bssid));
+			continue;
 		}
+
+		target = wpa_bss_get_bssid(wpa_s, nei->bssid);
+		if (!target) {
+			wpa_printf(MSG_DEBUG, "Candidate BSS " MACSTR
+				   " (pref %d) not found in scan results",
+				   MAC2STR(nei->bssid),
+				   nei->preference_present ? nei->preference :
+				   -1);
+			continue;
+		}
+
+		if (bss->ssid_len != target->ssid_len ||
+		    os_memcmp(bss->ssid, target->ssid, bss->ssid_len) != 0) {
+			/*
+			 * TODO: Could consider allowing transition to another
+			 * ESS if PMF was enabled for the association.
+			 */
+			wpa_printf(MSG_DEBUG, "Candidate BSS " MACSTR
+				   " (pref %d) in different ESS",
+				   MAC2STR(nei->bssid),
+				   nei->preference_present ? nei->preference :
+				   -1);
+			continue;
+		}
+
+		if (target->level < bss->level && target->level < -80) {
+			wpa_printf(MSG_DEBUG, "Candidate BSS " MACSTR
+				   " (pref %d) does not have sufficient signal level (%d)",
+				   MAC2STR(nei->bssid),
+				   nei->preference_present ? nei->preference :
+				   -1,
+				   target->level);
+			continue;
+		}
+
+		wpa_printf(MSG_DEBUG,
+			   "WNM: Found an acceptable preferred transition candidate BSS "
+			   MACSTR " (RSSI %d)",
+			   MAC2STR(nei->bssid), target->level);
+		return target;
 	}
 
-	return 0;
+	return NULL;
 }
 
 
@@ -530,10 +550,16 @@
 	u8 buf[1000], *pos;
 	struct ieee80211_mgmt *mgmt;
 	size_t len;
+	int res;
 
 	wpa_printf(MSG_DEBUG, "WNM: Send BSS Transition Management Response "
 		   "to " MACSTR " dialog_token=%u status=%u delay=%d",
 		   MAC2STR(wpa_s->bssid), dialog_token, status, delay);
+	if (!wpa_s->current_bss) {
+		wpa_printf(MSG_DEBUG,
+			   "WNM: Current BSS not known - drop response");
+		return;
+	}
 
 	mgmt = (struct ieee80211_mgmt *) buf;
 	os_memset(&buf, 0, sizeof(buf));
@@ -563,62 +589,203 @@
 
 	len = pos - (u8 *) &mgmt->u.action.category;
 
-	wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid,
-			    wpa_s->own_addr, wpa_s->bssid,
-			    &mgmt->u.action.category, len, 0);
+	res = wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid,
+				  wpa_s->own_addr, wpa_s->bssid,
+				  &mgmt->u.action.category, len, 0);
+	if (res < 0) {
+		wpa_printf(MSG_DEBUG,
+			   "WNM: Failed to send BSS Transition Management Response");
+	}
 }
 
 
-void wnm_scan_response(struct wpa_supplicant *wpa_s,
-		       struct wpa_scan_results *scan_res)
+int wnm_scan_process(struct wpa_supplicant *wpa_s, int reply_on_fail)
 {
-	u8 bssid[ETH_ALEN];
+	struct wpa_bss *bss;
+	struct wpa_ssid *ssid = wpa_s->current_ssid;
+	enum bss_trans_mgmt_status_code status = WNM_BSS_TM_REJECT_UNSPECIFIED;
 
-	if (scan_res == NULL) {
-		wpa_printf(MSG_ERROR, "Scan result is NULL");
-		goto send_bss_resp_fail;
+	if (!wpa_s->wnm_neighbor_report_elements)
+		return 0;
+
+	if (os_reltime_before(&wpa_s->wnm_cand_valid_until,
+			      &wpa_s->scan_trigger_time)) {
+		wpa_printf(MSG_DEBUG, "WNM: Previously stored BSS transition candidate list is not valid anymore - drop it");
+		wnm_deallocate_memory(wpa_s);
+		return 0;
+	}
+
+	if (!wpa_s->current_bss ||
+	    os_memcmp(wpa_s->wnm_cand_from_bss, wpa_s->current_bss->bssid,
+		      ETH_ALEN) != 0) {
+		wpa_printf(MSG_DEBUG, "WNM: Stored BSS transition candidate list not from the current BSS - ignore it");
+		return 0;
 	}
 
 	/* Compare the Neighbor Report and scan results */
-	if (compare_scan_neighbor_results(wpa_s, scan_res,
-					  wpa_s->wnm_neighbor_report_elements,
-					  wpa_s->wnm_num_neighbor_report,
-					  bssid) == 1) {
-		/* Associate to the network */
-		struct wpa_bss *bss;
-		struct wpa_ssid *ssid = wpa_s->current_ssid;
+	bss = compare_scan_neighbor_results(wpa_s);
+	if (!bss) {
+		wpa_printf(MSG_DEBUG, "WNM: No BSS transition candidate match found");
+		status = WNM_BSS_TM_REJECT_NO_SUITABLE_CANDIDATES;
+		goto send_bss_resp_fail;
+	}
 
-		bss = wpa_bss_get_bssid(wpa_s, bssid);
-		if (!bss) {
-			wpa_printf(MSG_DEBUG, "WNM: Target AP not found from "
-				   "BSS table");
-			goto send_bss_resp_fail;
-		}
-
-		/* Send the BSS Management Response - Accept */
-		if (wpa_s->wnm_reply) {
-			wnm_send_bss_transition_mgmt_resp(wpa_s,
+	/* Associate to the network */
+	/* Send the BSS Management Response - Accept */
+	if (wpa_s->wnm_reply) {
+		wpa_s->wnm_reply = 0;
+		wnm_send_bss_transition_mgmt_resp(wpa_s,
 						  wpa_s->wnm_dialog_token,
 						  WNM_BSS_TM_ACCEPT,
-						  0, bssid);
-		}
+						  0, bss->bssid);
+	}
 
-		wpa_s->reassociate = 1;
-		wpa_supplicant_connect(wpa_s, bss, ssid);
-		wnm_deallocate_memory(wpa_s);
+	if (bss == wpa_s->current_bss) {
+		wpa_printf(MSG_DEBUG,
+			   "WNM: Already associated with the preferred candidate");
+		return 1;
+	}
+
+	wpa_s->reassociate = 1;
+	wpa_supplicant_connect(wpa_s, bss, ssid);
+	wnm_deallocate_memory(wpa_s);
+	return 1;
+
+send_bss_resp_fail:
+	if (!reply_on_fail)
+		return 0;
+
+	/* Send reject response for all the failures */
+
+	if (wpa_s->wnm_reply) {
+		wpa_s->wnm_reply = 0;
+		wnm_send_bss_transition_mgmt_resp(wpa_s,
+						  wpa_s->wnm_dialog_token,
+						  status, 0, NULL);
+	}
+	wnm_deallocate_memory(wpa_s);
+
+	return 0;
+}
+
+
+static int cand_pref_compar(const void *a, const void *b)
+{
+	const struct neighbor_report *aa = a;
+	const struct neighbor_report *bb = b;
+
+	if (!aa->preference_present && !bb->preference_present)
+		return 0;
+	if (!aa->preference_present)
+		return 1;
+	if (!bb->preference_present)
+		return -1;
+	if (bb->preference > aa->preference)
+		return 1;
+	if (bb->preference < aa->preference)
+		return -1;
+	return 0;
+}
+
+
+static void wnm_sort_cand_list(struct wpa_supplicant *wpa_s)
+{
+	if (!wpa_s->wnm_neighbor_report_elements)
+		return;
+	qsort(wpa_s->wnm_neighbor_report_elements,
+	      wpa_s->wnm_num_neighbor_report, sizeof(struct neighbor_report),
+	      cand_pref_compar);
+}
+
+
+static void wnm_dump_cand_list(struct wpa_supplicant *wpa_s)
+{
+	unsigned int i;
+
+	wpa_printf(MSG_DEBUG, "WNM: BSS Transition Candidate List");
+	if (!wpa_s->wnm_neighbor_report_elements)
+		return;
+	for (i = 0; i < wpa_s->wnm_num_neighbor_report; i++) {
+		struct neighbor_report *nei;
+
+		nei = &wpa_s->wnm_neighbor_report_elements[i];
+		wpa_printf(MSG_DEBUG, "%u: " MACSTR
+			   " info=0x%x op_class=%u chan=%u phy=%u pref=%d freq=%d",
+			   i, MAC2STR(nei->bssid), nei->bssid_info,
+			   nei->regulatory_class,
+			   nei->channel_number, nei->phy_type,
+			   nei->preference_present ? nei->preference : -1,
+			   nei->freq);
+	}
+}
+
+
+static int chan_supported(struct wpa_supplicant *wpa_s, int freq)
+{
+	unsigned int i;
+
+	for (i = 0; i < wpa_s->hw.num_modes; i++) {
+		struct hostapd_hw_modes *mode = &wpa_s->hw.modes[i];
+		int j;
+
+		for (j = 0; j < mode->num_channels; j++) {
+			struct hostapd_channel_data *chan;
+
+			chan = &mode->channels[j];
+			if (chan->freq == freq &&
+			    !(chan->flag & HOSTAPD_CHAN_DISABLED))
+				return 1;
+		}
+	}
+
+	return 0;
+}
+
+
+static void wnm_set_scan_freqs(struct wpa_supplicant *wpa_s)
+{
+	int *freqs;
+	int num_freqs = 0;
+	unsigned int i;
+
+	if (!wpa_s->wnm_neighbor_report_elements)
+		return;
+
+	if (wpa_s->hw.modes == NULL)
+		return;
+
+	os_free(wpa_s->next_scan_freqs);
+	wpa_s->next_scan_freqs = NULL;
+
+	freqs = os_calloc(wpa_s->wnm_num_neighbor_report + 1, sizeof(int));
+	if (freqs == NULL)
+		return;
+
+	for (i = 0; i < wpa_s->wnm_num_neighbor_report; i++) {
+		struct neighbor_report *nei;
+
+		nei = &wpa_s->wnm_neighbor_report_elements[i];
+		if (nei->freq <= 0) {
+			wpa_printf(MSG_DEBUG,
+				   "WNM: Unknown neighbor operating frequency for "
+				   MACSTR " - scan all channels",
+				   MAC2STR(nei->bssid));
+			os_free(freqs);
+			return;
+		}
+		if (chan_supported(wpa_s, nei->freq))
+			add_freq(freqs, &num_freqs, nei->freq);
+	}
+
+	if (num_freqs == 0) {
+		os_free(freqs);
 		return;
 	}
 
-	/* Send reject response for all the failures */
-send_bss_resp_fail:
-	wnm_deallocate_memory(wpa_s);
-	if (wpa_s->wnm_reply) {
-		wnm_send_bss_transition_mgmt_resp(wpa_s,
-						  wpa_s->wnm_dialog_token,
-						  WNM_BSS_TM_REJECT_UNSPECIFIED,
-						  0, NULL);
-	}
-	return;
+	wpa_printf(MSG_DEBUG,
+		   "WNM: Scan %d frequencies based on transition candidate list",
+		   num_freqs);
+	wpa_s->next_scan_freqs = freqs;
 }
 
 
@@ -626,20 +793,28 @@
 					     const u8 *pos, const u8 *end,
 					     int reply)
 {
+	unsigned int beacon_int;
+	u8 valid_int;
+
 	if (pos + 5 > end)
 		return;
 
+	if (wpa_s->current_bss)
+		beacon_int = wpa_s->current_bss->beacon_int;
+	else
+		beacon_int = 100; /* best guess */
+
 	wpa_s->wnm_dialog_token = pos[0];
 	wpa_s->wnm_mode = pos[1];
 	wpa_s->wnm_dissoc_timer = WPA_GET_LE16(pos + 2);
-	wpa_s->wnm_validity_interval = pos[4];
+	valid_int = pos[4];
 	wpa_s->wnm_reply = reply;
 
 	wpa_printf(MSG_DEBUG, "WNM: BSS Transition Management Request: "
 		   "dialog_token=%u request_mode=0x%x "
 		   "disassoc_timer=%u validity_interval=%u",
 		   wpa_s->wnm_dialog_token, wpa_s->wnm_mode,
-		   wpa_s->wnm_dissoc_timer, wpa_s->wnm_validity_interval);
+		   wpa_s->wnm_dissoc_timer, valid_int);
 
 	pos += 5;
 
@@ -654,7 +829,6 @@
 
 	if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_ESS_DISASSOC_IMMINENT) {
 		char url[256];
-		unsigned int beacon_int;
 
 		if (pos + 1 > end || pos + 1 + pos[0] > end) {
 			wpa_printf(MSG_DEBUG, "WNM: Invalid BSS Transition "
@@ -665,11 +839,6 @@
 		url[pos[0]] = '\0';
 		pos += 1 + pos[0];
 
-		if (wpa_s->current_bss)
-			beacon_int = wpa_s->current_bss->beacon_int;
-		else
-			beacon_int = 100; /* best guess */
-
 		wpa_msg(wpa_s, MSG_INFO, ESS_DISASSOC_IMMINENT "%d %u %s",
 			wpa_sm_pmf_enabled(wpa_s->wpa),
 			wpa_s->wnm_dissoc_timer * beacon_int * 128 / 125, url);
@@ -687,11 +856,12 @@
 	}
 
 	if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_PREF_CAND_LIST_INCLUDED) {
+		unsigned int valid_ms;
+
 		wpa_msg(wpa_s, MSG_INFO, "WNM: Preferred List Available");
-		wpa_s->wnm_num_neighbor_report = 0;
-		os_free(wpa_s->wnm_neighbor_report_elements);
-		wpa_s->wnm_neighbor_report_elements = os_zalloc(
-			WNM_MAX_NEIGHBOR_REPORT *
+		wnm_deallocate_memory(wpa_s);
+		wpa_s->wnm_neighbor_report_elements = os_calloc(
+			WNM_MAX_NEIGHBOR_REPORT,
 			sizeof(struct neighbor_report));
 		if (wpa_s->wnm_neighbor_report_elements == NULL)
 			return;
@@ -718,8 +888,34 @@
 			pos += len;
 			wpa_s->wnm_num_neighbor_report++;
 		}
+		wnm_sort_cand_list(wpa_s);
+		wnm_dump_cand_list(wpa_s);
+		valid_ms = valid_int * beacon_int * 128 / 125;
+		wpa_printf(MSG_DEBUG, "WNM: Candidate list valid for %u ms",
+			   valid_ms);
+		os_get_reltime(&wpa_s->wnm_cand_valid_until);
+		wpa_s->wnm_cand_valid_until.sec += valid_ms / 1000;
+		wpa_s->wnm_cand_valid_until.usec += (valid_ms % 1000) * 1000;
+		wpa_s->wnm_cand_valid_until.sec +=
+			wpa_s->wnm_cand_valid_until.usec / 1000000;
+		wpa_s->wnm_cand_valid_until.usec %= 1000000;
+		os_memcpy(wpa_s->wnm_cand_from_bss, wpa_s->bssid, ETH_ALEN);
 
-		wpa_s->scan_res_handler = wnm_scan_response;
+		if (wpa_s->last_scan_res_used > 0) {
+			struct os_reltime now;
+
+			os_get_reltime(&now);
+			if (!os_reltime_expired(&now, &wpa_s->last_scan, 10)) {
+				wpa_printf(MSG_DEBUG,
+					   "WNM: Try to use recent scan results");
+				if (wnm_scan_process(wpa_s, 0) > 0)
+					return;
+				wpa_printf(MSG_DEBUG,
+					   "WNM: No match in previous scan results - try a new scan");
+			}
+		}
+
+		wnm_set_scan_freqs(wpa_s);
 		wpa_supplicant_req_scan(wpa_s, 0, 0);
 	} else if (reply) {
 		enum bss_trans_mgmt_status_code status;
diff --git a/wpa_supplicant/wnm_sta.h b/wpa_supplicant/wnm_sta.h
index d2eb96d..8de4348 100644
--- a/wpa_supplicant/wnm_sta.h
+++ b/wpa_supplicant/wnm_sta.h
@@ -9,37 +9,12 @@
 #ifndef WNM_STA_H
 #define WNM_STA_H
 
-struct tsf_info {
-	u8 tsf_offset[2];
-	u8 beacon_interval[2];
-};
-
-struct condensed_country_string {
-	u8 country_string[2];
-};
-
-struct bss_transition_candidate {
-	u8 preference;
-};
-
-struct bss_termination_duration {
-	u8 duration[10];
-};
-
-struct bearing {
-	u8 bearing[8];
-};
-
 struct measurement_pilot {
 	u8 measurement_pilot;
 	u8 subelem_len;
 	u8 subelems[255];
 };
 
-struct rrm_enabled_capabilities {
-	u8 capabilities[5];
-};
-
 struct multiple_bssid {
 	u8 max_bssid_indicator;
 	u8 subelem_len;
@@ -48,18 +23,29 @@
 
 struct neighbor_report {
 	u8 bssid[ETH_ALEN];
-	u8 bssid_information[4];
+	u32 bssid_info;
 	u8 regulatory_class;
 	u8 channel_number;
 	u8 phy_type;
-	struct tsf_info *tsf_info;
-	struct condensed_country_string *con_coun_str;
-	struct bss_transition_candidate *bss_tran_can;
-	struct bss_termination_duration *bss_term_dur;
-	struct bearing *bearing;
+	u8 preference; /* valid if preference_present=1 */
+	u16 tsf_offset; /* valid if tsf_present=1 */
+	u16 beacon_int; /* valid if tsf_present=1 */
+	char country[2]; /* valid if country_present=1 */
+	u8 rm_capab[5]; /* valid if rm_capab_present=1 */
+	u16 bearing; /* valid if bearing_present=1 */
+	u16 rel_height; /* valid if bearing_present=1 */
+	u32 distance; /* valid if bearing_present=1 */
+	u64 bss_term_tsf; /* valid if bss_term_present=1 */
+	u16 bss_term_dur; /* valid if bss_term_present=1 */
+	unsigned int preference_present:1;
+	unsigned int tsf_present:1;
+	unsigned int country_present:1;
+	unsigned int rm_capab_present:1;
+	unsigned int bearing_present:1;
+	unsigned int bss_term_present:1;
 	struct measurement_pilot *meas_pilot;
-	struct rrm_enabled_capabilities *rrm_cap;
 	struct multiple_bssid *mul_bssid;
+	int freq;
 };
 
 
@@ -69,11 +55,23 @@
 void ieee802_11_rx_wnm_action(struct wpa_supplicant *wpa_s,
 			      const struct ieee80211_mgmt *mgmt, size_t len);
 
-void wnm_scan_response(struct wpa_supplicant *wpa_s,
-		       struct wpa_scan_results *scan_res);
-
 int wnm_send_bss_transition_mgmt_query(struct wpa_supplicant *wpa_s,
 				       u8 query_reason);
 void wnm_deallocate_memory(struct wpa_supplicant *wpa_s);
 
+
+#ifdef CONFIG_WNM
+
+int wnm_scan_process(struct wpa_supplicant *wpa_s, int reply_on_fail);
+
+#else /* CONFIG_WNM */
+
+static inline int wnm_scan_process(struct wpa_supplicant *wpa_s,
+				   int reply_on_fail)
+{
+	return 0;
+}
+
+#endif /* CONFIG_WNM */
+
 #endif /* WNM_STA_H */
diff --git a/wpa_supplicant/wpa_cli.c b/wpa_supplicant/wpa_cli.c
index fe30b41..c5d8333 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-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.
@@ -26,16 +26,16 @@
 #endif /* ANDROID */
 
 
-static const char *wpa_cli_version =
+static const char *const wpa_cli_version =
 "wpa_cli v" VERSION_STR "\n"
-"Copyright (c) 2004-2014, Jouni Malinen <j@w1.fi> and contributors";
+"Copyright (c) 2004-2015, Jouni Malinen <j@w1.fi> and contributors";
 
 
-static const char *wpa_cli_license =
+static const char *const wpa_cli_license =
 "This software may be distributed under the terms of the BSD license.\n"
 "See README for more details.\n";
 
-static const char *wpa_cli_full_license =
+static const char *const wpa_cli_full_license =
 "This software may be distributed under the terms of the BSD license.\n"
 "\n"
 "Redistribution and use in source and binary forms, with or without\n"
@@ -92,6 +92,7 @@
 static DEFINE_DL_LIST(p2p_peers); /* struct cli_txt_entry */
 static DEFINE_DL_LIST(p2p_groups); /* struct cli_txt_entry */
 static DEFINE_DL_LIST(ifnames); /* struct cli_txt_entry */
+static DEFINE_DL_LIST(networks); /* struct cli_txt_entry */
 
 
 static void print_help(const char *cmd);
@@ -99,6 +100,7 @@
 static void wpa_cli_close_connection(void);
 static char * wpa_cli_get_default_ifname(void);
 static char ** wpa_list_cmd_list(void);
+static void update_networks(struct wpa_ctrl *ctrl);
 
 
 static void usage(void)
@@ -168,11 +170,12 @@
 
 
 #ifdef CONFIG_P2P
-static void cli_txt_list_del_word(struct dl_list *txt_list, const char *txt)
+static void cli_txt_list_del_word(struct dl_list *txt_list, const char *txt,
+				  int separator)
 {
 	const char *end;
 	char *buf;
-	end = os_strchr(txt, ' ');
+	end = os_strchr(txt, separator);
 	if (end == NULL)
 		end = txt + os_strlen(txt);
 	buf = dup_binstr(txt, end - txt);
@@ -213,14 +216,16 @@
 	os_snprintf(buf, sizeof(buf), MACSTR, MAC2STR(addr));
 	return cli_txt_list_add(txt_list, buf);
 }
+#endif /* CONFIG_P2P */
 
 
-static int cli_txt_list_add_word(struct dl_list *txt_list, const char *txt)
+static int cli_txt_list_add_word(struct dl_list *txt_list, const char *txt,
+				 int separator)
 {
 	const char *end;
 	char *buf;
 	int ret;
-	end = os_strchr(txt, ' ');
+	end = os_strchr(txt, separator);
 	if (end == NULL)
 		end = txt + os_strlen(txt);
 	buf = dup_binstr(txt, end - txt);
@@ -230,7 +235,6 @@
 	os_free(buf);
 	return ret;
 }
-#endif /* CONFIG_P2P */
 
 
 static char ** cli_txt_list_array(struct dl_list *txt_list)
@@ -333,7 +337,7 @@
 			return -1;
 		res = os_snprintf(cfile, flen, "%s/%s", ctrl_iface_dir,
 				  ifname);
-		if (res < 0 || res >= flen) {
+		if (os_snprintf_error(flen, res)) {
 			os_free(cfile);
 			return -1;
 		}
@@ -448,13 +452,13 @@
 	end = buf + buflen;
 
 	res = os_snprintf(pos, end - pos, "%s", cmd);
-	if (res < 0 || res >= end - pos)
+	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 (res < 0 || res >= end - pos)
+		if (os_snprintf_error(end - pos, res))
 			goto fail;
 		pos += res;
 	}
@@ -584,7 +588,7 @@
 
 	if (argc == 1) {
 		res = os_snprintf(cmd, sizeof(cmd), "SET %s ", argv[0]);
-		if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
+		if (os_snprintf_error(sizeof(cmd), res)) {
 			printf("Too long SET command.\n");
 			return -1;
 		}
@@ -608,34 +612,57 @@
 		"uapsd", "ps", "wifi_display", "bssid_filter", "disallow_aps",
 		"no_keep_alive",
 		/* global configuration parameters */
-		"eapol_version", "ap_scan", "disable_scan_offload",
-		"fast_reauth", "opensc_engine_path", "pkcs11_engine_path",
-		"pkcs11_module_path", "pcsc_reader", "pcsc_pin",
-		"driver_param", "dot11RSNAConfigPMKLifetime",
+#ifdef CONFIG_CTRL_IFACE
+		"ctrl_interface", "no_ctrl_interface", "ctrl_interface_group",
+#endif /* CONFIG_CTRL_IFACE */
+		"eapol_version", "ap_scan", "bgscan",
+#ifdef CONFIG_MESH
+		"user_mpm", "max_peer_links", "mesh_max_inactivity",
+#endif /* CONFIG_MESH */
+		"disable_scan_offload", "fast_reauth", "opensc_engine_path",
+		"pkcs11_engine_path", "pkcs11_module_path", "openssl_ciphers",
+		"pcsc_reader", "pcsc_pin", "external_sim", "driver_param",
+		"dot11RSNAConfigPMKLifetime",
 		"dot11RSNAConfigPMKReauthThreshold",
 		"dot11RSNAConfigSATimeout",
-		"update_config", "load_dynamic_eap", "uuid", "device_name",
-		"manufacturer", "model_name", "model_number", "serial_number",
-		"device_type", "os_version", "config_methods",
-		"wps_cred_processing", "wps_vendor_ext_m1", "sec_device_type",
+#ifndef CONFIG_NO_CONFIG_WRITE
+		"update_config",
+#endif /* CONFIG_NO_CONFIG_WRITE */
+		"load_dynamic_eap",
+#ifdef CONFIG_WPS
+		"uuid", "device_name", "manufacturer", "model_name",
+		"model_number", "serial_number", "device_type", "os_version",
+		"config_methods", "wps_cred_processing", "wps_vendor_ext_m1",
+#endif /* CONFIG_WPS */
+#ifdef CONFIG_P2P
+		"sec_device_type",
 		"p2p_listen_reg_class", "p2p_listen_channel",
-		"p2p_oper_reg_class", "p2p_oper_channel",
-		"p2p_go_intent", "p2p_ssid_postfix", "persistent_reconnect",
-		"p2p_intra_bss", "p2p_group_idle", "p2p_pref_chan",
-		"p2p_no_go_freq",
-		"p2p_go_ht40", "p2p_disabled", "p2p_no_group_iface",
-		"p2p_go_vht",
-		"p2p_ignore_shared_freq", "country", "bss_max_count",
-		"bss_expiration_age", "bss_expiration_scan_count",
-		"filter_ssids", "filter_rssi", "max_num_sta",
-		"disassoc_low_ack", "hs20", "interworking", "hessid",
-		"access_network_type", "pbc_in_m1", "autoscan",
-		"wps_nfc_dev_pw_id", "wps_nfc_dh_pubkey", "wps_nfc_dh_privkey",
-		"wps_nfc_dev_pw", "ext_password_backend",
+		"p2p_oper_reg_class", "p2p_oper_channel", "p2p_go_intent",
+		"p2p_ssid_postfix", "persistent_reconnect", "p2p_intra_bss",
+		"p2p_group_idle", "p2p_passphrase_len", "p2p_pref_chan",
+		"p2p_no_go_freq", "p2p_add_cli_chan",
+		"p2p_optimize_listen_chan", "p2p_go_ht40", "p2p_go_vht",
+		"p2p_disabled", "p2p_go_ctwindow", "p2p_no_group_iface",
+		"p2p_ignore_shared_freq", "ip_addr_go", "ip_addr_mask",
+		"ip_addr_start", "ip_addr_end",
+#endif /* CONFIG_P2P */
+		"country", "bss_max_count", "bss_expiration_age",
+		"bss_expiration_scan_count", "filter_ssids", "filter_rssi",
+		"max_num_sta", "disassoc_low_ack",
+#ifdef CONFIG_HS20
+		"hs20",
+#endif /* CONFIG_HS20 */
+		"interworking", "hessid", "access_network_type", "pbc_in_m1",
+		"autoscan", "wps_nfc_dev_pw_id", "wps_nfc_dh_pubkey",
+		"wps_nfc_dh_privkey", "wps_nfc_dev_pw", "ext_password_backend",
 		"p2p_go_max_inactivity", "auto_interworking", "okc", "pmf",
-		"sae_groups", "dtim_period", "beacon_int", "ap_vendor_elements",
-		"ignore_old_scan_res", "freq_list", "external_sim",
-		"tdls_external_control", "p2p_search_delay"
+		"sae_groups", "dtim_period", "beacon_int",
+		"ap_vendor_elements", "ignore_old_scan_res", "freq_list",
+		"scan_cur_freq", "sched_scan_interval",
+		"tdls_external_control", "osu_dir", "wowlan_triggers",
+		"p2p_search_delay", "mac_addr", "rand_addr_lifetime",
+		"preassoc_mac_addr", "key_mgmt_offload", "passive_scan",
+		"reassoc_same_bss_optim", "wps_priority"
 	};
 	int i, num_fields = ARRAY_SIZE(fields);
 
@@ -657,6 +684,11 @@
 	return NULL;
 }
 
+static int wpa_cli_cmd_dump(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+	return wpa_ctrl_command(ctrl, "DUMP");
+}
+
 
 static int wpa_cli_cmd_get(struct wpa_ctrl *ctrl, int argc, char *argv[])
 {
@@ -664,6 +696,74 @@
 }
 
 
+static char ** wpa_cli_complete_get(const char *str, int pos)
+{
+	int arg = get_cmd_arg_num(str, pos);
+	const char *fields[] = {
+#ifdef CONFIG_CTRL_IFACE
+		"ctrl_interface", "ctrl_interface_group",
+#endif /* CONFIG_CTRL_IFACE */
+		"eapol_version", "ap_scan",
+#ifdef CONFIG_MESH
+		"user_mpm", "max_peer_links", "mesh_max_inactivity",
+#endif /* CONFIG_MESH */
+		"disable_scan_offload", "fast_reauth", "opensc_engine_path",
+		"pkcs11_engine_path", "pkcs11_module_path", "openssl_ciphers",
+		"pcsc_reader", "pcsc_pin", "external_sim", "driver_param",
+		"dot11RSNAConfigPMKLifetime",
+		"dot11RSNAConfigPMKReauthThreshold",
+		"dot11RSNAConfigSATimeout",
+#ifndef CONFIG_NO_CONFIG_WRITE
+		"update_config",
+#endif /* CONFIG_NO_CONFIG_WRITE */
+#ifdef CONFIG_WPS
+		"device_name", "manufacturer", "model_name", "model_number",
+		"serial_number", "config_methods", "wps_cred_processing",
+#endif /* CONFIG_WPS */
+#ifdef CONFIG_P2P
+		"p2p_listen_reg_class", "p2p_listen_channel",
+		"p2p_oper_reg_class", "p2p_oper_channel", "p2p_go_intent",
+		"p2p_ssid_postfix", "persistent_reconnect", "p2p_intra_bss",
+		"p2p_group_idle", "p2p_passphrase_len", "p2p_add_cli_chan",
+		"p2p_optimize_listen_chan", "p2p_go_ht40", "p2p_go_vht",
+		"p2p_disabled", "p2p_go_ctwindow", "p2p_no_group_iface",
+		"p2p_ignore_shared_freq", "ip_addr_go", "ip_addr_mask",
+		"ip_addr_start", "ip_addr_end",
+#endif /* CONFIG_P2P */
+		"bss_max_count", "bss_expiration_age",
+		"bss_expiration_scan_count", "filter_ssids", "filter_rssi",
+		"max_num_sta", "disassoc_low_ack",
+#ifdef CONFIG_HS20
+		"hs20",
+#endif /* CONFIG_HS20 */
+		"interworking", "access_network_type", "pbc_in_m1", "autoscan",
+		"wps_nfc_dev_pw_id", "ext_password_backend",
+		"p2p_go_max_inactivity", "auto_interworking", "okc", "pmf",
+		"dtim_period", "beacon_int", "ignore_old_scan_res",
+		"scan_cur_freq", "sched_scan_interval",
+		"tdls_external_control", "osu_dir", "wowlan_triggers",
+		"p2p_search_delay", "mac_addr", "rand_addr_lifetime",
+		"preassoc_mac_addr", "key_mgmt_offload", "passive_scan",
+		"reassoc_same_bss_optim"
+	};
+	int i, num_fields = ARRAY_SIZE(fields);
+
+	if (arg == 1) {
+		char **res = os_calloc(num_fields + 1, sizeof(char *));
+		if (res == NULL)
+			return NULL;
+		for (i = 0; i < num_fields; i++) {
+			res[i] = os_strdup(fields[i]);
+			if (res[i] == NULL)
+				return res;
+		}
+		return res;
+	}
+
+	return NULL;
+}
+
+
 static int wpa_cli_cmd_logoff(struct wpa_ctrl *ctrl, int argc, char *argv[])
 {
 	return wpa_ctrl_command(ctrl, "LOGOFF");
@@ -732,7 +832,7 @@
 		res = os_snprintf(cmd, sizeof(cmd), "BSS_FLUSH 0");
 	else
 		res = os_snprintf(cmd, sizeof(cmd), "BSS_FLUSH %s", argv[0]);
-	if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
+	if (os_snprintf_error(sizeof(cmd), res)) {
 		printf("Too long BSS_FLUSH command.\n");
 		return -1;
 	}
@@ -867,12 +967,12 @@
 		res = os_snprintf(cmd, sizeof(cmd), "WPS_REG %s %s",
 				  argv[0], argv[1]);
 	else if (argc == 5 || argc == 6) {
-		char ssid_hex[2 * 32 + 1];
+		char ssid_hex[2 * SSID_MAX_LEN + 1];
 		char key_hex[2 * 64 + 1];
 		int i;
 
 		ssid_hex[0] = '\0';
-		for (i = 0; i < 32; i++) {
+		for (i = 0; i < SSID_MAX_LEN; i++) {
 			if (argv[2][i] == '\0')
 				break;
 			os_snprintf(&ssid_hex[i * 2], 3, "%02x", argv[2][i]);
@@ -907,7 +1007,7 @@
 		return -1;
 	}
 
-	if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
+	if (os_snprintf_error(sizeof(cmd), res)) {
 		printf("Too long WPS_REG command.\n");
 		return -1;
 	}
@@ -996,12 +1096,12 @@
 	int res;
 
 	if (argc == 5 || argc == 6) {
-		char ssid_hex[2 * 32 + 1];
+		char ssid_hex[2 * SSID_MAX_LEN + 1];
 		char key_hex[2 * 64 + 1];
 		int i;
 
 		ssid_hex[0] = '\0';
-		for (i = 0; i < 32; i++) {
+		for (i = 0; i < SSID_MAX_LEN; i++) {
 			if (argv[2][i] == '\0')
 				break;
 			os_snprintf(&ssid_hex[i * 2], 3, "%02x", argv[2][i]);
@@ -1032,7 +1132,7 @@
 		return -1;
 	}
 
-	if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
+	if (os_snprintf_error(sizeof(cmd), res)) {
 		printf("Too long WPS_ER_CONFIG command.\n");
 		return -1;
 	}
@@ -1084,14 +1184,14 @@
 	pos = cmd;
 	ret = os_snprintf(pos, end - pos, WPA_CTRL_RSP "IDENTITY-%s:%s",
 			  argv[0], argv[1]);
-	if (ret < 0 || ret >= end - pos) {
+	if (os_snprintf_error(end - pos, ret)) {
 		printf("Too long IDENTITY command.\n");
 		return -1;
 	}
 	pos += ret;
 	for (i = 2; i < argc; i++) {
 		ret = os_snprintf(pos, end - pos, " %s", argv[i]);
-		if (ret < 0 || ret >= end - pos) {
+		if (os_snprintf_error(end - pos, ret)) {
 			printf("Too long IDENTITY command.\n");
 			return -1;
 		}
@@ -1117,14 +1217,14 @@
 	pos = cmd;
 	ret = os_snprintf(pos, end - pos, WPA_CTRL_RSP "PASSWORD-%s:%s",
 			  argv[0], argv[1]);
-	if (ret < 0 || ret >= end - pos) {
+	if (os_snprintf_error(end - pos, ret)) {
 		printf("Too long PASSWORD command.\n");
 		return -1;
 	}
 	pos += ret;
 	for (i = 2; i < argc; i++) {
 		ret = os_snprintf(pos, end - pos, " %s", argv[i]);
-		if (ret < 0 || ret >= end - pos) {
+		if (os_snprintf_error(end - pos, ret)) {
 			printf("Too long PASSWORD command.\n");
 			return -1;
 		}
@@ -1151,14 +1251,14 @@
 	pos = cmd;
 	ret = os_snprintf(pos, end - pos, WPA_CTRL_RSP "NEW_PASSWORD-%s:%s",
 			  argv[0], argv[1]);
-	if (ret < 0 || ret >= end - pos) {
+	if (os_snprintf_error(end - pos, ret)) {
 		printf("Too long NEW_PASSWORD command.\n");
 		return -1;
 	}
 	pos += ret;
 	for (i = 2; i < argc; i++) {
 		ret = os_snprintf(pos, end - pos, " %s", argv[i]);
-		if (ret < 0 || ret >= end - pos) {
+		if (os_snprintf_error(end - pos, ret)) {
 			printf("Too long NEW_PASSWORD command.\n");
 			return -1;
 		}
@@ -1184,14 +1284,14 @@
 	pos = cmd;
 	ret = os_snprintf(pos, end - pos, WPA_CTRL_RSP "PIN-%s:%s",
 			  argv[0], argv[1]);
-	if (ret < 0 || ret >= end - pos) {
+	if (os_snprintf_error(end - pos, ret)) {
 		printf("Too long PIN command.\n");
 		return -1;
 	}
 	pos += ret;
 	for (i = 2; i < argc; i++) {
 		ret = os_snprintf(pos, end - pos, " %s", argv[i]);
-		if (ret < 0 || ret >= end - pos) {
+		if (os_snprintf_error(end - pos, ret)) {
 			printf("Too long PIN command.\n");
 			return -1;
 		}
@@ -1216,14 +1316,14 @@
 	pos = cmd;
 	ret = os_snprintf(pos, end - pos, WPA_CTRL_RSP "OTP-%s:%s",
 			  argv[0], argv[1]);
-	if (ret < 0 || ret >= end - pos) {
+	if (os_snprintf_error(end - pos, ret)) {
 		printf("Too long OTP command.\n");
 		return -1;
 	}
 	pos += ret;
 	for (i = 2; i < argc; i++) {
 		ret = os_snprintf(pos, end - pos, " %s", argv[i]);
-		if (ret < 0 || ret >= end - pos) {
+		if (os_snprintf_error(end - pos, ret)) {
 			printf("Too long OTP command.\n");
 			return -1;
 		}
@@ -1249,14 +1349,14 @@
 	pos = cmd;
 	ret = os_snprintf(pos, end - pos, WPA_CTRL_RSP "SIM-%s:%s",
 			  argv[0], argv[1]);
-	if (ret < 0 || ret >= end - pos) {
+	if (os_snprintf_error(end - pos, ret)) {
 		printf("Too long SIM command.\n");
 		return -1;
 	}
 	pos += ret;
 	for (i = 2; i < argc; i++) {
 		ret = os_snprintf(pos, end - pos, " %s", argv[i]);
-		if (ret < 0 || ret >= end - pos) {
+		if (os_snprintf_error(end - pos, ret)) {
 			printf("Too long SIM command.\n");
 			return -1;
 		}
@@ -1282,14 +1382,14 @@
 	pos = cmd;
 	ret = os_snprintf(pos, end - pos, WPA_CTRL_RSP "PASSPHRASE-%s:%s",
 			  argv[0], argv[1]);
-	if (ret < 0 || ret >= end - pos) {
+	if (os_snprintf_error(end - pos, ret)) {
 		printf("Too long PASSPHRASE command.\n");
 		return -1;
 	}
 	pos += ret;
 	for (i = 2; i < argc; i++) {
 		ret = os_snprintf(pos, end - pos, " %s", argv[i]);
-		if (ret < 0 || ret >= end - pos) {
+		if (os_snprintf_error(end - pos, ret)) {
 			printf("Too long PASSPHRASE command.\n");
 			return -1;
 		}
@@ -1355,14 +1455,20 @@
 static int wpa_cli_cmd_add_network(struct wpa_ctrl *ctrl, int argc,
 				   char *argv[])
 {
-	return wpa_ctrl_command(ctrl, "ADD_NETWORK");
+	int res = wpa_ctrl_command(ctrl, "ADD_NETWORK");
+	if (interactive)
+		update_networks(ctrl);
+	return res;
 }
 
 
 static int wpa_cli_cmd_remove_network(struct wpa_ctrl *ctrl, int argc,
 				      char *argv[])
 {
-	return wpa_cli_cmd(ctrl, "REMOVE_NETWORK", 1, argc, argv);
+	int res = wpa_cli_cmd(ctrl, "REMOVE_NETWORK", 1, argc, argv);
+	if (interactive)
+		update_networks(ctrl);
+	return res;
 }
 
 
@@ -1423,6 +1529,105 @@
 }
 
 
+static const char *network_fields[] = {
+	"ssid", "scan_ssid", "bssid", "bssid_blacklist",
+	"bssid_whitelist", "psk", "proto", "key_mgmt",
+	"bg_scan_period", "pairwise", "group", "auth_alg", "scan_freq",
+	"freq_list",
+#ifdef IEEE8021X_EAPOL
+	"eap", "identity", "anonymous_identity", "password", "ca_cert",
+	"ca_path", "client_cert", "private_key", "private_key_passwd",
+	"dh_file", "subject_match", "altsubject_match",
+	"domain_suffix_match", "domain_match", "ca_cert2", "ca_path2",
+	"client_cert2", "private_key2", "private_key2_passwd",
+	"dh_file2", "subject_match2", "altsubject_match2",
+	"domain_suffix_match2", "domain_match2", "phase1", "phase2",
+	"pcsc", "pin", "engine_id", "key_id", "cert_id", "ca_cert_id",
+	"pin2", "engine2_id", "key2_id", "cert2_id", "ca_cert2_id",
+	"engine", "engine2", "eapol_flags", "sim_num",
+	"openssl_ciphers", "erp",
+#endif /* IEEE8021X_EAPOL */
+	"wep_key0", "wep_key1", "wep_key2", "wep_key3",
+	"wep_tx_keyidx", "priority",
+#ifdef IEEE8021X_EAPOL
+	"eap_workaround", "pac_file", "fragment_size", "ocsp",
+#endif /* IEEE8021X_EAPOL */
+#ifdef CONFIG_MESH
+	"mode", "no_auto_peer",
+#else /* CONFIG_MESH */
+	"mode",
+#endif /* CONFIG_MESH */
+	"proactive_key_caching", "disabled", "id_str",
+#ifdef CONFIG_IEEE80211W
+	"ieee80211w",
+#endif /* CONFIG_IEEE80211W */
+	"peerkey", "mixed_cell", "frequency", "fixed_freq",
+#ifdef CONFIG_MESH
+	"mesh_basic_rates", "dot11MeshMaxRetries",
+	"dot11MeshRetryTimeout", "dot11MeshConfirmTimeout",
+	"dot11MeshHoldingTimeout",
+#endif /* CONFIG_MESH */
+	"wpa_ptk_rekey", "bgscan", "ignore_broadcast_ssid",
+#ifdef CONFIG_P2P
+	"go_p2p_dev_addr", "p2p_client_list", "psk_list",
+#endif /* CONFIG_P2P */
+#ifdef CONFIG_HT_OVERRIDES
+	"disable_ht", "disable_ht40", "disable_sgi", "disable_ldpc",
+	"ht40_intolerant", "disable_max_amsdu", "ampdu_factor",
+	"ampdu_density", "ht_mcs",
+#endif /* CONFIG_HT_OVERRIDES */
+#ifdef CONFIG_VHT_OVERRIDES
+	"disable_vht", "vht_capa", "vht_capa_mask", "vht_rx_mcs_nss_1",
+	"vht_rx_mcs_nss_2", "vht_rx_mcs_nss_3", "vht_rx_mcs_nss_4",
+	"vht_rx_mcs_nss_5", "vht_rx_mcs_nss_6", "vht_rx_mcs_nss_7",
+	"vht_rx_mcs_nss_8", "vht_tx_mcs_nss_1", "vht_tx_mcs_nss_2",
+	"vht_tx_mcs_nss_3", "vht_tx_mcs_nss_4", "vht_tx_mcs_nss_5",
+	"vht_tx_mcs_nss_6", "vht_tx_mcs_nss_7", "vht_tx_mcs_nss_8",
+#endif /* CONFIG_VHT_OVERRIDES */
+	"ap_max_inactivity", "dtim_period", "beacon_int",
+#ifdef CONFIG_MACSEC
+	"macsec_policy",
+#endif /* CONFIG_MACSEC */
+#ifdef CONFIG_HS20
+	"update_identifier",
+#endif /* CONFIG_HS20 */
+	"mac_addr"
+};
+
+
+static char ** wpa_cli_complete_network(const char *str, int pos)
+{
+	int arg = get_cmd_arg_num(str, pos);
+	int i, num_fields = ARRAY_SIZE(network_fields);
+	char **res = NULL;
+
+	switch (arg) {
+	case 1:
+		res = cli_txt_list_array(&networks);
+		break;
+	case 2:
+		res = os_calloc(num_fields + 1, sizeof(char *));
+		if (res == NULL)
+			return NULL;
+		for (i = 0; i < num_fields; i++) {
+			res[i] = os_strdup(network_fields[i]);
+			if (res[i] == NULL)
+				break;
+		}
+	}
+	return res;
+}
+
+
+static char ** wpa_cli_complete_network_id(const char *str, int pos)
+{
+	int arg = get_cmd_arg_num(str, pos);
+	if (arg == 1)
+		return cli_txt_list_array(&networks);
+	return NULL;
+}
+
+
 static int wpa_cli_cmd_dup_network(struct wpa_ctrl *ctrl, int argc,
 				   char *argv[])
 {
@@ -1441,6 +1646,31 @@
 }
 
 
+static char ** wpa_cli_complete_dup_network(const char *str, int pos)
+{
+	int arg = get_cmd_arg_num(str, pos);
+	int i, num_fields = ARRAY_SIZE(network_fields);
+	char **res = NULL;
+
+	switch (arg) {
+	case 1:
+	case 2:
+		res = cli_txt_list_array(&networks);
+		break;
+	case 3:
+		res = os_calloc(num_fields + 1, sizeof(char *));
+		if (res == NULL)
+			return NULL;
+		for (i = 0; i < num_fields; i++) {
+			res[i] = os_strdup(network_fields[i]);
+			if (res[i] == NULL)
+				break;
+		}
+	}
+	return res;
+}
+
+
 static int wpa_cli_cmd_list_creds(struct wpa_ctrl *ctrl, int argc,
 				  char *argv[])
 {
@@ -1576,6 +1806,10 @@
 	wpa_cli_close_connection();
 	os_free(ctrl_ifname);
 	ctrl_ifname = os_strdup(argv[0]);
+	if (!ctrl_ifname) {
+		printf("Failed to allocate memory\n");
+		return 0;
+	}
 
 	if (wpa_cli_open_connection(ctrl_ifname, 1) == 0) {
 		printf("Connected to interface '%s.\n", ctrl_ifname);
@@ -1611,21 +1845,21 @@
 		printf("Invalid INTERFACE_ADD command: needs at least one "
 		       "argument (interface name)\n"
 		       "All arguments: ifname confname driver ctrl_interface "
-		       "driver_param bridge_name\n");
+		       "driver_param bridge_name [create]\n");
 		return -1;
 	}
 
 	/*
 	 * INTERFACE_ADD <ifname>TAB<confname>TAB<driver>TAB<ctrl_interface>TAB
-	 * <driver_param>TAB<bridge_name>
+	 * <driver_param>TAB<bridge_name>[TAB<create>]
 	 */
 	res = os_snprintf(cmd, sizeof(cmd),
-			  "INTERFACE_ADD %s\t%s\t%s\t%s\t%s\t%s",
+			  "INTERFACE_ADD %s\t%s\t%s\t%s\t%s\t%s\t%s",
 			  argv[0],
 			  argc > 1 ? argv[1] : "", argc > 2 ? argv[2] : "",
 			  argc > 3 ? argv[3] : "", argc > 4 ? argv[4] : "",
-			  argc > 5 ? argv[5] : "");
-	if (res < 0 || (size_t) res >= sizeof(cmd))
+			  argc > 5 ? argv[5] : "", argc > 6 ? argv[6] : "");
+	if (os_snprintf_error(sizeof(cmd), res))
 		return -1;
 	cmd[sizeof(cmd) - 1] = '\0';
 	return wpa_ctrl_command(ctrl, cmd);
@@ -1751,6 +1985,31 @@
 }
 
 
+#ifdef CONFIG_MESH
+
+static int wpa_cli_cmd_mesh_interface_add(struct wpa_ctrl *ctrl, int argc,
+					  char *argv[])
+{
+	return wpa_cli_cmd(ctrl, "MESH_INTERFACE_ADD", 0, argc, argv);
+}
+
+
+static int wpa_cli_cmd_mesh_group_add(struct wpa_ctrl *ctrl, int argc,
+				      char *argv[])
+{
+	return wpa_cli_cmd(ctrl, "MESH_GROUP_ADD", 1, argc, argv);
+}
+
+
+static int wpa_cli_cmd_mesh_group_remove(struct wpa_ctrl *ctrl, int argc,
+					 char *argv[])
+{
+	return wpa_cli_cmd(ctrl, "MESH_GROUP_REMOVE", 1, argc, argv);
+}
+
+#endif /* CONFIG_MESH */
+
+
 #ifdef CONFIG_P2P
 
 static int wpa_cli_cmd_p2p_find(struct wpa_ctrl *ctrl, int argc, char *argv[])
@@ -1795,6 +2054,20 @@
 }
 
 
+static int wpa_cli_cmd_p2p_asp_provision(struct wpa_ctrl *ctrl, int argc,
+					 char *argv[])
+{
+	return wpa_cli_cmd(ctrl, "P2P_ASP_PROVISION", 3, argc, argv);
+}
+
+
+static int wpa_cli_cmd_p2p_asp_provision_resp(struct wpa_ctrl *ctrl, int argc,
+					      char *argv[])
+{
+	return wpa_cli_cmd(ctrl, "P2P_ASP_PROVISION_RESP", 2, argc, argv);
+}
+
+
 static int wpa_cli_cmd_p2p_connect(struct wpa_ctrl *ctrl, int argc,
 				   char *argv[])
 {
@@ -1879,11 +2152,9 @@
 {
 	char cmd[4096];
 
-	if (argc != 2 && argc != 4) {
+	if (argc < 2) {
 		printf("Invalid P2P_SERV_DISC_REQ command: needs two "
-		       "arguments (address and TLVs) or four arguments "
-		       "(address, \"upnp\", version, search target "
-		       "(SSDP ST:)\n");
+		       "or more arguments (address and TLVs)\n");
 		return -1;
 	}
 
@@ -1914,7 +2185,7 @@
 
 	res = os_snprintf(cmd, sizeof(cmd), "P2P_SERV_DISC_RESP %s %s %s %s",
 			  argv[0], argv[1], argv[2], argv[3]);
-	if (res < 0 || (size_t) res >= sizeof(cmd))
+	if (os_snprintf_error(sizeof(cmd), res))
 		return -1;
 	cmd[sizeof(cmd) - 1] = '\0';
 	return wpa_ctrl_command(ctrl, cmd);
@@ -1945,27 +2216,25 @@
 static int wpa_cli_cmd_p2p_service_add(struct wpa_ctrl *ctrl, int argc,
 				       char *argv[])
 {
-	char cmd[4096];
-	int res;
+	if (argc < 3) {
+		printf("Invalid P2P_SERVICE_ADD command: needs 3-6 arguments\n");
+		return -1;
+	}
 
-	if (argc != 3 && argc != 4) {
-		printf("Invalid P2P_SERVICE_ADD command: needs three or four "
+	return wpa_cli_cmd(ctrl, "P2P_SERVICE_ADD", 3, argc, argv);
+}
+
+
+static int wpa_cli_cmd_p2p_service_rep(struct wpa_ctrl *ctrl, int argc,
+				       char *argv[])
+{
+	if (argc < 5 || argc > 6) {
+		printf("Invalid P2P_SERVICE_REP command: needs 5-6 "
 		       "arguments\n");
 		return -1;
 	}
 
-	if (argc == 4)
-		res = os_snprintf(cmd, sizeof(cmd),
-				  "P2P_SERVICE_ADD %s %s %s %s",
-				  argv[0], argv[1], argv[2], argv[3]);
-	else
-		res = os_snprintf(cmd, sizeof(cmd),
-				  "P2P_SERVICE_ADD %s %s %s",
-				  argv[0], argv[1], argv[2]);
-	if (res < 0 || (size_t) res >= sizeof(cmd))
-		return -1;
-	cmd[sizeof(cmd) - 1] = '\0';
-	return wpa_ctrl_command(ctrl, cmd);
+	return wpa_cli_cmd(ctrl, "P2P_SERVICE_REP", 5, argc, argv);
 }
 
 
@@ -1989,7 +2258,7 @@
 		res = os_snprintf(cmd, sizeof(cmd),
 				  "P2P_SERVICE_DEL %s %s",
 				  argv[0], argv[1]);
-	if (res < 0 || (size_t) res >= sizeof(cmd))
+	if (os_snprintf_error(sizeof(cmd), res))
 		return -1;
 	cmd[sizeof(cmd) - 1] = '\0';
 	return wpa_ctrl_command(ctrl, cmd);
@@ -2211,7 +2480,7 @@
 
 	res = os_snprintf(cmd, sizeof(cmd), "WFD_SUBELEM_SET %s %s",
 			  argv[0], argc > 1 ? argv[1] : "");
-	if (res < 0 || (size_t) res >= sizeof(cmd))
+	if (os_snprintf_error(sizeof(cmd), res))
 		return -1;
 	cmd[sizeof(cmd) - 1] = '\0';
 	return wpa_ctrl_command(ctrl, cmd);
@@ -2232,7 +2501,7 @@
 
 	res = os_snprintf(cmd, sizeof(cmd), "WFD_SUBELEM_GET %s",
 			  argv[0]);
-	if (res < 0 || (size_t) res >= sizeof(cmd))
+	if (os_snprintf_error(sizeof(cmd), res))
 		return -1;
 	cmd[sizeof(cmd) - 1] = '\0';
 	return wpa_ctrl_command(ctrl, cmd);
@@ -2269,6 +2538,13 @@
 }
 
 
+static int wpa_cli_cmd_interworking_add_network(struct wpa_ctrl *ctrl, int argc,
+						char *argv[])
+{
+	return wpa_cli_cmd(ctrl, "INTERWORKING_ADD_NETWORK", 1, argc, argv);
+}
+
+
 static int wpa_cli_cmd_anqp_get(struct wpa_ctrl *ctrl, int argc, char *argv[])
 {
 	return wpa_cli_cmd(ctrl, "ANQP_GET", 2, argc, argv);
@@ -2379,6 +2655,48 @@
 }
 
 
+static int wpa_cli_cmd_tdls_link_status(struct wpa_ctrl *ctrl, int argc,
+					char *argv[])
+{
+	return wpa_cli_cmd(ctrl, "TDLS_LINK_STATUS", 1, argc, argv);
+}
+
+
+static int wpa_cli_cmd_wmm_ac_addts(struct wpa_ctrl *ctrl, int argc,
+				    char *argv[])
+{
+	return wpa_cli_cmd(ctrl, "WMM_AC_ADDTS", 3, argc, argv);
+}
+
+
+static int wpa_cli_cmd_wmm_ac_delts(struct wpa_ctrl *ctrl, int argc,
+				    char *argv[])
+{
+	return wpa_cli_cmd(ctrl, "WMM_AC_DELTS", 1, argc, argv);
+}
+
+
+static int wpa_cli_cmd_wmm_ac_status(struct wpa_ctrl *ctrl, int argc,
+				    char *argv[])
+{
+	return wpa_ctrl_command(ctrl, "WMM_AC_STATUS");
+}
+
+
+static int wpa_cli_cmd_tdls_chan_switch(struct wpa_ctrl *ctrl, int argc,
+					char *argv[])
+{
+	return wpa_cli_cmd(ctrl, "TDLS_CHAN_SWITCH", 2, argc, argv);
+}
+
+
+static int wpa_cli_cmd_tdls_cancel_chan_switch(struct wpa_ctrl *ctrl, int argc,
+					       char *argv[])
+{
+	return wpa_cli_cmd(ctrl, "TDLS_CANCEL_CHAN_SWITCH", 1, argc, argv);
+}
+
+
 static int wpa_cli_cmd_signal_poll(struct wpa_ctrl *ctrl, int argc,
 				   char *argv[])
 {
@@ -2463,6 +2781,26 @@
 }
 
 
+static int wpa_cli_cmd_neighbor_rep_request(struct wpa_ctrl *ctrl, int argc,
+					    char *argv[])
+{
+	return wpa_cli_cmd(ctrl, "NEIGHBOR_REP_REQUEST", 0, argc, argv);
+}
+
+
+static int wpa_cli_cmd_erp_flush(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+	return wpa_ctrl_command(ctrl, "ERP_FLUSH");
+}
+
+
+static int wpa_cli_cmd_mac_rand_scan(struct wpa_ctrl *ctrl, int argc,
+				     char *argv[])
+{
+	return wpa_cli_cmd(ctrl, "MAC_RAND_SCAN", 1, argc, argv);
+}
+
+
 enum wpa_cli_cmd_flags {
 	cli_cmd_flag_none		= 0x00,
 	cli_cmd_flag_sensitive		= 0x01
@@ -2476,7 +2814,7 @@
 	const char *usage;
 };
 
-static struct wpa_cli_cmd wpa_cli_commands[] = {
+static const struct wpa_cli_cmd wpa_cli_commands[] = {
 	{ "status", wpa_cli_cmd_status, NULL,
 	  cli_cmd_flag_none,
 	  "[verbose] = get current WPA/EAPOL/EAP status" },
@@ -2514,7 +2852,10 @@
 	  cli_cmd_flag_none,
 	  "= set variables (shows list of variables when run without "
 	  "arguments)" },
-	{ "get", wpa_cli_cmd_get, NULL,
+	{ "dump", wpa_cli_cmd_dump, NULL,
+	  cli_cmd_flag_none,
+	  "= dump config variables" },
+	{ "get", wpa_cli_cmd_get, wpa_cli_complete_get,
 	  cli_cmd_flag_none,
 	  "<name> = get information" },
 	{ "logon", wpa_cli_cmd_logon, NULL,
@@ -2576,29 +2917,33 @@
 	{ "list_networks", wpa_cli_cmd_list_networks, NULL,
 	  cli_cmd_flag_none,
 	  "= list configured networks" },
-	{ "select_network", wpa_cli_cmd_select_network, NULL,
+	{ "select_network", wpa_cli_cmd_select_network,
+	  wpa_cli_complete_network_id,
 	  cli_cmd_flag_none,
 	  "<network id> = select a network (disable others)" },
-	{ "enable_network", wpa_cli_cmd_enable_network, NULL,
+	{ "enable_network", wpa_cli_cmd_enable_network,
+	  wpa_cli_complete_network_id,
 	  cli_cmd_flag_none,
 	  "<network id> = enable a network" },
-	{ "disable_network", wpa_cli_cmd_disable_network, NULL,
+	{ "disable_network", wpa_cli_cmd_disable_network,
+	  wpa_cli_complete_network_id,
 	  cli_cmd_flag_none,
 	  "<network id> = disable a network" },
 	{ "add_network", wpa_cli_cmd_add_network, NULL,
 	  cli_cmd_flag_none,
 	  "= add a network" },
-	{ "remove_network", wpa_cli_cmd_remove_network, NULL,
+	{ "remove_network", wpa_cli_cmd_remove_network,
+	  wpa_cli_complete_network_id,
 	  cli_cmd_flag_none,
 	  "<network id> = remove a network" },
-	{ "set_network", wpa_cli_cmd_set_network, NULL,
+	{ "set_network", wpa_cli_cmd_set_network, wpa_cli_complete_network,
 	  cli_cmd_flag_sensitive,
 	  "<network id> <variable> <value> = set network variables (shows\n"
 	  "  list of variables when run without arguments)" },
-	{ "get_network", wpa_cli_cmd_get_network, NULL,
+	{ "get_network", wpa_cli_cmd_get_network, wpa_cli_complete_network,
 	  cli_cmd_flag_none,
 	  "<network id> <variable> = get network variables" },
-	{ "dup_network", wpa_cli_cmd_dup_network, NULL,
+	{ "dup_network", wpa_cli_cmd_dup_network, wpa_cli_complete_dup_network,
 	  cli_cmd_flag_none,
 	  "<src network id> <dst network id> <variable> = duplicate network variables"
 	},
@@ -2640,7 +2985,7 @@
 	{ "get_capability", wpa_cli_cmd_get_capability, NULL,
 	  cli_cmd_flag_none,
 	  "<eap/pairwise/group/key_mgmt/proto/auth_alg/channels/freq/modes> "
-	  "= get capabilies" },
+	  "= get capabilities" },
 	{ "reconfigure", wpa_cli_cmd_reconfigure, NULL,
 	  cli_cmd_flag_none,
 	  "= force wpa_supplicant to re-read its configuration file" },
@@ -2780,12 +3125,29 @@
 	{ "roam", wpa_cli_cmd_roam, wpa_cli_complete_bss,
 	  cli_cmd_flag_none,
 	  "<addr> = roam to the specified BSS" },
+#ifdef CONFIG_MESH
+	{ "mesh_interface_add", wpa_cli_cmd_mesh_interface_add, NULL,
+	  cli_cmd_flag_none,
+	  "[ifname] = Create a new mesh interface" },
+	{ "mesh_group_add", wpa_cli_cmd_mesh_group_add, NULL,
+	  cli_cmd_flag_none,
+	  "<network id> = join a mesh network (disable others)" },
+	{ "mesh_group_remove", wpa_cli_cmd_mesh_group_remove, NULL,
+	  cli_cmd_flag_none,
+	  "<ifname> = Remove mesh group interface" },
+#endif /* CONFIG_MESH */
 #ifdef CONFIG_P2P
 	{ "p2p_find", wpa_cli_cmd_p2p_find, wpa_cli_complete_p2p_find,
 	  cli_cmd_flag_none,
 	  "[timeout] [type=*] = find P2P Devices for up-to timeout seconds" },
 	{ "p2p_stop_find", wpa_cli_cmd_p2p_stop_find, NULL, cli_cmd_flag_none,
 	  "= stop P2P Devices search" },
+	{ "p2p_asp_provision", wpa_cli_cmd_p2p_asp_provision, NULL,
+	  cli_cmd_flag_none,
+	  "<addr> adv_id=<adv_id> conncap=<conncap> [info=<infodata>] = provision with a P2P ASP Device" },
+	{ "p2p_asp_provision_resp", wpa_cli_cmd_p2p_asp_provision_resp, NULL,
+	  cli_cmd_flag_none,
+	  "<addr> adv_id=<adv_id> [role<conncap>] [info=<infodata>] = provision with a P2P ASP Device" },
 	{ "p2p_connect", wpa_cli_cmd_p2p_connect, wpa_cli_complete_p2p_connect,
 	  cli_cmd_flag_none,
 	  "<addr> <\"pbc\"|PIN> [ht40] = connect to a P2P Device" },
@@ -2822,8 +3184,12 @@
 	  "= remove all stored service entries" },
 	{ "p2p_service_add", wpa_cli_cmd_p2p_service_add, NULL,
 	  cli_cmd_flag_none,
-	  "<bonjour|upnp> <query|version> <response|service> = add a local "
+	  "<bonjour|upnp|asp> <query|version> <response|service> = add a local "
 	  "service" },
+	{ "p2p_service_rep", wpa_cli_cmd_p2p_service_rep, NULL,
+	  cli_cmd_flag_none,
+	  "asp <auto> <adv_id> <svc_state> <svc_string> [<svc_info>] = replace "
+	  "local ASP service" },
 	{ "p2p_service_del", wpa_cli_cmd_p2p_service_del, NULL,
 	  cli_cmd_flag_none,
 	  "<bonjour|upnp> <query|version> [|service] = remove a local "
@@ -2881,6 +3247,9 @@
 	{ "interworking_connect", wpa_cli_cmd_interworking_connect,
 	  wpa_cli_complete_bss, cli_cmd_flag_none,
 	  "<BSSID> = connect using Interworking credentials" },
+	{ "interworking_add_network", wpa_cli_cmd_interworking_add_network,
+	  wpa_cli_complete_bss, cli_cmd_flag_none,
+	  "<BSSID> = connect using Interworking credentials" },
 	{ "anqp_get", wpa_cli_cmd_anqp_get, wpa_cli_complete_bss,
 	  cli_cmd_flag_none,
 	  "<addr> <info id>[,<info id>]... = request ANQP information" },
@@ -2920,6 +3289,28 @@
 	{ "tdls_teardown", wpa_cli_cmd_tdls_teardown, NULL,
 	  cli_cmd_flag_none,
 	  "<addr> = tear down TDLS with <addr>" },
+	{ "tdls_link_status", wpa_cli_cmd_tdls_link_status, NULL,
+	  cli_cmd_flag_none,
+	  "<addr> = TDLS link status with <addr>" },
+	{ "wmm_ac_addts", wpa_cli_cmd_wmm_ac_addts, NULL,
+	  cli_cmd_flag_none,
+	  "<uplink/downlink/bidi> <tsid=0..7> <up=0..7> [nominal_msdu_size=#] "
+	  "[mean_data_rate=#] [min_phy_rate=#] [sba=#] [fixed_nominal_msdu] "
+	  "= add WMM-AC traffic stream" },
+	{ "wmm_ac_delts", wpa_cli_cmd_wmm_ac_delts, NULL,
+	  cli_cmd_flag_none,
+	  "<tsid> = delete WMM-AC traffic stream" },
+	{ "wmm_ac_status", wpa_cli_cmd_wmm_ac_status, NULL,
+	  cli_cmd_flag_none,
+	  "= show status for Wireless Multi-Media Admission-Control" },
+	{ "tdls_chan_switch", wpa_cli_cmd_tdls_chan_switch, NULL,
+	  cli_cmd_flag_none,
+	  "<addr> <oper class> <freq> [sec_channel_offset=] [center_freq1=] "
+	  "[center_freq2=] [bandwidth=] [ht|vht] = enable channel switching "
+	  "with TDLS peer" },
+	{ "tdls_cancel_chan_switch", wpa_cli_cmd_tdls_cancel_chan_switch, NULL,
+	  cli_cmd_flag_none,
+	  "<addr> = disable channel switching with TDLS peer <addr>" },
 	{ "signal_poll", wpa_cli_cmd_signal_poll, NULL,
 	  cli_cmd_flag_none,
 	  "= get signal parameters" },
@@ -2952,6 +3343,18 @@
 	{ "vendor", wpa_cli_cmd_vendor, NULL, cli_cmd_flag_none,
 	  "<vendor id> <command id> [<hex formatted command argument>] = Send vendor command"
 	},
+	{ "neighbor_rep_request",
+	  wpa_cli_cmd_neighbor_rep_request, NULL, cli_cmd_flag_none,
+	  "[ssid=<SSID>] = Trigger request to AP for neighboring AP report "
+	  "(with optional given SSID, default: current SSID)"
+	},
+	{ "erp_flush", wpa_cli_cmd_erp_flush, NULL, cli_cmd_flag_none,
+	  "= flush ERP keys" },
+	{ "mac_rand_scan",
+	  wpa_cli_cmd_mac_rand_scan, NULL, cli_cmd_flag_none,
+	  "<scan|sched|pno|all> enable=<0/1> [addr=mac-address "
+	  "mask=mac-address-mask] = scan MAC randomization"
+	},
 	{ NULL, NULL, NULL, cli_cmd_flag_none, NULL }
 };
 
@@ -2959,7 +3362,7 @@
 /*
  * Prints command usage, lines are padded with the specified string.
  */
-static void print_cmd_help(struct wpa_cli_cmd *cmd, const char *pad)
+static void print_cmd_help(const struct wpa_cli_cmd *cmd, const char *pad)
 {
 	char c;
 	size_t n;
@@ -3097,7 +3500,7 @@
 
 static int wpa_request(struct wpa_ctrl *ctrl, int argc, char *argv[])
 {
-	struct wpa_cli_cmd *cmd, *match = NULL;
+	const struct wpa_cli_cmd *cmd, *match = NULL;
 	int count;
 	int ret = 0;
 
@@ -3245,6 +3648,14 @@
 			wpa_cli_connected = 0;
 			wpa_cli_exec(action_file, ifname, "DISCONNECTED");
 		}
+	} else if (str_match(pos, MESH_GROUP_STARTED)) {
+		wpa_cli_exec(action_file, ctrl_ifname, pos);
+	} else if (str_match(pos, MESH_GROUP_REMOVED)) {
+		wpa_cli_exec(action_file, ctrl_ifname, pos);
+	} else if (str_match(pos, MESH_PEER_CONNECTED)) {
+		wpa_cli_exec(action_file, ctrl_ifname, pos);
+	} else if (str_match(pos, MESH_PEER_DISCONNECTED)) {
+		wpa_cli_exec(action_file, ctrl_ifname, pos);
 	} else if (str_match(pos, P2P_EVENT_GROUP_STARTED)) {
 		wpa_cli_exec(action_file, ifname, pos);
 	} else if (str_match(pos, P2P_EVENT_GROUP_REMOVED)) {
@@ -3351,7 +3762,7 @@
 		s = os_strchr(start, ' ');
 		if (s == NULL)
 			return;
-		cli_txt_list_add_word(&p2p_groups, s + 1);
+		cli_txt_list_add_word(&p2p_groups, s + 1, ' ');
 		return;
 	}
 
@@ -3359,7 +3770,7 @@
 		s = os_strchr(start, ' ');
 		if (s == NULL)
 			return;
-		cli_txt_list_del_word(&p2p_groups, s + 1);
+		cli_txt_list_del_word(&p2p_groups, s + 1, ' ');
 		return;
 	}
 #endif /* CONFIG_P2P */
@@ -3518,7 +3929,11 @@
 	ps = wpa_ctrl_get_remote_ifname(ctrl_conn);
 #endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */
 
+#ifdef CONFIG_WPA_CLI_HISTORY_DIR
+	home = CONFIG_WPA_CLI_HISTORY_DIR;
+#else /* CONFIG_WPA_CLI_HISTORY_DIR */
 	home = getenv("HOME");
+#endif /* CONFIG_WPA_CLI_HISTORY_DIR */
 	if (home) {
 		const char *fname = ".wpa_cli_history";
 		int hfile_len = os_strlen(home) + 1 + os_strlen(fname) + 1;
@@ -3594,13 +4009,45 @@
 			break;
 		*end = '\0';
 		ret = os_snprintf(txt, sizeof(txt), "ifname=%s", pos);
-		if (ret > 0 && ret < (int) sizeof(txt))
+		if (!os_snprintf_error(sizeof(txt), ret))
 			cli_txt_list_add(&ifnames, txt);
 		pos = end + 1;
 	}
 }
 
 
+static void update_networks(struct wpa_ctrl *ctrl)
+{
+	char buf[4096];
+	size_t len = sizeof(buf);
+	int ret;
+	char *cmd = "LIST_NETWORKS";
+	char *pos, *end;
+	int header = 1;
+
+	cli_txt_list_flush(&networks);
+
+	if (ctrl == NULL)
+		return;
+	ret = wpa_ctrl_request(ctrl, cmd, os_strlen(cmd), buf, &len, NULL);
+	if (ret < 0)
+		return;
+	buf[len] = '\0';
+
+	pos = buf;
+	while (pos) {
+		end = os_strchr(pos, '\n');
+		if (end == NULL)
+			break;
+		*end = '\0';
+		if (!header)
+			cli_txt_list_add_word(&networks, pos, '\t');
+		header = 0;
+		pos = end + 1;
+	}
+}
+
+
 static void try_connection(void *eloop_ctx, void *timeout_ctx)
 {
 	if (ctrl_conn)
@@ -3612,7 +4059,8 @@
 	if (!wpa_cli_open_connection(ctrl_ifname, 1) == 0) {
 		if (!warning_displayed) {
 			printf("Could not connect to wpa_supplicant: "
-			       "%s - re-trying\n", ctrl_ifname);
+			       "%s - re-trying\n",
+			       ctrl_ifname ? ctrl_ifname : "(nil)");
 			warning_displayed = 1;
 		}
 		eloop_register_timeout(1, 0, try_connection, NULL, NULL);
@@ -3620,6 +4068,7 @@
 	}
 
 	update_bssid_list(ctrl_conn);
+	update_networks(ctrl_conn);
 
 	if (warning_displayed)
 		printf("Connection established.\n");
@@ -3641,6 +4090,7 @@
 	cli_txt_list_flush(&p2p_groups);
 	cli_txt_list_flush(&bsses);
 	cli_txt_list_flush(&ifnames);
+	cli_txt_list_flush(&networks);
 	if (edit_started)
 		edit_deinit(hfile, wpa_cli_edit_filter_history_cb);
 	os_free(hfile);
@@ -3649,45 +4099,49 @@
 }
 
 
+static void wpa_cli_action_ping(void *eloop_ctx, void *timeout_ctx)
+{
+	struct wpa_ctrl *ctrl = eloop_ctx;
+	char buf[256];
+	size_t len;
+
+	/* verify that connection is still working */
+	len = sizeof(buf) - 1;
+	if (wpa_ctrl_request(ctrl, "PING", 4, buf, &len,
+			     wpa_cli_action_cb) < 0 ||
+	    len < 4 || os_memcmp(buf, "PONG", 4) != 0) {
+		printf("wpa_supplicant did not reply to PING command - exiting\n");
+		eloop_terminate();
+		return;
+	}
+	eloop_register_timeout(ping_interval, 0, wpa_cli_action_ping,
+			       ctrl, NULL);
+}
+
+
+static void wpa_cli_action_receive(int sock, void *eloop_ctx, void *sock_ctx)
+{
+	struct wpa_ctrl *ctrl = eloop_ctx;
+
+	wpa_cli_recv_pending(ctrl, 1);
+}
+
+
 static void wpa_cli_action(struct wpa_ctrl *ctrl)
 {
 #ifdef CONFIG_ANSI_C_EXTRA
 	/* TODO: ANSI C version(?) */
 	printf("Action processing not supported in ANSI C build.\n");
 #else /* CONFIG_ANSI_C_EXTRA */
-	fd_set rfds;
-	int fd, res;
-	struct timeval tv;
-	char buf[256]; /* note: large enough to fit in unsolicited messages */
-	size_t len;
+	int fd;
 
 	fd = wpa_ctrl_get_fd(ctrl);
-
-	while (!wpa_cli_quit) {
-		FD_ZERO(&rfds);
-		FD_SET(fd, &rfds);
-		tv.tv_sec = ping_interval;
-		tv.tv_usec = 0;
-		res = select(fd + 1, &rfds, NULL, NULL, &tv);
-		if (res < 0 && errno != EINTR) {
-			perror("select");
-			break;
-		}
-
-		if (FD_ISSET(fd, &rfds))
-			wpa_cli_recv_pending(ctrl, 1);
-		else {
-			/* verify that connection is still working */
-			len = sizeof(buf) - 1;
-			if (wpa_ctrl_request(ctrl, "PING", 4, buf, &len,
-					     wpa_cli_action_cb) < 0 ||
-			    len < 4 || os_memcmp(buf, "PONG", 4) != 0) {
-				printf("wpa_supplicant did not reply to PING "
-				       "command - exiting\n");
-				break;
-			}
-		}
-	}
+	eloop_register_timeout(ping_interval, 0, wpa_cli_action_ping,
+			       ctrl, NULL);
+	eloop_register_read_sock(fd, wpa_cli_action_receive, ctrl, NULL);
+	eloop_run();
+	eloop_cancel_timeout(wpa_cli_action_ping, ctrl, NULL);
+	eloop_unregister_read_sock(fd);
 #endif /* CONFIG_ANSI_C_EXTRA */
 }
 
@@ -3873,7 +4327,8 @@
 		    wpa_cli_open_connection(ctrl_ifname, 0) < 0) {
 			fprintf(stderr, "Failed to connect to non-global "
 				"ctrl_ifname: %s  error: %s\n",
-				ctrl_ifname, strerror(errno));
+				ctrl_ifname ? ctrl_ifname : "(nil)",
+				strerror(errno));
 			return -1;
 		}
 
diff --git a/wpa_supplicant/wpa_gui-qt4/addinterface.cpp b/wpa_supplicant/wpa_gui-qt4/addinterface.cpp
index 27cbdd6..7d92f63 100644
--- a/wpa_supplicant/wpa_gui-qt4/addinterface.cpp
+++ b/wpa_supplicant/wpa_gui-qt4/addinterface.cpp
@@ -41,8 +41,8 @@
 	interfaceWidget->headerItem()->setText(0, tr("driver"));
 	interfaceWidget->headerItem()->setText(1, tr("interface"));
 	interfaceWidget->headerItem()->setText(2, tr("description"));
-	interfaceWidget->setItemsExpandable(FALSE);
-	interfaceWidget->setRootIsDecorated(FALSE);
+	interfaceWidget->setItemsExpandable(false);
+	interfaceWidget->setRootIsDecorated(false);
 	vboxLayout->addWidget(interfaceWidget);
 
 	connect(interfaceWidget,
@@ -196,9 +196,9 @@
 	 */
 	snprintf(cmd, sizeof(cmd),
 		 "INTERFACE_ADD %s\t%s\t%s\t%s\t%s\t%s",
-		 sel->text(1).toAscii().constData(),
+		 sel->text(1).toLocal8Bit().constData(),
 		 "default",
-		 sel->text(0).toAscii().constData(),
+		 sel->text(0).toLocal8Bit().constData(),
 		 "yes", "", "");
 	cmd[sizeof(cmd) - 1] = '\0';
 
diff --git a/wpa_supplicant/wpa_gui-qt4/addinterface.h b/wpa_supplicant/wpa_gui-qt4/addinterface.h
index 1b4c98d..332fc71 100644
--- a/wpa_supplicant/wpa_gui-qt4/addinterface.h
+++ b/wpa_supplicant/wpa_gui-qt4/addinterface.h
@@ -11,9 +11,9 @@
 
 #include <QObject>
 
-#include <QtGui/QDialog>
-#include <QtGui/QTreeWidget>
-#include <QtGui/QVBoxLayout>
+#include <QDialog>
+#include <QTreeWidget>
+#include <QVBoxLayout>
 
 class WpaGui;
 
diff --git a/wpa_supplicant/wpa_gui-qt4/eventhistory.cpp b/wpa_supplicant/wpa_gui-qt4/eventhistory.cpp
index a36085d..09145cd 100644
--- a/wpa_supplicant/wpa_gui-qt4/eventhistory.cpp
+++ b/wpa_supplicant/wpa_gui-qt4/eventhistory.cpp
@@ -73,15 +73,15 @@
 }
 
 
-EventHistory::EventHistory(QWidget *parent, const char *, bool, Qt::WFlags)
+EventHistory::EventHistory(QWidget *parent, const char *, bool, Qt::WindowFlags)
 	: QDialog(parent)
 {
 	setupUi(this);
 
 	connect(closeButton, SIGNAL(clicked()), this, SLOT(close()));
 
-	eventListView->setItemsExpandable(FALSE);
-	eventListView->setRootIsDecorated(FALSE);
+	eventListView->setItemsExpandable(false);
+	eventListView->setRootIsDecorated(false);
 	elm = new EventListModel(parent);
 	eventListView->setModel(elm);
 }
diff --git a/wpa_supplicant/wpa_gui-qt4/eventhistory.h b/wpa_supplicant/wpa_gui-qt4/eventhistory.h
index 3c01aa8..afd7b63 100644
--- a/wpa_supplicant/wpa_gui-qt4/eventhistory.h
+++ b/wpa_supplicant/wpa_gui-qt4/eventhistory.h
@@ -40,7 +40,7 @@
 
 public:
 	EventHistory(QWidget *parent = 0, const char *name = 0,
-		     bool modal = false, Qt::WFlags fl = 0);
+		     bool modal = false, Qt::WindowFlags fl = 0);
 	~EventHistory();
 
 public slots:
diff --git a/wpa_supplicant/wpa_gui-qt4/main.cpp b/wpa_supplicant/wpa_gui-qt4/main.cpp
index 73d677c..bbd45c6 100644
--- a/wpa_supplicant/wpa_gui-qt4/main.cpp
+++ b/wpa_supplicant/wpa_gui-qt4/main.cpp
@@ -14,24 +14,15 @@
 #include <QtCore/QTranslator>
 #include "wpagui.h"
 
-
-class WpaGuiApp : public QApplication
+WpaGuiApp::WpaGuiApp(int &argc, char **argv) :
+	QApplication(argc, argv),
+	argc(argc),
+	argv(argv)
 {
-public:
-	WpaGuiApp(int &argc, char **argv);
-
-#ifndef QT_NO_SESSIONMANAGER
-	virtual void saveState(QSessionManager &manager);
-#endif
-
-	WpaGui *w;
-};
-
-WpaGuiApp::WpaGuiApp(int &argc, char **argv) : QApplication(argc, argv)
-{
+	w = NULL;
 }
 
-#ifndef QT_NO_SESSIONMANAGER
+#if !defined(QT_NO_SESSIONMANAGER) && QT_VERSION < 0x050000
 void WpaGuiApp::saveState(QSessionManager &manager)
 {
 	QApplication::saveState(manager);
diff --git a/wpa_supplicant/wpa_gui-qt4/networkconfig.cpp b/wpa_supplicant/wpa_gui-qt4/networkconfig.cpp
index 737c41c..2727318 100644
--- a/wpa_supplicant/wpa_gui-qt4/networkconfig.cpp
+++ b/wpa_supplicant/wpa_gui-qt4/networkconfig.cpp
@@ -26,7 +26,8 @@
 #define WPA_GUI_KEY_DATA "[key is configured]"
 
 
-NetworkConfig::NetworkConfig(QWidget *parent, const char *, bool, Qt::WFlags)
+NetworkConfig::NetworkConfig(QWidget *parent, const char *, bool,
+			     Qt::WindowFlags)
 	: QDialog(parent)
 {
 	setupUi(this);
@@ -237,7 +238,7 @@
 	} else
 		id = edit_network_id;
 
-	setNetworkParam(id, "ssid", ssidEdit->text().toAscii().constData(),
+	setNetworkParam(id, "ssid", ssidEdit->text().toLocal8Bit().constData(),
 			true);
 
 	const char *key_mgmt = NULL, *proto = NULL, *pairwise = NULL;
@@ -291,14 +292,14 @@
 		setNetworkParam(id, "group", "TKIP CCMP WEP104 WEP40", false);
 	}
 	if (pskEdit->isEnabled() &&
-	    strcmp(pskEdit->text().toAscii().constData(),
+	    strcmp(pskEdit->text().toLocal8Bit().constData(),
 		   WPA_GUI_KEY_DATA) != 0)
 		setNetworkParam(id, "psk",
-				pskEdit->text().toAscii().constData(),
+				pskEdit->text().toLocal8Bit().constData(),
 				psklen != 64);
 	if (eapSelect->isEnabled()) {
 		const char *eap =
-			eapSelect->currentText().toAscii().constData();
+			eapSelect->currentText().toLocal8Bit().constData();
 		setNetworkParam(id, "eap", eap, false);
 		if (strcmp(eap, "SIM") == 0 || strcmp(eap, "AKA") == 0)
 			setNetworkParam(id, "pcsc", "", true);
@@ -314,21 +315,21 @@
 			if (inner.startsWith("EAP-"))
 				snprintf(phase2, sizeof(phase2), "auth=%s",
 					 inner.right(inner.size() - 4).
-					 toAscii().constData());
+					 toLocal8Bit().constData());
 		} else if (eap.compare("TTLS") == 0) {
 			if (inner.startsWith("EAP-"))
 				snprintf(phase2, sizeof(phase2), "autheap=%s",
 					 inner.right(inner.size() - 4).
-					 toAscii().constData());
+					 toLocal8Bit().constData());
 			else
 				snprintf(phase2, sizeof(phase2), "auth=%s",
-					 inner.toAscii().constData());
+					 inner.toLocal8Bit().constData());
 		} else if (eap.compare("FAST") == 0) {
 			const char *provisioning = NULL;
 			if (inner.startsWith("EAP-")) {
 				snprintf(phase2, sizeof(phase2), "auth=%s",
 					 inner.right(inner.size() - 4).
-					 toAscii().constData());
+					 toLocal8Bit().constData());
 				provisioning = "fast_provisioning=2";
 			} else if (inner.compare("GTC(auth) + MSCHAPv2(prov)")
 				   == 0) {
@@ -354,21 +355,21 @@
 		setNetworkParam(id, "phase2", "NULL", false);
 	if (identityEdit->isEnabled() && identityEdit->text().length() > 0)
 		setNetworkParam(id, "identity",
-				identityEdit->text().toAscii().constData(),
+				identityEdit->text().toLocal8Bit().constData(),
 				true);
 	else
 		setNetworkParam(id, "identity", "NULL", false);
 	if (passwordEdit->isEnabled() && passwordEdit->text().length() > 0 &&
-	    strcmp(passwordEdit->text().toAscii().constData(),
+	    strcmp(passwordEdit->text().toLocal8Bit().constData(),
 		   WPA_GUI_KEY_DATA) != 0)
 		setNetworkParam(id, "password",
-				passwordEdit->text().toAscii().constData(),
+				passwordEdit->text().toLocal8Bit().constData(),
 				true);
 	else if (passwordEdit->text().length() == 0)
 		setNetworkParam(id, "password", "NULL", false);
 	if (cacertEdit->isEnabled() && cacertEdit->text().length() > 0)
 		setNetworkParam(id, "ca_cert",
-				cacertEdit->text().toAscii().constData(),
+				cacertEdit->text().toLocal8Bit().constData(),
 				true);
 	else
 		setNetworkParam(id, "ca_cert", "NULL", false);
@@ -388,7 +389,7 @@
 
 	if (idstrEdit->isEnabled() && idstrEdit->text().length() > 0)
 		setNetworkParam(id, "id_str",
-				idstrEdit->text().toAscii().constData(),
+				idstrEdit->text().toLocal8Bit().constData(),
 				true);
 	else
 		setNetworkParam(id, "id_str", "NULL", false);
@@ -396,7 +397,7 @@
 	if (prioritySpinBox->isEnabled()) {
 		QString prio;
 		prio = prio.setNum(prioritySpinBox->value());
-		setNetworkParam(id, "priority", prio.toAscii().constData(),
+		setNetworkParam(id, "priority", prio.toLocal8Bit().constData(),
 				false);
 	}
 
@@ -468,7 +469,7 @@
 	 * Assume hex key if only hex characters are present and length matches
 	 * with 40, 104, or 128-bit key
 	 */
-	txt = edit->text().toAscii().constData();
+	txt = edit->text().toLocal8Bit().constData();
 	if (strcmp(txt, WPA_GUI_KEY_DATA) == 0)
 		return;
 	len = strlen(txt);
diff --git a/wpa_supplicant/wpa_gui-qt4/networkconfig.h b/wpa_supplicant/wpa_gui-qt4/networkconfig.h
index 429b648..fd09dec 100644
--- a/wpa_supplicant/wpa_gui-qt4/networkconfig.h
+++ b/wpa_supplicant/wpa_gui-qt4/networkconfig.h
@@ -20,7 +20,7 @@
 
 public:
 	NetworkConfig(QWidget *parent = 0, const char *name = 0,
-		      bool modal = false, Qt::WFlags fl = 0);
+		      bool modal = false, Qt::WindowFlags fl = 0);
 	~NetworkConfig();
 
 	virtual void paramsFromScanResults(QTreeWidgetItem *sel);
diff --git a/wpa_supplicant/wpa_gui-qt4/peers.cpp b/wpa_supplicant/wpa_gui-qt4/peers.cpp
index f5aa9f7..3bcf2f5 100644
--- a/wpa_supplicant/wpa_gui-qt4/peers.cpp
+++ b/wpa_supplicant/wpa_gui-qt4/peers.cpp
@@ -62,7 +62,7 @@
 };
 
 
-Peers::Peers(QWidget *parent, const char *, bool, Qt::WFlags)
+Peers::Peers(QWidget *parent, const char *, bool, Qt::WindowFlags)
 	: QDialog(parent)
 {
 	setupUi(this);
@@ -323,13 +323,13 @@
 
 	if (peer_type == PEER_TYPE_WPS_ER_ENROLLEE) {
 		snprintf(cmd, sizeof(cmd), "WPS_ER_PIN %s %s %s",
-			 uuid.toAscii().constData(),
-			 input.get_string().toAscii().constData(),
-			 addr.toAscii().constData());
+			 uuid.toLocal8Bit().constData(),
+			 input.get_string().toLocal8Bit().constData(),
+			 addr.toLocal8Bit().constData());
 	} else {
 		snprintf(cmd, sizeof(cmd), "WPS_PIN %s %s",
-			 addr.toAscii().constData(),
-			 input.get_string().toAscii().constData());
+			 addr.toLocal8Bit().constData(),
+			 input.get_string().toLocal8Bit().constData());
 	}
 	reply_len = sizeof(reply) - 1;
 	if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0) {
@@ -868,7 +868,7 @@
 		QStandardItem *item = find_addr(addr);
 		if (item == NULL || item->data(peer_role_type).toInt() !=
 		    PEER_TYPE_ASSOCIATED_STATION)
-			add_single_station(addr.toAscii().constData());
+			add_single_station(addr.toLocal8Bit().constData());
 		return;
 	}
 
@@ -1350,8 +1350,8 @@
 		char reply[100];
 		size_t reply_len;
 		snprintf(cmd, sizeof(cmd), "P2P_CONNECT %s %s display",
-			 addr.toAscii().constData(),
-			 arg.toAscii().constData());
+			 addr.toLocal8Bit().constData(),
+			 arg.toLocal8Bit().constData());
 		reply_len = sizeof(reply) - 1;
 		if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0) {
 			QMessageBox msg;
@@ -1384,8 +1384,8 @@
 	char reply[100];
 	size_t reply_len;
 	snprintf(cmd, sizeof(cmd), "P2P_CONNECT %s %s",
-		 addr.toAscii().constData(),
-		 arg.toAscii().constData());
+		 addr.toLocal8Bit().constData(),
+		 arg.toLocal8Bit().constData());
 	reply_len = sizeof(reply) - 1;
 	if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0) {
 		QMessageBox msg;
@@ -1408,7 +1408,7 @@
 	char reply[100];
 	size_t reply_len;
 	snprintf(cmd, sizeof(cmd), "P2P_PROV_DISC %s display",
-		 addr.toAscii().constData());
+		 addr.toLocal8Bit().constData());
 	reply_len = sizeof(reply) - 1;
 	if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0) {
 		QMessageBox msg;
@@ -1431,7 +1431,7 @@
 	char reply[100];
 	size_t reply_len;
 	snprintf(cmd, sizeof(cmd), "P2P_PROV_DISC %s keypad",
-		 addr.toAscii().constData());
+		 addr.toLocal8Bit().constData());
 	reply_len = sizeof(reply) - 1;
 	if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0) {
 		QMessageBox msg;
@@ -1452,7 +1452,7 @@
 	char reply[100];
 	size_t reply_len;
 	snprintf(cmd, sizeof(cmd), "P2P_CONNECT %s pin",
-		 addr.toAscii().constData());
+		 addr.toLocal8Bit().constData());
 	reply_len = sizeof(reply) - 1;
 	if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0) {
 		QMessageBox msg;
@@ -1480,8 +1480,8 @@
 	char reply[100];
 	size_t reply_len;
 	snprintf(cmd, sizeof(cmd), "P2P_CONNECT %s %s display",
-		 addr.toAscii().constData(),
-		 arg.toAscii().constData());
+		 addr.toLocal8Bit().constData(),
+		 arg.toLocal8Bit().constData());
 	reply_len = sizeof(reply) - 1;
 	if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0) {
 		QMessageBox msg;
@@ -1515,8 +1515,8 @@
 	char reply[100];
 	size_t reply_len;
 	snprintf(cmd, sizeof(cmd), "P2P_CONNECT %s %s keypad",
-		 addr.toAscii().constData(),
-		 arg.toAscii().constData());
+		 addr.toLocal8Bit().constData(),
+		 arg.toLocal8Bit().constData());
 	reply_len = sizeof(reply) - 1;
 	if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0) {
 		QMessageBox msg;
@@ -1535,7 +1535,7 @@
 	char reply[100];
 	size_t reply_len;
 	snprintf(cmd, sizeof(cmd), "P2P_GROUP_REMOVE %s",
-		 ctx_item->data(peer_role_ifname).toString().toAscii().
+		 ctx_item->data(peer_role_ifname).toString().toLocal8Bit().
 		 constData());
 	reply_len = sizeof(reply) - 1;
 	if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0) {
@@ -1713,13 +1713,13 @@
 	int peer_type = ctx_item->data(peer_role_type).toInt();
 	if (peer_type == PEER_TYPE_WPS_ER_ENROLLEE) {
 		snprintf(cmd, sizeof(cmd), "WPS_ER_PBC %s",
-			 ctx_item->data(peer_role_uuid).toString().toAscii().
+			 ctx_item->data(peer_role_uuid).toString().toLocal8Bit().
 			 constData());
 	} else if (peer_type == PEER_TYPE_P2P ||
 		   peer_type == PEER_TYPE_P2P_CLIENT) {
 		snprintf(cmd, sizeof(cmd), "P2P_CONNECT %s pbc",
 			 ctx_item->data(peer_role_address).toString().
-			 toAscii().constData());
+			 toLocal8Bit().constData());
 	} else {
 		snprintf(cmd, sizeof(cmd), "WPS_PBC");
 	}
@@ -1750,8 +1750,8 @@
 	size_t reply_len;
 
 	snprintf(cmd, sizeof(cmd), "WPS_ER_LEARN %s %s",
-		 uuid.toAscii().constData(),
-		 input.get_string().toAscii().constData());
+		 uuid.toLocal8Bit().constData(),
+		 input.get_string().toLocal8Bit().constData());
 	reply_len = sizeof(reply) - 1;
 	if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0) {
 		QMessageBox msg;
diff --git a/wpa_supplicant/wpa_gui-qt4/peers.h b/wpa_supplicant/wpa_gui-qt4/peers.h
index bac77dc..bb73737 100644
--- a/wpa_supplicant/wpa_gui-qt4/peers.h
+++ b/wpa_supplicant/wpa_gui-qt4/peers.h
@@ -22,7 +22,7 @@
 
 public:
 	Peers(QWidget *parent = 0, const char *name = 0,
-		    bool modal = false, Qt::WFlags fl = 0);
+		    bool modal = false, Qt::WindowFlags fl = 0);
 	~Peers();
 	void setWpaGui(WpaGui *_wpagui);
 	void event_notify(WpaMsg msg);
diff --git a/wpa_supplicant/wpa_gui-qt4/scanresults.cpp b/wpa_supplicant/wpa_gui-qt4/scanresults.cpp
index 063347e..a2e3072 100644
--- a/wpa_supplicant/wpa_gui-qt4/scanresults.cpp
+++ b/wpa_supplicant/wpa_gui-qt4/scanresults.cpp
@@ -12,9 +12,10 @@
 #include "signalbar.h"
 #include "wpagui.h"
 #include "networkconfig.h"
+#include "scanresultsitem.h"
 
 
-ScanResults::ScanResults(QWidget *parent, const char *, bool, Qt::WFlags)
+ScanResults::ScanResults(QWidget *parent, const char *, bool, Qt::WindowFlags)
 	: QDialog(parent)
 {
 	setupUi(this);
@@ -26,8 +27,8 @@
 		SLOT(bssSelected(QTreeWidgetItem *)));
 
 	wpagui = NULL;
-	scanResultsWidget->setItemsExpandable(FALSE);
-	scanResultsWidget->setRootIsDecorated(FALSE);
+	scanResultsWidget->setItemsExpandable(false);
+	scanResultsWidget->setRootIsDecorated(false);
 	scanResultsWidget->setItemDelegate(new SignalBar(scanResultsWidget));
 }
 
@@ -95,7 +96,7 @@
 				ssid = (*it).mid(pos);
 		}
 
-		QTreeWidgetItem *item = new QTreeWidgetItem(scanResultsWidget);
+		ScanResultsItem *item = new ScanResultsItem(scanResultsWidget);
 		if (item) {
 			item->setText(0, ssid);
 			item->setText(1, bssid);
diff --git a/wpa_supplicant/wpa_gui-qt4/scanresults.h b/wpa_supplicant/wpa_gui-qt4/scanresults.h
index 4a5842c..2cddd13 100644
--- a/wpa_supplicant/wpa_gui-qt4/scanresults.h
+++ b/wpa_supplicant/wpa_gui-qt4/scanresults.h
@@ -20,7 +20,7 @@
 
 public:
 	ScanResults(QWidget *parent = 0, const char *name = 0,
-		    bool modal = false, Qt::WFlags fl = 0);
+		    bool modal = false, Qt::WindowFlags fl = 0);
 	~ScanResults();
 
 public slots:
diff --git a/wpa_supplicant/wpa_gui-qt4/scanresultsitem.cpp b/wpa_supplicant/wpa_gui-qt4/scanresultsitem.cpp
new file mode 100644
index 0000000..9cd937c
--- /dev/null
+++ b/wpa_supplicant/wpa_gui-qt4/scanresultsitem.cpp
@@ -0,0 +1,18 @@
+/*
+ * wpa_gui - ScanResultsItem class
+ * Copyright (c) 2015, Adrian Nowicki <adinowicki@gmail.com>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "scanresultsitem.h"
+
+bool ScanResultsItem::operator< (const QTreeWidgetItem &other) const
+{
+	int sortCol = treeWidget()->sortColumn();
+	if (sortCol == 2 || sortCol == 3) {
+		return text(sortCol).toInt() < other.text(sortCol).toInt();
+	}
+	return text(sortCol) < other.text(sortCol);
+}
diff --git a/wpa_supplicant/wpa_gui-qt4/scanresultsitem.h b/wpa_supplicant/wpa_gui-qt4/scanresultsitem.h
new file mode 100644
index 0000000..74887ee
--- /dev/null
+++ b/wpa_supplicant/wpa_gui-qt4/scanresultsitem.h
@@ -0,0 +1,21 @@
+/*
+ * wpa_gui - ScanResultsItem class
+ * Copyright (c) 2015, Adrian Nowicki <adinowicki@gmail.com>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef SCANRESULTSITEM_H
+#define SCANRESULTSITEM_H
+
+#include <QTreeWidgetItem>
+
+class ScanResultsItem : public QTreeWidgetItem
+{
+public:
+	ScanResultsItem(QTreeWidget *tree) : QTreeWidgetItem(tree) {}
+	bool operator< (const QTreeWidgetItem &other) const;
+};
+
+#endif /* SCANRESULTSITEM_H */
diff --git a/wpa_supplicant/wpa_gui-qt4/userdatarequest.cpp b/wpa_supplicant/wpa_gui-qt4/userdatarequest.cpp
index ba4c9f4..9d933b0 100644
--- a/wpa_supplicant/wpa_gui-qt4/userdatarequest.cpp
+++ b/wpa_supplicant/wpa_gui-qt4/userdatarequest.cpp
@@ -12,7 +12,7 @@
 
 
 UserDataRequest::UserDataRequest(QWidget *parent, const char *, bool,
-				 Qt::WFlags)
+				 Qt::WindowFlags)
 	: QDialog(parent)
 {
 	setupUi(this);
@@ -89,6 +89,6 @@
 	QString cmd = QString(WPA_CTRL_RSP) + field + '-' +
 		QString::number(networkid) + ':' +
 		queryEdit->text();
-	wpagui->ctrlRequest(cmd.toAscii().constData(), reply, &reply_len);
+	wpagui->ctrlRequest(cmd.toLocal8Bit().constData(), reply, &reply_len);
 	accept();
 }
diff --git a/wpa_supplicant/wpa_gui-qt4/userdatarequest.h b/wpa_supplicant/wpa_gui-qt4/userdatarequest.h
index 0d9dbfc..b6d1ad2 100644
--- a/wpa_supplicant/wpa_gui-qt4/userdatarequest.h
+++ b/wpa_supplicant/wpa_gui-qt4/userdatarequest.h
@@ -20,7 +20,7 @@
 
 public:
 	UserDataRequest(QWidget *parent = 0, const char *name = 0,
-			bool modal = false, Qt::WFlags fl = 0);
+			bool modal = false, Qt::WindowFlags fl = 0);
 	~UserDataRequest();
 
 	int setParams(WpaGui *_wpagui, const char *reqMsg);
diff --git a/wpa_supplicant/wpa_gui-qt4/wpa_gui.pro b/wpa_supplicant/wpa_gui-qt4/wpa_gui.pro
index 3c81929..3fa734b 100644
--- a/wpa_supplicant/wpa_gui-qt4/wpa_gui.pro
+++ b/wpa_supplicant/wpa_gui-qt4/wpa_gui.pro
@@ -1,6 +1,7 @@
 TEMPLATE	= app
 LANGUAGE	= C++
 TRANSLATIONS	= lang/wpa_gui_de.ts
+greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
 
 CONFIG	+= qt warn_on release
 
@@ -34,6 +35,7 @@
 	wpagui.h \
 	eventhistory.h \
 	scanresults.h \
+	scanresultsitem.h \
 	signalbar.h \
 	userdatarequest.h \
 	networkconfig.h \
@@ -45,6 +47,7 @@
 	wpagui.cpp \
 	eventhistory.cpp \
 	scanresults.cpp \
+	scanresultsitem.cpp \
 	signalbar.cpp \
 	userdatarequest.cpp \
 	networkconfig.cpp \
diff --git a/wpa_supplicant/wpa_gui-qt4/wpagui.cpp b/wpa_supplicant/wpa_gui-qt4/wpagui.cpp
index 6bba8d2..3bd3a9c 100644
--- a/wpa_supplicant/wpa_gui-qt4/wpagui.cpp
+++ b/wpa_supplicant/wpa_gui-qt4/wpagui.cpp
@@ -23,19 +23,20 @@
 #include "userdatarequest.h"
 #include "networkconfig.h"
 
-#if 1
-/* Silence stdout */
-#define printf wpagui_printf
-static int wpagui_printf(const char *, ...)
-{
-	return 0;
-}
+
+#ifndef QT_NO_DEBUG
+#define debug(M, ...) qDebug("DEBUG %d: " M, __LINE__, ##__VA_ARGS__)
+#else
+#define debug(M, ...) do {} while (0)
 #endif
 
-WpaGui::WpaGui(QApplication *_app, QWidget *parent, const char *, Qt::WFlags)
+
+WpaGui::WpaGui(QApplication *_app, QWidget *parent, const char *,
+	       Qt::WindowFlags)
 	: QMainWindow(parent), app(_app)
 {
 	setupUi(this);
+	this->setWindowFlags(Qt::Dialog);
 
 #ifdef CONFIG_NATIVE_WINDOWS
 	fileStopServiceAction = new QAction(this);
@@ -129,11 +130,13 @@
 	udr = NULL;
 	tray_icon = NULL;
 	startInTray = false;
+	quietMode = false;
 	ctrl_iface = NULL;
 	ctrl_conn = NULL;
 	monitor_conn = NULL;
 	msgNotifier = NULL;
 	ctrl_iface_dir = strdup("/var/run/wpa_supplicant");
+	signalMeterInterval = 0;
 
 	parse_argv();
 
@@ -157,12 +160,16 @@
 	textStatus->setText(tr("connecting to wpa_supplicant"));
 	timer = new QTimer(this);
 	connect(timer, SIGNAL(timeout()), SLOT(ping()));
-	timer->setSingleShot(FALSE);
+	timer->setSingleShot(false);
 	timer->start(1000);
 
+	signalMeterTimer = new QTimer(this);
+	signalMeterTimer->setInterval(signalMeterInterval);
+	connect(signalMeterTimer, SIGNAL(timeout()), SLOT(signalMeterUpdate()));
+
 	if (openCtrlConnection(ctrl_iface) < 0) {
-		printf("Failed to open control connection to "
-		       "wpa_supplicant.\n");
+		debug("Failed to open control connection to "
+		      "wpa_supplicant.");
 	}
 
 	updateStatus();
@@ -232,8 +239,9 @@
 void WpaGui::parse_argv()
 {
 	int c;
+	WpaGuiApp *app = qobject_cast<WpaGuiApp*>(qApp);
 	for (;;) {
-		c = getopt(qApp->argc(), qApp->argv(), "i:p:t");
+		c = getopt(app->argc, app->argv, "i:m:p:tq");
 		if (c < 0)
 			break;
 		switch (c) {
@@ -241,6 +249,9 @@
 			free(ctrl_iface);
 			ctrl_iface = strdup(optarg);
 			break;
+		case 'm':
+			signalMeterInterval = atoi(optarg) * 1000;
+			break;
 		case 'p':
 			free(ctrl_iface_dir);
 			ctrl_iface_dir = strdup(optarg);
@@ -248,6 +259,9 @@
 		case 't':
 			startInTray = true;
 			break;
+		case 'q':
+			quietMode = true;
+			break;
 		}
 	}
 }
@@ -290,8 +304,8 @@
 				if (strcmp(dent->d_name, ".") == 0 ||
 				    strcmp(dent->d_name, "..") == 0)
 					continue;
-				printf("Selected interface '%s'\n",
-				       dent->d_name);
+				debug("Selected interface '%s'",
+				      dent->d_name);
 				ctrl_iface = strdup(dent->d_name);
 				break;
 			}
@@ -368,7 +382,7 @@
 		monitor_conn = NULL;
 	}
 
-	printf("Trying to connect to '%s'\n", cfile);
+	debug("Trying to connect to '%s'", cfile);
 	ctrl_conn = wpa_ctrl_open(cfile);
 	if (ctrl_conn == NULL) {
 		free(cfile);
@@ -381,7 +395,7 @@
 		return -1;
 	}
 	if (wpa_ctrl_attach(monitor_conn)) {
-		printf("Failed to attach to wpa_supplicant\n");
+		debug("Failed to attach to wpa_supplicant");
 		wpa_ctrl_close(monitor_conn);
 		monitor_conn = NULL;
 		wpa_ctrl_close(ctrl_conn);
@@ -442,9 +456,9 @@
 		return -3;
 	ret = wpa_ctrl_request(ctrl_conn, cmd, strlen(cmd), buf, buflen, NULL);
 	if (ret == -2)
-		printf("'%s' command timed out.\n", cmd);
+		debug("'%s' command timed out.", cmd);
 	else if (ret < 0)
-		printf("'%s' command failed.\n", cmd);
+		debug("'%s' command failed.", cmd);
 
 	return ret;
 }
@@ -491,6 +505,9 @@
 		textSsid->clear();
 		textBssid->clear();
 		textIpAddress->clear();
+		updateTrayToolTip(tr("no status information"));
+		updateTrayIcon(TrayIconOffline);
+		signalMeterTimer->stop();
 
 #ifdef CONFIG_NATIVE_WINDOWS
 		static bool first = true;
@@ -538,6 +555,12 @@
 			} else if (strcmp(start, "ssid") == 0) {
 				ssid_updated = true;
 				textSsid->setText(pos);
+				updateTrayToolTip(pos + tr(" (associated)"));
+				if (!signalMeterInterval) {
+					/* if signal meter is not enabled show
+					 * full signal strength */
+					updateTrayIcon(TrayIconSignalExcellent);
+				}
 			} else if (strcmp(start, "ip_address") == 0) {
 				ipaddr_updated = true;
 				textIpAddress->setText(pos);
@@ -581,12 +604,32 @@
 	} else
 		textEncryption->clear();
 
+	if (signalMeterInterval) {
+		/*
+		 * Handle signal meter service. When network is not associated,
+		 * deactivate timer, otherwise keep it going. Tray icon has to
+		 * be initialized here, because of the initial delay of the
+		 * timer.
+		 */
+		if (ssid_updated) {
+			if (!signalMeterTimer->isActive()) {
+				updateTrayIcon(TrayIconConnected);
+				signalMeterTimer->start();
+			}
+		} else {
+			signalMeterTimer->stop();
+		}
+	}
+
 	if (!status_updated)
 		textStatus->clear();
 	if (!auth_updated)
 		textAuthentication->clear();
-	if (!ssid_updated)
+	if (!ssid_updated) {
 		textSsid->clear();
+		updateTrayToolTip(tr("(not-associated)"));
+		updateTrayIcon(TrayIconOffline);
+	}
 	if (!bssid_updated)
 		textBssid->clear();
 	if (!ipaddr_updated)
@@ -696,20 +739,20 @@
 
 void WpaGui::helpIndex()
 {
-	printf("helpIndex\n");
+	debug("helpIndex");
 }
 
 
 void WpaGui::helpContents()
 {
-	printf("helpContents\n");
+	debug("helpContents");
 }
 
 
 void WpaGui::helpAbout()
 {
 	QMessageBox::about(this, "wpa_gui for wpa_supplicant",
-			   "Copyright (c) 2003-2013,\n"
+			   "Copyright (c) 2003-2015,\n"
 			   "Jouni Malinen <j@w1.fi>\n"
 			   "and contributors.\n"
 			   "\n"
@@ -797,9 +840,9 @@
 
 	len = sizeof(buf) - 1;
 	if (ctrlRequest("PING", buf, &len) < 0) {
-		printf("PING failed - trying to reconnect\n");
+		debug("PING failed - trying to reconnect");
 		if (openCtrlConnection(ctrl_iface) >= 0) {
-			printf("Reconnected successfully\n");
+			debug("Reconnected successfully");
 			pingsToStatusUpdate = 0;
 		}
 	}
@@ -820,6 +863,53 @@
 }
 
 
+void WpaGui::signalMeterUpdate()
+{
+	char reply[128];
+	size_t reply_len = sizeof(reply);
+	char *rssi;
+	int rssi_value;
+
+	ctrlRequest("SIGNAL_POLL", reply, &reply_len);
+
+	/* In order to eliminate signal strength fluctuations, try
+	 * to obtain averaged RSSI value in the first place. */
+	if ((rssi = strstr(reply, "AVG_RSSI=")) != NULL)
+		rssi_value = atoi(&rssi[sizeof("AVG_RSSI")]);
+	else if ((rssi = strstr(reply, "RSSI=")) != NULL)
+		rssi_value = atoi(&rssi[sizeof("RSSI")]);
+	else {
+		debug("Failed to get RSSI value");
+		updateTrayIcon(TrayIconSignalNone);
+		return;
+	}
+
+	debug("RSSI value: %d", rssi_value);
+
+	/*
+	 * NOTE: The code below assumes, that the unit of the value returned
+	 * by the SIGNAL POLL request is dBm. It might not be true for all
+	 * wpa_supplicant drivers.
+	 */
+
+	/*
+	 * Calibration is based on "various Internet sources". Nonetheless,
+	 * it seems to be compatible with the Windows 8.1 strength meter -
+	 * tested on Intel Centrino Advanced-N 6235.
+	 */
+	if (rssi_value >= -60)
+		updateTrayIcon(TrayIconSignalExcellent);
+	else if (rssi_value >= -68)
+		updateTrayIcon(TrayIconSignalGood);
+	else if (rssi_value >= -76)
+		updateTrayIcon(TrayIconSignalOk);
+	else if (rssi_value >= -84)
+		updateTrayIcon(TrayIconSignalWeak);
+	else
+		updateTrayIcon(TrayIconSignalNone);
+}
+
+
 static int str_match(const char *a, const char *b)
 {
 	return strncmp(a, b, strlen(b)) == 0;
@@ -978,7 +1068,7 @@
 	else
 		cmd = "any";
 	cmd.prepend("SELECT_NETWORK ");
-	ctrlRequest(cmd.toAscii().constData(), reply, &reply_len);
+	ctrlRequest(cmd.toLocal8Bit().constData(), reply, &reply_len);
 	triggerUpdate();
 	stopWpsRun(false);
 }
@@ -993,12 +1083,12 @@
 	if (cmd.contains(QRegExp("^\\d+:")))
 		cmd.truncate(cmd.indexOf(':'));
 	else if (!cmd.startsWith("all")) {
-		printf("Invalid editNetwork '%s'\n",
-		       cmd.toAscii().constData());
+		debug("Invalid editNetwork '%s'",
+		      cmd.toLocal8Bit().constData());
 		return;
 	}
 	cmd.prepend("ENABLE_NETWORK ");
-	ctrlRequest(cmd.toAscii().constData(), reply, &reply_len);
+	ctrlRequest(cmd.toLocal8Bit().constData(), reply, &reply_len);
 	triggerUpdate();
 }
 
@@ -1012,12 +1102,12 @@
 	if (cmd.contains(QRegExp("^\\d+:")))
 		cmd.truncate(cmd.indexOf(':'));
 	else if (!cmd.startsWith("all")) {
-		printf("Invalid editNetwork '%s'\n",
-		       cmd.toAscii().constData());
+		debug("Invalid editNetwork '%s'",
+		      cmd.toLocal8Bit().constData());
 		return;
 	}
 	cmd.prepend("DISABLE_NETWORK ");
-	ctrlRequest(cmd.toAscii().constData(), reply, &reply_len);
+	ctrlRequest(cmd.toLocal8Bit().constData(), reply, &reply_len);
 	triggerUpdate();
 }
 
@@ -1102,12 +1192,12 @@
 	if (cmd.contains(QRegExp("^\\d+:")))
 		cmd.truncate(cmd.indexOf(':'));
 	else if (!cmd.startsWith("all")) {
-		printf("Invalid editNetwork '%s'\n",
-		       cmd.toAscii().constData());
+		debug("Invalid editNetwork '%s'",
+		      cmd.toLocal8Bit().constData());
 		return;
 	}
 	cmd.prepend("REMOVE_NETWORK ");
-	ctrlRequest(cmd.toAscii().constData(), reply, &reply_len);
+	ctrlRequest(cmd.toLocal8Bit().constData(), reply, &reply_len);
 	triggerUpdate();
 }
 
@@ -1166,15 +1256,15 @@
 	size_t reply_len = sizeof(reply) - 1;
 	int pos = cmd.indexOf(':');
 	if (pos < 0) {
-		printf("Invalid getNetworkDisabled '%s'\n",
-		       cmd.toAscii().constData());
+		debug("Invalid getNetworkDisabled '%s'",
+		      cmd.toLocal8Bit().constData());
 		return -1;
 	}
 	cmd.truncate(pos);
 	cmd.prepend("GET_NETWORK ");
 	cmd.append(" disabled");
 
-	if (ctrlRequest(cmd.toAscii().constData(), reply, &reply_len) >= 0
+	if (ctrlRequest(cmd.toLocal8Bit().constData(), reply, &reply_len) >= 0
 	    && reply_len >= 1) {
 		reply[reply_len] = '\0';
 		if (!str_match(reply, "FAIL"))
@@ -1257,9 +1347,9 @@
 
 void WpaGui::selectAdapter( const QString & sel )
 {
-	if (openCtrlConnection(sel.toAscii().constData()) < 0)
-		printf("Failed to open control connection to "
-		       "wpa_supplicant.\n");
+	if (openCtrlConnection(sel.toLocal8Bit().constData()) < 0)
+		debug("Failed to open control connection to "
+		      "wpa_supplicant.");
 	updateStatus();
 	updateNetworks();
 }
@@ -1270,11 +1360,7 @@
 	QApplication::setQuitOnLastWindowClosed(false);
 
 	tray_icon = new QSystemTrayIcon(this);
-	tray_icon->setToolTip(qAppName() + tr(" - wpa_supplicant user interface"));
-	if (QImageReader::supportedImageFormats().contains(QByteArray("svg")))
-		tray_icon->setIcon(QIcon(":/icons/wpa_gui.svg"));
-	else
-		tray_icon->setIcon(QIcon(":/icons/wpa_gui.png"));
+	updateTrayIcon(TrayIconOffline);
 
 	connect(tray_icon,
 		SIGNAL(activated(QSystemTrayIcon::ActivationReason)),
@@ -1332,7 +1418,7 @@
 	if (!QSystemTrayIcon::supportsMessages())
 		return;
 
-	if (isVisible() || !tray_icon || !tray_icon->isVisible())
+	if (isVisible() || !tray_icon || !tray_icon->isVisible() || quietMode)
 		return;
 
 	tray_icon->showMessage(qAppName(), msg, type, sec * 1000);
@@ -1407,6 +1493,84 @@
 }
 
 
+void WpaGui::updateTrayToolTip(const QString &msg)
+{
+	if (tray_icon)
+		tray_icon->setToolTip(msg);
+}
+
+
+void WpaGui::updateTrayIcon(TrayIconType type)
+{
+	if (!tray_icon || currentIconType == type)
+		return;
+
+	QIcon fallback_icon;
+	QStringList names;
+
+	if (QImageReader::supportedImageFormats().contains(QByteArray("svg")))
+		fallback_icon = QIcon(":/icons/wpa_gui.svg");
+	else
+		fallback_icon = QIcon(":/icons/wpa_gui.png");
+
+	switch (type) {
+	case TrayIconOffline:
+		names << "network-wireless-offline-symbolic"
+		      << "network-wireless-offline"
+		      << "network-wireless-signal-none-symbolic"
+		      << "network-wireless-signal-none";
+		break;
+	case TrayIconAcquiring:
+		names << "network-wireless-acquiring-symbolic"
+		      << "network-wireless-acquiring";
+		break;
+	case TrayIconConnected:
+		names << "network-wireless-connected-symbolic"
+		      << "network-wireless-connected";
+		break;
+	case TrayIconSignalNone:
+		names << "network-wireless-signal-none-symbolic"
+		      << "network-wireless-signal-none";
+		break;
+	case TrayIconSignalWeak:
+		names << "network-wireless-signal-weak-symbolic"
+		      << "network-wireless-signal-weak";
+		break;
+	case TrayIconSignalOk:
+		names << "network-wireless-signal-ok-symbolic"
+		      << "network-wireless-signal-ok";
+		break;
+	case TrayIconSignalGood:
+		names << "network-wireless-signal-good-symbolic"
+		      << "network-wireless-signal-good";
+		break;
+	case TrayIconSignalExcellent:
+		names << "network-wireless-signal-excellent-symbolic"
+		      << "network-wireless-signal-excellent";
+		break;
+	}
+
+	currentIconType = type;
+	tray_icon->setIcon(loadThemedIcon(names, fallback_icon));
+}
+
+
+QIcon WpaGui::loadThemedIcon(const QStringList &names,
+			     const QIcon &fallback)
+{
+	QIcon icon;
+
+	for (QStringList::ConstIterator it = names.begin();
+	     it != names.end(); it++) {
+		icon = QIcon::fromTheme(*it);
+		if (!icon.isNull())
+			return icon;
+	}
+
+	return fallback;
+}
+
+
 void WpaGui::closeEvent(QCloseEvent *event)
 {
 	if (eh) {
@@ -1557,7 +1721,7 @@
 	size_t reply_len = sizeof(reply);
 
 	QString cmd("WPS_REG " + bssFromScan + " " + wpsApPinEdit->text());
-	if (ctrlRequest(cmd.toAscii().constData(), reply, &reply_len) < 0)
+	if (ctrlRequest(cmd.toLocal8Bit().constData(), reply, &reply_len) < 0)
 		return;
 
 	wpsStatusText->setText(tr("Waiting for AP/Enrollee"));
@@ -1685,13 +1849,13 @@
 
 	scm = OpenSCManager(0, 0, SC_MANAGER_CONNECT);
 	if (!scm) {
-		printf("OpenSCManager failed: %d\n", (int) GetLastError());
+		debug("OpenSCManager failed: %d", (int) GetLastError());
 		return false;
 	}
 
 	svc = OpenService(scm, WPASVC_NAME, SERVICE_QUERY_STATUS);
 	if (!svc) {
-		printf("OpenService failed: %d\n\n", (int) GetLastError());
+		debug("OpenService failed: %d", (int) GetLastError());
 		CloseServiceHandle(scm);
 		return false;
 	}
diff --git a/wpa_supplicant/wpa_gui-qt4/wpagui.h b/wpa_supplicant/wpa_gui-qt4/wpagui.h
index 340286c..f0a34c9 100644
--- a/wpa_supplicant/wpa_gui-qt4/wpagui.h
+++ b/wpa_supplicant/wpa_gui-qt4/wpagui.h
@@ -16,14 +16,40 @@
 
 class UserDataRequest;
 
+class WpaGuiApp : public QApplication
+{
+	Q_OBJECT
+public:
+	WpaGuiApp(int &argc, char **argv);
+
+#if !defined(QT_NO_SESSIONMANAGER) && QT_VERSION < 0x050000
+	virtual void saveState(QSessionManager &manager);
+#endif
+
+	WpaGui *w;
+	int argc;
+	char **argv;
+};
 
 class WpaGui : public QMainWindow, public Ui::WpaGui
 {
 	Q_OBJECT
 
 public:
+
+	enum TrayIconType {
+		TrayIconOffline = 0,
+		TrayIconAcquiring,
+		TrayIconConnected,
+		TrayIconSignalNone,
+		TrayIconSignalWeak,
+		TrayIconSignalOk,
+		TrayIconSignalGood,
+		TrayIconSignalExcellent,
+	};
+
 	WpaGui(QApplication *app, QWidget *parent = 0, const char *name = 0,
-	       Qt::WFlags fl = 0);
+	       Qt::WindowFlags fl = 0);
 	~WpaGui();
 
 	virtual int ctrlRequest(const char *cmd, char *buf, size_t *buflen);
@@ -49,6 +75,7 @@
 	virtual void scan();
 	virtual void eventHistory();
 	virtual void ping();
+	virtual void signalMeterUpdate();
 	virtual void processMsg(char *msg);
 	virtual void processCtrlReq(const char *req);
 	virtual void receiveMsgs();
@@ -70,6 +97,10 @@
 	virtual void showTrayMessage(QSystemTrayIcon::MessageIcon type,
 				     int sec, const QString &msg);
 	virtual void showTrayStatus();
+	virtual void updateTrayIcon(TrayIconType type);
+	virtual void updateTrayToolTip(const QString &msg);
+	virtual QIcon loadThemedIcon(const QStringList &names,
+				     const QIcon &fallback);
 	virtual void wpsDialog();
 	virtual void peersDialog();
 	virtual void tabChanged(int index);
@@ -112,10 +143,12 @@
 	QAction *quitAction;
 	QMenu *tray_menu;
 	QSystemTrayIcon *tray_icon;
+	TrayIconType currentIconType;
 	QString wpaStateTranslate(char *state);
 	void createTrayIcon(bool);
 	bool ackTrayIcon;
 	bool startInTray;
+	bool quietMode;
 
 	int openCtrlConnection(const char *ifname);
 
@@ -125,6 +158,9 @@
 
 	void stopWpsRun(bool success);
 
+	QTimer *signalMeterTimer;
+	int signalMeterInterval;
+
 #ifdef CONFIG_NATIVE_WINDOWS
 	QAction *fileStartServiceAction;
 	QAction *fileStopServiceAction;
diff --git a/wpa_supplicant/wpa_priv.c b/wpa_supplicant/wpa_priv.c
index 5426177..4611a1d 100644
--- a/wpa_supplicant/wpa_priv.c
+++ b/wpa_supplicant/wpa_priv.c
@@ -29,7 +29,7 @@
 	char *sock_name;
 	int fd;
 
-	struct wpa_driver_ops *driver;
+	const struct wpa_driver_ops *driver;
 	void *drv_priv;
 	struct sockaddr_un drv_addr;
 	int wpas_registered;
@@ -199,10 +199,12 @@
 	if (bssid[0] | bssid[1] | bssid[2] | bssid[3] | bssid[4] | bssid[5])
 		params.bssid = bssid;
 	params.ssid = assoc->ssid;
-	if (assoc->ssid_len > 32)
+	if (assoc->ssid_len > SSID_MAX_LEN)
 		return;
 	params.ssid_len = assoc->ssid_len;
-	params.freq = assoc->freq;
+	params.freq.mode = assoc->hwmode;
+	params.freq.freq = assoc->freq;
+	params.freq.channel = assoc->channel;
 	if (assoc->wpa_ie_len) {
 		params.wpa_ie = (u8 *) (assoc + 1);
 		params.wpa_ie_len = assoc->wpa_ie_len;
@@ -242,7 +244,7 @@
 static void wpa_priv_cmd_get_ssid(struct wpa_priv_interface *iface,
 				  struct sockaddr_un *from)
 {
-	u8 ssid[sizeof(int) + 32];
+	u8 ssid[sizeof(int) + SSID_MAX_LEN];
 	int res;
 
 	if (iface->drv_priv == NULL)
@@ -252,7 +254,7 @@
 		goto fail;
 
 	res = iface->driver->get_ssid(iface->drv_priv, &ssid[sizeof(int)]);
-	if (res < 0 || res > 32)
+	if (res < 0 || res > SSID_MAX_LEN)
 		goto fail;
 	os_memcpy(ssid, &res, sizeof(int));
 
@@ -333,7 +335,7 @@
 	msg.msg_namelen = sizeof(iface->l2_addr);
 
 	if (sendmsg(iface->fd, &msg, 0) < 0) {
-		perror("sendmsg(l2 rx)");
+		wpa_printf(MSG_ERROR, "sendmsg(l2 rx): %s", strerror(errno));
 	}
 }
 
@@ -465,7 +467,7 @@
 	res = recvfrom(sock, buf, sizeof(buf), 0, (struct sockaddr *) &from,
 		       &fromlen);
 	if (res < 0) {
-		perror("recvfrom");
+		wpa_printf(MSG_ERROR, "recvfrom: %s", strerror(errno));
 		return;
 	}
 
@@ -613,7 +615,7 @@
 
 	iface->fd = socket(PF_UNIX, SOCK_DGRAM, 0);
 	if (iface->fd < 0) {
-		perror("socket(PF_UNIX)");
+		wpa_printf(MSG_ERROR, "socket(PF_UNIX): %s", strerror(errno));
 		wpa_priv_interface_deinit(iface);
 		return NULL;
 	}
@@ -631,15 +633,16 @@
 				   "allow connections - assuming it was "
 				   "leftover from forced program termination");
 			if (unlink(iface->sock_name) < 0) {
-				perror("unlink[ctrl_iface]");
-				wpa_printf(MSG_ERROR, "Could not unlink "
-					   "existing ctrl_iface socket '%s'",
-					   iface->sock_name);
+				wpa_printf(MSG_ERROR,
+					   "Could not unlink existing ctrl_iface socket '%s': %s",
+					   iface->sock_name, strerror(errno));
 				goto fail;
 			}
 			if (bind(iface->fd, (struct sockaddr *) &addr,
 				 sizeof(addr)) < 0) {
-				perror("wpa-priv-iface-init: bind(PF_UNIX)");
+				wpa_printf(MSG_ERROR,
+					   "wpa-priv-iface-init: bind(PF_UNIX): %s",
+					   strerror(errno));
 				goto fail;
 			}
 			wpa_printf(MSG_DEBUG, "Successfully replaced leftover "
@@ -654,7 +657,7 @@
 	}
 
 	if (chmod(iface->sock_name, S_IRWXU | S_IRWXG | S_IRWXO) < 0) {
-		perror("chmod");
+		wpa_printf(MSG_ERROR, "chmod: %s", strerror(errno));
 		goto fail;
 	}
 
@@ -686,7 +689,8 @@
 	msg.msg_namelen = sizeof(iface->drv_addr);
 
 	if (sendmsg(iface->fd, &msg, 0) < 0) {
-		perror("sendmsg(wpas_socket)");
+		wpa_printf(MSG_ERROR, "sendmsg(wpas_socket): %s",
+			   strerror(errno));
 		return -1;
 	}
 
@@ -901,7 +905,8 @@
 	msg.msg_namelen = sizeof(iface->drv_addr);
 
 	if (sendmsg(iface->fd, &msg, 0) < 0)
-		perror("sendmsg(wpas_socket)");
+		wpa_printf(MSG_ERROR, "sendmsg(wpas_socket): %s",
+			   strerror(errno));
 }
 
 
diff --git a/wpa_supplicant/wpa_supplicant.c b/wpa_supplicant/wpa_supplicant.c
index eef3d21..722294d 100644
--- a/wpa_supplicant/wpa_supplicant.c
+++ b/wpa_supplicant/wpa_supplicant.c
@@ -1,6 +1,6 @@
 /*
  * WPA Supplicant
- * Copyright (c) 2003-2014, 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.
@@ -33,6 +33,7 @@
 #include "rsn_supp/pmksa_cache.h"
 #include "common/wpa_ctrl.h"
 #include "common/ieee802_11_defs.h"
+#include "common/hw_features_common.h"
 #include "p2p/p2p.h"
 #include "blacklist.h"
 #include "wpas_glue.h"
@@ -52,12 +53,13 @@
 #include "hs20_supplicant.h"
 #include "wnm_sta.h"
 #include "wpas_kay.h"
+#include "mesh.h"
 
-const char *wpa_supplicant_version =
+const char *const wpa_supplicant_version =
 "wpa_supplicant v" VERSION_STR "\n"
-"Copyright (c) 2003-2014, Jouni Malinen <j@w1.fi> and contributors";
+"Copyright (c) 2003-2015, Jouni Malinen <j@w1.fi> and contributors";
 
-const char *wpa_supplicant_license =
+const char *const wpa_supplicant_license =
 "This software may be distributed under the terms of the BSD license.\n"
 "See README for more details.\n"
 #ifdef EAP_TLS_OPENSSL
@@ -68,16 +70,16 @@
 
 #ifndef CONFIG_NO_STDOUT_DEBUG
 /* Long text divided into parts in order to fit in C89 strings size limits. */
-const char *wpa_supplicant_full_license1 =
+const char *const wpa_supplicant_full_license1 =
 "";
-const char *wpa_supplicant_full_license2 =
+const char *const wpa_supplicant_full_license2 =
 "This software may be distributed under the terms of the BSD license.\n"
 "\n"
 "Redistribution and use in source and binary forms, with or without\n"
 "modification, are permitted provided that the following conditions are\n"
 "met:\n"
 "\n";
-const char *wpa_supplicant_full_license3 =
+const char *const wpa_supplicant_full_license3 =
 "1. Redistributions of source code must retain the above copyright\n"
 "   notice, this list of conditions and the following disclaimer.\n"
 "\n"
@@ -85,7 +87,7 @@
 "   notice, this list of conditions and the following disclaimer in the\n"
 "   documentation and/or other materials provided with the distribution.\n"
 "\n";
-const char *wpa_supplicant_full_license4 =
+const char *const wpa_supplicant_full_license4 =
 "3. Neither the name(s) of the above-listed copyright holder(s) nor the\n"
 "   names of its contributors may be used to endorse or promote products\n"
 "   derived from this software without specific prior written permission.\n"
@@ -94,7 +96,7 @@
 "\"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n"
 "LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n"
 "A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n";
-const char *wpa_supplicant_full_license5 =
+const char *const wpa_supplicant_full_license5 =
 "OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n"
 "SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n"
 "LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n"
@@ -105,9 +107,6 @@
 "\n";
 #endif /* CONFIG_NO_STDOUT_DEBUG */
 
-struct wowlan_triggers *wpa_get_wowlan_triggers(const char *wowlan_triggers,
-						struct wpa_driver_capa *capa);
-
 /* Configure default/group WEP keys for static WEP */
 int wpa_set_wep_keys(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid)
 {
@@ -134,6 +133,7 @@
 	size_t keylen;
 	enum wpa_alg alg;
 	u8 seq[6] = { 0 };
+	int ret;
 
 	/* IBSS/WPA-None uses only one key (Group) for both receiving and
 	 * sending unicast and multicast packets. */
@@ -177,7 +177,9 @@
 	/* TODO: should actually remember the previously used seq#, both for TX
 	 * and RX from each STA.. */
 
-	return wpa_drv_set_key(wpa_s, alg, NULL, 0, 1, seq, 6, key, keylen);
+	ret = wpa_drv_set_key(wpa_s, alg, NULL, 0, 1, seq, 6, key, keylen);
+	os_memset(key, 0, sizeof(key));
+	return ret;
 }
 
 
@@ -300,11 +302,28 @@
 		wpa_s->key_mgmt != WPA_KEY_MGMT_IEEE8021X_NO_WPA &&
 		wpa_s->key_mgmt != WPA_KEY_MGMT_WPS;
 	eapol_conf.external_sim = wpa_s->conf->external_sim;
-	eapol_conf.wps = wpa_s->key_mgmt == WPA_KEY_MGMT_WPS;
+
+#ifdef CONFIG_WPS
+	if (wpa_s->key_mgmt == WPA_KEY_MGMT_WPS) {
+		eapol_conf.wps |= EAPOL_LOCAL_WPS_IN_USE;
+		if (wpa_s->current_bss) {
+			struct wpabuf *ie;
+			ie = wpa_bss_get_vendor_ie_multi(wpa_s->current_bss,
+							 WPS_IE_VENDOR_TYPE);
+			if (ie) {
+				if (wps_is_20(ie))
+					eapol_conf.wps |=
+						EAPOL_PEER_IS_WPS20_AP;
+				wpabuf_free(ie);
+			}
+		}
+	}
+#endif /* CONFIG_WPS */
+
 	eapol_sm_notify_config(wpa_s->eapol, &ssid->eap, &eapol_conf);
-#endif /* IEEE8021X_EAPOL */
 
 	ieee802_1x_alloc_kay_sm(wpa_s, ssid);
+#endif /* IEEE8021X_EAPOL */
 }
 
 
@@ -393,6 +412,10 @@
 		l2_packet_deinit(wpa_s->l2_br);
 		wpa_s->l2_br = NULL;
 	}
+#ifdef CONFIG_TESTING_OPTIONS
+	l2_packet_deinit(wpa_s->l2_test);
+	wpa_s->l2_test = NULL;
+#endif /* CONFIG_TESTING_OPTIONS */
 
 	if (wpa_s->conf != NULL) {
 		struct wpa_ssid *ssid;
@@ -416,6 +439,7 @@
 	wpa_tdls_deinit(wpa_s->wpa);
 #endif /* CONFIG_TDLS */
 
+	wmm_ac_clear_saved_tspecs(wpa_s);
 	pmksa_candidate_free(wpa_s->wpa);
 	wpa_sm_deinit(wpa_s->wpa);
 	wpa_s->wpa = NULL;
@@ -432,6 +456,8 @@
 			     wpa_s, NULL);
 #endif /* CONFIG_DELAYED_MIC_ERROR_REPORT */
 
+	eloop_cancel_timeout(wpas_network_reenabled, wpa_s, NULL);
+
 	wpas_wps_deinit(wpa_s);
 
 	wpabuf_free(wpa_s->pending_eapol_rx);
@@ -465,6 +491,18 @@
 	os_free(wpa_s->manual_sched_scan_freqs);
 	wpa_s->manual_sched_scan_freqs = NULL;
 
+	wpas_mac_addr_rand_scan_clear(wpa_s, MAC_ADDR_RAND_ALL);
+
+	/*
+	 * Need to remove any pending gas-query radio work before the
+	 * gas_query_deinit() call because gas_query::work has not yet been set
+	 * for works that have not been started. gas_query_free() will be unable
+	 * to cancel such pending radio works and once the pending gas-query
+	 * radio work eventually gets removed, the deinit notification call to
+	 * gas_query_start_cb() would result in dereferencing freed memory.
+	 */
+	if (wpa_s->radio)
+		radio_remove_works(wpa_s, "gas-query", 0);
 	gas_query_deinit(wpa_s->gas);
 	wpa_s->gas = NULL;
 
@@ -504,6 +542,8 @@
 		wpabuf_free(wpa_s->vendor_elem[i]);
 		wpa_s->vendor_elem[i] = NULL;
 	}
+
+	wmm_ac_notify_disassoc(wpa_s);
 }
 
 
@@ -688,6 +728,30 @@
 		wpa_s->normal_scans = 0;
 	}
 
+#ifdef CONFIG_P2P
+	/*
+	 * P2PS client has to reply to Probe Request frames received on the
+	 * group operating channel. Enable Probe Request frame reporting for
+	 * P2P connected client in case p2p_cli_probe configuration property is
+	 * set to 1.
+	 */
+	if (wpa_s->conf->p2p_cli_probe && wpa_s->current_ssid &&
+	    wpa_s->current_ssid->mode == WPAS_MODE_INFRA &&
+	    wpa_s->current_ssid->p2p_group) {
+		if (state == WPA_COMPLETED && !wpa_s->p2p_cli_probe) {
+			wpa_dbg(wpa_s, MSG_DEBUG,
+				"P2P: Enable CLI Probe Request RX reporting");
+			wpa_s->p2p_cli_probe =
+				wpa_drv_probe_req_report(wpa_s, 1) >= 0;
+		} else if (state != WPA_COMPLETED && wpa_s->p2p_cli_probe) {
+			wpa_dbg(wpa_s, MSG_DEBUG,
+				"P2P: Disable CLI Probe Request RX reporting");
+			wpa_s->p2p_cli_probe = 0;
+			wpa_drv_probe_req_report(wpa_s, 0);
+		}
+	}
+#endif /* CONFIG_P2P */
+
 	if (state != WPA_SCANNING)
 		wpa_supplicant_notify_scanning(wpa_s, 0);
 
@@ -701,6 +765,7 @@
 			ssid && ssid->id_str ? ssid->id_str : "");
 #endif /* CONFIG_CTRL_IFACE || !CONFIG_NO_STDOUT_DEBUG */
 		wpas_clear_temp_disabled(wpa_s, ssid, 1);
+		wpa_blacklist_clear(wpa_s);
 		wpa_s->extra_blacklist_count = 0;
 		wpa_s->new_connection = 0;
 		wpa_drv_set_operstate(wpa_s, 1);
@@ -736,6 +801,9 @@
 	if (state == WPA_DISCONNECTED || state == WPA_INACTIVE)
 		wpa_supplicant_start_autoscan(wpa_s);
 
+	if (old_state >= WPA_ASSOCIATED && wpa_s->wpa_state < WPA_ASSOCIATED)
+		wmm_ac_notify_disassoc(wpa_s);
+
 	if (wpa_s->wpa_state != old_state) {
 		wpas_notify_state_changed(wpa_s, wpa_s->wpa_state, old_state);
 
@@ -839,13 +907,15 @@
 
 	eapol_sm_invalidate_cached_session(wpa_s->eapol);
 	if (wpa_s->current_ssid) {
+		if (wpa_s->wpa_state >= WPA_AUTHENTICATING)
+			wpa_s->own_disconnect_req = 1;
 		wpa_supplicant_deauthenticate(wpa_s,
 					      WLAN_REASON_DEAUTH_LEAVING);
 	}
 
 	/*
 	 * TODO: should notify EAPOL SM about changes in opensc_engine_path,
-	 * pkcs11_engine_path, pkcs11_module_path.
+	 * pkcs11_engine_path, pkcs11_module_path, openssl_ciphers.
 	 */
 	if (wpa_key_mgmt_wpa_psk(wpa_s->key_mgmt)) {
 		/*
@@ -931,9 +1001,7 @@
 
 #ifdef CONFIG_IEEE80211W
 	if (!(ie->capabilities & WPA_CAPABILITY_MFPC) &&
-	    (ssid->ieee80211w == MGMT_FRAME_PROTECTION_DEFAULT ?
-	     wpa_s->conf->pmf : ssid->ieee80211w) ==
-	    MGMT_FRAME_PROTECTION_REQUIRED) {
+	    wpas_get_ssid_pmf(wpa_s, ssid) == MGMT_FRAME_PROTECTION_REQUIRED) {
 		wpa_msg(wpa_s, MSG_INFO, "WPA: Driver associated with an AP "
 			"that does not support management frame protection - "
 			"reject");
@@ -982,7 +1050,7 @@
 		wpa_dbg(wpa_s, MSG_DEBUG, "RSN: using IEEE 802.11i/D9.0");
 		proto = WPA_PROTO_RSN;
 	} else if (bss_wpa && (ssid->proto & WPA_PROTO_WPA) &&
-		   wpa_parse_wpa_ie(bss_wpa, 2 +bss_wpa[1], &ie) == 0 &&
+		   wpa_parse_wpa_ie(bss_wpa, 2 + bss_wpa[1], &ie) == 0 &&
 		   (ie.group_cipher & ssid->group_cipher) &&
 		   (ie.pairwise_cipher & ssid->pairwise_cipher) &&
 		   (ie.key_mgmt & ssid->key_mgmt)) {
@@ -1000,6 +1068,40 @@
 #endif /* CONFIG_HS20 */
 	} else if (bss) {
 		wpa_msg(wpa_s, MSG_WARNING, "WPA: Failed to select WPA/RSN");
+		wpa_dbg(wpa_s, MSG_DEBUG,
+			"WPA: ssid proto=0x%x pairwise_cipher=0x%x group_cipher=0x%x key_mgmt=0x%x",
+			ssid->proto, ssid->pairwise_cipher, ssid->group_cipher,
+			ssid->key_mgmt);
+		wpa_dbg(wpa_s, MSG_DEBUG, "WPA: BSS " MACSTR " ssid='%s'%s%s%s",
+			MAC2STR(bss->bssid),
+			wpa_ssid_txt(bss->ssid, bss->ssid_len),
+			bss_wpa ? " WPA" : "",
+			bss_rsn ? " RSN" : "",
+			bss_osen ? " OSEN" : "");
+		if (bss_rsn) {
+			wpa_hexdump(MSG_DEBUG, "RSN", bss_rsn, 2 + bss_rsn[1]);
+			if (wpa_parse_wpa_ie(bss_rsn, 2 + bss_rsn[1], &ie)) {
+				wpa_dbg(wpa_s, MSG_DEBUG,
+					"Could not parse RSN element");
+			} else {
+				wpa_dbg(wpa_s, MSG_DEBUG,
+					"RSN: pairwise_cipher=0x%x group_cipher=0x%x key_mgmt=0x%x",
+					ie.pairwise_cipher, ie.group_cipher,
+					ie.key_mgmt);
+			}
+		}
+		if (bss_wpa) {
+			wpa_hexdump(MSG_DEBUG, "WPA", bss_wpa, 2 + bss_wpa[1]);
+			if (wpa_parse_wpa_ie(bss_wpa, 2 + bss_wpa[1], &ie)) {
+				wpa_dbg(wpa_s, MSG_DEBUG,
+					"Could not parse WPA element");
+			} else {
+				wpa_dbg(wpa_s, MSG_DEBUG,
+					"WPA: pairwise_cipher=0x%x group_cipher=0x%x key_mgmt=0x%x",
+					ie.pairwise_cipher, ie.group_cipher,
+					ie.key_mgmt);
+			}
+		}
 		return -1;
 	} else {
 		if (ssid->proto & WPA_PROTO_OSEN)
@@ -1073,6 +1175,18 @@
 		sel &= ~(WPA_KEY_MGMT_SAE | WPA_KEY_MGMT_FT_SAE);
 #endif /* CONFIG_SAE */
 	if (0) {
+#ifdef CONFIG_SUITEB192
+	} else if (sel & WPA_KEY_MGMT_IEEE8021X_SUITE_B_192) {
+		wpa_s->key_mgmt = WPA_KEY_MGMT_IEEE8021X_SUITE_B_192;
+		wpa_dbg(wpa_s, MSG_DEBUG,
+			"WPA: using KEY_MGMT 802.1X with Suite B (192-bit)");
+#endif /* CONFIG_SUITEB192 */
+#ifdef CONFIG_SUITEB
+	} else if (sel & WPA_KEY_MGMT_IEEE8021X_SUITE_B) {
+		wpa_s->key_mgmt = WPA_KEY_MGMT_IEEE8021X_SUITE_B;
+		wpa_dbg(wpa_s, MSG_DEBUG,
+			"WPA: using KEY_MGMT 802.1X with Suite B");
+#endif /* CONFIG_SUITEB */
 #ifdef CONFIG_IEEE80211R
 	} else if (sel & WPA_KEY_MGMT_FT_IEEE8021X) {
 		wpa_s->key_mgmt = WPA_KEY_MGMT_FT_IEEE8021X;
@@ -1126,8 +1240,7 @@
 
 #ifdef CONFIG_IEEE80211W
 	sel = ie.mgmt_group_cipher;
-	if ((ssid->ieee80211w == MGMT_FRAME_PROTECTION_DEFAULT ?
-	     wpa_s->conf->pmf : ssid->ieee80211w) == NO_MGMT_FRAME_PROTECTION ||
+	if (wpas_get_ssid_pmf(wpa_s, ssid) == NO_MGMT_FRAME_PROTECTION ||
 	    !(ie.capabilities & WPA_CAPABILITY_MFPC))
 		sel = 0;
 	if (sel & WPA_CIPHER_AES_128_CMAC) {
@@ -1153,8 +1266,7 @@
 	wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_MGMT_GROUP,
 			 wpa_s->mgmt_group_cipher);
 	wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_MFP,
-			 (ssid->ieee80211w == MGMT_FRAME_PROTECTION_DEFAULT ?
-			  wpa_s->conf->pmf : ssid->ieee80211w));
+			 wpas_get_ssid_pmf(wpa_s, ssid));
 #endif /* CONFIG_IEEE80211W */
 
 	if (wpa_sm_set_assoc_wpa_ie_default(wpa_s->wpa, wpa_ie, wpa_ie_len)) {
@@ -1163,7 +1275,12 @@
 	}
 
 	if (wpa_key_mgmt_wpa_psk(ssid->key_mgmt)) {
-		wpa_sm_set_pmk(wpa_s->wpa, ssid->psk, PMK_LEN);
+		int psk_set = 0;
+
+		if (ssid->psk_set) {
+			wpa_sm_set_pmk(wpa_s->wpa, ssid->psk, PMK_LEN, NULL);
+			psk_set = 1;
+		}
 #ifndef CONFIG_NO_PBKDF2
 		if (bss && ssid->bssid_set && ssid->ssid_len == 0 &&
 		    ssid->passphrase) {
@@ -1172,7 +1289,9 @@
 				    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);
+			wpa_sm_set_pmk(wpa_s->wpa, psk, PMK_LEN, NULL);
+			psk_set = 1;
+			os_memset(psk, 0, sizeof(psk));
 		}
 #endif /* CONFIG_NO_PBKDF2 */
 #ifdef CONFIG_EXT_PASSWORD
@@ -1208,7 +1327,9 @@
 				wpa_hexdump_key(MSG_MSGDUMP, "PSK (from "
 						"external passphrase)",
 						psk, PMK_LEN);
-				wpa_sm_set_pmk(wpa_s->wpa, psk, PMK_LEN);
+				wpa_sm_set_pmk(wpa_s->wpa, psk, PMK_LEN, NULL);
+				psk_set = 1;
+				os_memset(psk, 0, sizeof(psk));
 			} else
 #endif /* CONFIG_NO_PBKDF2 */
 			if (wpabuf_len(pw) == 2 * PMK_LEN) {
@@ -1219,7 +1340,9 @@
 					ext_password_free(pw);
 					return -1;
 				}
-				wpa_sm_set_pmk(wpa_s->wpa, psk, PMK_LEN);
+				wpa_sm_set_pmk(wpa_s->wpa, psk, PMK_LEN, NULL);
+				psk_set = 1;
+				os_memset(psk, 0, sizeof(psk));
 			} else {
 				wpa_msg(wpa_s, MSG_INFO, "EXT PW: No suitable "
 					"PSK available");
@@ -1232,6 +1355,12 @@
 			ext_password_free(pw);
 		}
 #endif /* CONFIG_EXT_PASSWORD */
+
+		if (!psk_set) {
+			wpa_msg(wpa_s, MSG_INFO,
+				"No PSK available for association");
+			return -1;
+		}
 	} else
 		wpa_sm_set_pmk_from_pmksa(wpa_s->wpa);
 
@@ -1461,8 +1590,15 @@
 	else
 		rand_style = ssid->mac_addr;
 
+	wmm_ac_clear_saved_tspecs(wpa_s);
+	wpa_s->reassoc_same_bss = 0;
+
 	if (wpa_s->last_ssid == ssid) {
 		wpa_dbg(wpa_s, MSG_DEBUG, "Re-association to the same ESS");
+		if (wpa_s->current_bss && wpa_s->current_bss == bss) {
+			wmm_ac_save_tspecs(wpa_s);
+			wpa_s->reassoc_same_bss = 1;
+		}
 	} else if (rand_style > 0) {
 		if (wpas_update_random_addr(wpa_s, rand_style) < 0)
 			return;
@@ -1510,6 +1646,31 @@
 		return;
 	}
 
+	if (ssid->mode == WPAS_MODE_MESH) {
+#ifdef CONFIG_MESH
+		if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_MESH)) {
+			wpa_msg(wpa_s, MSG_INFO,
+				"Driver does not support mesh mode");
+			return;
+		}
+		if (bss)
+			ssid->frequency = bss->freq;
+		if (wpa_supplicant_join_mesh(wpa_s, ssid) < 0) {
+			wpa_msg(wpa_s, MSG_ERROR, "Could not join mesh");
+			return;
+		}
+		wpa_s->current_bss = bss;
+		wpa_msg_ctrl(wpa_s, MSG_INFO, MESH_GROUP_STARTED
+			     "ssid=\"%s\" id=%d",
+			     wpa_ssid_txt(ssid->ssid, ssid->ssid_len),
+			     ssid->id);
+#else /* CONFIG_MESH */
+		wpa_msg(wpa_s, MSG_ERROR,
+			"mesh mode support not included in the build");
+#endif /* CONFIG_MESH */
+		return;
+	}
+
 #ifdef CONFIG_TDLS
 	if (bss)
 		wpa_tdls_ap_ies(wpa_s->wpa, (const u8 *) (bss + 1),
@@ -1546,6 +1707,227 @@
 }
 
 
+static int bss_is_ibss(struct wpa_bss *bss)
+{
+	return (bss->caps & (IEEE80211_CAP_ESS | IEEE80211_CAP_IBSS)) ==
+		IEEE80211_CAP_IBSS;
+}
+
+
+void ibss_mesh_setup_freq(struct wpa_supplicant *wpa_s,
+			  const struct wpa_ssid *ssid,
+			  struct hostapd_freq_params *freq)
+{
+	enum hostapd_hw_mode hw_mode;
+	struct hostapd_hw_modes *mode = NULL;
+	int ht40plus[] = { 36, 44, 52, 60, 100, 108, 116, 124, 132, 149, 157,
+			   184, 192 };
+	int vht80[] = { 36, 52, 100, 116, 132, 149 };
+	struct hostapd_channel_data *pri_chan = NULL, *sec_chan = NULL;
+	u8 channel;
+	int i, chan_idx, ht40 = -1, res, obss_scan = 1;
+	unsigned int j;
+	struct hostapd_freq_params vht_freq;
+
+	freq->freq = ssid->frequency;
+
+	for (j = 0; j < wpa_s->last_scan_res_used; j++) {
+		struct wpa_bss *bss = wpa_s->last_scan_res[j];
+
+		if (ssid->mode != WPAS_MODE_IBSS)
+			break;
+
+		/* Don't adjust control freq in case of fixed_freq */
+		if (ssid->fixed_freq)
+			break;
+
+		if (!bss_is_ibss(bss))
+			continue;
+
+		if (ssid->ssid_len == bss->ssid_len &&
+		    os_memcmp(ssid->ssid, bss->ssid, bss->ssid_len) == 0) {
+			wpa_printf(MSG_DEBUG,
+				   "IBSS already found in scan results, adjust control freq: %d",
+				   bss->freq);
+			freq->freq = bss->freq;
+			obss_scan = 0;
+			break;
+		}
+	}
+
+	/* For IBSS check HT_IBSS flag */
+	if (ssid->mode == WPAS_MODE_IBSS &&
+	    !(wpa_s->drv_flags & WPA_DRIVER_FLAGS_HT_IBSS))
+		return;
+
+	if (wpa_s->group_cipher == WPA_CIPHER_WEP40 ||
+	    wpa_s->group_cipher == WPA_CIPHER_WEP104 ||
+	    wpa_s->pairwise_cipher == WPA_CIPHER_TKIP) {
+		wpa_printf(MSG_DEBUG,
+			   "IBSS: WEP/TKIP detected, do not try to enable HT");
+		return;
+	}
+
+	hw_mode = ieee80211_freq_to_chan(freq->freq, &channel);
+	for (i = 0; wpa_s->hw.modes && i < wpa_s->hw.num_modes; i++) {
+		if (wpa_s->hw.modes[i].mode == hw_mode) {
+			mode = &wpa_s->hw.modes[i];
+			break;
+		}
+	}
+
+	if (!mode)
+		return;
+
+	freq->ht_enabled = ht_supported(mode);
+	if (!freq->ht_enabled)
+		return;
+
+	/* Setup higher BW only for 5 GHz */
+	if (mode->mode != HOSTAPD_MODE_IEEE80211A)
+		return;
+
+	for (chan_idx = 0; chan_idx < mode->num_channels; chan_idx++) {
+		pri_chan = &mode->channels[chan_idx];
+		if (pri_chan->chan == channel)
+			break;
+		pri_chan = NULL;
+	}
+	if (!pri_chan)
+		return;
+
+	/* Check primary channel flags */
+	if (pri_chan->flag & (HOSTAPD_CHAN_DISABLED | HOSTAPD_CHAN_NO_IR))
+		return;
+
+	/* Check/setup HT40+/HT40- */
+	for (j = 0; j < ARRAY_SIZE(ht40plus); j++) {
+		if (ht40plus[j] == channel) {
+			ht40 = 1;
+			break;
+		}
+	}
+
+	/* Find secondary channel */
+	for (i = 0; i < mode->num_channels; i++) {
+		sec_chan = &mode->channels[i];
+		if (sec_chan->chan == channel + ht40 * 4)
+			break;
+		sec_chan = NULL;
+	}
+	if (!sec_chan)
+		return;
+
+	/* Check secondary channel flags */
+	if (sec_chan->flag & (HOSTAPD_CHAN_DISABLED | HOSTAPD_CHAN_NO_IR))
+		return;
+
+	freq->channel = pri_chan->chan;
+
+	switch (ht40) {
+	case -1:
+		if (!(pri_chan->flag & HOSTAPD_CHAN_HT40MINUS))
+			return;
+		freq->sec_channel_offset = -1;
+		break;
+	case 1:
+		if (!(pri_chan->flag & HOSTAPD_CHAN_HT40PLUS))
+			return;
+		freq->sec_channel_offset = 1;
+		break;
+	default:
+		break;
+	}
+
+	if (freq->sec_channel_offset && obss_scan) {
+		struct wpa_scan_results *scan_res;
+
+		scan_res = wpa_supplicant_get_scan_results(wpa_s, NULL, 0);
+		if (scan_res == NULL) {
+			/* Back to HT20 */
+			freq->sec_channel_offset = 0;
+			return;
+		}
+
+		res = check_40mhz_5g(mode, scan_res, pri_chan->chan,
+				     sec_chan->chan);
+		switch (res) {
+		case 0:
+			/* Back to HT20 */
+			freq->sec_channel_offset = 0;
+			break;
+		case 1:
+			/* Configuration allowed */
+			break;
+		case 2:
+			/* Switch pri/sec channels */
+			freq->freq = hw_get_freq(mode, sec_chan->chan);
+			freq->sec_channel_offset = -freq->sec_channel_offset;
+			freq->channel = sec_chan->chan;
+			break;
+		default:
+			freq->sec_channel_offset = 0;
+			break;
+		}
+
+		wpa_scan_results_free(scan_res);
+	}
+
+	wpa_printf(MSG_DEBUG,
+		   "IBSS/mesh: setup freq channel %d, sec_channel_offset %d",
+		   freq->channel, freq->sec_channel_offset);
+
+	/* Not sure if mesh is ready for VHT */
+	if (ssid->mode != WPAS_MODE_IBSS)
+		return;
+
+	/* For IBSS check VHT_IBSS flag */
+	if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_VHT_IBSS))
+		return;
+
+	vht_freq = *freq;
+
+	vht_freq.vht_enabled = vht_supported(mode);
+	if (!vht_freq.vht_enabled)
+		return;
+
+	/* setup center_freq1, bandwidth */
+	for (j = 0; j < ARRAY_SIZE(vht80); j++) {
+		if (freq->channel >= vht80[j] &&
+		    freq->channel < vht80[j] + 16)
+			break;
+	}
+
+	if (j == ARRAY_SIZE(vht80))
+		return;
+
+	for (i = vht80[j]; i < vht80[j] + 16; i += 4) {
+		struct hostapd_channel_data *chan;
+
+		chan = hw_get_channel_chan(mode, i, NULL);
+		if (!chan)
+			return;
+
+		/* Back to HT configuration if channel not usable */
+		if (chan->flag & (HOSTAPD_CHAN_DISABLED | HOSTAPD_CHAN_NO_IR))
+			return;
+	}
+
+	if (hostapd_set_freq_params(&vht_freq, mode->mode, freq->freq,
+				    freq->channel, freq->ht_enabled,
+				    vht_freq.vht_enabled,
+				    freq->sec_channel_offset,
+				    VHT_CHANWIDTH_80MHZ,
+				    vht80[j] + 6, 0, 0) != 0)
+		return;
+
+	*freq = vht_freq;
+
+	wpa_printf(MSG_DEBUG, "IBSS: VHT setup freq cf1 %d, cf2 %d, bw %d",
+		   freq->center_freq1, freq->center_freq2, freq->bandwidth);
+}
+
+
 static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit)
 {
 	struct wpa_connect_work *cwork = work->ctx;
@@ -1593,7 +1975,8 @@
 	os_memset(&params, 0, sizeof(params));
 	wpa_s->reassociate = 0;
 	wpa_s->eap_expected_failure = 0;
-	if (bss && !wpas_driver_bss_selection(wpa_s)) {
+	if (bss &&
+	    (!wpas_driver_bss_selection(wpa_s) || wpas_wps_searching(wpa_s))) {
 #ifdef CONFIG_IEEE80211R
 		const u8 *ie, *md = NULL;
 #endif /* CONFIG_IEEE80211R */
@@ -1666,7 +2049,7 @@
 			(ssid->proto & WPA_PROTO_RSN);
 		if (pmksa_cache_set_current(wpa_s->wpa, NULL, bss->bssid,
 					    ssid, try_opportunistic) == 0)
-			eapol_sm_notify_pmkid_attempt(wpa_s->eapol, 1);
+			eapol_sm_notify_pmkid_attempt(wpa_s->eapol);
 		wpa_ie_len = sizeof(wpa_ie);
 		if (wpa_supplicant_set_suites(wpa_s, bss, ssid,
 					      wpa_ie, &wpa_ie_len)) {
@@ -1793,6 +2176,18 @@
 		}
 	}
 
+	if (wpa_s->vendor_elem[VENDOR_ELEM_ASSOC_REQ]) {
+		struct wpabuf *buf = wpa_s->vendor_elem[VENDOR_ELEM_ASSOC_REQ];
+		size_t len;
+
+		len = sizeof(wpa_ie) - wpa_ie_len;
+		if (wpabuf_len(buf) <= len) {
+			os_memcpy(wpa_ie + wpa_ie_len,
+				  wpabuf_head(buf), wpabuf_len(buf));
+			wpa_ie_len += wpabuf_len(buf);
+		}
+	}
+
 	wpa_clear_keys(wpa_s, bss ? bss->bssid : NULL);
 	use_crypt = 1;
 	cipher_pairwise = wpa_s->pairwise_cipher;
@@ -1856,26 +2251,13 @@
 		params.fixed_bssid = 1;
 	}
 
-	if (ssid->mode == WPAS_MODE_IBSS && ssid->frequency > 0 &&
-	    params.freq.freq == 0) {
-		enum hostapd_hw_mode hw_mode;
-		u8 channel;
-
-		params.freq.freq = ssid->frequency;
-
-		hw_mode = ieee80211_freq_to_chan(ssid->frequency, &channel);
-		for (i = 0; wpa_s->hw.modes && i < wpa_s->hw.num_modes; i++) {
-			if (wpa_s->hw.modes[i].mode == hw_mode) {
-				struct hostapd_hw_modes *mode;
-
-				mode = &wpa_s->hw.modes[i];
-				params.freq.ht_enabled = ht_supported(mode);
-				break;
-			}
-		}
-	}
+	/* Initial frequency for IBSS/mesh */
+	if ((ssid->mode == WPAS_MODE_IBSS || ssid->mode == WPAS_MODE_MESH) &&
+	    ssid->frequency > 0 && params.freq.freq == 0)
+		ibss_mesh_setup_freq(wpa_s, ssid, &params.freq);
 
 	if (ssid->mode == WPAS_MODE_IBSS) {
+		params.fixed_freq = ssid->fixed_freq;
 		if (ssid->beacon_int)
 			params.beacon_int = ssid->beacon_int;
 		else
@@ -1906,12 +2288,28 @@
 			params.psk = ssid->psk;
 	}
 
+	if (wpa_s->conf->key_mgmt_offload) {
+		if (params.key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X ||
+		    params.key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X_SHA256 ||
+		    params.key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X_SUITE_B ||
+		    params.key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X_SUITE_B_192)
+			params.req_key_mgmt_offload =
+				ssid->proactive_key_caching < 0 ?
+				wpa_s->conf->okc : ssid->proactive_key_caching;
+		else
+			params.req_key_mgmt_offload = 1;
+
+		if ((params.key_mgmt_suite == WPA_KEY_MGMT_PSK ||
+		     params.key_mgmt_suite == WPA_KEY_MGMT_PSK_SHA256 ||
+		     params.key_mgmt_suite == WPA_KEY_MGMT_FT_PSK) &&
+		    ssid->psk_set)
+			params.psk = ssid->psk;
+	}
+
 	params.drop_unencrypted = use_crypt;
 
 #ifdef CONFIG_IEEE80211W
-	params.mgmt_frame_protection =
-		ssid->ieee80211w == MGMT_FRAME_PROTECTION_DEFAULT ?
-		wpa_s->conf->pmf : ssid->ieee80211w;
+	params.mgmt_frame_protection = wpas_get_ssid_pmf(wpa_s, ssid);
 	if (params.mgmt_frame_protection != NO_MGMT_FRAME_PROTECTION && bss) {
 		const u8 *rsn = wpa_bss_get_ie(bss, WLAN_EID_RSN);
 		struct wpa_ie_data ie;
@@ -1945,7 +2343,7 @@
 	os_memset(&vhtcaps_mask, 0, sizeof(vhtcaps_mask));
 	params.vhtcaps = &vhtcaps;
 	params.vhtcaps_mask = &vhtcaps_mask;
-	wpa_supplicant_apply_vht_overrides(wpa_s, wpa_s->current_ssid, &params);
+	wpa_supplicant_apply_vht_overrides(wpa_s, ssid, &params);
 #endif /* CONFIG_VHT_OVERRIDES */
 
 #ifdef CONFIG_P2P
@@ -2037,7 +2435,8 @@
 	}
 	old_ssid = wpa_s->current_ssid;
 	wpa_s->current_ssid = ssid;
-	wpa_s->current_bss = bss;
+	if (!wpas_driver_bss_selection(wpa_s) || ssid->bssid_set)
+		wpa_s->current_bss = bss;
 	wpa_supplicant_rsn_supp_set_config(wpa_s, wpa_s->current_ssid);
 	wpa_supplicant_initiate_eapol(wpa_s);
 	if (old_ssid != wpa_s->current_ssid)
@@ -2050,6 +2449,7 @@
 {
 	struct wpa_ssid *old_ssid;
 
+	wpas_connect_work_done(wpa_s);
 	wpa_clear_keys(wpa_s, addr);
 	old_ssid = wpa_s->current_ssid;
 	wpa_supplicant_mark_disassoc(wpa_s);
@@ -2102,6 +2502,14 @@
 	wpa_tdls_teardown_peers(wpa_s->wpa);
 #endif /* CONFIG_TDLS */
 
+#ifdef CONFIG_MESH
+	if (wpa_s->ifmsh) {
+		wpa_msg_ctrl(wpa_s, MSG_INFO, MESH_GROUP_REMOVED "%s",
+			     wpa_s->ifname);
+		wpa_supplicant_leave_mesh(wpa_s);
+	}
+#endif /* CONFIG_MESH */
+
 	if (addr) {
 		wpa_drv_deauthenticate(wpa_s, addr, reason_code);
 		os_memset(&event, 0, sizeof(event));
@@ -2231,6 +2639,8 @@
 	int disconnected = 0;
 
 	if (ssid && ssid != wpa_s->current_ssid && wpa_s->current_ssid) {
+		if (wpa_s->wpa_state >= WPA_AUTHENTICATING)
+			wpa_s->own_disconnect_req = 1;
 		wpa_supplicant_deauthenticate(
 			wpa_s, WLAN_REASON_DEAUTH_LEAVING);
 		disconnected = 1;
@@ -2267,12 +2677,24 @@
 	if (ssid) {
 		wpa_s->current_ssid = ssid;
 		eapol_sm_notify_config(wpa_s->eapol, NULL, NULL);
+		wpa_s->connect_without_scan =
+			(ssid->mode == WPAS_MODE_MESH) ? ssid : NULL;
+
+		/*
+		 * Don't optimize next scan freqs since a new ESS has been
+		 * selected.
+		 */
+		os_free(wpa_s->next_scan_freqs);
+		wpa_s->next_scan_freqs = NULL;
+	} else {
+		wpa_s->connect_without_scan = NULL;
 	}
-	wpa_s->connect_without_scan = NULL;
+
 	wpa_s->disconnected = 0;
 	wpa_s->reassociate = 1;
 
-	if (wpa_supplicant_fast_associate(wpa_s) != 1)
+	if (wpa_s->connect_without_scan ||
+	    wpa_supplicant_fast_associate(wpa_s) != 1)
 		wpa_supplicant_req_scan(wpa_s, 0, disconnected ? 100000 : 0);
 
 	if (ssid)
@@ -2487,7 +2909,7 @@
 struct wpa_ssid * wpa_supplicant_get_ssid(struct wpa_supplicant *wpa_s)
 {
 	struct wpa_ssid *entry;
-	u8 ssid[MAX_SSID_LEN];
+	u8 ssid[SSID_MAX_LEN];
 	int res;
 	size_t ssid_len;
 	u8 bssid[ETH_ALEN];
@@ -2692,12 +3114,36 @@
 	    (wpa_s->current_ssid == NULL ||
 	     wpa_s->current_ssid->mode != IEEE80211_MODE_IBSS)) {
 		/* Timeout for completing IEEE 802.1X and WPA authentication */
-		wpa_supplicant_req_auth_timeout(
-			wpa_s,
-			(wpa_key_mgmt_wpa_ieee8021x(wpa_s->key_mgmt) ||
-			 wpa_s->key_mgmt == WPA_KEY_MGMT_IEEE8021X_NO_WPA ||
-			 wpa_s->key_mgmt == WPA_KEY_MGMT_WPS) ?
-			70 : 10, 0);
+		int timeout = 10;
+
+		if (wpa_key_mgmt_wpa_ieee8021x(wpa_s->key_mgmt) ||
+		    wpa_s->key_mgmt == WPA_KEY_MGMT_IEEE8021X_NO_WPA ||
+		    wpa_s->key_mgmt == WPA_KEY_MGMT_WPS) {
+			/* Use longer timeout for IEEE 802.1X/EAP */
+			timeout = 70;
+		}
+
+#ifdef CONFIG_WPS
+		if (wpa_s->current_ssid && wpa_s->current_bss &&
+		    (wpa_s->current_ssid->key_mgmt & WPA_KEY_MGMT_WPS) &&
+		    eap_is_wps_pin_enrollee(&wpa_s->current_ssid->eap)) {
+			/*
+			 * Use shorter timeout if going through WPS AP iteration
+			 * for PIN config method with an AP that does not
+			 * advertise Selected Registrar.
+			 */
+			struct wpabuf *wps_ie;
+
+			wps_ie = wpa_bss_get_vendor_ie_multi(
+				wpa_s->current_bss, WPS_IE_VENDOR_TYPE);
+			if (wps_ie &&
+			    !wps_is_addr_authorized(wps_ie, wpa_s->own_addr, 1))
+				timeout = 10;
+			wpabuf_free(wps_ie);
+		}
+#endif /* CONFIG_WPS */
+
+		wpa_supplicant_req_auth_timeout(wpa_s, timeout, 0);
 	}
 	wpa_s->eapol_received++;
 
@@ -2742,15 +3188,9 @@
 
 int wpa_supplicant_update_mac_addr(struct wpa_supplicant *wpa_s)
 {
-	if (wpa_s->driver->send_eapol) {
-		const u8 *addr = wpa_drv_get_mac_addr(wpa_s);
-		if (addr)
-			os_memcpy(wpa_s->own_addr, addr, ETH_ALEN);
-	} else if ((!wpa_s->p2p_mgmt ||
-		    !(wpa_s->drv_flags &
-		      WPA_DRIVER_FLAGS_DEDICATED_P2P_DEVICE)) &&
-		   !(wpa_s->drv_flags &
-		     WPA_DRIVER_FLAGS_P2P_DEDICATED_INTERFACE)) {
+	if ((!wpa_s->p2p_mgmt ||
+	     !(wpa_s->drv_flags & WPA_DRIVER_FLAGS_DEDICATED_P2P_DEVICE)) &&
+	    !(wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_DEDICATED_INTERFACE)) {
 		l2_packet_deinit(wpa_s->l2);
 		wpa_s->l2 = l2_packet_init(wpa_s->ifname,
 					   wpa_drv_get_mac_addr(wpa_s),
@@ -2824,11 +3264,9 @@
 	if (wpa_s->bridge_ifname[0]) {
 		wpa_dbg(wpa_s, MSG_DEBUG, "Receiving packets from bridge "
 			"interface '%s'", wpa_s->bridge_ifname);
-		wpa_s->l2_br = l2_packet_init(wpa_s->bridge_ifname,
-					      wpa_s->own_addr,
-					      ETH_P_EAPOL,
-					      wpa_supplicant_rx_eapol_bridge,
-					      wpa_s, 1);
+		wpa_s->l2_br = l2_packet_init_bridge(
+			wpa_s->bridge_ifname, wpa_s->ifname, wpa_s->own_addr,
+			ETH_P_EAPOL, wpa_supplicant_rx_eapol_bridge, wpa_s, 1);
 		if (wpa_s->l2_br == NULL) {
 			wpa_msg(wpa_s, MSG_ERROR, "Failed to open l2_packet "
 				"connection for the bridge interface '%s'",
@@ -2854,12 +3292,14 @@
 			wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED);
 			interface_count = 0;
 		}
+#ifndef ANDROID
 		if (!wpa_s->p2p_mgmt &&
 		    wpa_supplicant_delayed_sched_scan(wpa_s,
 						      interface_count % 3,
 						      100000))
 			wpa_supplicant_req_scan(wpa_s, interface_count % 3,
 						100000);
+#endif /* ANDROID */
 		interface_count++;
 	} else
 		wpa_supplicant_set_state(wpa_s, WPA_INACTIVE);
@@ -2875,7 +3315,8 @@
 }
 
 
-static struct wpa_supplicant * wpa_supplicant_alloc(void)
+static struct wpa_supplicant *
+wpa_supplicant_alloc(struct wpa_supplicant *parent)
 {
 	struct wpa_supplicant *wpa_s;
 
@@ -2885,7 +3326,7 @@
 	wpa_s->scan_req = INITIAL_SCAN_REQ;
 	wpa_s->scan_interval = 5;
 	wpa_s->new_connection = 1;
-	wpa_s->parent = wpa_s;
+	wpa_s->parent = parent ? parent : wpa_s;
 	wpa_s->sched_scanning = 0;
 
 	return wpa_s;
@@ -3127,10 +3568,6 @@
 {
 	struct ieee80211_vht_capabilities *vhtcaps;
 	struct ieee80211_vht_capabilities *vhtcaps_mask;
-#ifdef CONFIG_HT_OVERRIDES
-	int max_ampdu;
-	const u32 max_ampdu_mask = VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MAX;
-#endif /* CONFIG_HT_OVERRIDES */
 
 	if (!ssid)
 		return;
@@ -3148,9 +3585,12 @@
 
 #ifdef CONFIG_HT_OVERRIDES
 	/* if max ampdu is <= 3, we have to make the HT cap the same */
-	if (ssid->vht_capa_mask & max_ampdu_mask) {
-		max_ampdu = (ssid->vht_capa & max_ampdu_mask) >>
-			find_first_bit(max_ampdu_mask);
+	if (ssid->vht_capa_mask & VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MAX) {
+		int max_ampdu;
+
+		max_ampdu = (ssid->vht_capa &
+			     VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MAX) >>
+			VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MAX_SHIFT;
 
 		max_ampdu = max_ampdu < 3 ? max_ampdu : 3;
 		wpa_set_ampdu_factor(wpa_s,
@@ -3261,7 +3701,7 @@
 
 
 static int wpas_set_wowlan_triggers(struct wpa_supplicant *wpa_s,
-				    struct wpa_driver_capa *capa)
+				    const struct wpa_driver_capa *capa)
 {
 	struct wowlan_triggers *triggers;
 	int ret = 0;
@@ -3351,7 +3791,7 @@
 
 	wpa_s = dl_list_first(&radio->ifaces, struct wpa_supplicant,
 			      radio_list);
-	if (wpa_s && wpa_s->external_scan_running) {
+	if (wpa_s && wpa_s->radio->external_scan_running) {
 		wpa_printf(MSG_DEBUG, "Delay radio work start until externally triggered scan completes");
 		return;
 	}
@@ -3430,6 +3870,11 @@
 
 	if (dl_list_empty(&radio->work))
 		return;
+	if (wpa_s->ext_work_in_progress) {
+		wpa_printf(MSG_DEBUG,
+			   "External radio work in progress - delay start of pending item");
+		return;
+	}
 	eloop_cancel_timeout(radio_start_next_work, radio, NULL);
 	eloop_register_timeout(0, 0, radio_start_next_work, radio, NULL);
 }
@@ -3585,6 +4030,7 @@
 				     struct wpa_interface *iface)
 {
 	struct wpa_driver_capa capa;
+	int capa_res;
 
 	wpa_printf(MSG_DEBUG, "Initializing interface '%s' conf '%s' driver "
 		   "'%s' ctrl_interface '%s' bridge '%s'", iface->ifname,
@@ -3713,11 +4159,31 @@
 	wpa_s->hw.modes = wpa_drv_get_hw_feature_data(wpa_s,
 						      &wpa_s->hw.num_modes,
 						      &wpa_s->hw.flags);
+	if (wpa_s->hw.modes) {
+		u16 i;
 
-	if (wpa_drv_get_capa(wpa_s, &capa) == 0) {
+		for (i = 0; i < wpa_s->hw.num_modes; i++) {
+			if (wpa_s->hw.modes[i].vht_capab) {
+				wpa_s->hw_capab = CAPAB_VHT;
+				break;
+			}
+
+			if (wpa_s->hw.modes[i].ht_capab &
+			    HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET)
+				wpa_s->hw_capab = CAPAB_HT40;
+			else if (wpa_s->hw.modes[i].ht_capab &&
+				 wpa_s->hw_capab == CAPAB_NO_HT_VHT)
+				wpa_s->hw_capab = CAPAB_HT;
+		}
+	}
+
+	capa_res = wpa_drv_get_capa(wpa_s, &capa);
+	if (capa_res == 0) {
 		wpa_s->drv_capa_known = 1;
 		wpa_s->drv_flags = capa.flags;
 		wpa_s->drv_enc = capa.enc;
+		wpa_s->drv_smps_modes = capa.smps_modes;
+		wpa_s->drv_rrm_flags = capa.rrm_flags;
 		wpa_s->probe_resp_offloads = capa.probe_resp_offloads;
 		wpa_s->max_scan_ssids = capa.max_scan_ssids;
 		wpa_s->max_sched_scan_ssids = capa.max_sched_scan_ssids;
@@ -3730,6 +4196,14 @@
 		wpa_s->extended_capa_len = capa.extended_capa_len;
 		wpa_s->num_multichan_concurrent =
 			capa.num_multichan_concurrent;
+		wpa_s->wmm_ac_supported = capa.wmm_ac_supported;
+
+		if (capa.mac_addr_rand_scan_supported)
+			wpa_s->mac_addr_rand_supported |= MAC_ADDR_RAND_SCAN;
+		if (wpa_s->sched_scan_supported &&
+		    capa.mac_addr_rand_sched_scan_supported)
+			wpa_s->mac_addr_rand_supported |=
+				(MAC_ADDR_RAND_SCHED_SCAN | MAC_ADDR_RAND_PNO);
 	}
 	if (wpa_s->max_remain_on_chan == 0)
 		wpa_s->max_remain_on_chan = 1000;
@@ -3804,7 +4278,7 @@
 	 * Note: We don't restore/remove the triggers on shutdown (it doesn't
 	 * have effect anyway when the interface is down).
 	 */
-	if (wpas_set_wowlan_triggers(wpa_s, &capa) < 0)
+	if (capa_res == 0 && wpas_set_wowlan_triggers(wpa_s, &capa) < 0)
 		return -1;
 
 #ifdef CONFIG_EAP_PROXY
@@ -3828,6 +4302,8 @@
 	if (wpas_init_ext_pw(wpa_s) < 0)
 		return -1;
 
+	wpas_rrm_reset(wpa_s);
+
 	return 0;
 }
 
@@ -3835,6 +4311,26 @@
 static void wpa_supplicant_deinit_iface(struct wpa_supplicant *wpa_s,
 					int notify, int terminate)
 {
+	struct wpa_global *global = wpa_s->global;
+	struct wpa_supplicant *iface, *prev;
+
+	if (wpa_s == wpa_s->parent)
+		wpas_p2p_group_remove(wpa_s, "*");
+
+	iface = global->ifaces;
+	while (iface) {
+		if (iface == wpa_s || iface->parent != wpa_s) {
+			iface = iface->next;
+			continue;
+		}
+		wpa_printf(MSG_DEBUG,
+			   "Remove remaining child interface %s from parent %s",
+			   iface->ifname, wpa_s->ifname);
+		prev = iface;
+		iface = iface->next;
+		wpa_supplicant_remove_iface(global, prev, terminate);
+	}
+
 	wpa_s->disconnected = 1;
 	if (wpa_s->drv_priv) {
 		wpa_supplicant_deauthenticate(wpa_s,
@@ -3864,11 +4360,20 @@
 		wpa_s->ctrl_iface = NULL;
 	}
 
+#ifdef CONFIG_MESH
+	if (wpa_s->ifmsh) {
+		wpa_supplicant_mesh_iface_deinit(wpa_s, wpa_s->ifmsh);
+		wpa_s->ifmsh = NULL;
+	}
+#endif /* CONFIG_MESH */
+
 	if (wpa_s->conf != NULL) {
 		wpa_config_free(wpa_s->conf);
 		wpa_s->conf = NULL;
 	}
 
+	os_free(wpa_s->ssids_from_scan_req);
+
 	os_free(wpa_s);
 }
 
@@ -3877,6 +4382,7 @@
  * wpa_supplicant_add_iface - Add a new network interface
  * @global: Pointer to global data from wpa_supplicant_init()
  * @iface: Interface configuration options
+ * @parent: Parent interface or %NULL to assign new interface as parent
  * Returns: Pointer to the created interface or %NULL on failure
  *
  * This function is used to add new network interfaces for %wpa_supplicant.
@@ -3886,7 +4392,8 @@
  * e.g., when a hotplug network adapter is inserted.
  */
 struct wpa_supplicant * wpa_supplicant_add_iface(struct wpa_global *global,
-						 struct wpa_interface *iface)
+						 struct wpa_interface *iface,
+						 struct wpa_supplicant *parent)
 {
 	struct wpa_supplicant *wpa_s;
 	struct wpa_interface t_iface;
@@ -3895,7 +4402,7 @@
 	if (global == NULL || iface == NULL)
 		return NULL;
 
-	wpa_s = wpa_supplicant_alloc();
+	wpa_s = wpa_supplicant_alloc(parent);
 	if (wpa_s == NULL)
 		return NULL;
 
@@ -3923,14 +4430,16 @@
 		return NULL;
 	}
 
-	/* Notify the control interfaces about new iface */
-	if (wpas_notify_iface_added(wpa_s)) {
-		wpa_supplicant_deinit_iface(wpa_s, 1, 0);
-		return NULL;
-	}
+	if (iface->p2p_mgmt == 0) {
+		/* Notify the control interfaces about new iface */
+		if (wpas_notify_iface_added(wpa_s)) {
+			wpa_supplicant_deinit_iface(wpa_s, 1, 0);
+			return NULL;
+		}
 
-	for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next)
-		wpas_notify_network_added(wpa_s, ssid);
+		for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next)
+			wpas_notify_network_added(wpa_s, ssid);
+	}
 
 	wpa_s->next = global->ifaces;
 	global->ifaces = wpa_s;
@@ -3938,6 +4447,18 @@
 	wpa_dbg(wpa_s, MSG_DEBUG, "Added interface %s", wpa_s->ifname);
 	wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED);
 
+#ifdef CONFIG_P2P
+	if (wpa_s->global->p2p == NULL &&
+	    !wpa_s->global->p2p_disabled && !wpa_s->conf->p2p_disabled &&
+	    (wpa_s->drv_flags & WPA_DRIVER_FLAGS_DEDICATED_P2P_DEVICE) &&
+	    wpas_p2p_add_p2pdev_interface(
+		    wpa_s, wpa_s->global->params.conf_p2p_dev) < 0) {
+		wpa_printf(MSG_INFO,
+			   "P2P: Failed to enable P2P Device interface");
+		/* Try to continue without. P2P will be disabled. */
+	}
+#endif /* CONFIG_P2P */
+
 	return wpa_s;
 }
 
@@ -3958,6 +4479,10 @@
 				int terminate)
 {
 	struct wpa_supplicant *prev;
+#ifdef CONFIG_MESH
+	unsigned int mesh_if_created = wpa_s->mesh_if_created;
+	char *ifname = NULL;
+#endif /* CONFIG_MESH */
 
 	/* Remove interface from the global list of interfaces */
 	prev = global->ifaces;
@@ -3973,12 +4498,30 @@
 
 	wpa_dbg(wpa_s, MSG_DEBUG, "Removing interface %s", wpa_s->ifname);
 
+#ifdef CONFIG_MESH
+	if (mesh_if_created) {
+		ifname = os_strdup(wpa_s->ifname);
+		if (ifname == NULL) {
+			wpa_dbg(wpa_s, MSG_ERROR,
+				"mesh: Failed to malloc ifname");
+			return -1;
+		}
+	}
+#endif /* CONFIG_MESH */
+
 	if (global->p2p_group_formation == wpa_s)
 		global->p2p_group_formation = NULL;
 	if (global->p2p_invite_group == wpa_s)
 		global->p2p_invite_group = NULL;
 	wpa_supplicant_deinit_iface(wpa_s, 1, terminate);
 
+#ifdef CONFIG_MESH
+	if (mesh_if_created) {
+		wpa_drv_if_remove(global->ifaces, WPA_IF_MESH, ifname);
+		os_free(ifname);
+	}
+#endif /* CONFIG_MESH */
+
 	return 0;
 }
 
@@ -4063,7 +4606,10 @@
 	wpa_msg_register_ifname_cb(wpa_supplicant_msg_ifname_cb);
 #endif /* CONFIG_NO_WPA_MSG */
 
-	wpa_debug_open_file(params->wpa_debug_file_path);
+	if (params->wpa_debug_file_path)
+		wpa_debug_open_file(params->wpa_debug_file_path);
+	else
+		wpa_debug_setup_stdout();
 	if (params->wpa_debug_syslog)
 		wpa_debug_open_syslog();
 	if (params->wpa_debug_tracing) {
@@ -4106,6 +4652,9 @@
 	if (params->override_ctrl_interface)
 		global->params.override_ctrl_interface =
 			os_strdup(params->override_ctrl_interface);
+	if (params->conf_p2p_dev)
+		global->params.conf_p2p_dev =
+			os_strdup(params->conf_p2p_dev);
 	wpa_debug_level = global->params.wpa_debug_level =
 		params->wpa_debug_level;
 	wpa_debug_show_keys = global->params.wpa_debug_show_keys =
@@ -4141,7 +4690,7 @@
 		wpa_supplicant_deinit(global);
 		return NULL;
 	}
-	global->drv_priv = os_zalloc(global->drv_count * sizeof(void *));
+	global->drv_priv = os_calloc(global->drv_count, sizeof(void *));
 	if (global->drv_priv == NULL) {
 		wpa_supplicant_deinit(global);
 		return NULL;
@@ -4242,6 +4791,7 @@
 	os_free(global->params.ctrl_interface_group);
 	os_free(global->params.override_driver);
 	os_free(global->params.override_ctrl_interface);
+	os_free(global->params.conf_p2p_dev);
 
 	os_free(global->p2p_disallow_freq.range);
 	os_free(global->p2p_go_avoid_freq.range);
@@ -4279,7 +4829,7 @@
 }
 
 
-static void add_freq(int *freqs, int *num_freqs, int freq)
+void add_freq(int *freqs, int *num_freqs, int freq)
 {
 	int i;
 
@@ -4300,7 +4850,7 @@
 	int *freqs;
 	int num_freqs = 0;
 
-	freqs = os_zalloc(sizeof(int) * (max_freqs + 1));
+	freqs = os_calloc(max_freqs + 1, sizeof(int));
 	if (freqs == NULL)
 		return NULL;
 
@@ -4340,11 +4890,17 @@
 	 */
 	eloop_cancel_timeout(wpa_supplicant_timeout, wpa_s, NULL);
 
+	/*
+	 * There is no point in blacklisting the AP if this event is
+	 * generated based on local request to disconnect.
+	 */
+	if (wpa_s->own_disconnect_req) {
+		wpa_s->own_disconnect_req = 0;
+		wpa_dbg(wpa_s, MSG_DEBUG,
+			"Ignore connection failure due to local request to disconnect");
+		return;
+	}
 	if (wpa_s->disconnected) {
-		/*
-		 * There is no point in blacklisting the AP if this event is
-		 * generated based on local request to disconnect.
-		 */
 		wpa_dbg(wpa_s, MSG_DEBUG, "Ignore connection failure "
 			"indication since interface has been put into "
 			"disconnected state");
@@ -4495,6 +5051,15 @@
 		str_clear_free(eap->external_sim_resp);
 		eap->external_sim_resp = os_strdup(value);
 		break;
+	case WPA_CTRL_REQ_PSK_PASSPHRASE:
+		if (wpa_config_set(ssid, "psk", value, 0) < 0)
+			return -1;
+		ssid->mem_only_psk = 1;
+		if (ssid->passphrase)
+			wpa_config_update_psk(ssid);
+		if (wpa_s->wpa_state == WPA_SCANNING && !wpa_s->scanning)
+			wpa_supplicant_req_scan(wpa_s, 0, 0);
+		break;
 	default:
 		wpa_printf(MSG_DEBUG, "CTRL_IFACE: Unknown field '%s'", field);
 		return -1;
@@ -4514,13 +5079,16 @@
 	int i;
 	unsigned int drv_enc;
 
+	if (wpa_s->p2p_mgmt)
+		return 1; /* no normal network profiles on p2p_mgmt interface */
+
 	if (ssid == NULL)
 		return 1;
 
 	if (ssid->disabled)
 		return 1;
 
-	if (wpa_s && wpa_s->drv_capa_known)
+	if (wpa_s->drv_capa_known)
 		drv_enc = wpa_s->drv_enc;
 	else
 		drv_enc = (unsigned int) -1;
@@ -4539,13 +5107,38 @@
 	}
 
 	if (wpa_key_mgmt_wpa_psk(ssid->key_mgmt) && !ssid->psk_set &&
-	    (!ssid->passphrase || ssid->ssid_len != 0) && !ssid->ext_psk)
+	    (!ssid->passphrase || ssid->ssid_len != 0) && !ssid->ext_psk &&
+	    !ssid->mem_only_psk)
 		return 1;
 
 	return 0;
 }
 
 
+int wpas_get_ssid_pmf(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid)
+{
+#ifdef CONFIG_IEEE80211W
+	if (ssid == NULL || ssid->ieee80211w == MGMT_FRAME_PROTECTION_DEFAULT) {
+		if (wpa_s->conf->pmf == MGMT_FRAME_PROTECTION_OPTIONAL &&
+		    !(wpa_s->drv_enc & WPA_DRIVER_CAPA_ENC_BIP)) {
+			/*
+			 * Driver does not support BIP -- ignore pmf=1 default
+			 * since the connection with PMF would fail and the
+			 * configuration does not require PMF to be enabled.
+			 */
+			return NO_MGMT_FRAME_PROTECTION;
+		}
+
+		return wpa_s->conf->pmf;
+	}
+
+	return ssid->ieee80211w;
+#else /* CONFIG_IEEE80211W */
+	return NO_MGMT_FRAME_PROTECTION;
+#endif /* CONFIG_IEEE80211W */
+}
+
+
 int wpas_is_p2p_prioritized(struct wpa_supplicant *wpa_s)
 {
 	if (wpa_s->global->conc_pref == WPA_CONC_PREF_P2P)
@@ -4681,6 +5274,7 @@
 void wpas_request_connection(struct wpa_supplicant *wpa_s)
 {
 	wpa_s->normal_scans = 0;
+	wpa_s->scan_req = NORMAL_SCAN_REQ;
 	wpa_supplicant_reinit_autoscan(wpa_s);
 	wpa_s->extra_blacklist_count = 0;
 	wpa_s->disconnected = 0;
@@ -4688,6 +5282,8 @@
 
 	if (wpa_supplicant_fast_associate(wpa_s) != 1)
 		wpa_supplicant_req_scan(wpa_s, 0, 0);
+	else
+		wpa_s->reattach = 0;
 }
 
 
@@ -4785,3 +5381,268 @@
 
 	return num;
 }
+
+
+static void wpas_rrm_neighbor_rep_timeout_handler(void *data, void *user_ctx)
+{
+	struct rrm_data *rrm = data;
+
+	if (!rrm->notify_neighbor_rep) {
+		wpa_printf(MSG_ERROR,
+			   "RRM: Unexpected neighbor report timeout");
+		return;
+	}
+
+	wpa_printf(MSG_DEBUG, "RRM: Notifying neighbor report - NONE");
+	rrm->notify_neighbor_rep(rrm->neighbor_rep_cb_ctx, NULL);
+
+	rrm->notify_neighbor_rep = NULL;
+	rrm->neighbor_rep_cb_ctx = NULL;
+}
+
+
+/*
+ * wpas_rrm_reset - Clear and reset all RRM data in wpa_supplicant
+ * @wpa_s: Pointer to wpa_supplicant
+ */
+void wpas_rrm_reset(struct wpa_supplicant *wpa_s)
+{
+	wpa_s->rrm.rrm_used = 0;
+
+	eloop_cancel_timeout(wpas_rrm_neighbor_rep_timeout_handler, &wpa_s->rrm,
+			     NULL);
+	if (wpa_s->rrm.notify_neighbor_rep)
+		wpas_rrm_neighbor_rep_timeout_handler(&wpa_s->rrm, NULL);
+	wpa_s->rrm.next_neighbor_rep_token = 1;
+}
+
+
+/*
+ * wpas_rrm_process_neighbor_rep - Handle incoming neighbor report
+ * @wpa_s: Pointer to wpa_supplicant
+ * @report: Neighbor report buffer, prefixed by a 1-byte dialog token
+ * @report_len: Length of neighbor report buffer
+ */
+void wpas_rrm_process_neighbor_rep(struct wpa_supplicant *wpa_s,
+				   const u8 *report, size_t report_len)
+{
+	struct wpabuf *neighbor_rep;
+
+	wpa_hexdump(MSG_DEBUG, "RRM: New Neighbor Report", report, report_len);
+	if (report_len < 1)
+		return;
+
+	if (report[0] != wpa_s->rrm.next_neighbor_rep_token - 1) {
+		wpa_printf(MSG_DEBUG,
+			   "RRM: Discarding neighbor report with token %d (expected %d)",
+			   report[0], wpa_s->rrm.next_neighbor_rep_token - 1);
+		return;
+	}
+
+	eloop_cancel_timeout(wpas_rrm_neighbor_rep_timeout_handler, &wpa_s->rrm,
+			     NULL);
+
+	if (!wpa_s->rrm.notify_neighbor_rep) {
+		wpa_printf(MSG_ERROR, "RRM: Unexpected neighbor report");
+		return;
+	}
+
+	/* skipping the first byte, which is only an id (dialog token) */
+	neighbor_rep = wpabuf_alloc(report_len - 1);
+	if (neighbor_rep == NULL)
+		return;
+	wpabuf_put_data(neighbor_rep, report + 1, report_len - 1);
+	wpa_printf(MSG_DEBUG, "RRM: Notifying neighbor report (token = %d)",
+		   report[0]);
+	wpa_s->rrm.notify_neighbor_rep(wpa_s->rrm.neighbor_rep_cb_ctx,
+				       neighbor_rep);
+	wpa_s->rrm.notify_neighbor_rep = NULL;
+	wpa_s->rrm.neighbor_rep_cb_ctx = NULL;
+}
+
+
+#if defined(__CYGWIN__) || defined(CONFIG_NATIVE_WINDOWS)
+/* Workaround different, undefined for Windows, error codes used here */
+#define ENOTCONN -1
+#define EOPNOTSUPP -1
+#define ECANCELED -1
+#endif
+
+/**
+ * wpas_rrm_send_neighbor_rep_request - Request a neighbor report from our AP
+ * @wpa_s: Pointer to wpa_supplicant
+ * @ssid: if not null, this is sent in the request. Otherwise, no SSID IE
+ *	  is sent in the request.
+ * @cb: Callback function to be called once the requested report arrives, or
+ *	timed out after RRM_NEIGHBOR_REPORT_TIMEOUT seconds.
+ *	In the former case, 'neighbor_rep' is a newly allocated wpabuf, and it's
+ *	the requester's responsibility to free it.
+ *	In the latter case NULL will be sent in 'neighbor_rep'.
+ * @cb_ctx: Context value to send the callback function
+ * Returns: 0 in case of success, negative error code otherwise
+ *
+ * In case there is a previous request which has not been answered yet, the
+ * new request fails. The caller may retry after RRM_NEIGHBOR_REPORT_TIMEOUT.
+ * Request must contain a callback function.
+ */
+int wpas_rrm_send_neighbor_rep_request(struct wpa_supplicant *wpa_s,
+				       const struct wpa_ssid *ssid,
+				       void (*cb)(void *ctx,
+						  struct wpabuf *neighbor_rep),
+				       void *cb_ctx)
+{
+	struct wpabuf *buf;
+	const u8 *rrm_ie;
+
+	if (wpa_s->wpa_state != WPA_COMPLETED || wpa_s->current_ssid == NULL) {
+		wpa_printf(MSG_DEBUG, "RRM: No connection, no RRM.");
+		return -ENOTCONN;
+	}
+
+	if (!wpa_s->rrm.rrm_used) {
+		wpa_printf(MSG_DEBUG, "RRM: No RRM in current connection.");
+		return -EOPNOTSUPP;
+	}
+
+	rrm_ie = wpa_bss_get_ie(wpa_s->current_bss,
+				WLAN_EID_RRM_ENABLED_CAPABILITIES);
+	if (!rrm_ie || !(wpa_s->current_bss->caps & IEEE80211_CAP_RRM) ||
+	    !(rrm_ie[2] & WLAN_RRM_CAPS_NEIGHBOR_REPORT)) {
+		wpa_printf(MSG_DEBUG,
+			   "RRM: No network support for Neighbor Report.");
+		return -EOPNOTSUPP;
+	}
+
+	if (!cb) {
+		wpa_printf(MSG_DEBUG,
+			   "RRM: Neighbor Report request must provide a callback.");
+		return -EINVAL;
+	}
+
+	/* Refuse if there's a live request */
+	if (wpa_s->rrm.notify_neighbor_rep) {
+		wpa_printf(MSG_DEBUG,
+			   "RRM: Currently handling previous Neighbor Report.");
+		return -EBUSY;
+	}
+
+	/* 3 = action category + action code + dialog token */
+	buf = wpabuf_alloc(3 + (ssid ? 2 + ssid->ssid_len : 0));
+	if (buf == NULL) {
+		wpa_printf(MSG_DEBUG,
+			   "RRM: Failed to allocate Neighbor Report Request");
+		return -ENOMEM;
+	}
+
+	wpa_printf(MSG_DEBUG, "RRM: Neighbor report request (for %s), token=%d",
+		   (ssid ? wpa_ssid_txt(ssid->ssid, ssid->ssid_len) : ""),
+		   wpa_s->rrm.next_neighbor_rep_token);
+
+	wpabuf_put_u8(buf, WLAN_ACTION_RADIO_MEASUREMENT);
+	wpabuf_put_u8(buf, WLAN_RRM_NEIGHBOR_REPORT_REQUEST);
+	wpabuf_put_u8(buf, wpa_s->rrm.next_neighbor_rep_token);
+	if (ssid) {
+		wpabuf_put_u8(buf, WLAN_EID_SSID);
+		wpabuf_put_u8(buf, ssid->ssid_len);
+		wpabuf_put_data(buf, ssid->ssid, ssid->ssid_len);
+	}
+
+	wpa_s->rrm.next_neighbor_rep_token++;
+
+	if (wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid,
+				wpa_s->own_addr, wpa_s->bssid,
+				wpabuf_head(buf), wpabuf_len(buf), 0) < 0) {
+		wpa_printf(MSG_DEBUG,
+			   "RRM: Failed to send Neighbor Report Request");
+		wpabuf_free(buf);
+		return -ECANCELED;
+	}
+
+	wpa_s->rrm.neighbor_rep_cb_ctx = cb_ctx;
+	wpa_s->rrm.notify_neighbor_rep = cb;
+	eloop_register_timeout(RRM_NEIGHBOR_REPORT_TIMEOUT, 0,
+			       wpas_rrm_neighbor_rep_timeout_handler,
+			       &wpa_s->rrm, NULL);
+
+	wpabuf_free(buf);
+	return 0;
+}
+
+
+void wpas_rrm_handle_link_measurement_request(struct wpa_supplicant *wpa_s,
+					      const u8 *src,
+					      const u8 *frame, size_t len,
+					      int rssi)
+{
+	struct wpabuf *buf;
+	const struct rrm_link_measurement_request *req;
+	struct rrm_link_measurement_report report;
+
+	if (wpa_s->wpa_state != WPA_COMPLETED) {
+		wpa_printf(MSG_INFO,
+			   "RRM: Ignoring link measurement request. Not associated");
+		return;
+	}
+
+	if (!wpa_s->rrm.rrm_used) {
+		wpa_printf(MSG_INFO,
+			   "RRM: Ignoring link measurement request. Not RRM network");
+		return;
+	}
+
+	if (!(wpa_s->drv_rrm_flags & WPA_DRIVER_FLAGS_TX_POWER_INSERTION)) {
+		wpa_printf(MSG_INFO,
+			   "RRM: Measurement report failed. TX power insertion not supported");
+		return;
+	}
+
+	req = (const struct rrm_link_measurement_request *) frame;
+	if (len < sizeof(*req)) {
+		wpa_printf(MSG_INFO,
+			   "RRM: Link measurement report failed. Request too short");
+		return;
+	}
+
+	os_memset(&report, 0, sizeof(report));
+	report.tpc.eid = WLAN_EID_TPC_REPORT;
+	report.tpc.len = 2;
+	report.rsni = 255; /* 255 indicates that RSNI is not available */
+	report.dialog_token = req->dialog_token;
+
+	/*
+	 * It's possible to estimate RCPI based on RSSI in dBm. This
+	 * calculation will not reflect the correct value for high rates,
+	 * but it's good enough for Action frames which are transmitted
+	 * with up to 24 Mbps rates.
+	 */
+	if (!rssi)
+		report.rcpi = 255; /* not available */
+	else if (rssi < -110)
+		report.rcpi = 0;
+	else if (rssi > 0)
+		report.rcpi = 220;
+	else
+		report.rcpi = (rssi + 110) * 2;
+
+	/* action_category + action_code */
+	buf = wpabuf_alloc(2 + sizeof(report));
+	if (buf == NULL) {
+		wpa_printf(MSG_ERROR,
+			   "RRM: Link measurement report failed. Buffer allocation failed");
+		return;
+	}
+
+	wpabuf_put_u8(buf, WLAN_ACTION_RADIO_MEASUREMENT);
+	wpabuf_put_u8(buf, WLAN_RRM_LINK_MEASUREMENT_REPORT);
+	wpabuf_put_data(buf, &report, sizeof(report));
+	wpa_hexdump(MSG_DEBUG, "RRM: Link measurement report:",
+		    wpabuf_head(buf), wpabuf_len(buf));
+
+	if (wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, src,
+				wpa_s->own_addr, wpa_s->bssid,
+				wpabuf_head(buf), wpabuf_len(buf), 0)) {
+		wpa_printf(MSG_ERROR,
+			   "RRM: Link measurement report failed. Send action failed");
+	}
+	wpabuf_free(buf);
+}
diff --git a/wpa_supplicant/wpa_supplicant.conf b/wpa_supplicant/wpa_supplicant.conf
index 89da0da..d380965 100644
--- a/wpa_supplicant/wpa_supplicant.conf
+++ b/wpa_supplicant/wpa_supplicant.conf
@@ -114,6 +114,30 @@
 # networks are found, a new IBSS or AP mode network is created.
 ap_scan=1
 
+# MPM residency
+# By default, wpa_supplicant implements the mesh peering manager (MPM) for an
+# open mesh. However, if the driver can implement the MPM, you may set this to
+# 0 to use the driver version. When AMPE is enabled, the wpa_supplicant MPM is
+# always used.
+# 0: MPM lives in the driver
+# 1: wpa_supplicant provides an MPM which handles peering (default)
+#user_mpm=1
+
+# Maximum number of peer links (0-255; default: 99)
+# Maximum number of mesh peering currently maintained by the STA.
+#max_peer_links=99
+
+# Timeout in seconds to detect STA inactivity (default: 300 seconds)
+#
+# This timeout value is used in mesh STA to clean up inactive stations.
+#mesh_max_inactivity=300
+
+# cert_in_cb - Whether to include a peer certificate dump in events
+# This controls whether peer certificates for authentication server and
+# its certificate chain are included in EAP peer certificate events. This is
+# enabled by default.
+#cert_in_cb=1
+
 # EAP fast re-authentication
 # By default, fast re-authentication is enabled for all EAP methods that
 # support it. This variable can be used to disable fast re-authentication.
@@ -132,6 +156,16 @@
 # configure the path to the pkcs11 module required by the pkcs11 engine
 #pkcs11_module_path=/usr/lib/pkcs11/opensc-pkcs11.so
 
+# OpenSSL cipher string
+#
+# This is an OpenSSL specific configuration option for configuring the default
+# ciphers. If not set, "DEFAULT:!EXP:!LOW" is used as the default.
+# See https://www.openssl.org/docs/apps/ciphers.html for OpenSSL documentation
+# on cipher suite configuration. This is applicable only if wpa_supplicant is
+# built to use OpenSSL.
+#openssl_ciphers=DEFAULT:!EXP:!LOW
+
+
 # Dynamic EAP methods
 # If EAP methods were built dynamically as shared object files, they need to be
 # loaded here before being used in the network blocks. By default, EAP methods
@@ -234,6 +268,11 @@
 #wps_nfc_dh_privkey: Hexdump of DH Private Key
 #wps_nfc_dev_pw: Hexdump of Device Password
 
+# Priority for the networks added through WPS
+# This priority value will be set to each network profile that is added
+# by executing the WPS protocol.
+#wps_priority=0
+
 # Maximum number of BSS entries to keep in memory
 # Default: 200
 # This can be used to limit memory use on the BSS entries (cached scan
@@ -263,6 +302,10 @@
 # format: <backend name>[:<optional backend parameters>]
 #ext_password_backend=test:pw1=password|pw2=testing
 
+
+# Disable P2P functionality
+# p2p_disabled=1
+
 # Timeout in seconds to detect STA inactivity (default: 300 seconds)
 #
 # This timeout value is used in P2P GO mode to clean up
@@ -706,6 +749,11 @@
 # startup and reconfiguration time can be optimized by generating the PSK only
 # only when the passphrase or SSID has actually changed.
 #
+# mem_only_psk: Whether to keep PSK/passphrase only in memory
+# 0 = allow psk/passphrase to be stored to the configuration file
+# 1 = do not store psk/passphrase to the configuration file
+#mem_only_psk=0
+#
 # eapol_flags: IEEE 802.1X/EAPOL options (bit field)
 # Dynamic WEP key required for non-WPA mode
 # bit0 (1): require dynamically generated unicast WEP key
@@ -842,6 +890,10 @@
 #	sertificate is only accepted if it contains this string in the subject.
 #	The subject string is in following format:
 #	/C=US/ST=CA/L=San Francisco/CN=Test AS/emailAddress=as@example.com
+#	Note: Since this is a substring match, this cannot be used securily to
+#	do a suffix match against a possible domain name in the CN entry. For
+#	such a use case, domain_suffix_match or domain_match should be used
+#	instead.
 # altsubject_match: Semicolon separated string of entries to be matched against
 #	the alternative subject name of the authentication server certificate.
 #	If this string is set, the server sertificate is only accepted if it
@@ -850,6 +902,30 @@
 #	Example: EMAIL:server@example.com
 #	Example: DNS:server.example.com;DNS:server2.example.com
 #	Following types are supported: EMAIL, DNS, URI
+# domain_suffix_match: Constraint for server domain name. If set, this FQDN is
+#	used as a suffix match requirement for the AAAserver certificate in
+#	SubjectAltName dNSName element(s). If a matching dNSName is found, this
+#	constraint is met. If no dNSName values are present, this constraint is
+#	matched against SubjectName CN using same suffix match comparison.
+#
+#	Suffix match here means that the host/domain name is compared one label
+#	at a time starting from the top-level domain and all the labels in
+#	domain_suffix_match shall be included in the certificate. The
+#	certificate may include additional sub-level labels in addition to the
+#	required labels.
+#
+#	For example, domain_suffix_match=example.com would match
+#	test.example.com but would not match test-example.com.
+# domain_match: Constraint for server domain name
+#	If set, this FQDN is used as a full match requirement for the
+#	server certificate in SubjectAltName dNSName element(s). If a
+#	matching dNSName is found, this constraint is met. If no dNSName
+#	values are present, this constraint is matched against SubjectName CN
+#	using same full match comparison. This behavior is similar to
+#	domain_suffix_match, but has the requirement of a full match, i.e.,
+#	no subdomains or wildcard matches are allowed. Case-insensitive
+#	comparison is used, so "Example.com" matches "example.com", but would
+#	not match "test.Example.com".
 # phase1: Phase1 (outer authentication, i.e., TLS tunnel) parameters
 #	(string with field-value pairs, e.g., "peapver=0" or
 #	"peapver=1 peaplabel=1")
@@ -878,9 +954,20 @@
 #	 * 2 = require cryptobinding
 #	EAP-WSC (WPS) uses following options: pin=<Device Password> or
 #	pbc=1.
+#
+#	For wired IEEE 802.1X authentication, "allow_canned_success=1" can be
+#	used to configure a mode that allows EAP-Success (and EAP-Failure)
+#	without going through authentication step. Some switches use such
+#	sequence when forcing the port to be authorized/unauthorized or as a
+#	fallback option if the authentication server is unreachable. By default,
+#	wpa_supplicant discards such frames to protect against potential attacks
+#	by rogue devices, but this option can be used to disable that protection
+#	for cases where the server/authenticator does not need to be
+#	authenticated.
 # phase2: Phase2 (inner authentication with TLS tunnel) parameters
 #	(string with field-value pairs, e.g., "auth=MSCHAPV2" for EAP-PEAP or
-#	"autheap=MSCHAPV2 autheap=MD5" for EAP-TTLS)
+#	"autheap=MSCHAPV2 autheap=MD5" for EAP-TTLS). "mschapv2_retry=0" can be
+#	used to disable MSCHAPv2 password retry in authentication failure cases.
 #
 # TLS-based methods can use the following parameters to control TLS behavior
 # (these are normally in the phase1 parameter, but can be used also in the
@@ -896,7 +983,7 @@
 # tls_disable_session_ticket=0 - allow TLS Session Ticket extension to be used
 #	Note: If not set, this is automatically set to 1 for EAP-TLS/PEAP/TTLS
 #	as a workaround for broken authentication server implementations unless
-#	EAP workarounds are disabled with eap_workarounds=0.
+#	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_1=1 - disable use of TLSv1.1 (a workaround for AAA servers
@@ -916,9 +1003,12 @@
 # private_key2_passwd: Password for private key file
 # dh_file2: File path to DH/DSA parameters file (in PEM format)
 # subject_match2: Substring to be matched against the subject of the
-#	authentication server certificate.
-# altsubject_match2: Substring to be matched against the alternative subject
-#	name of the authentication server certificate.
+#	authentication server certificate. See subject_match for more details.
+# altsubject_match2: Semicolon separated string of entries to be matched
+#	against the alternative subject name of the authentication server
+#	certificate. See altsubject_match documentation for more details.
+# domain_suffix_match2: Constraint for server domain name. See
+#	domain_suffix_match for more details.
 #
 # fragment_size: Maximum EAP fragment size in bytes (default 1398).
 #	This value limits the fragment size for EAP methods that support
@@ -932,6 +1022,12 @@
 #	1 = try to use OCSP stapling, but not require response
 #	2 = require valid OCSP stapling response
 #
+# openssl_ciphers: OpenSSL specific cipher configuration
+#	This can be used to override the global openssl_ciphers configuration
+#	parameter (see above).
+#
+# erp: Whether EAP Re-authentication Protocol (ERP) is enabled
+#
 # EAP-FAST variables:
 # pac_file: File path for the PAC entries. wpa_supplicant will need to be able
 #	to create this file and write updates to it when PAC is being
@@ -1310,6 +1406,23 @@
 	psk="secret passphrase"
 }
 
+# open mesh network
+network={
+	ssid="test mesh"
+	mode=5
+	frequency=2437
+	key_mgmt=NONE
+}
+
+# secure (SAE + AMPE) network
+network={
+	ssid="secure mesh"
+	mode=5
+	frequency=2437
+	key_mgmt=SAE
+	psk="very secret passphrase"
+}
+
 
 # Catch all example that allows more or less all configuration modes
 network={
@@ -1385,6 +1498,21 @@
 	key_mgmt=NONE
 }
 
+# Example configuration blacklisting two APs - these will be ignored
+# for this network.
+network={
+	ssid="example"
+	psk="very secret passphrase"
+	bssid_blacklist=02:11:22:33:44:55 02:22:aa:44:55:66
+}
+
+# Example configuration limiting AP selection to a specific set of APs;
+# any other AP not matching the masked address will be ignored.
+network={
+	ssid="example"
+	psk="very secret passphrase"
+	bssid_whitelist=02:55:ae:bc:00:00/ff:ff:ff:ff:00:00 00:00:77:66:55:44/00:00:ff:ff:ff:ff
+}
 
 # Example config file that will only scan on channel 36.
 freq_list=5180
diff --git a/wpa_supplicant/wpa_supplicant_i.h b/wpa_supplicant/wpa_supplicant_i.h
index ae9dddd..dd5b245 100644
--- a/wpa_supplicant/wpa_supplicant_i.h
+++ b/wpa_supplicant/wpa_supplicant_i.h
@@ -15,15 +15,16 @@
 #include "common/wpa_ctrl.h"
 #include "wps/wps_defs.h"
 #include "config_ssid.h"
+#include "wmm_ac.h"
 
-extern const char *wpa_supplicant_version;
-extern const char *wpa_supplicant_license;
+extern const char *const wpa_supplicant_version;
+extern const char *const wpa_supplicant_license;
 #ifndef CONFIG_NO_STDOUT_DEBUG
-extern const char *wpa_supplicant_full_license1;
-extern const char *wpa_supplicant_full_license2;
-extern const char *wpa_supplicant_full_license3;
-extern const char *wpa_supplicant_full_license4;
-extern const char *wpa_supplicant_full_license5;
+extern const char *const wpa_supplicant_full_license1;
+extern const char *const wpa_supplicant_full_license2;
+extern const char *const wpa_supplicant_full_license3;
+extern const char *const wpa_supplicant_full_license4;
+extern const char *const wpa_supplicant_full_license5;
 #endif /* CONFIG_NO_STDOUT_DEBUG */
 
 struct wpa_sm;
@@ -65,17 +66,6 @@
 	 */
 	const char *confanother;
 
-#ifdef CONFIG_P2P
-	/**
-	 * conf_p2p_dev - Configuration file used to hold the
-	 * P2P Device configuration parameters.
-	 *
-	 * This can also be %NULL. In such a case, if a P2P Device dedicated
-	 * interfaces is created, the main configuration file will be used.
-	 */
-	const char *conf_p2p_dev;
-#endif /* CONFIG_P2P */
-
 	/**
 	 * ctrl_interface - Control interface parameter
 	 *
@@ -226,6 +216,18 @@
 	 * its internal entropy store over restarts.
 	 */
 	char *entropy_file;
+
+#ifdef CONFIG_P2P
+	/**
+	 * conf_p2p_dev - Configuration file used to hold the
+	 * P2P Device configuration parameters.
+	 *
+	 * This can also be %NULL. In such a case, if a P2P Device dedicated
+	 * interfaces is created, the main configuration file will be used.
+	 */
+	const char *conf_p2p_dev;
+#endif /* CONFIG_P2P */
+
 };
 
 struct p2p_srv_bonjour {
@@ -273,6 +275,9 @@
 	} conc_pref;
 	unsigned int p2p_per_sta_psk:1;
 	unsigned int p2p_fail_on_wps_complete:1;
+	unsigned int p2p_24ghz_social_channels:1;
+	unsigned int pending_p2ps_group:1;
+	unsigned int pending_group_iface_for_p2ps:1;
 
 #ifdef CONFIG_WIFI_DISPLAY
 	int wifi_display;
@@ -294,6 +299,7 @@
 struct wpa_radio {
 	char name[16]; /* from driver_ops get_radio_name() or empty if not
 			* available */
+	unsigned int external_scan_running:1;
 	struct dl_list ifaces; /* struct wpa_supplicant::radio_list entries */
 	struct dl_list work; /* struct wpa_radio_work::list entries */
 };
@@ -361,10 +367,12 @@
 	} type;
 	unsigned int tries;
 	struct os_reltime last_attempt;
+	unsigned int pbc_active;
+	u8 uuid[WPS_UUID_LEN];
 };
 
 struct wpa_ssid_value {
-	u8 ssid[32];
+	u8 ssid[SSID_MAX_LEN];
 	size_t ssid_len;
 };
 
@@ -376,6 +384,36 @@
 	unsigned int flags;
 };
 
+#define RRM_NEIGHBOR_REPORT_TIMEOUT 1 /* 1 second for AP to send a report */
+
+/*
+ * struct rrm_data - Data used for managing RRM features
+ */
+struct rrm_data {
+	/* rrm_used - indication regarding the current connection */
+	unsigned int rrm_used:1;
+
+	/*
+	 * notify_neighbor_rep - Callback for notifying report requester
+	 */
+	void (*notify_neighbor_rep)(void *ctx, struct wpabuf *neighbor_rep);
+
+	/*
+	 * neighbor_rep_cb_ctx - Callback context
+	 * Received in the callback registration, and sent to the callback
+	 * function as a parameter.
+	 */
+	void *neighbor_rep_cb_ctx;
+
+	/* next_neighbor_rep_token - Next request's dialog token */
+	u8 next_neighbor_rep_token;
+};
+
+enum wpa_supplicant_test_failure {
+	WPAS_TEST_FAILURE_NONE,
+	WPAS_TEST_FAILURE_SCAN_TRIGGER,
+};
+
 /**
  * struct wpa_supplicant - Internal data for wpa_supplicant interface
  *
@@ -417,6 +455,7 @@
 	u8 pending_bssid[ETH_ALEN]; /* If wpa_state == WPA_ASSOCIATING, this
 				     * field contains the target BSSID. */
 	int reassociate; /* reassociation requested */
+	int reassoc_same_bss; /* reassociating to the same bss */
 	int disconnected; /* all connections disabled; i.e., do no reassociate
 			   * before this has been cleared */
 	struct wpa_ssid *current_ssid;
@@ -482,7 +521,7 @@
 	unsigned int last_scan_res_size;
 	struct os_reltime last_scan;
 
-	struct wpa_driver_ops *driver;
+	const struct wpa_driver_ops *driver;
 	int interface_removed; /* whether the network interface has been
 				* removed */
 	struct wpa_sm *wpa;
@@ -552,6 +591,7 @@
 		 */
 		MANUAL_SCAN_REQ
 	} scan_req, last_scan_req;
+	enum wpa_states scan_prev_wpa_state;
 	struct os_reltime scan_trigger_time, scan_start_time;
 	int scan_runs; /* number of scan runs since WPS was started */
 	int *next_scan_freqs;
@@ -562,7 +602,6 @@
 	unsigned int manual_scan_only_new:1;
 	unsigned int own_scan_requested:1;
 	unsigned int own_scan_running:1;
-	unsigned int external_scan_running:1;
 	unsigned int clear_driver_scan_cache:1;
 	unsigned int manual_scan_id;
 	int scan_interval; /* time in sec between scans to find suitable AP */
@@ -573,8 +612,13 @@
 	int scan_id[MAX_SCAN_ID];
 	unsigned int scan_id_count;
 
-	unsigned int drv_flags;
+	struct wpa_ssid_value *ssids_from_scan_req;
+	unsigned int num_ssids_from_scan_req;
+
+	u64 drv_flags;
 	unsigned int drv_enc;
+	unsigned int drv_smps_modes;
+	unsigned int drv_rrm_flags;
 
 	/*
 	 * A bitmap of supported protocols for probe response offload. See
@@ -601,6 +645,7 @@
 	int wps_success; /* WPS success event received */
 	struct wps_er *wps_er;
 	unsigned int wps_run;
+	struct os_reltime wps_pin_start_time;
 	int blacklist_cleared;
 
 	struct wpabuf *pending_eapol_rx;
@@ -610,6 +655,7 @@
 	unsigned int eap_expected_failure:1;
 	unsigned int reattach:1; /* reassociation to the same BSS requested */
 	unsigned int mac_addr_changed:1;
+	unsigned int added_vif:1;
 
 	struct os_reltime last_mac_addr_change;
 	int last_mac_addr_style;
@@ -623,7 +669,7 @@
 
 #ifdef CONFIG_SME
 	struct {
-		u8 ssid[32];
+		u8 ssid[SSID_MAX_LEN];
 		size_t ssid_len;
 		int freq;
 		u8 assoc_req_ie[200];
@@ -646,6 +692,9 @@
 					* SA Query transaction identifiers */
 		struct os_reltime sa_query_start;
 		struct os_reltime last_unprot_disconnect;
+		enum { HT_SEC_CHAN_UNKNOWN,
+		       HT_SEC_CHAN_ABOVE,
+		       HT_SEC_CHAN_BELOW } ht_sec_chan;
 		u8 sched_obss_scan;
 		u16 obss_scan_int;
 		u16 bss_max_idle_period;
@@ -653,6 +702,7 @@
 		struct sae_data sae;
 		struct wpabuf *sae_token;
 		int sae_group_index;
+		unsigned int sae_pmksa_caching:1;
 #endif /* CONFIG_SAE */
 	} sme;
 #endif /* CONFIG_SME */
@@ -664,6 +714,15 @@
 	void *ap_configured_cb_data;
 #endif /* CONFIG_AP */
 
+	struct hostapd_iface *ifmsh;
+#ifdef CONFIG_MESH
+	struct mesh_rsn *mesh_rsn;
+	int mesh_if_idx;
+	unsigned int mesh_if_created:1;
+	unsigned int mesh_ht_enabled:1;
+	int mesh_auth_block_duration; /* sec */
+#endif /* CONFIG_MESH */
+
 	unsigned int off_channel_freq;
 	struct wpabuf *pending_action_tx;
 	u8 pending_action_src[ETH_ALEN];
@@ -715,14 +774,14 @@
 	u8 pending_join_iface_addr[ETH_ALEN];
 	u8 pending_join_dev_addr[ETH_ALEN];
 	int pending_join_wps_method;
-	u8 p2p_join_ssid[32];
+	u8 p2p_join_ssid[SSID_MAX_LEN];
 	size_t p2p_join_ssid_len;
 	int p2p_join_scan_count;
 	int auto_pd_scan_retry;
 	int force_long_sd;
 	u16 pending_pd_config_methods;
 	enum {
-		NORMAL_PD, AUTO_PD_GO_NEG, AUTO_PD_JOIN
+		NORMAL_PD, AUTO_PD_GO_NEG, AUTO_PD_JOIN, AUTO_PD_ASP
 	} pending_pd_use;
 
 	/*
@@ -755,11 +814,14 @@
 	unsigned int p2p_go_vht:1;
 	unsigned int user_initiated_pd:1;
 	unsigned int p2p_go_group_formation_completed:1;
+	unsigned int group_formation_reported:1;
 	unsigned int waiting_presence_resp;
 	int p2p_first_connection_timeout;
 	unsigned int p2p_nfc_tag_enabled:1;
 	unsigned int p2p_peer_oob_pk_hash_known:1;
 	unsigned int p2p_disable_ip_addr_req:1;
+	unsigned int p2ps_join_addr_valid:1;
+	unsigned int p2p_cli_probe:1;
 	int p2p_persistent_go_freq;
 	int p2p_persistent_id;
 	int p2p_go_intent;
@@ -775,6 +837,11 @@
 					* formation */
 	u8 p2p_peer_oob_pubkey_hash[WPS_OOB_PUBKEY_HASH_LEN];
 	u8 p2p_ip_addr_info[3 * 4];
+
+	/* group common frequencies */
+	int *p2p_group_common_freqs;
+	unsigned int p2p_group_common_freqs_num;
+	u8 p2ps_join_addr[ETH_ALEN];
 #endif /* CONFIG_P2P */
 
 	struct wpa_ssid *bgscan_ssid;
@@ -809,8 +876,10 @@
 	unsigned int network_select:1;
 	unsigned int auto_select:1;
 	unsigned int auto_network_select:1;
+	unsigned int interworking_fast_assoc_tried:1;
 	unsigned int fetch_all_anqp:1;
 	unsigned int fetch_osu_info:1;
+	unsigned int fetch_osu_waiting_scan:1;
 	unsigned int fetch_osu_icon_in_progress:1;
 	struct wpa_bss *interworking_gas_bss;
 	unsigned int osu_icon_id;
@@ -827,6 +896,12 @@
 		u16 num_modes;
 		u16 flags;
 	} hw;
+	enum local_hw_capab {
+		CAPAB_NO_HT_VHT,
+		CAPAB_HT,
+		CAPAB_HT40,
+		CAPAB_VHT,
+	} hw_capab;
 #ifdef CONFIG_MACSEC
 	struct ieee802_1x_kay *kay;
 #endif /* CONFIG_MACSEC */
@@ -845,6 +920,24 @@
 
 	unsigned int no_keep_alive:1;
 	unsigned int ext_mgmt_frame_handling:1;
+	unsigned int ext_eapol_frame_io:1;
+	unsigned int wmm_ac_supported:1;
+	unsigned int ext_work_in_progress:1;
+	unsigned int own_disconnect_req:1;
+
+#define MAC_ADDR_RAND_SCAN       BIT(0)
+#define MAC_ADDR_RAND_SCHED_SCAN BIT(1)
+#define MAC_ADDR_RAND_PNO        BIT(2)
+#define MAC_ADDR_RAND_ALL        (MAC_ADDR_RAND_SCAN | \
+				  MAC_ADDR_RAND_SCHED_SCAN | \
+				  MAC_ADDR_RAND_PNO)
+	unsigned int mac_addr_rand_supported;
+	unsigned int mac_addr_rand_enable;
+
+	/* MAC Address followed by mask (2 * ETH_ALEN) */
+	u8 *mac_addr_scan;
+	u8 *mac_addr_sched_scan;
+	u8 *mac_addr_pno;
 
 #ifdef CONFIG_WNM
 	u8 wnm_dialog_token;
@@ -852,9 +945,10 @@
 	u8 wnm_num_neighbor_report;
 	u8 wnm_mode;
 	u16 wnm_dissoc_timer;
-	u8 wnm_validity_interval;
 	u8 wnm_bss_termination_duration[12];
 	struct neighbor_report *wnm_neighbor_report_elements;
+	struct os_reltime wnm_cand_valid_until;
+	u8 wnm_cand_from_bss[ETH_ALEN];
 #endif /* CONFIG_WNM */
 
 #ifdef CONFIG_TESTING_GET_GTK
@@ -868,6 +962,21 @@
 	unsigned int ext_work_id;
 
 	struct wpabuf *vendor_elem[NUM_VENDOR_ELEM_FRAMES];
+
+#ifdef CONFIG_TESTING_OPTIONS
+	struct l2_packet_data *l2_test;
+	unsigned int extra_roc_dur;
+	enum wpa_supplicant_test_failure test_failure;
+#endif /* CONFIG_TESTING_OPTIONS */
+
+	struct wmm_ac_assoc_data *wmm_ac_assoc_info;
+	struct wmm_tspec_element *tspecs[WMM_AC_NUM][TS_DIR_IDX_COUNT];
+	struct wmm_ac_addts_request *addts_request;
+	u8 wmm_ac_last_dialog_token;
+	struct wmm_tspec_element *last_tspecs;
+	u8 last_tspecs_count;
+
+	struct rrm_data rrm;
 };
 
 
@@ -934,7 +1043,8 @@
 void wpa_show_license(void);
 
 struct wpa_supplicant * wpa_supplicant_add_iface(struct wpa_global *global,
-						 struct wpa_interface *iface);
+						 struct wpa_interface *iface,
+						 struct wpa_supplicant *parent);
 int wpa_supplicant_remove_iface(struct wpa_global *global,
 				struct wpa_supplicant *wpa_s,
 				int terminate);
@@ -964,6 +1074,20 @@
 int wpas_build_ext_capab(struct wpa_supplicant *wpa_s, u8 *buf, size_t buflen);
 int wpas_update_random_addr(struct wpa_supplicant *wpa_s, int style);
 int wpas_update_random_addr_disassoc(struct wpa_supplicant *wpa_s);
+void add_freq(int *freqs, int *num_freqs, int freq);
+
+void wpas_rrm_reset(struct wpa_supplicant *wpa_s);
+void wpas_rrm_process_neighbor_rep(struct wpa_supplicant *wpa_s,
+				   const u8 *report, size_t report_len);
+int wpas_rrm_send_neighbor_rep_request(struct wpa_supplicant *wpa_s,
+				       const struct wpa_ssid *ssid,
+				       void (*cb)(void *ctx,
+						  struct wpabuf *neighbor_rep),
+				       void *cb_ctx);
+void wpas_rrm_handle_link_measurement_request(struct wpa_supplicant *wpa_s,
+					      const u8 *src,
+					      const u8 *frame, size_t len,
+					      int rssi);
 
 /**
  * wpa_supplicant_ctrl_iface_ctrl_rsp_handle - Handle a control response
@@ -980,6 +1104,10 @@
 					      const char *field,
 					      const char *value);
 
+void ibss_mesh_setup_freq(struct wpa_supplicant *wpa_s,
+			  const struct wpa_ssid *ssid,
+			  struct hostapd_freq_params *freq);
+
 /* events.c */
 void wpa_supplicant_mark_disassoc(struct wpa_supplicant *wpa_s);
 int wpa_supplicant_connect(struct wpa_supplicant *wpa_s,
@@ -991,23 +1119,22 @@
 int wpa_supplicant_fast_associate(struct wpa_supplicant *wpa_s);
 struct wpa_bss * wpa_supplicant_pick_network(struct wpa_supplicant *wpa_s,
 					     struct wpa_ssid **selected_ssid);
-int ht_supported(const struct hostapd_hw_modes *mode);
-int vht_supported(const struct hostapd_hw_modes *mode);
 
 /* eap_register.c */
 int eap_register_methods(void);
 
 /**
- * Utility method to tell if a given network is a persistent group
+ * Utility method to tell if a given network is for persistent group storage
  * @ssid: Network object
  * Returns: 1 if network is a persistent group, 0 otherwise
  */
 static inline int network_is_persistent_group(struct wpa_ssid *ssid)
 {
-	return ((ssid->disabled == 2) || ssid->p2p_persistent_group);
+	return ssid->disabled == 2 && ssid->p2p_persistent_group;
 }
 
 int wpas_network_disabled(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid);
+int wpas_get_ssid_pmf(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid);
 
 int wpas_init_ext_pw(struct wpa_supplicant *wpa_s);
 
@@ -1021,4 +1148,5 @@
 int get_shared_radio_freqs(struct wpa_supplicant *wpa_s,
 			   int *freq_array, unsigned int len);
 
+void wpas_network_reenabled(void *eloop_ctx, void *timeout_ctx);
 #endif /* WPA_SUPPLICANT_I_H */
diff --git a/wpa_supplicant/wpas_glue.c b/wpa_supplicant/wpas_glue.c
index 38279b1..29c22ba 100644
--- a/wpa_supplicant/wpas_glue.c
+++ b/wpa_supplicant/wpas_glue.c
@@ -1,6 +1,6 @@
 /*
  * WPA Supplicant - Glue code to setup EAPOL and RSN modules
- * 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.
@@ -96,11 +96,26 @@
 static int wpa_ether_send(struct wpa_supplicant *wpa_s, const u8 *dest,
 			  u16 proto, const u8 *buf, size_t len)
 {
+#ifdef CONFIG_TESTING_OPTIONS
+	if (wpa_s->ext_eapol_frame_io && proto == ETH_P_EAPOL) {
+		size_t hex_len = 2 * len + 1;
+		char *hex = os_malloc(hex_len);
+
+		if (hex == NULL)
+			return -1;
+		wpa_snprintf_hex(hex, hex_len, buf, len);
+		wpa_msg(wpa_s, MSG_INFO, "EAPOL-TX " MACSTR " %s",
+			MAC2STR(dest), hex);
+		os_free(hex);
+		return 0;
+	}
+#endif /* CONFIG_TESTING_OPTIONS */
+
 	if (wpa_s->l2) {
 		return l2_packet_send(wpa_s->l2, dest, proto, buf, len);
 	}
 
-	return wpa_drv_send_eapol(wpa_s, dest, proto, buf, len);
+	return -1;
 }
 #endif /* IEEE8021X_EAPOL || !CONFIG_NO_WPA */
 
@@ -528,7 +543,44 @@
 					 const u8 *ies, size_t ies_len)
 {
 	struct wpa_supplicant *wpa_s = ctx;
-	return wpa_drv_send_ft_action(wpa_s, action, target_ap, ies, ies_len);
+	int ret;
+	u8 *data, *pos;
+	size_t data_len;
+
+	if (action != 1) {
+		wpa_printf(MSG_ERROR, "Unsupported send_ft_action action %d",
+			   action);
+		return -1;
+	}
+
+	/*
+	 * Action frame payload:
+	 * Category[1] = 6 (Fast BSS Transition)
+	 * Action[1] = 1 (Fast BSS Transition Request)
+	 * STA Address
+	 * Target AP Address
+	 * FT IEs
+	 */
+
+	data_len = 2 + 2 * ETH_ALEN + ies_len;
+	data = os_malloc(data_len);
+	if (data == NULL)
+		return -1;
+	pos = data;
+	*pos++ = 0x06; /* FT Action category */
+	*pos++ = action;
+	os_memcpy(pos, wpa_s->own_addr, ETH_ALEN);
+	pos += ETH_ALEN;
+	os_memcpy(pos, target_ap, ETH_ALEN);
+	pos += ETH_ALEN;
+	os_memcpy(pos, ies, ies_len);
+
+	ret = wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0,
+				  wpa_s->bssid, wpa_s->own_addr, wpa_s->bssid,
+				  data, data_len, 0);
+	os_free(data);
+
+	return ret;
 }
 
 
@@ -557,12 +609,14 @@
 #ifdef CONFIG_TDLS
 
 static int wpa_supplicant_tdls_get_capa(void *ctx, int *tdls_supported,
-					int *tdls_ext_setup)
+					int *tdls_ext_setup,
+					int *tdls_chan_switch)
 {
 	struct wpa_supplicant *wpa_s = ctx;
 
 	*tdls_supported = 0;
 	*tdls_ext_setup = 0;
+	*tdls_chan_switch = 0;
 
 	if (!wpa_s->drv_capa_known)
 		return -1;
@@ -573,6 +627,9 @@
 	if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_TDLS_EXTERNAL_SETUP)
 		*tdls_ext_setup = 1;
 
+	if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_TDLS_CHANNEL_SWITCH)
+		*tdls_chan_switch = 1;
+
 	return 0;
 }
 
@@ -640,6 +697,25 @@
 	return wpa_drv_sta_add(wpa_s, &params);
 }
 
+
+static int wpa_supplicant_tdls_enable_channel_switch(
+	void *ctx, const u8 *addr, u8 oper_class,
+	const struct hostapd_freq_params *params)
+{
+	struct wpa_supplicant *wpa_s = ctx;
+
+	return wpa_drv_tdls_enable_channel_switch(wpa_s, addr, oper_class,
+						  params);
+}
+
+
+static int wpa_supplicant_tdls_disable_channel_switch(void *ctx, const u8 *addr)
+{
+	struct wpa_supplicant *wpa_s = ctx;
+
+	return wpa_drv_tdls_disable_channel_switch(wpa_s, addr);
+}
+
 #endif /* CONFIG_TDLS */
 
 #endif /* CONFIG_NO_WPA */
@@ -661,6 +737,8 @@
 		return WPA_CTRL_REQ_EAP_PASSPHRASE;
 	else if (os_strcmp(field, "SIM") == 0)
 		return WPA_CTRL_REQ_SIM;
+	else if (os_strcmp(field, "PSK_PASSPHRASE") == 0)
+		return WPA_CTRL_REQ_PSK_PASSPHRASE;
 	return WPA_CTRL_REQ_UNKNOWN;
 }
 
@@ -700,6 +778,10 @@
 	case WPA_CTRL_REQ_SIM:
 		ret = "SIM";
 		break;
+	case WPA_CTRL_REQ_PSK_PASSPHRASE:
+		*txt = "PSK or passphrase";
+		ret = "PSK_PASSPHRASE";
+		break;
 	default:
 		break;
 	}
@@ -713,6 +795,35 @@
 	return ret;
 }
 
+
+void wpas_send_ctrl_req(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid,
+			const char *field_name, const char *txt)
+{
+	char *buf;
+	size_t buflen;
+	int len;
+
+	buflen = 100 + os_strlen(txt) + ssid->ssid_len;
+	buf = os_malloc(buflen);
+	if (buf == NULL)
+		return;
+	len = os_snprintf(buf, buflen, "%s-%d:%s needed for SSID ",
+			  field_name, ssid->id, txt);
+	if (os_snprintf_error(buflen, len)) {
+		os_free(buf);
+		return;
+	}
+	if (ssid->ssid && buflen > len + ssid->ssid_len) {
+		os_memcpy(buf + len, ssid->ssid, ssid->ssid_len);
+		len += ssid->ssid_len;
+		buf[len] = '\0';
+	}
+	buf[buflen - 1] = '\0';
+	wpa_msg(wpa_s, MSG_INFO, WPA_CTRL_REQ "%s", buf);
+	os_free(buf);
+}
+
+
 #ifdef IEEE8021X_EAPOL
 #if defined(CONFIG_CTRL_IFACE) || !defined(CONFIG_NO_STDOUT_DEBUG)
 static void wpa_supplicant_eap_param_needed(void *ctx,
@@ -722,9 +833,6 @@
 	struct wpa_supplicant *wpa_s = ctx;
 	struct wpa_ssid *ssid = wpa_s->current_ssid;
 	const char *field_name, *txt = NULL;
-	char *buf;
-	size_t buflen;
-	int len;
 
 	if (ssid == NULL)
 		return;
@@ -741,31 +849,32 @@
 
 	wpas_notify_eap_status(wpa_s, "eap parameter needed", field_name);
 
-	buflen = 100 + os_strlen(txt) + ssid->ssid_len;
-	buf = os_malloc(buflen);
-	if (buf == NULL)
-		return;
-	len = os_snprintf(buf, buflen,
-			  WPA_CTRL_REQ "%s-%d:%s needed for SSID ",
-			  field_name, ssid->id, txt);
-	if (len < 0 || (size_t) len >= buflen) {
-		os_free(buf);
-		return;
-	}
-	if (ssid->ssid && buflen > len + ssid->ssid_len) {
-		os_memcpy(buf + len, ssid->ssid, ssid->ssid_len);
-		len += ssid->ssid_len;
-		buf[len] = '\0';
-	}
-	buf[buflen - 1] = '\0';
-	wpa_msg(wpa_s, MSG_INFO, "%s", buf);
-	os_free(buf);
+	wpas_send_ctrl_req(wpa_s, ssid, field_name, txt);
 }
 #else /* CONFIG_CTRL_IFACE || !CONFIG_NO_STDOUT_DEBUG */
 #define wpa_supplicant_eap_param_needed NULL
 #endif /* CONFIG_CTRL_IFACE || !CONFIG_NO_STDOUT_DEBUG */
 
 
+#ifdef CONFIG_EAP_PROXY
+static void wpa_supplicant_eap_proxy_cb(void *ctx)
+{
+	struct wpa_supplicant *wpa_s = ctx;
+	size_t len;
+
+	wpa_s->mnc_len = eapol_sm_get_eap_proxy_imsi(wpa_s->eapol,
+						     wpa_s->imsi, &len);
+	if (wpa_s->mnc_len > 0) {
+		wpa_s->imsi[len] = '\0';
+		wpa_printf(MSG_DEBUG, "eap_proxy: IMSI %s (MNC length %d)",
+			   wpa_s->imsi, wpa_s->mnc_len);
+	} else {
+		wpa_printf(MSG_DEBUG, "eap_proxy: IMSI not available");
+	}
+}
+#endif /* CONFIG_EAP_PROXY */
+
+
 static void wpa_supplicant_port_cb(void *ctx, int authorized)
 {
 	struct wpa_supplicant *wpa_s = ctx;
@@ -784,12 +893,14 @@
 
 
 static void wpa_supplicant_cert_cb(void *ctx, int depth, const char *subject,
+				   const char *altsubject[], int num_altsubject,
 				   const char *cert_hash,
 				   const struct wpabuf *cert)
 {
 	struct wpa_supplicant *wpa_s = ctx;
 
-	wpas_notify_certification(wpa_s, depth, subject, cert_hash, cert);
+	wpas_notify_certification(wpa_s, depth, subject, altsubject,
+				  num_altsubject, cert_hash, cert);
 }
 
 
@@ -866,11 +977,16 @@
 	ctx->opensc_engine_path = wpa_s->conf->opensc_engine_path;
 	ctx->pkcs11_engine_path = wpa_s->conf->pkcs11_engine_path;
 	ctx->pkcs11_module_path = wpa_s->conf->pkcs11_module_path;
+	ctx->openssl_ciphers = wpa_s->conf->openssl_ciphers;
 	ctx->wps = wpa_s->wps;
 	ctx->eap_param_needed = wpa_supplicant_eap_param_needed;
+#ifdef CONFIG_EAP_PROXY
+	ctx->eap_proxy_cb = wpa_supplicant_eap_proxy_cb;
+#endif /* CONFIG_EAP_PROXY */
 	ctx->port_cb = wpa_supplicant_port_cb;
 	ctx->cb = wpa_supplicant_eapol_cb;
 	ctx->cert_cb = wpa_supplicant_cert_cb;
+	ctx->cert_in_cb = wpa_s->conf->cert_in_cb;
 	ctx->status_cb = wpa_supplicant_status_cb;
 	ctx->set_anon_id = wpa_supplicant_set_anon_id;
 	ctx->cb_ctx = wpa_s;
@@ -888,17 +1004,32 @@
 
 
 #ifndef CONFIG_NO_WPA
-static void wpa_supplicant_set_rekey_offload(void *ctx, const u8 *kek,
-					     const u8 *kck,
+static void wpa_supplicant_set_rekey_offload(void *ctx,
+					     const u8 *kek, size_t kek_len,
+					     const u8 *kck, size_t kck_len,
 					     const u8 *replay_ctr)
 {
 	struct wpa_supplicant *wpa_s = ctx;
 
-	wpa_drv_set_rekey_info(wpa_s, kek, kck, replay_ctr);
+	wpa_drv_set_rekey_info(wpa_s, kek, kek_len, kck, kck_len, replay_ctr);
 }
 #endif /* CONFIG_NO_WPA */
 
 
+static int wpa_supplicant_key_mgmt_set_pmk(void *ctx, const u8 *pmk,
+					   size_t pmk_len)
+{
+	struct wpa_supplicant *wpa_s = ctx;
+
+	if (wpa_s->conf->key_mgmt_offload &&
+	    (wpa_s->drv_flags & WPA_DRIVER_FLAGS_KEY_MGMT_OFFLOAD))
+		return wpa_drv_set_key(wpa_s, WPA_ALG_PMK, NULL, 0, 0,
+				       NULL, 0, pmk, pmk_len);
+	else
+		return 0;
+}
+
+
 int wpa_supplicant_init_wpa(struct wpa_supplicant *wpa_s)
 {
 #ifndef CONFIG_NO_WPA
@@ -938,13 +1069,19 @@
 	ctx->send_tdls_mgmt = wpa_supplicant_send_tdls_mgmt;
 	ctx->tdls_oper = wpa_supplicant_tdls_oper;
 	ctx->tdls_peer_addset = wpa_supplicant_tdls_peer_addset;
+	ctx->tdls_enable_channel_switch =
+		wpa_supplicant_tdls_enable_channel_switch;
+	ctx->tdls_disable_channel_switch =
+		wpa_supplicant_tdls_disable_channel_switch;
 #endif /* CONFIG_TDLS */
 	ctx->set_rekey_offload = wpa_supplicant_set_rekey_offload;
+	ctx->key_mgmt_set_pmk = wpa_supplicant_key_mgmt_set_pmk;
 
 	wpa_s->wpa = wpa_sm_init(ctx);
 	if (wpa_s->wpa == NULL) {
 		wpa_printf(MSG_ERROR, "Failed to initialize WPA state "
 			   "machine");
+		os_free(ctx);
 		return -1;
 	}
 #endif /* CONFIG_NO_WPA */
diff --git a/wpa_supplicant/wpas_glue.h b/wpa_supplicant/wpas_glue.h
index 9808c22..5585e56 100644
--- a/wpa_supplicant/wpas_glue.h
+++ b/wpa_supplicant/wpas_glue.h
@@ -22,4 +22,7 @@
 
 enum wpa_ctrl_req_type wpa_supplicant_ctrl_req_from_string(const char *field);
 
+void wpas_send_ctrl_req(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid,
+			const char *field_name, const char *txt);
+
 #endif /* WPAS_GLUE_H */
diff --git a/wpa_supplicant/wpas_module_tests.c b/wpa_supplicant/wpas_module_tests.c
index e4c83b5..6af1678 100644
--- a/wpa_supplicant/wpas_module_tests.c
+++ b/wpa_supplicant/wpas_module_tests.c
@@ -98,5 +98,11 @@
 			ret = -1;
 	}
 
+	{
+		int crypto_module_tests(void);
+		if (crypto_module_tests() < 0)
+			ret = -1;
+	}
+
 	return ret;
 }
diff --git a/wpa_supplicant/wps_supplicant.c b/wpa_supplicant/wps_supplicant.c
index 40a5c69..2db7914 100644
--- a/wpa_supplicant/wps_supplicant.c
+++ b/wpa_supplicant/wps_supplicant.c
@@ -39,6 +39,14 @@
 #define WPS_PIN_SCAN_IGNORE_SEL_REG 3
 #endif /* WPS_PIN_SCAN_IGNORE_SEL_REG */
 
+/*
+ * The minimum time in seconds before trying to associate to a WPS PIN AP that
+ * does not have Selected Registrar TRUE.
+ */
+#ifndef WPS_PIN_TIME_IGNORE_SEL_REG
+#define WPS_PIN_TIME_IGNORE_SEL_REG 5
+#endif /* WPS_PIN_TIME_IGNORE_SEL_REG */
+
 static void wpas_wps_timeout(void *eloop_ctx, void *timeout_ctx);
 static void wpas_clear_wps(struct wpa_supplicant *wpa_s);
 
@@ -113,6 +121,7 @@
 		wpa_printf(MSG_DEBUG, "WPS: Network configuration replaced - "
 			   "try to associate with the received credential "
 			   "(freq=%u)", freq);
+		wpa_s->own_disconnect_req = 1;
 		wpa_supplicant_deauthenticate(wpa_s,
 					      WLAN_REASON_DEAUTH_LEAVING);
 		if (disabled) {
@@ -160,6 +169,7 @@
 		wpa_printf(MSG_DEBUG, "WPS: Registration completed - waiting "
 			   "for external credential processing");
 		wpas_clear_wps(wpa_s);
+		wpa_s->own_disconnect_req = 1;
 		wpa_supplicant_deauthenticate(wpa_s,
 					      WLAN_REASON_DEAUTH_LEAVING);
 		return 1;
@@ -286,7 +296,9 @@
 		/* compare security parameters */
 		if (ssid->auth_alg != new_ssid->auth_alg ||
 		    ssid->key_mgmt != new_ssid->key_mgmt ||
-		    ssid->group_cipher != new_ssid->group_cipher)
+		    (ssid->group_cipher != new_ssid->group_cipher &&
+		     !(ssid->group_cipher & new_ssid->group_cipher &
+		       WPA_CIPHER_CCMP)))
 			continue;
 
 		/*
@@ -337,6 +349,8 @@
 		/* Remove the duplicated older network entry. */
 		wpa_printf(MSG_DEBUG, "Remove duplicate network %d", ssid->id);
 		wpas_notify_network_removed(wpa_s, ssid);
+		if (wpa_s->current_ssid == ssid)
+			wpa_s->current_ssid = NULL;
 		wpa_config_remove_network(wpa_s->conf, ssid->id);
 	}
 }
@@ -471,6 +485,11 @@
 		break;
 	case WPS_ENCR_AES:
 		ssid->pairwise_cipher = WPA_CIPHER_CCMP;
+		if (wpa_s->drv_capa_known &&
+		    (wpa_s->drv_enc & WPA_DRIVER_CAPA_ENC_GCMP)) {
+			ssid->pairwise_cipher |= WPA_CIPHER_GCMP;
+			ssid->group_cipher |= WPA_CIPHER_GCMP;
+		}
 		break;
 	}
 
@@ -528,6 +547,7 @@
 			return -1;
 		}
 	}
+	ssid->priority = wpa_s->conf->wps_priority;
 
 	wpas_wps_security_workaround(wpa_s, ssid, cred);
 
@@ -541,6 +561,9 @@
 	}
 #endif /* CONFIG_NO_CONFIG_WRITE */
 
+	if (ssid->priority)
+		wpa_config_update_prio_list(wpa_s->conf);
+
 	/*
 	 * Optimize the post-WPS scan based on the channel used during
 	 * the provisioning in case EAP-Failure is not received.
@@ -869,7 +892,8 @@
 	if (!wpa_s->current_ssid || !wpa_s->assoc_freq)
 		return 0;
 
-	return (wpa_s->assoc_freq > 2484) ? WPS_RF_50GHZ : WPS_RF_24GHZ;
+	return (wpa_s->assoc_freq > 50000) ? WPS_RF_60GHZ :
+		(wpa_s->assoc_freq > 2484) ? WPS_RF_50GHZ : WPS_RF_24GHZ;
 }
 
 
@@ -904,6 +928,7 @@
 	while (ssid) {
 		if (ssid->key_mgmt & WPA_KEY_MGMT_WPS) {
 			if (ssid == wpa_s->current_ssid) {
+				wpa_s->own_disconnect_req = 1;
 				wpa_supplicant_deauthenticate(
 					wpa_s, WLAN_REASON_DEAUTH_LEAVING);
 			}
@@ -1013,9 +1038,11 @@
 {
 	struct wpa_ssid *ssid;
 
-	if (wpa_s->current_ssid)
+	if (wpa_s->current_ssid) {
+		wpa_s->own_disconnect_req = 1;
 		wpa_supplicant_deauthenticate(
 			wpa_s, WLAN_REASON_DEAUTH_LEAVING);
+	}
 
 	/* Mark all other networks disabled and trigger reassociation */
 	ssid = wpa_s->conf->ssid;
@@ -1082,6 +1109,14 @@
 		       int p2p_group)
 {
 	struct wpa_ssid *ssid;
+
+#ifdef CONFIG_AP
+	if (wpa_s->ap_iface) {
+		wpa_printf(MSG_DEBUG,
+			   "WPS: Reject request to start Registrar(as station) operation while AP mode is enabled");
+		return -1;
+	}
+#endif /* CONFIG_AP */
 	wpas_clear_wps(wpa_s);
 	ssid = wpas_wps_add_network(wpa_s, 0, NULL, bssid);
 	if (ssid == NULL)
@@ -1122,6 +1157,13 @@
 	unsigned int rpin = 0;
 	char hash[2 * WPS_OOB_PUBKEY_HASH_LEN + 10];
 
+#ifdef CONFIG_AP
+	if (wpa_s->ap_iface) {
+		wpa_printf(MSG_DEBUG,
+			   "WPS: Reject request to start Registrar(as station) operation while AP mode is enabled");
+		return -1;
+	}
+#endif /* CONFIG_AP */
 	wpas_clear_wps(wpa_s);
 	if (bssid && is_zero_ether_addr(bssid))
 		bssid = NULL;
@@ -1149,6 +1191,7 @@
 	}
 #ifdef CONFIG_P2P
 	if (p2p_group && wpa_s->go_params && wpa_s->go_params->ssid_len) {
+		os_free(ssid->ssid);
 		ssid->ssid = os_zalloc(wpa_s->go_params->ssid_len + 1);
 		if (ssid->ssid) {
 			ssid->ssid_len = wpa_s->go_params->ssid_len;
@@ -1187,6 +1230,7 @@
 int wpas_wps_start_pin(struct wpa_supplicant *wpa_s, const u8 *bssid,
 		       const char *pin, int p2p_group, u16 dev_pw_id)
 {
+	os_get_reltime(&wpa_s->wps_pin_start_time);
 	return wpas_wps_start_dev_pw(wpa_s, NULL, bssid, pin, p2p_group,
 				     dev_pw_id, NULL, NULL, 0, 0);
 }
@@ -1210,6 +1254,7 @@
 	} else if (wpa_s->wpa_state >= WPA_ASSOCIATED) {
 		wpa_printf(MSG_DEBUG, "WPS: Cancel operation - "
 			   "deauthenticate");
+		wpa_s->own_disconnect_req = 1;
 		wpa_supplicant_deauthenticate(wpa_s,
 					      WLAN_REASON_DEAUTH_LEAVING);
 		wpas_clear_wps(wpa_s);
@@ -1235,6 +1280,13 @@
 	char *pos, *end;
 	int res;
 
+#ifdef CONFIG_AP
+	if (wpa_s->ap_iface) {
+		wpa_printf(MSG_DEBUG,
+			   "WPS: Reject request to start Registrar(as station) operation while AP mode is enabled");
+		return -1;
+	}
+#endif /* CONFIG_AP */
 	if (!pin)
 		return -1;
 	wpas_clear_wps(wpa_s);
@@ -1245,7 +1297,7 @@
 	pos = val;
 	end = pos + sizeof(val);
 	res = os_snprintf(pos, end - pos, "\"pin=%s", pin);
-	if (res < 0 || res >= end - pos)
+	if (os_snprintf_error(end - pos, res))
 		return -1;
 	pos += res;
 	if (settings) {
@@ -1253,12 +1305,12 @@
 				  "new_encr=%s new_key=%s",
 				  settings->ssid_hex, settings->auth,
 				  settings->encr, settings->key_hex);
-		if (res < 0 || res >= end - pos)
+		if (os_snprintf_error(end - pos, res))
 			return -1;
 		pos += res;
 	}
 	res = os_snprintf(pos, end - pos, "\"");
-	if (res < 0 || res >= end - pos)
+	if (os_snprintf_error(end - pos, res))
 		return -1;
 	if (wpa_config_set(ssid, "phase1", val, 0) < 0)
 		return -1;
@@ -1309,7 +1361,7 @@
 			  dev->model_number, dev->serial_number,
 			  wps_dev_type_bin2str(dev->pri_dev_type, devtype,
 					       sizeof(devtype)));
-	if (len > 0 && len < (int) sizeof(txt))
+	if (!os_snprintf_error(sizeof(txt), len))
 		wpa_printf(MSG_INFO, "%s", txt);
 }
 
@@ -1450,6 +1502,8 @@
 				wps->dev.rf_bands |= WPS_RF_24GHZ;
 			else if (modes[m].mode == HOSTAPD_MODE_IEEE80211A)
 				wps->dev.rf_bands |= WPS_RF_50GHZ;
+			else if (modes[m].mode == HOSTAPD_MODE_IEEE80211AD)
+				wps->dev.rf_bands |= WPS_RF_60GHZ;
 		}
 	}
 	if (wps->dev.rf_bands == 0) {
@@ -1572,9 +1626,15 @@
 		 * external Registrar.
 		 */
 		if (!wps_is_addr_authorized(wps_ie, wpa_s->own_addr, 1)) {
-			if (wpa_s->scan_runs < WPS_PIN_SCAN_IGNORE_SEL_REG) {
-				wpa_printf(MSG_DEBUG, "   skip - WPS AP "
-					   "without active PIN Registrar");
+			struct os_reltime age;
+
+			os_reltime_age(&wpa_s->wps_pin_start_time, &age);
+
+			if (wpa_s->scan_runs < WPS_PIN_SCAN_IGNORE_SEL_REG ||
+			    age.sec < WPS_PIN_TIME_IGNORE_SEL_REG) {
+				wpa_printf(MSG_DEBUG,
+					   "   skip - WPS AP without active PIN Registrar (scan_runs=%d age=%d)",
+					   wpa_s->scan_runs, (int) age.sec);
 				wpabuf_free(wps_ie);
 				return 0;
 			}
@@ -1657,10 +1717,10 @@
 int wpas_wps_scan_pbc_overlap(struct wpa_supplicant *wpa_s,
 			      struct wpa_bss *selected, struct wpa_ssid *ssid)
 {
-	const u8 *sel_uuid, *uuid;
+	const u8 *sel_uuid;
 	struct wpabuf *wps_ie;
 	int ret = 0;
-	struct wpa_bss *bss;
+	size_t i;
 
 	if (!eap_is_wps_pbc_enrollee(&ssid->eap))
 		return 0;
@@ -1681,36 +1741,28 @@
 		sel_uuid = NULL;
 	}
 
-	dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
-		struct wpabuf *ie;
-		if (bss == selected)
+	for (i = 0; i < wpa_s->num_wps_ap; i++) {
+		struct wps_ap_info *ap = &wpa_s->wps_ap[i];
+
+		if (!ap->pbc_active ||
+		    os_memcmp(selected->bssid, ap->bssid, ETH_ALEN) == 0)
 			continue;
-		ie = wpa_bss_get_vendor_ie_multi(bss, WPS_IE_VENDOR_TYPE);
-		if (!ie)
-			continue;
-		if (!wps_is_selected_pbc_registrar(ie)) {
-			wpabuf_free(ie);
-			continue;
-		}
+
 		wpa_printf(MSG_DEBUG, "WPS: Another BSS in active PBC mode: "
-			   MACSTR, MAC2STR(bss->bssid));
-		uuid = wps_get_uuid_e(ie);
+			   MACSTR, MAC2STR(ap->bssid));
 		wpa_hexdump(MSG_DEBUG, "WPS: UUID of the other BSS",
-			    uuid, UUID_LEN);
-		if (sel_uuid == NULL || uuid == NULL ||
-		    os_memcmp(sel_uuid, uuid, UUID_LEN) != 0) {
+			    ap->uuid, UUID_LEN);
+		if (sel_uuid == NULL ||
+		    os_memcmp(sel_uuid, ap->uuid, UUID_LEN) != 0) {
 			ret = 1; /* PBC overlap */
 			wpa_msg(wpa_s, MSG_INFO, "WPS: PBC overlap detected: "
 				MACSTR " and " MACSTR,
 				MAC2STR(selected->bssid),
-				MAC2STR(bss->bssid));
-			wpabuf_free(ie);
+				MAC2STR(ap->bssid));
 			break;
 		}
 
 		/* TODO: verify that this is reasonable dual-band situation */
-
-		wpabuf_free(ie);
 	}
 
 	wpabuf_free(wps_ie);
@@ -1800,13 +1852,12 @@
 }
 
 
-int wpas_wps_er_stop(struct wpa_supplicant *wpa_s)
+void wpas_wps_er_stop(struct wpa_supplicant *wpa_s)
 {
 #ifdef CONFIG_WPS_ER
 	wps_er_deinit(wpa_s->wps_er, NULL, NULL);
 	wpa_s->wps_er = NULL;
 #endif /* CONFIG_WPS_ER */
-	return 0;
 }
 
 
@@ -1870,7 +1921,7 @@
 				    struct wps_credential *cred)
 {
 	os_memset(cred, 0, sizeof(*cred));
-	if (ssid->ssid_len > 32)
+	if (ssid->ssid_len > SSID_MAX_LEN)
 		return -1;
 	os_memcpy(cred->ssid, ssid->ssid, ssid->ssid_len);
 	cred->ssid_len = ssid->ssid_len;
@@ -1907,6 +1958,7 @@
 	u8 addr[ETH_ALEN], *use_addr = NULL;
 	struct wpa_ssid *ssid;
 	struct wps_credential cred;
+	int ret;
 
 	if (uuid_str2bin(uuid, u) == 0)
 		use_uuid = u;
@@ -1920,7 +1972,9 @@
 
 	if (wpas_wps_network_to_cred(ssid, &cred) < 0)
 		return -1;
-	return wps_er_set_config(wpa_s->wps_er, use_uuid, use_addr, &cred);
+	ret = wps_er_set_config(wpa_s->wps_er, use_uuid, use_addr, &cred);
+	os_memset(&cred, 0, sizeof(cred));
+	return ret;
 }
 
 
@@ -2539,6 +2593,10 @@
 			 (attr.rf_bands == NULL ||
 			  *attr.rf_bands & WPS_RF_50GHZ))
 			freq = 5000 + 5 * chan;
+		else if (chan >= 1 && chan <= 4 &&
+			 (attr.rf_bands == NULL ||
+			  *attr.rf_bands & WPS_RF_60GHZ))
+			freq = 56160 + 2160 * chan;
 
 		if (freq) {
 			wpa_printf(MSG_DEBUG,
@@ -2728,7 +2786,8 @@
 	struct wpabuf *wps;
 	enum wps_ap_info_type type;
 	struct wps_ap_info *ap;
-	int r;
+	int r, pbc_active;
+	const u8 *uuid;
 
 	if (wpa_scan_get_vendor_ie(res, WPS_IE_VENDOR_TYPE) == NULL)
 		return;
@@ -2745,7 +2804,8 @@
 	else
 		type = WPS_AP_NOT_SEL_REG;
 
-	wpabuf_free(wps);
+	uuid = wps_get_uuid_e(wps);
+	pbc_active = wps_is_selected_pbc_registrar(wps);
 
 	ap = wpas_wps_get_ap_info(wpa_s, res->bssid);
 	if (ap) {
@@ -2757,13 +2817,16 @@
 			if (type != WPS_AP_NOT_SEL_REG)
 				wpa_blacklist_del(wpa_s, ap->bssid);
 		}
-		return;
+		ap->pbc_active = pbc_active;
+		if (uuid)
+			os_memcpy(ap->uuid, uuid, WPS_UUID_LEN);
+		goto out;
 	}
 
 	ap = os_realloc_array(wpa_s->wps_ap, wpa_s->num_wps_ap + 1,
 			      sizeof(struct wps_ap_info));
 	if (ap == NULL)
-		return;
+		goto out;
 
 	wpa_s->wps_ap = ap;
 	ap = &wpa_s->wps_ap[wpa_s->num_wps_ap];
@@ -2772,8 +2835,14 @@
 	os_memset(ap, 0, sizeof(*ap));
 	os_memcpy(ap->bssid, res->bssid, ETH_ALEN);
 	ap->type = type;
+	ap->pbc_active = pbc_active;
+	if (uuid)
+		os_memcpy(ap->uuid, uuid, WPS_UUID_LEN);
 	wpa_printf(MSG_DEBUG, "WPS: AP " MACSTR " type %d added",
 		   MAC2STR(ap->bssid), ap->type);
+
+out:
+	wpabuf_free(wps);
 }
 
 
diff --git a/wpa_supplicant/wps_supplicant.h b/wpa_supplicant/wps_supplicant.h
index 2263512..683bd50 100644
--- a/wpa_supplicant/wps_supplicant.h
+++ b/wpa_supplicant/wps_supplicant.h
@@ -47,7 +47,7 @@
 int wpas_wps_scan_result_text(const u8 *ies, size_t ies_len, char *pos,
 			      char *end);
 int wpas_wps_er_start(struct wpa_supplicant *wpa_s, const char *filter);
-int wpas_wps_er_stop(struct wpa_supplicant *wpa_s);
+void wpas_wps_er_stop(struct wpa_supplicant *wpa_s);
 int wpas_wps_er_add_pin(struct wpa_supplicant *wpa_s, const u8 *addr,
 			const char *uuid, const char *pin);
 int wpas_wps_er_pbc(struct wpa_supplicant *wpa_s, const char *uuid);