DO NOT MERGE Merge remote-tracking branch 'goog/master' into mm-wireless-internal-dev

Pull in WiFi changes from goog/master to partner branch.

Merge from d7ff03d48f825360eec2a371e3361306f2fd721b
diff --git a/CONTRIBUTIONS b/CONTRIBUTIONS
index ca09bae..caef362 100644
--- a/CONTRIBUTIONS
+++ b/CONTRIBUTIONS
@@ -29,6 +29,34 @@
 unfortunately be accepted.
 
 
+The preferred method of submitting the contribution to the project is by
+email to the hostap mailing list:
+hostap@lists.infradead.org
+Note that the list may require subscription before accepting message
+without moderation. You can subscribe to the list at this address:
+http://lists.infradead.org/mailman/listinfo/hostap
+
+The message should contain an inlined patch against the current
+development branch (i.e., the master branch of
+git://w1.fi/hostap.git). Please make sure the software you use for
+sending the patch does not corrupt whitespace. If that cannot be fixed
+for some reason, it is better to include an attached version of the
+patch file than just send a whitespace damaged version in the message
+body.
+
+The patches should be separate logical changes rather than doing
+everything in a single patch. In other words, please keep cleanup, new
+features, and bug fixes all in their own patches. Each patch needs a
+commit log that describes the changes (what the changes fix, what
+functionality is added, why the changes are useful, etc.).
+
+Please try to follow the coding style used in the project.
+
+In general, the best way of generating a suitable formatted patch file
+is by committing the changes to a cloned git repository and using git
+format-patch. The patch can then be sent, e.g., with git send-email.
+
+
 History of license and contributions terms
 ------------------------------------------
 
diff --git a/hostapd/Android.mk b/hostapd/Android.mk
index 52d4cfe..a550866 100644
--- a/hostapd/Android.mk
+++ b/hostapd/Android.mk
@@ -126,6 +126,15 @@
 endif
 
 OBJS += src/utils/eloop.c
+
+ifdef CONFIG_ELOOP_POLL
+L_CFLAGS += -DCONFIG_ELOOP_POLL
+endif
+
+ifdef CONFIG_ELOOP_EPOLL
+L_CFLAGS += -DCONFIG_ELOOP_EPOLL
+endif
+
 OBJS += src/utils/common.c
 OBJS += src/utils/wpa_debug.c
 OBJS += src/utils/wpabuf.c
@@ -252,6 +261,22 @@
 L_CFLAGS += -DCONFIG_IEEE80211AC
 endif
 
+ifdef CONFIG_FST
+L_CFLAGS += -DCONFIG_FST
+OBJS += src/fst/fst.c
+OBJS += src/fst/fst_group.c
+OBJS += src/fst/fst_iface.c
+OBJS += src/fst/fst_session.c
+OBJS += src/fst/fst_ctrl_aux.c
+ifdef CONFIG_FST_TEST
+L_CFLAGS += -DCONFIG_FST_TEST
+endif
+ifndef CONFIG_NO_CTRL_IFACE
+OBJS += src/fst/fst_ctrl_iface.c
+endif
+endif
+
+
 include $(LOCAL_PATH)/src/drivers/drivers.mk
 
 OBJS += $(DRV_AP_OBJS)
@@ -529,6 +554,7 @@
 ifeq ($(CONFIG_TLS), openssl)
 ifdef TLS_FUNCS
 OBJS += src/crypto/tls_openssl.c
+OBJS += src/crypto/tls_openssl_ocsp.c
 LIBS += -lssl
 endif
 OBJS += src/crypto/crypto_openssl.c
@@ -536,6 +562,8 @@
 ifdef NEED_FIPS186_2_PRF
 OBJS += src/crypto/fips_prf_openssl.c
 endif
+NEED_SHA256=y
+NEED_TLS_PRF_SHA256=y
 LIBS += -lcrypto
 LIBS_h += -lcrypto
 endif
@@ -623,6 +651,8 @@
 CONFIG_INTERNAL_MD4=y
 CONFIG_INTERNAL_MD5=y
 CONFIG_INTERNAL_SHA256=y
+CONFIG_INTERNAL_SHA384=y
+CONFIG_INTERNAL_SHA512=y
 CONFIG_INTERNAL_RC4=y
 CONFIG_INTERNAL_DH_GROUP5=y
 endif
@@ -751,11 +781,17 @@
 endif
 endif
 
+ifdef CONFIG_NO_RC4
+L_CFLAGS += -DCONFIG_NO_RC4
+endif
+
 ifdef NEED_RC4
 ifdef CONFIG_INTERNAL_RC4
+ifndef CONFIG_NO_RC4
 OBJS += src/crypto/rc4.c
 endif
 endif
+endif
 
 ifdef NEED_SHA256
 L_CFLAGS += -DCONFIG_SHA256
@@ -772,6 +808,17 @@
 endif
 ifdef NEED_SHA384
 L_CFLAGS += -DCONFIG_SHA384
+OBJS += src/crypto/sha384-prf.c
+endif
+
+ifdef CONFIG_INTERNAL_SHA384
+L_CFLAGS += -DCONFIG_INTERNAL_SHA384
+OBJS += src/crypto/sha384-internal.c
+endif
+
+ifdef CONFIG_INTERNAL_SHA512
+L_CFLAGS += -DCONFIG_INTERNAL_SHA512
+OBJS += src/crypto/sha512-internal.c
 endif
 
 ifdef NEED_DH_GROUPS
diff --git a/hostapd/ChangeLog b/hostapd/ChangeLog
index e6f8c6a..af54e1e 100644
--- a/hostapd/ChangeLog
+++ b/hostapd/ChangeLog
@@ -1,5 +1,41 @@
 ChangeLog for hostapd
 
+2015-09-27 - v2.5
+	* fixed WPS UPnP vulnerability with HTTP chunked transfer encoding
+	  [http://w1.fi/security/2015-2/] (CVE-2015-4141)
+	* fixed WMM Action frame parser
+	  [http://w1.fi/security/2015-3/] (CVE-2015-4142)
+	* fixed EAP-pwd server missing payload length validation
+	  [http://w1.fi/security/2015-4/]
+	  (CVE-2015-4143, CVE-2015-4144, CVE-2015-4145)
+	* fixed validation of WPS and P2P NFC NDEF record payload length
+	  [http://w1.fi/security/2015-5/]
+	* nl80211:
+	  - fixed vendor command handling to check OUI properly
+	* fixed hlr_auc_gw build with OpenSSL
+	* hlr_auc_gw: allow Milenage RES length to be reduced
+	* disable HT for a station that does not support WMM/QoS
+	* added support for hashed password (NtHash) in EAP-pwd server
+	* fixed and extended dynamic VLAN cases
+	* added EAP-EKE server support for deriving Session-Id
+	* set Acct-Session-Id to a random value to make it more likely to be
+	  unique even if the device does not have a proper clock
+	* added more 2.4 GHz channels for 20/40 MHz HT co-ex scan
+	* modified SAE routines to be more robust and PWE generation to be
+	  stronger against timing attacks
+	* added support for Brainpool Elliptic Curves with SAE
+	* increases maximum value accepted for cwmin/cwmax
+	* added support for CCMP-256 and GCMP-256 as group ciphers with FT
+	* added Fast Session Transfer (FST) module
+	* removed optional fields from RSNE when using FT with PMF
+	  (workaround for interoperability issues with iOS 8.4)
+	* added EAP server support for TLS session resumption
+	* fixed key derivation for Suite B 192-bit AKM (this breaks
+	  compatibility with the earlier version)
+	* added mechanism to track unconnected stations and do minimal band
+	  steering
+	* number of small fixes
+
 2015-03-15 - v2.4
 	* allow OpenSSL cipher configuration to be set for internal EAP server
 	  (openssl_ciphers parameter)
diff --git a/hostapd/Makefile b/hostapd/Makefile
index d4fd36e..fa5435d 100644
--- a/hostapd/Makefile
+++ b/hostapd/Makefile
@@ -18,6 +18,16 @@
 
 -include .config
 
+ifndef CONFIG_NO_GITVER
+# Add VERSION_STR postfix for builds from a git repository
+ifeq ($(wildcard ../.git),../.git)
+GITVER := $(shell git describe --dirty=+)
+ifneq ($(GITVER),)
+CFLAGS += -DGIT_VERSION_STR_POSTFIX=\"-$(GITVER)\"
+endif
+endif
+endif
+
 ifdef CONFIG_TESTING_OPTIONS
 CFLAGS += -DCONFIG_TESTING_OPTIONS
 CONFIG_WPS_TESTING=y
@@ -107,6 +117,14 @@
 LIBS_n += -lrt
 endif
 
+ifdef CONFIG_ELOOP_POLL
+CFLAGS += -DCONFIG_ELOOP_POLL
+endif
+
+ifdef CONFIG_ELOOP_EPOLL
+CFLAGS += -DCONFIG_ELOOP_EPOLL
+endif
+
 OBJS += ../src/utils/common.o
 OBJS_c += ../src/utils/common.o
 OBJS += ../src/utils/wpa_debug.o
@@ -526,6 +544,7 @@
 ifeq ($(CONFIG_TLS), openssl)
 ifdef TLS_FUNCS
 OBJS += ../src/crypto/tls_openssl.o
+OBJS += ../src/crypto/tls_openssl_ocsp.o
 LIBS += -lssl
 endif
 OBJS += ../src/crypto/crypto_openssl.o
@@ -533,8 +552,14 @@
 ifdef NEED_FIPS186_2_PRF
 OBJS += ../src/crypto/fips_prf_openssl.o
 endif
+NEED_SHA256=y
+NEED_TLS_PRF_SHA256=y
 LIBS += -lcrypto
 LIBS_h += -lcrypto
+ifdef CONFIG_TLS_ADD_DL
+LIBS += -ldl
+LIBS_h += -ldl
+endif
 endif
 
 ifeq ($(CONFIG_TLS), gnutls)
@@ -620,6 +645,8 @@
 CONFIG_INTERNAL_MD4=y
 CONFIG_INTERNAL_MD5=y
 CONFIG_INTERNAL_SHA256=y
+CONFIG_INTERNAL_SHA384=y
+CONFIG_INTERNAL_SHA512=y
 CONFIG_INTERNAL_RC4=y
 CONFIG_INTERNAL_DH_GROUP5=y
 endif
@@ -747,11 +774,17 @@
 endif
 endif
 
+ifdef CONFIG_NO_RC4
+CFLAGS += -DCONFIG_NO_RC4
+endif
+
 ifdef NEED_RC4
 ifdef CONFIG_INTERNAL_RC4
+ifndef CONFIG_NO_RC4
 OBJS += ../src/crypto/rc4.o
 endif
 endif
+endif
 
 ifdef NEED_SHA256
 CFLAGS += -DCONFIG_SHA256
@@ -771,6 +804,17 @@
 endif
 ifdef NEED_SHA384
 CFLAGS += -DCONFIG_SHA384
+OBJS += ../src/crypto/sha384-prf.o
+endif
+
+ifdef CONFIG_INTERNAL_SHA384
+CFLAGS += -DCONFIG_INTERNAL_SHA384
+OBJS += ../src/crypto/sha384-internal.o
+endif
+
+ifdef CONFIG_INTERNAL_SHA512
+CFLAGS += -DCONFIG_INTERNAL_SHA512
+OBJS += ../src/crypto/sha512-internal.o
 endif
 
 ifdef NEED_DH_GROUPS
@@ -898,6 +942,21 @@
 LIBS_h += -lsqlite3
 endif
 
+ifdef CONFIG_FST
+CFLAGS += -DCONFIG_FST
+OBJS += ../src/fst/fst.o
+OBJS += ../src/fst/fst_group.o
+OBJS += ../src/fst/fst_iface.o
+OBJS += ../src/fst/fst_session.o
+OBJS += ../src/fst/fst_ctrl_aux.o
+ifdef CONFIG_FST_TEST
+CFLAGS += -DCONFIG_FST_TEST
+endif
+ifndef CONFIG_NO_CTRL_IFACE
+OBJS += ../src/fst/fst_ctrl_iface.o
+endif
+endif
+
 ALL=hostapd hostapd_cli
 
 all: verify_config $(ALL)
@@ -960,9 +1019,11 @@
 NOBJS += ../src/utils/common.o
 ifdef NEED_RC4
 ifdef CONFIG_INTERNAL_RC4
+ifndef CONFIG_NO_RC4
 NOBJS += ../src/crypto/rc4.o
 endif
 endif
+endif
 ifdef CONFIG_INTERNAL_MD5
 NOBJS += ../src/crypto/md5-internal.o
 endif
diff --git a/hostapd/android.config b/hostapd/android.config
index 938aa54..c2d8b22 100644
--- a/hostapd/android.config
+++ b/hostapd/android.config
@@ -25,6 +25,9 @@
 #LIBS += -L$(LIBNL)/lib
 CONFIG_LIBNL20=y
 
+# QCA vendor extensions to nl80211
+CONFIG_DRIVER_NL80211_QCA=y
+
 # Driver interface for FreeBSD net80211 layer (e.g., Atheros driver)
 #CONFIG_DRIVER_BSD=y
 #CFLAGS += -I/usr/local/include
@@ -180,5 +183,14 @@
 #LIBS_p += -lbfd -liberty -lz
 #LIBS_c += -lbfd -liberty -lz
 
+# Should we use poll instead of select? Select is used by default.
+#CONFIG_ELOOP_POLL=y
+
+# Should we use epoll instead of select? Select is used by default.
+#CONFIG_ELOOP_EPOLL=y
+
 # Enable AP
 CONFIG_AP=y
+
+# Enable Fast Session Transfer (FST)
+#CONFIG_FST=y
diff --git a/hostapd/config_file.c b/hostapd/config_file.c
index 49f8320..bf42466 100644
--- a/hostapd/config_file.c
+++ b/hostapd/config_file.c
@@ -912,11 +912,11 @@
 	IEEE80211_TX_QUEUE_DATA3 = 3 /* used for EDCA AC_BK data */
 };
 
-static int hostapd_config_tx_queue(struct hostapd_config *conf, char *name,
-				   char *val)
+static int hostapd_config_tx_queue(struct hostapd_config *conf,
+				   const char *name, const char *val)
 {
 	int num;
-	char *pos;
+	const char *pos;
 	struct hostapd_tx_queue_params *queue;
 
 	/* skip 'tx_queue_' prefix */
@@ -1160,9 +1160,21 @@
 	if (os_strstr(capab, "[BF-ANTENNA-2]") &&
 	    (conf->vht_capab & VHT_CAP_SU_BEAMFORMEE_CAPABLE))
 		conf->vht_capab |= (1 << VHT_CAP_BEAMFORMEE_STS_OFFSET);
+	if (os_strstr(capab, "[BF-ANTENNA-3]") &&
+	    (conf->vht_capab & VHT_CAP_SU_BEAMFORMEE_CAPABLE))
+		conf->vht_capab |= (2 << VHT_CAP_BEAMFORMEE_STS_OFFSET);
+	if (os_strstr(capab, "[BF-ANTENNA-4]") &&
+	    (conf->vht_capab & VHT_CAP_SU_BEAMFORMEE_CAPABLE))
+		conf->vht_capab |= (3 << VHT_CAP_BEAMFORMEE_STS_OFFSET);
 	if (os_strstr(capab, "[SOUNDING-DIMENSION-2]") &&
 	    (conf->vht_capab & VHT_CAP_SU_BEAMFORMER_CAPABLE))
 		conf->vht_capab |= (1 << VHT_CAP_SOUNDING_DIMENSION_OFFSET);
+	if (os_strstr(capab, "[SOUNDING-DIMENSION-3]") &&
+	    (conf->vht_capab & VHT_CAP_SU_BEAMFORMER_CAPABLE))
+		conf->vht_capab |= (2 << VHT_CAP_SOUNDING_DIMENSION_OFFSET);
+	if (os_strstr(capab, "[SOUNDING-DIMENSION-4]") &&
+	    (conf->vht_capab & VHT_CAP_SU_BEAMFORMER_CAPABLE))
+		conf->vht_capab |= (3 << VHT_CAP_SOUNDING_DIMENSION_OFFSET);
 	if (os_strstr(capab, "[MU-BEAMFORMER]"))
 		conf->vht_capab |= VHT_CAP_MU_BEAMFORMER_CAPABLE;
 	if (os_strstr(capab, "[VHT-TXOP-PS]"))
@@ -1507,6 +1519,54 @@
 }
 
 
+static int parse_anqp_elem(struct hostapd_bss_config *bss, char *buf, int line)
+{
+	char *delim;
+	u16 infoid;
+	size_t len;
+	struct wpabuf *payload;
+	struct anqp_element *elem;
+
+	delim = os_strchr(buf, ':');
+	if (!delim)
+		return -1;
+	delim++;
+	infoid = atoi(buf);
+	len = os_strlen(delim);
+	if (len & 1)
+		return -1;
+	len /= 2;
+	payload = wpabuf_alloc(len);
+	if (!payload)
+		return -1;
+	if (hexstr2bin(delim, wpabuf_put(payload, len), len) < 0) {
+		wpabuf_free(payload);
+		return -1;
+	}
+
+	dl_list_for_each(elem, &bss->anqp_elem, struct anqp_element, list) {
+		if (elem->infoid == infoid) {
+			/* Update existing entry */
+			wpabuf_free(elem->payload);
+			elem->payload = payload;
+			return 0;
+		}
+	}
+
+	/* Add a new entry */
+	elem = os_zalloc(sizeof(*elem));
+	if (!elem) {
+		wpabuf_free(payload);
+		return -1;
+	}
+	elem->infoid = infoid;
+	elem->payload = payload;
+	dl_list_add(&bss->anqp_elem, &elem->list);
+
+	return 0;
+}
+
+
 static int parse_qos_map_set(struct hostapd_bss_config *bss,
 			     char *buf, int line)
 {
@@ -1924,7 +1984,7 @@
 
 static int hostapd_config_fill(struct hostapd_config *conf,
 			       struct hostapd_bss_config *bss,
-			       char *buf, char *pos, int line)
+			       const char *buf, char *pos, int line)
 {
 	if (os_strcmp(buf, "interface") == 0) {
 		os_strlcpy(conf->bss[0]->iface, pos,
@@ -2067,6 +2127,8 @@
 		bss->private_key_passwd = os_strdup(pos);
 	} else if (os_strcmp(buf, "check_crl") == 0) {
 		bss->check_crl = atoi(pos);
+	} else if (os_strcmp(buf, "tls_session_lifetime") == 0) {
+		bss->tls_session_lifetime = atoi(pos);
 	} else if (os_strcmp(buf, "ocsp_stapling_response") == 0) {
 		os_free(bss->ocsp_stapling_response);
 		bss->ocsp_stapling_response = os_strdup(pos);
@@ -2125,6 +2187,8 @@
 	} else if (os_strcmp(buf, "eap_sim_db") == 0) {
 		os_free(bss->eap_sim_db);
 		bss->eap_sim_db = os_strdup(pos);
+	} else if (os_strcmp(buf, "eap_sim_db_timeout") == 0) {
+		bss->eap_sim_db_timeout = atoi(pos);
 	} else if (os_strcmp(buf, "eap_sim_aka_result_ind") == 0) {
 		bss->eap_sim_aka_result_ind = atoi(pos);
 #endif /* EAP_SERVER_SIM */
@@ -2630,7 +2694,7 @@
 		}
 	} else if (os_strcmp(buf, "rts_threshold") == 0) {
 		conf->rts_threshold = atoi(pos);
-		if (conf->rts_threshold < 0 || conf->rts_threshold > 2347) {
+		if (conf->rts_threshold < -1 || conf->rts_threshold > 65535) {
 			wpa_printf(MSG_ERROR,
 				   "Line %d: invalid rts_threshold %d",
 				   line, conf->rts_threshold);
@@ -2638,8 +2702,10 @@
 		}
 	} else if (os_strcmp(buf, "fragm_threshold") == 0) {
 		conf->fragm_threshold = atoi(pos);
-		if (conf->fragm_threshold < 256 ||
-		    conf->fragm_threshold > 2346) {
+		if (conf->fragm_threshold == -1) {
+			/* allow a value of -1 */
+		} else if (conf->fragm_threshold < 256 ||
+			   conf->fragm_threshold > 2346) {
 			wpa_printf(MSG_ERROR,
 				   "Line %d: invalid fragm_threshold %d",
 				   line, conf->fragm_threshold);
@@ -2672,6 +2738,8 @@
 			conf->preamble = LONG_PREAMBLE;
 	} else if (os_strcmp(buf, "ignore_broadcast_ssid") == 0) {
 		bss->ignore_broadcast_ssid = atoi(pos);
+	} else if (os_strcmp(buf, "no_probe_resp_if_max_sta") == 0) {
+		bss->no_probe_resp_if_max_sta = atoi(pos);
 	} else if (os_strcmp(buf, "wep_default_key") == 0) {
 		bss->ssid.wep.idx = atoi(pos);
 		if (bss->ssid.wep.idx > 3) {
@@ -3122,6 +3190,9 @@
 	} else if (os_strcmp(buf, "nai_realm") == 0) {
 		if (parse_nai_realm(bss, pos, line) < 0)
 			return 1;
+	} else if (os_strcmp(buf, "anqp_elem") == 0) {
+		if (parse_anqp_elem(bss, pos, line) < 0)
+			return 1;
 	} else if (os_strcmp(buf, "gas_frag_limit") == 0) {
 		bss->gas_frag_limit = atoi(pos);
 	} else if (os_strcmp(buf, "gas_comeback_delay") == 0) {
@@ -3235,6 +3306,8 @@
 	PARSE_TEST_PROBABILITY(ignore_assoc_probability)
 	PARSE_TEST_PROBABILITY(ignore_reassoc_probability)
 	PARSE_TEST_PROBABILITY(corrupt_gtk_rekey_mic_probability)
+	} else if (os_strcmp(buf, "ecsa_ie_only") == 0) {
+		conf->ecsa_ie_only = atoi(pos);
 	} else if (os_strcmp(buf, "bss_load_test") == 0) {
 		WPA_PUT_LE16(bss->bss_load_test, atoi(pos));
 		pos = os_strchr(pos, ':');
@@ -3256,6 +3329,24 @@
 		bss->bss_load_test_set = 1;
 	} else if (os_strcmp(buf, "radio_measurements") == 0) {
 		bss->radio_measurements = atoi(pos);
+	} else if (os_strcmp(buf, "own_ie_override") == 0) {
+		struct wpabuf *tmp;
+		size_t len = os_strlen(pos) / 2;
+
+		tmp = wpabuf_alloc(len);
+		if (!tmp)
+			return 1;
+
+		if (hexstr2bin(pos, wpabuf_put(tmp, len), len)) {
+			wpabuf_free(tmp);
+			wpa_printf(MSG_ERROR,
+				   "Line %d: Invalid own_ie_override '%s'",
+				   line, pos);
+			return 1;
+		}
+
+		wpabuf_free(bss->own_ie_override);
+		bss->own_ie_override = tmp;
 #endif /* CONFIG_TESTING_OPTIONS */
 	} else if (os_strcmp(buf, "vendor_elements") == 0) {
 		struct wpabuf *elems;
@@ -3309,6 +3400,74 @@
 	} else if (os_strcmp(buf, "wowlan_triggers") == 0) {
 		os_free(bss->wowlan_triggers);
 		bss->wowlan_triggers = os_strdup(pos);
+#ifdef CONFIG_FST
+	} else if (os_strcmp(buf, "fst_group_id") == 0) {
+		size_t len = os_strlen(pos);
+
+		if (!len || len >= sizeof(conf->fst_cfg.group_id)) {
+			wpa_printf(MSG_ERROR,
+				   "Line %d: Invalid fst_group_id value '%s'",
+				   line, pos);
+			return 1;
+		}
+
+		if (conf->fst_cfg.group_id[0]) {
+			wpa_printf(MSG_ERROR,
+				   "Line %d: Duplicate fst_group value '%s'",
+				   line, pos);
+			return 1;
+		}
+
+		os_strlcpy(conf->fst_cfg.group_id, pos,
+			   sizeof(conf->fst_cfg.group_id));
+	} else if (os_strcmp(buf, "fst_priority") == 0) {
+		char *endp;
+		long int val;
+
+		if (!*pos) {
+			wpa_printf(MSG_ERROR,
+				   "Line %d: fst_priority value not supplied (expected 1..%u)",
+				   line, FST_MAX_PRIO_VALUE);
+			return -1;
+		}
+
+		val = strtol(pos, &endp, 0);
+		if (*endp || val < 1 || val > FST_MAX_PRIO_VALUE) {
+			wpa_printf(MSG_ERROR,
+				   "Line %d: Invalid fst_priority %ld (%s) (expected 1..%u)",
+				   line, val, pos, FST_MAX_PRIO_VALUE);
+			return 1;
+		}
+		conf->fst_cfg.priority = (u8) val;
+	} else if (os_strcmp(buf, "fst_llt") == 0) {
+		char *endp;
+		long int val;
+
+		if (!*pos) {
+			wpa_printf(MSG_ERROR,
+				   "Line %d: fst_llt value not supplied (expected 1..%u)",
+				   line, FST_MAX_LLT_MS);
+			return -1;
+		}
+		val = strtol(pos, &endp, 0);
+		if (*endp || val < 1 || val > FST_MAX_LLT_MS) {
+			wpa_printf(MSG_ERROR,
+				   "Line %d: Invalid fst_llt %ld (%s) (expected 1..%u)",
+				   line, val, pos, FST_MAX_LLT_MS);
+			return 1;
+		}
+		conf->fst_cfg.llt = (u32) val;
+#endif /* CONFIG_FST */
+	} else if (os_strcmp(buf, "track_sta_max_num") == 0) {
+		conf->track_sta_max_num = atoi(pos);
+	} else if (os_strcmp(buf, "track_sta_max_age") == 0) {
+		conf->track_sta_max_age = atoi(pos);
+	} else if (os_strcmp(buf, "no_probe_resp_if_seen_on") == 0) {
+		os_free(bss->no_probe_resp_if_seen_on);
+		bss->no_probe_resp_if_seen_on = os_strdup(pos);
+	} else if (os_strcmp(buf, "no_auth_if_seen_on") == 0) {
+		os_free(bss->no_auth_if_seen_on);
+		bss->no_auth_if_seen_on = os_strdup(pos);
 	} else {
 		wpa_printf(MSG_ERROR,
 			   "Line %d: unknown configuration item '%s'",
@@ -3329,7 +3488,7 @@
 {
 	struct hostapd_config *conf;
 	FILE *f;
-	char buf[512], *pos;
+	char buf[4096], *pos;
 	int line = 0;
 	int errors = 0;
 	size_t i;
@@ -3411,7 +3570,8 @@
 
 
 int hostapd_set_iface(struct hostapd_config *conf,
-		      struct hostapd_bss_config *bss, char *field, char *value)
+		      struct hostapd_bss_config *bss, const char *field,
+		      char *value)
 {
 	int errors;
 	size_t i;
diff --git a/hostapd/config_file.h b/hostapd/config_file.h
index fba57b8..c98bdb6 100644
--- a/hostapd/config_file.h
+++ b/hostapd/config_file.h
@@ -11,7 +11,7 @@
 
 struct hostapd_config * hostapd_config_read(const char *fname);
 int hostapd_set_iface(struct hostapd_config *conf,
-		      struct hostapd_bss_config *bss, char *field,
+		      struct hostapd_bss_config *bss, const char *field,
 		      char *value);
 
 #endif /* CONFIG_FILE_H */
diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
index c606f2c..cb6fb17 100644
--- a/hostapd/ctrl_iface.c
+++ b/hostapd/ctrl_iface.c
@@ -25,6 +25,7 @@
 #include "common/ieee802_11_defs.h"
 #include "crypto/tls.h"
 #include "drivers/driver.h"
+#include "eapol_auth/eapol_auth_sm.h"
 #include "radius/radius_client.h"
 #include "radius/radius_server.h"
 #include "l2_packet/l2_packet.h"
@@ -43,10 +44,13 @@
 #include "ap/beacon.h"
 #include "wps/wps_defs.h"
 #include "wps/wps.h"
+#include "fst/fst_ctrl_iface.h"
 #include "config_file.h"
 #include "ctrl_iface.h"
 
 
+#define HOSTAPD_CLI_DUP_VALUE_MAX_LEN 256
+
 struct wpa_ctrl_dst {
 	struct wpa_ctrl_dst *next;
 	struct sockaddr_un addr;
@@ -1056,6 +1060,97 @@
 #endif /* CONFIG_WNM */
 
 
+static int hostapd_ctrl_iface_get_key_mgmt(struct hostapd_data *hapd,
+					   char *buf, size_t buflen)
+{
+	int ret = 0;
+	char *pos, *end;
+
+	pos = buf;
+	end = buf + buflen;
+
+	WPA_ASSERT(hapd->conf->wpa_key_mgmt);
+
+	if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_PSK) {
+		ret = os_snprintf(pos, end - pos, "WPA-PSK ");
+		if (os_snprintf_error(end - pos, ret))
+			return pos - buf;
+		pos += ret;
+	}
+	if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X) {
+		ret = os_snprintf(pos, end - pos, "WPA-EAP ");
+		if (os_snprintf_error(end - pos, ret))
+			return pos - buf;
+		pos += ret;
+	}
+#ifdef CONFIG_IEEE80211R
+	if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_PSK) {
+		ret = os_snprintf(pos, end - pos, "FT-PSK ");
+		if (os_snprintf_error(end - pos, ret))
+			return pos - buf;
+		pos += ret;
+	}
+	if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X) {
+		ret = os_snprintf(pos, end - pos, "FT-EAP ");
+		if (os_snprintf_error(end - pos, ret))
+			return pos - buf;
+		pos += ret;
+	}
+#ifdef CONFIG_SAE
+	if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_SAE) {
+		ret = os_snprintf(pos, end - pos, "FT-SAE ");
+		if (os_snprintf_error(end - pos, ret))
+			return pos - buf;
+		pos += ret;
+	}
+#endif /* CONFIG_SAE */
+#endif /* CONFIG_IEEE80211R */
+#ifdef CONFIG_IEEE80211W
+	if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_PSK_SHA256) {
+		ret = os_snprintf(pos, end - pos, "WPA-PSK-SHA256 ");
+		if (os_snprintf_error(end - pos, ret))
+			return pos - buf;
+		pos += ret;
+	}
+	if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X_SHA256) {
+		ret = os_snprintf(pos, end - pos, "WPA-EAP-SHA256 ");
+		if (os_snprintf_error(end - pos, ret))
+			return pos - buf;
+		pos += ret;
+	}
+#endif /* CONFIG_IEEE80211W */
+#ifdef CONFIG_SAE
+	if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_SAE) {
+		ret = os_snprintf(pos, end - pos, "SAE ");
+		if (os_snprintf_error(end - pos, ret))
+			return pos - buf;
+		pos += ret;
+	}
+#endif /* CONFIG_SAE */
+	if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B) {
+		ret = os_snprintf(pos, end - pos, "WPA-EAP-SUITE-B ");
+		if (os_snprintf_error(end - pos, ret))
+			return pos - buf;
+		pos += ret;
+	}
+	if (hapd->conf->wpa_key_mgmt &
+	    WPA_KEY_MGMT_IEEE8021X_SUITE_B_192) {
+		ret = os_snprintf(pos, end - pos,
+				  "WPA-EAP-SUITE-B-192 ");
+		if (os_snprintf_error(end - pos, ret))
+			return pos - buf;
+		pos += ret;
+	}
+
+	if (pos > buf && *(pos - 1) == ' ') {
+		*(pos - 1) = '\0';
+		pos--;
+	}
+
+	return pos - buf;
+}
+
+
 static int hostapd_ctrl_iface_get_config(struct hostapd_data *hapd,
 					 char *buf, size_t buflen)
 {
@@ -1105,82 +1200,20 @@
 	}
 #endif /* CONFIG_WPS */
 
+	if (hapd->conf->wpa) {
+		ret = os_snprintf(pos, end - pos, "wpa=%d\n", hapd->conf->wpa);
+		if (os_snprintf_error(end - pos, ret))
+			return pos - buf;
+		pos += ret;
+	}
+
 	if (hapd->conf->wpa && hapd->conf->wpa_key_mgmt) {
 		ret = os_snprintf(pos, end - pos, "key_mgmt=");
 		if (os_snprintf_error(end - pos, ret))
 			return pos - buf;
 		pos += ret;
 
-		if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_PSK) {
-			ret = os_snprintf(pos, end - pos, "WPA-PSK ");
-			if (os_snprintf_error(end - pos, ret))
-				return pos - buf;
-			pos += ret;
-		}
-		if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X) {
-			ret = os_snprintf(pos, end - pos, "WPA-EAP ");
-			if (os_snprintf_error(end - pos, ret))
-				return pos - buf;
-			pos += ret;
-		}
-#ifdef CONFIG_IEEE80211R
-		if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_PSK) {
-			ret = os_snprintf(pos, end - pos, "FT-PSK ");
-			if (os_snprintf_error(end - pos, ret))
-				return pos - buf;
-			pos += ret;
-		}
-		if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X) {
-			ret = os_snprintf(pos, end - pos, "FT-EAP ");
-			if (os_snprintf_error(end - pos, ret))
-				return pos - buf;
-			pos += ret;
-		}
-#ifdef CONFIG_SAE
-		if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_SAE) {
-			ret = os_snprintf(pos, end - pos, "FT-SAE ");
-			if (os_snprintf_error(end - pos, ret))
-				return pos - buf;
-			pos += ret;
-		}
-#endif /* CONFIG_SAE */
-#endif /* CONFIG_IEEE80211R */
-#ifdef CONFIG_IEEE80211W
-		if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_PSK_SHA256) {
-			ret = os_snprintf(pos, end - pos, "WPA-PSK-SHA256 ");
-			if (os_snprintf_error(end - pos, ret))
-				return pos - buf;
-			pos += ret;
-		}
-		if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X_SHA256) {
-			ret = os_snprintf(pos, end - pos, "WPA-EAP-SHA256 ");
-			if (os_snprintf_error(end - pos, ret))
-				return pos - buf;
-			pos += ret;
-		}
-#endif /* CONFIG_IEEE80211W */
-#ifdef CONFIG_SAE
-		if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_SAE) {
-			ret = os_snprintf(pos, end - pos, "SAE ");
-			if (os_snprintf_error(end - pos, ret))
-				return pos - buf;
-			pos += ret;
-		}
-#endif /* CONFIG_SAE */
-		if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B) {
-			ret = os_snprintf(pos, end - pos, "WPA-EAP-SUITE-B ");
-			if (os_snprintf_error(end - pos, ret))
-				return pos - buf;
-			pos += ret;
-		}
-		if (hapd->conf->wpa_key_mgmt &
-		    WPA_KEY_MGMT_IEEE8021X_SUITE_B_192) {
-			ret = os_snprintf(pos, end - pos,
-					  "WPA-EAP-SUITE-B-192 ");
-			if (os_snprintf_error(end - pos, ret))
-				return pos - buf;
-			pos += ret;
-		}
+		pos += hostapd_ctrl_iface_get_key_mgmt(hapd, pos, end - pos);
 
 		ret = os_snprintf(pos, end - pos, "\n");
 		if (os_snprintf_error(end - pos, ret))
@@ -1529,7 +1562,7 @@
 {
 	struct hostapd_data *hapd = ctx;
 	const struct ether_header *eth;
-	const struct iphdr *ip;
+	struct iphdr ip;
 	const u8 *pos;
 	unsigned int i;
 
@@ -1537,14 +1570,14 @@
 		return;
 
 	eth = (const struct ether_header *) buf;
-	ip = (const struct iphdr *) (eth + 1);
-	pos = (const u8 *) (ip + 1);
+	os_memcpy(&ip, eth + 1, sizeof(ip));
+	pos = &buf[sizeof(*eth) + sizeof(ip)];
 
-	if (ip->ihl != 5 || ip->version != 4 ||
-	    ntohs(ip->tot_len) != HWSIM_IP_LEN)
+	if (ip.ihl != 5 || ip.version != 4 ||
+	    ntohs(ip.tot_len) != HWSIM_IP_LEN)
 		return;
 
-	for (i = 0; i < HWSIM_IP_LEN - sizeof(*ip); i++) {
+	for (i = 0; i < HWSIM_IP_LEN - sizeof(ip); i++) {
 		if (*pos != (u8) i)
 			return;
 		pos++;
@@ -1600,7 +1633,7 @@
 	int used;
 	long int val;
 	u8 tos;
-	u8 buf[HWSIM_PACKETLEN];
+	u8 buf[2 + HWSIM_PACKETLEN];
 	struct ether_header *eth;
 	struct iphdr *ip;
 	u8 *dpos;
@@ -1628,7 +1661,7 @@
 		return -1;
 	tos = val;
 
-	eth = (struct ether_header *) buf;
+	eth = (struct ether_header *) &buf[2];
 	os_memcpy(eth->ether_dhost, dst, ETH_ALEN);
 	os_memcpy(eth->ether_shost, src, ETH_ALEN);
 	eth->ether_type = htons(ETHERTYPE_IP);
@@ -1640,14 +1673,14 @@
 	ip->tos = tos;
 	ip->tot_len = htons(HWSIM_IP_LEN);
 	ip->protocol = 1;
-	ip->saddr = htonl(192 << 24 | 168 << 16 | 1 << 8 | 1);
-	ip->daddr = htonl(192 << 24 | 168 << 16 | 1 << 8 | 2);
+	ip->saddr = htonl(192U << 24 | 168 << 16 | 1 << 8 | 1);
+	ip->daddr = htonl(192U << 24 | 168 << 16 | 1 << 8 | 2);
 	ip->check = ipv4_hdr_checksum(ip, sizeof(*ip));
 	dpos = (u8 *) (ip + 1);
 	for (i = 0; i < HWSIM_IP_LEN - sizeof(*ip); i++)
 		*dpos++ = i;
 
-	if (l2_packet_send(hapd->l2_test, dst, ETHERTYPE_IP, buf,
+	if (l2_packet_send(hapd->l2_test, dst, ETHERTYPE_IP, &buf[2],
 			   HWSIM_PACKETLEN) < 0)
 		return -1;
 
@@ -1747,6 +1780,45 @@
 #endif /* WPA_TRACE_BFD */
 }
 
+
+static int hostapd_ctrl_test_fail(struct hostapd_data *hapd, char *cmd)
+{
+#ifdef WPA_TRACE_BFD
+	extern char wpa_trace_test_fail_func[256];
+	extern unsigned int wpa_trace_test_fail_after;
+	char *pos;
+
+	wpa_trace_test_fail_after = atoi(cmd);
+	pos = os_strchr(cmd, ':');
+	if (pos) {
+		pos++;
+		os_strlcpy(wpa_trace_test_fail_func, pos,
+			   sizeof(wpa_trace_test_fail_func));
+	} else {
+		wpa_trace_test_fail_after = 0;
+	}
+
+	return 0;
+#else /* WPA_TRACE_BFD */
+	return -1;
+#endif /* WPA_TRACE_BFD */
+}
+
+
+static int hostapd_ctrl_get_fail(struct hostapd_data *hapd,
+				 char *buf, size_t buflen)
+{
+#ifdef WPA_TRACE_BFD
+	extern char wpa_trace_test_fail_func[256];
+	extern unsigned int wpa_trace_test_fail_after;
+
+	return os_snprintf(buf, buflen, "%u:%s", wpa_trace_test_fail_after,
+			   wpa_trace_test_fail_func);
+#else /* WPA_TRACE_BFD */
+	return -1;
+#endif /* WPA_TRACE_BFD */
+}
+
 #endif /* CONFIG_TESTING_OPTIONS */
 
 
@@ -1848,41 +1920,134 @@
 }
 
 
-static void hostapd_ctrl_iface_receive(int sock, void *eloop_ctx,
-				       void *sock_ctx)
+static int hostapd_ctrl_iface_eapol_reauth(struct hostapd_data *hapd,
+					   const char *cmd)
 {
-	struct hostapd_data *hapd = eloop_ctx;
-	char buf[4096];
-	int res;
-	struct sockaddr_un from;
-	socklen_t fromlen = sizeof(from);
-	char *reply;
-	const int reply_size = 4096;
-	int reply_len;
-	int level = MSG_DEBUG;
+	u8 addr[ETH_ALEN];
+	struct sta_info *sta;
 
-	res = recvfrom(sock, buf, sizeof(buf) - 1, 0,
-		       (struct sockaddr *) &from, &fromlen);
-	if (res < 0) {
-		wpa_printf(MSG_ERROR, "recvfrom(ctrl_iface): %s",
-			   strerror(errno));
-		return;
+	if (hwaddr_aton(cmd, addr))
+		return -1;
+
+	sta = ap_get_sta(hapd, addr);
+	if (!sta || !sta->eapol_sm)
+		return -1;
+
+	eapol_auth_reauthenticate(sta->eapol_sm);
+	return 0;
+}
+
+
+static int hostapd_ctrl_iface_eapol_set(struct hostapd_data *hapd, char *cmd)
+{
+	u8 addr[ETH_ALEN];
+	struct sta_info *sta;
+	char *pos = cmd, *param;
+
+	if (hwaddr_aton(pos, addr) || pos[17] != ' ')
+		return -1;
+	pos += 18;
+	param = pos;
+	pos = os_strchr(pos, ' ');
+	if (!pos)
+		return -1;
+	*pos++ = '\0';
+
+	sta = ap_get_sta(hapd, addr);
+	if (!sta || !sta->eapol_sm)
+		return -1;
+
+	return eapol_auth_set_conf(sta->eapol_sm, param, pos);
+}
+
+
+static int hostapd_ctrl_iface_log_level(struct hostapd_data *hapd, char *cmd,
+					char *buf, size_t buflen)
+{
+	char *pos, *end, *stamp;
+	int ret;
+
+	/* cmd: "LOG_LEVEL [<level>]" */
+	if (*cmd == '\0') {
+		pos = buf;
+		end = buf + buflen;
+		ret = os_snprintf(pos, end - pos, "Current level: %s\n"
+				  "Timestamp: %d\n",
+				  debug_level_str(wpa_debug_level),
+				  wpa_debug_timestamp);
+		if (os_snprintf_error(end - pos, ret))
+			ret = 0;
+
+		return ret;
 	}
-	buf[res] = '\0';
-	if (os_strcmp(buf, "PING") == 0)
-		level = MSG_EXCESSIVE;
-	wpa_hexdump_ascii(level, "RX ctrl_iface", (u8 *) buf, res);
 
-	reply = os_malloc(reply_size);
-	if (reply == NULL) {
-		if (sendto(sock, "FAIL\n", 5, 0, (struct sockaddr *) &from,
-			   fromlen) < 0) {
-			wpa_printf(MSG_DEBUG, "CTRL: sendto failed: %s",
-				   strerror(errno));
+	while (*cmd == ' ')
+		cmd++;
+
+	stamp = os_strchr(cmd, ' ');
+	if (stamp) {
+		*stamp++ = '\0';
+		while (*stamp == ' ') {
+			stamp++;
 		}
-		return;
 	}
 
+	if (os_strlen(cmd)) {
+		int level = str_to_debug_level(cmd);
+		if (level < 0)
+			return -1;
+		wpa_debug_level = level;
+	}
+
+	if (stamp && os_strlen(stamp))
+		wpa_debug_timestamp = atoi(stamp);
+
+	os_memcpy(buf, "OK\n", 3);
+	return 3;
+}
+
+
+#ifdef NEED_AP_MLME
+static int hostapd_ctrl_iface_track_sta_list(struct hostapd_data *hapd,
+					     char *buf, size_t buflen)
+{
+	struct hostapd_iface *iface = hapd->iface;
+	char *pos, *end;
+	struct hostapd_sta_info *info;
+	struct os_reltime now;
+
+	sta_track_expire(iface, 0);
+
+	pos = buf;
+	end = buf + buflen;
+
+	os_get_reltime(&now);
+	dl_list_for_each_reverse(info, &iface->sta_seen,
+				 struct hostapd_sta_info, list) {
+		struct os_reltime age;
+		int ret;
+
+		os_reltime_sub(&now, &info->last_seen, &age);
+		ret = os_snprintf(pos, end - pos, MACSTR " %u\n",
+				  MAC2STR(info->addr), (unsigned int) age.sec);
+		if (os_snprintf_error(end - pos, ret))
+			break;
+		pos += ret;
+	}
+
+	return pos - buf;
+}
+#endif /* NEED_AP_MLME */
+
+
+static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
+					      char *buf, char *reply,
+					      int reply_size,
+					      struct sockaddr_un *from,
+					      socklen_t fromlen)
+{
+	int reply_len, res;
+
 	os_memcpy(reply, "OK\n", 3);
 	reply_len = 3;
 
@@ -1939,13 +2104,13 @@
 		reply_len = hostapd_ctrl_iface_sta_next(hapd, buf + 9, reply,
 							reply_size);
 	} else if (os_strcmp(buf, "ATTACH") == 0) {
-		if (hostapd_ctrl_iface_attach(hapd, &from, fromlen))
+		if (hostapd_ctrl_iface_attach(hapd, from, fromlen))
 			reply_len = -1;
 	} else if (os_strcmp(buf, "DETACH") == 0) {
-		if (hostapd_ctrl_iface_detach(hapd, &from, fromlen))
+		if (hostapd_ctrl_iface_detach(hapd, from, fromlen))
 			reply_len = -1;
 	} else if (os_strncmp(buf, "LEVEL ", 6) == 0) {
-		if (hostapd_ctrl_iface_level(hapd, &from, fromlen,
+		if (hostapd_ctrl_iface_level(hapd, from, fromlen,
 						    buf + 6))
 			reply_len = -1;
 	} else if (os_strncmp(buf, "NEW_STA ", 8) == 0) {
@@ -2080,6 +2245,11 @@
 	} else if (os_strcmp(buf, "GET_ALLOC_FAIL") == 0) {
 		reply_len = hostapd_ctrl_get_alloc_fail(hapd, reply,
 							reply_size);
+	} else if (os_strncmp(buf, "TEST_FAIL ", 10) == 0) {
+		if (hostapd_ctrl_test_fail(hapd, buf + 10) < 0)
+			reply_len = -1;
+	} else if (os_strcmp(buf, "GET_FAIL") == 0) {
+		reply_len = hostapd_ctrl_get_fail(hapd, reply, reply_size);
 #endif /* CONFIG_TESTING_OPTIONS */
 	} else if (os_strncmp(buf, "CHAN_SWITCH ", 12) == 0) {
 		if (hostapd_ctrl_iface_chan_switch(hapd->iface, buf + 12))
@@ -2092,6 +2262,20 @@
 #ifdef RADIUS_SERVER
 		radius_server_erp_flush(hapd->radius_srv);
 #endif /* RADIUS_SERVER */
+	} else if (os_strncmp(buf, "EAPOL_REAUTH ", 13) == 0) {
+		if (hostapd_ctrl_iface_eapol_reauth(hapd, buf + 13))
+			reply_len = -1;
+	} else if (os_strncmp(buf, "EAPOL_SET ", 10) == 0) {
+		if (hostapd_ctrl_iface_eapol_set(hapd, buf + 10))
+			reply_len = -1;
+	} else if (os_strncmp(buf, "LOG_LEVEL", 9) == 0) {
+		reply_len = hostapd_ctrl_iface_log_level(
+			hapd, buf + 9, reply, reply_size);
+#ifdef NEED_AP_MLME
+	} else if (os_strcmp(buf, "TRACK_STA_LIST") == 0) {
+		reply_len = hostapd_ctrl_iface_track_sta_list(
+			hapd, reply, reply_size);
+#endif /* NEED_AP_MLME */
 	} else {
 		os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
 		reply_len = 16;
@@ -2101,6 +2285,50 @@
 		os_memcpy(reply, "FAIL\n", 5);
 		reply_len = 5;
 	}
+
+	return reply_len;
+}
+
+
+static void hostapd_ctrl_iface_receive(int sock, void *eloop_ctx,
+				       void *sock_ctx)
+{
+	struct hostapd_data *hapd = eloop_ctx;
+	char buf[4096];
+	int res;
+	struct sockaddr_un from;
+	socklen_t fromlen = sizeof(from);
+	char *reply;
+	const int reply_size = 4096;
+	int reply_len;
+	int level = MSG_DEBUG;
+
+	res = recvfrom(sock, buf, sizeof(buf) - 1, 0,
+		       (struct sockaddr *) &from, &fromlen);
+	if (res < 0) {
+		wpa_printf(MSG_ERROR, "recvfrom(ctrl_iface): %s",
+			   strerror(errno));
+		return;
+	}
+	buf[res] = '\0';
+	if (os_strcmp(buf, "PING") == 0)
+		level = MSG_EXCESSIVE;
+	wpa_hexdump_ascii(level, "RX ctrl_iface", (u8 *) buf, res);
+
+	reply = os_malloc(reply_size);
+	if (reply == NULL) {
+		if (sendto(sock, "FAIL\n", 5, 0, (struct sockaddr *) &from,
+			   fromlen) < 0) {
+			wpa_printf(MSG_DEBUG, "CTRL: sendto failed: %s",
+				   strerror(errno));
+		}
+		return;
+	}
+
+	reply_len = hostapd_ctrl_iface_receive_process(hapd, buf,
+						       reply, reply_size,
+						       &from, fromlen);
+
 	if (sendto(sock, reply, reply_len, 0, (struct sockaddr *) &from,
 		   fromlen) < 0) {
 		wpa_printf(MSG_DEBUG, "CTRL: sendto failed: %s",
@@ -2423,6 +2651,214 @@
 }
 
 
+#ifdef CONFIG_FST
+
+static int
+hostapd_global_ctrl_iface_fst_attach(struct hapd_interfaces *interfaces,
+				     const char *cmd)
+{
+	char ifname[IFNAMSIZ + 1];
+	struct fst_iface_cfg cfg;
+	struct hostapd_data *hapd;
+	struct fst_wpa_obj iface_obj;
+
+	if (!fst_parse_attach_command(cmd, ifname, sizeof(ifname), &cfg)) {
+		hapd = hostapd_get_iface(interfaces, ifname);
+		if (hapd) {
+			if (hapd->iface->fst) {
+				wpa_printf(MSG_INFO, "FST: Already attached");
+				return -1;
+			}
+			fst_hostapd_fill_iface_obj(hapd, &iface_obj);
+			hapd->iface->fst = fst_attach(ifname, hapd->own_addr,
+						      &iface_obj, &cfg);
+			if (hapd->iface->fst)
+				return 0;
+		}
+	}
+
+	return -EINVAL;
+}
+
+
+static int
+hostapd_global_ctrl_iface_fst_detach(struct hapd_interfaces *interfaces,
+				     const char *cmd)
+{
+	char ifname[IFNAMSIZ + 1];
+	struct hostapd_data * hapd;
+
+	if (!fst_parse_detach_command(cmd, ifname, sizeof(ifname))) {
+		hapd = hostapd_get_iface(interfaces, ifname);
+		if (hapd) {
+			if (!fst_iface_detach(ifname)) {
+				hapd->iface->fst = NULL;
+				hapd->iface->fst_ies = NULL;
+				return 0;
+			}
+		}
+	}
+
+	return -EINVAL;
+}
+
+#endif /* CONFIG_FST */
+
+
+static struct hostapd_data *
+hostapd_interfaces_get_hapd(struct hapd_interfaces *interfaces,
+			    const char *ifname)
+{
+	size_t i, j;
+
+	for (i = 0; i < interfaces->count; i++) {
+		struct hostapd_iface *iface = interfaces->iface[i];
+
+		for (j = 0; j < iface->num_bss; j++) {
+			struct hostapd_data *hapd;
+
+			hapd = iface->bss[j];
+			if (os_strcmp(ifname, hapd->conf->iface) == 0)
+				return hapd;
+		}
+	}
+
+	return NULL;
+}
+
+
+static int hostapd_ctrl_iface_dup_param(struct hostapd_data *src_hapd,
+					struct hostapd_data *dst_hapd,
+					const char *param)
+{
+	int res;
+	char *value;
+
+	value = os_zalloc(HOSTAPD_CLI_DUP_VALUE_MAX_LEN);
+	if (!value) {
+		wpa_printf(MSG_ERROR,
+			   "DUP: cannot allocate buffer to stringify %s",
+			   param);
+		goto error_return;
+	}
+
+	if (os_strcmp(param, "wpa") == 0) {
+		os_snprintf(value, HOSTAPD_CLI_DUP_VALUE_MAX_LEN, "%d",
+			    src_hapd->conf->wpa);
+	} else if (os_strcmp(param, "wpa_key_mgmt") == 0 &&
+		   src_hapd->conf->wpa_key_mgmt) {
+		res = hostapd_ctrl_iface_get_key_mgmt(
+			src_hapd, value, HOSTAPD_CLI_DUP_VALUE_MAX_LEN);
+		if (os_snprintf_error(HOSTAPD_CLI_DUP_VALUE_MAX_LEN, res))
+			goto error_stringify;
+	} else if (os_strcmp(param, "wpa_pairwise") == 0 &&
+		   src_hapd->conf->wpa_pairwise) {
+		res = wpa_write_ciphers(value,
+					value + HOSTAPD_CLI_DUP_VALUE_MAX_LEN,
+					src_hapd->conf->wpa_pairwise, " ");
+		if (res < 0)
+			goto error_stringify;
+	} else if (os_strcmp(param, "rsn_pairwise") == 0 &&
+		   src_hapd->conf->rsn_pairwise) {
+		res = wpa_write_ciphers(value,
+					value + HOSTAPD_CLI_DUP_VALUE_MAX_LEN,
+					src_hapd->conf->rsn_pairwise, " ");
+		if (res < 0)
+			goto error_stringify;
+	} else if (os_strcmp(param, "wpa_passphrase") == 0 &&
+		   src_hapd->conf->ssid.wpa_passphrase) {
+		os_snprintf(value, HOSTAPD_CLI_DUP_VALUE_MAX_LEN, "%s",
+			    src_hapd->conf->ssid.wpa_passphrase);
+	} else if (os_strcmp(param, "wpa_psk") == 0 &&
+		   src_hapd->conf->ssid.wpa_psk_set) {
+		wpa_snprintf_hex(value, HOSTAPD_CLI_DUP_VALUE_MAX_LEN,
+			src_hapd->conf->ssid.wpa_psk->psk, PMK_LEN);
+	} else {
+		wpa_printf(MSG_WARNING, "DUP: %s cannot be duplicated", param);
+		goto error_return;
+	}
+
+	res = hostapd_set_iface(dst_hapd->iconf, dst_hapd->conf, param, value);
+	os_free(value);
+	return res;
+
+error_stringify:
+	wpa_printf(MSG_ERROR, "DUP: cannot stringify %s", param);
+error_return:
+	os_free(value);
+	return -1;
+}
+
+
+static int
+hostapd_global_ctrl_iface_dup_network(struct hapd_interfaces *interfaces,
+				      char *cmd)
+{
+	char *p_start = cmd, *p_end;
+	struct hostapd_data *src_hapd, *dst_hapd;
+
+	/* cmd: "<src ifname> <dst ifname> <variable name> */
+
+	p_end = os_strchr(p_start, ' ');
+	if (!p_end) {
+		wpa_printf(MSG_ERROR, "DUP: no src ifname found in cmd: '%s'",
+			   cmd);
+		return -1;
+	}
+
+	*p_end = '\0';
+	src_hapd = hostapd_interfaces_get_hapd(interfaces, p_start);
+	if (!src_hapd) {
+		wpa_printf(MSG_ERROR, "DUP: no src ifname found: '%s'",
+			   p_start);
+		return -1;
+	}
+
+	p_start = p_end + 1;
+	p_end = os_strchr(p_start, ' ');
+	if (!p_end) {
+		wpa_printf(MSG_ERROR, "DUP: no dst ifname found in cmd: '%s'",
+			   cmd);
+		return -1;
+	}
+
+	*p_end = '\0';
+	dst_hapd = hostapd_interfaces_get_hapd(interfaces, p_start);
+	if (!dst_hapd) {
+		wpa_printf(MSG_ERROR, "DUP: no dst ifname found: '%s'",
+			   p_start);
+		return -1;
+	}
+
+	p_start = p_end + 1;
+	return hostapd_ctrl_iface_dup_param(src_hapd, dst_hapd, p_start);
+}
+
+
+static int hostapd_global_ctrl_iface_ifname(struct hapd_interfaces *interfaces,
+					    const char *ifname,
+					    char *buf, char *reply,
+					    int reply_size,
+					    struct sockaddr_un *from,
+					    socklen_t fromlen)
+{
+	struct hostapd_data *hapd;
+
+	hapd = hostapd_interfaces_get_hapd(interfaces, ifname);
+	if (hapd == NULL) {
+		int res;
+
+		res = os_snprintf(reply, reply_size, "FAIL-NO-IFNAME-MATCH\n");
+		if (os_snprintf_error(reply_size, res))
+			return -1;
+		return res;
+	}
+
+	return hostapd_ctrl_iface_receive_process(hapd, buf, reply,reply_size,
+						  from, fromlen);
+}
+
+
 static void hostapd_global_ctrl_iface_receive(int sock, void *eloop_ctx,
 					      void *sock_ctx)
 {
@@ -2458,6 +2894,18 @@
 	os_memcpy(reply, "OK\n", 3);
 	reply_len = 3;
 
+	if (os_strncmp(buf, "IFNAME=", 7) == 0) {
+		char *pos = os_strchr(buf + 7, ' ');
+
+		if (pos) {
+			*pos++ = '\0';
+			reply_len = hostapd_global_ctrl_iface_ifname(
+				interfaces, buf + 7, pos, reply, reply_size,
+				&from, fromlen);
+			goto send_reply;
+		}
+	}
+
 	if (os_strcmp(buf, "PING") == 0) {
 		os_memcpy(reply, "PONG\n", 5);
 		reply_len = 5;
@@ -2486,12 +2934,33 @@
 		if (hapd_module_tests() < 0)
 			reply_len = -1;
 #endif /* CONFIG_MODULE_TESTS */
+#ifdef CONFIG_FST
+	} else if (os_strncmp(buf, "FST-ATTACH ", 11) == 0) {
+		if (!hostapd_global_ctrl_iface_fst_attach(interfaces, buf + 11))
+			reply_len = os_snprintf(reply, reply_size, "OK\n");
+		else
+			reply_len = -1;
+	} else if (os_strncmp(buf, "FST-DETACH ", 11) == 0) {
+		if (!hostapd_global_ctrl_iface_fst_detach(interfaces, buf + 11))
+			reply_len = os_snprintf(reply, reply_size, "OK\n");
+		else
+			reply_len = -1;
+	} else if (os_strncmp(buf, "FST-MANAGER ", 12) == 0) {
+		reply_len = fst_ctrl_iface_receive(buf + 12, reply, reply_size);
+#endif /* CONFIG_FST */
+	} else if (os_strncmp(buf, "DUP_NETWORK ", 12) == 0) {
+		if (!hostapd_global_ctrl_iface_dup_network(interfaces,
+							   buf + 12))
+			reply_len = os_snprintf(reply, reply_size, "OK\n");
+		else
+			reply_len = -1;
 	} else {
 		wpa_printf(MSG_DEBUG, "Unrecognized global ctrl_iface command "
 			   "ignored");
 		reply_len = -1;
 	}
 
+send_reply:
 	if (reply_len < 0) {
 		os_memcpy(reply, "FAIL\n", 5);
 		reply_len = 5;
diff --git a/hostapd/defconfig b/hostapd/defconfig
index 4cde2b5..2a749dd 100644
--- a/hostapd/defconfig
+++ b/hostapd/defconfig
@@ -18,6 +18,9 @@
 # Driver interface for drivers using the nl80211 kernel interface
 CONFIG_DRIVER_NL80211=y
 
+# QCA vendor extensions to nl80211
+#CONFIG_DRIVER_NL80211_QCA=y
+
 # driver_nl80211.c requires libnl. If you are compiling it yourself
 # you may need to point hostapd to your version of libnl.
 #
@@ -240,6 +243,12 @@
 # requirements described above.
 #CONFIG_NO_RANDOM_POOL=y
 
+# Should we use poll instead of select? Select is used by default.
+#CONFIG_ELOOP_POLL=y
+
+# Should we use epoll instead of select? Select is used by default.
+#CONFIG_ELOOP_EPOLL=y
+
 # Select TLS implementation
 # openssl = OpenSSL (default)
 # gnutls = GnuTLS
@@ -283,6 +292,12 @@
 # Enable SQLite database support in hlr_auc_gw, EAP-SIM DB, and eap_user_file
 #CONFIG_SQLITE=y
 
+# Enable Fast Session Transfer (FST)
+#CONFIG_FST=y
+
+# Enable CLI commands for FST testing
+#CONFIG_FST_TEST=y
+
 # Testing options
 # This can be used to enable some testing options (see also the example
 # configuration file) that are really useful only for testing clients that
diff --git a/hostapd/hlr_auc_gw.c b/hostapd/hlr_auc_gw.c
index 8afe457..84d0308 100644
--- a/hostapd/hlr_auc_gw.c
+++ b/hostapd/hlr_auc_gw.c
@@ -550,7 +550,7 @@
 static void update_milenage_file(const char *fname)
 {
 	FILE *f, *f2;
-	char buf[500], *pos;
+	char name[500], buf[500], *pos;
 	char *end = buf + sizeof(buf);
 	struct milenage_parameters *m;
 	size_t imsi_len;
@@ -561,10 +561,10 @@
 		return;
 	}
 
-	snprintf(buf, sizeof(buf), "%s.new", fname);
-	f2 = fopen(buf, "w");
+	snprintf(name, sizeof(name), "%s.new", fname);
+	f2 = fopen(name, "w");
 	if (f2 == NULL) {
-		printf("Could not write Milenage data file '%s'\n", buf);
+		printf("Could not write Milenage data file '%s'\n", name);
 		fclose(f);
 		return;
 	}
@@ -606,14 +606,14 @@
 	fclose(f2);
 	fclose(f);
 
-	snprintf(buf, sizeof(buf), "%s.bak", fname);
-	if (rename(fname, buf) < 0) {
+	snprintf(name, sizeof(name), "%s.bak", fname);
+	if (rename(fname, name) < 0) {
 		perror("rename");
 		return;
 	}
 
-	snprintf(buf, sizeof(buf), "%s.new", fname);
-	if (rename(buf, fname) < 0) {
+	snprintf(name, sizeof(name), "%s.new", fname);
+	if (rename(name, fname) < 0) {
 		perror("rename");
 		return;
 	}
diff --git a/hostapd/hostapd.conf b/hostapd/hostapd.conf
index 00fc142..4f51140 100644
--- a/hostapd/hostapd.conf
+++ b/hostapd/hostapd.conf
@@ -173,7 +173,7 @@
 # Channel list restriction. This option allows hostapd to select one of the
 # provided channels when a channel should be automatically selected.
 # Channel list can be provided as range using hyphen ('-') or individual
-# channels can be specified by space (' ') seperated values
+# channels can be specified by space (' ') separated values
 # Default: all channels allowed in selected hw_mode
 #chanlist=100 104 108 112 116
 #chanlist=1 6 11-13
@@ -192,16 +192,16 @@
 # (default: 2007)
 max_num_sta=255
 
-# RTS/CTS threshold; 2347 = disabled (default); range 0..2347
+# RTS/CTS threshold; -1 = disabled (default); range -1..65535
 # If this field is not included in hostapd.conf, hostapd will not control
 # RTS threshold and 'iwconfig wlan# rts <val>' can be used to set it.
-rts_threshold=2347
+rts_threshold=-1
 
-# Fragmentation threshold; 2346 = disabled (default); range 256..2346
+# Fragmentation threshold; -1 = disabled (default); range -1, 256..2346
 # If this field is not included in hostapd.conf, hostapd will not control
 # fragmentation threshold and 'iwconfig wlan# frag <val>' can be used to set
 # it.
-fragm_threshold=2346
+fragm_threshold=-1
 
 # Rate configuration
 # Default is to enable all rates supported by the hardware. This configuration
@@ -267,7 +267,14 @@
 #     requests for broadcast SSID
 ignore_broadcast_ssid=0
 
-# Additional vendor specfic elements for Beacon and Probe Response frames
+# Do not reply to broadcast Probe Request frames from unassociated STA if there
+# is no room for additional stations (max_num_sta). This can be used to
+# discourage a STA from trying to associate with this AP if the association
+# would be rejected due to maximum STA limit.
+# Default: 0 (disabled)
+#no_probe_resp_if_max_sta=0
+
+# Additional vendor specific elements for Beacon and Probe Response frames
 # This parameter can be used to add additional vendor specific element(s) into
 # the end of the Beacon and Probe Response frames. The format for these
 # element(s) is a hexdump of the raw information elements (id+len+payload for
@@ -582,14 +589,16 @@
 # 0 = Not supported (default)
 # 1 = Supported
 #
-# Compressed Steering Number of Beamformer Antennas Supported: [BF-ANTENNA-2]
+# Compressed Steering Number of Beamformer Antennas Supported:
+# [BF-ANTENNA-2] [BF-ANTENNA-3] [BF-ANTENNA-4]
 #   Beamformee's capability indicating the maximum number of beamformer
 #   antennas the beamformee can support when sending compressed beamforming
 #   feedback
 # If SU beamformer capable, set to maximum value minus 1
 # else reserved (default)
 #
-# Number of Sounding Dimensions: [SOUNDING-DIMENSION-2]
+# Number of Sounding Dimensions:
+# [SOUNDING-DIMENSION-2] [SOUNDING-DIMENSION-3] [SOUNDING-DIMENSION-4]
 # Beamformer's capability indicating the maximum value of the NUM_STS parameter
 # in the TXVECTOR of a VHT NDP
 # If SU beamformer capable, set to maximum value minus 1
@@ -603,9 +612,9 @@
 # VHT TXOP PS: [VHT-TXOP-PS]
 # Indicates whether or not the AP supports VHT TXOP Power Save Mode
 #  or whether or not the STA is in VHT TXOP Power Save mode
-# 0 = VHT AP doesnt support VHT TXOP PS mode (OR) VHT Sta not in VHT TXOP PS
+# 0 = VHT AP doesn't support VHT TXOP PS mode (OR) VHT STA not in VHT TXOP PS
 #  mode
-# 1 = VHT AP supports VHT TXOP PS mode (OR) VHT Sta is in VHT TXOP power save
+# 1 = VHT AP supports VHT TXOP PS mode (OR) VHT STA is in VHT TXOP power save
 #  mode
 #
 # +HTC-VHT Capable: [HTC-VHT]
@@ -766,6 +775,12 @@
 # 2 = check all CRLs in the certificate path
 #check_crl=1
 
+# TLS Session Lifetime in seconds
+# This can be used to allow TLS sessions to be cached and resumed with an
+# abbreviated handshake when using EAP-TLS/TTLS/PEAP.
+# (default: 0 = session caching and resumption disabled)
+#tls_session_lifetime=3600
+
 # Cached OCSP stapling response (DER encoded)
 # If set, this file is sent as a certificate status response by the EAP server
 # if the EAP peer requests certificate status in the ClientHello message.
@@ -817,6 +832,11 @@
 #eap_sim_db=unix:/tmp/hlr_auc_gw.sock
 #eap_sim_db=unix:/tmp/hlr_auc_gw.sock db=/tmp/hostapd.db
 
+# EAP-SIM DB request timeout
+# This parameter sets the maximum time to wait for a database request response.
+# The parameter value is in seconds.
+#eap_sim_db_timeout=1
+
 # Encryption key for EAP-FAST PAC-Opaque values. This key must be a secret,
 # random value. It is configured as a 16-octet value in hex format. It can be
 # generated, e.g., with the following command:
@@ -1249,6 +1269,11 @@
 # 1 = push PMK-R1 to all configured R1KHs whenever a new PMK-R0 is derived
 #pmk_r1_push=1
 
+# Whether to enable FT-over-DS
+# 0 = FT-over-DS disabled
+# 1 = FT-over-DS enabled (default)
+#ft_over_ds=1
+
 ##### Neighbor table ##########################################################
 # Maximum number of entries kept in AP table (either for neigbor table or for
 # detecting Overlapping Legacy BSS Condition). The oldest entry will be
@@ -1266,6 +1291,43 @@
 # default: 60
 #ap_table_expiration_time=3600
 
+# Maximum number of stations to track on the operating channel
+# This can be used to detect dualband capable stations before they have
+# associated, e.g., to provide guidance on which colocated BSS to use.
+# Default: 0 (disabled)
+#track_sta_max_num=100
+
+# Maximum age of a station tracking entry in seconds
+# Default: 180
+#track_sta_max_age=180
+
+# Do not reply to group-addressed Probe Request from a station that was seen on
+# another radio.
+# Default: Disabled
+#
+# This can be used with enabled track_sta_max_num configuration on another
+# interface controlled by the same hostapd process to restrict Probe Request
+# frame handling from replying to group-addressed Probe Request frames from a
+# station that has been detected to be capable of operating on another band,
+# e.g., to try to reduce likelihood of the station selecting a 2.4 GHz BSS when
+# the AP operates both a 2.4 GHz and 5 GHz BSS concurrently.
+#
+# Note: Enabling this can cause connectivity issues and increase latency for
+# discovering the AP.
+#no_probe_resp_if_seen_on=wlan1
+
+# Reject authentication from a station that was seen on another radio.
+# Default: Disabled
+#
+# This can be used with enabled track_sta_max_num configuration on another
+# interface controlled by the same hostapd process to reject authentication
+# attempts from a station that has been detected to be capable of operating on
+# another band, e.g., to try to reduce likelihood of the station selecting a
+# 2.4 GHz BSS when the AP operates both a 2.4 GHz and 5 GHz BSS concurrently.
+#
+# Note: Enabling this can cause connectivity issues and increase latency for
+# connecting with the AP.
+#no_auth_if_seen_on=wlan1
 
 ##### Wi-Fi Protected Setup (WPS) #############################################
 
@@ -1634,6 +1696,17 @@
 # username/password
 #nai_realm=0,example.org,13[5:6],21[2:4][5:7]
 
+# Arbitrary ANQP-element configuration
+# Additional ANQP-elements with arbitrary values can be defined by specifying
+# their contents in raw format as a hexdump of the payload. Note that these
+# values will override ANQP-element contents that may have been specified in the
+# more higher layer configuration parameters listed above.
+# format: anqp_elem=<InfoID>:<hexdump of payload>
+# For example, AP Geospatial Location ANQP-element with unknown location:
+#anqp_elem=265:0000
+# For example, AP Civic Location ANQP-element with unknown location:
+#anqp_elem=266:000000
+
 # QoS Map Set configuration
 #
 # Comma delimited QoS Map Set in decimal values
@@ -1747,6 +1820,32 @@
 #
 #osu_server_uri=...
 
+##### Fast Session Transfer (FST) support #####################################
+#
+# The options in this section are only available when the build configuration
+# option CONFIG_FST is set while compiling hostapd. They allow this interface
+# to be a part of FST setup.
+#
+# FST is the transfer of a session from a channel to another channel, in the
+# same or different frequency bands.
+#
+# For detals, see IEEE Std 802.11ad-2012.
+
+# Identifier of an FST Group the interface belongs to.
+#fst_group_id=bond0
+
+# Interface priority within the FST Group.
+# Announcing a higher priority for an interface means declaring it more
+# preferable for FST switch.
+# fst_priority is in 1..255 range with 1 being the lowest priority.
+#fst_priority=100
+
+# Default LLT value for this interface in milliseconds. The value used in case
+# no value provided during session setup. Default is 50 ms.
+# fst_llt is in 1..4294967 range (due to spec limitation, see 10.32.2.2
+# Transitioning between states).
+#fst_llt=100
+
 ##### TESTING OPTIONS #########################################################
 #
 # The options in this section are only available when the build configuration
@@ -1768,6 +1867,10 @@
 #
 # Corrupt Key MIC in GTK rekey EAPOL-Key frames with the given probability
 #corrupt_gtk_rekey_mic_probability=0.0
+#
+# Include only ECSA IE without CSA IE where possible
+# (channel switch operating class is needed)
+#ecsa_ie_only=0
 
 ##### Multiple BSSID support ##################################################
 #
diff --git a/hostapd/hostapd_cli.c b/hostapd/hostapd_cli.c
index e299183..46c2f37 100644
--- a/hostapd/hostapd_cli.c
+++ b/hostapd/hostapd_cli.c
@@ -97,6 +97,7 @@
 #define CONFIG_CTRL_IFACE_DIR "/var/run/hostapd"
 #endif /* CONFIG_CTRL_IFACE_DIR */
 static const char *ctrl_iface_dir = CONFIG_CTRL_IFACE_DIR;
+static const char *client_socket_dir = NULL;
 
 static char *ctrl_ifname = NULL;
 static const char *pid_file = NULL;
@@ -112,13 +113,15 @@
 		"\n"
 		"usage: hostapd_cli [-p<path>] [-i<ifname>] [-hvB] "
 		"[-a<path>] \\\n"
-		"                   [-G<ping interval>] [command..]\n"
+		"                   [-P<pid file>] [-G<ping interval>] [command..]\n"
 		"\n"
 		"Options:\n"
 		"   -h           help (show this usage text)\n"
 		"   -v           shown version information\n"
 		"   -p<path>     path to find control sockets (default: "
 		"/var/run/hostapd)\n"
+		"   -s<dir_path> dir path to open client sockets (default: "
+		CONFIG_CTRL_IFACE_DIR ")\n"
 		"   -a<file>     run in daemon mode executing the action file "
 		"based on events\n"
 		"                from hostapd\n"
@@ -145,7 +148,14 @@
 		return NULL;
 	snprintf(cfile, flen, "%s/%s", ctrl_iface_dir, ifname);
 
-	ctrl_conn = wpa_ctrl_open(cfile);
+	if (client_socket_dir && client_socket_dir[0] &&
+	    access(client_socket_dir, F_OK) < 0) {
+		perror(client_socket_dir);
+		free(cfile);
+		return NULL;
+	}
+
+	ctrl_conn = wpa_ctrl_open2(cfile, client_socket_dir);
 	free(cfile);
 	return ctrl_conn;
 }
@@ -922,6 +932,35 @@
 }
 
 
+#ifdef CONFIG_FST
+static int hostapd_cli_cmd_fst(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+	char cmd[256];
+	int res;
+	int i;
+	int total;
+
+	if (argc <= 0) {
+		printf("FST command: parameters are required.\n");
+		return -1;
+	}
+
+	total = os_snprintf(cmd, sizeof(cmd), "FST-MANAGER");
+
+	for (i = 0; i < argc; i++) {
+		res = os_snprintf(cmd + total, sizeof(cmd) - total, " %s",
+				  argv[i]);
+		if (os_snprintf_error(sizeof(cmd) - total, res)) {
+			printf("Too long fst command.\n");
+			return -1;
+		}
+		total += res;
+	}
+	return wpa_ctrl_command(ctrl, cmd);
+}
+#endif /* CONFIG_FST */
+
+
 static int hostapd_cli_cmd_chan_switch(struct wpa_ctrl *ctrl,
 				       int argc, char *argv[])
 {
@@ -1010,6 +1049,25 @@
 }
 
 
+static int hostapd_cli_cmd_log_level(struct wpa_ctrl *ctrl, int argc,
+				     char *argv[])
+{
+	char cmd[256];
+	int res;
+
+	res = os_snprintf(cmd, sizeof(cmd), "LOG_LEVEL%s%s%s%s",
+			  argc >= 1 ? " " : "",
+			  argc >= 1 ? argv[0] : "",
+			  argc == 2 ? " " : "",
+			  argc == 2 ? argv[1] : "");
+	if (os_snprintf_error(sizeof(cmd), res)) {
+		printf("Too long option\n");
+		return -1;
+	}
+	return wpa_ctrl_command(ctrl, cmd);
+}
+
+
 struct hostapd_cli_cmd {
 	const char *cmd;
 	int (*handler)(struct wpa_ctrl *ctrl, int argc, char *argv[]);
@@ -1049,6 +1107,9 @@
 	{ "get_config", hostapd_cli_cmd_get_config },
 	{ "help", hostapd_cli_cmd_help },
 	{ "interface", hostapd_cli_cmd_interface },
+#ifdef CONFIG_FST
+	{ "fst", hostapd_cli_cmd_fst },
+#endif /* CONFIG_FST */
 	{ "level", hostapd_cli_cmd_level },
 	{ "license", hostapd_cli_cmd_license },
 	{ "quit", hostapd_cli_cmd_quit },
@@ -1064,6 +1125,7 @@
 	{ "reload", hostapd_cli_cmd_reload },
 	{ "disable", hostapd_cli_cmd_disable },
 	{ "erp_flush", hostapd_cli_cmd_erp_flush },
+	{ "log_level", hostapd_cli_cmd_log_level },
 	{ NULL, NULL }
 };
 
@@ -1285,7 +1347,7 @@
 		return -1;
 
 	for (;;) {
-		c = getopt(argc, argv, "a:BhG:i:p:v");
+		c = getopt(argc, argv, "a:BhG:i:p:P:s:v");
 		if (c < 0)
 			break;
 		switch (c) {
@@ -1311,6 +1373,12 @@
 		case 'p':
 			ctrl_iface_dir = optarg;
 			break;
+		case 'P':
+			pid_file = optarg;
+			break;
+		case 's':
+			client_socket_dir = optarg;
+			break;
 		default:
 			usage();
 			return -1;
diff --git a/hostapd/main.c b/hostapd/main.c
index 62d0775..72b1d91 100644
--- a/hostapd/main.c
+++ b/hostapd/main.c
@@ -24,6 +24,7 @@
 #include "ap/hostapd.h"
 #include "ap/ap_config.h"
 #include "ap/ap_drv_ops.h"
+#include "fst/fst.h"
 #include "config_file.h"
 #include "eap_register.h"
 #include "ctrl_iface.h"
@@ -455,6 +456,7 @@
 		"   -T = record to Linux tracing in addition to logging\n"
 		"        (records all messages regardless of debug verbosity)\n"
 #endif /* CONFIG_DEBUG_LINUX_TRACING */
+		"   -S   start all the interfaces synchronously\n"
 		"   -t   include timestamps in some debug messages\n"
 		"   -v   show hostapd version\n");
 
@@ -533,6 +535,28 @@
 #endif /* CONFIG_WPS */
 
 
+#ifndef HOSTAPD_CLEANUP_INTERVAL
+#define HOSTAPD_CLEANUP_INTERVAL 10
+#endif /* HOSTAPD_CLEANUP_INTERVAL */
+
+static int hostapd_periodic_call(struct hostapd_iface *iface, void *ctx)
+{
+	hostapd_periodic_iface(iface);
+	return 0;
+}
+
+
+/* Periodic cleanup tasks */
+static void hostapd_periodic(void *eloop_ctx, void *timeout_ctx)
+{
+	struct hapd_interfaces *interfaces = eloop_ctx;
+
+	eloop_register_timeout(HOSTAPD_CLEANUP_INTERVAL, 0,
+			       hostapd_periodic, interfaces, NULL);
+	hostapd_for_each_interface(interfaces, hostapd_periodic_call, NULL);
+}
+
+
 int main(int argc, char *argv[])
 {
 	struct hapd_interfaces interfaces;
@@ -547,6 +571,7 @@
 #ifdef CONFIG_DEBUG_LINUX_TRACING
 	int enable_trace_dbg = 0;
 #endif /* CONFIG_DEBUG_LINUX_TRACING */
+	int start_ifaces_in_sync = 0;
 
 	if (os_program_init())
 		return -1;
@@ -564,7 +589,7 @@
 	interfaces.global_ctrl_dst = NULL;
 
 	for (;;) {
-		c = getopt(argc, argv, "b:Bde:f:hKP:Ttu:vg:G:");
+		c = getopt(argc, argv, "b:Bde:f:hKP:STtu:vg:G:");
 		if (c < 0)
 			break;
 		switch (c) {
@@ -621,6 +646,9 @@
 			bss_config = tmp_bss;
 			bss_config[num_bss_configs++] = optarg;
 			break;
+		case 'S':
+			start_ifaces_in_sync = 1;
+			break;
 #ifdef CONFIG_WPS
 		case 'u':
 			return gen_uuid(optarg);
@@ -666,6 +694,20 @@
 		return -1;
 	}
 
+	eloop_register_timeout(HOSTAPD_CLEANUP_INTERVAL, 0,
+			       hostapd_periodic, &interfaces, NULL);
+
+	if (fst_global_init()) {
+		wpa_printf(MSG_ERROR,
+			   "Failed to initialize global FST context");
+		goto out;
+	}
+
+#if defined(CONFIG_FST) && defined(CONFIG_CTRL_IFACE)
+	if (!fst_global_add_ctrl(fst_ctrl_cli))
+		wpa_printf(MSG_WARNING, "Failed to add CLI FST ctrl");
+#endif /* CONFIG_FST && CONFIG_CTRL_IFACE */
+
 	/* Allocate and parse configuration for full interface files */
 	for (i = 0; i < interfaces.count; i++) {
 		interfaces.iface[i] = hostapd_interface_init(&interfaces,
@@ -675,6 +717,8 @@
 			wpa_printf(MSG_ERROR, "Failed to initialize interface");
 			goto out;
 		}
+		if (start_ifaces_in_sync)
+			interfaces.iface[i]->need_to_start_in_sync = 1;
 	}
 
 	/* Allocate and parse configuration for per-BSS files */
@@ -750,6 +794,7 @@
 	}
 	os_free(interfaces.iface);
 
+	eloop_cancel_timeout(hostapd_periodic, &interfaces, NULL);
 	hostapd_global_deinit(pid_file);
 	os_free(pid_file);
 
@@ -759,6 +804,8 @@
 
 	os_free(bss_config);
 
+	fst_global_deinit();
+
 	os_program_deinit();
 
 	return ret;
diff --git a/hs20/client/Android.mk b/hs20/client/Android.mk
index a71e86d..e4db322 100644
--- a/hs20/client/Android.mk
+++ b/hs20/client/Android.mk
@@ -54,6 +54,7 @@
 OBJS += ../../src/crypto/md5-internal.c
 OBJS += ../../src/crypto/sha1-internal.c
 OBJS += ../../src/crypto/sha256-internal.c
+OBJS += ../../src/crypto/tls_openssl_ocsp.c
 
 L_CFLAGS += -DEAP_TLS_OPENSSL
 
diff --git a/hs20/client/Makefile b/hs20/client/Makefile
index 94cd5f1..fc9b619 100644
--- a/hs20/client/Makefile
+++ b/hs20/client/Makefile
@@ -76,6 +76,7 @@
 endif
 
 CFLAGS += -DEAP_TLS_OPENSSL
+OBJS += ../../src/crypto/tls_openssl_ocsp.o
 LIBS += -lssl -lcrypto
 
 hs20-osu-client: $(OBJS)
diff --git a/hs20/client/est.c b/hs20/client/est.c
index ec05bc4..9f1519b 100644
--- a/hs20/client/est.c
+++ b/hs20/client/est.c
@@ -16,6 +16,9 @@
 #include <openssl/asn1t.h>
 #include <openssl/x509.h>
 #include <openssl/x509v3.h>
+#ifdef OPENSSL_IS_BORINGSSL
+#include <openssl/buf.h>
+#endif /* OPENSSL_IS_BORINGSSL */
 
 #include "common.h"
 #include "utils/base64.h"
@@ -27,12 +30,28 @@
 static int pkcs7_to_cert(struct hs20_osu_client *ctx, const u8 *pkcs7,
 			 size_t len, char *pem_file, char *der_file)
 {
+#ifdef OPENSSL_IS_BORINGSSL
+	CBS pkcs7_cbs;
+#else /* OPENSSL_IS_BORINGSSL */
 	PKCS7 *p7 = NULL;
 	const unsigned char *p = pkcs7;
+#endif /* OPENSSL_IS_BORINGSSL */
 	STACK_OF(X509) *certs;
 	int i, num, ret = -1;
 	BIO *out = NULL;
 
+#ifdef OPENSSL_IS_BORINGSSL
+	certs = sk_X509_new_null();
+	if (!certs)
+		goto fail;
+	CBS_init(&pkcs7_cbs, pkcs7, len);
+	if (!PKCS7_get_certificates(certs, &pkcs7_cbs)) {
+		wpa_printf(MSG_INFO, "Could not parse PKCS#7 object: %s",
+			   ERR_error_string(ERR_get_error(), NULL));
+		write_result(ctx, "Could not parse PKCS#7 object from EST");
+		goto fail;
+	}
+#else /* OPENSSL_IS_BORINGSSL */
 	p7 = d2i_PKCS7(NULL, &p, len);
 	if (p7 == NULL) {
 		wpa_printf(MSG_INFO, "Could not parse PKCS#7 object: %s",
@@ -52,6 +71,7 @@
 		certs = NULL;
 		break;
 	}
+#endif /* OPENSSL_IS_BORINGSSL */
 
 	if (!certs || ((num = sk_X509_num(certs)) == 0)) {
 		wpa_printf(MSG_INFO, "No certificates found in PKCS#7 object");
@@ -84,7 +104,12 @@
 	ret = 0;
 
 fail:
+#ifdef OPENSSL_IS_BORINGSSL
+	if (certs)
+		sk_X509_pop_free(certs, X509_free);
+#else /* OPENSSL_IS_BORINGSSL */
 	PKCS7_free(p7);
+#endif /* OPENSSL_IS_BORINGSSL */
 	if (out)
 		BIO_free_all(out);
 
@@ -310,6 +335,23 @@
 	if (!csrattrs || ! csrattrs->attrs)
 		return;
 
+#ifdef OPENSSL_IS_BORINGSSL
+	num = sk_num(CHECKED_CAST(_STACK *, STACK_OF(AttrOrOID) *,
+				  csrattrs->attrs));
+	for (i = 0; i < num; i++) {
+		AttrOrOID *ao = sk_value(
+			CHECKED_CAST(_STACK *, const STACK_OF(AttrOrOID) *,
+				     csrattrs->attrs), i);
+		switch (ao->type) {
+		case 0:
+			add_csrattrs_oid(ctx, ao->d.oid, exts);
+			break;
+		case 1:
+			add_csrattrs_attr(ctx, ao->d.attribute, exts);
+			break;
+		}
+	}
+#else /* OPENSSL_IS_BORINGSSL */
 	num = SKM_sk_num(AttrOrOID, csrattrs->attrs);
 	for (i = 0; i < num; i++) {
 		AttrOrOID *ao = SKM_sk_value(AttrOrOID, csrattrs->attrs, i);
@@ -322,6 +364,7 @@
 			break;
 		}
 	}
+#endif /* OPENSSL_IS_BORINGSSL */
 }
 
 
@@ -340,6 +383,7 @@
 	STACK_OF(X509_EXTENSION) *exts = NULL;
 	X509_EXTENSION *ex;
 	BIO *out;
+	CONF *ctmp = NULL;
 
 	wpa_printf(MSG_INFO, "Generate RSA private key");
 	write_summary(ctx, "Generate RSA private key");
@@ -421,20 +465,20 @@
 	if (!exts)
 		goto fail;
 
-	ex = X509V3_EXT_conf_nid(NULL, NULL, NID_basic_constraints,
-				 "CA:FALSE");
+	ex = X509V3_EXT_nconf_nid(ctmp, NULL, NID_basic_constraints,
+				  "CA:FALSE");
 	if (ex == NULL ||
 	    !sk_X509_EXTENSION_push(exts, ex))
 		goto fail;
 
-	ex = X509V3_EXT_conf_nid(NULL, NULL, NID_key_usage,
-				 "nonRepudiation,digitalSignature,keyEncipherment");
+	ex = X509V3_EXT_nconf_nid(ctmp, NULL, NID_key_usage,
+				  "nonRepudiation,digitalSignature,keyEncipherment");
 	if (ex == NULL ||
 	    !sk_X509_EXTENSION_push(exts, ex))
 		goto fail;
 
-	ex = X509V3_EXT_conf_nid(NULL, NULL, NID_ext_key_usage,
-				 "1.3.6.1.4.1.40808.1.1.2");
+	ex = X509V3_EXT_nconf_nid(ctmp, NULL, NID_ext_key_usage,
+				  "1.3.6.1.4.1.40808.1.1.2");
 	if (ex == NULL ||
 	    !sk_X509_EXTENSION_push(exts, ex))
 		goto fail;
@@ -454,7 +498,9 @@
 		char *txt;
 		size_t rlen;
 
+#if !defined(ANDROID) || !defined(OPENSSL_IS_BORINGSSL)
 		X509_REQ_print(out, req);
+#endif
 		rlen = BIO_ctrl_pending(out);
 		txt = os_malloc(rlen + 1);
 		if (txt) {
@@ -473,7 +519,9 @@
 		FILE *f = fopen(csr_pem, "w");
 		if (f == NULL)
 			goto fail;
+#if !defined(ANDROID) || !defined(OPENSSL_IS_BORINGSSL)
 		X509_REQ_print_fp(f, req);
+#endif
 		if (!PEM_write_X509_REQ(f, req)) {
 			fclose(f);
 			goto fail;
diff --git a/src/Makefile b/src/Makefile
index 10e0171..c9e84c1 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -1,4 +1,5 @@
 SUBDIRS=ap common crypto drivers eapol_auth eapol_supp eap_common eap_peer eap_server l2_packet p2p pae radius rsn_supp tls utils wps
+SUBDIRS += fst
 
 all:
 	for d in $(SUBDIRS); do [ -d $$d ] && $(MAKE) -C $$d; done
diff --git a/src/ap/accounting.c b/src/ap/accounting.c
index a096de4..c60b3a6 100644
--- a/src/ap/accounting.c
+++ b/src/ap/accounting.c
@@ -1,6 +1,6 @@
 /*
  * hostapd / RADIUS Accounting
- * Copyright (c) 2002-2009, 2012, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2002-2009, 2012-2015, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -147,6 +147,15 @@
 			wpa_printf(MSG_ERROR, "Could not add CUI from ACL");
 			goto fail;
 		}
+
+		if (sta->ipaddr &&
+		    !radius_msg_add_attr_int32(msg,
+					       RADIUS_ATTR_FRAMED_IP_ADDRESS,
+					       be_to_host32(sta->ipaddr))) {
+			wpa_printf(MSG_ERROR,
+				   "Could not add Framed-IP-Address");
+			goto fail;
+		}
 	}
 
 	return msg;
diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c
index 455013a..cf9b2ce 100644
--- a/src/ap/ap_config.c
+++ b/src/ap/ap_config.c
@@ -38,6 +38,8 @@
 
 void hostapd_config_defaults_bss(struct hostapd_bss_config *bss)
 {
+	dl_list_init(&bss->anqp_elem);
+
 	bss->logger_syslog_level = HOSTAPD_LEVEL_INFO;
 	bss->logger_stdout_level = HOSTAPD_LEVEL_INFO;
 	bss->logger_syslog = (unsigned int) -1;
@@ -63,6 +65,7 @@
 	bss->dtim_period = 2;
 
 	bss->radius_server_auth_port = 1812;
+	bss->eap_sim_db_timeout = 1;
 	bss->ap_max_inactivity = AP_MAX_INACTIVITY;
 	bss->eapol_version = EAPOL_VERSION;
 
@@ -172,6 +175,7 @@
 
 	conf->ap_table_max_size = 255;
 	conf->ap_table_expiration_time = 60;
+	conf->track_sta_max_age = 180;
 
 #ifdef CONFIG_TESTING_OPTIONS
 	conf->ignore_probe_probability = 0.0;
@@ -179,6 +183,7 @@
 	conf->ignore_assoc_probability = 0.0;
 	conf->ignore_reassoc_probability = 0.0;
 	conf->corrupt_gtk_rekey_mic_probability = 0.0;
+	conf->ecsa_ie_only = 0;
 #endif /* CONFIG_TESTING_OPTIONS */
 
 	conf->acs = 0;
@@ -409,6 +414,19 @@
 }
 
 
+static void hostapd_config_free_anqp_elem(struct hostapd_bss_config *conf)
+{
+	struct anqp_element *elem;
+
+	while ((elem = dl_list_first(&conf->anqp_elem, struct anqp_element,
+				     list))) {
+		dl_list_del(&elem->list);
+		wpabuf_free(elem->payload);
+		os_free(elem);
+	}
+}
+
+
 void hostapd_config_free_bss(struct hostapd_bss_config *conf)
 {
 	struct hostapd_eap_user *user, *prev_user;
@@ -522,6 +540,7 @@
 	os_free(conf->network_auth_type);
 	os_free(conf->anqp_3gpp_cell_net);
 	os_free(conf->domain_name);
+	hostapd_config_free_anqp_elem(conf);
 
 #ifdef CONFIG_RADIUS_TEST
 	os_free(conf->dump_msk_file);
@@ -561,6 +580,13 @@
 
 	os_free(conf->server_id);
 
+#ifdef CONFIG_TESTING_OPTIONS
+	wpabuf_free(conf->own_ie_override);
+#endif /* CONFIG_TESTING_OPTIONS */
+
+	os_free(conf->no_probe_resp_if_seen_on);
+	os_free(conf->no_auth_if_seen_on);
+
 	os_free(conf);
 }
 
@@ -967,10 +993,11 @@
 		bss->rsn_pairwise = WPA_CIPHER_CCMP;
 	} else {
 		bss->ssid.security_policy = SECURITY_PLAINTEXT;
-		bss->wpa_group = WPA_CIPHER_NONE;
-		bss->wpa_pairwise = WPA_CIPHER_NONE;
-		bss->rsn_pairwise = WPA_CIPHER_NONE;
-		if (full_config)
+		if (full_config) {
+			bss->wpa_group = WPA_CIPHER_NONE;
+			bss->wpa_pairwise = WPA_CIPHER_NONE;
+			bss->rsn_pairwise = WPA_CIPHER_NONE;
 			bss->wpa_key_mgmt = WPA_KEY_MGMT_NONE;
+		}
 	}
 }
diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
index c14eeda..ff9dcb0 100644
--- a/src/ap/ap_config.h
+++ b/src/ap/ap_config.h
@@ -1,6 +1,6 @@
 /*
  * hostapd / Configuration definitions and helpers functions
- * Copyright (c) 2003-2012, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2003-2015, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -10,11 +10,13 @@
 #define HOSTAPD_CONFIG_H
 
 #include "common/defs.h"
+#include "utils/list.h"
 #include "ip_addr.h"
 #include "common/wpa_common.h"
 #include "common/ieee802_11_defs.h"
 #include "common/ieee802_11_common.h"
 #include "wps/wps.h"
+#include "fst/fst.h"
 
 /**
  * mesh_conf - local MBSS state and settings
@@ -32,8 +34,8 @@
 	u8 mesh_sp_id;
 	/* Authentication Protocol Identifier */
 	u8 mesh_auth_id;
-	u8 *ies;
-	int ie_len;
+	u8 *rsn_ie;
+	int rsn_ie_len;
 #define MESH_CONF_SEC_NONE BIT(0)
 #define MESH_CONF_SEC_AUTH BIT(1)
 #define MESH_CONF_SEC_AMPE BIT(2)
@@ -204,6 +206,13 @@
 	} eap_method[MAX_NAI_EAP_METHODS];
 };
 
+struct anqp_element {
+	struct dl_list list;
+	u16 infoid;
+	struct wpabuf *payload;
+};
+
+
 /**
  * struct hostapd_bss_config - Per-BSS configuration
  */
@@ -230,6 +239,7 @@
 	struct hostapd_eap_user *eap_user;
 	char *eap_user_sqlite;
 	char *eap_sim_db;
+	unsigned int eap_sim_db_timeout;
 	int eap_server_erp; /* Whether ERP is enabled on internal EAP server */
 	struct hostapd_ip_addr own_ip_addr;
 	char *nas_identifier;
@@ -329,6 +339,7 @@
 	char *private_key;
 	char *private_key_passwd;
 	int check_crl;
+	unsigned int tls_session_lifetime;
 	char *ocsp_stapling_response;
 	char *dh_file;
 	char *openssl_ciphers;
@@ -356,6 +367,7 @@
 
 	int ap_max_inactivity;
 	int ignore_broadcast_ssid;
+	int no_probe_resp_if_max_sta;
 
 	int wmm_enabled;
 	int wmm_uapsd;
@@ -479,6 +491,8 @@
 	unsigned int nai_realm_count;
 	struct hostapd_nai_realm_data *nai_realm_data;
 
+	struct dl_list anqp_elem; /* list of struct anqp_element */
+
 	u16 gas_comeback_delay;
 	int gas_frag_limit;
 
@@ -543,6 +557,7 @@
 #ifdef CONFIG_TESTING_OPTIONS
 	u8 bss_load_test[5];
 	u8 bss_load_test_set;
+	struct wpabuf *own_ie_override;
 #endif /* CONFIG_TESTING_OPTIONS */
 
 #define MESH_ENABLED BIT(0)
@@ -551,6 +566,9 @@
 	int radio_measurements;
 
 	int vendor_vht;
+
+	char *no_probe_resp_if_seen_on;
+	char *no_auth_if_seen_on;
 };
 
 
@@ -583,6 +601,9 @@
 	int ap_table_max_size;
 	int ap_table_expiration_time;
 
+	unsigned int track_sta_max_num;
+	unsigned int track_sta_max_age;
+
 	char country[3]; /* first two octets: country code as described in
 			  * ISO/IEC 3166-1. Third octet:
 			  * ' ' (ascii 32): all environments
@@ -619,6 +640,7 @@
 	u16 ht_capab;
 	int ieee80211n;
 	int secondary_channel;
+	int no_pri_sec_switch;
 	int require_ht;
 	int obss_interval;
 	u32 vht_capab;
@@ -628,6 +650,10 @@
 	u8 vht_oper_centr_freq_seg0_idx;
 	u8 vht_oper_centr_freq_seg1_idx;
 
+#ifdef CONFIG_FST
+	struct fst_iface_cfg fst_cfg;
+#endif /* CONFIG_FST */
+
 #ifdef CONFIG_P2P
 	u8 p2p_go_ctwindow;
 #endif /* CONFIG_P2P */
@@ -638,6 +664,7 @@
 	double ignore_assoc_probability;
 	double ignore_reassoc_probability;
 	double corrupt_gtk_rekey_mic_probability;
+	int ecsa_ie_only;
 #endif /* CONFIG_TESTING_OPTIONS */
 
 #ifdef CONFIG_ACS
diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
index f3f7edd..b390450 100644
--- a/src/ap/ap_drv_ops.c
+++ b/src/ap/ap_drv_ops.c
@@ -81,6 +81,22 @@
 		wpabuf_put_data(proberesp, buf, pos - buf);
 	}
 
+#ifdef CONFIG_FST
+	if (hapd->iface->fst_ies) {
+		size_t add = wpabuf_len(hapd->iface->fst_ies);
+
+		if (wpabuf_resize(&beacon, add) < 0)
+			goto fail;
+		wpabuf_put_buf(beacon, hapd->iface->fst_ies);
+		if (wpabuf_resize(&proberesp, add) < 0)
+			goto fail;
+		wpabuf_put_buf(proberesp, hapd->iface->fst_ies);
+		if (wpabuf_resize(&assocresp, add) < 0)
+			goto fail;
+		wpabuf_put_buf(assocresp, hapd->iface->fst_ies);
+	}
+#endif /* CONFIG_FST */
+
 	if (hapd->wps_beacon_ie) {
 		if (wpabuf_resize(&beacon, wpabuf_len(hapd->wps_beacon_ie)) <
 		    0)
@@ -452,7 +468,7 @@
 		return -1;
 	return hapd->driver->if_add(hapd->drv_priv, type, ifname, addr,
 				    bss_ctx, drv_priv, force_ifname, if_addr,
-				    bridge, use_existing);
+				    bridge, use_existing, 1);
 }
 
 
@@ -633,7 +649,19 @@
 {
 	if (hapd->driver == NULL || hapd->driver->send_mlme == NULL)
 		return 0;
-	return hapd->driver->send_mlme(hapd->drv_priv, msg, len, noack, 0);
+	return hapd->driver->send_mlme(hapd->drv_priv, msg, len, noack, 0,
+				       NULL, 0);
+}
+
+
+int hostapd_drv_send_mlme_csa(struct hostapd_data *hapd,
+			      const void *msg, size_t len, int noack,
+			      const u16 *csa_offs, size_t csa_offs_len)
+{
+	if (hapd->driver == NULL || hapd->driver->send_mlme == NULL)
+		return 0;
+	return hapd->driver->send_mlme(hapd->drv_priv, msg, len, noack, 0,
+				       csa_offs, csa_offs_len);
 }
 
 
@@ -727,6 +755,25 @@
 }
 
 
+static void hostapd_get_hw_mode_any_channels(struct hostapd_data *hapd,
+					     struct hostapd_hw_modes *mode,
+					     int acs_ch_list_all,
+					     int **freq_list)
+{
+	int i;
+
+	for (i = 0; i < mode->num_channels; i++) {
+		struct hostapd_channel_data *chan = &mode->channels[i];
+
+		if ((acs_ch_list_all ||
+		     freq_range_list_includes(&hapd->iface->conf->acs_ch_list,
+					      chan->chan)) &&
+		    !(chan->flag & HOSTAPD_CHAN_DISABLED))
+			int_array_add_unique(freq_list, chan->freq);
+	}
+}
+
+
 int hostapd_drv_do_acs(struct hostapd_data *hapd)
 {
 	struct drv_acs_params params;
@@ -734,6 +781,7 @@
 	u8 *channels = NULL;
 	unsigned int num_channels = 0;
 	struct hostapd_hw_modes *mode;
+	int *freq_list = NULL;
 
 	if (hapd->driver == NULL || hapd->driver->do_acs == NULL)
 		return 0;
@@ -749,24 +797,35 @@
 		acs_ch_list_all = 1;
 
 	mode = hapd->iface->current_mode;
-	if (mode == NULL)
-		return -1;
-	channels = os_malloc(mode->num_channels);
-	if (channels == NULL)
-		return -1;
+	if (mode) {
+		channels = os_malloc(mode->num_channels);
+		if (channels == NULL)
+			return -1;
 
-	for (i = 0; i < mode->num_channels; i++) {
-		struct hostapd_channel_data *chan = &mode->channels[i];
-		if (!acs_ch_list_all &&
-		    !freq_range_list_includes(&hapd->iface->conf->acs_ch_list,
-					      chan->chan))
-			continue;
-		if (!(chan->flag & HOSTAPD_CHAN_DISABLED))
-			channels[num_channels++] = chan->chan;
+		for (i = 0; i < mode->num_channels; i++) {
+			struct hostapd_channel_data *chan = &mode->channels[i];
+			if (!acs_ch_list_all &&
+			    !freq_range_list_includes(
+				    &hapd->iface->conf->acs_ch_list,
+				    chan->chan))
+				continue;
+			if (!(chan->flag & HOSTAPD_CHAN_DISABLED)) {
+				channels[num_channels++] = chan->chan;
+				int_array_add_unique(&freq_list, chan->freq);
+			}
+		}
+	} else {
+		for (i = 0; i < hapd->iface->num_hw_features; i++) {
+			mode = &hapd->iface->hw_features[i];
+			hostapd_get_hw_mode_any_channels(hapd, mode,
+							 acs_ch_list_all,
+							 &freq_list);
+		}
 	}
 
 	params.ch_list = channels;
 	params.ch_list_len = num_channels;
+	params.freq_list = freq_list;
 
 	params.ht_enabled = !!(hapd->iface->conf->ieee80211n);
 	params.ht40_enabled = !!(hapd->iface->conf->ht_capab &
diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
index 82eaf3f..5a1e28e 100644
--- a/src/ap/ap_drv_ops.h
+++ b/src/ap/ap_drv_ops.h
@@ -88,6 +88,9 @@
 			const u8 *key, size_t key_len);
 int hostapd_drv_send_mlme(struct hostapd_data *hapd,
 			  const void *msg, size_t len, int noack);
+int hostapd_drv_send_mlme_csa(struct hostapd_data *hapd,
+			      const void *msg, size_t len, int noack,
+			      const u16 *csa_offs, size_t csa_offs_len);
 int hostapd_drv_sta_deauth(struct hostapd_data *hapd,
 			   const u8 *addr, int reason);
 int hostapd_drv_sta_disassoc(struct hostapd_data *hapd,
diff --git a/src/ap/ap_list.c b/src/ap/ap_list.c
index 78a1f7c..8bf6dde 100644
--- a/src/ap/ap_list.c
+++ b/src/ap/ap_list.c
@@ -248,15 +248,12 @@
 }
 
 
-static void ap_list_timer(void *eloop_ctx, void *timeout_ctx)
+void ap_list_timer(struct hostapd_iface *iface)
 {
-	struct hostapd_iface *iface = eloop_ctx;
 	struct os_reltime now;
 	struct ap_info *ap;
 	int set_beacon = 0;
 
-	eloop_register_timeout(10, 0, ap_list_timer, iface, NULL);
-
 	if (!iface->ap_list)
 		return;
 
@@ -305,13 +302,11 @@
 
 int ap_list_init(struct hostapd_iface *iface)
 {
-	eloop_register_timeout(10, 0, ap_list_timer, iface, NULL);
 	return 0;
 }
 
 
 void ap_list_deinit(struct hostapd_iface *iface)
 {
-	eloop_cancel_timeout(ap_list_timer, iface, NULL);
 	hostapd_free_aps(iface);
 }
diff --git a/src/ap/ap_list.h b/src/ap/ap_list.h
index 93dc0ed..9e0353c 100644
--- a/src/ap/ap_list.h
+++ b/src/ap/ap_list.h
@@ -39,6 +39,7 @@
 #ifdef NEED_AP_MLME
 int ap_list_init(struct hostapd_iface *iface);
 void ap_list_deinit(struct hostapd_iface *iface);
+void ap_list_timer(struct hostapd_iface *iface);
 #else /* NEED_AP_MLME */
 static inline int ap_list_init(struct hostapd_iface *iface)
 {
@@ -48,6 +49,10 @@
 static inline void ap_list_deinit(struct hostapd_iface *iface)
 {
 }
+
+static inline void ap_list_timer(struct hostapd_iface *iface)
+{
+}
 #endif /* NEED_AP_MLME */
 
 #endif /* AP_LIST_H */
diff --git a/src/ap/authsrv.c b/src/ap/authsrv.c
index f10e1b7..c9111f6 100644
--- a/src/ap/authsrv.c
+++ b/src/ap/authsrv.c
@@ -132,6 +132,7 @@
 #endif /* CONFIG_HS20 */
 	srv.erp = conf->eap_server_erp;
 	srv.erp_domain = conf->erp_domain;
+	srv.tls_session_lifetime = conf->tls_session_lifetime;
 
 	hapd->radius_srv = radius_server_init(&srv);
 	if (hapd->radius_srv == NULL) {
@@ -151,9 +152,12 @@
 	if (hapd->conf->eap_server &&
 	    (hapd->conf->ca_cert || hapd->conf->server_cert ||
 	     hapd->conf->private_key || hapd->conf->dh_file)) {
+		struct tls_config conf;
 		struct tls_connection_params params;
 
-		hapd->ssl_ctx = tls_init(NULL);
+		os_memset(&conf, 0, sizeof(conf));
+		conf.tls_session_lifetime = hapd->conf->tls_session_lifetime;
+		hapd->ssl_ctx = tls_init(&conf);
 		if (hapd->ssl_ctx == NULL) {
 			wpa_printf(MSG_ERROR, "Failed to initialize TLS");
 			authsrv_deinit(hapd);
@@ -189,6 +193,7 @@
 	if (hapd->conf->eap_sim_db) {
 		hapd->eap_sim_db_priv =
 			eap_sim_db_init(hapd->conf->eap_sim_db,
+					hapd->conf->eap_sim_db_timeout,
 					hostapd_sim_db_cb, hapd);
 		if (hapd->eap_sim_db_priv == NULL) {
 			wpa_printf(MSG_ERROR, "Failed to initialize EAP-SIM "
diff --git a/src/ap/beacon.c b/src/ap/beacon.c
index 51d0c15..9490e21 100644
--- a/src/ap/beacon.c
+++ b/src/ap/beacon.c
@@ -297,65 +297,65 @@
 
 static u8 * hostapd_eid_csa(struct hostapd_data *hapd, u8 *eid)
 {
-	u8 chan;
-
-	if (!hapd->cs_freq_params.freq)
+#ifdef CONFIG_TESTING_OPTIONS
+	if (hapd->iface->cs_oper_class && hapd->iconf->ecsa_ie_only)
 		return eid;
+#endif /* CONFIG_TESTING_OPTIONS */
 
-	if (ieee80211_freq_to_chan(hapd->cs_freq_params.freq, &chan) ==
-	    NUM_HOSTAPD_MODES)
+	if (!hapd->cs_freq_params.channel)
 		return eid;
 
 	*eid++ = WLAN_EID_CHANNEL_SWITCH;
 	*eid++ = 3;
 	*eid++ = hapd->cs_block_tx;
-	*eid++ = chan;
+	*eid++ = hapd->cs_freq_params.channel;
 	*eid++ = hapd->cs_count;
 
 	return eid;
 }
 
 
-static u8 * hostapd_eid_secondary_channel(struct hostapd_data *hapd, u8 *eid)
+static u8 * hostapd_eid_ecsa(struct hostapd_data *hapd, u8 *eid)
 {
-	u8 sec_ch;
-
-	if (!hapd->cs_freq_params.sec_channel_offset)
+	if (!hapd->cs_freq_params.channel || !hapd->iface->cs_oper_class)
 		return eid;
 
-	if (hapd->cs_freq_params.sec_channel_offset == -1)
-		sec_ch = HT_INFO_HT_PARAM_SECONDARY_CHNL_BELOW;
-	else if (hapd->cs_freq_params.sec_channel_offset == 1)
-		sec_ch = HT_INFO_HT_PARAM_SECONDARY_CHNL_ABOVE;
-	else
-		return eid;
-
-	*eid++ = WLAN_EID_SECONDARY_CHANNEL_OFFSET;
-	*eid++ = 1;
-	*eid++ = sec_ch;
+	*eid++ = WLAN_EID_EXT_CHANSWITCH_ANN;
+	*eid++ = 4;
+	*eid++ = hapd->cs_block_tx;
+	*eid++ = hapd->iface->cs_oper_class;
+	*eid++ = hapd->cs_freq_params.channel;
+	*eid++ = hapd->cs_count;
 
 	return eid;
 }
 
 
-static u8 * hostapd_add_csa_elems(struct hostapd_data *hapd, u8 *pos,
-				  u8 *start, unsigned int *csa_counter_off)
+static u8 * hostapd_eid_supported_op_classes(struct hostapd_data *hapd, u8 *eid)
 {
-	u8 *old_pos = pos;
+	u8 op_class, channel;
 
-	if (!csa_counter_off)
-		return pos;
+	if (!(hapd->iface->drv_flags & WPA_DRIVER_FLAGS_AP_CSA) ||
+	    !hapd->iface->freq)
+		return eid;
 
-	*csa_counter_off = 0;
-	pos = hostapd_eid_csa(hapd, pos);
+	if (ieee80211_freq_to_channel_ext(hapd->iface->freq,
+					  hapd->iconf->secondary_channel,
+					  hapd->iconf->vht_oper_chwidth,
+					  &op_class, &channel) ==
+	    NUM_HOSTAPD_MODES)
+		return eid;
 
-	if (pos != old_pos) {
-		/* save an offset to the counter - should be last byte */
-		*csa_counter_off = pos - start - 1;
-		pos = hostapd_eid_secondary_channel(hapd, pos);
-	}
+	*eid++ = WLAN_EID_SUPPORTED_OPERATING_CLASSES;
+	*eid++ = 2;
 
-	return pos;
+	/* Current Operating Class */
+	*eid++ = op_class;
+
+	/* TODO: Advertise all the supported operating classes */
+	*eid++ = 0;
+
+	return eid;
 }
 
 
@@ -364,7 +364,7 @@
 				   int is_p2p, size_t *resp_len)
 {
 	struct ieee80211_mgmt *resp;
-	u8 *pos, *epos;
+	u8 *pos, *epos, *csa_pos;
 	size_t buflen;
 
 #define MAX_PROBERESP_LEN 768
@@ -377,6 +377,10 @@
 	if (hapd->p2p_probe_resp_ie)
 		buflen += wpabuf_len(hapd->p2p_probe_resp_ie);
 #endif /* CONFIG_P2P */
+#ifdef CONFIG_FST
+	if (hapd->iface->fst_ies)
+		buflen += wpabuf_len(hapd->iface->fst_ies);
+#endif /* CONFIG_FST */
 	if (hapd->conf->vendor_elements)
 		buflen += wpabuf_len(hapd->conf->vendor_elements);
 	if (hapd->conf->vendor_vht) {
@@ -420,6 +424,12 @@
 	/* Power Constraint element */
 	pos = hostapd_eid_pwr_constraint(hapd, pos);
 
+	/* CSA IE */
+	csa_pos = hostapd_eid_csa(hapd, pos);
+	if (csa_pos != pos)
+		hapd->cs_c_off_proberesp = csa_pos - (u8 *) resp - 1;
+	pos = csa_pos;
+
 	/* ERP Information element */
 	pos = hostapd_eid_erp_info(hapd, pos);
 
@@ -433,7 +443,19 @@
 
 	pos = hostapd_eid_rm_enabled_capab(hapd, pos, epos - pos);
 
+	/* eCSA IE */
+	csa_pos = hostapd_eid_ecsa(hapd, pos);
+	if (csa_pos != pos)
+		hapd->cs_c_off_ecsa_proberesp = csa_pos - (u8 *) resp - 1;
+	pos = csa_pos;
+
+	pos = hostapd_eid_supported_op_classes(hapd, pos);
+
 #ifdef CONFIG_IEEE80211N
+	/* Secondary Channel Offset element */
+	/* TODO: The standard doesn't specify a position for this element. */
+	pos = hostapd_eid_secondary_channel(hapd, pos);
+
 	pos = hostapd_eid_ht_capabilities(hapd, pos);
 	pos = hostapd_eid_ht_operation(hapd, pos);
 #endif /* CONFIG_IEEE80211N */
@@ -447,12 +469,20 @@
 	pos = hostapd_eid_adv_proto(hapd, pos);
 	pos = hostapd_eid_roaming_consortium(hapd, pos);
 
-	pos = hostapd_add_csa_elems(hapd, pos, (u8 *)resp,
-				    &hapd->cs_c_off_proberesp);
+#ifdef CONFIG_FST
+	if (hapd->iface->fst_ies) {
+		os_memcpy(pos, wpabuf_head(hapd->iface->fst_ies),
+			  wpabuf_len(hapd->iface->fst_ies));
+		pos += wpabuf_len(hapd->iface->fst_ies);
+	}
+#endif /* CONFIG_FST */
+
 #ifdef CONFIG_IEEE80211AC
 	if (hapd->iconf->ieee80211ac && !hapd->conf->disable_11ac) {
 		pos = hostapd_eid_vht_capabilities(hapd, pos);
 		pos = hostapd_eid_vht_operation(hapd, pos);
+		pos = hostapd_eid_txpower_envelope(hapd, pos);
+		pos = hostapd_eid_wb_chsw_wrapper(hapd, pos);
 	}
 	if (hapd->conf->vendor_vht)
 		pos = hostapd_eid_vendor_vht(hapd, pos);
@@ -524,8 +554,8 @@
 
 	pos = ssid_list;
 	end = ssid_list + ssid_list_len;
-	while (pos + 1 <= end) {
-		if (pos + 2 + pos[1] > end)
+	while (end - pos >= 1) {
+		if (2 + pos[1] > end - pos)
 			break;
 		if (pos[1] == 0)
 			wildcard = 1;
@@ -539,6 +569,102 @@
 }
 
 
+void sta_track_expire(struct hostapd_iface *iface, int force)
+{
+	struct os_reltime now;
+	struct hostapd_sta_info *info;
+
+	if (!iface->num_sta_seen)
+		return;
+
+	os_get_reltime(&now);
+	while ((info = dl_list_first(&iface->sta_seen, struct hostapd_sta_info,
+				     list))) {
+		if (!force &&
+		    !os_reltime_expired(&now, &info->last_seen,
+					iface->conf->track_sta_max_age))
+			break;
+		force = 0;
+
+		wpa_printf(MSG_MSGDUMP, "%s: Expire STA tracking entry for "
+			   MACSTR, iface->bss[0]->conf->iface,
+			   MAC2STR(info->addr));
+		dl_list_del(&info->list);
+		iface->num_sta_seen--;
+		os_free(info);
+	}
+}
+
+
+static struct hostapd_sta_info * sta_track_get(struct hostapd_iface *iface,
+					       const u8 *addr)
+{
+	struct hostapd_sta_info *info;
+
+	dl_list_for_each(info, &iface->sta_seen, struct hostapd_sta_info, list)
+		if (os_memcmp(addr, info->addr, ETH_ALEN) == 0)
+			return info;
+
+	return NULL;
+}
+
+
+void sta_track_add(struct hostapd_iface *iface, const u8 *addr)
+{
+	struct hostapd_sta_info *info;
+
+	info = sta_track_get(iface, addr);
+	if (info) {
+		/* Move the most recent entry to the end of the list */
+		dl_list_del(&info->list);
+		dl_list_add_tail(&iface->sta_seen, &info->list);
+		os_get_reltime(&info->last_seen);
+		return;
+	}
+
+	/* Add a new entry */
+	info = os_zalloc(sizeof(*info));
+	os_memcpy(info->addr, addr, ETH_ALEN);
+	os_get_reltime(&info->last_seen);
+
+	if (iface->num_sta_seen >= iface->conf->track_sta_max_num) {
+		/* Expire oldest entry to make room for a new one */
+		sta_track_expire(iface, 1);
+	}
+
+	wpa_printf(MSG_MSGDUMP, "%s: Add STA tracking entry for "
+		   MACSTR, iface->bss[0]->conf->iface, MAC2STR(addr));
+	dl_list_add_tail(&iface->sta_seen, &info->list);
+	iface->num_sta_seen++;
+}
+
+
+struct hostapd_data *
+sta_track_seen_on(struct hostapd_iface *iface, const u8 *addr,
+		  const char *ifname)
+{
+	struct hapd_interfaces *interfaces = iface->interfaces;
+	size_t i, j;
+
+	for (i = 0; i < interfaces->count; i++) {
+		struct hostapd_data *hapd = NULL;
+
+		iface = interfaces->iface[i];
+		for (j = 0; j < iface->num_bss; j++) {
+			hapd = iface->bss[j];
+			if (os_strcmp(ifname, hapd->conf->iface) == 0)
+				break;
+			hapd = NULL;
+		}
+
+		if (hapd && sta_track_get(iface, addr))
+			return hapd;
+	}
+
+	return NULL;
+}
+
+
 void handle_probe_req(struct hostapd_data *hapd,
 		      const struct ieee80211_mgmt *mgmt, size_t len,
 		      int ssi_signal)
@@ -550,10 +676,15 @@
 	size_t i, resp_len;
 	int noack;
 	enum ssid_match_result res;
+	int ret;
+	u16 csa_offs[2];
+	size_t csa_offs_len;
 
 	ie = mgmt->u.probe_req.variable;
 	if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.probe_req))
 		return;
+	if (hapd->iconf->track_sta_max_num)
+		sta_track_add(hapd->iface, mgmt->sa);
 	ie_len = len - (IEEE80211_HDRLEN + sizeof(mgmt->u.probe_req));
 
 	for (i = 0; hapd->probereq_cb && i < hapd->num_probereq_cb; i++)
@@ -702,6 +833,29 @@
 	/* TODO: verify that supp_rates contains at least one matching rate
 	 * with AP configuration */
 
+	if (hapd->conf->no_probe_resp_if_seen_on &&
+	    is_multicast_ether_addr(mgmt->da) &&
+	    is_multicast_ether_addr(mgmt->bssid) &&
+	    sta_track_seen_on(hapd->iface, mgmt->sa,
+			      hapd->conf->no_probe_resp_if_seen_on)) {
+		wpa_printf(MSG_MSGDUMP, "%s: Ignore Probe Request from " MACSTR
+			   " since STA has been seen on %s",
+			   hapd->conf->iface, MAC2STR(mgmt->sa),
+			   hapd->conf->no_probe_resp_if_seen_on);
+		return;
+	}
+
+	if (hapd->conf->no_probe_resp_if_max_sta &&
+	    is_multicast_ether_addr(mgmt->da) &&
+	    is_multicast_ether_addr(mgmt->bssid) &&
+	    hapd->num_sta >= hapd->conf->max_num_sta &&
+	    !ap_get_sta(hapd, mgmt->sa)) {
+		wpa_printf(MSG_MSGDUMP, "%s: Ignore Probe Request from " MACSTR
+			   " since no room for additional STA",
+			   hapd->conf->iface, MAC2STR(mgmt->sa));
+		return;
+	}
+
 #ifdef CONFIG_TESTING_OPTIONS
 	if (hapd->iconf->ignore_probe_probability > 0.0 &&
 	    drand48() < hapd->iconf->ignore_probe_probability) {
@@ -724,7 +878,22 @@
 	noack = !!(res == WILDCARD_SSID_MATCH &&
 		   is_broadcast_ether_addr(mgmt->da));
 
-	if (hostapd_drv_send_mlme(hapd, resp, resp_len, noack) < 0)
+	csa_offs_len = 0;
+	if (hapd->csa_in_progress) {
+		if (hapd->cs_c_off_proberesp)
+			csa_offs[csa_offs_len++] =
+				hapd->cs_c_off_proberesp;
+
+		if (hapd->cs_c_off_ecsa_proberesp)
+			csa_offs[csa_offs_len++] =
+				hapd->cs_c_off_ecsa_proberesp;
+	}
+
+	ret = hostapd_drv_send_mlme_csa(hapd, resp, resp_len, noack,
+					csa_offs_len ? csa_offs : NULL,
+					csa_offs_len);
+
+	if (ret < 0)
 		wpa_printf(MSG_INFO, "handle_probe_req: send failed");
 
 	os_free(resp);
@@ -783,7 +952,7 @@
 	size_t resp_len = 0;
 #ifdef NEED_AP_MLME
 	u16 capab_info;
-	u8 *pos, *tailpos;
+	u8 *pos, *tailpos, *csa_pos;
 
 #define BEACON_HEAD_BUF_SIZE 256
 #define BEACON_TAIL_BUF_SIZE 512
@@ -797,6 +966,10 @@
 	if (hapd->p2p_beacon_ie)
 		tail_len += wpabuf_len(hapd->p2p_beacon_ie);
 #endif /* CONFIG_P2P */
+#ifdef CONFIG_FST
+	if (hapd->iface->fst_ies)
+		tail_len += wpabuf_len(hapd->iface->fst_ies);
+#endif /* CONFIG_FST */
 	if (hapd->conf->vendor_elements)
 		tail_len += wpabuf_len(hapd->conf->vendor_elements);
 
@@ -860,6 +1033,12 @@
 	/* Power Constraint element */
 	tailpos = hostapd_eid_pwr_constraint(hapd, tailpos);
 
+	/* CSA IE */
+	csa_pos = hostapd_eid_csa(hapd, tailpos);
+	if (csa_pos != tailpos)
+		hapd->cs_c_off_beacon = csa_pos - tail - 1;
+	tailpos = csa_pos;
+
 	/* ERP Information element */
 	tailpos = hostapd_eid_erp_info(hapd, tailpos);
 
@@ -877,7 +1056,19 @@
 	tailpos = hostapd_eid_bss_load(hapd, tailpos,
 				       tail + BEACON_TAIL_BUF_SIZE - tailpos);
 
+	/* eCSA IE */
+	csa_pos = hostapd_eid_ecsa(hapd, tailpos);
+	if (csa_pos != tailpos)
+		hapd->cs_c_off_ecsa_beacon = csa_pos - tail - 1;
+	tailpos = csa_pos;
+
+	tailpos = hostapd_eid_supported_op_classes(hapd, tailpos);
+
 #ifdef CONFIG_IEEE80211N
+	/* Secondary Channel Offset element */
+	/* TODO: The standard doesn't specify a position for this element. */
+	tailpos = hostapd_eid_secondary_channel(hapd, tailpos);
+
 	tailpos = hostapd_eid_ht_capabilities(hapd, tailpos);
 	tailpos = hostapd_eid_ht_operation(hapd, tailpos);
 #endif /* CONFIG_IEEE80211N */
@@ -893,12 +1084,21 @@
 	tailpos = hostapd_eid_interworking(hapd, tailpos);
 	tailpos = hostapd_eid_adv_proto(hapd, tailpos);
 	tailpos = hostapd_eid_roaming_consortium(hapd, tailpos);
-	tailpos = hostapd_add_csa_elems(hapd, tailpos, tail,
-					&hapd->cs_c_off_beacon);
+
+#ifdef CONFIG_FST
+	if (hapd->iface->fst_ies) {
+		os_memcpy(tailpos, wpabuf_head(hapd->iface->fst_ies),
+			  wpabuf_len(hapd->iface->fst_ies));
+		tailpos += wpabuf_len(hapd->iface->fst_ies);
+	}
+#endif /* CONFIG_FST */
+
 #ifdef CONFIG_IEEE80211AC
 	if (hapd->iconf->ieee80211ac && !hapd->conf->disable_11ac) {
 		tailpos = hostapd_eid_vht_capabilities(hapd, tailpos);
 		tailpos = hostapd_eid_vht_operation(hapd, tailpos);
+		tailpos = hostapd_eid_txpower_envelope(hapd, tailpos);
+		tailpos = hostapd_eid_wb_chsw_wrapper(hapd, tailpos);
 	}
 	if (hapd->conf->vendor_vht)
 		tailpos = hostapd_eid_vendor_vht(hapd, tailpos);
diff --git a/src/ap/beacon.h b/src/ap/beacon.h
index 722159a..d98f42e 100644
--- a/src/ap/beacon.h
+++ b/src/ap/beacon.h
@@ -21,5 +21,10 @@
 int ieee802_11_build_ap_params(struct hostapd_data *hapd,
 			       struct wpa_driver_ap_params *params);
 void ieee802_11_free_ap_params(struct wpa_driver_ap_params *params);
+void sta_track_add(struct hostapd_iface *iface, const u8 *addr);
+void sta_track_expire(struct hostapd_iface *iface, int force);
+struct hostapd_data *
+sta_track_seen_on(struct hostapd_iface *iface, const u8 *addr,
+		  const char *ifname);
 
 #endif /* BEACON_H */
diff --git a/src/ap/ctrl_iface_ap.c b/src/ap/ctrl_iface_ap.c
index 60afcb0..c98978f 100644
--- a/src/ap/ctrl_iface_ap.c
+++ b/src/ap/ctrl_iface_ap.c
@@ -12,6 +12,7 @@
 #include "common/ieee802_11_defs.h"
 #include "common/sae.h"
 #include "eapol_auth/eapol_auth_sm.h"
+#include "fst/fst_ctrl_iface.h"
 #include "hostapd.h"
 #include "ieee802_1x.h"
 #include "wpa_auth.h"
@@ -206,7 +207,10 @@
 		return -1;
 	}
 
-	return hostapd_ctrl_iface_sta_mib(hapd, sta, buf, buflen);
+	ret = hostapd_ctrl_iface_sta_mib(hapd, sta, buf, buflen);
+	ret += fst_ctrl_iface_mb_info(addr, buf + ret, buflen - ret);
+
+	return ret;
 }
 
 
diff --git a/src/ap/dfs.c b/src/ap/dfs.c
index 715f19b..7273caa 100644
--- a/src/ap/dfs.c
+++ b/src/ap/dfs.c
@@ -817,16 +817,6 @@
 }
 
 
-static int hostapd_csa_in_progress(struct hostapd_iface *iface)
-{
-	unsigned int i;
-	for (i = 0; i < iface->num_bss; i++)
-		if (iface->bss[i]->csa_in_progress)
-			return 1;
-	return 0;
-}
-
-
 static int hostapd_dfs_start_channel_switch(struct hostapd_iface *iface)
 {
 	struct hostapd_channel_data *channel;
diff --git a/src/ap/drv_callbacks.c b/src/ap/drv_callbacks.c
index 6ecd094..fd07201 100644
--- a/src/ap/drv_callbacks.c
+++ b/src/ap/drv_callbacks.c
@@ -18,9 +18,11 @@
 #include "crypto/random.h"
 #include "p2p/p2p.h"
 #include "wps/wps.h"
+#include "fst/fst.h"
 #include "wnm_ap.h"
 #include "hostapd.h"
 #include "ieee802_11.h"
+#include "ieee802_11_auth.h"
 #include "sta_info.h"
 #include "accounting.h"
 #include "tkip_countermeasures.h"
@@ -42,10 +44,10 @@
 	struct ieee802_11_elems elems;
 	const u8 *ie;
 	size_t ielen;
-#ifdef CONFIG_IEEE80211R
+#if defined(CONFIG_IEEE80211R) || defined(CONFIG_IEEE80211W)
 	u8 buf[sizeof(struct ieee80211_mgmt) + 1024];
 	u8 *p = buf;
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R || CONFIG_IEEE80211W */
 	u16 reason = WLAN_REASON_UNSPECIFIED;
 	u16 status = WLAN_STATUS_SUCCESS;
 	const u8 *p2p_dev_addr = NULL;
@@ -58,8 +60,8 @@
 		 * running, so better make sure we stop processing such an
 		 * event here.
 		 */
-		wpa_printf(MSG_DEBUG, "hostapd_notif_assoc: Skip event with "
-			   "no address");
+		wpa_printf(MSG_DEBUG,
+			   "hostapd_notif_assoc: Skip event with no address");
 		return -1;
 	}
 	random_add_randomness(addr, ETH_ALEN);
@@ -89,8 +91,8 @@
 	} else {
 		ie = NULL;
 		ielen = 0;
-		wpa_printf(MSG_DEBUG, "STA did not include WPS/RSN/WPA IE in "
-			   "(Re)AssocReq");
+		wpa_printf(MSG_DEBUG,
+			   "STA did not include WPS/RSN/WPA IE in (Re)AssocReq");
 	}
 
 	sta = ap_get_sta(hapd, addr);
@@ -113,6 +115,14 @@
 	}
 	sta->flags &= ~(WLAN_STA_WPS | WLAN_STA_MAYBE_WPS | WLAN_STA_WPS2);
 
+	res = hostapd_check_acl(hapd, addr, NULL);
+	if (res != HOSTAPD_ACL_ACCEPT) {
+		wpa_printf(MSG_INFO, "STA " MACSTR " not allowed to connect",
+			   MAC2STR(addr));
+		reason = WLAN_REASON_UNSPECIFIED;
+		goto fail;
+	}
+
 #ifdef CONFIG_P2P
 	if (elems.p2p) {
 		wpabuf_free(sta->p2p_ie);
@@ -155,13 +165,20 @@
 		sta->hs20_ie = NULL;
 #endif /* CONFIG_HS20 */
 
+#ifdef CONFIG_FST
+	wpabuf_free(sta->mb_ies);
+	if (hapd->iface->fst)
+		sta->mb_ies = mb_ies_by_info(&elems.mb_ies);
+	else
+		sta->mb_ies = NULL;
+#endif /* CONFIG_FST */
+
 	if (hapd->conf->wpa) {
 		if (ie == NULL || ielen == 0) {
 #ifdef CONFIG_WPS
 			if (hapd->conf->wps_state) {
-				wpa_printf(MSG_DEBUG, "STA did not include "
-					   "WPA/RSN IE in (Re)Association "
-					   "Request - possible WPS use");
+				wpa_printf(MSG_DEBUG,
+					   "STA did not include WPA/RSN IE in (Re)Association Request - possible WPS use");
 				sta->flags |= WLAN_STA_MAYBE_WPS;
 				goto skip_wpa_check;
 			}
@@ -174,13 +191,14 @@
 		if (hapd->conf->wps_state && ie[0] == 0xdd && ie[1] >= 4 &&
 		    os_memcmp(ie + 2, "\x00\x50\xf2\x04", 4) == 0) {
 			struct wpabuf *wps;
+
 			sta->flags |= WLAN_STA_WPS;
 			wps = ieee802_11_vendor_ie_concat(ie, ielen,
 							  WPS_IE_VENDOR_TYPE);
 			if (wps) {
 				if (wps_is_20(wps)) {
-					wpa_printf(MSG_DEBUG, "WPS: STA "
-						   "supports WPS 2.0");
+					wpa_printf(MSG_DEBUG,
+						   "WPS: STA supports WPS 2.0");
 					sta->flags |= WLAN_STA_WPS2;
 				}
 				wpabuf_free(wps);
@@ -194,16 +212,17 @@
 							sta->addr,
 							p2p_dev_addr);
 		if (sta->wpa_sm == NULL) {
-			wpa_printf(MSG_ERROR, "Failed to initialize WPA state "
-				   "machine");
+			wpa_printf(MSG_ERROR,
+				   "Failed to initialize WPA state machine");
 			return -1;
 		}
 		res = wpa_validate_wpa_ie(hapd->wpa_auth, sta->wpa_sm,
 					  ie, ielen,
 					  elems.mdie, elems.mdie_len);
 		if (res != WPA_IE_OK) {
-			wpa_printf(MSG_DEBUG, "WPA/RSN information element "
-				   "rejected? (res %u)", res);
+			wpa_printf(MSG_DEBUG,
+				   "WPA/RSN information element rejected? (res %u)",
+				   res);
 			wpa_hexdump(MSG_DEBUG, "IE", ie, ielen);
 			if (res == WPA_INVALID_GROUP) {
 				reason = WLAN_REASON_GROUP_CIPHER_NOT_VALID;
@@ -246,14 +265,12 @@
 			if (sta->sa_query_count == 0)
 				ap_sta_start_sa_query(hapd, sta);
 
-#ifdef CONFIG_IEEE80211R
 			status = WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY;
 
 			p = hostapd_eid_assoc_comeback_time(hapd, sta, p);
 
 			hostapd_sta_assoc(hapd, addr, reassoc, status, buf,
 					  p - buf);
-#endif /* CONFIG_IEEE80211R */
 			return 0;
 		}
 
@@ -281,6 +298,7 @@
 	} else if (hapd->conf->wps_state) {
 #ifdef CONFIG_WPS
 		struct wpabuf *wps;
+
 		if (req_ies)
 			wps = ieee802_11_vendor_ie_concat(req_ies, req_ies_len,
 							  WPS_IE_VENDOR_TYPE);
@@ -297,8 +315,8 @@
 		if (wps) {
 			sta->flags |= WLAN_STA_WPS;
 			if (wps_is_20(wps)) {
-				wpa_printf(MSG_DEBUG, "WPS: STA supports "
-					   "WPS 2.0");
+				wpa_printf(MSG_DEBUG,
+					   "WPS: STA supports WPS 2.0");
 				sta->flags |= WLAN_STA_WPS2;
 			}
 		} else
@@ -320,8 +338,8 @@
 			sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth,
 							sta->addr, NULL);
 		if (sta->wpa_sm == NULL) {
-			wpa_printf(MSG_WARNING, "Failed to initialize WPA "
-				   "state machine");
+			wpa_printf(MSG_WARNING,
+				   "Failed to initialize WPA state machine");
 			return WLAN_STATUS_UNSPECIFIED_FAILURE;
 		}
 		if (wpa_validate_osen(hapd->wpa_auth, sta->wpa_sm,
@@ -393,8 +411,8 @@
 		 * was running, so better make sure we stop processing such an
 		 * event here.
 		 */
-		wpa_printf(MSG_DEBUG, "hostapd_notif_disassoc: Skip event "
-			   "with no address");
+		wpa_printf(MSG_DEBUG,
+			   "hostapd_notif_disassoc: Skip event with no address");
 		return;
 	}
 
@@ -403,8 +421,9 @@
 
 	sta = ap_get_sta(hapd, addr);
 	if (sta == NULL) {
-		wpa_printf(MSG_DEBUG, "Disassociation notification for "
-			   "unknown STA " MACSTR, MAC2STR(addr));
+		wpa_printf(MSG_DEBUG,
+			   "Disassociation notification for unknown STA "
+			   MACSTR, MAC2STR(addr));
 		return;
 	}
 
@@ -425,8 +444,8 @@
 		return;
 
 	hostapd_logger(hapd, addr, HOSTAPD_MODULE_IEEE80211,
-		       HOSTAPD_LEVEL_INFO, "disconnected due to excessive "
-		       "missing ACKs");
+		       HOSTAPD_LEVEL_INFO,
+		       "disconnected due to excessive missing ACKs");
 	hostapd_drv_sta_disassoc(hapd, addr, WLAN_REASON_DISASSOC_LOW_ACK);
 	if (sta)
 		ap_sta_disassociate(hapd, sta, WLAN_REASON_DISASSOC_LOW_ACK);
@@ -437,7 +456,8 @@
 			     int offset, int width, int cf1, int cf2)
 {
 #ifdef NEED_AP_MLME
-	int channel, chwidth, seg0_idx = 0, seg1_idx = 0, is_dfs;
+	int channel, chwidth, is_dfs;
+	u8 seg0_idx = 0, seg1_idx = 0;
 
 	hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211,
 		       HOSTAPD_LEVEL_INFO,
@@ -450,8 +470,8 @@
 	channel = hostapd_hw_get_channel(hapd, freq);
 	if (!channel) {
 		hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211,
-			       HOSTAPD_LEVEL_WARNING, "driver switched to "
-			       "bad channel!");
+			       HOSTAPD_LEVEL_WARNING,
+			       "driver switched to bad channel!");
 		return;
 	}
 
@@ -481,8 +501,8 @@
 			seg1_idx = (cf2 - 5000) / 5;
 		break;
 	default:
-		seg0_idx = hostapd_hw_get_channel(hapd, cf1);
-		seg1_idx = hostapd_hw_get_channel(hapd, cf2);
+		ieee80211_freq_to_chan(cf1, &seg0_idx);
+		ieee80211_freq_to_chan(cf2, &seg1_idx);
 		break;
 	}
 
@@ -688,8 +708,8 @@
 			sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth,
 							sta->addr, NULL);
 		if (sta->wpa_sm == NULL) {
-			wpa_printf(MSG_DEBUG, "FT: Failed to initialize WPA "
-				   "state machine");
+			wpa_printf(MSG_DEBUG,
+				   "FT: Failed to initialize WPA state machine");
 			status = WLAN_STATUS_UNSPECIFIED_FAILURE;
 			goto fail;
 		}
@@ -724,7 +744,7 @@
 	if (WLAN_FC_GET_STYPE(fc) != WLAN_FC_STYPE_ACTION)
 		return; /* handled by the driver */
 
-        wpa_printf(MSG_DEBUG, "RX_ACTION cat %d action plen %d",
+	wpa_printf(MSG_DEBUG, "RX_ACTION cat %d action plen %d",
 		   mgmt->u.action.category, (int) plen);
 
 	sta = ap_get_sta(hapd, mgmt->sa);
@@ -735,6 +755,7 @@
 #ifdef CONFIG_IEEE80211R
 	if (mgmt->u.action.category == WLAN_ACTION_FT) {
 		const u8 *payload = drv_mgmt->frame + 24 + 1;
+
 		wpa_ft_action_rx(sta->wpa_sm, payload, plen);
 	}
 #endif /* CONFIG_IEEE80211R */
@@ -751,6 +772,13 @@
 		ieee802_11_rx_wnm_action_ap(hapd, mgmt, drv_mgmt->frame_len);
 	}
 #endif /* CONFIG_WNM */
+#ifdef CONFIG_FST
+	if (mgmt->u.action.category == WLAN_ACTION_FST && hapd->iface->fst) {
+		fst_rx_action(hapd->iface->fst, mgmt, drv_mgmt->frame_len);
+		return;
+	}
+#endif /* CONFIG_FST */
+
 }
 
 
@@ -802,6 +830,7 @@
 	if (hapd->ext_mgmt_frame_handling) {
 		size_t hex_len = 2 * rx_mgmt->frame_len + 1;
 		char *hex = os_malloc(hex_len);
+
 		if (hex) {
 			wpa_snprintf_hex(hex, hex_len, rx_mgmt->frame,
 					 rx_mgmt->frame_len);
@@ -819,8 +848,7 @@
 
 	hapd = get_hapd_bssid(iface, bssid);
 	if (hapd == NULL) {
-		u16 fc;
-		fc = le_to_host16(hdr->frame_control);
+		u16 fc = le_to_host16(hdr->frame_control);
 
 		/*
 		 * Drop frames to unknown BSSIDs except for Beacon frames which
@@ -839,6 +867,7 @@
 
 	if (hapd == HAPD_BROADCAST) {
 		size_t i;
+
 		ret = 0;
 		for (i = 0; i < iface->num_bss; i++) {
 			/* if bss is set, driver will call this function for
@@ -865,6 +894,7 @@
 			       size_t len, u16 stype, int ok)
 {
 	struct ieee80211_hdr *hdr;
+
 	hdr = (struct ieee80211_hdr *) buf;
 	hapd = get_hapd_bssid(hapd->iface, get_hdr_bssid(hdr, len));
 	if (hapd == NULL || hapd == HAPD_BROADCAST)
@@ -878,6 +908,7 @@
 static int hostapd_event_new_sta(struct hostapd_data *hapd, const u8 *addr)
 {
 	struct sta_info *sta = ap_get_sta(hapd, addr);
+
 	if (sta)
 		return 0;
 
@@ -904,11 +935,10 @@
 	size_t j;
 
 	for (j = 0; j < iface->num_bss; j++) {
-		if ((sta = ap_get_sta(iface->bss[j], src))) {
-			if (sta->flags & WLAN_STA_ASSOC) {
-				hapd = iface->bss[j];
-				break;
-			}
+		sta = ap_get_sta(iface->bss[j], src);
+		if (sta && sta->flags & WLAN_STA_ASSOC) {
+			hapd = iface->bss[j];
+			break;
 		}
 	}
 
@@ -968,7 +998,8 @@
 	if (!chan || chan->flag & HOSTAPD_CHAN_DISABLED)
 		return;
 
-	wpa_printf(MSG_DEBUG, "Single Channel Survey: (freq=%d channel_time=%ld channel_time_busy=%ld)",
+	wpa_printf(MSG_DEBUG,
+		   "Single Channel Survey: (freq=%d channel_time=%ld channel_time_busy=%ld)",
 		   survey->freq,
 		   (unsigned long int) survey->channel_time,
 		   (unsigned long int) survey->channel_time_busy);
@@ -1102,6 +1133,7 @@
 	    data->rx_mgmt.frame_len >= 24) {
 		const struct ieee80211_hdr *hdr;
 		u16 fc;
+
 		hdr = (const struct ieee80211_hdr *) data->rx_mgmt.frame;
 		fc = le_to_host16(hdr->frame_control);
 		if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT &&
diff --git a/src/ap/gas_serv.c b/src/ap/gas_serv.c
index 9d19f98..4bcdf6f 100644
--- a/src/ap/gas_serv.c
+++ b/src/ap/gas_serv.c
@@ -167,27 +167,107 @@
 #endif /* CONFIG_HS20 */
 
 
+static struct anqp_element * get_anqp_elem(struct hostapd_data *hapd,
+					   u16 infoid)
+{
+	struct anqp_element *elem;
+
+	dl_list_for_each(elem, &hapd->conf->anqp_elem, struct anqp_element,
+			 list) {
+		if (elem->infoid == infoid)
+			return elem;
+	}
+
+	return NULL;
+}
+
+
+static void anqp_add_elem(struct hostapd_data *hapd, struct wpabuf *buf,
+			  u16 infoid)
+{
+	struct anqp_element *elem;
+
+	elem = get_anqp_elem(hapd, infoid);
+	if (!elem)
+		return;
+	if (wpabuf_tailroom(buf) < 2 + 2 + wpabuf_len(elem->payload)) {
+		wpa_printf(MSG_DEBUG, "ANQP: No room for InfoID %u payload",
+			   infoid);
+		return;
+	}
+
+	wpabuf_put_le16(buf, infoid);
+	wpabuf_put_le16(buf, wpabuf_len(elem->payload));
+	wpabuf_put_buf(buf, elem->payload);
+}
+
+
+static int anqp_add_override(struct hostapd_data *hapd, struct wpabuf *buf,
+			     u16 infoid)
+{
+	if (get_anqp_elem(hapd, infoid)) {
+		anqp_add_elem(hapd, buf, infoid);
+		return 1;
+	}
+
+	return 0;
+}
+
+
 static void anqp_add_capab_list(struct hostapd_data *hapd,
 				struct wpabuf *buf)
 {
 	u8 *len;
+	u16 id;
+
+	if (anqp_add_override(hapd, buf, ANQP_CAPABILITY_LIST))
+		return;
 
 	len = gas_anqp_add_element(buf, ANQP_CAPABILITY_LIST);
 	wpabuf_put_le16(buf, ANQP_CAPABILITY_LIST);
-	if (hapd->conf->venue_name)
+	if (hapd->conf->venue_name || get_anqp_elem(hapd, ANQP_VENUE_NAME))
 		wpabuf_put_le16(buf, ANQP_VENUE_NAME);
-	if (hapd->conf->network_auth_type)
+	if (get_anqp_elem(hapd, ANQP_EMERGENCY_CALL_NUMBER))
+		wpabuf_put_le16(buf, ANQP_EMERGENCY_CALL_NUMBER);
+	if (hapd->conf->network_auth_type ||
+	    get_anqp_elem(hapd, ANQP_NETWORK_AUTH_TYPE))
 		wpabuf_put_le16(buf, ANQP_NETWORK_AUTH_TYPE);
-	if (hapd->conf->roaming_consortium)
+	if (hapd->conf->roaming_consortium ||
+	    get_anqp_elem(hapd, ANQP_ROAMING_CONSORTIUM))
 		wpabuf_put_le16(buf, ANQP_ROAMING_CONSORTIUM);
-	if (hapd->conf->ipaddr_type_configured)
+	if (hapd->conf->ipaddr_type_configured ||
+	    get_anqp_elem(hapd, ANQP_IP_ADDR_TYPE_AVAILABILITY))
 		wpabuf_put_le16(buf, ANQP_IP_ADDR_TYPE_AVAILABILITY);
-	if (hapd->conf->nai_realm_data)
+	if (hapd->conf->nai_realm_data ||
+	    get_anqp_elem(hapd, ANQP_NAI_REALM))
 		wpabuf_put_le16(buf, ANQP_NAI_REALM);
-	if (hapd->conf->anqp_3gpp_cell_net)
+	if (hapd->conf->anqp_3gpp_cell_net ||
+	    get_anqp_elem(hapd, ANQP_3GPP_CELLULAR_NETWORK))
 		wpabuf_put_le16(buf, ANQP_3GPP_CELLULAR_NETWORK);
-	if (hapd->conf->domain_name)
+	if (get_anqp_elem(hapd, ANQP_AP_GEOSPATIAL_LOCATION))
+		wpabuf_put_le16(buf, ANQP_AP_GEOSPATIAL_LOCATION);
+	if (get_anqp_elem(hapd, ANQP_AP_CIVIC_LOCATION))
+		wpabuf_put_le16(buf, ANQP_AP_CIVIC_LOCATION);
+	if (get_anqp_elem(hapd, ANQP_AP_LOCATION_PUBLIC_URI))
+		wpabuf_put_le16(buf, ANQP_AP_LOCATION_PUBLIC_URI);
+	if (hapd->conf->domain_name || get_anqp_elem(hapd, ANQP_DOMAIN_NAME))
 		wpabuf_put_le16(buf, ANQP_DOMAIN_NAME);
+	if (get_anqp_elem(hapd, ANQP_EMERGENCY_ALERT_URI))
+		wpabuf_put_le16(buf, ANQP_EMERGENCY_ALERT_URI);
+	if (get_anqp_elem(hapd, ANQP_EMERGENCY_NAI))
+		wpabuf_put_le16(buf, ANQP_EMERGENCY_NAI);
+	if (get_anqp_elem(hapd, ANQP_NEIGHBOR_REPORT))
+		wpabuf_put_le16(buf, ANQP_NEIGHBOR_REPORT);
+	for (id = 273; id < 277; id++) {
+		if (get_anqp_elem(hapd, id))
+			wpabuf_put_le16(buf, id);
+	}
+	if (get_anqp_elem(hapd, ANQP_VENUE_URL))
+		wpabuf_put_le16(buf, ANQP_VENUE_URL);
+	if (get_anqp_elem(hapd, ANQP_ADVICE_OF_CHARGE))
+		wpabuf_put_le16(buf, ANQP_ADVICE_OF_CHARGE);
+	if (get_anqp_elem(hapd, ANQP_LOCAL_CONTENT))
+		wpabuf_put_le16(buf, ANQP_LOCAL_CONTENT);
 #ifdef CONFIG_HS20
 	anqp_add_hs_capab_list(hapd, buf);
 #endif /* CONFIG_HS20 */
@@ -197,6 +277,9 @@
 
 static void anqp_add_venue_name(struct hostapd_data *hapd, struct wpabuf *buf)
 {
+	if (anqp_add_override(hapd, buf, ANQP_VENUE_NAME))
+		return;
+
 	if (hapd->conf->venue_name) {
 		u8 *len;
 		unsigned int i;
@@ -218,6 +301,9 @@
 static void anqp_add_network_auth_type(struct hostapd_data *hapd,
 				       struct wpabuf *buf)
 {
+	if (anqp_add_override(hapd, buf, ANQP_NETWORK_AUTH_TYPE))
+		return;
+
 	if (hapd->conf->network_auth_type) {
 		wpabuf_put_le16(buf, ANQP_NETWORK_AUTH_TYPE);
 		wpabuf_put_le16(buf, hapd->conf->network_auth_type_len);
@@ -233,6 +319,9 @@
 	unsigned int i;
 	u8 *len;
 
+	if (anqp_add_override(hapd, buf, ANQP_ROAMING_CONSORTIUM))
+		return;
+
 	len = gas_anqp_add_element(buf, ANQP_ROAMING_CONSORTIUM);
 	for (i = 0; i < hapd->conf->roaming_consortium_count; i++) {
 		struct hostapd_roaming_consortium *rc;
@@ -247,6 +336,9 @@
 static void anqp_add_ip_addr_type_availability(struct hostapd_data *hapd,
 					       struct wpabuf *buf)
 {
+	if (anqp_add_override(hapd, buf, ANQP_IP_ADDR_TYPE_AVAILABILITY))
+		return;
+
 	if (hapd->conf->ipaddr_type_configured) {
 		wpabuf_put_le16(buf, ANQP_IP_ADDR_TYPE_AVAILABILITY);
 		wpabuf_put_le16(buf, 1);
@@ -309,7 +401,7 @@
 
 	pos = home_realm;
 	end = pos + home_realm_len;
-	if (pos + 1 > end) {
+	if (end - pos < 1) {
 		wpa_hexdump(MSG_DEBUG, "Too short NAI Home Realm Query",
 			    home_realm, home_realm_len);
 		return -1;
@@ -317,7 +409,7 @@
 	num_realms = *pos++;
 
 	for (i = 0; i < num_realms && num_matching < 10; i++) {
-		if (pos + 2 > end) {
+		if (end - pos < 2) {
 			wpa_hexdump(MSG_DEBUG,
 				    "Truncated NAI Home Realm Query",
 				    home_realm, home_realm_len);
@@ -325,7 +417,7 @@
 		}
 		encoding = *pos++;
 		realm_len = *pos++;
-		if (pos + realm_len > end) {
+		if (realm_len > end - pos) {
 			wpa_hexdump(MSG_DEBUG,
 				    "Truncated NAI Home Realm Query",
 				    home_realm, home_realm_len);
@@ -391,6 +483,10 @@
 			       const u8 *home_realm, size_t home_realm_len,
 			       int nai_realm, int nai_home_realm)
 {
+	if (nai_realm && !nai_home_realm &&
+	    anqp_add_override(hapd, buf, ANQP_NAI_REALM))
+		return;
+
 	if (nai_realm && hapd->conf->nai_realm_data) {
 		u8 *len;
 		unsigned int i, j;
@@ -424,6 +520,9 @@
 static void anqp_add_3gpp_cellular_network(struct hostapd_data *hapd,
 					   struct wpabuf *buf)
 {
+	if (anqp_add_override(hapd, buf, ANQP_3GPP_CELLULAR_NETWORK))
+		return;
+
 	if (hapd->conf->anqp_3gpp_cell_net) {
 		wpabuf_put_le16(buf, ANQP_3GPP_CELLULAR_NETWORK);
 		wpabuf_put_le16(buf,
@@ -436,6 +535,9 @@
 
 static void anqp_add_domain_name(struct hostapd_data *hapd, struct wpabuf *buf)
 {
+	if (anqp_add_override(hapd, buf, ANQP_DOMAIN_NAME))
+		return;
+
 	if (hapd->conf->domain_name) {
 		wpabuf_put_le16(buf, ANQP_DOMAIN_NAME);
 		wpabuf_put_le16(buf, hapd->conf->domain_name_len);
@@ -687,16 +789,20 @@
 gas_serv_build_gas_resp_payload(struct hostapd_data *hapd,
 				unsigned int request,
 				const u8 *home_realm, size_t home_realm_len,
-				const u8 *icon_name, size_t icon_name_len)
+				const u8 *icon_name, size_t icon_name_len,
+				const u16 *extra_req,
+				unsigned int num_extra_req)
 {
 	struct wpabuf *buf;
 	size_t len;
+	unsigned int i;
 
 	len = 1400;
 	if (request & (ANQP_REQ_NAI_REALM | ANQP_REQ_NAI_HOME_REALM))
 		len += 1000;
 	if (request & ANQP_REQ_ICON_REQUEST)
 		len += 65536;
+	len += num_extra_req * 1000;
 
 	buf = wpabuf_alloc(len);
 	if (buf == NULL)
@@ -706,6 +812,8 @@
 		anqp_add_capab_list(hapd, buf);
 	if (request & ANQP_REQ_VENUE_NAME)
 		anqp_add_venue_name(hapd, buf);
+	if (request & ANQP_REQ_EMERGENCY_CALL_NUMBER)
+		anqp_add_elem(hapd, buf, ANQP_EMERGENCY_CALL_NUMBER);
 	if (request & ANQP_REQ_NETWORK_AUTH_TYPE)
 		anqp_add_network_auth_type(hapd, buf);
 	if (request & ANQP_REQ_ROAMING_CONSORTIUM)
@@ -718,8 +826,23 @@
 				   request & ANQP_REQ_NAI_HOME_REALM);
 	if (request & ANQP_REQ_3GPP_CELLULAR_NETWORK)
 		anqp_add_3gpp_cellular_network(hapd, buf);
+	if (request & ANQP_REQ_AP_GEOSPATIAL_LOCATION)
+		anqp_add_elem(hapd, buf, ANQP_AP_GEOSPATIAL_LOCATION);
+	if (request & ANQP_REQ_AP_CIVIC_LOCATION)
+		anqp_add_elem(hapd, buf, ANQP_AP_CIVIC_LOCATION);
+	if (request & ANQP_REQ_AP_LOCATION_PUBLIC_URI)
+		anqp_add_elem(hapd, buf, ANQP_AP_LOCATION_PUBLIC_URI);
 	if (request & ANQP_REQ_DOMAIN_NAME)
 		anqp_add_domain_name(hapd, buf);
+	if (request & ANQP_REQ_EMERGENCY_ALERT_URI)
+		anqp_add_elem(hapd, buf, ANQP_EMERGENCY_ALERT_URI);
+	if (request & ANQP_REQ_TDLS_CAPABILITY)
+		anqp_add_elem(hapd, buf, ANQP_TDLS_CAPABILITY);
+	if (request & ANQP_REQ_EMERGENCY_NAI)
+		anqp_add_elem(hapd, buf, ANQP_EMERGENCY_NAI);
+
+	for (i = 0; i < num_extra_req; i++)
+		anqp_add_elem(hapd, buf, extra_req[i]);
 
 #ifdef CONFIG_HS20
 	if (request & ANQP_REQ_HS_CAPABILITY_LIST)
@@ -742,6 +865,8 @@
 }
 
 
+#define ANQP_MAX_EXTRA_REQ 20
+
 struct anqp_query_info {
 	unsigned int request;
 	const u8 *home_realm_query;
@@ -749,6 +874,8 @@
 	const u8 *icon_name;
 	size_t icon_name_len;
 	int p2p_sd;
+	u16 extra_req[ANQP_MAX_EXTRA_REQ];
+	unsigned int num_extra_req;
 };
 
 
@@ -776,6 +903,11 @@
 		set_anqp_req(ANQP_REQ_VENUE_NAME, "Venue Name",
 			     hapd->conf->venue_name != NULL, qi);
 		break;
+	case ANQP_EMERGENCY_CALL_NUMBER:
+		set_anqp_req(ANQP_REQ_EMERGENCY_CALL_NUMBER,
+			     "Emergency Call Number",
+			     get_anqp_elem(hapd, info_id) != NULL, qi);
+		break;
 	case ANQP_NETWORK_AUTH_TYPE:
 		set_anqp_req(ANQP_REQ_NETWORK_AUTH_TYPE, "Network Auth Type",
 			     hapd->conf->network_auth_type != NULL, qi);
@@ -798,13 +930,55 @@
 			     "3GPP Cellular Network",
 			     hapd->conf->anqp_3gpp_cell_net != NULL, qi);
 		break;
+	case ANQP_AP_GEOSPATIAL_LOCATION:
+		set_anqp_req(ANQP_REQ_AP_GEOSPATIAL_LOCATION,
+			     "AP Geospatial Location",
+			     get_anqp_elem(hapd, info_id) != NULL, qi);
+		break;
+	case ANQP_AP_CIVIC_LOCATION:
+		set_anqp_req(ANQP_REQ_AP_CIVIC_LOCATION,
+			     "AP Civic Location",
+			     get_anqp_elem(hapd, info_id) != NULL, qi);
+		break;
+	case ANQP_AP_LOCATION_PUBLIC_URI:
+		set_anqp_req(ANQP_REQ_AP_LOCATION_PUBLIC_URI,
+			     "AP Location Public URI",
+			     get_anqp_elem(hapd, info_id) != NULL, qi);
+		break;
 	case ANQP_DOMAIN_NAME:
 		set_anqp_req(ANQP_REQ_DOMAIN_NAME, "Domain Name",
 			     hapd->conf->domain_name != NULL, qi);
 		break;
+	case ANQP_EMERGENCY_ALERT_URI:
+		set_anqp_req(ANQP_REQ_EMERGENCY_ALERT_URI,
+			     "Emergency Alert URI",
+			     get_anqp_elem(hapd, info_id) != NULL, qi);
+		break;
+	case ANQP_TDLS_CAPABILITY:
+		set_anqp_req(ANQP_REQ_TDLS_CAPABILITY,
+			     "TDLS Capability",
+			     get_anqp_elem(hapd, info_id) != NULL, qi);
+		break;
+	case ANQP_EMERGENCY_NAI:
+		set_anqp_req(ANQP_REQ_EMERGENCY_NAI,
+			     "Emergency NAI",
+			     get_anqp_elem(hapd, info_id) != NULL, qi);
+		break;
 	default:
-		wpa_printf(MSG_DEBUG, "ANQP: Unsupported Info Id %u",
-			   info_id);
+		if (!get_anqp_elem(hapd, info_id)) {
+			wpa_printf(MSG_DEBUG, "ANQP: Unsupported Info Id %u",
+				   info_id);
+			break;
+		}
+		if (qi->num_extra_req == ANQP_MAX_EXTRA_REQ) {
+			wpa_printf(MSG_DEBUG,
+				   "ANQP: No more room for extra requests - ignore Info Id %u",
+				   info_id);
+			break;
+		}
+		wpa_printf(MSG_DEBUG, "ANQP: Info Id %u (local)", info_id);
+		qi->extra_req[qi->num_extra_req] = info_id;
+		qi->num_extra_req++;
 		break;
 	}
 }
@@ -817,7 +991,7 @@
 	wpa_printf(MSG_DEBUG, "ANQP: %u Info IDs requested in Query list",
 		   (unsigned int) (end - pos) / 2);
 
-	while (pos + 2 <= end) {
+	while (end - pos >= 2) {
 		rx_anqp_query_list_id(hapd, WPA_GET_LE16(pos), qi);
 		pos += 2;
 	}
@@ -906,7 +1080,7 @@
 	u32 oui;
 	u8 subtype;
 
-	if (pos + 4 > end) {
+	if (end - pos < 4) {
 		wpa_printf(MSG_DEBUG, "ANQP: Too short vendor specific ANQP "
 			   "Query element");
 		return;
@@ -942,7 +1116,7 @@
 	}
 	pos++;
 
-	if (pos + 1 >= end)
+	if (end - pos <= 1)
 		return;
 
 	subtype = *pos++;
@@ -980,7 +1154,8 @@
 	buf = gas_serv_build_gas_resp_payload(hapd, qi->request,
 					      qi->home_realm_query,
 					      qi->home_realm_query_len,
-					      qi->icon_name, qi->icon_name_len);
+					      qi->icon_name, qi->icon_name_len,
+					      qi->extra_req, qi->num_extra_req);
 	wpa_hexdump_buf(MSG_MSGDUMP, "ANQP: Locally generated ANQP responses",
 			buf);
 	if (!buf)
@@ -1069,12 +1244,12 @@
 	adv_proto = pos++;
 
 	slen = *pos++;
-	next = pos + slen;
-	if (next > end || slen < 2) {
+	if (slen > end - pos || slen < 2) {
 		wpa_msg(hapd->msg_ctx, MSG_DEBUG,
 			"GAS: Invalid IE in GAS Initial Request");
 		return;
 	}
+	next = pos + slen;
 	pos++; /* skip QueryRespLenLimit and PAME-BI */
 
 	if (*pos != ACCESS_NETWORK_QUERY_PROTOCOL) {
@@ -1101,11 +1276,11 @@
 
 	pos = next;
 	/* Query Request */
-	if (pos + 2 > end)
+	if (end - pos < 2)
 		return;
 	slen = WPA_GET_LE16(pos);
 	pos += 2;
-	if (pos + slen > end)
+	if (slen > end - pos)
 		return;
 	end = pos + slen;
 
@@ -1113,7 +1288,7 @@
 	while (pos < end) {
 		u16 info_id, elen;
 
-		if (pos + 4 > end)
+		if (end - pos < 4)
 			return;
 
 		info_id = WPA_GET_LE16(pos);
@@ -1121,7 +1296,7 @@
 		elen = WPA_GET_LE16(pos);
 		pos += 2;
 
-		if (pos + elen > end) {
+		if (elen > end - pos) {
 			wpa_printf(MSG_DEBUG, "ANQP: Invalid Query Request");
 			return;
 		}
diff --git a/src/ap/gas_serv.h b/src/ap/gas_serv.h
index 4ec3201..9051e4f 100644
--- a/src/ap/gas_serv.h
+++ b/src/ap/gas_serv.h
@@ -9,10 +9,13 @@
 #ifndef GAS_SERV_H
 #define GAS_SERV_H
 
+/* First 16 ANQP InfoIDs can be included in the optimized bitmap */
 #define ANQP_REQ_CAPABILITY_LIST \
 	(1 << (ANQP_CAPABILITY_LIST - ANQP_QUERY_LIST))
 #define ANQP_REQ_VENUE_NAME \
 	(1 << (ANQP_VENUE_NAME - ANQP_QUERY_LIST))
+#define ANQP_REQ_EMERGENCY_CALL_NUMBER \
+	(1 << (ANQP_EMERGENCY_CALL_NUMBER - ANQP_QUERY_LIST))
 #define ANQP_REQ_NETWORK_AUTH_TYPE \
 	(1 << (ANQP_NETWORK_AUTH_TYPE - ANQP_QUERY_LIST))
 #define ANQP_REQ_ROAMING_CONSORTIUM \
@@ -23,8 +26,24 @@
 	(1 << (ANQP_NAI_REALM - ANQP_QUERY_LIST))
 #define ANQP_REQ_3GPP_CELLULAR_NETWORK \
 	(1 << (ANQP_3GPP_CELLULAR_NETWORK - ANQP_QUERY_LIST))
+#define ANQP_REQ_AP_GEOSPATIAL_LOCATION \
+	(1 << (ANQP_AP_GEOSPATIAL_LOCATION - ANQP_QUERY_LIST))
+#define ANQP_REQ_AP_CIVIC_LOCATION \
+	(1 << (ANQP_AP_CIVIC_LOCATION - ANQP_QUERY_LIST))
+#define ANQP_REQ_AP_LOCATION_PUBLIC_URI \
+	(1 << (ANQP_AP_LOCATION_PUBLIC_URI - ANQP_QUERY_LIST))
 #define ANQP_REQ_DOMAIN_NAME \
 	(1 << (ANQP_DOMAIN_NAME - ANQP_QUERY_LIST))
+#define ANQP_REQ_EMERGENCY_ALERT_URI \
+	(1 << (ANQP_EMERGENCY_ALERT_URI - ANQP_QUERY_LIST))
+#define ANQP_REQ_TDLS_CAPABILITY \
+	(1 << (ANQP_TDLS_CAPABILITY - ANQP_QUERY_LIST))
+#define ANQP_REQ_EMERGENCY_NAI \
+	(1 << (ANQP_EMERGENCY_NAI - ANQP_QUERY_LIST))
+/*
+ * First 16 Hotspot 2.0 vendor specific ANQP-elements can be included in the
+ * optimized bitmap.
+ */
 #define ANQP_REQ_HS_CAPABILITY_LIST \
 	(0x10000 << HS20_STYPE_CAPABILITY_LIST)
 #define ANQP_REQ_OPERATOR_FRIENDLY_NAME \
diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
index 5abe5ed..b10b454 100644
--- a/src/ap/hostapd.c
+++ b/src/ap/hostapd.c
@@ -12,11 +12,13 @@
 #include "utils/eloop.h"
 #include "common/ieee802_11_defs.h"
 #include "common/wpa_ctrl.h"
+#include "common/hw_features_common.h"
 #include "radius/radius_client.h"
 #include "radius/radius_das.h"
 #include "eap_server/tncs.h"
 #include "eapol_auth/eapol_auth_sm.h"
 #include "eapol_auth/eapol_auth_sm_i.h"
+#include "fst/fst.h"
 #include "hostapd.h"
 #include "authsrv.h"
 #include "sta_info.h"
@@ -260,6 +262,7 @@
 {
 	os_free(hapd->probereq_cb);
 	hapd->probereq_cb = NULL;
+	hapd->num_probereq_cb = 0;
 
 #ifdef CONFIG_P2P
 	wpabuf_free(hapd->p2p_beacon_ie);
@@ -354,6 +357,22 @@
 }
 
 
+static void sta_track_deinit(struct hostapd_iface *iface)
+{
+	struct hostapd_sta_info *info;
+
+	if (!iface->num_sta_seen)
+		return;
+
+	while ((info = dl_list_first(&iface->sta_seen, struct hostapd_sta_info,
+				     list))) {
+		dl_list_del(&info->list);
+		iface->num_sta_seen--;
+		os_free(info);
+	}
+}
+
+
 static void hostapd_cleanup_iface_partial(struct hostapd_iface *iface)
 {
 	wpa_printf(MSG_DEBUG, "%s(%p)", __func__, iface);
@@ -369,6 +388,7 @@
 	os_free(iface->basic_rates);
 	iface->basic_rates = NULL;
 	ap_list_deinit(iface);
+	sta_track_deinit(iface);
 }
 
 
@@ -1364,15 +1384,134 @@
 }
 
 
-/**
- * hostapd_setup_interface_complete - Complete interface setup
- *
- * This function is called when previous steps in the interface setup has been
- * completed. This can also start operations, e.g., DFS, that will require
- * additional processing before interface is ready to be enabled. Such
- * operations will call this function from eloop callbacks when finished.
- */
-int hostapd_setup_interface_complete(struct hostapd_iface *iface, int err)
+#ifdef CONFIG_FST
+
+static const u8 * fst_hostapd_get_bssid_cb(void *ctx)
+{
+	struct hostapd_data *hapd = ctx;
+
+	return hapd->own_addr;
+}
+
+
+static void fst_hostapd_get_channel_info_cb(void *ctx,
+					    enum hostapd_hw_mode *hw_mode,
+					    u8 *channel)
+{
+	struct hostapd_data *hapd = ctx;
+
+	*hw_mode = ieee80211_freq_to_chan(hapd->iface->freq, channel);
+}
+
+
+static void fst_hostapd_set_ies_cb(void *ctx, const struct wpabuf *fst_ies)
+{
+	struct hostapd_data *hapd = ctx;
+
+	if (hapd->iface->fst_ies != fst_ies) {
+		hapd->iface->fst_ies = fst_ies;
+		if (ieee802_11_set_beacon(hapd))
+			wpa_printf(MSG_WARNING, "FST: Cannot set beacon");
+	}
+}
+
+
+static int fst_hostapd_send_action_cb(void *ctx, const u8 *da,
+				      struct wpabuf *buf)
+{
+	struct hostapd_data *hapd = ctx;
+
+	return hostapd_drv_send_action(hapd, hapd->iface->freq, 0, da,
+				       wpabuf_head(buf), wpabuf_len(buf));
+}
+
+
+static const struct wpabuf * fst_hostapd_get_mb_ie_cb(void *ctx, const u8 *addr)
+{
+	struct hostapd_data *hapd = ctx;
+	struct sta_info *sta = ap_get_sta(hapd, addr);
+
+	return sta ? sta->mb_ies : NULL;
+}
+
+
+static void fst_hostapd_update_mb_ie_cb(void *ctx, const u8 *addr,
+					const u8 *buf, size_t size)
+{
+	struct hostapd_data *hapd = ctx;
+	struct sta_info *sta = ap_get_sta(hapd, addr);
+
+	if (sta) {
+		struct mb_ies_info info;
+
+		if (!mb_ies_info_by_ies(&info, buf, size)) {
+			wpabuf_free(sta->mb_ies);
+			sta->mb_ies = mb_ies_by_info(&info);
+		}
+	}
+}
+
+
+static const u8 * fst_hostapd_get_sta(struct fst_get_peer_ctx **get_ctx,
+				      Boolean mb_only)
+{
+	struct sta_info *s = (struct sta_info *) *get_ctx;
+
+	if (mb_only) {
+		for (; s && !s->mb_ies; s = s->next)
+			;
+	}
+
+	if (s) {
+		*get_ctx = (struct fst_get_peer_ctx *) s->next;
+
+		return s->addr;
+	}
+
+	*get_ctx = NULL;
+	return NULL;
+}
+
+
+static const u8 * fst_hostapd_get_peer_first(void *ctx,
+					     struct fst_get_peer_ctx **get_ctx,
+					     Boolean mb_only)
+{
+	struct hostapd_data *hapd = ctx;
+
+	*get_ctx = (struct fst_get_peer_ctx *) hapd->sta_list;
+
+	return fst_hostapd_get_sta(get_ctx, mb_only);
+}
+
+
+static const u8 * fst_hostapd_get_peer_next(void *ctx,
+					    struct fst_get_peer_ctx **get_ctx,
+					    Boolean mb_only)
+{
+	return fst_hostapd_get_sta(get_ctx, mb_only);
+}
+
+
+void fst_hostapd_fill_iface_obj(struct hostapd_data *hapd,
+				struct fst_wpa_obj *iface_obj)
+{
+	iface_obj->ctx = hapd;
+	iface_obj->get_bssid = fst_hostapd_get_bssid_cb;
+	iface_obj->get_channel_info = fst_hostapd_get_channel_info_cb;
+	iface_obj->set_ies = fst_hostapd_set_ies_cb;
+	iface_obj->send_action = fst_hostapd_send_action_cb;
+	iface_obj->get_mb_ie = fst_hostapd_get_mb_ie_cb;
+	iface_obj->update_mb_ie = fst_hostapd_update_mb_ie_cb;
+	iface_obj->get_peer_first = fst_hostapd_get_peer_first;
+	iface_obj->get_peer_next = fst_hostapd_get_peer_next;
+}
+
+#endif /* CONFIG_FST */
+
+
+static int hostapd_setup_interface_complete_sync(struct hostapd_iface *iface,
+						 int err)
 {
 	struct hostapd_data *hapd = iface->bss[0];
 	size_t j;
@@ -1496,6 +1635,7 @@
 	hostapd_tx_queue_params(iface);
 
 	ap_list_init(iface);
+	dl_list_init(&iface->sta_seen);
 
 	hostapd_set_acl(hapd);
 
@@ -1529,6 +1669,22 @@
 #ifdef NEED_AP_MLME
 dfs_offload:
 #endif /* NEED_AP_MLME */
+
+#ifdef CONFIG_FST
+	if (hapd->iconf->fst_cfg.group_id[0]) {
+		struct fst_wpa_obj iface_obj;
+
+		fst_hostapd_fill_iface_obj(hapd, &iface_obj);
+		iface->fst = fst_attach(hapd->conf->iface, hapd->own_addr,
+					&iface_obj, &hapd->iconf->fst_cfg);
+		if (!iface->fst) {
+			wpa_printf(MSG_ERROR, "Could not attach to FST %s",
+				   hapd->iconf->fst_cfg.group_id);
+			goto fail;
+		}
+	}
+#endif /* CONFIG_FST */
+
 	hostapd_set_state(iface, HAPD_IFACE_ENABLED);
 	wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, AP_EVENT_ENABLED);
 	if (hapd->setup_complete_cb)
@@ -1545,6 +1701,12 @@
 	wpa_printf(MSG_ERROR, "Interface initialization failed");
 	hostapd_set_state(iface, HAPD_IFACE_DISABLED);
 	wpa_msg(hapd->msg_ctx, MSG_INFO, AP_EVENT_DISABLED);
+#ifdef CONFIG_FST
+	if (iface->fst) {
+		fst_detach(iface->fst);
+		iface->fst = NULL;
+	}
+#endif /* CONFIG_FST */
 	if (iface->interfaces && iface->interfaces->terminate_on_error)
 		eloop_terminate();
 	return -1;
@@ -1552,6 +1714,89 @@
 
 
 /**
+ * hostapd_setup_interface_complete - Complete interface setup
+ *
+ * This function is called when previous steps in the interface setup has been
+ * completed. This can also start operations, e.g., DFS, that will require
+ * additional processing before interface is ready to be enabled. Such
+ * operations will call this function from eloop callbacks when finished.
+ */
+int hostapd_setup_interface_complete(struct hostapd_iface *iface, int err)
+{
+	struct hapd_interfaces *interfaces = iface->interfaces;
+	struct hostapd_data *hapd = iface->bss[0];
+	unsigned int i;
+	int not_ready_in_sync_ifaces = 0;
+
+	if (!iface->need_to_start_in_sync)
+		return hostapd_setup_interface_complete_sync(iface, err);
+
+	if (err) {
+		wpa_printf(MSG_ERROR, "Interface initialization failed");
+		hostapd_set_state(iface, HAPD_IFACE_DISABLED);
+		iface->need_to_start_in_sync = 0;
+		wpa_msg(hapd->msg_ctx, MSG_INFO, AP_EVENT_DISABLED);
+		if (interfaces && interfaces->terminate_on_error)
+			eloop_terminate();
+		return -1;
+	}
+
+	if (iface->ready_to_start_in_sync) {
+		/* Already in ready and waiting. should never happpen */
+		return 0;
+	}
+
+	for (i = 0; i < interfaces->count; i++) {
+		if (interfaces->iface[i]->need_to_start_in_sync &&
+		    !interfaces->iface[i]->ready_to_start_in_sync)
+			not_ready_in_sync_ifaces++;
+	}
+
+	/*
+	 * Check if this is the last interface, if yes then start all the other
+	 * waiting interfaces. If not, add this interface to the waiting list.
+	 */
+	if (not_ready_in_sync_ifaces > 1 && iface->state == HAPD_IFACE_DFS) {
+		/*
+		 * If this interface went through CAC, do not synchronize, just
+		 * start immediately.
+		 */
+		iface->need_to_start_in_sync = 0;
+		wpa_printf(MSG_INFO,
+			   "%s: Finished CAC - bypass sync and start interface",
+			   iface->bss[0]->conf->iface);
+		return hostapd_setup_interface_complete_sync(iface, err);
+	}
+
+	if (not_ready_in_sync_ifaces > 1) {
+		/* need to wait as there are other interfaces still coming up */
+		iface->ready_to_start_in_sync = 1;
+		wpa_printf(MSG_INFO,
+			   "%s: Interface waiting to sync with other interfaces",
+			   iface->bss[0]->conf->iface);
+		return 0;
+	}
+
+	wpa_printf(MSG_INFO,
+		   "%s: Last interface to sync - starting all interfaces",
+		   iface->bss[0]->conf->iface);
+	iface->need_to_start_in_sync = 0;
+	hostapd_setup_interface_complete_sync(iface, err);
+	for (i = 0; i < interfaces->count; i++) {
+		if (interfaces->iface[i]->need_to_start_in_sync &&
+		    interfaces->iface[i]->ready_to_start_in_sync) {
+			hostapd_setup_interface_complete_sync(
+				interfaces->iface[i], 0);
+			/* Only once the interfaces are sync started */
+			interfaces->iface[i]->need_to_start_in_sync = 0;
+		}
+	}
+
+	return 0;
+}
+
+
+/**
  * hostapd_setup_interface - Setup of an interface
  * @iface: Pointer to interface data.
  * Returns: 0 on success, -1 on failure
@@ -1644,6 +1889,13 @@
 	eloop_cancel_timeout(channel_list_update_timeout, iface, NULL);
 	iface->wait_channel_update = 0;
 
+#ifdef CONFIG_FST
+	if (iface->fst) {
+		fst_detach(iface->fst);
+		iface->fst = NULL;
+	}
+#endif /* CONFIG_FST */
+
 	for (j = iface->num_bss - 1; j >= 0; j--)
 		hostapd_bss_deinit(iface->bss[j]);
 }
@@ -2030,7 +2282,7 @@
 
 static struct hostapd_config *
 hostapd_config_alloc(struct hapd_interfaces *interfaces, const char *ifname,
-		     const char *ctrl_iface)
+		     const char *ctrl_iface, const char *driver)
 {
 	struct hostapd_bss_config *bss;
 	struct hostapd_config *conf;
@@ -2043,6 +2295,21 @@
 		return NULL;
 	}
 
+	if (driver) {
+		int j;
+
+		for (j = 0; wpa_drivers[j]; j++) {
+			if (os_strcmp(driver, wpa_drivers[j]->name) == 0) {
+				conf->driver = wpa_drivers[j];
+				goto skip;
+			}
+		}
+
+		wpa_printf(MSG_ERROR,
+			   "Invalid/unknown driver '%s' - registering the default driver",
+			   driver);
+	}
+
 	conf->driver = wpa_drivers[0];
 	if (conf->driver == NULL) {
 		wpa_printf(MSG_ERROR, "No driver wrappers registered!");
@@ -2050,6 +2317,7 @@
 		return NULL;
 	}
 
+skip:
 	bss = conf->last_bss = conf->bss[0];
 
 	os_strlcpy(bss->iface, ifname, sizeof(bss->iface));
@@ -2210,8 +2478,14 @@
 		if (conf && conf->bss)
 			os_strlcpy(conf->bss[0]->iface, buf,
 				   sizeof(conf->bss[0]->iface));
-	} else
-		conf = hostapd_config_alloc(interfaces, buf, ptr);
+	} else {
+		char *driver = os_strchr(ptr, ' ');
+
+		if (driver)
+			*driver++ = '\0';
+		conf = hostapd_config_alloc(interfaces, buf, ptr, driver);
+	}
+
 	if (conf == NULL || conf->bss == NULL) {
 		wpa_printf(MSG_ERROR, "%s: Failed to allocate memory "
 			   "for configuration", __func__);
@@ -2436,6 +2710,17 @@
 }
 
 
+int hostapd_csa_in_progress(struct hostapd_iface *iface)
+{
+	unsigned int i;
+
+	for (i = 0; i < iface->num_bss; i++)
+		if (iface->bss[i]->csa_in_progress)
+			return 1;
+	return 0;
+}
+
+
 #ifdef NEED_AP_MLME
 
 static void free_beacon_data(struct beacon_data *beacon)
@@ -2547,9 +2832,9 @@
 
 
 /*
- * TODO: This flow currently supports only changing frequency within the
- * same hw_mode. Any other changes to MAC parameters or provided settings (even
- * width) are not supported.
+ * TODO: This flow currently supports only changing channel and width within
+ * the same hw_mode. Any other changes to MAC parameters or provided settings
+ * are not supported.
  */
 static int hostapd_change_config_freq(struct hostapd_data *hapd,
 				      struct hostapd_config *conf,
@@ -2568,15 +2853,44 @@
 		return -1;
 
 	/* if a pointer to old_params is provided we save previous state */
-	if (old_params) {
-		old_params->channel = conf->channel;
-		old_params->ht_enabled = conf->ieee80211n;
-		old_params->sec_channel_offset = conf->secondary_channel;
+	if (old_params &&
+	    hostapd_set_freq_params(old_params, conf->hw_mode,
+				    hostapd_hw_get_freq(hapd, conf->channel),
+				    conf->channel, conf->ieee80211n,
+				    conf->ieee80211ac,
+				    conf->secondary_channel,
+				    conf->vht_oper_chwidth,
+				    conf->vht_oper_centr_freq_seg0_idx,
+				    conf->vht_oper_centr_freq_seg1_idx,
+				    conf->vht_capab))
+		return -1;
+
+	switch (params->bandwidth) {
+	case 0:
+	case 20:
+	case 40:
+		conf->vht_oper_chwidth = VHT_CHANWIDTH_USE_HT;
+		break;
+	case 80:
+		if (params->center_freq2)
+			conf->vht_oper_chwidth = VHT_CHANWIDTH_80P80MHZ;
+		else
+			conf->vht_oper_chwidth = VHT_CHANWIDTH_80MHZ;
+		break;
+	case 160:
+		conf->vht_oper_chwidth = VHT_CHANWIDTH_160MHZ;
+		break;
+	default:
+		return -1;
 	}
 
 	conf->channel = channel;
 	conf->ieee80211n = params->ht_enabled;
 	conf->secondary_channel = params->sec_channel_offset;
+	ieee80211_freq_to_chan(params->center_freq1,
+			       &conf->vht_oper_centr_freq_seg0_idx);
+	ieee80211_freq_to_chan(params->center_freq2,
+			       &conf->vht_oper_centr_freq_seg1_idx);
 
 	/* TODO: maybe call here hostapd_config_check here? */
 
@@ -2590,11 +2904,43 @@
 	struct hostapd_iface *iface = hapd->iface;
 	struct hostapd_freq_params old_freq;
 	int ret;
+	u8 chan, vht_bandwidth;
 
 	os_memset(&old_freq, 0, sizeof(old_freq));
 	if (!iface || !iface->freq || hapd->csa_in_progress)
 		return -1;
 
+	switch (settings->freq_params.bandwidth) {
+	case 80:
+		if (settings->freq_params.center_freq2)
+			vht_bandwidth = VHT_CHANWIDTH_80P80MHZ;
+		else
+			vht_bandwidth = VHT_CHANWIDTH_80MHZ;
+		break;
+	case 160:
+		vht_bandwidth = VHT_CHANWIDTH_160MHZ;
+		break;
+	default:
+		vht_bandwidth = VHT_CHANWIDTH_USE_HT;
+		break;
+	}
+
+	if (ieee80211_freq_to_channel_ext(
+		    settings->freq_params.freq,
+		    settings->freq_params.sec_channel_offset,
+		    vht_bandwidth,
+		    &hapd->iface->cs_oper_class,
+		    &chan) == NUM_HOSTAPD_MODES) {
+		wpa_printf(MSG_DEBUG,
+			   "invalid frequency for channel switch (freq=%d, sec_channel_offset=%d, vht_enabled=%d)",
+			   settings->freq_params.freq,
+			   settings->freq_params.sec_channel_offset,
+			   settings->freq_params.vht_enabled);
+		return -1;
+	}
+
+	settings->freq_params.channel = chan;
+
 	ret = hostapd_change_config_freq(iface->bss[0], iface->conf,
 					 &settings->freq_params,
 					 &old_freq);
@@ -2621,8 +2967,10 @@
 		return ret;
 	}
 
-	settings->counter_offset_beacon = hapd->cs_c_off_beacon;
-	settings->counter_offset_presp = hapd->cs_c_off_proberesp;
+	settings->counter_offset_beacon[0] = hapd->cs_c_off_beacon;
+	settings->counter_offset_presp[0] = hapd->cs_c_off_proberesp;
+	settings->counter_offset_beacon[1] = hapd->cs_c_off_ecsa_beacon;
+	settings->counter_offset_presp[1] = hapd->cs_c_off_ecsa_proberesp;
 
 	return 0;
 }
@@ -2636,6 +2984,8 @@
 	hapd->cs_c_off_beacon = 0;
 	hapd->cs_c_off_proberesp = 0;
 	hapd->csa_in_progress = 0;
+	hapd->cs_c_off_ecsa_beacon = 0;
+	hapd->cs_c_off_ecsa_proberesp = 0;
 }
 
 
@@ -2723,4 +3073,43 @@
 	hostapd_enable_iface(iface);
 }
 
+
+struct hostapd_data * hostapd_get_iface(struct hapd_interfaces *interfaces,
+					const char *ifname)
+{
+	size_t i, j;
+
+	for (i = 0; i < interfaces->count; i++) {
+		struct hostapd_iface *iface = interfaces->iface[i];
+
+		for (j = 0; j < iface->num_bss; j++) {
+			struct hostapd_data *hapd = iface->bss[j];
+
+			if (os_strcmp(ifname, hapd->conf->iface) == 0)
+				return hapd;
+		}
+	}
+
+	return NULL;
+}
+
 #endif /* NEED_AP_MLME */
+
+
+void hostapd_periodic_iface(struct hostapd_iface *iface)
+{
+	size_t i;
+
+	ap_list_timer(iface);
+
+	for (i = 0; i < iface->num_bss; i++) {
+		struct hostapd_data *hapd = iface->bss[i];
+
+		if (!hapd->started)
+			continue;
+
+#ifndef CONFIG_NO_RADIUS
+		hostapd_acl_expire(hapd);
+#endif /* CONFIG_NO_RADIUS */
+	}
+}
diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h
index dc71694..8161a59 100644
--- a/src/ap/hostapd.h
+++ b/src/ap/hostapd.h
@@ -228,6 +228,8 @@
 	unsigned int cs_c_off_beacon;
 	unsigned int cs_c_off_proberesp;
 	int csa_in_progress;
+	unsigned int cs_c_off_ecsa_beacon;
+	unsigned int cs_c_off_ecsa_proberesp;
 
 	/* BSS Load */
 	unsigned int bss_load_update_timeout;
@@ -269,6 +271,7 @@
 	/** Key used for generating SAE anti-clogging tokens */
 	u8 sae_token_key[8];
 	struct os_reltime last_sae_token_key_update;
+	int dot11RSNASAERetransPeriod; /* msec */
 #endif /* CONFIG_SAE */
 
 #ifdef CONFIG_TESTING_OPTIONS
@@ -280,6 +283,12 @@
 };
 
 
+struct hostapd_sta_info {
+	struct dl_list list;
+	u8 addr[ETH_ALEN];
+	struct os_reltime last_seen;
+};
+
 /**
  * struct hostapd_iface - hostapd per-interface data structure
  */
@@ -309,6 +318,10 @@
 
 	unsigned int wait_channel_update:1;
 	unsigned int cac_started:1;
+#ifdef CONFIG_FST
+	struct fst_iface *fst;
+	const struct wpabuf *fst_ies;
+#endif /* CONFIG_FST */
 
 	/*
 	 * When set, indicates that the driver will handle the AP
@@ -316,6 +329,15 @@
 	 */
 	unsigned int driver_ap_teardown:1;
 
+	/*
+	 * When set, indicates that this interface is part of list of
+	 * interfaces that need to be started together (synchronously).
+	 */
+	unsigned int need_to_start_in_sync:1;
+
+	/* Ready to start but waiting for other interfaces to become ready. */
+	unsigned int ready_to_start_in_sync:1;
+
 	int num_ap; /* number of entries in ap_list */
 	struct ap_info *ap_list; /* AP info list head */
 	struct ap_info *ap_hash[STA_HASH_SIZE];
@@ -391,6 +413,9 @@
 	u64 last_channel_time_busy;
 	u8 channel_utilization;
 
+	/* eCSA IE will be added only if operating class is specified */
+	u8 cs_oper_class;
+
 	unsigned int dfs_cac_ms;
 	struct os_reltime dfs_cac_start;
 
@@ -404,6 +429,9 @@
 
 	void (*scan_cb)(struct hostapd_iface *iface);
 	int num_ht40_scan_tries;
+
+	struct dl_list sta_seen; /* struct hostapd_sta_info */
+	unsigned int num_sta_seen;
 };
 
 /* hostapd.c */
@@ -435,12 +463,14 @@
 void hostapd_channel_list_updated(struct hostapd_iface *iface, int initiator);
 void hostapd_set_state(struct hostapd_iface *iface, enum hostapd_iface_state s);
 const char * hostapd_state_text(enum hostapd_iface_state s);
+int hostapd_csa_in_progress(struct hostapd_iface *iface);
 int hostapd_switch_channel(struct hostapd_data *hapd,
 			   struct csa_settings *settings);
 void
 hostapd_switch_channel_fallback(struct hostapd_iface *iface,
 				const struct hostapd_freq_params *freq_params);
 void hostapd_cleanup_cs_params(struct hostapd_data *hapd);
+void hostapd_periodic_iface(struct hostapd_iface *iface);
 
 /* utils.c */
 int hostapd_register_probereq_cb(struct hostapd_data *hapd,
@@ -468,4 +498,12 @@
 hostapd_get_eap_user(struct hostapd_data *hapd, const u8 *identity,
 		     size_t identity_len, int phase2);
 
+struct hostapd_data * hostapd_get_iface(struct hapd_interfaces *interfaces,
+					const char *ifname);
+
+#ifdef CONFIG_FST
+void fst_hostapd_fill_iface_obj(struct hostapd_data *hapd,
+				struct fst_wpa_obj *iface_obj);
+#endif /* CONFIG_FST */
+
 #endif /* HOSTAPD_H */
diff --git a/src/ap/hw_features.c b/src/ap/hw_features.c
index 28324a8..3607066 100644
--- a/src/ap/hw_features.c
+++ b/src/ap/hw_features.c
@@ -260,8 +260,14 @@
 
 	res = check_40mhz_5g(iface->current_mode, scan_res, pri_chan, sec_chan);
 
-	if (res == 2)
-		ieee80211n_switch_pri_sec(iface);
+	if (res == 2) {
+		if (iface->conf->no_pri_sec_switch) {
+			wpa_printf(MSG_DEBUG,
+				   "Cannot switch PRI/SEC channels due to local constraint");
+		} else {
+			ieee80211n_switch_pri_sec(iface);
+		}
+	}
 
 	return !!res;
 }
@@ -466,8 +472,9 @@
 	struct wpa_driver_scan_params params;
 	int ret;
 
-	if (!iface->conf->secondary_channel)
-		return 0; /* HT40 not used */
+	/* Check that HT40 is used and PRI / SEC switch is allowed */
+	if (!iface->conf->secondary_channel || iface->conf->no_pri_sec_switch)
+		return 0;
 
 	hostapd_set_state(iface, HAPD_IFACE_HT_SCAN);
 	wpa_printf(MSG_DEBUG, "Scan for neighboring BSSes prior to enabling "
@@ -726,6 +733,15 @@
 	int ret;
 	if (!iface->conf->ieee80211n)
 		return 0;
+
+	if (iface->current_mode->mode != HOSTAPD_MODE_IEEE80211B &&
+	    iface->current_mode->mode != HOSTAPD_MODE_IEEE80211G &&
+	    (iface->conf->ht_capab & HT_CAP_INFO_DSSS_CCK40MHZ)) {
+		wpa_printf(MSG_DEBUG,
+			   "Disable HT capability [DSSS_CCK-40] on 5 GHz band");
+		iface->conf->ht_capab &= ~HT_CAP_INFO_DSSS_CCK40MHZ;
+	}
+
 	if (!ieee80211n_supported_ht_capab(iface))
 		return -1;
 #ifdef CONFIG_IEEE80211AC
diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
index db20c86..01b514e 100644
--- a/src/ap/ieee802_11.c
+++ b/src/ap/ieee802_11.c
@@ -23,6 +23,7 @@
 #include "radius/radius_client.h"
 #include "p2p/p2p.h"
 #include "wps/wps.h"
+#include "fst/fst.h"
 #include "hostapd.h"
 #include "beacon.h"
 #include "ieee802_11_auth.h"
@@ -38,6 +39,7 @@
 #include "p2p_hostapd.h"
 #include "ap_drv_ops.h"
 #include "wnm_ap.h"
+#include "hw_features.h"
 #include "ieee802_11.h"
 #include "dfs.h"
 
@@ -191,6 +193,7 @@
 }
 
 
+#ifndef CONFIG_NO_RC4
 static u16 auth_shared_key(struct hostapd_data *hapd, struct sta_info *sta,
 			   u16 auth_transaction, const u8 *challenge,
 			   int iswep)
@@ -244,6 +247,7 @@
 
 	return 0;
 }
+#endif /* CONFIG_NO_RC4 */
 
 
 static void send_auth_reply(struct hostapd_data *hapd,
@@ -313,7 +317,6 @@
 
 #ifdef CONFIG_SAE
 
-#define dot11RSNASAERetransPeriod 40	/* msec */
 #define dot11RSNASAESync 5		/* attempts */
 
 
@@ -496,12 +499,14 @@
 	switch (sta->sae->state) {
 	case SAE_COMMITTED:
 		ret = auth_sae_send_commit(hapd, sta, hapd->own_addr, 0);
-		eloop_register_timeout(0, dot11RSNASAERetransPeriod * 1000,
+		eloop_register_timeout(0,
+				       hapd->dot11RSNASAERetransPeriod * 1000,
 				       auth_sae_retransmit_timer, hapd, sta);
 		break;
 	case SAE_CONFIRMED:
 		ret = auth_sae_send_confirm(hapd, sta, hapd->own_addr);
-		eloop_register_timeout(0, dot11RSNASAERetransPeriod * 1000,
+		eloop_register_timeout(0,
+				       hapd->dot11RSNASAERetransPeriod * 1000,
 				       auth_sae_retransmit_timer, hapd, sta);
 		break;
 	default:
@@ -527,7 +532,7 @@
 		return;
 
 	eloop_cancel_timeout(auth_sae_retransmit_timer, hapd, sta);
-	eloop_register_timeout(0, dot11RSNASAERetransPeriod * 1000,
+	eloop_register_timeout(0, hapd->dot11RSNASAERetransPeriod * 1000,
 			       auth_sae_retransmit_timer, hapd, sta);
 }
 
@@ -925,6 +930,16 @@
 		   challenge ? " challenge" : "",
 		   seq_ctrl, (fc & WLAN_FC_RETRY) ? " retry" : "");
 
+#ifdef CONFIG_NO_RC4
+	if (auth_alg == WLAN_AUTH_SHARED_KEY) {
+		wpa_printf(MSG_INFO,
+			   "Unsupported authentication algorithm (%d)",
+			   auth_alg);
+		resp = WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG;
+		goto fail;
+	}
+#endif /* CONFIG_NO_RC4 */
+
 	if (hapd->tkip_countermeasures) {
 		resp = WLAN_REASON_MICHAEL_MIC_FAILURE;
 		goto fail;
@@ -963,6 +978,61 @@
 		goto fail;
 	}
 
+	if (hapd->conf->no_auth_if_seen_on) {
+		struct hostapd_data *other;
+
+		other = sta_track_seen_on(hapd->iface, mgmt->sa,
+					  hapd->conf->no_auth_if_seen_on);
+		if (other) {
+			u8 *pos;
+			u32 info;
+			u8 op_class, channel, phytype;
+
+			wpa_printf(MSG_DEBUG, "%s: Reject authentication from "
+				   MACSTR " since STA has been seen on %s",
+				   hapd->conf->iface, MAC2STR(mgmt->sa),
+				   hapd->conf->no_auth_if_seen_on);
+
+			resp = WLAN_STATUS_REJECTED_WITH_SUGGESTED_BSS_TRANSITION;
+			pos = &resp_ies[0];
+			*pos++ = WLAN_EID_NEIGHBOR_REPORT;
+			*pos++ = 13;
+			os_memcpy(pos, other->own_addr, ETH_ALEN);
+			pos += ETH_ALEN;
+			info = 0; /* TODO: BSSID Information */
+			WPA_PUT_LE32(pos, info);
+			pos += 4;
+			if (other->iconf->hw_mode == HOSTAPD_MODE_IEEE80211AD)
+				phytype = 8; /* dmg */
+			else if (other->iconf->ieee80211ac)
+				phytype = 9; /* vht */
+			else if (other->iconf->ieee80211n)
+				phytype = 7; /* ht */
+			else if (other->iconf->hw_mode ==
+				 HOSTAPD_MODE_IEEE80211A)
+				phytype = 4; /* ofdm */
+			else if (other->iconf->hw_mode ==
+				 HOSTAPD_MODE_IEEE80211G)
+				phytype = 6; /* erp */
+			else
+				phytype = 5; /* hrdsss */
+			if (ieee80211_freq_to_channel_ext(
+				    hostapd_hw_get_freq(other,
+							other->iconf->channel),
+				    other->iconf->secondary_channel,
+				    other->iconf->ieee80211ac,
+				    &op_class, &channel) == NUM_HOSTAPD_MODES) {
+				op_class = 0;
+				channel = other->iconf->channel;
+			}
+			*pos++ = op_class;
+			*pos++ = channel;
+			*pos++ = phytype;
+			resp_ies_len = pos - &resp_ies[0];
+			goto fail;
+		}
+	}
+
 	res = hostapd_allowed_address(hapd, mgmt->sa, (u8 *) mgmt, len,
 				      &session_timeout,
 				      &acct_interim_interval, &vlan_id,
@@ -1072,6 +1142,7 @@
 		sta->auth_alg = WLAN_AUTH_OPEN;
 		mlme_authenticate_indication(hapd, sta);
 		break;
+#ifndef CONFIG_NO_RC4
 	case WLAN_AUTH_SHARED_KEY:
 		resp = auth_shared_key(hapd, sta, auth_transaction, challenge,
 				       fc & WLAN_FC_ISWEP);
@@ -1085,6 +1156,7 @@
 			resp_ies_len = 2 + WLAN_AUTH_CHALLENGE_LEN;
 		}
 		break;
+#endif /* CONFIG_NO_RC4 */
 #ifdef CONFIG_IEEE80211R
 	case WLAN_AUTH_FT:
 		sta->auth_alg = WLAN_AUTH_FT;
@@ -1138,7 +1210,7 @@
 }
 
 
-static int hostapd_get_aid(struct hostapd_data *hapd, struct sta_info *sta)
+int hostapd_get_aid(struct hostapd_data *hapd, struct sta_info *sta)
 {
 	int i, j = 32, aid;
 
@@ -1255,6 +1327,9 @@
 	}
 #endif /* CONFIG_INTERWORKING */
 
+	if (ext_capab_ie_len > 0)
+		sta->ecsa_supported = !!(ext_capab_ie[0] & BIT(2));
+
 	return WLAN_STATUS_SUCCESS;
 }
 
@@ -1301,13 +1376,15 @@
 #endif /* CONFIG_IEEE80211N */
 
 #ifdef CONFIG_IEEE80211AC
-	resp = copy_sta_vht_capab(hapd, sta, elems.vht_capabilities);
-	if (resp != WLAN_STATUS_SUCCESS)
-		return resp;
+	if (hapd->iconf->ieee80211ac) {
+		resp = copy_sta_vht_capab(hapd, sta, elems.vht_capabilities);
+		if (resp != WLAN_STATUS_SUCCESS)
+			return resp;
 
-	resp = set_sta_vht_opmode(hapd, sta, elems.vht_opmode_notif);
-	if (resp != WLAN_STATUS_SUCCESS)
-		return resp;
+		resp = set_sta_vht_opmode(hapd, sta, elems.vht_opmode_notif);
+		if (resp != WLAN_STATUS_SUCCESS)
+			return resp;
+	}
 
 	if (hapd->iconf->ieee80211ac && hapd->iconf->require_vht &&
 	    !(sta->flags & WLAN_STA_VHT)) {
@@ -1535,6 +1612,14 @@
 		sta->hs20_ie = NULL;
 #endif /* CONFIG_HS20 */
 
+#ifdef CONFIG_FST
+	wpabuf_free(sta->mb_ies);
+	if (hapd->iface->fst)
+		sta->mb_ies = mb_ies_by_info(&elems.mb_ies);
+	else
+		sta->mb_ies = NULL;
+#endif /* CONFIG_FST */
+
 	return WLAN_STATUS_SUCCESS;
 }
 
@@ -1623,6 +1708,14 @@
 	if (sta->qos_map_enabled)
 		p = hostapd_eid_qos_map_set(hapd, p);
 
+#ifdef CONFIG_FST
+	if (hapd->iface->fst_ies) {
+		os_memcpy(p, wpabuf_head(hapd->iface->fst_ies),
+			  wpabuf_len(hapd->iface->fst_ies));
+		p += wpabuf_len(hapd->iface->fst_ies);
+	}
+#endif /* CONFIG_FST */
+
 #ifdef CONFIG_IEEE80211AC
 	if (hapd->conf->vendor_vht && (sta->flags & WLAN_STA_VENDOR_VHT))
 		p = hostapd_eid_vendor_vht(hapd, p);
@@ -2101,6 +2194,15 @@
 		ieee802_11_rx_wnm_action_ap(hapd, mgmt, len);
 		return 1;
 #endif /* CONFIG_WNM */
+#ifdef CONFIG_FST
+	case WLAN_ACTION_FST:
+		if (hapd->iface->fst)
+			fst_rx_action(hapd->iface->fst, mgmt, len);
+		else
+			wpa_printf(MSG_DEBUG,
+				   "FST: Ignore FST Action frame - no FST attached");
+		return 1;
+#endif /* CONFIG_FST */
 	case WLAN_ACTION_PUBLIC:
 	case WLAN_ACTION_PROTECTED_DUAL:
 #ifdef CONFIG_IEEE80211N
@@ -2238,6 +2340,9 @@
 		return 0;
 	}
 
+	if (hapd->iconf->track_sta_max_num)
+		sta_track_add(hapd->iface, mgmt->sa);
+
 	switch (stype) {
 	case WLAN_FC_STYPE_AUTH:
 		wpa_printf(MSG_DEBUG, "mgmt::auth");
diff --git a/src/ap/ieee802_11.h b/src/ap/ieee802_11.h
index 44c1bff..0020ff5 100644
--- a/src/ap/ieee802_11.h
+++ b/src/ap/ieee802_11.h
@@ -49,9 +49,13 @@
 u8 * hostapd_eid_ext_supp_rates(struct hostapd_data *hapd, u8 *eid);
 u8 * hostapd_eid_ht_capabilities(struct hostapd_data *hapd, u8 *eid);
 u8 * hostapd_eid_ht_operation(struct hostapd_data *hapd, u8 *eid);
+u8 * hostapd_eid_secondary_channel(struct hostapd_data *hapd, u8 *eid);
 u8 * hostapd_eid_vht_capabilities(struct hostapd_data *hapd, u8 *eid);
 u8 * hostapd_eid_vht_operation(struct hostapd_data *hapd, u8 *eid);
 u8 * hostapd_eid_vendor_vht(struct hostapd_data *hapd, u8 *eid);
+u8 * hostapd_eid_wb_chsw_wrapper(struct hostapd_data *hapd, u8 *eid);
+u8 * hostapd_eid_txpower_envelope(struct hostapd_data *hapd, u8 *eid);
+
 int hostapd_ht_operation_update(struct hostapd_iface *iface);
 void ieee802_11_send_sa_query_req(struct hostapd_data *hapd,
 				  const u8 *addr, const u8 *trans_id);
@@ -61,6 +65,7 @@
 void hostapd_get_vht_capab(struct hostapd_data *hapd,
 			   struct ieee80211_vht_capabilities *vht_cap,
 			   struct ieee80211_vht_capabilities *neg_vht_cap);
+int hostapd_get_aid(struct hostapd_data *hapd, struct sta_info *sta);
 u16 copy_sta_ht_capab(struct hostapd_data *hapd, struct sta_info *sta,
 		      const u8 *ht_capab);
 u16 copy_sta_vendor_vht(struct hostapd_data *hapd, struct sta_info *sta,
diff --git a/src/ap/ieee802_11_auth.c b/src/ap/ieee802_11_auth.c
index 0238257..b7e7ce3 100644
--- a/src/ap/ieee802_11_auth.c
+++ b/src/ap/ieee802_11_auth.c
@@ -213,6 +213,32 @@
 
 
 /**
+ * hostapd_check_acl - Check a specified STA against accept/deny ACLs
+ * @hapd: hostapd BSS data
+ * @addr: MAC address of the STA
+ * @vlan_id: Buffer for returning VLAN ID
+ * Returns: HOSTAPD_ACL_ACCEPT, HOSTAPD_ACL_REJECT, or HOSTAPD_ACL_PENDING
+ */
+ int hostapd_check_acl(struct hostapd_data *hapd, const u8 *addr, int *vlan_id)
+{
+	if (hostapd_maclist_found(hapd->conf->accept_mac,
+				  hapd->conf->num_accept_mac, addr, vlan_id))
+		return HOSTAPD_ACL_ACCEPT;
+
+	if (hostapd_maclist_found(hapd->conf->deny_mac,
+				  hapd->conf->num_deny_mac, addr, vlan_id))
+		return HOSTAPD_ACL_REJECT;
+
+	if (hapd->conf->macaddr_acl == ACCEPT_UNLESS_DENIED)
+		return HOSTAPD_ACL_ACCEPT;
+	if (hapd->conf->macaddr_acl == DENY_UNLESS_ACCEPTED)
+		return HOSTAPD_ACL_REJECT;
+
+	return HOSTAPD_ACL_PENDING;
+}
+
+
+/**
  * hostapd_allowed_address - Check whether a specified STA can be authenticated
  * @hapd: hostapd BSS data
  * @addr: MAC address of the STA
@@ -235,6 +261,8 @@
 			    struct hostapd_sta_wpa_psk_short **psk,
 			    char **identity, char **radius_cui)
 {
+	int res;
+
 	if (session_timeout)
 		*session_timeout = 0;
 	if (acct_interim_interval)
@@ -248,18 +276,9 @@
 	if (radius_cui)
 		*radius_cui = NULL;
 
-	if (hostapd_maclist_found(hapd->conf->accept_mac,
-				  hapd->conf->num_accept_mac, addr, vlan_id))
-		return HOSTAPD_ACL_ACCEPT;
-
-	if (hostapd_maclist_found(hapd->conf->deny_mac,
-				  hapd->conf->num_deny_mac, addr, vlan_id))
-		return HOSTAPD_ACL_REJECT;
-
-	if (hapd->conf->macaddr_acl == ACCEPT_UNLESS_DENIED)
-		return HOSTAPD_ACL_ACCEPT;
-	if (hapd->conf->macaddr_acl == DENY_UNLESS_ACCEPTED)
-		return HOSTAPD_ACL_REJECT;
+	res = hostapd_check_acl(hapd, addr, vlan_id);
+	if (res != HOSTAPD_ACL_PENDING)
+		return res;
 
 	if (hapd->conf->macaddr_acl == USE_EXTERNAL_RADIUS_AUTH) {
 #ifdef CONFIG_NO_RADIUS
@@ -268,10 +287,9 @@
 		struct hostapd_acl_query_data *query;
 
 		/* Check whether ACL cache has an entry for this station */
-		int res = hostapd_acl_cache_get(hapd, addr, session_timeout,
-						acct_interim_interval,
-						vlan_id, psk,
-						identity, radius_cui);
+		res = hostapd_acl_cache_get(hapd, addr, session_timeout,
+					    acct_interim_interval, vlan_id, psk,
+					    identity, radius_cui);
 		if (res == HOSTAPD_ACL_ACCEPT ||
 		    res == HOSTAPD_ACL_ACCEPT_TIMEOUT)
 			return res;
@@ -399,19 +417,15 @@
 
 /**
  * hostapd_acl_expire - ACL cache expiration callback
- * @eloop_ctx: struct hostapd_data *
- * @timeout_ctx: Not used
+ * @hapd: struct hostapd_data *
  */
-static void hostapd_acl_expire(void *eloop_ctx, void *timeout_ctx)
+void hostapd_acl_expire(struct hostapd_data *hapd)
 {
-	struct hostapd_data *hapd = eloop_ctx;
 	struct os_reltime now;
 
 	os_get_reltime(&now);
 	hostapd_acl_expire_cache(hapd, &now);
 	hostapd_acl_expire_queries(hapd, &now);
-
-	eloop_register_timeout(10, 0, hostapd_acl_expire, hapd, NULL);
 }
 
 
@@ -615,8 +629,6 @@
 	if (radius_client_register(hapd->radius, RADIUS_AUTH,
 				   hostapd_acl_recv_radius, hapd))
 		return -1;
-
-	eloop_register_timeout(10, 0, hostapd_acl_expire, hapd, NULL);
 #endif /* CONFIG_NO_RADIUS */
 
 	return 0;
@@ -632,8 +644,6 @@
 	struct hostapd_acl_query_data *query, *prev;
 
 #ifndef CONFIG_NO_RADIUS
-	eloop_cancel_timeout(hostapd_acl_expire, hapd, NULL);
-
 	hostapd_acl_cache_free(hapd->acl_cache);
 #endif /* CONFIG_NO_RADIUS */
 
diff --git a/src/ap/ieee802_11_auth.h b/src/ap/ieee802_11_auth.h
index 2bc1065..da81c14 100644
--- a/src/ap/ieee802_11_auth.h
+++ b/src/ap/ieee802_11_auth.h
@@ -16,6 +16,7 @@
 	HOSTAPD_ACL_ACCEPT_TIMEOUT = 3
 };
 
+int hostapd_check_acl(struct hostapd_data *hapd, const u8 *addr, int *vlan_id);
 int hostapd_allowed_address(struct hostapd_data *hapd, const u8 *addr,
 			    const u8 *msg, size_t len, u32 *session_timeout,
 			    u32 *acct_interim_interval, int *vlan_id,
@@ -24,5 +25,6 @@
 int hostapd_acl_init(struct hostapd_data *hapd);
 void hostapd_acl_deinit(struct hostapd_data *hapd);
 void hostapd_free_psk_list(struct hostapd_sta_wpa_psk_short *psk);
+void hostapd_acl_expire(struct hostapd_data *hapd);
 
 #endif /* IEEE802_11_AUTH_H */
diff --git a/src/ap/ieee802_11_ht.c b/src/ap/ieee802_11_ht.c
index 11fde2a..5eb1060 100644
--- a/src/ap/ieee802_11_ht.c
+++ b/src/ap/ieee802_11_ht.c
@@ -108,6 +108,29 @@
 }
 
 
+u8 * hostapd_eid_secondary_channel(struct hostapd_data *hapd, u8 *eid)
+{
+	u8 sec_ch;
+
+	if (!hapd->cs_freq_params.channel ||
+	    !hapd->cs_freq_params.sec_channel_offset)
+		return eid;
+
+	if (hapd->cs_freq_params.sec_channel_offset == -1)
+		sec_ch = HT_INFO_HT_PARAM_SECONDARY_CHNL_BELOW;
+	else if (hapd->cs_freq_params.sec_channel_offset == 1)
+		sec_ch = HT_INFO_HT_PARAM_SECONDARY_CHNL_ABOVE;
+	else
+		return eid;
+
+	*eid++ = WLAN_EID_SECONDARY_CHANNEL_OFFSET;
+	*eid++ = 1;
+	*eid++ = sec_ch;
+
+	return eid;
+}
+
+
 /*
 op_mode
 Set to 0 (HT pure) under the followign conditions
diff --git a/src/ap/ieee802_11_shared.c b/src/ap/ieee802_11_shared.c
index d462ac8..9e3363e 100644
--- a/src/ap/ieee802_11_shared.c
+++ b/src/ap/ieee802_11_shared.c
@@ -172,6 +172,8 @@
 	case 0: /* Bits 0-7 */
 		if (hapd->iconf->obss_interval)
 			*pos |= 0x01; /* Bit 0 - Coexistence management */
+		if (hapd->iface->drv_flags & WPA_DRIVER_FLAGS_AP_CSA)
+			*pos |= 0x04; /* Bit 2 - Extended Channel Switching */
 		break;
 	case 1: /* Bits 8-15 */
 		if (hapd->conf->proxy_arp)
diff --git a/src/ap/ieee802_11_vht.c b/src/ap/ieee802_11_vht.c
index 5bf1b5d..3236016 100644
--- a/src/ap/ieee802_11_vht.c
+++ b/src/ap/ieee802_11_vht.c
@@ -17,6 +17,7 @@
 #include "sta_info.h"
 #include "beacon.h"
 #include "ieee802_11.h"
+#include "dfs.h"
 
 
 u8 * hostapd_eid_vht_capabilities(struct hostapd_data *hapd, u8 *eid)
@@ -131,6 +132,171 @@
 }
 
 
+u8 * hostapd_eid_wb_chsw_wrapper(struct hostapd_data *hapd, u8 *eid)
+{
+	u8 bw, chan1, chan2 = 0;
+	int freq1;
+
+	if (!hapd->cs_freq_params.channel ||
+	    !hapd->cs_freq_params.vht_enabled)
+		return eid;
+
+	/* bandwidth: 0: 40, 1: 80, 2: 160, 3: 80+80 */
+	switch (hapd->cs_freq_params.bandwidth) {
+	case 40:
+		bw = 0;
+		break;
+	case 80:
+		/* check if it's 80+80 */
+		if (!hapd->cs_freq_params.center_freq2)
+			bw = 1;
+		else
+			bw = 3;
+		break;
+	case 160:
+		bw = 2;
+		break;
+	default:
+		/* not valid VHT bandwidth or not in CSA */
+		return eid;
+	}
+
+	freq1 = hapd->cs_freq_params.center_freq1 ?
+		hapd->cs_freq_params.center_freq1 :
+		hapd->cs_freq_params.freq;
+	if (ieee80211_freq_to_chan(freq1, &chan1) !=
+	    HOSTAPD_MODE_IEEE80211A)
+		return eid;
+
+	if (hapd->cs_freq_params.center_freq2 &&
+	    ieee80211_freq_to_chan(hapd->cs_freq_params.center_freq2,
+				   &chan2) != HOSTAPD_MODE_IEEE80211A)
+		return eid;
+
+	*eid++ = WLAN_EID_VHT_CHANNEL_SWITCH_WRAPPER;
+	*eid++ = 5; /* Length of Channel Switch Wrapper */
+	*eid++ = WLAN_EID_VHT_WIDE_BW_CHSWITCH;
+	*eid++ = 3; /* Length of Wide Bandwidth Channel Switch element */
+	*eid++ = bw; /* New Channel Width */
+	*eid++ = chan1; /* New Channel Center Frequency Segment 0 */
+	*eid++ = chan2; /* New Channel Center Frequency Segment 1 */
+
+	return eid;
+}
+
+
+u8 * hostapd_eid_txpower_envelope(struct hostapd_data *hapd, u8 *eid)
+{
+	struct hostapd_iface *iface = hapd->iface;
+	struct hostapd_config *iconf = iface->conf;
+	struct hostapd_hw_modes *mode = iface->current_mode;
+	struct hostapd_channel_data *chan;
+	int dfs, i;
+	u8 channel, tx_pwr_count, local_pwr_constraint;
+	int max_tx_power;
+	u8 tx_pwr;
+
+	if (!mode)
+		return eid;
+
+	if (ieee80211_freq_to_chan(iface->freq, &channel) == NUM_HOSTAPD_MODES)
+		return eid;
+
+	for (i = 0; i < mode->num_channels; i++) {
+		if (mode->channels[i].freq == iface->freq)
+			break;
+	}
+	if (i == mode->num_channels)
+		return eid;
+
+	switch (iface->conf->vht_oper_chwidth) {
+	case VHT_CHANWIDTH_USE_HT:
+		if (iconf->secondary_channel == 0) {
+			/* Max Transmit Power count = 0 (20 MHz) */
+			tx_pwr_count = 0;
+		} else {
+			/* Max Transmit Power count = 1 (20, 40 MHz) */
+			tx_pwr_count = 1;
+		}
+		break;
+	case VHT_CHANWIDTH_80MHZ:
+		/* Max Transmit Power count = 2 (20, 40, and 80 MHz) */
+		tx_pwr_count = 2;
+		break;
+	case VHT_CHANWIDTH_80P80MHZ:
+	case VHT_CHANWIDTH_160MHZ:
+		/* Max Transmit Power count = 3 (20, 40, 80, 160/80+80 MHz) */
+		tx_pwr_count = 3;
+		break;
+	default:
+		return eid;
+	}
+
+	/*
+	 * Below local_pwr_constraint logic is referred from
+	 * hostapd_eid_pwr_constraint.
+	 *
+	 * Check if DFS is required by regulatory.
+	 */
+	dfs = hostapd_is_dfs_required(hapd->iface);
+	if (dfs < 0)
+		dfs = 0;
+
+	/*
+	 * In order to meet regulations when TPC is not implemented using
+	 * a transmit power that is below the legal maximum (including any
+	 * mitigation factor) should help. In this case, indicate 3 dB below
+	 * maximum allowed transmit power.
+	 */
+	if (hapd->iconf->local_pwr_constraint == -1)
+		local_pwr_constraint = (dfs == 0) ? 0 : 3;
+	else
+		local_pwr_constraint = hapd->iconf->local_pwr_constraint;
+
+	/*
+	 * A STA that is not an AP shall use a transmit power less than or
+	 * equal to the local maximum transmit power level for the channel.
+	 * The local maximum transmit power can be calculated from the formula:
+	 * local max TX pwr = max TX pwr - local pwr constraint
+	 * Where max TX pwr is maximum transmit power level specified for
+	 * channel in Country element and local pwr constraint is specified
+	 * for channel in this Power Constraint element.
+	 */
+	chan = &mode->channels[i];
+	max_tx_power = chan->max_tx_power - local_pwr_constraint;
+
+	/*
+	 * Local Maximum Transmit power is encoded as two's complement
+	 * with a 0.5 dB step.
+	 */
+	max_tx_power *= 2; /* in 0.5 dB steps */
+	if (max_tx_power > 127) {
+		/* 63.5 has special meaning of 63.5 dBm or higher */
+		max_tx_power = 127;
+	}
+	if (max_tx_power < -128)
+		max_tx_power = -128;
+	if (max_tx_power < 0)
+		tx_pwr = 0x80 + max_tx_power + 128;
+	else
+		tx_pwr = max_tx_power;
+
+	*eid++ = WLAN_EID_VHT_TRANSMIT_POWER_ENVELOPE;
+	*eid++ = 2 + tx_pwr_count;
+
+	/*
+	 * Max Transmit Power count and
+	 * Max Transmit Power units = 0 (EIRP)
+	 */
+	*eid++ = tx_pwr_count;
+
+	for (i = 0; i <= tx_pwr_count; i++)
+		*eid++ = tx_pwr;
+
+	return eid;
+}
+
+
 u16 copy_sta_vht_capab(struct hostapd_data *hapd, struct sta_info *sta,
 		       const u8 *vht_capab)
 {
diff --git a/src/ap/ieee802_1x.c b/src/ap/ieee802_1x.c
index d45b98f..f566603 100644
--- a/src/ap/ieee802_1x.c
+++ b/src/ap/ieee802_1x.c
@@ -125,6 +125,9 @@
 }
 
 
+#ifndef CONFIG_FIPS
+#ifndef CONFIG_NO_RC4
+
 static void ieee802_1x_tx_key_one(struct hostapd_data *hapd,
 				  struct sta_info *sta,
 				  int idx, int broadcast,
@@ -204,7 +207,7 @@
 }
 
 
-void ieee802_1x_tx_key(struct hostapd_data *hapd, struct sta_info *sta)
+static void ieee802_1x_tx_key(struct hostapd_data *hapd, struct sta_info *sta)
 {
 	struct eapol_authenticator *eapol = hapd->eapol_auth;
 	struct eapol_state_machine *sm = sta->eapol_sm;
@@ -259,6 +262,9 @@
 	}
 }
 
+#endif /* CONFIG_NO_RC4 */
+#endif /* CONFIG_FIPS */
+
 
 const char *radius_mode_txt(struct hostapd_data *hapd)
 {
@@ -1100,6 +1106,7 @@
 		sta->eapol_sm->be_auth_state = BE_AUTH_SUCCESS;
 		sta->eapol_sm->authSuccess = TRUE;
 		sta->eapol_sm->authFail = FALSE;
+		sta->eapol_sm->portValid = TRUE;
 		if (sta->eapol_sm->eap)
 			eap_sm_notify_cached(sta->eapol_sm->eap);
 		/* TODO: get vlan_id from R0KH using RRB message */
@@ -1709,15 +1716,6 @@
 		ieee802_1x_check_hs20(hapd, sta, msg,
 				      session_timeout_set ?
 				      (int) session_timeout : -1);
-		if (sm->eap_if->eapKeyAvailable && !sta->remediation &&
-		    !sta->hs20_deauth_requested &&
-		    wpa_auth_pmksa_add(sta->wpa_sm, sm->eapol_key_crypt,
-				       session_timeout_set ?
-				       (int) session_timeout : -1, sm) == 0) {
-			hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA,
-				       HOSTAPD_LEVEL_DEBUG,
-				       "Added PMKSA cache entry");
-		}
 		break;
 	case RADIUS_CODE_ACCESS_REJECT:
 		sm->eap_if->aaaFail = TRUE;
@@ -2023,9 +2021,13 @@
 
 static void _ieee802_1x_tx_key(void *ctx, void *sta_ctx)
 {
+#ifndef CONFIG_FIPS
+#ifndef CONFIG_NO_RC4
 	struct hostapd_data *hapd = ctx;
 	struct sta_info *sta = sta_ctx;
 	ieee802_1x_tx_key(hapd, sta);
+#endif /* CONFIG_NO_RC4 */
+#endif /* CONFIG_FIPS */
 }
 
 
@@ -2096,6 +2098,7 @@
 	conf.erp_send_reauth_start = hapd->conf->erp_send_reauth_start;
 	conf.erp_domain = hapd->conf->erp_domain;
 	conf.erp = hapd->conf->eap_server_erp;
+	conf.tls_session_lifetime = hapd->conf->tls_session_lifetime;
 	conf.pac_opaque_encr_key = hapd->conf->pac_opaque_encr_key;
 	conf.eap_fast_a_id = hapd->conf->eap_fast_a_id;
 	conf.eap_fast_a_id_len = hapd->conf->eap_fast_a_id_len;
@@ -2179,7 +2182,7 @@
 {
 	eloop_cancel_timeout(ieee802_1x_rekey, hapd, NULL);
 
-	if (hapd->driver != NULL &&
+	if (hapd->driver && hapd->drv_priv &&
 	    (hapd->conf->ieee802_1x || hapd->conf->wpa))
 		hostapd_set_drv_ieee8021x(hapd, hapd->conf->iface, 0);
 
@@ -2573,7 +2576,7 @@
 		session_timeout = dot11RSNAConfigPMKLifetime;
 	if (success && key && len >= PMK_LEN && !sta->remediation &&
 	    !sta->hs20_deauth_requested &&
-	    wpa_auth_pmksa_add(sta->wpa_sm, key, session_timeout,
+	    wpa_auth_pmksa_add(sta->wpa_sm, key, len, session_timeout,
 			       sta->eapol_sm) == 0) {
 		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA,
 			       HOSTAPD_LEVEL_DEBUG,
diff --git a/src/ap/ieee802_1x.h b/src/ap/ieee802_1x.h
index de6e0e7..14d6955 100644
--- a/src/ap/ieee802_1x.h
+++ b/src/ap/ieee802_1x.h
@@ -23,7 +23,6 @@
 void ieee802_1x_new_station(struct hostapd_data *hapd, struct sta_info *sta);
 void ieee802_1x_free_station(struct sta_info *sta);
 
-void ieee802_1x_tx_key(struct hostapd_data *hapd, struct sta_info *sta);
 void ieee802_1x_abort_auth(struct hostapd_data *hapd, struct sta_info *sta);
 void ieee802_1x_set_sta_authorized(struct hostapd_data *hapd,
 				   struct sta_info *sta, int authorized);
diff --git a/src/ap/ndisc_snoop.c b/src/ap/ndisc_snoop.c
index 0adcc97..4a87721 100644
--- a/src/ap/ndisc_snoop.c
+++ b/src/ap/ndisc_snoop.c
@@ -98,7 +98,7 @@
 {
 	struct hostapd_data *hapd = ctx;
 	struct icmpv6_ndmsg *msg;
-	struct in6_addr *saddr;
+	struct in6_addr saddr;
 	struct sta_info *sta;
 	int res;
 	char addrtxt[INET6_ADDRSTRLEN + 1];
@@ -113,25 +113,30 @@
 		if (msg->opt_type != SOURCE_LL_ADDR)
 			return;
 
-		saddr = &msg->ipv6h.ip6_src;
-		if (!(saddr->s6_addr32[0] == 0 && saddr->s6_addr32[1] == 0 &&
-		      saddr->s6_addr32[2] == 0 && saddr->s6_addr32[3] == 0)) {
+		/*
+		 * IPv6 header may not be 32-bit aligned in the buffer, so use
+		 * a local copy to avoid unaligned reads.
+		 */
+		os_memcpy(&saddr, &msg->ipv6h.ip6_src, sizeof(saddr));
+		if (!(saddr.s6_addr32[0] == 0 && saddr.s6_addr32[1] == 0 &&
+		      saddr.s6_addr32[2] == 0 && saddr.s6_addr32[3] == 0)) {
 			if (len < ETH_HLEN + sizeof(*msg) + ETH_ALEN)
 				return;
 			sta = ap_get_sta(hapd, msg->opt_lladdr);
 			if (!sta)
 				return;
 
-			if (sta_has_ip6addr(sta, saddr))
+			if (sta_has_ip6addr(sta, &saddr))
 				return;
 
-			if (inet_ntop(AF_INET6, saddr, addrtxt, sizeof(addrtxt))
-			    == NULL)
+			if (inet_ntop(AF_INET6, &saddr, addrtxt,
+				      sizeof(addrtxt)) == NULL)
 				addrtxt[0] = '\0';
 			wpa_printf(MSG_DEBUG, "ndisc_snoop: Learned new IPv6 address %s for "
 				   MACSTR, addrtxt, MAC2STR(sta->addr));
-			hostapd_drv_br_delete_ip_neigh(hapd, 6, (u8 *) saddr);
-			res = hostapd_drv_br_add_ip_neigh(hapd, 6, (u8 *) saddr,
+			hostapd_drv_br_delete_ip_neigh(hapd, 6, (u8 *) &saddr);
+			res = hostapd_drv_br_add_ip_neigh(hapd, 6,
+							  (u8 *) &saddr,
 							  128, sta->addr);
 			if (res) {
 				wpa_printf(MSG_ERROR,
@@ -140,7 +145,7 @@
 				return;
 			}
 
-			if (sta_ip6addr_add(sta, saddr))
+			if (sta_ip6addr_add(sta, &saddr))
 				return;
 		}
 		break;
diff --git a/src/ap/pmksa_cache_auth.c b/src/ap/pmksa_cache_auth.c
index 877affe..83e4bda 100644
--- a/src/ap/pmksa_cache_auth.c
+++ b/src/ap/pmksa_cache_auth.c
@@ -258,7 +258,7 @@
 	struct rsn_pmksa_cache_entry *entry, *pos;
 	struct os_reltime now;
 
-	if (pmk_len > PMK_LEN)
+	if (pmk_len > PMK_LEN_MAX)
 		return NULL;
 
 	if (wpa_key_mgmt_suite_b(akmp) && !kck)
diff --git a/src/ap/pmksa_cache_auth.h b/src/ap/pmksa_cache_auth.h
index 8b7be12..b2da379 100644
--- a/src/ap/pmksa_cache_auth.h
+++ b/src/ap/pmksa_cache_auth.h
@@ -17,7 +17,7 @@
 struct rsn_pmksa_cache_entry {
 	struct rsn_pmksa_cache_entry *next, *hnext;
 	u8 pmkid[PMKID_LEN];
-	u8 pmk[PMK_LEN];
+	u8 pmk[PMK_LEN_MAX];
 	size_t pmk_len;
 	os_time_t expiration;
 	int akmp; /* WPA_KEY_MGMT_* */
diff --git a/src/ap/sta_info.c b/src/ap/sta_info.c
index 20847d5..500beff 100644
--- a/src/ap/sta_info.c
+++ b/src/ap/sta_info.c
@@ -16,6 +16,7 @@
 #include "radius/radius.h"
 #include "radius/radius_client.h"
 #include "p2p/p2p.h"
+#include "fst/fst.h"
 #include "hostapd.h"
 #include "accounting.h"
 #include "ieee802_1x.h"
@@ -171,19 +172,6 @@
 	    !(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);
 
@@ -273,6 +261,24 @@
 		radius_client_flush_auth(hapd->radius, sta->addr);
 #endif /* CONFIG_NO_RADIUS */
 
+#ifndef CONFIG_NO_VLAN
+	/*
+	 * sta->wpa_sm->group needs to be released before so that
+	 * vlan_remove_dynamic() can check that no stations are left on the
+	 * AP_VLAN netdev.
+	 */
+	if (sta->vlan_id_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 */
+
 	os_free(sta->challenge);
 
 #ifdef CONFIG_IEEE80211W
@@ -296,6 +302,9 @@
 	wpabuf_free(sta->wps_ie);
 	wpabuf_free(sta->p2p_ie);
 	wpabuf_free(sta->hs20_ie);
+#ifdef CONFIG_FST
+	wpabuf_free(sta->mb_ies);
+#endif /* CONFIG_FST */
 
 	os_free(sta->ht_capabilities);
 	os_free(sta->vht_capabilities);
@@ -838,41 +847,17 @@
 		}
 
 		iface = vlan->ifname;
-		if (vlan_setup_encryption_dyn(hapd, iface) != 0) {
-			hostapd_logger(hapd, sta->addr,
-				       HOSTAPD_MODULE_IEEE80211,
-				       HOSTAPD_LEVEL_DEBUG, "could not "
-				       "configure encryption for dynamic VLAN "
-				       "interface for vlan_id=%d",
-				       sta->vlan_id);
-		}
-
 		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
 			       HOSTAPD_LEVEL_DEBUG, "added new dynamic VLAN "
 			       "interface '%s'", iface);
-	} else if (vlan && vlan->vlan_id == sta->vlan_id) {
-		if (vlan->dynamic_vlan > 0) {
-			vlan->dynamic_vlan++;
-			hostapd_logger(hapd, sta->addr,
-				       HOSTAPD_MODULE_IEEE80211,
-				       HOSTAPD_LEVEL_DEBUG, "updated existing "
-				       "dynamic VLAN interface '%s'", iface);
-		}
-
-		/*
-		 * Update encryption configuration for statically generated
-		 * VLAN interface. This is only used for static WEP
-		 * configuration for the case where hostapd did not yet know
-		 * which keys are to be used when the interface was added.
-		 */
-		if (vlan_setup_encryption_dyn(hapd, iface) != 0) {
-			hostapd_logger(hapd, sta->addr,
-				       HOSTAPD_MODULE_IEEE80211,
-				       HOSTAPD_LEVEL_DEBUG, "could not "
-				       "configure encryption for VLAN "
-				       "interface for vlan_id=%d",
-				       sta->vlan_id);
-		}
+	} else if (vlan && vlan->vlan_id == sta->vlan_id &&
+		   vlan->dynamic_vlan > 0) {
+		vlan->dynamic_vlan++;
+		hostapd_logger(hapd, sta->addr,
+			       HOSTAPD_MODULE_IEEE80211,
+			       HOSTAPD_LEVEL_DEBUG,
+			       "updated existing dynamic VLAN interface '%s'",
+			       iface);
 	}
 
 	/* ref counters have been increased, so mark the station */
@@ -1060,6 +1045,16 @@
 			wpa_msg_no_global(hapd->msg_ctx_parent, MSG_INFO,
 					  AP_STA_DISCONNECTED "%s", buf);
 	}
+
+#ifdef CONFIG_FST
+	if (hapd->iface->fst) {
+		if (authorized)
+			fst_notify_peer_connected(hapd->iface->fst, sta->addr);
+		else
+			fst_notify_peer_disconnected(hapd->iface->fst,
+						     sta->addr);
+	}
+#endif /* CONFIG_FST */
 }
 
 
diff --git a/src/ap/sta_info.h b/src/ap/sta_info.h
index 52a9997..09deac6 100644
--- a/src/ap/sta_info.h
+++ b/src/ap/sta_info.h
@@ -86,6 +86,7 @@
 	unsigned int hs20_deauth_requested:1;
 	unsigned int session_timeout_set:1;
 	unsigned int radius_das_match:1;
+	unsigned int ecsa_supported:1;
 
 	u16 auth_alg;
 
@@ -153,6 +154,9 @@
 	struct wpabuf *hs20_deauth_req;
 	char *hs20_session_info_url;
 	int hs20_disassoc_timer;
+#ifdef CONFIG_FST
+	struct wpabuf *mb_ies; /* MB IEs from (Re)Association Request */
+#endif /* CONFIG_FST */
 
 	struct os_reltime connected_time;
 
@@ -177,7 +181,7 @@
  * AP_DISASSOC_DELAY seconds. Similarly, the station will be deauthenticated
  * after AP_DEAUTH_DELAY seconds has passed after disassociation. */
 #define AP_MAX_INACTIVITY (5 * 60)
-#define AP_DISASSOC_DELAY (1)
+#define AP_DISASSOC_DELAY (3)
 #define AP_DEAUTH_DELAY (1)
 /* Number of seconds to keep STA entry with Authenticated flag after it has
  * been disassociated. */
diff --git a/src/ap/utils.c b/src/ap/utils.c
index d60555a..fcb371b 100644
--- a/src/ap/utils.c
+++ b/src/ap/utils.c
@@ -10,6 +10,7 @@
 
 #include "common.h"
 #include "common/ieee802_11_defs.h"
+#include "fst/fst.h"
 #include "sta_info.h"
 #include "hostapd.h"
 
@@ -55,6 +56,14 @@
 		ohapd = iface->bss[j];
 		if (ohapd == data->hapd)
 			continue;
+#ifdef CONFIG_FST
+		/* Don't prune STAs belong to same FST */
+		if (ohapd->iface->fst &&
+		    data->hapd->iface->fst &&
+		    fst_are_ifaces_aggregated(ohapd->iface->fst,
+					      data->hapd->iface->fst))
+			continue;
+#endif /* CONFIG_FST */
 		osta = ap_get_sta(ohapd, data->addr);
 		if (!osta)
 			continue;
diff --git a/src/ap/vlan_init.c b/src/ap/vlan_init.c
index fd1c8dd..e3df164 100644
--- a/src/ap/vlan_init.c
+++ b/src/ap/vlan_init.c
@@ -9,9 +9,9 @@
  */
 
 #include "utils/includes.h"
-#ifdef CONFIG_FULL_DYNAMIC_VLAN
 #include <net/if.h>
 #include <sys/ioctl.h>
+#ifdef CONFIG_FULL_DYNAMIC_VLAN
 #include <linux/sockios.h>
 #include <linux/if_vlan.h>
 #include <linux/if_bridge.h>
@@ -21,6 +21,7 @@
 #include "hostapd.h"
 #include "ap_config.h"
 #include "ap_drv_ops.h"
+#include "wpa_auth.h"
 #include "vlan_init.h"
 #include "vlan_util.h"
 
@@ -119,6 +120,8 @@
 	return clean;
 }
 
+#endif /* CONFIG_FULL_DYNAMIC_VLAN */
+
 
 static int ifconfig_helper(const char *if_name, int up)
 {
@@ -167,6 +170,67 @@
 }
 
 
+static int vlan_if_add(struct hostapd_data *hapd, struct hostapd_vlan *vlan,
+		       int existsok)
+{
+	int ret, i;
+
+	for (i = 0; i < NUM_WEP_KEYS; i++) {
+		if (!hapd->conf->ssid.wep.key[i])
+			continue;
+		wpa_printf(MSG_ERROR,
+			   "VLAN: Refusing to set up VLAN iface %s with WEP",
+			   vlan->ifname);
+		return -1;
+	}
+
+	if (!if_nametoindex(vlan->ifname))
+		ret = hostapd_vlan_if_add(hapd, vlan->ifname);
+	else if (!existsok)
+		return -1;
+	else
+		ret = 0;
+
+	if (ret)
+		return ret;
+
+	ifconfig_up(vlan->ifname); /* else wpa group will fail fatal */
+
+	if (hapd->wpa_auth)
+		ret = wpa_auth_ensure_group(hapd->wpa_auth, vlan->vlan_id);
+
+	if (ret == 0)
+		return ret;
+
+	wpa_printf(MSG_ERROR, "WPA initialization for VLAN %d failed (%d)",
+		   vlan->vlan_id, ret);
+	if (wpa_auth_release_group(hapd->wpa_auth, vlan->vlan_id))
+		wpa_printf(MSG_ERROR, "WPA deinit of %s failed", vlan->ifname);
+
+	/* group state machine setup failed */
+	if (hostapd_vlan_if_remove(hapd, vlan->ifname))
+		wpa_printf(MSG_ERROR, "Removal of %s failed", vlan->ifname);
+
+	return ret;
+}
+
+
+static int vlan_if_remove(struct hostapd_data *hapd, struct hostapd_vlan *vlan)
+{
+	int ret;
+
+	ret = wpa_auth_release_group(hapd->wpa_auth, vlan->vlan_id);
+	if (ret)
+		wpa_printf(MSG_ERROR,
+			   "WPA deinitialization for VLAN %d failed (%d)",
+			   vlan->vlan_id, ret);
+
+	return hostapd_vlan_if_remove(hapd, vlan->ifname);
+}
+
+
+#ifdef CONFIG_FULL_DYNAMIC_VLAN
+
 static int ifconfig_down(const char *if_name)
 {
 	wpa_printf(MSG_DEBUG, "VLAN: Set interface %s down", if_name);
@@ -882,48 +946,19 @@
 #endif /* CONFIG_FULL_DYNAMIC_VLAN */
 
 
-int vlan_setup_encryption_dyn(struct hostapd_data *hapd, const char *dyn_vlan)
-{
-        int i;
-
-        if (dyn_vlan == NULL)
-		return 0;
-
-	/* Static WEP keys are set here; IEEE 802.1X and WPA uses their own
-	 * functions for setting up dynamic broadcast keys. */
-	for (i = 0; i < 4; i++) {
-		if (hapd->conf->ssid.wep.key[i] &&
-		    hostapd_drv_set_key(dyn_vlan, hapd, WPA_ALG_WEP, NULL, i,
-					i == hapd->conf->ssid.wep.idx, NULL, 0,
-					hapd->conf->ssid.wep.key[i],
-					hapd->conf->ssid.wep.len[i]))
-		{
-			wpa_printf(MSG_ERROR, "VLAN: Could not set WEP "
-				   "encryption for dynamic VLAN");
-			return -1;
-		}
-	}
-
-	return 0;
-}
-
-
 static int vlan_dynamic_add(struct hostapd_data *hapd,
 			    struct hostapd_vlan *vlan)
 {
 	while (vlan) {
 		if (vlan->vlan_id != VLAN_ID_WILDCARD) {
-			if (hostapd_vlan_if_add(hapd, vlan->ifname)) {
-				if (errno != EEXIST) {
-					wpa_printf(MSG_ERROR, "VLAN: Could "
-						   "not add VLAN %s: %s",
-						   vlan->ifname,
-						   strerror(errno));
-					return -1;
-				}
+			if (vlan_if_add(hapd, vlan, 1)) {
+				wpa_printf(MSG_ERROR,
+					   "VLAN: Could not add VLAN %s: %s",
+					   vlan->ifname, strerror(errno));
+				return -1;
 			}
 #ifdef CONFIG_FULL_DYNAMIC_VLAN
-			ifconfig_up(vlan->ifname);
+			vlan_newlink(vlan->ifname, hapd);
 #endif /* CONFIG_FULL_DYNAMIC_VLAN */
 		}
 
@@ -943,7 +978,7 @@
 		next = vlan->next;
 
 		if (vlan->vlan_id != VLAN_ID_WILDCARD &&
-		    hostapd_vlan_if_remove(hapd, vlan->ifname)) {
+		    vlan_if_remove(hapd, vlan)) {
 			wpa_printf(MSG_ERROR, "VLAN: Could not remove VLAN "
 				   "iface: %s: %s",
 				   vlan->ifname, strerror(errno));
@@ -1031,19 +1066,17 @@
 	os_snprintf(n->ifname, sizeof(n->ifname), "%s%d%s", ifname, vlan_id,
 		    pos);
 
-	if (hostapd_vlan_if_add(hapd, n->ifname)) {
+	n->next = hapd->conf->vlan;
+	hapd->conf->vlan = n;
+
+	/* hapd->conf->vlan needs this new VLAN here for WPA setup */
+	if (vlan_if_add(hapd, n, 0)) {
+		hapd->conf->vlan = n->next;
 		os_free(n);
 		n = NULL;
 		goto free_ifname;
 	}
 
-	n->next = hapd->conf->vlan;
-	hapd->conf->vlan = n;
-
-#ifdef CONFIG_FULL_DYNAMIC_VLAN
-	ifconfig_up(n->ifname);
-#endif /* CONFIG_FULL_DYNAMIC_VLAN */
-
 free_ifname:
 	os_free(ifname);
 	return n;
@@ -1073,7 +1106,7 @@
 		return 1;
 
 	if (vlan->dynamic_vlan == 0) {
-		hostapd_vlan_if_remove(hapd, vlan->ifname);
+		vlan_if_remove(hapd, vlan);
 #ifdef CONFIG_FULL_DYNAMIC_VLAN
 		vlan_dellink(vlan->ifname, hapd);
 #endif /* CONFIG_FULL_DYNAMIC_VLAN */
diff --git a/src/ap/vlan_init.h b/src/ap/vlan_init.h
index fc39443..aeb2dc6 100644
--- a/src/ap/vlan_init.h
+++ b/src/ap/vlan_init.h
@@ -17,8 +17,6 @@
 				       struct hostapd_vlan *vlan,
 				       int vlan_id);
 int vlan_remove_dynamic(struct hostapd_data *hapd, int vlan_id);
-int vlan_setup_encryption_dyn(struct hostapd_data *hapd,
-			      const char *dyn_vlan);
 #else /* CONFIG_NO_VLAN */
 static inline int vlan_init(struct hostapd_data *hapd)
 {
@@ -40,12 +38,6 @@
 {
 	return -1;
 }
-
-static inline int vlan_setup_encryption_dyn(struct hostapd_data *hapd,
-					    const char *dyn_vlan)
-{
-	return -1;
-}
 #endif /* CONFIG_NO_VLAN */
 
 #endif /* VLAN_INIT_H */
diff --git a/src/ap/wpa_auth.c b/src/ap/wpa_auth.c
index f23a57a..c2c5693 100644
--- a/src/ap/wpa_auth.c
+++ b/src/ap/wpa_auth.c
@@ -44,7 +44,8 @@
 static int wpa_group_config_group_keys(struct wpa_authenticator *wpa_auth,
 				       struct wpa_group *group);
 static int wpa_derive_ptk(struct wpa_state_machine *sm, const u8 *snonce,
-			  const u8 *pmk, struct wpa_ptk *ptk);
+			  const u8 *pmk, unsigned int pmk_len,
+			  struct wpa_ptk *ptk);
 static void wpa_group_free(struct wpa_authenticator *wpa_auth,
 			   struct wpa_group *group);
 static void wpa_group_get(struct wpa_authenticator *wpa_auth,
@@ -827,6 +828,7 @@
 	struct wpa_ptk PTK;
 	int ok = 0;
 	const u8 *pmk = NULL;
+	unsigned int pmk_len;
 
 	for (;;) {
 		if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt)) {
@@ -834,10 +836,13 @@
 					       sm->p2p_dev_addr, pmk);
 			if (pmk == NULL)
 				break;
-		} else
+			pmk_len = PMK_LEN;
+		} else {
 			pmk = sm->PMK;
+			pmk_len = sm->pmk_len;
+		}
 
-		wpa_derive_ptk(sm, sm->alt_SNonce, pmk, &PTK);
+		wpa_derive_ptk(sm, sm->alt_SNonce, pmk, pmk_len, &PTK);
 
 		if (wpa_verify_key_mic(sm->wpa_key_mgmt, &PTK, data, data_len)
 		    == 0) {
@@ -1540,6 +1545,7 @@
 			else
 				WPA_PUT_BE16(key->key_data_length,
 					     key_data_len);
+#ifndef CONFIG_NO_RC4
 		} else if (sm->PTK.kek_len == 16) {
 			u8 ek[32];
 			os_memcpy(key->key_iv,
@@ -1555,6 +1561,7 @@
 			else
 				WPA_PUT_BE16(key->key_data_length,
 					     key_data_len);
+#endif /* CONFIG_NO_RC4 */
 		} else {
 			os_free(hdr);
 			os_free(buf);
@@ -1669,7 +1676,7 @@
 }
 
 
-int wpa_auth_sm_event(struct wpa_state_machine *sm, wpa_event event)
+int wpa_auth_sm_event(struct wpa_state_machine *sm, enum wpa_event event)
 {
 	int remove_ptk = 1;
 
@@ -1757,6 +1764,14 @@
 			wpa_remove_ptk(sm);
 	}
 
+	if (sm->in_step_loop) {
+		/*
+		 * wpa_sm_step() is already running - avoid recursive call to
+		 * it by making the existing loop process the new update.
+		 */
+		sm->changed = TRUE;
+		return 0;
+	}
 	return wpa_sm_step(sm);
 }
 
@@ -1841,9 +1856,13 @@
 		group->reject_4way_hs_for_entropy = FALSE;
 	}
 
-	wpa_group_init_gmk_and_counter(wpa_auth, group);
-	wpa_gtk_update(wpa_auth, group);
-	wpa_group_config_group_keys(wpa_auth, group);
+	if (wpa_group_init_gmk_and_counter(wpa_auth, group) < 0 ||
+	    wpa_gtk_update(wpa_auth, group) < 0 ||
+	    wpa_group_config_group_keys(wpa_auth, group) < 0) {
+		wpa_printf(MSG_INFO, "WPA: GMK/GTK setup failed");
+		group->first_sta_seen = FALSE;
+		group->reject_4way_hs_for_entropy = TRUE;
+	}
 }
 
 
@@ -1890,11 +1909,27 @@
 #endif /* CONFIG_IEEE80211R */
 	if (sm->pmksa) {
 		wpa_printf(MSG_DEBUG, "WPA: PMK from PMKSA cache");
-		os_memcpy(sm->PMK, sm->pmksa->pmk, PMK_LEN);
+		os_memcpy(sm->PMK, sm->pmksa->pmk, sm->pmksa->pmk_len);
+		sm->pmk_len = sm->pmksa->pmk_len;
 	} else if (wpa_auth_get_msk(sm->wpa_auth, sm->addr, msk, &len) == 0) {
+		unsigned int pmk_len;
+
+		if (sm->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B_192)
+			pmk_len = PMK_LEN_SUITE_B_192;
+		else
+			pmk_len = PMK_LEN;
 		wpa_printf(MSG_DEBUG, "WPA: PMK from EAPOL state machine "
-			   "(len=%lu)", (unsigned long) len);
-		os_memcpy(sm->PMK, msk, PMK_LEN);
+			   "(MSK len=%lu PMK len=%u)", (unsigned long) len,
+			   pmk_len);
+		if (len < pmk_len) {
+			wpa_printf(MSG_DEBUG,
+				   "WPA: MSK not long enough (%u) to create PMK (%u)",
+				   (unsigned int) len, (unsigned int) pmk_len);
+			sm->Disconnect = TRUE;
+			return;
+		}
+		os_memcpy(sm->PMK, msk, pmk_len);
+		sm->pmk_len = pmk_len;
 #ifdef CONFIG_IEEE80211R
 		if (len >= 2 * PMK_LEN) {
 			os_memcpy(sm->xxkey, msk + PMK_LEN, PMK_LEN);
@@ -1929,6 +1964,7 @@
 	psk = wpa_auth_get_psk(sm->wpa_auth, sm->addr, sm->p2p_dev_addr, NULL);
 	if (psk) {
 		os_memcpy(sm->PMK, psk, PMK_LEN);
+		sm->pmk_len = PMK_LEN;
 #ifdef CONFIG_IEEE80211R
 		os_memcpy(sm->xxkey, psk, PMK_LEN);
 		sm->xxkey_len = PMK_LEN;
@@ -1980,7 +2016,7 @@
 			 * Calculate PMKID since no PMKSA cache entry was
 			 * available with pre-calculated PMKID.
 			 */
-			rsn_pmkid(sm->PMK, PMK_LEN, sm->wpa_auth->addr,
+			rsn_pmkid(sm->PMK, sm->pmk_len, sm->wpa_auth->addr,
 				  sm->addr, &pmkid[2 + RSN_SELECTOR_LEN],
 				  wpa_key_mgmt_sha256(sm->wpa_key_mgmt));
 		}
@@ -1992,14 +2028,15 @@
 
 
 static int wpa_derive_ptk(struct wpa_state_machine *sm, const u8 *snonce,
-			  const u8 *pmk, struct wpa_ptk *ptk)
+			  const u8 *pmk, unsigned int pmk_len,
+			  struct wpa_ptk *ptk)
 {
 #ifdef CONFIG_IEEE80211R
 	if (wpa_key_mgmt_ft(sm->wpa_key_mgmt))
 		return wpa_auth_derive_ptk_ft(sm, pmk, ptk);
 #endif /* CONFIG_IEEE80211R */
 
-	return wpa_pmk_to_ptk(pmk, PMK_LEN, "Pairwise key expansion",
+	return wpa_pmk_to_ptk(pmk, pmk_len, "Pairwise key expansion",
 			      sm->wpa_auth->addr, sm->addr, sm->ANonce, snonce,
 			      ptk, sm->wpa_key_mgmt, sm->pairwise);
 }
@@ -2010,6 +2047,7 @@
 	struct wpa_ptk PTK;
 	int ok = 0, psk_found = 0;
 	const u8 *pmk = NULL;
+	unsigned int pmk_len;
 
 	SM_ENTRY_MA(WPA_PTK, PTKCALCNEGOTIATING, wpa_ptk);
 	sm->EAPOLKeyReceived = FALSE;
@@ -2025,10 +2063,13 @@
 			if (pmk == NULL)
 				break;
 			psk_found = 1;
-		} else
+			pmk_len = PMK_LEN;
+		} else {
 			pmk = sm->PMK;
+			pmk_len = sm->pmk_len;
+		}
 
-		wpa_derive_ptk(sm, sm->SNonce, pmk, &PTK);
+		wpa_derive_ptk(sm, sm->SNonce, pmk, pmk_len, &PTK);
 
 		if (wpa_verify_key_mic(sm->wpa_key_mgmt, &PTK,
 				       sm->last_rx_eapol_key,
@@ -2078,6 +2119,7 @@
 		 * state machine data based on whatever PSK was selected here.
 		 */
 		os_memcpy(sm->PMK, pmk, PMK_LEN);
+		sm->pmk_len = PMK_LEN;
 	}
 
 	sm->MICVerified = TRUE;
@@ -3229,13 +3271,21 @@
 
 
 int wpa_auth_pmksa_add(struct wpa_state_machine *sm, const u8 *pmk,
+		       unsigned int pmk_len,
 		       int session_timeout, struct eapol_state_machine *eapol)
 {
 	if (sm == NULL || sm->wpa != WPA_VERSION_WPA2 ||
 	    sm->wpa_auth->conf.disable_pmksa_caching)
 		return -1;
 
-	if (pmksa_cache_auth_add(sm->wpa_auth->pmksa, pmk, PMK_LEN,
+	if (sm->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B_192) {
+		if (pmk_len > PMK_LEN_SUITE_B_192)
+			pmk_len = PMK_LEN_SUITE_B_192;
+	} else if (pmk_len > PMK_LEN) {
+		pmk_len = PMK_LEN;
+	}
+
+	if (pmksa_cache_auth_add(sm->wpa_auth->pmksa, pmk, pmk_len,
 				 sm->PTK.kck, sm->PTK.kck_len,
 				 sm->wpa_auth->addr, sm->addr, session_timeout,
 				 eapol, sm->wpa_key_mgmt))
@@ -3374,6 +3424,98 @@
 }
 
 
+/*
+ * Enforce that the group state machine for the VLAN is running, increase
+ * reference counter as interface is up. References might have been increased
+ * even if a negative value is returned.
+ * Returns: -1 on error (group missing, group already failed); otherwise, 0
+ */
+int wpa_auth_ensure_group(struct wpa_authenticator *wpa_auth, int vlan_id)
+{
+	struct wpa_group *group;
+
+	if (wpa_auth == NULL)
+		return 0;
+
+	group = wpa_auth->group;
+	while (group) {
+		if (group->vlan_id == vlan_id)
+			break;
+		group = group->next;
+	}
+
+	if (group == NULL) {
+		group = wpa_auth_add_group(wpa_auth, vlan_id);
+		if (group == NULL)
+			return -1;
+	}
+
+	wpa_printf(MSG_DEBUG,
+		   "WPA: Ensure group state machine running for VLAN ID %d",
+		   vlan_id);
+
+	wpa_group_get(wpa_auth, group);
+	group->num_setup_iface++;
+
+	if (group->wpa_group_state == WPA_GROUP_FATAL_FAILURE)
+		return -1;
+
+	return 0;
+}
+
+
+/*
+ * Decrease reference counter, expected to be zero afterwards.
+ * returns: -1 on error (group not found, group in fail state)
+ *          -2 if wpa_group is still referenced
+ *           0 else
+ */
+int wpa_auth_release_group(struct wpa_authenticator *wpa_auth, int vlan_id)
+{
+	struct wpa_group *group;
+	int ret = 0;
+
+	if (wpa_auth == NULL)
+		return 0;
+
+	group = wpa_auth->group;
+	while (group) {
+		if (group->vlan_id == vlan_id)
+			break;
+		group = group->next;
+	}
+
+	if (group == NULL)
+		return -1;
+
+	wpa_printf(MSG_DEBUG,
+		   "WPA: Try stopping group state machine for VLAN ID %d",
+		   vlan_id);
+
+	if (group->num_setup_iface <= 0) {
+		wpa_printf(MSG_ERROR,
+			   "WPA: wpa_auth_release_group called more often than wpa_auth_ensure_group for VLAN ID %d, skipping.",
+			   vlan_id);
+		return -1;
+	}
+	group->num_setup_iface--;
+
+	if (group->wpa_group_state == WPA_GROUP_FATAL_FAILURE)
+		ret = -1;
+
+	if (group->references > 1) {
+		wpa_printf(MSG_DEBUG,
+			   "WPA: Cannot stop group state machine for VLAN ID %d as references are still hold",
+			   vlan_id);
+		ret = -2;
+	}
+
+	wpa_group_put(wpa_auth, group);
+
+	return ret;
+}
+
+
 int wpa_auth_sta_set_vlan(struct wpa_state_machine *sm, int vlan_id)
 {
 	struct wpa_group *group;
diff --git a/src/ap/wpa_auth.h b/src/ap/wpa_auth.h
index e747806..75b73f0 100644
--- a/src/ap/wpa_auth.h
+++ b/src/ap/wpa_auth.h
@@ -1,6 +1,6 @@
 /*
  * hostapd - IEEE 802.11i-2004 / WPA Authenticator
- * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2015, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -14,6 +14,8 @@
 #include "common/wpa_common.h"
 #include "common/ieee802_11_defs.h"
 
+#define MAX_OWN_IE_OVERRIDE 256
+
 #ifdef _MSC_VER
 #pragma pack(push, 1)
 #endif /* _MSC_VER */
@@ -164,6 +166,8 @@
 	int ap_mlme;
 #ifdef CONFIG_TESTING_OPTIONS
 	double corrupt_gtk_rekey_mic_probability;
+	u8 own_ie_override[MAX_OWN_IE_OVERRIDE];
+	size_t own_ie_override_len;
 #endif /* CONFIG_TESTING_OPTIONS */
 #ifdef CONFIG_P2P
 	u8 ip_addr_go[4];
@@ -252,12 +256,12 @@
 void wpa_receive(struct wpa_authenticator *wpa_auth,
 		 struct wpa_state_machine *sm,
 		 u8 *data, size_t data_len);
-typedef enum {
+enum wpa_event {
 	WPA_AUTH, WPA_ASSOC, WPA_DISASSOC, WPA_DEAUTH, WPA_REAUTH,
 	WPA_REAUTH_EAPOL, WPA_ASSOC_FT
-} wpa_event;
+};
 void wpa_remove_ptk(struct wpa_state_machine *sm);
-int wpa_auth_sm_event(struct wpa_state_machine *sm, wpa_event event);
+int wpa_auth_sm_event(struct wpa_state_machine *sm, enum wpa_event event);
 void wpa_auth_sm_notify(struct wpa_state_machine *sm);
 void wpa_gtk_rekey(struct wpa_authenticator *wpa_auth);
 int wpa_get_mib(struct wpa_authenticator *wpa_auth, char *buf, size_t buflen);
@@ -275,6 +279,7 @@
 const u8 * wpa_auth_get_wpa_ie(struct wpa_authenticator *wpa_auth,
 			       size_t *len);
 int wpa_auth_pmksa_add(struct wpa_state_machine *sm, const u8 *pmk,
+		       unsigned int pmk_len,
 		       int session_timeout, struct eapol_state_machine *eapol);
 int wpa_auth_pmksa_add_preauth(struct wpa_authenticator *wpa_auth,
 			       const u8 *pmk, size_t len, const u8 *sta_addr,
@@ -321,4 +326,7 @@
 					 struct radius_das_attrs *attr);
 void wpa_auth_reconfig_group_keys(struct wpa_authenticator *wpa_auth);
 
+int wpa_auth_ensure_group(struct wpa_authenticator *wpa_auth, int vlan_id);
+int wpa_auth_release_group(struct wpa_authenticator *wpa_auth, int vlan_id);
+
 #endif /* WPA_AUTH_H */
diff --git a/src/ap/wpa_auth_glue.c b/src/ap/wpa_auth_glue.c
index 7cd0b6c..ffd0790 100644
--- a/src/ap/wpa_auth_glue.c
+++ b/src/ap/wpa_auth_glue.c
@@ -92,6 +92,13 @@
 #ifdef CONFIG_TESTING_OPTIONS
 	wconf->corrupt_gtk_rekey_mic_probability =
 		iconf->corrupt_gtk_rekey_mic_probability;
+	if (conf->own_ie_override &&
+	    wpabuf_len(conf->own_ie_override) <= MAX_OWN_IE_OVERRIDE) {
+		wconf->own_ie_override_len = wpabuf_len(conf->own_ie_override);
+		os_memcpy(wconf->own_ie_override,
+			  wpabuf_head(conf->own_ie_override),
+			  wconf->own_ie_override_len);
+	}
 #endif /* CONFIG_TESTING_OPTIONS */
 #ifdef CONFIG_P2P
 	os_memcpy(wconf->ip_addr_go, conf->ip_addr_go, 4);
@@ -630,7 +637,8 @@
 	}
 
 #ifdef CONFIG_IEEE80211R
-	if (!hostapd_drv_none(hapd)) {
+	if (!hostapd_drv_none(hapd) && hapd->conf->ft_over_ds &&
+	    wpa_key_mgmt_ft(hapd->conf->wpa_key_mgmt)) {
 		hapd->l2 = l2_packet_init(hapd->conf->bridge[0] ?
 					  hapd->conf->bridge :
 					  hapd->conf->iface, NULL, ETH_P_RRB,
@@ -666,13 +674,14 @@
 		wpa_deinit(hapd->wpa_auth);
 		hapd->wpa_auth = NULL;
 
-		if (hostapd_set_privacy(hapd, 0)) {
+		if (hapd->drv_priv && hostapd_set_privacy(hapd, 0)) {
 			wpa_printf(MSG_DEBUG, "Could not disable "
 				   "PrivacyInvoked for interface %s",
 				   hapd->conf->iface);
 		}
 
-		if (hostapd_set_generic_elem(hapd, (u8 *) "", 0)) {
+		if (hapd->drv_priv &&
+		    hostapd_set_generic_elem(hapd, (u8 *) "", 0)) {
 			wpa_printf(MSG_DEBUG, "Could not remove generic "
 				   "information element from interface %s",
 				   hapd->conf->iface);
diff --git a/src/ap/wpa_auth_i.h b/src/ap/wpa_auth_i.h
index 57b098f..72b7eb3 100644
--- a/src/ap/wpa_auth_i.h
+++ b/src/ap/wpa_auth_i.h
@@ -60,7 +60,8 @@
 	u8 SNonce[WPA_NONCE_LEN];
 	u8 alt_SNonce[WPA_NONCE_LEN];
 	u8 alt_replay_counter[WPA_REPLAY_COUNTER_LEN];
-	u8 PMK[PMK_LEN];
+	u8 PMK[PMK_LEN_MAX];
+	unsigned int pmk_len;
 	struct wpa_ptk PTK;
 	Boolean PTK_valid;
 	Boolean pairwise_set;
@@ -171,6 +172,7 @@
 #endif /* CONFIG_IEEE80211W */
 	/* Number of references except those in struct wpa_group->next */
 	unsigned int references;
+	unsigned int num_setup_iface;
 };
 
 
diff --git a/src/ap/wpa_auth_ie.c b/src/ap/wpa_auth_ie.c
index f287297..52ccac3 100644
--- a/src/ap/wpa_auth_ie.c
+++ b/src/ap/wpa_auth_ie.c
@@ -251,7 +251,7 @@
 	pos += 2;
 
 	if (pmkid) {
-		if (pos + 2 + PMKID_LEN > buf + len)
+		if (2 + PMKID_LEN > buf + len - pos)
 			return -1;
 		/* PMKID Count */
 		WPA_PUT_LE16(pos, 1);
@@ -261,8 +261,9 @@
 	}
 
 #ifdef CONFIG_IEEE80211W
-	if (conf->ieee80211w != NO_MGMT_FRAME_PROTECTION) {
-		if (pos + 2 + 4 > buf + len)
+	if (conf->ieee80211w != NO_MGMT_FRAME_PROTECTION &&
+	    conf->group_mgmt_cipher != WPA_CIPHER_AES_128_CMAC) {
+		if (2 + 4 > buf + len - pos)
 			return -1;
 		if (pmkid == NULL) {
 			/* PMKID Count */
@@ -377,6 +378,23 @@
 	u8 *pos, buf[128];
 	int res;
 
+#ifdef CONFIG_TESTING_OPTIONS
+	if (wpa_auth->conf.own_ie_override_len) {
+		wpa_hexdump(MSG_DEBUG, "WPA: Forced own IE(s) for testing",
+			    wpa_auth->conf.own_ie_override,
+			    wpa_auth->conf.own_ie_override_len);
+		os_free(wpa_auth->wpa_ie);
+		wpa_auth->wpa_ie =
+			os_malloc(wpa_auth->conf.own_ie_override_len);
+		if (wpa_auth->wpa_ie == NULL)
+			return -1;
+		os_memcpy(wpa_auth->wpa_ie, wpa_auth->conf.own_ie_override,
+			  wpa_auth->conf.own_ie_override_len);
+		wpa_auth->wpa_ie_len = wpa_auth->conf.own_ie_override_len;
+		return 0;
+	}
+#endif /* CONFIG_TESTING_OPTIONS */
+
 	pos = buf;
 
 	if (wpa_auth->conf.wpa == WPA_PROTO_OSEN) {
@@ -773,7 +791,7 @@
 		return 0;
 	}
 
-	if (pos + 1 + RSN_SELECTOR_LEN < end &&
+	if (1 + RSN_SELECTOR_LEN < end - pos &&
 	    pos[1] >= RSN_SELECTOR_LEN + PMKID_LEN &&
 	    RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_PMKID) {
 		ie->pmkid = pos + 2 + RSN_SELECTOR_LEN;
@@ -869,13 +887,13 @@
 	int ret = 0;
 
 	os_memset(ie, 0, sizeof(*ie));
-	for (pos = buf, end = pos + len; pos + 1 < end; pos += 2 + pos[1]) {
+	for (pos = buf, end = pos + len; end - pos > 1; pos += 2 + pos[1]) {
 		if (pos[0] == 0xdd &&
 		    ((pos == buf + len - 1) || pos[1] == 0)) {
 			/* Ignore padding */
 			break;
 		}
-		if (pos + 2 + pos[1] > end) {
+		if (2 + pos[1] > end - pos) {
 			wpa_printf(MSG_DEBUG, "WPA: EAPOL-Key Key Data "
 				   "underflow (ie=%d len=%d pos=%d)",
 				   pos[0], pos[1], (int) (pos - buf));
diff --git a/src/ap/wps_hostapd.c b/src/ap/wps_hostapd.c
index caed01e..66a43eb 100644
--- a/src/ap/wps_hostapd.c
+++ b/src/ap/wps_hostapd.c
@@ -452,6 +452,11 @@
 		os_free(hapd->wps->network_key);
 		hapd->wps->network_key = NULL;
 		hapd->wps->network_key_len = 0;
+	} else if ((cred->auth_type & (WPS_AUTH_WPA2PSK | WPS_AUTH_WPAPSK)) &&
+		   (cred->key_len < 8 || cred->key_len > 2 * PMK_LEN)) {
+		wpa_printf(MSG_INFO, "WPS: Invalid key length %lu for WPA/WPA2",
+			   (unsigned long) cred->key_len);
+		return -1;
 	} else {
 		if (hapd->wps->network_key == NULL ||
 		    hapd->wps->network_key_len < cred->key_len) {
@@ -867,7 +872,8 @@
 	hapd->wps_probe_resp_ie = NULL;
 
 	if (deinit_only) {
-		hostapd_reset_ap_wps_ie(hapd);
+		if (hapd->drv_priv)
+			hostapd_reset_ap_wps_ie(hapd);
 		return;
 	}
 
@@ -1299,30 +1305,53 @@
 }
 
 
+struct wps_button_pushed_ctx {
+	const u8 *p2p_dev_addr;
+	unsigned int count;
+};
+
 static int wps_button_pushed(struct hostapd_data *hapd, void *ctx)
 {
-	const u8 *p2p_dev_addr = ctx;
-	if (hapd->wps == NULL)
-		return -1;
-	return wps_registrar_button_pushed(hapd->wps->registrar, p2p_dev_addr);
+	struct wps_button_pushed_ctx *data = ctx;
+
+	if (hapd->wps) {
+		data->count++;
+		return wps_registrar_button_pushed(hapd->wps->registrar,
+						   data->p2p_dev_addr);
+	}
+
+	return 0;
 }
 
 
 int hostapd_wps_button_pushed(struct hostapd_data *hapd,
 			      const u8 *p2p_dev_addr)
 {
-	return hostapd_wps_for_each(hapd, wps_button_pushed,
-				    (void *) p2p_dev_addr);
+	struct wps_button_pushed_ctx ctx;
+	int ret;
+
+	os_memset(&ctx, 0, sizeof(ctx));
+	ctx.p2p_dev_addr = p2p_dev_addr;
+	ret = hostapd_wps_for_each(hapd, wps_button_pushed, &ctx);
+	if (ret == 0 && !ctx.count)
+		ret = -1;
+	return ret;
 }
 
 
+struct wps_cancel_ctx {
+	unsigned int count;
+};
+
 static int wps_cancel(struct hostapd_data *hapd, void *ctx)
 {
-	if (hapd->wps == NULL)
-		return -1;
+	struct wps_cancel_ctx *data = ctx;
 
-	wps_registrar_wps_cancel(hapd->wps->registrar);
-	ap_for_each_sta(hapd, ap_sta_wps_cancel, NULL);
+	if (hapd->wps) {
+		data->count++;
+		wps_registrar_wps_cancel(hapd->wps->registrar);
+		ap_for_each_sta(hapd, ap_sta_wps_cancel, NULL);
+	}
 
 	return 0;
 }
@@ -1330,7 +1359,14 @@
 
 int hostapd_wps_cancel(struct hostapd_data *hapd)
 {
-	return hostapd_wps_for_each(hapd, wps_cancel, NULL);
+	struct wps_cancel_ctx ctx;
+	int ret;
+
+	os_memset(&ctx, 0, sizeof(ctx));
+	ret = hostapd_wps_for_each(hapd, wps_cancel, &ctx);
+	if (ret == 0 && !ctx.count)
+		ret = -1;
+	return ret;
 }
 
 
@@ -1560,6 +1596,10 @@
 static int wps_ap_pin_set(struct hostapd_data *hapd, void *ctx)
 {
 	struct wps_ap_pin_data *data = ctx;
+
+	if (!hapd->wps)
+		return 0;
+
 	os_free(hapd->conf->ap_pin);
 	hapd->conf->ap_pin = os_strdup(data->pin_txt);
 #ifdef CONFIG_WPS_UPNP
diff --git a/src/common/defs.h b/src/common/defs.h
index eb080ea..6aea375 100644
--- a/src/common/defs.h
+++ b/src/common/defs.h
@@ -174,7 +174,7 @@
 	/**
 	 * WPA_INTERFACE_DISABLED - Interface disabled
 	 *
-	 * This stat eis entered if the network interface is disabled, e.g.,
+	 * This state is entered if the network interface is disabled, e.g.,
 	 * due to rfkill. wpa_supplicant refuses any new operations that would
 	 * use the radio until the interface has been enabled.
 	 */
diff --git a/src/common/hw_features_common.c b/src/common/hw_features_common.c
index e589a1a..9c37ea6 100644
--- a/src/common/hw_features_common.c
+++ b/src/common/hw_features_common.c
@@ -272,10 +272,8 @@
 	int affected_start, affected_end;
 	size_t i;
 
-	if (!mode || !scan_res || !pri_chan || !sec_chan)
-		return 0;
-
-	if (pri_chan == sec_chan)
+	if (!mode || !scan_res || !pri_chan || !sec_chan ||
+	    pri_chan == sec_chan)
 		return 0;
 
 	pri_freq = hw_get_freq(mode, pri_chan);
diff --git a/src/common/ieee802_11_common.c b/src/common/ieee802_11_common.c
index 5385faf..8dee883 100644
--- a/src/common/ieee802_11_common.c
+++ b/src/common/ieee802_11_common.c
@@ -11,6 +11,7 @@
 #include "common.h"
 #include "defs.h"
 #include "wpa_common.h"
+#include "qca-vendor.h"
 #include "ieee802_11_defs.h"
 #include "ieee802_11_common.h"
 
@@ -147,6 +148,20 @@
 		}
 		break;
 
+	case OUI_QCA:
+		switch (pos[3]) {
+		case QCA_VENDOR_ELEM_P2P_PREF_CHAN_LIST:
+			elems->pref_freq_list = pos;
+			elems->pref_freq_list_len = elen;
+			break;
+		default:
+			wpa_printf(MSG_EXCESSIVE,
+				   "Unknown QCA information element ignored (type=%d len=%lu)",
+				   pos[3], (unsigned long) elen);
+			return -1;
+		}
+		break;
+
 	default:
 		wpa_printf(MSG_EXCESSIVE, "unknown vendor specific "
 			   "information element ignored (vendor OUI "
@@ -339,6 +354,18 @@
 			/* after mic everything is encrypted, so stop. */
 			left = elen;
 			break;
+		case WLAN_EID_MULTI_BAND:
+			if (elems->mb_ies.nof_ies >= MAX_NOF_MB_IES_SUPPORTED) {
+				wpa_printf(MSG_MSGDUMP,
+					   "IEEE 802.11 element parse ignored MB IE (id=%d elen=%d)",
+					   id, elen);
+				break;
+			}
+
+			elems->mb_ies.ies[elems->mb_ies.nof_ies].ie = pos;
+			elems->mb_ies.ies[elems->mb_ies.nof_ies].ie_len = elen;
+			elems->mb_ies.nof_ies++;
+			break;
 		default:
 			unknown++;
 			if (!show_errors)
@@ -371,8 +398,8 @@
 	pos = ies;
 	end = ies + ies_len;
 
-	while (pos + 2 <= end) {
-		if (pos + 2 + pos[1] > end)
+	while (end - pos >= 2) {
+		if (2 + pos[1] > end - pos)
 			break;
 		count++;
 		pos += 2 + pos[1];
@@ -392,8 +419,8 @@
 	end = ies + ies_len;
 	ie = NULL;
 
-	while (pos + 1 < end) {
-		if (pos + 2 + pos[1] > end)
+	while (end - pos > 1) {
+		if (2 + pos[1] > end - pos)
 			return NULL;
 		if (pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 &&
 		    WPA_GET_BE32(&pos[2]) == oui_type) {
@@ -414,8 +441,8 @@
 	 * There may be multiple vendor IEs in the message, so need to
 	 * concatenate their data fields.
 	 */
-	while (pos + 1 < end) {
-		if (pos + 2 + pos[1] > end)
+	while (end - pos > 1) {
+		if (2 + pos[1] > end - pos)
 			break;
 		if (pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 &&
 		    WPA_GET_BE32(&pos[2]) == oui_type)
@@ -541,26 +568,166 @@
 
 enum hostapd_hw_mode ieee80211_freq_to_chan(int freq, u8 *channel)
 {
-	enum hostapd_hw_mode mode = NUM_HOSTAPD_MODES;
+	u8 op_class;
+
+	return ieee80211_freq_to_channel_ext(freq, 0, VHT_CHANWIDTH_USE_HT,
+					     &op_class, channel);
+}
+
+
+/**
+ * ieee80211_freq_to_channel_ext - Convert frequency into channel info
+ * for HT40 and VHT. DFS channels are not covered.
+ * @freq: Frequency (MHz) to convert
+ * @sec_channel: 0 = non-HT40, 1 = sec. channel above, -1 = sec. channel below
+ * @vht: VHT channel width (VHT_CHANWIDTH_*)
+ * @op_class: Buffer for returning operating class
+ * @channel: Buffer for returning channel number
+ * Returns: hw_mode on success, NUM_HOSTAPD_MODES on failure
+ */
+enum hostapd_hw_mode ieee80211_freq_to_channel_ext(unsigned int freq,
+						   int sec_channel, int vht,
+						   u8 *op_class, u8 *channel)
+{
+	u8 vht_opclass;
+
+	/* TODO: more operating classes */
+
+	if (sec_channel > 1 || sec_channel < -1)
+		return NUM_HOSTAPD_MODES;
 
 	if (freq >= 2412 && freq <= 2472) {
-		mode = HOSTAPD_MODE_IEEE80211G;
+		if ((freq - 2407) % 5)
+			return NUM_HOSTAPD_MODES;
+
+		if (vht)
+			return NUM_HOSTAPD_MODES;
+
+		/* 2.407 GHz, channels 1..13 */
+		if (sec_channel == 1)
+			*op_class = 83;
+		else if (sec_channel == -1)
+			*op_class = 84;
+		else
+			*op_class = 81;
+
 		*channel = (freq - 2407) / 5;
-	} else if (freq == 2484) {
-		mode = HOSTAPD_MODE_IEEE80211B;
-		*channel = 14;
-	} else if (freq >= 4900 && freq < 5000) {
-		mode = HOSTAPD_MODE_IEEE80211A;
-		*channel = (freq - 4000) / 5;
-	} else if (freq >= 5000 && freq < 5900) {
-		mode = HOSTAPD_MODE_IEEE80211A;
-		*channel = (freq - 5000) / 5;
-	} else if (freq >= 56160 + 2160 * 1 && freq <= 56160 + 2160 * 4) {
-		mode = HOSTAPD_MODE_IEEE80211AD;
-		*channel = (freq - 56160) / 2160;
+
+		return HOSTAPD_MODE_IEEE80211G;
 	}
 
-	return mode;
+	if (freq == 2484) {
+		if (sec_channel || vht)
+			return NUM_HOSTAPD_MODES;
+
+		*op_class = 82; /* channel 14 */
+		*channel = 14;
+
+		return HOSTAPD_MODE_IEEE80211B;
+	}
+
+	if (freq >= 4900 && freq < 5000) {
+		if ((freq - 4000) % 5)
+			return NUM_HOSTAPD_MODES;
+		*channel = (freq - 4000) / 5;
+		*op_class = 0; /* TODO */
+		return HOSTAPD_MODE_IEEE80211A;
+	}
+
+	switch (vht) {
+	case VHT_CHANWIDTH_80MHZ:
+		vht_opclass = 128;
+		break;
+	case VHT_CHANWIDTH_160MHZ:
+		vht_opclass = 129;
+		break;
+	case VHT_CHANWIDTH_80P80MHZ:
+		vht_opclass = 130;
+		break;
+	default:
+		vht_opclass = 0;
+		break;
+	}
+
+	/* 5 GHz, channels 36..48 */
+	if (freq >= 5180 && freq <= 5240) {
+		if ((freq - 5000) % 5)
+			return NUM_HOSTAPD_MODES;
+
+		if (vht_opclass)
+			*op_class = vht_opclass;
+		else if (sec_channel == 1)
+			*op_class = 116;
+		else if (sec_channel == -1)
+			*op_class = 117;
+		else
+			*op_class = 115;
+
+		*channel = (freq - 5000) / 5;
+
+		return HOSTAPD_MODE_IEEE80211A;
+	}
+
+	/* 5 GHz, channels 149..169 */
+	if (freq >= 5745 && freq <= 5845) {
+		if ((freq - 5000) % 5)
+			return NUM_HOSTAPD_MODES;
+
+		if (vht_opclass)
+			*op_class = vht_opclass;
+		else if (sec_channel == 1)
+			*op_class = 126;
+		else if (sec_channel == -1)
+			*op_class = 127;
+		else if (freq <= 5805)
+			*op_class = 124;
+		else
+			*op_class = 125;
+
+		*channel = (freq - 5000) / 5;
+
+		return HOSTAPD_MODE_IEEE80211A;
+	}
+
+	/* 5 GHz, channels 100..140 */
+	if (freq >= 5000 && freq <= 5700) {
+		if ((freq - 5000) % 5)
+			return NUM_HOSTAPD_MODES;
+
+		if (vht_opclass)
+			*op_class = vht_opclass;
+		else if (sec_channel == 1)
+			*op_class = 122;
+		else if (sec_channel == -1)
+			*op_class = 123;
+		else
+			*op_class = 121;
+
+		*channel = (freq - 5000) / 5;
+
+		return HOSTAPD_MODE_IEEE80211A;
+	}
+
+	if (freq >= 5000 && freq < 5900) {
+		if ((freq - 5000) % 5)
+			return NUM_HOSTAPD_MODES;
+		*channel = (freq - 5000) / 5;
+		*op_class = 0; /* TODO */
+		return HOSTAPD_MODE_IEEE80211A;
+	}
+
+	/* 56.16 GHz, channel 1..4 */
+	if (freq >= 56160 + 2160 * 1 && freq <= 56160 + 2160 * 4) {
+		if (sec_channel || vht)
+			return NUM_HOSTAPD_MODES;
+
+		*channel = (freq - 56160) / 2160;
+		*op_class = 180;
+
+		return HOSTAPD_MODE_IEEE80211AD;
+	}
+
+	return NUM_HOSTAPD_MODES;
 }
 
 
@@ -946,3 +1113,62 @@
 	return "WLAN_FC_TYPE_UNKNOWN";
 #undef C2S
 }
+
+
+int mb_ies_info_by_ies(struct mb_ies_info *info, const u8 *ies_buf,
+		       size_t ies_len)
+{
+	os_memset(info, 0, sizeof(*info));
+
+	while (ies_buf && ies_len >= 2 &&
+	       info->nof_ies < MAX_NOF_MB_IES_SUPPORTED) {
+		size_t len = 2 + ies_buf[1];
+
+		if (len > ies_len) {
+			wpa_hexdump(MSG_DEBUG, "Truncated IEs",
+				    ies_buf, ies_len);
+			return -1;
+		}
+
+		if (ies_buf[0] == WLAN_EID_MULTI_BAND) {
+			wpa_printf(MSG_DEBUG, "MB IE of %zu bytes found", len);
+			info->ies[info->nof_ies].ie = ies_buf + 2;
+			info->ies[info->nof_ies].ie_len = ies_buf[1];
+			info->nof_ies++;
+		}
+
+		ies_len -= len;
+		ies_buf += len;
+	}
+
+	return 0;
+}
+
+
+struct wpabuf * mb_ies_by_info(struct mb_ies_info *info)
+{
+	struct wpabuf *mb_ies = NULL;
+
+	WPA_ASSERT(info != NULL);
+
+	if (info->nof_ies) {
+		u8 i;
+		size_t mb_ies_size = 0;
+
+		for (i = 0; i < info->nof_ies; i++)
+			mb_ies_size += 2 + info->ies[i].ie_len;
+
+		mb_ies = wpabuf_alloc(mb_ies_size);
+		if (mb_ies) {
+			for (i = 0; i < info->nof_ies; i++) {
+				wpabuf_put_u8(mb_ies, WLAN_EID_MULTI_BAND);
+				wpabuf_put_u8(mb_ies, info->ies[i].ie_len);
+				wpabuf_put_data(mb_ies,
+						info->ies[i].ie,
+						info->ies[i].ie_len);
+			}
+		}
+	}
+
+	return mb_ies;
+}
diff --git a/src/common/ieee802_11_common.h b/src/common/ieee802_11_common.h
index c84d8a7..55ce022 100644
--- a/src/common/ieee802_11_common.h
+++ b/src/common/ieee802_11_common.h
@@ -9,6 +9,16 @@
 #ifndef IEEE802_11_COMMON_H
 #define IEEE802_11_COMMON_H
 
+#define MAX_NOF_MB_IES_SUPPORTED 5
+
+struct mb_ies_info {
+	struct {
+		const u8 *ie;
+		u8 ie_len;
+	} ies[MAX_NOF_MB_IES_SUPPORTED];
+	u8 nof_ies;
+};
+
 /* Parsed Information Elements */
 struct ieee802_11_elems {
 	const u8 *ssid;
@@ -48,6 +58,7 @@
 	const u8 *osen;
 	const u8 *ampe;
 	const u8 *mic;
+	const u8 *pref_freq_list;
 
 	u8 ssid_len;
 	u8 supp_rates_len;
@@ -76,6 +87,8 @@
 	u8 osen_len;
 	u8 ampe_len;
 	u8 mic_len;
+	u8 pref_freq_list_len;
+	struct mb_ies_info mb_ies;
 };
 
 typedef enum { ParseOK = 0, ParseUnknown = 1, ParseFailed = -1 } ParseRes;
@@ -101,9 +114,15 @@
 			  const char *name, const char *val);
 enum hostapd_hw_mode ieee80211_freq_to_chan(int freq, u8 *channel);
 int ieee80211_chan_to_freq(const char *country, u8 op_class, u8 chan);
+enum hostapd_hw_mode ieee80211_freq_to_channel_ext(unsigned int freq,
+						   int sec_channel, int vht,
+						   u8 *op_class, u8 *channel);
 int ieee80211_is_dfs(int freq);
 
 int supp_rates_11b_only(struct ieee802_11_elems *elems);
+int mb_ies_info_by_ies(struct mb_ies_info *info, const u8 *ies_buf,
+		       size_t ies_len);
+struct wpabuf * mb_ies_by_info(struct mb_ies_info *info);
 
 const char * fc2str(u16 fc);
 #endif /* IEEE802_11_COMMON_H */
diff --git a/src/common/ieee802_11_defs.h b/src/common/ieee802_11_defs.h
index 47b15de..5b8d130 100644
--- a/src/common/ieee802_11_defs.h
+++ b/src/common/ieee802_11_defs.h
@@ -165,7 +165,10 @@
 #define WLAN_STATUS_ANTI_CLOGGING_TOKEN_REQ 76
 #define WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED 77
 #define WLAN_STATUS_TRANSMISSION_FAILURE 79
+#define WLAN_STATUS_REJECTED_WITH_SUGGESTED_BSS_TRANSITION 82
+#define WLAN_STATUS_PENDING_ADMITTING_FST_SESSION 86
 #define WLAN_STATUS_QUERY_RESP_OUTSTANDING 95
+#define WLAN_STATUS_DENIED_WITH_SUGGESTED_BAND_AND_CHANNEL 99
 #define WLAN_STATUS_ASSOC_DENIED_NO_VHT 104
 
 /* Reason codes (IEEE 802.11-2007, 7.3.1.7, Table 7-22) */
@@ -244,6 +247,7 @@
 #define WLAN_EID_TIMEOUT_INTERVAL 56
 #define WLAN_EID_RIC_DATA 57
 #define WLAN_EID_SUPPORTED_OPERATING_CLASSES 59
+#define WLAN_EID_EXT_CHANSWITCH_ANN 60
 #define WLAN_EID_HT_OPERATION 61
 #define WLAN_EID_SECONDARY_CHANNEL_OFFSET 62
 #define WLAN_EID_WAPI 68
@@ -271,6 +275,8 @@
 #define WLAN_EID_AMPE 139
 #define WLAN_EID_MIC 140
 #define WLAN_EID_CCKM 156
+#define WLAN_EID_MULTI_BAND 158
+#define WLAN_EID_SESSION_TRANSITION 164
 #define WLAN_EID_VHT_CAP 191
 #define WLAN_EID_VHT_OPERATION 192
 #define WLAN_EID_VHT_EXTENDED_BSS_LOAD 193
@@ -299,6 +305,7 @@
 #define WLAN_ACTION_TDLS 12
 #define WLAN_ACTION_SELF_PROTECTED 15
 #define WLAN_ACTION_WMM 17 /* WMM Specification 1.1 */
+#define WLAN_ACTION_FST 18
 #define WLAN_ACTION_VENDOR_SPECIFIC 127
 
 /* Public action codes */
@@ -401,7 +408,12 @@
 	ANQP_AP_LOCATION_PUBLIC_URI = 267,
 	ANQP_DOMAIN_NAME = 268,
 	ANQP_EMERGENCY_ALERT_URI = 269,
+	ANQP_TDLS_CAPABILITY = 270,
 	ANQP_EMERGENCY_NAI = 271,
+	ANQP_NEIGHBOR_REPORT = 272,
+	ANQP_VENUE_URL = 277,
+	ANQP_ADVICE_OF_CHARGE = 278,
+	ANQP_LOCAL_CONTENT = 279,
 	ANQP_VENDOR_SPECIFIC = 56797
 };
 
@@ -615,6 +627,10 @@
 					u8 action; /* 15 */
 					u8 variable[];
 				} STRUCT_PACKED slf_prot_action;
+				struct {
+					u8 action;
+					u8 variable[];
+				} STRUCT_PACKED fst_action;
 			} u;
 		} STRUCT_PACKED action;
 	} u;
@@ -1067,6 +1083,15 @@
 #define P2P_GROUP_CAPAB_GROUP_FORMATION BIT(6)
 #define P2P_GROUP_CAPAB_IP_ADDR_ALLOCATION BIT(7)
 
+/* P2PS Coordination Protocol Transport Bitmap */
+#define P2PS_FEATURE_CAPAB_UDP_TRANSPORT BIT(0)
+#define P2PS_FEATURE_CAPAB_MAC_TRANSPORT BIT(1)
+
+struct p2ps_feature_capab {
+	u8 cpt;
+	u8 reserved;
+} STRUCT_PACKED;
+
 /* Invitation Flags */
 #define P2P_INVITATION_FLAGS_TYPE BIT(0)
 
@@ -1358,4 +1383,60 @@
 
 #define SSID_MAX_LEN 32
 
+/* IEEE Std 802.11ad-2012 - Multi-band element */
+struct multi_band_ie {
+	u8 eid; /* WLAN_EID_MULTI_BAND */
+	u8 len;
+	u8 mb_ctrl;
+	u8 band_id;
+	u8 op_class;
+	u8 chan;
+	u8 bssid[ETH_ALEN];
+	le16 beacon_int;
+	u8 tsf_offs[8];
+	u8 mb_connection_capability;
+	u8 fst_session_tmout;
+	/* Optional:
+	 *   STA MAC Address
+	 *   Pairwise Cipher Suite Count
+	 *   Pairwise Cipher Suite List
+	 */
+	u8 variable[0];
+} STRUCT_PACKED;
+
+enum mb_ctrl_sta_role {
+	MB_STA_ROLE_AP = 0,
+	MB_STA_ROLE_TDLS_STA = 1,
+	MB_STA_ROLE_IBSS_STA = 2,
+	MB_STA_ROLE_PCP = 3,
+	MB_STA_ROLE_NON_PCP_NON_AP = 4
+};
+
+#define MB_CTRL_ROLE_MASK (BIT(0) | BIT(1) | BIT(2))
+#define MB_CTRL_ROLE(ctrl) ((u8) ((ctrl) & MB_CTRL_ROLE_MASK))
+#define MB_CTRL_STA_MAC_PRESENT ((u8) (BIT(3)))
+#define MB_CTRL_PAIRWISE_CIPHER_SUITE_PRESENT ((u8) (BIT(4)))
+
+enum mb_band_id {
+	MB_BAND_ID_WIFI_2_4GHZ = 2, /* 2.4 GHz */
+	MB_BAND_ID_WIFI_5GHZ = 4, /* 4.9 and 5 GHz */
+	MB_BAND_ID_WIFI_60GHZ = 5, /* 60 GHz */
+};
+
+#define MB_CONNECTION_CAPABILITY_AP ((u8) (BIT(0)))
+#define MB_CONNECTION_CAPABILITY_PCP ((u8) (BIT(1)))
+#define MB_CONNECTION_CAPABILITY_DLS ((u8) (BIT(2)))
+#define MB_CONNECTION_CAPABILITY_TDLS ((u8) (BIT(3)))
+#define MB_CONNECTION_CAPABILITY_IBSS ((u8) (BIT(4)))
+
+/* IEEE Std 802.11ad-2014 - FST Action field */
+enum fst_action {
+	FST_ACTION_SETUP_REQUEST = 0,
+	FST_ACTION_SETUP_RESPONSE = 1,
+	FST_ACTION_TEAR_DOWN = 2,
+	FST_ACTION_ACK_REQUEST = 3,
+	FST_ACTION_ACK_RESPONSE = 4,
+	FST_ACTION_ON_CHANNEL_TUNNEL = 5,
+};
+
 #endif /* IEEE802_11_DEFS_H */
diff --git a/src/common/privsep_commands.h b/src/common/privsep_commands.h
index c6a472d..8dff303 100644
--- a/src/common/privsep_commands.h
+++ b/src/common/privsep_commands.h
@@ -26,6 +26,25 @@
 	PRIVSEP_CMD_L2_NOTIFY_AUTH_START,
 	PRIVSEP_CMD_L2_SEND,
 	PRIVSEP_CMD_SET_COUNTRY,
+	PRIVSEP_CMD_AUTHENTICATE,
+};
+
+struct privsep_cmd_authenticate
+{
+	int freq;
+	u8 bssid[ETH_ALEN];
+	u8 ssid[SSID_MAX_LEN];
+	size_t ssid_len;
+	int auth_alg;
+	size_t ie_len;
+	u8 wep_key[4][16];
+	size_t wep_key_len[4];
+	int wep_tx_keyidx;
+	int local_state_change;
+	int p2p;
+	size_t sae_data_len;
+	/* followed by ie_len bytes of ie */
+	/* followed by sae_data_len bytes of sae_data */
 };
 
 struct privsep_cmd_associate
@@ -68,6 +87,18 @@
 	PRIVSEP_EVENT_STKSTART,
 	PRIVSEP_EVENT_FT_RESPONSE,
 	PRIVSEP_EVENT_RX_EAPOL,
+	PRIVSEP_EVENT_SCAN_STARTED,
+	PRIVSEP_EVENT_AUTH,
+};
+
+struct privsep_event_auth {
+	u8 peer[ETH_ALEN];
+	u8 bssid[ETH_ALEN];
+	u16 auth_type;
+	u16 auth_transaction;
+	u16 status_code;
+	size_t ies_len;
+	/* followed by ies_len bytes of ies */
 };
 
 #endif /* PRIVSEP_COMMANDS_H */
diff --git a/src/common/qca-vendor.h b/src/common/qca-vendor.h
index 2a6e242..a3ee91d 100644
--- a/src/common/qca-vendor.h
+++ b/src/common/qca-vendor.h
@@ -156,6 +156,12 @@
 	QCA_NL80211_VENDOR_SUBCMD_GET_PREFERRED_FREQ_LIST = 103,
 	QCA_NL80211_VENDOR_SUBCMD_SET_PROBABLE_OPER_CHANNEL = 104,
 	QCA_NL80211_VENDOR_SUBCMD_SETBAND = 105,
+	QCA_NL80211_VENDOR_SUBCMD_TRIGGER_SCAN = 106,
+	QCA_NL80211_VENDOR_SUBCMD_SCAN_DONE = 107,
+	QCA_NL80211_VENDOR_SUBCMD_OTA_TEST = 108,
+	QCA_NL80211_VENDOR_SUBCMD_SET_TXPOWER_SCALE = 109,
+	/* 110..114 - reserved for QCA */
+	QCA_NL80211_VENDOR_SUBCMD_SET_TXPOWER_DECR_DB = 115,
 };
 
 
@@ -205,6 +211,7 @@
 	QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_KEY_REPLAY_CTR,
 	QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_PTK_KCK,
 	QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_PTK_KEK,
+	QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_SUBNET_STATUS,
 	/* keep last */
 	QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_AFTER_LAST,
 	QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_MAX =
@@ -223,6 +230,7 @@
 	QCA_WLAN_VENDOR_ATTR_ACS_CH_LIST,
 	QCA_WLAN_VENDOR_ATTR_ACS_VHT_SEG0_CENTER_CHANNEL,
 	QCA_WLAN_VENDOR_ATTR_ACS_VHT_SEG1_CENTER_CHANNEL,
+	QCA_WLAN_VENDOR_ATTR_ACS_FREQ_LIST,
 	/* keep last */
 	QCA_WLAN_VENDOR_ATTR_ACS_AFTER_LAST,
 	QCA_WLAN_VENDOR_ATTR_ACS_MAX =
@@ -246,11 +254,14 @@
  *	after roaming, rather than having the user space wpa_supplicant do it.
  * @QCA_WLAN_VENDOR_FEATURE_SUPPORT_HW_MODE_ANY: Device supports automatic
  *	band selection based on channel selection results.
+ * @QCA_WLAN_VENDOR_FEATURE_OFFCHANNEL_SIMULTANEOUS: Device supports
+ * 	simultaneous off-channel operations.
  * @NUM_QCA_WLAN_VENDOR_FEATURES: Number of assigned feature bits
  */
 enum qca_wlan_vendor_features {
 	QCA_WLAN_VENDOR_FEATURE_KEY_MGMT_OFFLOAD	= 0,
 	QCA_WLAN_VENDOR_FEATURE_SUPPORT_HW_MODE_ANY     = 1,
+	QCA_WLAN_VENDOR_FEATURE_OFFCHANNEL_SIMULTANEOUS = 2,
 	NUM_QCA_WLAN_VENDOR_FEATURES /* keep last */
 };
 
@@ -323,4 +334,132 @@
 	QCA_SETBAND_2G,
 };
 
+/* IEEE 802.11 Vendor Specific elements */
+
+/**
+ * enum qca_vendor_element_id - QCA Vendor Specific element types
+ *
+ * These values are used to identify QCA Vendor Specific elements. The
+ * payload of the element starts with the three octet OUI (OUI_QCA) and
+ * is followed by a single octet type which is defined by this enum.
+ *
+ * @QCA_VENDOR_ELEM_P2P_PREF_CHAN_LIST: P2P preferred channel list.
+ *	This element can be used to specify preference order for supported
+ *	channels. The channels in this list are in preference order (the first
+ *	one has the highest preference) and are described as a pair of
+ *	(global) Operating Class and Channel Number (each one octet) fields.
+ *
+ *	This extends the standard P2P functionality by providing option to have
+ *	more than one preferred operating channel. When this element is present,
+ *	it replaces the preference indicated in the Operating Channel attribute.
+ *	For supporting other implementations, the Operating Channel attribute is
+ *	expected to be used with the highest preference channel. Similarly, all
+ *	the channels included in this Preferred channel list element are
+ *	expected to be included in the Channel List attribute.
+ *
+ *	This vendor element may be included in GO Negotiation Request, P2P
+ *	Invitation Request, and Provision Discovery Request frames.
+ */
+enum qca_vendor_element_id {
+	QCA_VENDOR_ELEM_P2P_PREF_CHAN_LIST = 0,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_scan - Specifies vendor scan attributes
+ *
+ * @QCA_WLAN_VENDOR_ATTR_SCAN_IE: IEs that should be included as part of scan
+ * @QCA_WLAN_VENDOR_ATTR_SCAN_FREQUENCIES: Nested unsigned 32-bit attributes
+ *	with frequencies to be scanned (in MHz)
+ * @QCA_WLAN_VENDOR_ATTR_SCAN_SSIDS: Nested attribute with SSIDs to be scanned
+ * @QCA_WLAN_VENDOR_ATTR_SCAN_SUPP_RATES: Nested array attribute of supported
+ *	rates to be included
+ * @QCA_WLAN_VENDOR_ATTR_SCAN_TX_NO_CCK_RATE: flag used to send probe requests
+ * 	at non CCK rate in 2GHz band
+ * @QCA_WLAN_VENDOR_ATTR_SCAN_FLAGS: Unsigned 32-bit scan flags
+ * @QCA_WLAN_VENDOR_ATTR_SCAN_COOKIE: Unsigned 64-bit cookie provided by the
+ * 	driver for the specific scan request
+ * @QCA_WLAN_VENDOR_ATTR_SCAN_STATUS: Unsigned 8-bit status of the scan
+ * 	request decoded as in enum scan_status
+ * @QCA_WLAN_VENDOR_ATTR_SCAN_MAC: 6-byte MAC address to use when randomisation
+ * 	scan flag is set
+ * @QCA_WLAN_VENDOR_ATTR_SCAN_MAC_MASK: 6-byte MAC address mask to be used with
+ * 	randomisation
+ */
+enum qca_wlan_vendor_attr_scan {
+	QCA_WLAN_VENDOR_ATTR_SCAN_INVALID_PARAM = 0,
+	QCA_WLAN_VENDOR_ATTR_SCAN_IE,
+	QCA_WLAN_VENDOR_ATTR_SCAN_FREQUENCIES,
+	QCA_WLAN_VENDOR_ATTR_SCAN_SSIDS,
+	QCA_WLAN_VENDOR_ATTR_SCAN_SUPP_RATES,
+	QCA_WLAN_VENDOR_ATTR_SCAN_TX_NO_CCK_RATE,
+	QCA_WLAN_VENDOR_ATTR_SCAN_FLAGS,
+	QCA_WLAN_VENDOR_ATTR_SCAN_COOKIE,
+	QCA_WLAN_VENDOR_ATTR_SCAN_STATUS,
+	QCA_WLAN_VENDOR_ATTR_SCAN_MAC,
+	QCA_WLAN_VENDOR_ATTR_SCAN_MAC_MASK,
+	QCA_WLAN_VENDOR_ATTR_SCAN_AFTER_LAST,
+	QCA_WLAN_VENDOR_ATTR_SCAN_MAX =
+	QCA_WLAN_VENDOR_ATTR_SCAN_AFTER_LAST - 1
+};
+
+/**
+ * enum scan_status - Specifies the valid values the vendor scan attribute
+ * 	QCA_WLAN_VENDOR_ATTR_SCAN_STATUS can take
+ *
+ * @VENDOR_SCAN_STATUS_NEW_RESULTS: implies the vendor scan is successful with
+ * 	new scan results
+ * @VENDOR_SCAN_STATUS_ABORTED: implies the vendor scan was aborted in-between
+ */
+enum scan_status {
+	VENDOR_SCAN_STATUS_NEW_RESULTS,
+	VENDOR_SCAN_STATUS_ABORTED,
+	VENDOR_SCAN_STATUS_MAX,
+};
+
+/**
+ * enum qca_vendor_attr_ota_test - Specifies the values for vendor
+ *                       command QCA_NL80211_VENDOR_SUBCMD_OTA_TEST
+ * @QCA_WLAN_VENDOR_ATTR_OTA_TEST_ENABLE: enable ota test
+ */
+enum qca_vendor_attr_ota_test {
+	QCA_WLAN_VENDOR_ATTR_OTA_TEST_INVALID,
+	/* 8-bit unsigned value to indicate if OTA test is enabled */
+	QCA_WLAN_VENDOR_ATTR_OTA_TEST_ENABLE,
+	/* keep last */
+	QCA_WLAN_VENDOR_ATTR_OTA_TEST_AFTER_LAST,
+	QCA_WLAN_VENDOR_ATTR_OTA_TEST_MAX =
+	QCA_WLAN_VENDOR_ATTR_OTA_TEST_AFTER_LAST - 1
+};
+
+/**
+ * enum qca_vendor_attr_txpower_scale - vendor sub commands index
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TXPOWER_SCALE: scaling value
+ */
+enum qca_vendor_attr_txpower_scale {
+	QCA_WLAN_VENDOR_ATTR_TXPOWER_SCALE_INVALID,
+	/* 8-bit unsigned value to indicate the scaling of tx power */
+	QCA_WLAN_VENDOR_ATTR_TXPOWER_SCALE,
+	/* keep last */
+	QCA_WLAN_VENDOR_ATTR_TXPOWER_SCALE_AFTER_LAST,
+	QCA_WLAN_VENDOR_ATTR_TXPOWER_SCALE_MAX =
+	QCA_WLAN_VENDOR_ATTR_TXPOWER_SCALE_AFTER_LAST - 1
+};
+
+/**
+ * enum qca_vendor_attr_txpower_decr_db - Attributes for TX power decrease
+ *
+ * These attributes are used with QCA_NL80211_VENDOR_SUBCMD_SET_TXPOWER_DECR_DB.
+ */
+enum qca_vendor_attr_txpower_decr_db {
+	QCA_WLAN_VENDOR_ATTR_TXPOWER_DECR_DB_INVALID,
+	/* 8-bit unsigned value to indicate the reduction of TX power in dB for
+	 * a virtual interface. */
+	QCA_WLAN_VENDOR_ATTR_TXPOWER_DECR_DB,
+	/* keep last */
+	QCA_WLAN_VENDOR_ATTR_TXPOWER_DECR_DB_AFTER_LAST,
+	QCA_WLAN_VENDOR_ATTR_TXPOWER_DECR_DB_MAX =
+	QCA_WLAN_VENDOR_ATTR_TXPOWER_DECR_DB_AFTER_LAST - 1
+};
+
 #endif /* QCA_VENDOR_H */
diff --git a/src/common/sae.c b/src/common/sae.c
index 503fa1d..b962ea2 100644
--- a/src/common/sae.c
+++ b/src/common/sae.c
@@ -923,7 +923,7 @@
 				   const u8 *end, const u8 **token,
 				   size_t *token_len)
 {
-	if (*pos + (sae->tmp->ec ? 3 : 2) * sae->tmp->prime_len < end) {
+	if ((sae->tmp->ec ? 3 : 2) * sae->tmp->prime_len < end - *pos) {
 		size_t tlen = end - (*pos + (sae->tmp->ec ? 3 : 2) *
 				     sae->tmp->prime_len);
 		wpa_hexdump(MSG_DEBUG, "SAE: Anti-Clogging Token", *pos, tlen);
@@ -946,7 +946,7 @@
 {
 	struct crypto_bignum *peer_scalar;
 
-	if (*pos + sae->tmp->prime_len > end) {
+	if (sae->tmp->prime_len > end - *pos) {
 		wpa_printf(MSG_DEBUG, "SAE: Not enough data for scalar");
 		return WLAN_STATUS_UNSPECIFIED_FAILURE;
 	}
@@ -994,7 +994,7 @@
 {
 	u8 prime[SAE_MAX_ECC_PRIME_LEN];
 
-	if (pos + 2 * sae->tmp->prime_len > end) {
+	if (2 * sae->tmp->prime_len > end - pos) {
 		wpa_printf(MSG_DEBUG, "SAE: Not enough data for "
 			   "commit-element");
 		return WLAN_STATUS_UNSPECIFIED_FAILURE;
@@ -1040,7 +1040,7 @@
 	struct crypto_bignum *res, *one;
 	const u8 one_bin[1] = { 0x01 };
 
-	if (pos + sae->tmp->prime_len > end) {
+	if (sae->tmp->prime_len > end - pos) {
 		wpa_printf(MSG_DEBUG, "SAE: Not enough data for "
 			   "commit-element");
 		return WLAN_STATUS_UNSPECIFIED_FAILURE;
@@ -1098,7 +1098,7 @@
 	u16 res;
 
 	/* Check Finite Cyclic Group */
-	if (pos + 2 > end)
+	if (end - pos < 2)
 		return WLAN_STATUS_UNSPECIFIED_FAILURE;
 	res = sae_group_allowed(sae, allowed_groups, WPA_GET_LE16(pos));
 	if (res != WLAN_STATUS_SUCCESS)
diff --git a/src/common/version.h b/src/common/version.h
index 5ddf617..ae5c9d4 100644
--- a/src/common/version.h
+++ b/src/common/version.h
@@ -5,6 +5,10 @@
 #define VERSION_STR_POSTFIX ""
 #endif /* VERSION_STR_POSTFIX */
 
-#define VERSION_STR "2.5-devel" VERSION_STR_POSTFIX
+#ifndef GIT_VERSION_STR_POSTFIX
+#define GIT_VERSION_STR_POSTFIX ""
+#endif /* GIT_VERSION_STR_POSTFIX */
+
+#define VERSION_STR "2.6-devel" VERSION_STR_POSTFIX GIT_VERSION_STR_POSTFIX
 
 #endif /* VERSION_H */
diff --git a/src/common/wpa_common.c b/src/common/wpa_common.c
index e485b5b..4091bed 100644
--- a/src/common/wpa_common.c
+++ b/src/common/wpa_common.c
@@ -170,6 +170,12 @@
 	ptk->tk_len = wpa_cipher_key_len(cipher);
 	ptk_len = ptk->kck_len + ptk->kek_len + ptk->tk_len;
 
+#ifdef CONFIG_SUITEB192
+	if (wpa_key_mgmt_sha384(akmp))
+		sha384_prf(pmk, pmk_len, label, data, sizeof(data),
+			   tmp, ptk_len);
+	else
+#endif /* CONFIG_SUITEB192 */
 #ifdef CONFIG_IEEE80211W
 	if (wpa_key_mgmt_sha256(akmp))
 		sha256_prf(pmk, pmk_len, label, data, sizeof(data),
@@ -286,38 +292,47 @@
 	pos = ie + sizeof(struct rsn_ftie);
 	end = ie + ie_len;
 
-	while (pos + 2 <= end && pos + 2 + pos[1] <= end) {
-		switch (pos[0]) {
+	while (end - pos >= 2) {
+		u8 id, len;
+
+		id = *pos++;
+		len = *pos++;
+		if (len > end - pos)
+			break;
+
+		switch (id) {
 		case FTIE_SUBELEM_R1KH_ID:
-			if (pos[1] != FT_R1KH_ID_LEN) {
-				wpa_printf(MSG_DEBUG, "FT: Invalid R1KH-ID "
-					   "length in FTIE: %d", pos[1]);
+			if (len != FT_R1KH_ID_LEN) {
+				wpa_printf(MSG_DEBUG,
+					   "FT: Invalid R1KH-ID length in FTIE: %d",
+					   len);
 				return -1;
 			}
-			parse->r1kh_id = pos + 2;
+			parse->r1kh_id = pos;
 			break;
 		case FTIE_SUBELEM_GTK:
-			parse->gtk = pos + 2;
-			parse->gtk_len = pos[1];
+			parse->gtk = pos;
+			parse->gtk_len = len;
 			break;
 		case FTIE_SUBELEM_R0KH_ID:
-			if (pos[1] < 1 || pos[1] > FT_R0KH_ID_MAX_LEN) {
-				wpa_printf(MSG_DEBUG, "FT: Invalid R0KH-ID "
-					   "length in FTIE: %d", pos[1]);
+			if (len < 1 || len > FT_R0KH_ID_MAX_LEN) {
+				wpa_printf(MSG_DEBUG,
+					   "FT: Invalid R0KH-ID length in FTIE: %d",
+					   len);
 				return -1;
 			}
-			parse->r0kh_id = pos + 2;
-			parse->r0kh_id_len = pos[1];
+			parse->r0kh_id = pos;
+			parse->r0kh_id_len = len;
 			break;
 #ifdef CONFIG_IEEE80211W
 		case FTIE_SUBELEM_IGTK:
-			parse->igtk = pos + 2;
-			parse->igtk_len = pos[1];
+			parse->igtk = pos;
+			parse->igtk_len = len;
 			break;
 #endif /* CONFIG_IEEE80211W */
 		}
 
-		pos += 2 + pos[1];
+		pos += len;
 	}
 
 	return 0;
@@ -339,11 +354,18 @@
 
 	pos = ies;
 	end = ies + ies_len;
-	while (pos + 2 <= end && pos + 2 + pos[1] <= end) {
-		switch (pos[0]) {
+	while (end - pos >= 2) {
+		u8 id, len;
+
+		id = *pos++;
+		len = *pos++;
+		if (len > end - pos)
+			break;
+
+		switch (id) {
 		case WLAN_EID_RSN:
-			parse->rsn = pos + 2;
-			parse->rsn_len = pos[1];
+			parse->rsn = pos;
+			parse->rsn_len = len;
 			ret = wpa_parse_wpa_ie_rsn(parse->rsn - 2,
 						   parse->rsn_len + 2,
 						   &data);
@@ -356,32 +378,32 @@
 				parse->rsn_pmkid = data.pmkid;
 			break;
 		case WLAN_EID_MOBILITY_DOMAIN:
-			if (pos[1] < sizeof(struct rsn_mdie))
+			if (len < sizeof(struct rsn_mdie))
 				return -1;
-			parse->mdie = pos + 2;
-			parse->mdie_len = pos[1];
+			parse->mdie = pos;
+			parse->mdie_len = len;
 			break;
 		case WLAN_EID_FAST_BSS_TRANSITION:
-			if (pos[1] < sizeof(*ftie))
+			if (len < sizeof(*ftie))
 				return -1;
-			ftie = (const struct rsn_ftie *) (pos + 2);
+			ftie = (const struct rsn_ftie *) pos;
 			prot_ie_count = ftie->mic_control[1];
-			if (wpa_ft_parse_ftie(pos + 2, pos[1], parse) < 0)
+			if (wpa_ft_parse_ftie(pos, len, parse) < 0)
 				return -1;
 			break;
 		case WLAN_EID_TIMEOUT_INTERVAL:
-			if (pos[1] != 5)
+			if (len != 5)
 				break;
-			parse->tie = pos + 2;
-			parse->tie_len = pos[1];
+			parse->tie = pos;
+			parse->tie_len = len;
 			break;
 		case WLAN_EID_RIC_DATA:
 			if (parse->ric == NULL)
-				parse->ric = pos;
+				parse->ric = pos - 2;
 			break;
 		}
 
-		pos += 2 + pos[1];
+		pos += len;
 	}
 
 	if (prot_ie_count == 0)
@@ -410,13 +432,15 @@
 	}
 
 	/* Determine the end of the RIC IE(s) */
-	pos = parse->ric;
-	while (pos && pos + 2 <= end && pos + 2 + pos[1] <= end &&
-	       prot_ie_count) {
-		prot_ie_count--;
-		pos += 2 + pos[1];
+	if (parse->ric) {
+		pos = parse->ric;
+		while (end - pos >= 2 && 2 + pos[1] <= end - pos &&
+		       prot_ie_count) {
+			prot_ie_count--;
+			pos += 2 + pos[1];
+		}
+		parse->ric_len = pos - parse->ric;
 	}
-	parse->ric_len = pos - parse->ric;
 	if (prot_ie_count) {
 		wpa_printf(MSG_DEBUG, "FT: %d protected IEs missing from "
 			   "frame", (int) prot_ie_count);
@@ -492,7 +516,7 @@
 }
 
 
-static int wpa_cipher_valid_group(int cipher)
+int wpa_cipher_valid_group(int cipher)
 {
 	return wpa_cipher_valid_pairwise(cipher) ||
 		cipher == WPA_CIPHER_GTK_NOT_USED;
@@ -1292,6 +1316,9 @@
 		os_memmove(rpos + 2, rpos, end - rpos);
 		*rpos++ = 0;
 		*rpos++ = 0;
+		added += 2;
+		start[1] += 2;
+		rend = rpos;
 	} else {
 		/* Skip RSN Capabilities */
 		rpos += 2;
@@ -1304,7 +1331,7 @@
 
 	if (rpos == rend) {
 		/* No PMKID-Count field included; add it */
-		os_memmove(rpos + 2 + PMKID_LEN, rpos, end - rpos);
+		os_memmove(rpos + 2 + PMKID_LEN, rpos, end + added - rpos);
 		WPA_PUT_LE16(rpos, 1);
 		rpos += 2;
 		os_memcpy(rpos, pmkid, PMKID_LEN);
@@ -1319,7 +1346,7 @@
 		}
 		WPA_PUT_LE16(rpos, 1);
 		rpos += 2;
-		os_memmove(rpos + PMKID_LEN, rpos, end - rpos);
+		os_memmove(rpos + PMKID_LEN, rpos, end + added - rpos);
 		os_memcpy(rpos, pmkid, PMKID_LEN);
 		added += PMKID_LEN;
 		start[1] += PMKID_LEN;
diff --git a/src/common/wpa_common.h b/src/common/wpa_common.h
index d7a590f..ee71bfc 100644
--- a/src/common/wpa_common.h
+++ b/src/common/wpa_common.h
@@ -12,6 +12,8 @@
 /* IEEE 802.11i */
 #define PMKID_LEN 16
 #define PMK_LEN 32
+#define PMK_LEN_SUITE_B_192 48
+#define PMK_LEN_MAX 48
 #define WPA_REPLAY_COUNTER_LEN 8
 #define WPA_NONCE_LEN 32
 #define WPA_KEY_RSC_LEN 8
@@ -435,6 +437,7 @@
 int wpa_cipher_key_len(int cipher);
 int wpa_cipher_rsc_len(int cipher);
 int wpa_cipher_to_alg(int cipher);
+int wpa_cipher_valid_group(int cipher);
 int wpa_cipher_valid_pairwise(int cipher);
 int wpa_cipher_valid_mgmt_group(int cipher);
 u32 wpa_cipher_to_suite(int proto, int cipher);
diff --git a/src/common/wpa_ctrl.c b/src/common/wpa_ctrl.c
index 82d4655..5733aa6 100644
--- a/src/common/wpa_ctrl.c
+++ b/src/common/wpa_ctrl.c
@@ -85,6 +85,13 @@
 
 struct wpa_ctrl * wpa_ctrl_open(const char *ctrl_path)
 {
+	return wpa_ctrl_open2(ctrl_path, NULL);
+}
+
+
+struct wpa_ctrl * wpa_ctrl_open2(const char *ctrl_path,
+				 const char *cli_path)
+{
 	struct wpa_ctrl *ctrl;
 	static int counter = 0;
 	int ret;
@@ -108,10 +115,18 @@
 	ctrl->local.sun_family = AF_UNIX;
 	counter++;
 try_again:
-	ret = os_snprintf(ctrl->local.sun_path, sizeof(ctrl->local.sun_path),
-			  CONFIG_CTRL_IFACE_CLIENT_DIR "/"
-			  CONFIG_CTRL_IFACE_CLIENT_PREFIX "%d-%d",
-			  (int) getpid(), counter);
+	if (cli_path && cli_path[0] == '/') {
+		ret = os_snprintf(ctrl->local.sun_path,
+				  sizeof(ctrl->local.sun_path),
+				  "%s/" CONFIG_CTRL_IFACE_CLIENT_PREFIX "%d-%d",
+				  cli_path, (int) getpid(), counter);
+	} else {
+		ret = os_snprintf(ctrl->local.sun_path,
+				  sizeof(ctrl->local.sun_path),
+				  CONFIG_CTRL_IFACE_CLIENT_DIR "/"
+				  CONFIG_CTRL_IFACE_CLIENT_PREFIX "%d-%d",
+				  (int) getpid(), counter);
+	}
 	if (os_snprintf_error(sizeof(ctrl->local.sun_path), ret)) {
 		close(ctrl->s);
 		os_free(ctrl);
@@ -137,6 +152,8 @@
 
 #ifdef ANDROID
 	chmod(ctrl->local.sun_path, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
+	/* Set group even if we do not have privileges to change owner */
+	chown(ctrl->local.sun_path, -1, AID_WIFI);
 	chown(ctrl->local.sun_path, AID_SYSTEM, AID_WIFI);
 
 	if (os_strncmp(ctrl_path, "@android:", 9) == 0) {
diff --git a/src/common/wpa_ctrl.h b/src/common/wpa_ctrl.h
index e700523..3e0a7ec 100644
--- a/src/common/wpa_ctrl.h
+++ b/src/common/wpa_ctrl.h
@@ -28,6 +28,8 @@
 #define WPA_EVENT_DISCONNECTED "CTRL-EVENT-DISCONNECTED "
 /** Association rejected during connection attempt */
 #define WPA_EVENT_ASSOC_REJECT "CTRL-EVENT-ASSOC-REJECT "
+/** Authentication rejected during connection attempt */
+#define WPA_EVENT_AUTH_REJECT "CTRL-EVENT-AUTH-REJECT "
 /** wpa_supplicant is exiting */
 #define WPA_EVENT_TERMINATING "CTRL-EVENT-TERMINATING "
 /** Password change was completed successfully */
@@ -75,6 +77,19 @@
 /** Regulatory domain channel */
 #define WPA_EVENT_REGDOM_CHANGE "CTRL-EVENT-REGDOM-CHANGE "
 
+/** IP subnet status change notification
+ *
+ * When using an offloaded roaming mechanism where driver/firmware takes care
+ * of roaming and IP subnet validation checks post-roaming, this event can
+ * indicate whether IP subnet has changed.
+ *
+ * The event has a status=<0/1/2> parameter where
+ * 0 = unknown
+ * 1 = IP subnet unchanged (can continue to use the old IP address)
+ * 2 = IP subnet changed (need to get a new IP address)
+ */
+#define WPA_EVENT_SUBNET_STATUS_UPDATE "CTRL-EVENT-SUBNET-STATUS-UPDATE "
+
 /** RSN IBSS 4-way handshakes completed with specified peer */
 #define IBSS_RSN_COMPLETED "IBSS-RSN-COMPLETED "
 
@@ -279,6 +294,7 @@
 #define WPA_BSS_MASK_MESH_SCAN		BIT(18)
 #define WPA_BSS_MASK_SNR		BIT(19)
 #define WPA_BSS_MASK_EST_THROUGHPUT	BIT(20)
+#define WPA_BSS_MASK_FST		BIT(21)
 
 
 /* VENDOR_ELEM_* frame id values */
@@ -315,6 +331,20 @@
  */
 struct wpa_ctrl * wpa_ctrl_open(const char *ctrl_path);
 
+/**
+ * wpa_ctrl_open2 - Open a control interface to wpa_supplicant/hostapd
+ * @ctrl_path: Path for UNIX domain sockets; ignored if UDP sockets are used.
+ * @cli_path: Path for client UNIX domain sockets; ignored if UDP socket
+ *            is used.
+ * Returns: Pointer to abstract control interface data or %NULL on failure
+ *
+ * This function is used to open a control interface to wpa_supplicant/hostapd
+ * when the socket path for client need to be specified explicitly. Default
+ * ctrl_path is usually /var/run/wpa_supplicant or /var/run/hostapd and client
+ * socket path is /tmp.
+ */
+struct wpa_ctrl * wpa_ctrl_open2(const char *ctrl_path, const char *cli_path);
+
 
 /**
  * wpa_ctrl_close - Close a control interface to wpa_supplicant/hostapd
diff --git a/src/common/wpa_helpers.c b/src/common/wpa_helpers.c
index 28913b9..f159421 100644
--- a/src/common/wpa_helpers.c
+++ b/src/common/wpa_helpers.c
@@ -172,7 +172,8 @@
 	if (ctrl == NULL)
 		return -1;
 	len = sizeof(buf);
-	if (wpa_ctrl_request(ctrl, "STATUS", 6, buf, &len, NULL) < 0) {
+	if (wpa_ctrl_request(ctrl, "STATUS-NO_EVENTS", 16, buf, &len,
+			     NULL) < 0) {
 		wpa_ctrl_close(ctrl);
 		return -1;
 	}
diff --git a/src/crypto/Makefile b/src/crypto/Makefile
index 3e90350..d181e72 100644
--- a/src/crypto/Makefile
+++ b/src/crypto/Makefile
@@ -47,7 +47,9 @@
 	sha256.o \
 	sha256-prf.o \
 	sha256-tlsprf.o \
-	sha256-internal.o
+	sha256-internal.o \
+	sha384-internal.o \
+	sha512-internal.o
 
 LIB_OBJS += crypto_internal.o
 LIB_OBJS += crypto_internal-cipher.o
diff --git a/src/crypto/aes-cbc.c b/src/crypto/aes-cbc.c
index 2833cfc..0835f2c 100644
--- a/src/crypto/aes-cbc.c
+++ b/src/crypto/aes-cbc.c
@@ -28,6 +28,9 @@
 	u8 *pos = data;
 	int i, j, blocks;
 
+	if (TEST_FAIL())
+		return -1;
+
 	ctx = aes_encrypt_init(key, 16);
 	if (ctx == NULL)
 		return -1;
@@ -61,6 +64,9 @@
 	u8 *pos = data;
 	int i, j, blocks;
 
+	if (TEST_FAIL())
+		return -1;
+
 	ctx = aes_decrypt_init(key, 16);
 	if (ctx == NULL)
 		return -1;
diff --git a/src/crypto/aes-omac1.c b/src/crypto/aes-omac1.c
index 375db57..8642516 100644
--- a/src/crypto/aes-omac1.c
+++ b/src/crypto/aes-omac1.c
@@ -48,6 +48,9 @@
 	const u8 *pos, *end;
 	size_t i, e, left, total_len;
 
+	if (TEST_FAIL())
+		return -1;
+
 	ctx = aes_encrypt_init(key, key_len);
 	if (ctx == NULL)
 		return -1;
diff --git a/src/crypto/crypto.h b/src/crypto/crypto.h
index 534c4bd..bdc3ba6 100644
--- a/src/crypto/crypto.h
+++ b/src/crypto/crypto.h
@@ -80,6 +80,28 @@
 		  u8 *mac);
 
 /**
+ * sha384_vector - SHA384 hash for data vector
+ * @num_elem: Number of elements in the data vector
+ * @addr: Pointers to the data areas
+ * @len: Lengths of the data blocks
+ * @mac: Buffer for the hash
+ * Returns: 0 on success, -1 on failure
+ */
+int sha384_vector(size_t num_elem, const u8 *addr[], const size_t *len,
+		  u8 *mac);
+
+/**
+ * sha512_vector - SHA512 hash for data vector
+ * @num_elem: Number of elements in the data vector
+ * @addr: Pointers to the data areas
+ * @len: Lengths of the data blocks
+ * @mac: Buffer for the hash
+ * Returns: 0 on success, -1 on failure
+ */
+int sha512_vector(size_t num_elem, const u8 *addr[], const size_t *len,
+		  u8 *mac);
+
+/**
  * des_encrypt - Encrypt one block with DES
  * @clear: 8 octets (in)
  * @key: 7 octets (in) (no parity bits included)
@@ -135,7 +157,8 @@
 enum crypto_hash_alg {
 	CRYPTO_HASH_ALG_MD5, CRYPTO_HASH_ALG_SHA1,
 	CRYPTO_HASH_ALG_HMAC_MD5, CRYPTO_HASH_ALG_HMAC_SHA1,
-	CRYPTO_HASH_ALG_SHA256, CRYPTO_HASH_ALG_HMAC_SHA256
+	CRYPTO_HASH_ALG_SHA256, CRYPTO_HASH_ALG_HMAC_SHA256,
+	CRYPTO_HASH_ALG_SHA384, CRYPTO_HASH_ALG_SHA512
 };
 
 struct crypto_hash;
diff --git a/src/crypto/crypto_internal.c b/src/crypto/crypto_internal.c
index f3602da..d391f48 100644
--- a/src/crypto/crypto_internal.c
+++ b/src/crypto/crypto_internal.c
@@ -11,6 +11,8 @@
 #include "common.h"
 #include "crypto.h"
 #include "sha256_i.h"
+#include "sha384_i.h"
+#include "sha512_i.h"
 #include "sha1_i.h"
 #include "md5_i.h"
 
@@ -22,6 +24,12 @@
 #ifdef CONFIG_SHA256
 		struct sha256_state sha256;
 #endif /* CONFIG_SHA256 */
+#ifdef CONFIG_INTERNAL_SHA384
+		struct sha384_state sha384;
+#endif /* CONFIG_INTERNAL_SHA384 */
+#ifdef CONFIG_INTERNAL_SHA512
+		struct sha512_state sha512;
+#endif /* CONFIG_INTERNAL_SHA512 */
 	} u;
 	u8 key[64];
 	size_t key_len;
@@ -54,6 +62,16 @@
 		sha256_init(&ctx->u.sha256);
 		break;
 #endif /* CONFIG_SHA256 */
+#ifdef CONFIG_INTERNAL_SHA384
+	case CRYPTO_HASH_ALG_SHA384:
+		sha384_init(&ctx->u.sha384);
+		break;
+#endif /* CONFIG_INTERNAL_SHA384 */
+#ifdef CONFIG_INTERNAL_SHA512
+	case CRYPTO_HASH_ALG_SHA512:
+		sha512_init(&ctx->u.sha512);
+		break;
+#endif /* CONFIG_INTERNAL_SHA512 */
 	case CRYPTO_HASH_ALG_HMAC_MD5:
 		if (key_len > sizeof(k_pad)) {
 			MD5Init(&ctx->u.md5);
@@ -142,6 +160,16 @@
 		sha256_process(&ctx->u.sha256, data, len);
 		break;
 #endif /* CONFIG_SHA256 */
+#ifdef CONFIG_INTERNAL_SHA384
+	case CRYPTO_HASH_ALG_SHA384:
+		sha384_process(&ctx->u.sha384, data, len);
+		break;
+#endif /* CONFIG_INTERNAL_SHA384 */
+#ifdef CONFIG_INTERNAL_SHA512
+	case CRYPTO_HASH_ALG_SHA512:
+		sha512_process(&ctx->u.sha512, data, len);
+		break;
+#endif /* CONFIG_INTERNAL_SHA512 */
 	default:
 		break;
 	}
@@ -191,6 +219,28 @@
 		sha256_done(&ctx->u.sha256, mac);
 		break;
 #endif /* CONFIG_SHA256 */
+#ifdef CONFIG_INTERNAL_SHA384
+	case CRYPTO_HASH_ALG_SHA384:
+		if (*len < 48) {
+			*len = 48;
+			os_free(ctx);
+			return -1;
+		}
+		*len = 48;
+		sha384_done(&ctx->u.sha384, mac);
+		break;
+#endif /* CONFIG_INTERNAL_SHA384 */
+#ifdef CONFIG_INTERNAL_SHA512
+	case CRYPTO_HASH_ALG_SHA512:
+		if (*len < 64) {
+			*len = 64;
+			os_free(ctx);
+			return -1;
+		}
+		*len = 64;
+		sha512_done(&ctx->u.sha512, mac);
+		break;
+#endif /* CONFIG_INTERNAL_SHA512 */
 	case CRYPTO_HASH_ALG_HMAC_MD5:
 		if (*len < 16) {
 			*len = 16;
diff --git a/src/crypto/crypto_module_tests.c b/src/crypto/crypto_module_tests.c
index 1d613c9..087953b 100644
--- a/src/crypto/crypto_module_tests.c
+++ b/src/crypto/crypto_module_tests.c
@@ -516,6 +516,7 @@
 		0xAE, 0xF3, 0x4B, 0xD8, 0xFB, 0x5A, 0x7B, 0x82,
 		0x9D, 0x3E, 0x86, 0x23, 0x71, 0xD2, 0xCF, 0xE5
 	};
+#ifndef CONFIG_BORINGSSL
 	/* RFC 3394 - Test vector 4.2 */
 	u8 kek42[] = {
 		0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
@@ -531,6 +532,7 @@
 		0xF9, 0x2B, 0x5B, 0x97, 0xC0, 0x50, 0xAE, 0xD2,
 		0x46, 0x8A, 0xB8, 0xA1, 0x7A, 0xD8, 0x4E, 0x5D
 	};
+#endif /* CONFIG_BORINGSSL */
 	/* RFC 3394 - Test vector 4.3 */
 	u8 kek43[] = {
 		0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
@@ -547,6 +549,7 @@
 		0x63, 0xE9, 0x77, 0x79, 0x05, 0x81, 0x8A, 0x2A,
 		0x93, 0xC8, 0x19, 0x1E, 0x7D, 0x6E, 0x8A, 0xE7,
 	};
+#ifndef CONFIG_BORINGSSL
 	/* RFC 3394 - Test vector 4.4 */
 	u8 kek44[] = {
 		0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
@@ -564,6 +567,7 @@
 		0xE1, 0xC6, 0xC7, 0xDD, 0xEE, 0x72, 0x5A, 0x93,
 		0x6B, 0xA8, 0x14, 0x91, 0x5C, 0x67, 0x62, 0xD2
 	};
+#endif /* CONFIG_BORINGSSL */
 	/* RFC 3394 - Test vector 4.5 */
 	u8 kek45[] = {
 		0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
@@ -624,6 +628,7 @@
 		ret++;
 	}
 
+#ifndef CONFIG_BORINGSSL
 	wpa_printf(MSG_INFO, "RFC 3394 - Test vector 4.2");
 	if (aes_wrap(kek42, sizeof(kek42), sizeof(plain42) / 8, plain42,
 		     result)) {
@@ -643,6 +648,7 @@
 		wpa_printf(MSG_ERROR, "AES-UNWRAP-192 failed");
 		ret++;
 	}
+#endif /* CONFIG_BORINGSSL */
 
 	wpa_printf(MSG_INFO, "RFC 3394 - Test vector 4.3");
 	if (aes_wrap(kek43, sizeof(kek43), sizeof(plain43) / 8, plain43,
@@ -664,6 +670,7 @@
 		ret++;
 	}
 
+#ifndef CONFIG_BORINGSSL
 	wpa_printf(MSG_INFO, "RFC 3394 - Test vector 4.4");
 	if (aes_wrap(kek44, sizeof(kek44), sizeof(plain44) / 8, plain44,
 		     result)) {
@@ -683,6 +690,7 @@
 		wpa_printf(MSG_ERROR, "AES-UNWRAP-192 failed");
 		ret++;
 	}
+#endif /* CONFIG_BORINGSSL */
 
 	wpa_printf(MSG_INFO, "RFC 3394 - Test vector 4.5");
 	if (aes_wrap(kek45, sizeof(kek45), sizeof(plain45) / 8, plain45,
@@ -733,6 +741,7 @@
 
 static int test_md5(void)
 {
+#ifndef CONFIG_FIPS
 	struct {
 		char *data;
 		char *hash;
@@ -811,6 +820,10 @@
 		wpa_printf(MSG_INFO, "MD5 test cases passed");
 
 	return errors;
+#else /* CONFIG_FIPS */
+	wpa_printf(MSG_INFO, "MD5 test cases skipped due to CONFIG_FIPS");
+	return 0;
+#endif /* CONFIG_FIPS */
 }
 
 
@@ -842,6 +855,7 @@
 		0x38, 0x4B, 0x7A, 0x85, 0xBE, 0x16, 0x4D, 0x27,
 		0x33, 0xD5, 0x24, 0x79, 0x87, 0xB1, 0xC5, 0xA2
 	};
+#ifndef CONFIG_FIPS
 	const u8 key_block[] = {
 		0x59, 0x59, 0xBE, 0x8E, 0x41, 0x3A, 0x77, 0x74,
 		0x8B, 0xB2, 0xE5, 0xD3, 0x60, 0xAC, 0x4D, 0x35,
@@ -858,6 +872,7 @@
 		0x64, 0xC1, 0xC8, 0x0C, 0x96, 0x44, 0x09, 0x98,
 		0xFF, 0x92, 0xA8, 0xB4, 0xC6, 0x42, 0x28, 0x71
 	};
+#endif /* CONFIG_FIPS */
 	const u8 sks[] = {
 		0xD6, 0x4B, 0x7D, 0x72, 0x17, 0x59, 0x28, 0x05,
 		0xAF, 0xF9, 0xB7, 0xFF, 0x66, 0x6D, 0xA1, 0x96,
@@ -932,6 +947,7 @@
 		errors++;
 	}
 
+#ifndef CONFIG_FIPS
 	wpa_printf(MSG_INFO, "- PRF (TLS, SHA1/MD5) test case / key_block");
 	if (tls_prf_sha1_md5(master_secret, sizeof(master_secret),
 			     "key expansion", seed, sizeof(seed),
@@ -940,6 +956,7 @@
 		wpa_printf(MSG_INFO, "PRF test - FAILED!");
 		errors++;
 	}
+#endif /* CONFIG_FIPS */
 
 	wpa_printf(MSG_INFO, "- T-PRF (SHA1) test case / IMCK");
 	if (sha1_t_prf(sks, sizeof(sks), "Inner Methods Compound Keys",
@@ -1486,6 +1503,7 @@
 	const u8 *addr[2];
 	size_t len[2];
 	int errors = 0;
+	u8 *key;
 
 	for (i = 0; i < ARRAY_SIZE(tests); i++) {
 		wpa_printf(MSG_INFO, "SHA256 test case %d:", i + 1);
@@ -1556,14 +1574,69 @@
 		   hash, sizeof(hash));
 	/* TODO: add proper test case for this */
 
+	key = os_malloc(8161);
+	if (key) {
+#ifdef CONFIG_HMAC_SHA256_KDF
+		int res;
+
+		res = hmac_sha256_kdf((u8 *) "secret", 6, "label",
+				      (u8 *) "seed", 4, key, 8160);
+		if (res) {
+			wpa_printf(MSG_INFO,
+				   "Unexpected hmac_sha256_kdf(outlen=8160) failure");
+			errors++;
+		}
+
+		res = hmac_sha256_kdf((u8 *) "secret", 6, "label",
+				      (u8 *) "seed", 4, key, 8161);
+		if (res == 0) {
+			wpa_printf(MSG_INFO,
+				   "Unexpected hmac_sha256_kdf(outlen=8161) success");
+			errors++;
+		}
+#endif /* CONFIG_HMAC_SHA256_KDF */
+
+		os_free(key);
+	}
+
 	if (!errors)
 		wpa_printf(MSG_INFO, "SHA256 test cases passed");
 	return errors;
 }
 
 
+static int test_fips186_2_prf(void)
+{
+	/* http://csrc.nist.gov/encryption/dss/Examples-1024bit.pdf */
+	u8 xkey[] = {
+		0xbd, 0x02, 0x9b, 0xbe, 0x7f, 0x51, 0x96, 0x0b,
+		0xcf, 0x9e, 0xdb, 0x2b, 0x61, 0xf0, 0x6f, 0x0f,
+		0xeb, 0x5a, 0x38, 0xb6
+	};
+	u8 w[] = {
+		0x20, 0x70, 0xb3, 0x22, 0x3d, 0xba, 0x37, 0x2f,
+		0xde, 0x1c, 0x0f, 0xfc, 0x7b, 0x2e, 0x3b, 0x49,
+		0x8b, 0x26, 0x06, 0x14, 0x3c, 0x6c, 0x18, 0xba,
+		0xcb, 0x0f, 0x6c, 0x55, 0xba, 0xbb, 0x13, 0x78,
+		0x8e, 0x20, 0xd7, 0x37, 0xa3, 0x27, 0x51, 0x16
+	};
+	u8 buf[40];
+
+	wpa_printf(MSG_INFO,
+		   "Testing EAP-SIM PRF (FIPS 186-2 + change notice 1)");
+	if (fips186_2_prf(xkey, sizeof(xkey), buf, sizeof(buf)) < 0 ||
+	    os_memcmp(w, buf, sizeof(w)) != 0) {
+		wpa_printf(MSG_INFO, "fips186_2_prf failed");
+		return 1;
+	}
+
+	return 0;
+}
+
+
 static int test_ms_funcs(void)
 {
+#ifndef CONFIG_FIPS
 	/* Test vector from RFC2759 example */
 	char *username = "User";
 	char *password = "clientPass";
@@ -1656,6 +1729,10 @@
 		wpa_printf(MSG_INFO, "ms_funcs test cases passed");
 
 	return errors;
+#else /* CONFIG_FIPS */
+	wpa_printf(MSG_INFO, "ms_funcs test cases skipped due to CONFIG_FIPS");
+	return 0;
+#endif /* CONFIG_FIPS */
 }
 
 
@@ -1673,6 +1750,7 @@
 	    test_md5() ||
 	    test_sha1() ||
 	    test_sha256() ||
+	    test_fips186_2_prf() ||
 	    test_ms_funcs())
 		ret = -1;
 
diff --git a/src/crypto/crypto_openssl.c b/src/crypto/crypto_openssl.c
index 3703b93..ad2d2d4 100644
--- a/src/crypto/crypto_openssl.c
+++ b/src/crypto/crypto_openssl.c
@@ -69,6 +69,9 @@
 	size_t i;
 	unsigned int mac_len;
 
+	if (TEST_FAIL())
+		return -1;
+
 	EVP_MD_CTX_init(&ctx);
 	if (!EVP_DigestInit_ex(&ctx, type, NULL)) {
 		wpa_printf(MSG_ERROR, "OpenSSL: EVP_DigestInit_ex failed: %s",
@@ -93,10 +96,12 @@
 }
 
 
+#ifndef CONFIG_FIPS
 int md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
 {
 	return openssl_digest_vector(EVP_md4(), num_elem, addr, len, mac);
 }
+#endif /* CONFIG_FIPS */
 
 
 void des_encrypt(const u8 *clear, const u8 *key, u8 *cypher)
@@ -120,6 +125,7 @@
 }
 
 
+#ifndef CONFIG_NO_RC4
 int rc4_skip(const u8 *key, size_t keylen, size_t skip,
 	     u8 *data, size_t data_len)
 {
@@ -155,12 +161,15 @@
 	return res;
 #endif /* OPENSSL_NO_RC4 */
 }
+#endif /* CONFIG_NO_RC4 */
 
 
+#ifndef CONFIG_FIPS
 int md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
 {
 	return openssl_digest_vector(EVP_md5(), num_elem, addr, len, mac);
 }
+#endif /* CONFIG_FIPS */
 
 
 int sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
@@ -297,6 +306,9 @@
 }
 
 
+#ifndef CONFIG_FIPS
+#ifndef CONFIG_OPENSSL_INTERNAL_AES_WRAP
+
 int aes_wrap(const u8 *kek, size_t kek_len, int n, const u8 *plain, u8 *cipher)
 {
 	AES_KEY actx;
@@ -323,6 +335,9 @@
 	return res <= 0 ? -1 : 0;
 }
 
+#endif /* CONFIG_OPENSSL_INTERNAL_AES_WRAP */
+#endif /* CONFIG_FIPS */
+
 
 int aes_128_cbc_encrypt(const u8 *key, const u8 *iv, u8 *data, size_t data_len)
 {
@@ -330,6 +345,9 @@
 	int clen, len;
 	u8 buf[16];
 
+	if (TEST_FAIL())
+		return -1;
+
 	EVP_CIPHER_CTX_init(&ctx);
 	if (EVP_EncryptInit_ex(&ctx, EVP_aes_128_cbc(), NULL, key, iv) != 1)
 		return -1;
@@ -355,6 +373,9 @@
 	int plen, len;
 	u8 buf[16];
 
+	if (TEST_FAIL())
+		return -1;
+
 	EVP_CIPHER_CTX_init(&ctx);
 	if (EVP_DecryptInit_ex(&ctx, EVP_aes_128_cbc(), NULL, key, iv) != 1)
 		return -1;
@@ -430,11 +451,13 @@
 		return NULL;
 
 	switch (alg) {
+#ifndef CONFIG_NO_RC4
 #ifndef OPENSSL_NO_RC4
 	case CRYPTO_CIPHER_ALG_RC4:
 		cipher = EVP_rc4();
 		break;
 #endif /* OPENSSL_NO_RC4 */
+#endif /* CONFIG_NO_RC4 */
 #ifndef OPENSSL_NO_AES
 	case CRYPTO_CIPHER_ALG_AES:
 		switch (key_len) {
@@ -757,6 +780,9 @@
 	size_t i;
 	int res;
 
+	if (TEST_FAIL())
+		return -1;
+
 	HMAC_CTX_init(&ctx);
 #if OPENSSL_VERSION_NUMBER < 0x00909000
 	HMAC_Init_ex(&ctx, key, key_len, type, NULL);
@@ -878,6 +904,9 @@
 	int ret = -1;
 	size_t outlen, i;
 
+	if (TEST_FAIL())
+		return -1;
+
 	ctx = CMAC_CTX_new();
 	if (ctx == NULL)
 		return -1;
diff --git a/src/crypto/dh_group5.c b/src/crypto/dh_group5.c
index ccdbfc8..425c848 100644
--- a/src/crypto/dh_group5.c
+++ b/src/crypto/dh_group5.c
@@ -15,6 +15,7 @@
 
 void * dh5_init(struct wpabuf **priv, struct wpabuf **publ)
 {
+	wpabuf_free(*publ);
 	*publ = dh_init(dh_groups_get(5), priv);
 	if (*publ == NULL)
 		return NULL;
diff --git a/src/crypto/dh_groups.c b/src/crypto/dh_groups.c
index 3aeb2bb..7912361 100644
--- a/src/crypto/dh_groups.c
+++ b/src/crypto/dh_groups.c
@@ -1218,14 +1218,19 @@
 
 	pv_len = dh->prime_len;
 	pv = wpabuf_alloc(pv_len);
-	if (pv == NULL)
+	if (pv == NULL) {
+		wpabuf_clear_free(*priv);
+		*priv = NULL;
 		return NULL;
+	}
 	if (crypto_mod_exp(dh->generator, dh->generator_len,
 			   wpabuf_head(*priv), wpabuf_len(*priv),
 			   dh->prime, dh->prime_len, wpabuf_mhead(pv),
 			   &pv_len) < 0) {
 		wpabuf_clear_free(pv);
 		wpa_printf(MSG_INFO, "DH: crypto_mod_exp failed");
+		wpabuf_clear_free(*priv);
+		*priv = NULL;
 		return NULL;
 	}
 	wpabuf_put(pv, pv_len);
diff --git a/src/crypto/fips_prf_openssl.c b/src/crypto/fips_prf_openssl.c
index fb03efc..9d094b8 100644
--- a/src/crypto/fips_prf_openssl.c
+++ b/src/crypto/fips_prf_openssl.c
@@ -17,6 +17,19 @@
 {
 	SHA_CTX context;
 	os_memset(&context, 0, sizeof(context));
+#if defined(OPENSSL_IS_BORINGSSL) && !defined(ANDROID)
+	context.h[0] = state[0];
+	context.h[1] = state[1];
+	context.h[2] = state[2];
+	context.h[3] = state[3];
+	context.h[4] = state[4];
+	SHA1_Transform(&context, data);
+	state[0] = context.h[0];
+	state[1] = context.h[1];
+	state[2] = context.h[2];
+	state[3] = context.h[3];
+	state[4] = context.h[4];
+#else
 	context.h0 = state[0];
 	context.h1 = state[1];
 	context.h2 = state[2];
@@ -28,6 +41,7 @@
 	state[2] = context.h2;
 	state[3] = context.h3;
 	state[4] = context.h4;
+#endif
 }
 
 
diff --git a/src/crypto/md4-internal.c b/src/crypto/md4-internal.c
index cd5e6ca..d9c737a 100644
--- a/src/crypto/md4-internal.c
+++ b/src/crypto/md4-internal.c
@@ -31,6 +31,9 @@
 	MD4_CTX ctx;
 	size_t i;
 
+	if (TEST_FAIL())
+		return -1;
+
 	MD4Init(&ctx);
 	for (i = 0; i < num_elem; i++)
 		MD4Update(&ctx, addr[i], len[i]);
diff --git a/src/crypto/md5-internal.c b/src/crypto/md5-internal.c
index f0a2a5d..944698a 100644
--- a/src/crypto/md5-internal.c
+++ b/src/crypto/md5-internal.c
@@ -33,6 +33,9 @@
 	MD5_CTX ctx;
 	size_t i;
 
+	if (TEST_FAIL())
+		return -1;
+
 	MD5Init(&ctx);
 	for (i = 0; i < num_elem; i++)
 		MD5Update(&ctx, addr[i], len[i]);
diff --git a/src/crypto/ms_funcs.c b/src/crypto/ms_funcs.c
index 5f57656..d0d6a96 100644
--- a/src/crypto/ms_funcs.c
+++ b/src/crypto/ms_funcs.c
@@ -48,7 +48,7 @@
 				WPA_PUT_LE16(ucs2_buffer + j,
 					     ((c & 0x1F) << 6) | (c2 & 0x3F));
 				j += 2;
-			} else if (i == utf8_string_len ||
+			} else if (i == utf8_string_len - 1 ||
 				   j >= ucs2_buffer_size - 1) {
 				/* incomplete surrogate */
 				return -1;
@@ -174,9 +174,8 @@
 	u8 password_hash[16];
 
 	if (challenge_hash(peer_challenge, auth_challenge, username,
-			   username_len, challenge))
-		return -1;
-	if (nt_password_hash(password, password_len, password_hash))
+			   username_len, challenge) ||
+	    nt_password_hash(password, password_len, password_hash))
 		return -1;
 	challenge_response(challenge, password_hash, response);
 	return 0;
@@ -256,12 +255,9 @@
 	addr2[1] = challenge;
 	addr2[2] = magic2;
 
-	if (hash_nt_password_hash(password_hash, password_hash_hash))
-		return -1;
-	if (sha1_vector(3, addr1, len1, response))
-		return -1;
-
-	if (challenge_hash(peer_challenge, auth_challenge, username,
+	if (hash_nt_password_hash(password_hash, password_hash_hash) ||
+	    sha1_vector(3, addr1, len1, response) ||
+	    challenge_hash(peer_challenge, auth_challenge, username,
 			   username_len, challenge))
 		return -1;
 	return sha1_vector(3, addr2, len2, response);
@@ -416,6 +412,8 @@
 }
 
 
+#ifndef CONFIG_NO_RC4
+
 #define PWBLOCK_LEN 516
 
 /**
@@ -435,10 +433,8 @@
 
 	os_memset(pw_block, 0, PWBLOCK_LEN);
 
-	if (utf8_to_ucs2(password, password_len, pw_block, 512, &ucs2_len) < 0)
-		return -1;
-
-	if (ucs2_len > 256)
+	if (utf8_to_ucs2(password, password_len, pw_block, 512, &ucs2_len) < 0
+	    || ucs2_len > 256)
 		return -1;
 
 	offset = (256 - ucs2_len) * 2;
@@ -483,6 +479,8 @@
 	return 0;
 }
 
+#endif /* CONFIG_NO_RC4 */
+
 
 /**
  * nt_password_hash_encrypted_with_block - NtPasswordHashEncryptedWithBlock() - RFC 2759, Sect 8.13
diff --git a/src/crypto/random.c b/src/crypto/random.c
index bc758aa..3a86a93 100644
--- a/src/crypto/random.c
+++ b/src/crypto/random.c
@@ -181,6 +181,7 @@
 
 #ifdef CONFIG_FIPS
 	/* Mix in additional entropy from the crypto module */
+	bytes = buf;
 	left = len;
 	while (left) {
 		size_t siz, i;
diff --git a/src/crypto/sha1-internal.c b/src/crypto/sha1-internal.c
index 24bc3ff..f6658e6 100644
--- a/src/crypto/sha1-internal.c
+++ b/src/crypto/sha1-internal.c
@@ -33,6 +33,9 @@
 	SHA1_CTX ctx;
 	size_t i;
 
+	if (TEST_FAIL())
+		return -1;
+
 	SHA1Init(&ctx);
 	for (i = 0; i < num_elem; i++)
 		SHA1Update(&ctx, addr[i], len[i]);
diff --git a/src/crypto/sha256-internal.c b/src/crypto/sha256-internal.c
index 35299b0..86a548e 100644
--- a/src/crypto/sha256-internal.c
+++ b/src/crypto/sha256-internal.c
@@ -28,6 +28,9 @@
 	struct sha256_state ctx;
 	size_t i;
 
+	if (TEST_FAIL())
+		return -1;
+
 	sha256_init(&ctx);
 	for (i = 0; i < num_elem; i++)
 		if (sha256_process(&ctx, addr[i], len[i]))
diff --git a/src/crypto/sha384-internal.c b/src/crypto/sha384-internal.c
new file mode 100644
index 0000000..646f729
--- /dev/null
+++ b/src/crypto/sha384-internal.c
@@ -0,0 +1,92 @@
+/*
+ * SHA-384 hash implementation and interface functions
+ * Copyright (c) 2015, Pali Rohár <pali.rohar@gmail.com>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "sha384_i.h"
+#include "crypto.h"
+
+
+/**
+ * sha384_vector - SHA384 hash for data vector
+ * @num_elem: Number of elements in the data vector
+ * @addr: Pointers to the data areas
+ * @len: Lengths of the data blocks
+ * @mac: Buffer for the hash
+ * Returns: 0 on success, -1 of failure
+ */
+int sha384_vector(size_t num_elem, const u8 *addr[], const size_t *len,
+		  u8 *mac)
+{
+	struct sha384_state ctx;
+	size_t i;
+
+	sha384_init(&ctx);
+	for (i = 0; i < num_elem; i++)
+		if (sha384_process(&ctx, addr[i], len[i]))
+			return -1;
+	if (sha384_done(&ctx, mac))
+		return -1;
+	return 0;
+}
+
+
+/* ===== start - public domain SHA384 implementation ===== */
+
+/* This is based on SHA384 implementation in LibTomCrypt that was released into
+ * public domain by Tom St Denis. */
+
+#define CONST64(n) n ## ULL
+
+/**
+   Initialize the hash state
+   @param md   The hash state you wish to initialize
+   @return CRYPT_OK if successful
+*/
+void sha384_init(struct sha384_state *md)
+{
+	md->curlen = 0;
+	md->length = 0;
+	md->state[0] = CONST64(0xcbbb9d5dc1059ed8);
+	md->state[1] = CONST64(0x629a292a367cd507);
+	md->state[2] = CONST64(0x9159015a3070dd17);
+	md->state[3] = CONST64(0x152fecd8f70e5939);
+	md->state[4] = CONST64(0x67332667ffc00b31);
+	md->state[5] = CONST64(0x8eb44a8768581511);
+	md->state[6] = CONST64(0xdb0c2e0d64f98fa7);
+	md->state[7] = CONST64(0x47b5481dbefa4fa4);
+}
+
+int sha384_process(struct sha384_state *md, const unsigned char *in,
+		   unsigned long inlen)
+{
+	return sha512_process(md, in, inlen);
+}
+
+/**
+   Terminate the hash to get the digest
+   @param md  The hash state
+   @param out [out] The destination of the hash (48 bytes)
+   @return CRYPT_OK if successful
+*/
+int sha384_done(struct sha384_state *md, unsigned char *out)
+{
+	unsigned char buf[64];
+
+	if (md->curlen >= sizeof(md->buf))
+		return -1;
+
+	if (sha512_done(md, buf) != 0)
+		return -1;
+
+	os_memcpy(out, buf, 48);
+	return 0;
+}
+
+/* ===== end - public domain SHA384 implementation ===== */
diff --git a/src/crypto/sha384-prf.c b/src/crypto/sha384-prf.c
new file mode 100644
index 0000000..653920b
--- /dev/null
+++ b/src/crypto/sha384-prf.c
@@ -0,0 +1,100 @@
+/*
+ * SHA384-based KDF (IEEE 802.11ac)
+ * Copyright (c) 2003-2015, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "sha384.h"
+#include "crypto.h"
+
+
+/**
+ * sha384_prf - SHA384-based Key derivation function (IEEE 802.11ac, 11.6.1.7.2)
+ * @key: Key for KDF
+ * @key_len: Length of the key in bytes
+ * @label: A unique label for each purpose of the PRF
+ * @data: Extra data to bind into the key
+ * @data_len: Length of the data
+ * @buf: Buffer for the generated pseudo-random key
+ * @buf_len: Number of bytes of key to generate
+ *
+ * This function is used to derive new, cryptographically separate keys from a
+ * given key.
+ */
+void sha384_prf(const u8 *key, size_t key_len, const char *label,
+		const u8 *data, size_t data_len, u8 *buf, size_t buf_len)
+{
+	sha384_prf_bits(key, key_len, label, data, data_len, buf, buf_len * 8);
+}
+
+
+/**
+ * sha384_prf_bits - IEEE Std 802.11ac-2013, 11.6.1.7.2 Key derivation function
+ * @key: Key for KDF
+ * @key_len: Length of the key in bytes
+ * @label: A unique label for each purpose of the PRF
+ * @data: Extra data to bind into the key
+ * @data_len: Length of the data
+ * @buf: Buffer for the generated pseudo-random key
+ * @buf_len: Number of bits of key to generate
+ *
+ * This function is used to derive new, cryptographically separate keys from a
+ * given key. If the requested buf_len is not divisible by eight, the least
+ * significant 1-7 bits of the last octet in the output are not part of the
+ * requested output.
+ */
+void sha384_prf_bits(const u8 *key, size_t key_len, const char *label,
+		     const u8 *data, size_t data_len, u8 *buf,
+		     size_t buf_len_bits)
+{
+	u16 counter = 1;
+	size_t pos, plen;
+	u8 hash[SHA384_MAC_LEN];
+	const u8 *addr[4];
+	size_t len[4];
+	u8 counter_le[2], length_le[2];
+	size_t buf_len = (buf_len_bits + 7) / 8;
+
+	addr[0] = counter_le;
+	len[0] = 2;
+	addr[1] = (u8 *) label;
+	len[1] = os_strlen(label);
+	addr[2] = data;
+	len[2] = data_len;
+	addr[3] = length_le;
+	len[3] = sizeof(length_le);
+
+	WPA_PUT_LE16(length_le, buf_len_bits);
+	pos = 0;
+	while (pos < buf_len) {
+		plen = buf_len - pos;
+		WPA_PUT_LE16(counter_le, counter);
+		if (plen >= SHA384_MAC_LEN) {
+			hmac_sha384_vector(key, key_len, 4, addr, len,
+					   &buf[pos]);
+			pos += SHA384_MAC_LEN;
+		} else {
+			hmac_sha384_vector(key, key_len, 4, addr, len, hash);
+			os_memcpy(&buf[pos], hash, plen);
+			pos += plen;
+			break;
+		}
+		counter++;
+	}
+
+	/*
+	 * Mask out unused bits in the last octet if it does not use all the
+	 * bits.
+	 */
+	if (buf_len_bits % 8) {
+		u8 mask = 0xff << (8 - buf_len_bits % 8);
+		buf[pos - 1] &= mask;
+	}
+
+	os_memset(hash, 0, sizeof(hash));
+}
diff --git a/src/crypto/sha384.h b/src/crypto/sha384.h
index e6a1fe4..3deafa5 100644
--- a/src/crypto/sha384.h
+++ b/src/crypto/sha384.h
@@ -15,5 +15,10 @@
 		       const u8 *addr[], const size_t *len, u8 *mac);
 int hmac_sha384(const u8 *key, size_t key_len, const u8 *data,
 		size_t data_len, u8 *mac);
+void sha384_prf(const u8 *key, size_t key_len, const char *label,
+		const u8 *data, size_t data_len, u8 *buf, size_t buf_len);
+void sha384_prf_bits(const u8 *key, size_t key_len, const char *label,
+		     const u8 *data, size_t data_len, u8 *buf,
+		     size_t buf_len_bits);
 
 #endif /* SHA384_H */
diff --git a/src/crypto/sha384_i.h b/src/crypto/sha384_i.h
new file mode 100644
index 0000000..a00253f
--- /dev/null
+++ b/src/crypto/sha384_i.h
@@ -0,0 +1,23 @@
+/*
+ * SHA-384 internal definitions
+ * Copyright (c) 2015, Pali Rohár <pali.rohar@gmail.com>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef SHA384_I_H
+#define SHA384_I_H
+
+#include "sha512_i.h"
+
+#define SHA384_BLOCK_SIZE SHA512_BLOCK_SIZE
+
+#define sha384_state sha512_state
+
+void sha384_init(struct sha384_state *md);
+int sha384_process(struct sha384_state *md, const unsigned char *in,
+		   unsigned long inlen);
+int sha384_done(struct sha384_state *md, unsigned char *out);
+
+#endif /* SHA384_I_H */
diff --git a/src/crypto/sha512-internal.c b/src/crypto/sha512-internal.c
new file mode 100644
index 0000000..66ef331
--- /dev/null
+++ b/src/crypto/sha512-internal.c
@@ -0,0 +1,264 @@
+/*
+ * SHA-512 hash implementation and interface functions
+ * Copyright (c) 2015, Pali Rohár <pali.rohar@gmail.com>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "sha512_i.h"
+#include "crypto.h"
+
+
+/**
+ * sha512_vector - SHA512 hash for data vector
+ * @num_elem: Number of elements in the data vector
+ * @addr: Pointers to the data areas
+ * @len: Lengths of the data blocks
+ * @mac: Buffer for the hash
+ * Returns: 0 on success, -1 of failure
+ */
+int sha512_vector(size_t num_elem, const u8 *addr[], const size_t *len,
+		  u8 *mac)
+{
+	struct sha512_state ctx;
+	size_t i;
+
+	sha512_init(&ctx);
+	for (i = 0; i < num_elem; i++)
+		if (sha512_process(&ctx, addr[i], len[i]))
+			return -1;
+	if (sha512_done(&ctx, mac))
+		return -1;
+	return 0;
+}
+
+
+/* ===== start - public domain SHA512 implementation ===== */
+
+/* This is based on SHA512 implementation in LibTomCrypt that was released into
+ * public domain by Tom St Denis. */
+
+#define CONST64(n) n ## ULL
+
+/* the K array */
+static const u64 K[80] = {
+	CONST64(0x428a2f98d728ae22), CONST64(0x7137449123ef65cd),
+	CONST64(0xb5c0fbcfec4d3b2f), CONST64(0xe9b5dba58189dbbc),
+	CONST64(0x3956c25bf348b538), CONST64(0x59f111f1b605d019),
+	CONST64(0x923f82a4af194f9b), CONST64(0xab1c5ed5da6d8118),
+	CONST64(0xd807aa98a3030242), CONST64(0x12835b0145706fbe),
+	CONST64(0x243185be4ee4b28c), CONST64(0x550c7dc3d5ffb4e2),
+	CONST64(0x72be5d74f27b896f), CONST64(0x80deb1fe3b1696b1),
+	CONST64(0x9bdc06a725c71235), CONST64(0xc19bf174cf692694),
+	CONST64(0xe49b69c19ef14ad2), CONST64(0xefbe4786384f25e3),
+	CONST64(0x0fc19dc68b8cd5b5), CONST64(0x240ca1cc77ac9c65),
+	CONST64(0x2de92c6f592b0275), CONST64(0x4a7484aa6ea6e483),
+	CONST64(0x5cb0a9dcbd41fbd4), CONST64(0x76f988da831153b5),
+	CONST64(0x983e5152ee66dfab), CONST64(0xa831c66d2db43210),
+	CONST64(0xb00327c898fb213f), CONST64(0xbf597fc7beef0ee4),
+	CONST64(0xc6e00bf33da88fc2), CONST64(0xd5a79147930aa725),
+	CONST64(0x06ca6351e003826f), CONST64(0x142929670a0e6e70),
+	CONST64(0x27b70a8546d22ffc), CONST64(0x2e1b21385c26c926),
+	CONST64(0x4d2c6dfc5ac42aed), CONST64(0x53380d139d95b3df),
+	CONST64(0x650a73548baf63de), CONST64(0x766a0abb3c77b2a8),
+	CONST64(0x81c2c92e47edaee6), CONST64(0x92722c851482353b),
+	CONST64(0xa2bfe8a14cf10364), CONST64(0xa81a664bbc423001),
+	CONST64(0xc24b8b70d0f89791), CONST64(0xc76c51a30654be30),
+	CONST64(0xd192e819d6ef5218), CONST64(0xd69906245565a910),
+	CONST64(0xf40e35855771202a), CONST64(0x106aa07032bbd1b8),
+	CONST64(0x19a4c116b8d2d0c8), CONST64(0x1e376c085141ab53),
+	CONST64(0x2748774cdf8eeb99), CONST64(0x34b0bcb5e19b48a8),
+	CONST64(0x391c0cb3c5c95a63), CONST64(0x4ed8aa4ae3418acb),
+	CONST64(0x5b9cca4f7763e373), CONST64(0x682e6ff3d6b2b8a3),
+	CONST64(0x748f82ee5defb2fc), CONST64(0x78a5636f43172f60),
+	CONST64(0x84c87814a1f0ab72), CONST64(0x8cc702081a6439ec),
+	CONST64(0x90befffa23631e28), CONST64(0xa4506cebde82bde9),
+	CONST64(0xbef9a3f7b2c67915), CONST64(0xc67178f2e372532b),
+	CONST64(0xca273eceea26619c), CONST64(0xd186b8c721c0c207),
+	CONST64(0xeada7dd6cde0eb1e), CONST64(0xf57d4f7fee6ed178),
+	CONST64(0x06f067aa72176fba), CONST64(0x0a637dc5a2c898a6),
+	CONST64(0x113f9804bef90dae), CONST64(0x1b710b35131c471b),
+	CONST64(0x28db77f523047d84), CONST64(0x32caab7b40c72493),
+	CONST64(0x3c9ebe0a15c9bebc), CONST64(0x431d67c49c100d4c),
+	CONST64(0x4cc5d4becb3e42b6), CONST64(0x597f299cfc657e2a),
+	CONST64(0x5fcb6fab3ad6faec), CONST64(0x6c44198c4a475817)
+};
+
+/* Various logical functions */
+#define Ch(x,y,z)       (z ^ (x & (y ^ z)))
+#define Maj(x,y,z)      (((x | y) & z) | (x & y))
+#define S(x, n)         ROR64c(x, n)
+#define R(x, n)         (((x) & CONST64(0xFFFFFFFFFFFFFFFF)) >> ((u64) n))
+#define Sigma0(x)       (S(x, 28) ^ S(x, 34) ^ S(x, 39))
+#define Sigma1(x)       (S(x, 14) ^ S(x, 18) ^ S(x, 41))
+#define Gamma0(x)       (S(x, 1) ^ S(x, 8) ^ R(x, 7))
+#define Gamma1(x)       (S(x, 19) ^ S(x, 61) ^ R(x, 6))
+#ifndef MIN
+#define MIN(x, y) (((x) < (y)) ? (x) : (y))
+#endif
+
+#define ROR64c(x, y) \
+    ( ((((x) & CONST64(0xFFFFFFFFFFFFFFFF)) >> ((u64) (y) & CONST64(63))) | \
+      ((x) << ((u64) (64 - ((y) & CONST64(63)))))) & \
+      CONST64(0xFFFFFFFFFFFFFFFF))
+
+/* compress 1024-bits */
+static int sha512_compress(struct sha512_state *md, unsigned char *buf)
+{
+	u64 S[8], W[80], t0, t1;
+	int i;
+
+	/* copy state into S */
+	for (i = 0; i < 8; i++) {
+		S[i] = md->state[i];
+	}
+
+	/* copy the state into 1024-bits into W[0..15] */
+	for (i = 0; i < 16; i++)
+		W[i] = WPA_GET_BE64(buf + (8 * i));
+
+	/* fill W[16..79] */
+	for (i = 16; i < 80; i++) {
+		W[i] = Gamma1(W[i - 2]) + W[i - 7] + Gamma0(W[i - 15]) +
+			W[i - 16];
+	}
+
+	/* Compress */
+	for (i = 0; i < 80; i++) {
+		t0 = S[7] + Sigma1(S[4]) + Ch(S[4], S[5], S[6]) + K[i] + W[i];
+		t1 = Sigma0(S[0]) + Maj(S[0], S[1], S[2]);
+		S[7] = S[6];
+		S[6] = S[5];
+		S[5] = S[4];
+		S[4] = S[3] + t0;
+		S[3] = S[2];
+		S[2] = S[1];
+		S[1] = S[0];
+		S[0] = t0 + t1;
+	}
+
+	/* feedback */
+	for (i = 0; i < 8; i++) {
+		md->state[i] = md->state[i] + S[i];
+	}
+
+	return 0;
+}
+
+
+/**
+   Initialize the hash state
+   @param md   The hash state you wish to initialize
+   @return CRYPT_OK if successful
+*/
+void sha512_init(struct sha512_state *md)
+{
+	md->curlen = 0;
+	md->length = 0;
+	md->state[0] = CONST64(0x6a09e667f3bcc908);
+	md->state[1] = CONST64(0xbb67ae8584caa73b);
+	md->state[2] = CONST64(0x3c6ef372fe94f82b);
+	md->state[3] = CONST64(0xa54ff53a5f1d36f1);
+	md->state[4] = CONST64(0x510e527fade682d1);
+	md->state[5] = CONST64(0x9b05688c2b3e6c1f);
+	md->state[6] = CONST64(0x1f83d9abfb41bd6b);
+	md->state[7] = CONST64(0x5be0cd19137e2179);
+}
+
+
+/**
+   Process a block of memory though the hash
+   @param md     The hash state
+   @param in     The data to hash
+   @param inlen  The length of the data (octets)
+   @return CRYPT_OK if successful
+*/
+int sha512_process(struct sha512_state *md, const unsigned char *in,
+		   unsigned long inlen)
+{
+	unsigned long n;
+
+	if (md->curlen >= sizeof(md->buf))
+		return -1;
+
+	while (inlen > 0) {
+		if (md->curlen == 0 && inlen >= SHA512_BLOCK_SIZE) {
+			if (sha512_compress(md, (unsigned char *) in) < 0)
+				return -1;
+			md->length += SHA512_BLOCK_SIZE * 8;
+			in += SHA512_BLOCK_SIZE;
+			inlen -= SHA512_BLOCK_SIZE;
+		} else {
+			n = MIN(inlen, (SHA512_BLOCK_SIZE - md->curlen));
+			os_memcpy(md->buf + md->curlen, in, n);
+			md->curlen += n;
+			in += n;
+			inlen -= n;
+			if (md->curlen == SHA512_BLOCK_SIZE) {
+				if (sha512_compress(md, md->buf) < 0)
+					return -1;
+				md->length += 8 * SHA512_BLOCK_SIZE;
+				md->curlen = 0;
+			}
+		}
+	}
+
+	return 0;
+}
+
+
+/**
+   Terminate the hash to get the digest
+   @param md  The hash state
+   @param out [out] The destination of the hash (64 bytes)
+   @return CRYPT_OK if successful
+*/
+int sha512_done(struct sha512_state *md, unsigned char *out)
+{
+	int i;
+
+	if (md->curlen >= sizeof(md->buf))
+		return -1;
+
+	/* increase the length of the message */
+	md->length += md->curlen * CONST64(8);
+
+	/* append the '1' bit */
+	md->buf[md->curlen++] = (unsigned char) 0x80;
+
+	/* if the length is currently above 112 bytes we append zeros
+	 * then compress.  Then we can fall back to padding zeros and length
+	 * encoding like normal.
+	 */
+	if (md->curlen > 112) {
+		while (md->curlen < 128) {
+			md->buf[md->curlen++] = (unsigned char) 0;
+		}
+		sha512_compress(md, md->buf);
+		md->curlen = 0;
+	}
+
+	/* pad upto 120 bytes of zeroes
+	 * note: that from 112 to 120 is the 64 MSB of the length.  We assume
+	 * that you won't hash > 2^64 bits of data... :-)
+	 */
+	while (md->curlen < 120) {
+		md->buf[md->curlen++] = (unsigned char) 0;
+	}
+
+	/* store length */
+	WPA_PUT_BE64(md->buf + 120, md->length);
+	sha512_compress(md, md->buf);
+
+	/* copy output */
+	for (i = 0; i < 8; i++)
+		WPA_PUT_BE64(out + (8 * i), md->state[i]);
+
+	return 0;
+}
+
+/* ===== end - public domain SHA512 implementation ===== */
diff --git a/src/crypto/sha512_i.h b/src/crypto/sha512_i.h
new file mode 100644
index 0000000..1089589
--- /dev/null
+++ b/src/crypto/sha512_i.h
@@ -0,0 +1,25 @@
+/*
+ * SHA-512 internal definitions
+ * Copyright (c) 2015, Pali Rohár <pali.rohar@gmail.com>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef SHA512_I_H
+#define SHA512_I_H
+
+#define SHA512_BLOCK_SIZE 128
+
+struct sha512_state {
+	u64 length, state[8];
+	u32 curlen;
+	u8 buf[SHA512_BLOCK_SIZE];
+};
+
+void sha512_init(struct sha512_state *md);
+int sha512_process(struct sha512_state *md, const unsigned char *in,
+		   unsigned long inlen);
+int sha512_done(struct sha512_state *md, unsigned char *out);
+
+#endif /* SHA512_I_H */
diff --git a/src/crypto/tls.h b/src/crypto/tls.h
index d13657e..2e56233 100644
--- a/src/crypto/tls.h
+++ b/src/crypto/tls.h
@@ -11,7 +11,7 @@
 
 struct tls_connection;
 
-struct tls_keys {
+struct tls_random {
 	const u8 *client_random;
 	size_t client_random_len;
 	const u8 *server_random;
@@ -79,6 +79,7 @@
 	int fips_mode;
 	int cert_in_cb;
 	const char *openssl_ciphers;
+	unsigned int tls_session_lifetime;
 
 	void (*event_cb)(void *ctx, enum tls_event ev,
 			 union tls_event_data *data);
@@ -93,6 +94,7 @@
 #define TLS_CONN_DISABLE_TLSv1_1 BIT(5)
 #define TLS_CONN_DISABLE_TLSv1_2 BIT(6)
 #define TLS_CONN_EAP_FAST BIT(7)
+#define TLS_CONN_DISABLE_TLSv1_0 BIT(8)
 
 /**
  * struct tls_connection_params - Parameters for TLS connection
@@ -304,22 +306,28 @@
  * @tls_ctx: TLS context data from tls_init()
  * @conn: Connection context data from tls_connection_init()
  * @verify_peer: 1 = verify peer certificate
+ * @flags: Connection flags (TLS_CONN_*)
+ * @session_ctx: Session caching context or %NULL to use default
+ * @session_ctx_len: Length of @session_ctx in bytes.
  * Returns: 0 on success, -1 on failure
  */
 int __must_check tls_connection_set_verify(void *tls_ctx,
 					   struct tls_connection *conn,
-					   int verify_peer);
+					   int verify_peer,
+					   unsigned int flags,
+					   const u8 *session_ctx,
+					   size_t session_ctx_len);
 
 /**
- * tls_connection_get_keys - Get random data from TLS connection
+ * tls_connection_get_random - Get random data from TLS connection
  * @tls_ctx: TLS context data from tls_init()
  * @conn: Connection context data from tls_connection_init()
- * @keys: Structure of client/server random data (filled on success)
+ * @data: Structure of client/server random data (filled on success)
  * Returns: 0 on success, -1 on failure
  */
-int __must_check tls_connection_get_keys(void *tls_ctx,
+int __must_check tls_connection_get_random(void *tls_ctx,
 					 struct tls_connection *conn,
-					 struct tls_keys *keys);
+					 struct tls_random *data);
 
 /**
  * tls_connection_prf - Use TLS-PRF to derive keying material
@@ -333,14 +341,11 @@
  * @out_len: Length of the output buffer
  * Returns: 0 on success, -1 on failure
  *
- * This function is optional to implement if tls_connection_get_keys() provides
- * access to master secret and server/client random values. If these values are
- * not exported from the TLS library, tls_connection_prf() is required so that
- * further keying material can be derived from the master secret. If not
- * implemented, the function will still need to be defined, but it can just
- * return -1. Example implementation of this function is in tls_prf_sha1_md5()
- * when it is called with seed set to client_random|server_random (or
- * server_random|client_random).
+ * tls_connection_prf() is required so that further keying material can be
+ * derived from the master secret. Example implementation of this function is in
+ * tls_prf_sha1_md5() when it is called with seed set to
+ * client_random|server_random (or server_random|client_random). For TLSv1.2 and
+ * newer, a different PRF is needed, though.
  */
 int __must_check  tls_connection_prf(void *tls_ctx,
 				     struct tls_connection *conn,
@@ -466,6 +471,19 @@
 						u8 *ciphers);
 
 /**
+ * tls_get_version - Get the current TLS version number
+ * @tls_ctx: TLS context data from tls_init()
+ * @conn: Connection context data from tls_connection_init()
+ * @buf: Buffer for returning the TLS version number
+ * @buflen: buf size
+ * Returns: 0 on success, -1 on failure
+ *
+ * Get the currently used TLS version number.
+ */
+int __must_check tls_get_version(void *tls_ctx, struct tls_connection *conn,
+				 char *buf, size_t buflen);
+
+/**
  * tls_get_cipher - Get current cipher name
  * @tls_ctx: TLS context data from tls_init()
  * @conn: Connection context data from tls_connection_init()
@@ -532,13 +550,6 @@
 int tls_connection_get_write_alerts(void *tls_ctx,
 				    struct tls_connection *conn);
 
-/**
- * tls_capabilities - Get supported TLS capabilities
- * @tls_ctx: TLS context data from tls_init()
- * Returns: Bit field of supported TLS capabilities (TLS_CAPABILITY_*)
- */
-unsigned int tls_capabilities(void *tls_ctx);
-
 typedef int (*tls_session_ticket_cb)
 (void *ctx, const u8 *ticket, size_t len, const u8 *client_random,
  const u8 *server_random, u8 *master_secret);
@@ -564,4 +575,14 @@
 
 int tls_get_library_version(char *buf, size_t buf_len);
 
+void tls_connection_set_success_data(struct tls_connection *conn,
+				     struct wpabuf *data);
+
+void tls_connection_set_success_data_resumed(struct tls_connection *conn);
+
+const struct wpabuf *
+tls_connection_get_success_data(struct tls_connection *conn);
+
+void tls_connection_remove_session(struct tls_connection *conn);
+
 #endif /* TLS_H */
diff --git a/src/crypto/tls_gnutls.c b/src/crypto/tls_gnutls.c
index c7f6464..f994379 100644
--- a/src/crypto/tls_gnutls.c
+++ b/src/crypto/tls_gnutls.c
@@ -708,7 +708,8 @@
 
 
 int tls_connection_set_verify(void *ssl_ctx, struct tls_connection *conn,
-			      int verify_peer)
+			      int verify_peer, unsigned int flags,
+			      const u8 *session_ctx, size_t session_ctx_len)
 {
 	if (conn == NULL || conn->session == NULL)
 		return -1;
@@ -722,8 +723,8 @@
 }
 
 
-int tls_connection_get_keys(void *ssl_ctx, struct tls_connection *conn,
-			    struct tls_keys *keys)
+int tls_connection_get_random(void *ssl_ctx, struct tls_connection *conn,
+			    struct tls_random *keys)
 {
 #if GNUTLS_VERSION_NUMBER >= 0x030012
 	gnutls_datum_t client, server;
@@ -1426,6 +1427,14 @@
 }
 
 
+int tls_get_version(void *ssl_ctx, struct tls_connection *conn,
+		    char *buf, size_t buflen)
+{
+	/* TODO */
+	return -1;
+}
+
+
 int tls_get_cipher(void *ssl_ctx, struct tls_connection *conn,
 		   char *buf, size_t buflen)
 {
@@ -1476,12 +1485,6 @@
 }
 
 
-unsigned int tls_capabilities(void *tls_ctx)
-{
-	return 0;
-}
-
-
 int tls_connection_set_session_ticket_cb(void *tls_ctx,
 					 struct tls_connection *conn,
 					 tls_session_ticket_cb cb, void *ctx)
@@ -1495,3 +1498,26 @@
 	return os_snprintf(buf, buf_len, "GnuTLS build=%s run=%s",
 			   GNUTLS_VERSION, gnutls_check_version(NULL));
 }
+
+
+void tls_connection_set_success_data(struct tls_connection *conn,
+				     struct wpabuf *data)
+{
+}
+
+
+void tls_connection_set_success_data_resumed(struct tls_connection *conn)
+{
+}
+
+
+const struct wpabuf *
+tls_connection_get_success_data(struct tls_connection *conn)
+{
+	return NULL;
+}
+
+
+void tls_connection_remove_session(struct tls_connection *conn)
+{
+}
diff --git a/src/crypto/tls_internal.c b/src/crypto/tls_internal.c
index afd4695..dcbb31d 100644
--- a/src/crypto/tls_internal.c
+++ b/src/crypto/tls_internal.c
@@ -23,6 +23,11 @@
 	int server;
 	struct tlsv1_credentials *server_cred;
 	int check_crl;
+
+	void (*event_cb)(void *ctx, enum tls_event ev,
+			 union tls_event_data *data);
+	void *cb_ctx;
+	int cert_in_cb;
 };
 
 struct tls_connection {
@@ -51,6 +56,11 @@
 	global = os_zalloc(sizeof(*global));
 	if (global == NULL)
 		return NULL;
+	if (conf) {
+		global->event_cb = conf->event_cb;
+		global->cb_ctx = conf->cb_ctx;
+		global->cert_in_cb = conf->cert_in_cb;
+	}
 
 	return global;
 }
@@ -64,10 +74,12 @@
 		tlsv1_client_global_deinit();
 #endif /* CONFIG_TLS_INTERNAL_CLIENT */
 #ifdef CONFIG_TLS_INTERNAL_SERVER
-		tlsv1_cred_free(global->server_cred);
 		tlsv1_server_global_deinit();
 #endif /* CONFIG_TLS_INTERNAL_SERVER */
 	}
+#ifdef CONFIG_TLS_INTERNAL_SERVER
+	tlsv1_cred_free(global->server_cred);
+#endif /* CONFIG_TLS_INTERNAL_SERVER */
 	os_free(global);
 }
 
@@ -95,6 +107,8 @@
 			os_free(conn);
 			return NULL;
 		}
+		tlsv1_client_set_cb(conn->client, global->event_cb,
+				    global->cb_ctx, global->cert_in_cb);
 	}
 #endif /* CONFIG_TLS_INTERNAL_CLIENT */
 #ifdef CONFIG_TLS_INTERNAL_SERVER
@@ -259,8 +273,7 @@
 		return -1;
 	}
 
-	tlsv1_client_set_time_checks(
-		conn->client, !(params->flags & TLS_CONN_DISABLE_TIME_CHECKS));
+	tlsv1_client_set_flags(conn->client, params->flags);
 
 	return 0;
 #else /* CONFIG_TLS_INTERNAL_CLIENT */
@@ -328,7 +341,8 @@
 
 
 int tls_connection_set_verify(void *tls_ctx, struct tls_connection *conn,
-			      int verify_peer)
+			      int verify_peer, unsigned int flags,
+			      const u8 *session_ctx, size_t session_ctx_len)
 {
 #ifdef CONFIG_TLS_INTERNAL_SERVER
 	if (conn->server)
@@ -338,16 +352,16 @@
 }
 
 
-int tls_connection_get_keys(void *tls_ctx, struct tls_connection *conn,
-			    struct tls_keys *keys)
+int tls_connection_get_random(void *tls_ctx, struct tls_connection *conn,
+			      struct tls_random *data)
 {
 #ifdef CONFIG_TLS_INTERNAL_CLIENT
 	if (conn->client)
-		return tlsv1_client_get_keys(conn->client, keys);
+		return tlsv1_client_get_random(conn->client, data);
 #endif /* CONFIG_TLS_INTERNAL_CLIENT */
 #ifdef CONFIG_TLS_INTERNAL_SERVER
 	if (conn->server)
-		return tlsv1_server_get_keys(conn->server, keys);
+		return tlsv1_server_get_random(conn->server, data);
 #endif /* CONFIG_TLS_INTERNAL_SERVER */
 	return -1;
 }
@@ -389,14 +403,14 @@
 	if (conn->client) {
 		ret = tlsv1_client_prf(conn->client, label,
 				       server_random_first,
-				       _out, out_len);
+				       _out, skip + out_len);
 	}
 #endif /* CONFIG_TLS_INTERNAL_CLIENT */
 #ifdef CONFIG_TLS_INTERNAL_SERVER
 	if (conn->server) {
 		ret = tlsv1_server_prf(conn->server, label,
 				       server_random_first,
-				       _out, out_len);
+				       _out, skip + out_len);
 	}
 #endif /* CONFIG_TLS_INTERNAL_SERVER */
 	if (ret == 0 && skip_keyblock)
@@ -617,6 +631,19 @@
 }
 
 
+int tls_get_version(void *ssl_ctx, struct tls_connection *conn,
+		    char *buf, size_t buflen)
+{
+	if (conn == NULL)
+		return -1;
+#ifdef CONFIG_TLS_INTERNAL_CLIENT
+	if (conn->client)
+		return tlsv1_client_get_version(conn->client, buf, buflen);
+#endif /* CONFIG_TLS_INTERNAL_CLIENT */
+	return -1;
+}
+
+
 int tls_get_cipher(void *tls_ctx, struct tls_connection *conn,
 		   char *buf, size_t buflen)
 {
@@ -674,12 +701,6 @@
 }
 
 
-unsigned int tls_capabilities(void *tls_ctx)
-{
-	return 0;
-}
-
-
 int tls_connection_set_session_ticket_cb(void *tls_ctx,
 					 struct tls_connection *conn,
 					 tls_session_ticket_cb cb,
@@ -705,3 +726,26 @@
 {
 	return os_snprintf(buf, buf_len, "internal");
 }
+
+
+void tls_connection_set_success_data(struct tls_connection *conn,
+				     struct wpabuf *data)
+{
+}
+
+
+void tls_connection_set_success_data_resumed(struct tls_connection *conn)
+{
+}
+
+
+const struct wpabuf *
+tls_connection_get_success_data(struct tls_connection *conn)
+{
+	return NULL;
+}
+
+
+void tls_connection_remove_session(struct tls_connection *conn)
+{
+}
diff --git a/src/crypto/tls_none.c b/src/crypto/tls_none.c
index 1b1ba56..ae392ad 100644
--- a/src/crypto/tls_none.c
+++ b/src/crypto/tls_none.c
@@ -72,14 +72,15 @@
 
 
 int tls_connection_set_verify(void *tls_ctx, struct tls_connection *conn,
-			      int verify_peer)
+			      int verify_peer, unsigned int flags,
+			      const u8 *session_ctx, size_t session_ctx_len)
 {
 	return -1;
 }
 
 
-int tls_connection_get_keys(void *tls_ctx, struct tls_connection *conn,
-			    struct tls_keys *keys)
+int tls_connection_get_random(void *tls_ctx, struct tls_connection *conn,
+			      struct tls_random *data)
 {
 	return -1;
 }
@@ -140,6 +141,13 @@
 }
 
 
+int tls_get_version(void *ssl_ctx, struct tls_connection *conn,
+		    char *buf, size_t buflen)
+{
+	return -1;
+}
+
+
 int tls_get_cipher(void *tls_ctx, struct tls_connection *conn,
 		   char *buf, size_t buflen)
 {
@@ -181,13 +189,30 @@
 }
 
 
-unsigned int tls_capabilities(void *tls_ctx)
-{
-	return 0;
-}
-
-
 int tls_get_library_version(char *buf, size_t buf_len)
 {
 	return os_snprintf(buf, buf_len, "none");
 }
+
+
+void tls_connection_set_success_data(struct tls_connection *conn,
+				     struct wpabuf *data)
+{
+}
+
+
+void tls_connection_set_success_data_resumed(struct tls_connection *conn)
+{
+}
+
+
+const struct wpabuf *
+tls_connection_get_success_data(struct tls_connection *conn)
+{
+	return NULL;
+}
+
+
+void tls_connection_remove_session(struct tls_connection *conn)
+{
+}
diff --git a/src/crypto/tls_openssl.c b/src/crypto/tls_openssl.c
index db2d73e..471ae2b 100644
--- a/src/crypto/tls_openssl.c
+++ b/src/crypto/tls_openssl.c
@@ -1,6 +1,6 @@
 /*
  * SSL/TLS interface functions for OpenSSL
- * Copyright (c) 2004-2013, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2015, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -23,11 +23,19 @@
 #ifndef OPENSSL_NO_ENGINE
 #include <openssl/engine.h>
 #endif /* OPENSSL_NO_ENGINE */
+#ifndef OPENSSL_NO_DSA
+#include <openssl/dsa.h>
+#endif
+#ifndef OPENSSL_NO_DH
+#include <openssl/dh.h>
+#endif
 
 #include "common.h"
 #include "crypto.h"
 #include "sha1.h"
+#include "sha256.h"
 #include "tls.h"
+#include "tls_openssl.h"
 
 #if OPENSSL_VERSION_NUMBER < 0x10000000L
 /* ERR_remove_thread_state replaces ERR_remove_state and the latter is
@@ -64,10 +72,10 @@
 	free(value);
 	return bio;
 }
-
 #endif /* ANDROID */
 
 static int tls_openssl_ref_count = 0;
+static int tls_ex_idx_session = -1;
 
 struct tls_context {
 	void (*event_cb)(void *ctx, enum tls_event ev,
@@ -80,6 +88,11 @@
 static struct tls_context *tls_global = NULL;
 
 
+struct tls_data {
+	SSL_CTX *ssl;
+	unsigned int tls_session_lifetime;
+};
+
 struct tls_connection {
 	struct tls_context *context;
 	SSL_CTX *ssl_ctx;
@@ -103,6 +116,7 @@
 	unsigned int cert_probe:1;
 	unsigned int server_cert_only:1;
 	unsigned int invalid_hb_used:1;
+	unsigned int success_data:1;
 
 	u8 srv_cert_hash[32];
 
@@ -111,6 +125,11 @@
 	X509 *peer_cert;
 	X509 *peer_issuer;
 	X509 *peer_issuer_issuer;
+
+#if OPENSSL_VERSION_NUMBER >= 0x10100000L
+	unsigned char client_random[SSL3_RANDOM_SIZE];
+	unsigned char server_random[SSL3_RANDOM_SIZE];
+#endif
 };
 
 
@@ -733,8 +752,27 @@
 #endif /* OPENSSL_NO_ENGINE */
 
 
+static void remove_session_cb(SSL_CTX *ctx, SSL_SESSION *sess)
+{
+	struct wpabuf *buf;
+
+	if (tls_ex_idx_session < 0)
+		return;
+	buf = SSL_SESSION_get_ex_data(sess, tls_ex_idx_session);
+	if (!buf)
+		return;
+	wpa_printf(MSG_DEBUG,
+		   "OpenSSL: Free application session data %p (sess %p)",
+		   buf, sess);
+	wpabuf_free(buf);
+
+	SSL_SESSION_set_ex_data(sess, tls_ex_idx_session, NULL);
+}
+
+
 void * tls_init(const struct tls_config *conf)
 {
+	struct tls_data *data;
 	SSL_CTX *ssl;
 	struct tls_context *context;
 	const char *ciphers;
@@ -746,7 +784,9 @@
 #ifdef CONFIG_FIPS
 #ifdef OPENSSL_FIPS
 		if (conf && conf->fips_mode) {
-			if (!FIPS_mode_set(1)) {
+			static int fips_enabled = 0;
+
+			if (!fips_enabled && !FIPS_mode_set(1)) {
 				wpa_printf(MSG_ERROR, "Failed to enable FIPS "
 					   "mode");
 				ERR_load_crypto_strings();
@@ -754,8 +794,10 @@
 				os_free(tls_global);
 				tls_global = NULL;
 				return NULL;
-			} else
+			} else {
 				wpa_printf(MSG_INFO, "Running in FIPS mode");
+				fips_enabled = 1;
+			}
 		}
 #else /* OPENSSL_FIPS */
 		if (conf && conf->fips_mode) {
@@ -795,7 +837,11 @@
 	}
 	tls_openssl_ref_count++;
 
-	ssl = SSL_CTX_new(SSLv23_method());
+	data = os_zalloc(sizeof(*data));
+	if (data)
+		ssl = SSL_CTX_new(SSLv23_method());
+	else
+		ssl = NULL;
 	if (ssl == NULL) {
 		tls_openssl_ref_count--;
 		if (context != tls_global)
@@ -806,12 +852,37 @@
 		}
 		return NULL;
 	}
+	data->ssl = ssl;
+	if (conf)
+		data->tls_session_lifetime = conf->tls_session_lifetime;
 
 	SSL_CTX_set_options(ssl, SSL_OP_NO_SSLv2);
 	SSL_CTX_set_options(ssl, SSL_OP_NO_SSLv3);
 
 	SSL_CTX_set_info_callback(ssl, ssl_info_cb);
 	SSL_CTX_set_app_data(ssl, context);
+	if (data->tls_session_lifetime > 0) {
+		SSL_CTX_set_quiet_shutdown(ssl, 1);
+		/*
+		 * Set default context here. In practice, this will be replaced
+		 * by the per-EAP method context in tls_connection_set_verify().
+		 */
+		SSL_CTX_set_session_id_context(ssl, (u8 *) "hostapd", 7);
+		SSL_CTX_set_session_cache_mode(ssl, SSL_SESS_CACHE_SERVER);
+		SSL_CTX_set_timeout(ssl, data->tls_session_lifetime);
+		SSL_CTX_sess_set_remove_cb(ssl, remove_session_cb);
+	} else {
+		SSL_CTX_set_session_cache_mode(ssl, SSL_SESS_CACHE_OFF);
+	}
+
+	if (tls_ex_idx_session < 0) {
+		tls_ex_idx_session = SSL_SESSION_get_ex_new_index(
+			0, NULL, NULL, NULL, NULL);
+		if (tls_ex_idx_session < 0) {
+			tls_deinit(data);
+			return NULL;
+		}
+	}
 
 #ifndef OPENSSL_NO_ENGINE
 	wpa_printf(MSG_DEBUG, "ENGINE: Loading dynamic engine");
@@ -824,7 +895,7 @@
 		if (tls_engine_load_dynamic_opensc(conf->opensc_engine_path) ||
 		    tls_engine_load_dynamic_pkcs11(conf->pkcs11_engine_path,
 						   conf->pkcs11_module_path)) {
-			tls_deinit(ssl);
+			tls_deinit(data);
 			return NULL;
 		}
 	}
@@ -838,20 +909,23 @@
 		wpa_printf(MSG_ERROR,
 			   "OpenSSL: Failed to set cipher string '%s'",
 			   ciphers);
-		tls_deinit(ssl);
+		tls_deinit(data);
 		return NULL;
 	}
 
-	return ssl;
+	return data;
 }
 
 
 void tls_deinit(void *ssl_ctx)
 {
-	SSL_CTX *ssl = ssl_ctx;
+	struct tls_data *data = ssl_ctx;
+	SSL_CTX *ssl = data->ssl;
 	struct tls_context *context = SSL_CTX_get_app_data(ssl);
 	if (context != tls_global)
 		os_free(context);
+	if (data->tls_session_lifetime > 0)
+		SSL_CTX_flush_sessions(ssl, 0);
 	SSL_CTX_free(ssl);
 
 	tls_openssl_ref_count--;
@@ -868,12 +942,10 @@
 		os_free(tls_global);
 		tls_global = NULL;
 	}
+
+	os_free(data);
 }
 
-#ifdef ANDROID
-/* EVP_PKEY_from_keystore comes from system/security/keystore-engine. */
-EVP_PKEY* EVP_PKEY_from_keystore(const char* key_id);
-#endif
 
 #ifndef OPENSSL_NO_ENGINE
 
@@ -896,6 +968,11 @@
 #endif /* OPENSSL_NO_ENGINE */
 
 
+#ifdef ANDROID
+/* EVP_PKEY_from_keystore comes from system/security/keystore-engine. */
+EVP_PKEY * EVP_PKEY_from_keystore(const char *key_id);
+#endif /* ANDROID */
+
 static int tls_engine_init(struct tls_connection *conn, const char *engine_id,
 			   const char *pin, const char *key_id,
 			   const char *cert_id, const char *ca_cert_id)
@@ -904,7 +981,8 @@
 #if !defined(OPENSSL_NO_ENGINE)
 #error "This code depends on OPENSSL_NO_ENGINE being defined by BoringSSL."
 #endif
-
+	if (!key_id)
+		return TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED;
 	conn->engine = NULL;
 	conn->private_key = EVP_PKEY_from_keystore(key_id);
 	if (!conn->private_key) {
@@ -914,7 +992,7 @@
 			   ERR_error_string(ERR_get_error(), NULL));
 		return TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED;
 	}
-#endif
+#endif /* ANDROID && OPENSSL_IS_BORINGSSL */
 
 #ifndef OPENSSL_NO_ENGINE
 	int ret = -1;
@@ -1022,10 +1100,10 @@
 	if (conn->engine) {
 #if !defined(OPENSSL_IS_BORINGSSL)
 		ENGINE_finish(conn->engine);
-#endif
+#endif /* !OPENSSL_IS_BORINGSSL */
 		conn->engine = NULL;
 	}
-#endif /* OPENSSL_NO_ENGINE */
+#endif /* ANDROID || !OPENSSL_NO_ENGINE */
 }
 
 
@@ -1044,14 +1122,83 @@
 }
 
 
+static const char * openssl_content_type(int content_type)
+{
+	switch (content_type) {
+	case 20:
+		return "change cipher spec";
+	case 21:
+		return "alert";
+	case 22:
+		return "handshake";
+	case 23:
+		return "application data";
+	case 24:
+		return "heartbeat";
+	case 256:
+		return "TLS header info"; /* pseudo content type */
+	default:
+		return "?";
+	}
+}
+
+
+static const char * openssl_handshake_type(int content_type, const u8 *buf,
+					   size_t len)
+{
+	if (content_type != 22 || !buf || len == 0)
+		return "";
+	switch (buf[0]) {
+	case 0:
+		return "hello request";
+	case 1:
+		return "client hello";
+	case 2:
+		return "server hello";
+	case 4:
+		return "new session ticket";
+	case 11:
+		return "certificate";
+	case 12:
+		return "server key exchange";
+	case 13:
+		return "certificate request";
+	case 14:
+		return "server hello done";
+	case 15:
+		return "certificate verify";
+	case 16:
+		return "client key exchange";
+	case 20:
+		return "finished";
+	case 21:
+		return "certificate url";
+	case 22:
+		return "certificate status";
+	default:
+		return "?";
+	}
+}
+
+
 static void tls_msg_cb(int write_p, int version, int content_type,
 		       const void *buf, size_t len, SSL *ssl, void *arg)
 {
 	struct tls_connection *conn = arg;
 	const u8 *pos = buf;
 
-	wpa_printf(MSG_DEBUG, "OpenSSL: %s ver=0x%x content_type=%d",
-		   write_p ? "TX" : "RX", version, content_type);
+	if (write_p == 2) {
+		wpa_printf(MSG_DEBUG,
+			   "OpenSSL: session ver=0x%x content_type=%d",
+			   version, content_type);
+		wpa_hexdump_key(MSG_MSGDUMP, "OpenSSL: Data", buf, len);
+		return;
+	}
+
+	wpa_printf(MSG_DEBUG, "OpenSSL: %s ver=0x%x content_type=%d (%s/%s)",
+		   write_p ? "TX" : "RX", version, content_type,
+		   openssl_content_type(content_type),
+		   openssl_handshake_type(content_type, buf, len));
 	wpa_hexdump_key(MSG_MSGDUMP, "OpenSSL: Message", buf, len);
 	if (content_type == 24 && len >= 3 && pos[0] == 1) {
 		size_t payload_len = WPA_GET_BE16(pos + 1);
@@ -1065,7 +1212,8 @@
 
 struct tls_connection * tls_connection_init(void *ssl_ctx)
 {
-	SSL_CTX *ssl = ssl_ctx;
+	struct tls_data *data = ssl_ctx;
+	SSL_CTX *ssl = data->ssl;
 	struct tls_connection *conn;
 	long options;
 	struct tls_context *context = SSL_CTX_get_app_data(ssl);
@@ -1073,7 +1221,7 @@
 	conn = os_zalloc(sizeof(*conn));
 	if (conn == NULL)
 		return NULL;
-	conn->ssl_ctx = ssl_ctx;
+	conn->ssl_ctx = ssl;
 	conn->ssl = SSL_new(ssl);
 	if (conn->ssl == NULL) {
 		tls_show_errors(MSG_INFO, __func__,
@@ -1122,6 +1270,14 @@
 {
 	if (conn == NULL)
 		return;
+	if (conn->success_data) {
+		/*
+		 * Make sure ssl_clear_bad_session() does not remove this
+		 * session.
+		 */
+		SSL_set_quiet_shutdown(conn->ssl, 1);
+		SSL_shutdown(conn->ssl);
+	}
 	SSL_free(conn->ssl);
 	tls_engine_deinit(conn);
 	os_free(conn->subject_match);
@@ -1639,6 +1795,32 @@
 				       TLS_FAIL_SERVER_CHAIN_PROBE);
 	}
 
+#ifdef OPENSSL_IS_BORINGSSL
+	if (depth == 0 && (conn->flags & TLS_CONN_REQUEST_OCSP) &&
+	    preverify_ok) {
+		enum ocsp_result res;
+
+		res = check_ocsp_resp(conn->ssl_ctx, conn->ssl, err_cert,
+				      conn->peer_issuer,
+				      conn->peer_issuer_issuer);
+		if (res == OCSP_REVOKED) {
+			preverify_ok = 0;
+			openssl_tls_fail_event(conn, err_cert, err, depth, buf,
+					       "certificate revoked",
+					       TLS_FAIL_REVOKED);
+			if (err == X509_V_OK)
+				X509_STORE_CTX_set_error(
+					x509_ctx, X509_V_ERR_CERT_REVOKED);
+		} else if (res != OCSP_GOOD &&
+			   (conn->flags & TLS_CONN_REQUIRE_OCSP)) {
+			preverify_ok = 0;
+			openssl_tls_fail_event(conn, err_cert, err, depth, buf,
+					       "bad certificate status response",
+					       TLS_FAIL_UNSPECIFIED);
+		}
+	}
+#endif /* OPENSSL_IS_BORINGSSL */
+
 	if (preverify_ok && context->event_cb != NULL)
 		context->event_cb(context->cb_ctx,
 				  TLS_CERT_CHAIN_SUCCESS, NULL);
@@ -1648,9 +1830,9 @@
 
 
 #ifndef OPENSSL_NO_STDIO
-static int tls_load_ca_der(void *_ssl_ctx, const char *ca_cert)
+static int tls_load_ca_der(struct tls_data *data, const char *ca_cert)
 {
-	SSL_CTX *ssl_ctx = _ssl_ctx;
+	SSL_CTX *ssl_ctx = data->ssl;
 	X509_LOOKUP *lookup;
 	int ret = 0;
 
@@ -1680,11 +1862,12 @@
 #endif /* OPENSSL_NO_STDIO */
 
 
-static int tls_connection_ca_cert(void *_ssl_ctx, struct tls_connection *conn,
+static int tls_connection_ca_cert(struct tls_data *data,
+				  struct tls_connection *conn,
 				  const char *ca_cert, const u8 *ca_cert_blob,
 				  size_t ca_cert_blob_len, const char *ca_path)
 {
-	SSL_CTX *ssl_ctx = _ssl_ctx;
+	SSL_CTX *ssl_ctx = data->ssl;
 	X509_STORE *store;
 
 	/*
@@ -1819,7 +2002,7 @@
 			tls_show_errors(MSG_WARNING, __func__,
 					"Failed to load root certificates");
 			if (ca_cert &&
-			    tls_load_ca_der(ssl_ctx, ca_cert) == 0) {
+			    tls_load_ca_der(data, ca_cert) == 0) {
 				wpa_printf(MSG_DEBUG, "OpenSSL: %s - loaded "
 					   "DER format CA certificate",
 					   __func__);
@@ -1828,7 +2011,7 @@
 		} else {
 			wpa_printf(MSG_DEBUG, "TLS: Trusted root "
 				   "certificate(s) loaded");
-			tls_get_errors(ssl_ctx);
+			tls_get_errors(data);
 		}
 #else /* OPENSSL_NO_STDIO */
 		wpa_printf(MSG_DEBUG, "OpenSSL: %s - OPENSSL_NO_STDIO",
@@ -1845,8 +2028,10 @@
 }
 
 
-static int tls_global_ca_cert(SSL_CTX *ssl_ctx, const char *ca_cert)
+static int tls_global_ca_cert(struct tls_data *data, const char *ca_cert)
 {
+	SSL_CTX *ssl_ctx = data->ssl;
+
 	if (ca_cert) {
 		if (SSL_CTX_load_verify_locations(ssl_ctx, ca_cert, NULL) != 1)
 		{
@@ -1874,7 +2059,8 @@
 	int flags;
 
 	if (check_crl) {
-		X509_STORE *cs = SSL_CTX_get_cert_store(ssl_ctx);
+		struct tls_data *data = ssl_ctx;
+		X509_STORE *cs = SSL_CTX_get_cert_store(data->ssl);
 		if (cs == NULL) {
 			tls_show_errors(MSG_INFO, __func__, "Failed to get "
 					"certificate store when enabling "
@@ -1932,10 +2118,44 @@
 }
 
 
+static void tls_set_conn_flags(SSL *ssl, unsigned int flags)
+{
+#ifdef SSL_OP_NO_TICKET
+	if (flags & TLS_CONN_DISABLE_SESSION_TICKET)
+		SSL_set_options(ssl, SSL_OP_NO_TICKET);
+#ifdef SSL_clear_options
+	else
+		SSL_clear_options(ssl, SSL_OP_NO_TICKET);
+#endif /* SSL_clear_options */
+#endif /* SSL_OP_NO_TICKET */
+
+#ifdef SSL_OP_NO_TLSv1
+	if (flags & TLS_CONN_DISABLE_TLSv1_0)
+		SSL_set_options(ssl, SSL_OP_NO_TLSv1);
+	else
+		SSL_clear_options(ssl, SSL_OP_NO_TLSv1);
+#endif /* SSL_OP_NO_TLSv1 */
+#ifdef SSL_OP_NO_TLSv1_1
+	if (flags & TLS_CONN_DISABLE_TLSv1_1)
+		SSL_set_options(ssl, SSL_OP_NO_TLSv1_1);
+	else
+		SSL_clear_options(ssl, SSL_OP_NO_TLSv1_1);
+#endif /* SSL_OP_NO_TLSv1_1 */
+#ifdef SSL_OP_NO_TLSv1_2
+	if (flags & TLS_CONN_DISABLE_TLSv1_2)
+		SSL_set_options(ssl, SSL_OP_NO_TLSv1_2);
+	else
+		SSL_clear_options(ssl, SSL_OP_NO_TLSv1_2);
+#endif /* SSL_OP_NO_TLSv1_2 */
+}
+
+
 int tls_connection_set_verify(void *ssl_ctx, struct tls_connection *conn,
-			      int verify_peer)
+			      int verify_peer, unsigned int flags,
+			      const u8 *session_ctx, size_t session_ctx_len)
 {
 	static int counter = 0;
+	struct tls_data *data = ssl_ctx;
 
 	if (conn == NULL)
 		return -1;
@@ -1950,20 +2170,25 @@
 		SSL_set_verify(conn->ssl, SSL_VERIFY_NONE, NULL);
 	}
 
+	tls_set_conn_flags(conn->ssl, flags);
+	conn->flags = flags;
+
 	SSL_set_accept_state(conn->ssl);
 
-	/*
-	 * Set session id context in order to avoid fatal errors when client
-	 * tries to resume a session. However, set the context to a unique
-	 * value in order to effectively disable session resumption for now
-	 * since not all areas of the server code are ready for it (e.g.,
-	 * EAP-TTLS needs special handling for Phase 2 after abbreviated TLS
-	 * handshake).
-	 */
-	counter++;
-	SSL_set_session_id_context(conn->ssl,
-				   (const unsigned char *) &counter,
-				   sizeof(counter));
+	if (data->tls_session_lifetime == 0) {
+		/*
+		 * Set session id context to a unique value to make sure
+		 * session resumption cannot be used either through session
+		 * caching or TLS ticket extension.
+		 */
+		counter++;
+		SSL_set_session_id_context(conn->ssl,
+					   (const unsigned char *) &counter,
+					   sizeof(counter));
+	} else if (session_ctx) {
+		SSL_set_session_id_context(conn->ssl, session_ctx,
+					   session_ctx_len);
+	}
 
 	return 0;
 }
@@ -2035,9 +2260,12 @@
 }
 
 
-static int tls_global_client_cert(SSL_CTX *ssl_ctx, const char *client_cert)
+static int tls_global_client_cert(struct tls_data *data,
+				  const char *client_cert)
 {
 #ifndef OPENSSL_NO_STDIO
+	SSL_CTX *ssl_ctx = data->ssl;
+
 	if (client_cert == NULL)
 		return 0;
 
@@ -2071,7 +2299,7 @@
 
 
 #ifdef PKCS12_FUNCS
-static int tls_parse_pkcs12(SSL_CTX *ssl_ctx, SSL *ssl, PKCS12 *p12,
+static int tls_parse_pkcs12(struct tls_data *data, SSL *ssl, PKCS12 *p12,
 			    const char *passwd)
 {
 	EVP_PKEY *pkey;
@@ -2083,6 +2311,8 @@
 	pkey = NULL;
 	cert = NULL;
 	certs = NULL;
+	if (!passwd)
+		passwd = "";
 	if (!PKCS12_parse(p12, passwd, &pkey, &cert, &certs)) {
 		tls_show_errors(MSG_DEBUG, __func__,
 				"Failed to parse PKCS12 file");
@@ -2100,7 +2330,7 @@
 			if (SSL_use_certificate(ssl, cert) != 1)
 				res = -1;
 		} else {
-			if (SSL_CTX_use_certificate(ssl_ctx, cert) != 1)
+			if (SSL_CTX_use_certificate(data->ssl, cert) != 1)
 				res = -1;
 		}
 		X509_free(cert);
@@ -2112,13 +2342,52 @@
 			if (SSL_use_PrivateKey(ssl, pkey) != 1)
 				res = -1;
 		} else {
-			if (SSL_CTX_use_PrivateKey(ssl_ctx, pkey) != 1)
+			if (SSL_CTX_use_PrivateKey(data->ssl, pkey) != 1)
 				res = -1;
 		}
 		EVP_PKEY_free(pkey);
 	}
 
 	if (certs) {
+#if OPENSSL_VERSION_NUMBER >= 0x10002000L && !defined(LIBRESSL_VERSION_NUMBER)
+		SSL_clear_chain_certs(ssl);
+		while ((cert = sk_X509_pop(certs)) != NULL) {
+			X509_NAME_oneline(X509_get_subject_name(cert), buf,
+					  sizeof(buf));
+			wpa_printf(MSG_DEBUG, "TLS: additional certificate"
+				   " from PKCS12: subject='%s'", buf);
+			if (SSL_add1_chain_cert(ssl, cert) != 1) {
+				tls_show_errors(MSG_DEBUG, __func__,
+						"Failed to add additional certificate");
+				res = -1;
+				break;
+			}
+		}
+		if (!res) {
+			/* Try to continue anyway */
+		}
+		sk_X509_free(certs);
+#ifndef OPENSSL_IS_BORINGSSL
+		res = SSL_build_cert_chain(ssl,
+					   SSL_BUILD_CHAIN_FLAG_CHECK |
+					   SSL_BUILD_CHAIN_FLAG_IGNORE_ERROR);
+		if (!res) {
+			tls_show_errors(MSG_DEBUG, __func__,
+					"Failed to build certificate chain");
+		} else if (res == 2) {
+			wpa_printf(MSG_DEBUG,
+				   "TLS: Ignore certificate chain verification error when building chain with PKCS#12 extra certificates");
+		}
+#endif /* OPENSSL_IS_BORINGSSL */
+		/*
+		 * Try to continue regardless of result since it is possible for
+		 * the extra certificates not to be required.
+		 */
+		res = 0;
+#else /* OPENSSL_VERSION_NUMBER >= 0x10002000L */
+#if OPENSSL_VERSION_NUMBER >= 0x10001000L
+		SSL_CTX_clear_extra_chain_certs(data->ssl);
+#endif /* OPENSSL_VERSION_NUMBER >= 0x10001000L */
 		while ((cert = sk_X509_pop(certs)) != NULL) {
 			X509_NAME_oneline(X509_get_subject_name(cert), buf,
 					  sizeof(buf));
@@ -2128,26 +2397,28 @@
 			 * There is no SSL equivalent for the chain cert - so
 			 * always add it to the context...
 			 */
-			if (SSL_CTX_add_extra_chain_cert(ssl_ctx, cert) != 1) {
+			if (SSL_CTX_add_extra_chain_cert(data->ssl, cert) != 1)
+			{
 				res = -1;
 				break;
 			}
 		}
 		sk_X509_free(certs);
+#endif /* OPENSSL_VERSION_NUMBER >= 0x10002000L */
 	}
 
 	PKCS12_free(p12);
 
 	if (res < 0)
-		tls_get_errors(ssl_ctx);
+		tls_get_errors(data);
 
 	return res;
 }
 #endif  /* PKCS12_FUNCS */
 
 
-static int tls_read_pkcs12(SSL_CTX *ssl_ctx, SSL *ssl, const char *private_key,
-			   const char *passwd)
+static int tls_read_pkcs12(struct tls_data *data, SSL *ssl,
+			   const char *private_key, const char *passwd)
 {
 #ifdef PKCS12_FUNCS
 	FILE *f;
@@ -2166,7 +2437,7 @@
 		return -1;
 	}
 
-	return tls_parse_pkcs12(ssl_ctx, ssl, p12, passwd);
+	return tls_parse_pkcs12(data, ssl, p12, passwd);
 
 #else /* PKCS12_FUNCS */
 	wpa_printf(MSG_INFO, "TLS: PKCS12 support disabled - cannot read "
@@ -2176,7 +2447,7 @@
 }
 
 
-static int tls_read_pkcs12_blob(SSL_CTX *ssl_ctx, SSL *ssl,
+static int tls_read_pkcs12_blob(struct tls_data *data, SSL *ssl,
 				const u8 *blob, size_t len, const char *passwd)
 {
 #ifdef PKCS12_FUNCS
@@ -2189,7 +2460,7 @@
 		return -1;
 	}
 
-	return tls_parse_pkcs12(ssl_ctx, ssl, p12, passwd);
+	return tls_parse_pkcs12(data, ssl, p12, passwd);
 
 #else /* PKCS12_FUNCS */
 	wpa_printf(MSG_INFO, "TLS: PKCS12 support disabled - cannot parse "
@@ -2260,13 +2531,13 @@
 }
 
 
-static int tls_connection_engine_ca_cert(void *_ssl_ctx,
+static int tls_connection_engine_ca_cert(struct tls_data *data,
 					 struct tls_connection *conn,
 					 const char *ca_cert_id)
 {
 #ifndef OPENSSL_NO_ENGINE
 	X509 *cert;
-	SSL_CTX *ssl_ctx = _ssl_ctx;
+	SSL_CTX *ssl_ctx = data->ssl;
 	X509_STORE *store;
 
 	if (tls_engine_get_cert(conn, ca_cert_id, &cert))
@@ -2332,14 +2603,14 @@
 }
 
 
-static int tls_connection_private_key(void *_ssl_ctx,
+static int tls_connection_private_key(struct tls_data *data,
 				      struct tls_connection *conn,
 				      const char *private_key,
 				      const char *private_key_passwd,
 				      const u8 *private_key_blob,
 				      size_t private_key_blob_len)
 {
-	SSL_CTX *ssl_ctx = _ssl_ctx;
+	SSL_CTX *ssl_ctx = data->ssl;
 	char *passwd;
 	int ok;
 
@@ -2385,7 +2656,7 @@
 			break;
 		}
 
-		if (tls_read_pkcs12_blob(ssl_ctx, conn->ssl, private_key_blob,
+		if (tls_read_pkcs12_blob(data, conn->ssl, private_key_blob,
 					 private_key_blob_len, passwd) == 0) {
 			wpa_printf(MSG_DEBUG, "OpenSSL: PKCS#12 as blob --> "
 				   "OK");
@@ -2418,7 +2689,7 @@
 			   __func__);
 #endif /* OPENSSL_NO_STDIO */
 
-		if (tls_read_pkcs12(ssl_ctx, conn->ssl, private_key, passwd)
+		if (tls_read_pkcs12(data, conn->ssl, private_key, passwd)
 		    == 0) {
 			wpa_printf(MSG_DEBUG, "OpenSSL: Reading PKCS#12 file "
 				   "--> OK");
@@ -2457,9 +2728,11 @@
 }
 
 
-static int tls_global_private_key(SSL_CTX *ssl_ctx, const char *private_key,
+static int tls_global_private_key(struct tls_data *data,
+				  const char *private_key,
 				  const char *private_key_passwd)
 {
+	SSL_CTX *ssl_ctx = data->ssl;
 	char *passwd;
 
 	if (private_key == NULL)
@@ -2481,7 +2754,7 @@
 	    SSL_CTX_use_PrivateKey_file(ssl_ctx, private_key,
 					SSL_FILETYPE_PEM) != 1 &&
 #endif /* OPENSSL_NO_STDIO */
-	    tls_read_pkcs12(ssl_ctx, NULL, private_key, passwd)) {
+	    tls_read_pkcs12(data, NULL, private_key, passwd)) {
 		tls_show_errors(MSG_INFO, __func__,
 				"Failed to load private key");
 		os_free(passwd);
@@ -2576,7 +2849,7 @@
 }
 
 
-static int tls_global_dh(SSL_CTX *ssl_ctx, const char *dh_file)
+static int tls_global_dh(struct tls_data *data, const char *dh_file)
 {
 #ifdef OPENSSL_NO_DH
 	if (dh_file == NULL)
@@ -2585,6 +2858,7 @@
 		   "dh_file specified");
 	return -1;
 #else /* OPENSSL_NO_DH */
+	SSL_CTX *ssl_ctx = data->ssl;
 	DH *dh;
 	BIO *bio;
 
@@ -2650,19 +2924,15 @@
 }
 
 
-int tls_connection_get_keys(void *ssl_ctx, struct tls_connection *conn,
-			    struct tls_keys *keys)
+int tls_connection_get_random(void *ssl_ctx, struct tls_connection *conn,
+			      struct tls_random *keys)
 {
-#ifdef CONFIG_FIPS
-	wpa_printf(MSG_ERROR, "OpenSSL: TLS keys cannot be exported in FIPS "
-		   "mode");
-	return -1;
-#else /* CONFIG_FIPS */
 	SSL *ssl;
 
 	if (conn == NULL || keys == NULL)
 		return -1;
 	ssl = conn->ssl;
+#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
 	if (ssl == NULL || ssl->s3 == NULL || ssl->session == NULL)
 		return -1;
 
@@ -2671,14 +2941,27 @@
 	keys->client_random_len = SSL3_RANDOM_SIZE;
 	keys->server_random = ssl->s3->server_random;
 	keys->server_random_len = SSL3_RANDOM_SIZE;
+#else
+	if (ssl == NULL)
+		return -1;
+
+	os_memset(keys, 0, sizeof(*keys));
+	keys->client_random = conn->client_random;
+	keys->client_random_len = SSL_get_client_random(
+		ssl, conn->client_random, sizeof(conn->client_random));
+	keys->server_random = conn->server_random;
+	keys->server_random_len = SSL_get_server_random(
+		ssl, conn->server_random, sizeof(conn->server_random));
+#endif
 
 	return 0;
-#endif /* CONFIG_FIPS */
 }
 
 
+#ifndef CONFIG_FIPS
 static int openssl_get_keyblock_size(SSL *ssl)
 {
+#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
 	const EVP_CIPHER *c;
 	const EVP_MD *h;
 	int md_size;
@@ -2708,10 +2991,38 @@
 	return 2 * (EVP_CIPHER_key_length(c) +
 		    md_size +
 		    EVP_CIPHER_iv_length(c));
+#else
+	const SSL_CIPHER *ssl_cipher;
+	int cipher, digest;
+	const EVP_CIPHER *c;
+	const EVP_MD *h;
+
+	ssl_cipher = SSL_get_current_cipher(ssl);
+	if (!ssl_cipher)
+		return -1;
+	cipher = SSL_CIPHER_get_cipher_nid(ssl_cipher);
+	digest = SSL_CIPHER_get_digest_nid(ssl_cipher);
+	wpa_printf(MSG_DEBUG, "OpenSSL: cipher nid %d digest nid %d",
+		   cipher, digest);
+	if (cipher < 0 || digest < 0)
+		return -1;
+	c = EVP_get_cipherbynid(cipher);
+	h = EVP_get_digestbynid(digest);
+	if (!c || !h)
+		return -1;
+
+	wpa_printf(MSG_DEBUG,
+		   "OpenSSL: keyblock size: key_len=%d MD_size=%d IV_len=%d",
+		   EVP_CIPHER_key_length(c), EVP_MD_size(h),
+		   EVP_CIPHER_iv_length(c));
+	return 2 * (EVP_CIPHER_key_length(c) + EVP_MD_size(h) +
+		    EVP_CIPHER_iv_length(c));
+#endif
 }
+#endif /* CONFIG_FIPS */
 
 
-static int openssl_tls_prf(void *tls_ctx, struct tls_connection *conn,
+static int openssl_tls_prf(struct tls_connection *conn,
 			   const char *label, int server_random_first,
 			   int skip_keyblock, u8 *out, size_t out_len)
 {
@@ -2720,12 +3031,14 @@
 		   "mode");
 	return -1;
 #else /* CONFIG_FIPS */
+#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
 	SSL *ssl;
 	u8 *rnd;
 	int ret = -1;
 	int skip = 0;
 	u8 *tmp_out = NULL;
 	u8 *_out = out;
+	const char *ver;
 
 	/*
 	 * TLS library did not support key generation, so get the needed TLS
@@ -2739,6 +3052,7 @@
 	if (ssl == NULL || ssl->s3 == NULL || ssl->session == NULL ||
 	    ssl->session->master_key_length <= 0)
 		return -1;
+	ver = SSL_get_version(ssl);
 
 	if (skip_keyblock) {
 		skip = openssl_get_keyblock_size(ssl);
@@ -2766,19 +3080,103 @@
 			SSL3_RANDOM_SIZE);
 	}
 
-	/* TODO: TLSv1.2 may need another PRF. This could use something closer
-	 * to SSL_export_keying_material() design. */
-	if (tls_prf_sha1_md5(ssl->session->master_key,
-			     ssl->session->master_key_length,
-			     label, rnd, 2 * SSL3_RANDOM_SIZE,
-			     _out, skip + out_len) == 0)
+	if (os_strcmp(ver, "TLSv1.2") == 0) {
+		tls_prf_sha256(ssl->session->master_key,
+			       ssl->session->master_key_length,
+			       label, rnd, 2 * SSL3_RANDOM_SIZE,
+			       _out, skip + out_len);
 		ret = 0;
+	} else if (tls_prf_sha1_md5(ssl->session->master_key,
+				    ssl->session->master_key_length,
+				    label, rnd, 2 * SSL3_RANDOM_SIZE,
+				    _out, skip + out_len) == 0) {
+		ret = 0;
+	}
 	os_free(rnd);
 	if (ret == 0 && skip_keyblock)
 		os_memcpy(out, _out + skip, out_len);
 	bin_clear_free(tmp_out, skip);
 
 	return ret;
+#else
+	SSL *ssl;
+	SSL_SESSION *sess;
+	u8 *rnd;
+	int ret = -1;
+	int skip = 0;
+	u8 *tmp_out = NULL;
+	u8 *_out = out;
+	unsigned char client_random[SSL3_RANDOM_SIZE];
+	unsigned char server_random[SSL3_RANDOM_SIZE];
+	unsigned char master_key[64];
+	size_t master_key_len;
+	const char *ver;
+
+	/*
+	 * TLS library did not support key generation, so get the needed TLS
+	 * session parameters and use an internal implementation of TLS PRF to
+	 * derive the key.
+	 */
+
+	if (conn == NULL)
+		return -1;
+	ssl = conn->ssl;
+	if (ssl == NULL)
+		return -1;
+	ver = SSL_get_version(ssl);
+	sess = SSL_get_session(ssl);
+	if (!ver || !sess)
+		return -1;
+
+	if (skip_keyblock) {
+		skip = openssl_get_keyblock_size(ssl);
+		if (skip < 0)
+			return -1;
+		tmp_out = os_malloc(skip + out_len);
+		if (!tmp_out)
+			return -1;
+		_out = tmp_out;
+	}
+
+	rnd = os_malloc(2 * SSL3_RANDOM_SIZE);
+	if (!rnd) {
+		os_free(tmp_out);
+		return -1;
+	}
+
+	SSL_get_client_random(ssl, client_random, sizeof(client_random));
+	SSL_get_server_random(ssl, server_random, sizeof(server_random));
+	master_key_len = SSL_SESSION_get_master_key(sess, master_key,
+						    sizeof(master_key));
+
+	if (server_random_first) {
+		os_memcpy(rnd, server_random, SSL3_RANDOM_SIZE);
+		os_memcpy(rnd + SSL3_RANDOM_SIZE, client_random,
+			  SSL3_RANDOM_SIZE);
+	} else {
+		os_memcpy(rnd, client_random, SSL3_RANDOM_SIZE);
+		os_memcpy(rnd + SSL3_RANDOM_SIZE, server_random,
+			  SSL3_RANDOM_SIZE);
+	}
+
+	if (os_strcmp(ver, "TLSv1.2") == 0) {
+		tls_prf_sha256(master_key, master_key_len,
+			       label, rnd, 2 * SSL3_RANDOM_SIZE,
+			       _out, skip + out_len);
+		ret = 0;
+	} else if (tls_prf_sha1_md5(master_key, master_key_len,
+				    label, rnd, 2 * SSL3_RANDOM_SIZE,
+				    _out, skip + out_len) == 0) {
+		ret = 0;
+	}
+	os_memset(master_key, 0, sizeof(master_key));
+	os_free(rnd);
+	if (ret == 0 && skip_keyblock)
+		os_memcpy(out, _out + skip, out_len);
+	bin_clear_free(tmp_out, skip);
+
+	return ret;
+#endif
 #endif /* CONFIG_FIPS */
 }
 
@@ -2792,7 +3190,7 @@
 	if (conn == NULL)
 		return -1;
 	if (server_random_first || skip_keyblock)
-		return openssl_tls_prf(tls_ctx, conn, label,
+		return openssl_tls_prf(conn, label,
 				       server_random_first, skip_keyblock,
 				       out, out_len);
 	ssl = conn->ssl;
@@ -2802,7 +3200,7 @@
 		return 0;
 	}
 #endif
-	return openssl_tls_prf(tls_ctx, conn, label, server_random_first,
+	return openssl_tls_prf(conn, label, server_random_first,
 			       skip_keyblock, out, out_len);
 }
 
@@ -2818,7 +3216,7 @@
 	 * Give TLS handshake data from the server (if available) to OpenSSL
 	 * for processing.
 	 */
-	if (in_data &&
+	if (in_data && wpabuf_len(in_data) > 0 &&
 	    BIO_write(conn->ssl_in, wpabuf_head(in_data), wpabuf_len(in_data))
 	    < 0) {
 		tls_show_errors(MSG_INFO, __func__,
@@ -2930,8 +3328,14 @@
 		return NULL;
 	}
 
-	if (SSL_is_init_finished(conn->ssl) && appl_data && in_data)
-		*appl_data = openssl_get_appl_data(conn, wpabuf_len(in_data));
+	if (SSL_is_init_finished(conn->ssl)) {
+		wpa_printf(MSG_DEBUG,
+			   "OpenSSL: Handshake finished - resumed=%d",
+			   tls_connection_resumed(conn->ssl_ctx, conn));
+		if (appl_data && in_data)
+			*appl_data = openssl_get_appl_data(conn,
+							   wpabuf_len(in_data));
+	}
 
 	if (conn->invalid_hb_used) {
 		wpa_printf(MSG_INFO, "TLS: Heartbeat attack detected - do not send response");
@@ -3110,6 +3514,21 @@
 
 	wpa_printf(MSG_DEBUG, "OpenSSL: cipher suites: %s", buf + 1);
 
+#if OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(LIBRESSL_VERSION_NUMBER)
+#if defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) || defined(EAP_SERVER_FAST)
+	if (os_strstr(buf, ":ADH-")) {
+		/*
+		 * Need to drop to security level 0 to allow anonymous
+		 * cipher suites for EAP-FAST.
+		 */
+		SSL_set_security_level(conn->ssl, 0);
+	} else if (SSL_get_security_level(conn->ssl) == 0) {
+		/* Force at least security level 1 */
+		SSL_set_security_level(conn->ssl, 1);
+	}
+#endif /* EAP_FAST || EAP_FAST_DYNAMIC || EAP_SERVER_FAST */
+#endif
+
 	if (SSL_set_cipher_list(conn->ssl, buf + 1) != 1) {
 		tls_show_errors(MSG_INFO, __func__,
 				"Cipher suite configuration failed");
@@ -3120,6 +3539,22 @@
 }
 
 
+int tls_get_version(void *ssl_ctx, struct tls_connection *conn,
+		    char *buf, size_t buflen)
+{
+	const char *name;
+	if (conn == NULL || conn->ssl == NULL)
+		return -1;
+
+	name = SSL_get_version(conn->ssl);
+	if (name == NULL)
+		return -1;
+
+	os_strlcpy(buf, name, buflen);
+	return 0;
+}
+
+
 int tls_get_cipher(void *ssl_ctx, struct tls_connection *conn,
 		   char *buf, size_t buflen)
 {
@@ -3442,6 +3877,7 @@
 int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn,
 			      const struct tls_connection_params *params)
 {
+	struct tls_data *data = tls_ctx;
 	int ret;
 	unsigned long err;
 	int can_pkcs11 = 0;
@@ -3483,6 +3919,8 @@
 	if (can_pkcs11 == 2 && !engine_id)
 		engine_id = "pkcs11";
 
+#if defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) || defined(EAP_SERVER_FAST)
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
 	if (params->flags & TLS_CONN_EAP_FAST) {
 		wpa_printf(MSG_DEBUG,
 			   "OpenSSL: Use TLSv1_method() for EAP-FAST");
@@ -3492,6 +3930,8 @@
 			return -1;
 		}
 	}
+#endif
+#endif /* EAP_FAST || EAP_FAST_DYNAMIC || EAP_SERVER_FAST */
 
 	while ((err = ERR_get_error())) {
 		wpa_printf(MSG_INFO, "%s: Clearing pending SSL error: %s",
@@ -3513,10 +3953,9 @@
 		return -1;
 
 	if (engine_id && ca_cert_id) {
-		if (tls_connection_engine_ca_cert(tls_ctx, conn,
-						  ca_cert_id))
+		if (tls_connection_engine_ca_cert(data, conn, ca_cert_id))
 			return TLS_SET_PARAMS_ENGINE_PRV_VERIFY_FAILED;
-	} else if (tls_connection_ca_cert(tls_ctx, conn, params->ca_cert,
+	} else if (tls_connection_ca_cert(data, conn, params->ca_cert,
 					  params->ca_cert_blob,
 					  params->ca_cert_blob_len,
 					  params->ca_path))
@@ -3534,7 +3973,7 @@
 		wpa_printf(MSG_DEBUG, "TLS: Using private key from engine");
 		if (tls_connection_engine_private_key(conn))
 			return TLS_SET_PARAMS_ENGINE_PRV_VERIFY_FAILED;
-	} else if (tls_connection_private_key(tls_ctx, conn,
+	} else if (tls_connection_private_key(data, conn,
 					      params->private_key,
 					      params->private_key_passwd,
 					      params->private_key_blob,
@@ -3558,40 +3997,36 @@
 		return -1;
 	}
 
-#ifdef SSL_OP_NO_TICKET
-	if (params->flags & TLS_CONN_DISABLE_SESSION_TICKET)
-		SSL_set_options(conn->ssl, SSL_OP_NO_TICKET);
-#ifdef SSL_clear_options
-	else
-		SSL_clear_options(conn->ssl, SSL_OP_NO_TICKET);
-#endif /* SSL_clear_options */
-#endif /*  SSL_OP_NO_TICKET */
+	tls_set_conn_flags(conn->ssl, params->flags);
 
-#ifdef SSL_OP_NO_TLSv1_1
-	if (params->flags & TLS_CONN_DISABLE_TLSv1_1)
-		SSL_set_options(conn->ssl, SSL_OP_NO_TLSv1_1);
-	else
-		SSL_clear_options(conn->ssl, SSL_OP_NO_TLSv1_1);
-#endif /* SSL_OP_NO_TLSv1_1 */
-#ifdef SSL_OP_NO_TLSv1_2
-	if (params->flags & TLS_CONN_DISABLE_TLSv1_2)
-		SSL_set_options(conn->ssl, SSL_OP_NO_TLSv1_2);
-	else
-		SSL_clear_options(conn->ssl, SSL_OP_NO_TLSv1_2);
-#endif /* SSL_OP_NO_TLSv1_2 */
-
+#ifdef OPENSSL_IS_BORINGSSL
+	if (params->flags & TLS_CONN_REQUEST_OCSP) {
+		SSL_enable_ocsp_stapling(conn->ssl);
+	}
+#else /* OPENSSL_IS_BORINGSSL */
 #ifdef HAVE_OCSP
 	if (params->flags & TLS_CONN_REQUEST_OCSP) {
-		SSL_CTX *ssl_ctx = tls_ctx;
+		SSL_CTX *ssl_ctx = data->ssl;
 		SSL_set_tlsext_status_type(conn->ssl, TLSEXT_STATUSTYPE_ocsp);
 		SSL_CTX_set_tlsext_status_cb(ssl_ctx, ocsp_resp_cb);
 		SSL_CTX_set_tlsext_status_arg(ssl_ctx, conn);
 	}
+#else /* HAVE_OCSP */
+	if (params->flags & TLS_CONN_REQUIRE_OCSP) {
+		wpa_printf(MSG_INFO,
+			   "OpenSSL: No OCSP support included - reject configuration");
+		return -1;
+	}
+	if (params->flags & TLS_CONN_REQUEST_OCSP) {
+		wpa_printf(MSG_DEBUG,
+			   "OpenSSL: No OCSP support included - allow optional OCSP case to continue");
+	}
 #endif /* HAVE_OCSP */
+#endif /* OPENSSL_IS_BORINGSSL */
 
 	conn->flags = params->flags;
 
-	tls_get_errors(tls_ctx);
+	tls_get_errors(data);
 
 	return 0;
 }
@@ -3600,7 +4035,8 @@
 int tls_global_set_params(void *tls_ctx,
 			  const struct tls_connection_params *params)
 {
-	SSL_CTX *ssl_ctx = tls_ctx;
+	struct tls_data *data = tls_ctx;
+	SSL_CTX *ssl_ctx = data->ssl;
 	unsigned long err;
 
 	while ((err = ERR_get_error())) {
@@ -3608,19 +4044,12 @@
 			   __func__, ERR_error_string(err, NULL));
 	}
 
-	if (tls_global_ca_cert(ssl_ctx, params->ca_cert))
-		return -1;
-
-	if (tls_global_client_cert(ssl_ctx, params->client_cert))
-		return -1;
-
-	if (tls_global_private_key(ssl_ctx, params->private_key,
-				   params->private_key_passwd))
-		return -1;
-
-	if (tls_global_dh(ssl_ctx, params->dh_file)) {
-		wpa_printf(MSG_INFO, "TLS: Failed to load DH file '%s'",
-			   params->dh_file);
+	if (tls_global_ca_cert(data, params->ca_cert) ||
+	    tls_global_client_cert(data, params->client_cert) ||
+	    tls_global_private_key(data, params->private_key,
+				   params->private_key_passwd) ||
+	    tls_global_dh(data, params->dh_file)) {
+		wpa_printf(MSG_INFO, "TLS: Failed to set global parameters");
 		return -1;
 	}
 
@@ -3656,12 +4085,6 @@
 }
 
 
-unsigned int tls_capabilities(void *tls_ctx)
-{
-	return 0;
-}
-
-
 #if defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) || defined(EAP_SERVER_FAST)
 /* Pre-shared secred requires a patch to openssl, so this function is
  * commented out unless explicitly needed for EAP-FAST in order to be able to
@@ -3680,6 +4103,7 @@
 	struct tls_connection *conn = arg;
 	int ret;
 
+#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
 	if (conn == NULL || conn->session_ticket_cb == NULL)
 		return 0;
 
@@ -3688,6 +4112,23 @@
 				      conn->session_ticket_len,
 				      s->s3->client_random,
 				      s->s3->server_random, secret);
+#else
+	unsigned char client_random[SSL3_RANDOM_SIZE];
+	unsigned char server_random[SSL3_RANDOM_SIZE];
+
+	if (conn == NULL || conn->session_ticket_cb == NULL)
+		return 0;
+
+	SSL_get_client_random(s, client_random, sizeof(client_random));
+	SSL_get_server_random(s, server_random, sizeof(server_random));
+
+	ret = conn->session_ticket_cb(conn->session_ticket_cb_ctx,
+				      conn->session_ticket,
+				      conn->session_ticket_len,
+				      client_random,
+				      server_random, secret);
+#endif
+
 	os_free(conn->session_ticket);
 	conn->session_ticket = NULL;
 
@@ -3757,7 +4198,80 @@
 
 int tls_get_library_version(char *buf, size_t buf_len)
 {
+#if OPENSSL_VERSION_NUMBER >= 0x10100000L
+	return os_snprintf(buf, buf_len, "OpenSSL build=%s run=%s",
+			   OPENSSL_VERSION_TEXT,
+			   OpenSSL_version(OPENSSL_VERSION));
+#else
 	return os_snprintf(buf, buf_len, "OpenSSL build=%s run=%s",
 			   OPENSSL_VERSION_TEXT,
 			   SSLeay_version(SSLEAY_VERSION));
+#endif
+}
+
+
+void tls_connection_set_success_data(struct tls_connection *conn,
+				     struct wpabuf *data)
+{
+	SSL_SESSION *sess;
+	struct wpabuf *old;
+
+	if (tls_ex_idx_session < 0)
+		goto fail;
+	sess = SSL_get_session(conn->ssl);
+	if (!sess)
+		goto fail;
+	old = SSL_SESSION_get_ex_data(sess, tls_ex_idx_session);
+	if (old) {
+		wpa_printf(MSG_DEBUG, "OpenSSL: Replacing old success data %p",
+			   old);
+		wpabuf_free(old);
+	}
+	if (SSL_SESSION_set_ex_data(sess, tls_ex_idx_session, data) != 1)
+		goto fail;
+
+	wpa_printf(MSG_DEBUG, "OpenSSL: Stored success data %p", data);
+	conn->success_data = 1;
+	return;
+
+fail:
+	wpa_printf(MSG_INFO, "OpenSSL: Failed to store success data");
+	wpabuf_free(data);
+}
+
+
+void tls_connection_set_success_data_resumed(struct tls_connection *conn)
+{
+	wpa_printf(MSG_DEBUG,
+		   "OpenSSL: Success data accepted for resumed session");
+	conn->success_data = 1;
+}
+
+
+const struct wpabuf *
+tls_connection_get_success_data(struct tls_connection *conn)
+{
+	SSL_SESSION *sess;
+
+	if (tls_ex_idx_session < 0 ||
+	    !(sess = SSL_get_session(conn->ssl)))
+		return NULL;
+	return SSL_SESSION_get_ex_data(sess, tls_ex_idx_session);
+}
+
+
+void tls_connection_remove_session(struct tls_connection *conn)
+{
+	SSL_SESSION *sess;
+
+	sess = SSL_get_session(conn->ssl);
+	if (!sess)
+		return;
+
+	if (SSL_CTX_remove_session(conn->ssl_ctx, sess) != 1)
+		wpa_printf(MSG_DEBUG,
+			   "OpenSSL: Session was not cached");
+	else
+		wpa_printf(MSG_DEBUG,
+			   "OpenSSL: Removed cached session to disable session resumption");
 }
diff --git a/src/crypto/tls_openssl.h b/src/crypto/tls_openssl.h
new file mode 100644
index 0000000..2a62d5c
--- /dev/null
+++ b/src/crypto/tls_openssl.h
@@ -0,0 +1,19 @@
+/*
+ * SSL/TLS interface functions for OpenSSL
+ * Copyright (c) 2004-2015, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef TLS_OPENSSL_H
+#define TLS_OPENSSL_H
+
+enum ocsp_result {
+	OCSP_GOOD, OCSP_REVOKED, OCSP_NO_RESPONSE, OCSP_INVALID
+};
+
+enum ocsp_result check_ocsp_resp(SSL_CTX *ssl_ctx, SSL *ssl, X509 *cert,
+				 X509 *issuer, X509 *issuer_issuer);
+
+#endif /* TLS_OPENSSL_H */
diff --git a/src/crypto/tls_openssl_ocsp.c b/src/crypto/tls_openssl_ocsp.c
new file mode 100644
index 0000000..37c87f4
--- /dev/null
+++ b/src/crypto/tls_openssl_ocsp.c
@@ -0,0 +1,843 @@
+/*
+ * SSL/TLS interface functions for OpenSSL - BoringSSL OCSP
+ * Copyright (c) 2004-2015, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include <openssl/ssl.h>
+#include <openssl/err.h>
+#include <openssl/x509v3.h>
+#ifdef OPENSSL_IS_BORINGSSL
+#include <openssl/asn1.h>
+#include <openssl/asn1t.h>
+#endif /* OPENSSL_IS_BORINGSSL */
+
+#include "common.h"
+#include "tls_openssl.h"
+
+
+#ifdef OPENSSL_IS_BORINGSSL
+
+static void tls_show_errors(int level, const char *func, const char *txt)
+{
+	unsigned long err;
+
+	wpa_printf(level, "OpenSSL: %s - %s %s",
+		   func, txt, ERR_error_string(ERR_get_error(), NULL));
+
+	while ((err = ERR_get_error())) {
+		wpa_printf(MSG_INFO, "OpenSSL: pending error: %s",
+			   ERR_error_string(err, NULL));
+	}
+}
+
+
+/*
+ * CertID ::= SEQUENCE {
+ *     hashAlgorithm      AlgorithmIdentifier,
+ *     issuerNameHash     OCTET STRING, -- Hash of Issuer's DN
+ *     issuerKeyHash      OCTET STRING, -- Hash of Issuer's public key
+ *     serialNumber       CertificateSerialNumber }
+ */
+typedef struct {
+	X509_ALGOR *hashAlgorithm;
+	ASN1_OCTET_STRING *issuerNameHash;
+	ASN1_OCTET_STRING *issuerKeyHash;
+	ASN1_INTEGER *serialNumber;
+} CertID;
+
+/*
+ * ResponseBytes ::=       SEQUENCE {
+ *     responseType   OBJECT IDENTIFIER,
+ *     response       OCTET STRING }
+ */
+typedef struct {
+	ASN1_OBJECT *responseType;
+	ASN1_OCTET_STRING *response;
+} ResponseBytes;
+
+/*
+ * OCSPResponse ::= SEQUENCE {
+ *    responseStatus         OCSPResponseStatus,
+ *    responseBytes          [0] EXPLICIT ResponseBytes OPTIONAL }
+ */
+typedef struct {
+	ASN1_ENUMERATED *responseStatus;
+	ResponseBytes *responseBytes;
+} OCSPResponse;
+
+ASN1_SEQUENCE(ResponseBytes) = {
+	ASN1_SIMPLE(ResponseBytes, responseType, ASN1_OBJECT),
+	ASN1_SIMPLE(ResponseBytes, response, ASN1_OCTET_STRING)
+} ASN1_SEQUENCE_END(ResponseBytes);
+
+ASN1_SEQUENCE(OCSPResponse) = {
+	ASN1_SIMPLE(OCSPResponse, responseStatus, ASN1_ENUMERATED),
+	ASN1_EXP_OPT(OCSPResponse, responseBytes, ResponseBytes, 0)
+} ASN1_SEQUENCE_END(OCSPResponse);
+
+IMPLEMENT_ASN1_FUNCTIONS(OCSPResponse);
+
+/*
+ * ResponderID ::= CHOICE {
+ *    byName               [1] Name,
+ *    byKey                [2] KeyHash }
+ */
+typedef struct {
+	int type;
+	union {
+		X509_NAME *byName;
+		ASN1_OCTET_STRING *byKey;
+	} value;
+} ResponderID;
+
+/*
+ * RevokedInfo ::= SEQUENCE {
+ *     revocationTime              GeneralizedTime,
+ *     revocationReason    [0]     EXPLICIT CRLReason OPTIONAL }
+ */
+typedef struct {
+	ASN1_GENERALIZEDTIME *revocationTime;
+	ASN1_ENUMERATED *revocationReason;
+} RevokedInfo;
+
+/*
+ * CertStatus ::= CHOICE {
+ *     good        [0]     IMPLICIT NULL,
+ *     revoked     [1]     IMPLICIT RevokedInfo,
+ *     unknown     [2]     IMPLICIT UnknownInfo }
+ */
+typedef struct {
+	int type;
+	union {
+		ASN1_NULL *good;
+		RevokedInfo *revoked;
+		ASN1_NULL *unknown;
+	} value;
+} CertStatus;
+
+/*
+ * SingleResponse ::= SEQUENCE {
+ *    certID                       CertID,
+ *    certStatus                   CertStatus,
+ *    thisUpdate                   GeneralizedTime,
+ *    nextUpdate         [0]       EXPLICIT GeneralizedTime OPTIONAL,
+ *    singleExtensions   [1]       EXPLICIT Extensions OPTIONAL }
+ */
+typedef struct {
+	CertID *certID;
+	CertStatus *certStatus;
+	ASN1_GENERALIZEDTIME *thisUpdate;
+	ASN1_GENERALIZEDTIME *nextUpdate;
+	STACK_OF(X509_EXTENSION) *singleExtensions;
+} SingleResponse;
+
+/*
+ * ResponseData ::= SEQUENCE {
+ *   version              [0] EXPLICIT Version DEFAULT v1,
+ *   responderID              ResponderID,
+ *   producedAt               GeneralizedTime,
+ *   responses                SEQUENCE OF SingleResponse,
+ *   responseExtensions   [1] EXPLICIT Extensions OPTIONAL }
+ */
+typedef struct {
+	ASN1_INTEGER *version;
+	ResponderID *responderID;
+	ASN1_GENERALIZEDTIME *producedAt;
+	STACK_OF(SingleResponse) *responses;
+	STACK_OF(X509_EXTENSION) *responseExtensions;
+} ResponseData;
+
+/*
+ * BasicOCSPResponse       ::= SEQUENCE {
+ *   tbsResponseData      ResponseData,
+ *   signatureAlgorithm   AlgorithmIdentifier,
+ *   signature            BIT STRING,
+ *   certs                [0] EXPLICIT SEQUENCE OF Certificate OPTIONAL }
+ */
+typedef struct {
+	ResponseData *tbsResponseData;
+	X509_ALGOR *signatureAlgorithm;
+	ASN1_BIT_STRING *signature;
+	STACK_OF(X509) *certs;
+} BasicOCSPResponse;
+
+ASN1_SEQUENCE(CertID) = {
+	ASN1_SIMPLE(CertID, hashAlgorithm, X509_ALGOR),
+	ASN1_SIMPLE(CertID, issuerNameHash, ASN1_OCTET_STRING),
+	ASN1_SIMPLE(CertID, issuerKeyHash, ASN1_OCTET_STRING),
+	ASN1_SIMPLE(CertID, serialNumber, ASN1_INTEGER)
+} ASN1_SEQUENCE_END(CertID);
+
+ASN1_CHOICE(ResponderID) = {
+	ASN1_EXP(ResponderID, value.byName, X509_NAME, 1),
+	ASN1_EXP(ResponderID, value.byKey, ASN1_OCTET_STRING, 2)
+} ASN1_CHOICE_END(ResponderID);
+
+ASN1_SEQUENCE(RevokedInfo) = {
+	ASN1_SIMPLE(RevokedInfo, revocationTime, ASN1_GENERALIZEDTIME),
+	ASN1_EXP_OPT(RevokedInfo, revocationReason, ASN1_ENUMERATED, 0)
+} ASN1_SEQUENCE_END(RevokedInfo);
+
+ASN1_CHOICE(CertStatus) = {
+	ASN1_IMP(CertStatus, value.good, ASN1_NULL, 0),
+	ASN1_IMP(CertStatus, value.revoked, RevokedInfo, 1),
+	ASN1_IMP(CertStatus, value.unknown, ASN1_NULL, 2)
+} ASN1_CHOICE_END(CertStatus);
+
+ASN1_SEQUENCE(SingleResponse) = {
+	ASN1_SIMPLE(SingleResponse, certID, CertID),
+	ASN1_SIMPLE(SingleResponse, certStatus, CertStatus),
+	ASN1_SIMPLE(SingleResponse, thisUpdate, ASN1_GENERALIZEDTIME),
+	ASN1_EXP_OPT(SingleResponse, nextUpdate, ASN1_GENERALIZEDTIME, 0),
+	ASN1_EXP_SEQUENCE_OF_OPT(SingleResponse, singleExtensions,
+				 X509_EXTENSION, 1)
+} ASN1_SEQUENCE_END(SingleResponse);
+
+ASN1_SEQUENCE(ResponseData) = {
+	ASN1_EXP_OPT(ResponseData, version, ASN1_INTEGER, 0),
+	ASN1_SIMPLE(ResponseData, responderID, ResponderID),
+	ASN1_SIMPLE(ResponseData, producedAt, ASN1_GENERALIZEDTIME),
+	ASN1_SEQUENCE_OF(ResponseData, responses, SingleResponse),
+	ASN1_EXP_SEQUENCE_OF_OPT(ResponseData, responseExtensions,
+				 X509_EXTENSION, 1)
+} ASN1_SEQUENCE_END(ResponseData);
+
+ASN1_SEQUENCE(BasicOCSPResponse) = {
+	ASN1_SIMPLE(BasicOCSPResponse, tbsResponseData, ResponseData),
+	ASN1_SIMPLE(BasicOCSPResponse, signatureAlgorithm, X509_ALGOR),
+	ASN1_SIMPLE(BasicOCSPResponse, signature, ASN1_BIT_STRING),
+	ASN1_EXP_SEQUENCE_OF_OPT(BasicOCSPResponse, certs, X509, 0)
+} ASN1_SEQUENCE_END(BasicOCSPResponse);
+
+IMPLEMENT_ASN1_FUNCTIONS(BasicOCSPResponse);
+
+#define sk_SingleResponse_num(sk) \
+sk_num(CHECKED_CAST(_STACK *, STACK_OF(SingleResponse) *, sk))
+
+#define sk_SingleResponse_value(sk, i) \
+	((SingleResponse *)						\
+	 sk_value(CHECKED_CAST(_STACK *, STACK_OF(SingleResponse) *, sk), (i)))
+
+
+static char * mem_bio_to_str(BIO *out)
+{
+	char *txt;
+	size_t rlen;
+	int res;
+
+	rlen = BIO_ctrl_pending(out);
+	txt = os_malloc(rlen + 1);
+	if (!txt) {
+		BIO_free(out);
+		return NULL;
+	}
+
+	res = BIO_read(out, txt, rlen);
+	BIO_free(out);
+	if (res < 0) {
+		os_free(txt);
+		return NULL;
+	}
+
+	txt[res] = '\0';
+	return txt;
+}
+
+
+static char * generalizedtime_str(ASN1_GENERALIZEDTIME *t)
+{
+	BIO *out;
+
+	out = BIO_new(BIO_s_mem());
+	if (!out)
+		return NULL;
+
+	if (!ASN1_GENERALIZEDTIME_print(out, t)) {
+		BIO_free(out);
+		return NULL;
+	}
+
+	return mem_bio_to_str(out);
+}
+
+
+static char * responderid_str(ResponderID *rid)
+{
+	BIO *out;
+
+	out = BIO_new(BIO_s_mem());
+	if (!out)
+		return NULL;
+
+	switch (rid->type) {
+	case 0:
+		X509_NAME_print_ex(out, rid->value.byName, 0, XN_FLAG_ONELINE);
+		break;
+	case 1:
+		i2a_ASN1_STRING(out, rid->value.byKey, V_ASN1_OCTET_STRING);
+		break;
+	default:
+		BIO_free(out);
+		return NULL;
+	}
+
+	return mem_bio_to_str(out);
+}
+
+
+static char * octet_string_str(ASN1_OCTET_STRING *o)
+{
+	BIO *out;
+
+	out = BIO_new(BIO_s_mem());
+	if (!out)
+		return NULL;
+
+	i2a_ASN1_STRING(out, o, V_ASN1_OCTET_STRING);
+	return mem_bio_to_str(out);
+}
+
+
+static char * integer_str(ASN1_INTEGER *i)
+{
+	BIO *out;
+
+	out = BIO_new(BIO_s_mem());
+	if (!out)
+		return NULL;
+
+	i2a_ASN1_INTEGER(out, i);
+	return mem_bio_to_str(out);
+}
+
+
+static char * algor_str(X509_ALGOR *alg)
+{
+	BIO *out;
+
+	out = BIO_new(BIO_s_mem());
+	if (!out)
+		return NULL;
+
+	i2a_ASN1_OBJECT(out, alg->algorithm);
+	return mem_bio_to_str(out);
+}
+
+
+static char * extensions_str(const char *title, STACK_OF(X509_EXTENSION) *ext)
+{
+	BIO *out;
+
+	if (!ext)
+		return NULL;
+
+	out = BIO_new(BIO_s_mem());
+	if (!out)
+		return NULL;
+
+	if (!X509V3_extensions_print(out, title, ext, 0, 0)) {
+		BIO_free(out);
+		return NULL;
+	}
+	return mem_bio_to_str(out);
+}
+
+
+static int ocsp_resp_valid(ASN1_GENERALIZEDTIME *thisupd,
+			   ASN1_GENERALIZEDTIME *nextupd)
+{
+	time_t now, tmp;
+
+	if (!ASN1_GENERALIZEDTIME_check(thisupd)) {
+		wpa_printf(MSG_DEBUG,
+			   "OpenSSL: Invalid OCSP response thisUpdate");
+		return 0;
+	}
+
+	time(&now);
+	tmp = now + 5 * 60; /* allow five minute clock difference */
+	if (X509_cmp_time(thisupd, &tmp) > 0) {
+		wpa_printf(MSG_DEBUG, "OpenSSL: OCSP response not yet valid");
+		return 0;
+	}
+
+	if (!nextupd)
+		return 1; /* OK - no limit on response age */
+
+	if (!ASN1_GENERALIZEDTIME_check(nextupd)) {
+		wpa_printf(MSG_DEBUG,
+			   "OpenSSL: Invalid OCSP response nextUpdate");
+		return 0;
+	}
+
+	tmp = now - 5 * 60; /* allow five minute clock difference */
+	if (X509_cmp_time(nextupd, &tmp) < 0) {
+		wpa_printf(MSG_DEBUG, "OpenSSL: OCSP response expired");
+		return 0;
+	}
+
+	if (ASN1_STRING_cmp(nextupd, thisupd) < 0) {
+		wpa_printf(MSG_DEBUG,
+			   "OpenSSL: OCSP response nextUpdate before thisUpdate");
+		return 0;
+	}
+
+	/* Both thisUpdate and nextUpdate are valid */
+	return -1;
+}
+
+
+static int issuer_match(X509 *cert, X509 *issuer, CertID *certid)
+{
+	X509_NAME *iname;
+	ASN1_BIT_STRING *ikey;
+	const EVP_MD *dgst;
+	unsigned int len;
+	unsigned char md[EVP_MAX_MD_SIZE];
+	ASN1_OCTET_STRING *hash;
+	char *txt;
+
+	dgst = EVP_get_digestbyobj(certid->hashAlgorithm->algorithm);
+	if (!dgst) {
+		wpa_printf(MSG_DEBUG,
+			   "OpenSSL: Could not find matching hash algorithm for OCSP");
+		return -1;
+	}
+
+	iname = X509_get_issuer_name(cert);
+	if (!X509_NAME_digest(iname, dgst, md, &len))
+		return -1;
+	hash = ASN1_OCTET_STRING_new();
+	if (!hash)
+		return -1;
+	if (!ASN1_OCTET_STRING_set(hash, md, len)) {
+		ASN1_OCTET_STRING_free(hash);
+		return -1;
+	}
+
+	txt = octet_string_str(hash);
+	if (txt) {
+		wpa_printf(MSG_DEBUG, "OpenSSL: calculated issuerNameHash: %s",
+			   txt);
+		os_free(txt);
+	}
+
+	if (ASN1_OCTET_STRING_cmp(certid->issuerNameHash, hash)) {
+		ASN1_OCTET_STRING_free(hash);
+		return -1;
+	}
+
+	ikey = X509_get0_pubkey_bitstr(issuer);
+	if (!EVP_Digest(ikey->data, ikey->length, md, &len, dgst, NULL) ||
+	    !ASN1_OCTET_STRING_set(hash, md, len)) {
+		ASN1_OCTET_STRING_free(hash);
+		return -1;
+	}
+
+	txt = octet_string_str(hash);
+	if (txt) {
+		wpa_printf(MSG_DEBUG, "OpenSSL: calculated issuerKeyHash: %s",
+			   txt);
+		os_free(txt);
+	}
+
+	if (ASN1_OCTET_STRING_cmp(certid->issuerKeyHash, hash)) {
+		ASN1_OCTET_STRING_free(hash);
+		return -1;
+	}
+
+	ASN1_OCTET_STRING_free(hash);
+	return 0;
+}
+
+
+static X509 * ocsp_find_signer(STACK_OF(X509) *certs, ResponderID *rid)
+{
+	unsigned int i;
+	unsigned char hash[SHA_DIGEST_LENGTH];
+
+	if (rid->type == 0) {
+		/* byName */
+		return X509_find_by_subject(certs, rid->value.byName);
+	}
+
+	/* byKey */
+	if (rid->value.byKey->length != SHA_DIGEST_LENGTH)
+		return NULL;
+	for (i = 0; i < sk_X509_num(certs); i++) {
+		X509 *x = sk_X509_value(certs, i);
+
+		X509_pubkey_digest(x, EVP_sha1(), hash, NULL);
+		if (os_memcmp(rid->value.byKey->data, hash,
+			      SHA_DIGEST_LENGTH) == 0)
+			return x;
+	}
+
+	return NULL;
+}
+
+
+enum ocsp_result check_ocsp_resp(SSL_CTX *ssl_ctx, SSL *ssl, X509 *cert,
+				 X509 *issuer, X509 *issuer_issuer)
+{
+	const uint8_t *resp_data;
+	size_t resp_len;
+	OCSPResponse *resp;
+	int status;
+	ResponseBytes *bytes;
+	const u8 *basic_data;
+	size_t basic_len;
+	BasicOCSPResponse *basic;
+	ResponseData *rd;
+	char *txt;
+	int i, num;
+	unsigned int j, num_resp;
+	SingleResponse *matching_resp = NULL, *cmp_sresp;
+	enum ocsp_result result = OCSP_INVALID;
+	X509_STORE *store;
+	STACK_OF(X509) *untrusted = NULL, *certs = NULL, *chain = NULL;
+	X509_STORE_CTX ctx;
+	X509 *signer, *tmp_cert;
+	int signer_trusted = 0;
+	EVP_PKEY *skey;
+	int ret;
+	char buf[256];
+
+	txt = integer_str(X509_get_serialNumber(cert));
+	if (txt) {
+		wpa_printf(MSG_DEBUG,
+			   "OpenSSL: Searching OCSP response for peer certificate serialNumber: %s", txt);
+		os_free(txt);
+	}
+
+	SSL_get0_ocsp_response(ssl, &resp_data, &resp_len);
+	if (resp_data == NULL || resp_len == 0) {
+		wpa_printf(MSG_DEBUG, "OpenSSL: No OCSP response received");
+		return OCSP_NO_RESPONSE;
+	}
+
+	wpa_hexdump(MSG_DEBUG, "OpenSSL: OCSP response", resp_data, resp_len);
+
+	resp = d2i_OCSPResponse(NULL, &resp_data, resp_len);
+	if (!resp) {
+		wpa_printf(MSG_INFO, "OpenSSL: Failed to parse OCSPResponse");
+		return OCSP_INVALID;
+	}
+
+	status = ASN1_ENUMERATED_get(resp->responseStatus);
+	if (status != 0) {
+		wpa_printf(MSG_INFO, "OpenSSL: OCSP responder error %d",
+			   status);
+		return OCSP_INVALID;
+	}
+
+	bytes = resp->responseBytes;
+
+	if (!bytes ||
+	    OBJ_obj2nid(bytes->responseType) != NID_id_pkix_OCSP_basic) {
+		wpa_printf(MSG_INFO,
+			   "OpenSSL: Could not find BasicOCSPResponse");
+		return OCSP_INVALID;
+	}
+
+	basic_data = ASN1_STRING_data(bytes->response);
+	basic_len = ASN1_STRING_length(bytes->response);
+	wpa_hexdump(MSG_DEBUG, "OpenSSL: BasicOCSPResponse",
+		    basic_data, basic_len);
+
+	basic = d2i_BasicOCSPResponse(NULL, &basic_data, basic_len);
+	if (!basic) {
+		wpa_printf(MSG_INFO,
+			   "OpenSSL: Could not parse BasicOCSPResponse");
+		OCSPResponse_free(resp);
+		return OCSP_INVALID;
+	}
+
+	rd = basic->tbsResponseData;
+
+	if (basic->certs) {
+		untrusted = sk_X509_dup(basic->certs);
+
+		num = sk_X509_num(basic->certs);
+		for (i = 0; i < num; i++) {
+			X509 *extra_cert;
+
+			extra_cert = sk_X509_value(basic->certs, i);
+			X509_NAME_oneline(X509_get_subject_name(extra_cert),
+					  buf, sizeof(buf));
+			wpa_printf(MSG_DEBUG,
+				   "OpenSSL: BasicOCSPResponse cert %s", buf);
+
+			if (!sk_X509_push(untrusted, extra_cert)) {
+				wpa_printf(MSG_DEBUG,
+					   "OpenSSL: Could not add certificate to the untrusted stack");
+			}
+		}
+	}
+
+	store = SSL_CTX_get_cert_store(ssl_ctx);
+	if (issuer) {
+		if (X509_STORE_add_cert(store, issuer) != 1) {
+			tls_show_errors(MSG_INFO, __func__,
+					"OpenSSL: Could not add issuer to certificate store");
+		}
+		certs = sk_X509_new_null();
+		if (certs) {
+			tmp_cert = X509_dup(issuer);
+			if (tmp_cert && !sk_X509_push(certs, tmp_cert)) {
+				tls_show_errors(
+					MSG_INFO, __func__,
+					"OpenSSL: Could not add issuer to OCSP responder trust store");
+				X509_free(tmp_cert);
+				sk_X509_free(certs);
+				certs = NULL;
+			}
+			if (certs && issuer_issuer) {
+				tmp_cert = X509_dup(issuer_issuer);
+				if (tmp_cert &&
+				    !sk_X509_push(certs, tmp_cert)) {
+					tls_show_errors(
+						MSG_INFO, __func__,
+						"OpenSSL: Could not add issuer's issuer to OCSP responder trust store");
+					X509_free(tmp_cert);
+				}
+			}
+		}
+	}
+
+	signer = ocsp_find_signer(certs, rd->responderID);
+	if (!signer)
+		signer = ocsp_find_signer(untrusted, rd->responderID);
+	else
+		signer_trusted = 1;
+	if (!signer) {
+		wpa_printf(MSG_DEBUG,
+			   "OpenSSL: Could not find OCSP signer certificate");
+		goto fail;
+	}
+
+	skey = X509_get_pubkey(signer);
+	if (!skey) {
+		wpa_printf(MSG_DEBUG,
+			   "OpenSSL: Could not get OCSP signer public key");
+		goto fail;
+	}
+	if (ASN1_item_verify(ASN1_ITEM_rptr(ResponseData),
+			     basic->signatureAlgorithm, basic->signature,
+			     basic->tbsResponseData, skey) <= 0) {
+		wpa_printf(MSG_DEBUG,
+			   "OpenSSL: BasicOCSPResponse signature is invalid");
+		goto fail;
+	}
+
+	X509_NAME_oneline(X509_get_subject_name(signer), buf, sizeof(buf));
+	wpa_printf(MSG_DEBUG,
+		   "OpenSSL: Found OCSP signer certificate %s and verified BasicOCSPResponse signature",
+		   buf);
+
+	if (!X509_STORE_CTX_init(&ctx, store, signer, untrusted))
+		goto fail;
+	X509_STORE_CTX_set_purpose(&ctx, X509_PURPOSE_OCSP_HELPER);
+	ret = X509_verify_cert(&ctx);
+	chain = X509_STORE_CTX_get1_chain(&ctx);
+	X509_STORE_CTX_cleanup(&ctx);
+	if (ret <= 0) {
+		wpa_printf(MSG_DEBUG,
+			   "OpenSSL: Could not validate OCSP signer certificate");
+		goto fail;
+	}
+
+	if (!chain || sk_X509_num(chain) <= 0) {
+		wpa_printf(MSG_DEBUG, "OpenSSL: No OCSP signer chain found");
+		goto fail;
+	}
+
+	if (!signer_trusted) {
+		X509_check_purpose(signer, -1, 0);
+		if ((signer->ex_flags & EXFLAG_XKUSAGE) &&
+		    (signer->ex_xkusage & XKU_OCSP_SIGN)) {
+			wpa_printf(MSG_DEBUG,
+				   "OpenSSL: OCSP signer certificate delegation OK");
+		} else {
+			tmp_cert = sk_X509_value(chain, sk_X509_num(chain) - 1);
+			if (X509_check_trust(tmp_cert, NID_OCSP_sign, 0) !=
+			    X509_TRUST_TRUSTED) {
+				wpa_printf(MSG_DEBUG,
+					   "OpenSSL: OCSP signer certificate not trusted");
+				result = OCSP_NO_RESPONSE;
+				goto fail;
+			}
+		}
+	}
+
+	wpa_printf(MSG_DEBUG, "OpenSSL: OCSP version: %lu",
+		   ASN1_INTEGER_get(rd->version));
+
+	txt = responderid_str(rd->responderID);
+	if (txt) {
+		wpa_printf(MSG_DEBUG, "OpenSSL: OCSP responderID: %s",
+			   txt);
+		os_free(txt);
+	}
+
+	txt = generalizedtime_str(rd->producedAt);
+	if (txt) {
+		wpa_printf(MSG_DEBUG, "OpenSSL: OCSP producedAt: %s",
+			   txt);
+		os_free(txt);
+	}
+
+	num_resp = sk_SingleResponse_num(rd->responses);
+	if (num_resp == 0) {
+		wpa_printf(MSG_DEBUG,
+			   "OpenSSL: No OCSP SingleResponse within BasicOCSPResponse");
+		result = OCSP_NO_RESPONSE;
+		goto fail;
+	}
+	cmp_sresp = sk_SingleResponse_value(rd->responses, 0);
+	for (j = 0; j < num_resp; j++) {
+		SingleResponse *sresp;
+		CertID *cid1, *cid2;
+
+		sresp = sk_SingleResponse_value(rd->responses, j);
+		wpa_printf(MSG_DEBUG, "OpenSSL: OCSP SingleResponse %u/%u",
+			   j + 1, num_resp);
+
+		txt = algor_str(sresp->certID->hashAlgorithm);
+		if (txt) {
+			wpa_printf(MSG_DEBUG,
+				   "OpenSSL: certID hashAlgorithm: %s", txt);
+			os_free(txt);
+		}
+
+		txt = octet_string_str(sresp->certID->issuerNameHash);
+		if (txt) {
+			wpa_printf(MSG_DEBUG,
+				   "OpenSSL: certID issuerNameHash: %s", txt);
+			os_free(txt);
+		}
+
+		txt = octet_string_str(sresp->certID->issuerKeyHash);
+		if (txt) {
+			wpa_printf(MSG_DEBUG,
+				   "OpenSSL: certID issuerKeyHash: %s", txt);
+			os_free(txt);
+		}
+
+		txt = integer_str(sresp->certID->serialNumber);
+		if (txt) {
+			wpa_printf(MSG_DEBUG,
+				   "OpenSSL: certID serialNumber: %s", txt);
+			os_free(txt);
+		}
+
+		switch (sresp->certStatus->type) {
+		case 0:
+			wpa_printf(MSG_DEBUG, "OpenSSL: certStatus: good");
+			break;
+		case 1:
+			wpa_printf(MSG_DEBUG, "OpenSSL: certStatus: revoked");
+			break;
+		default:
+			wpa_printf(MSG_DEBUG, "OpenSSL: certStatus: unknown");
+			break;
+		}
+
+		txt = generalizedtime_str(sresp->thisUpdate);
+		if (txt) {
+			wpa_printf(MSG_DEBUG, "OpenSSL: thisUpdate: %s", txt);
+			os_free(txt);
+		}
+
+		if (sresp->nextUpdate) {
+			txt = generalizedtime_str(sresp->nextUpdate);
+			if (txt) {
+				wpa_printf(MSG_DEBUG, "OpenSSL: nextUpdate: %s",
+					   txt);
+				os_free(txt);
+			}
+		}
+
+		txt = extensions_str("singleExtensions",
+				     sresp->singleExtensions);
+		if (txt) {
+			wpa_printf(MSG_DEBUG, "OpenSSL: %s", txt);
+			os_free(txt);
+		}
+
+		cid1 = cmp_sresp->certID;
+		cid2 = sresp->certID;
+		if (j > 0 &&
+		    (OBJ_cmp(cid1->hashAlgorithm->algorithm,
+			     cid2->hashAlgorithm->algorithm) != 0 ||
+		     ASN1_OCTET_STRING_cmp(cid1->issuerNameHash,
+					   cid2->issuerNameHash) != 0 ||
+		     ASN1_OCTET_STRING_cmp(cid1->issuerKeyHash,
+					   cid2->issuerKeyHash) != 0)) {
+			wpa_printf(MSG_DEBUG,
+				   "OpenSSL: Different OCSP response issuer information between SingleResponse values within BasicOCSPResponse");
+			goto fail;
+		}
+
+		if (!matching_resp && issuer &&
+		    ASN1_INTEGER_cmp(sresp->certID->serialNumber,
+				     X509_get_serialNumber(cert)) == 0 &&
+		    issuer_match(cert, issuer, sresp->certID) == 0) {
+			wpa_printf(MSG_DEBUG,
+				   "OpenSSL: This response matches peer certificate");
+			matching_resp = sresp;
+		}
+	}
+
+	txt = extensions_str("responseExtensions", rd->responseExtensions);
+	if (txt) {
+		wpa_printf(MSG_DEBUG, "OpenSSL: %s", txt);
+		os_free(txt);
+	}
+
+	if (!matching_resp) {
+		wpa_printf(MSG_DEBUG,
+			   "OpenSSL: Could not find OCSP response that matches the peer certificate");
+		result = OCSP_NO_RESPONSE;
+		goto fail;
+	}
+
+	if (!ocsp_resp_valid(matching_resp->thisUpdate,
+			     matching_resp->nextUpdate)) {
+		wpa_printf(MSG_DEBUG,
+			   "OpenSSL: OCSP response not valid at this time");
+		goto fail;
+	}
+
+	if (matching_resp->certStatus->type == 1) {
+		wpa_printf(MSG_DEBUG,
+			   "OpenSSL: OCSP response indicated that the peer certificate has been revoked");
+		result = OCSP_REVOKED;
+		goto fail;
+	}
+
+	if (matching_resp->certStatus->type != 0) {
+		wpa_printf(MSG_DEBUG,
+			   "OpenSSL: OCSP response did not indicate good status");
+		result = OCSP_NO_RESPONSE;
+		goto fail;
+	}
+
+	/* OCSP response indicated the certificate is good. */
+	result = OCSP_GOOD;
+fail:
+	sk_X509_pop_free(chain, X509_free);
+	sk_X509_free(untrusted);
+	sk_X509_pop_free(certs, X509_free);
+	BasicOCSPResponse_free(basic);
+	OCSPResponse_free(resp);
+
+	return result;
+}
+
+#endif /* OPENSSL_IS_BORINGSSL */
diff --git a/src/drivers/driver.h b/src/drivers/driver.h
index 7fe736b..6fd72c5 100644
--- a/src/drivers/driver.h
+++ b/src/drivers/driver.h
@@ -45,6 +45,15 @@
 #define HOSTAPD_CHAN_INDOOR_ONLY 0x00010000
 #define HOSTAPD_CHAN_GO_CONCURRENT 0x00020000
 
+#define HOSTAPD_CHAN_VHT_10_150 0x00100000
+#define HOSTAPD_CHAN_VHT_30_130 0x00200000
+#define HOSTAPD_CHAN_VHT_50_110 0x00400000
+#define HOSTAPD_CHAN_VHT_70_90  0x00800000
+#define HOSTAPD_CHAN_VHT_90_70  0x01000000
+#define HOSTAPD_CHAN_VHT_110_50 0x02000000
+#define HOSTAPD_CHAN_VHT_130_30 0x04000000
+#define HOSTAPD_CHAN_VHT_150_10 0x08000000
+
 /**
  * enum reg_change_initiator - Regulatory change initiator
  */
@@ -407,6 +416,28 @@
 	 */
 	const u8 *mac_addr_mask;
 
+	/**
+	 * sched_scan_plans - Scan plans for scheduled scan
+	 *
+	 * Each scan plan consists of the number of iterations to scan and the
+	 * interval between scans. When a scan plan finishes (i.e., it was run
+	 * for the specified number of iterations), the next scan plan is
+	 * executed. The scan plans are executed in the order they appear in
+	 * the array (lower index first). The last scan plan will run infinitely
+	 * (until requested to stop), thus must not specify the number of
+	 * iterations. All other scan plans must specify the number of
+	 * iterations.
+	 */
+	struct sched_scan_plan {
+		 u32 interval; /* In seconds */
+		 u32 iterations; /* Zero to run infinitely */
+	 } *sched_scan_plans;
+
+	/**
+	 * sched_scan_plans_num - Number of scan plans in sched_scan_plans array
+	 */
+	 unsigned int sched_scan_plans_num;
+
 	/*
 	 * NOTE: Whenever adding new parameters here, please make sure
 	 * wpa_scan_clone_params() and wpa_scan_free_params() get updated with
@@ -1214,6 +1245,8 @@
 #define WPA_DRIVER_FLAGS_VHT_IBSS		0x0000002000000000ULL
 /** Driver supports automatic band selection */
 #define WPA_DRIVER_FLAGS_SUPPORT_HW_MODE_ANY	0x0000004000000000ULL
+/** Driver supports simultaneous off-channel operations */
+#define WPA_DRIVER_FLAGS_OFFCHANNEL_SIMULTANEOUS	0x0000008000000000ULL
 	u64 flags;
 
 #define WPA_DRIVER_SMPS_MODE_STATIC			0x00000001
@@ -1231,6 +1264,15 @@
 	/** Maximum number of supported active probe SSIDs for sched_scan */
 	int max_sched_scan_ssids;
 
+	/** Maximum number of supported scan plans for scheduled scan */
+	unsigned int max_sched_scan_plans;
+
+	/** Maximum interval in a scan plan. In seconds */
+	u32 max_sched_scan_plan_interval;
+
+	/** Maximum number of iterations in a single scan plan */
+	u32 max_sched_scan_plan_iterations;
+
 	/** Whether sched_scan (offloaded scanning) is supported */
 	int sched_scan_supported;
 
@@ -1297,6 +1339,16 @@
  */
 #define WPA_DRIVER_FLAGS_TX_POWER_INSERTION		0x00000008
 	u32 rrm_flags;
+
+	/* Driver concurrency capabilities */
+	unsigned int conc_capab;
+	/* Maximum number of concurrent channels on 2.4 GHz */
+	unsigned int max_conc_chan_2_4;
+	/* Maximum number of concurrent channels on 5 GHz */
+	unsigned int max_conc_chan_5_0;
+
+	/* Maximum number of supported CSA counters */
+	u16 max_csa_counters;
 };
 
 
@@ -1397,6 +1449,16 @@
 	 * WPA_IF_MESH - Mesh interface
 	 */
 	WPA_IF_MESH,
+
+	/*
+	 * WPA_IF_TDLS - TDLS offchannel interface (used for pref freq only)
+	 */
+	WPA_IF_TDLS,
+
+	/*
+	 * WPA_IF_IBSS - IBSS interface (used for pref freq only)
+	 */
+	WPA_IF_IBSS,
 };
 
 struct wpa_init_params {
@@ -1537,8 +1599,8 @@
 	struct beacon_data beacon_csa;
 	struct beacon_data beacon_after;
 
-	u16 counter_offset_beacon;
-	u16 counter_offset_presp;
+	u16 counter_offset_beacon[2];
+	u16 counter_offset_presp[2];
 };
 
 /* TDLS peer capabilities for send_tdls_mgmt() */
@@ -1602,6 +1664,7 @@
 	/* ACS channel list info */
 	unsigned int ch_list_len;
 	const u8 *ch_list;
+	const int *freq_list;
 };
 
 
@@ -1942,10 +2005,13 @@
 	 * @noack: Do not wait for this frame to be acked (disable retries)
 	 * @freq: Frequency (in MHz) to send the frame on, or 0 to let the
 	 * driver decide
+	 * @csa_offs: Array of CSA offsets or %NULL
+	 * @csa_offs_len: Number of elements in csa_offs
 	 * Returns: 0 on success, -1 on failure
 	 */
 	int (*send_mlme)(void *priv, const u8 *data, size_t data_len,
-			 int noack, unsigned int freq);
+			 int noack, unsigned int freq, const u16 *csa_offs,
+			 size_t csa_offs_len);
 
 	/**
 	 * update_ft_ies - Update FT (IEEE 802.11r) IEs
@@ -2349,7 +2415,8 @@
 	 * Returns: 0 on success, -1 on failure
 	 */
 	int (*sta_set_flags)(void *priv, const u8 *addr,
-			     int total_flags, int flags_or, int flags_and);
+			     unsigned int total_flags, unsigned int flags_or,
+			     unsigned int flags_and);
 
 	/**
 	 * set_tx_queue_params - Set TX queue parameters
@@ -2380,12 +2447,13 @@
 	 *	change interface address)
 	 * @bridge: Bridge interface to use or %NULL if no bridge configured
 	 * @use_existing: Whether to allow existing interface to be used
+	 * @setup_ap: Whether to setup AP for %WPA_IF_AP_BSS interfaces
 	 * Returns: 0 on success, -1 on failure
 	 */
 	int (*if_add)(void *priv, enum wpa_driver_if_type type,
 		      const char *ifname, const u8 *addr, void *bss_ctx,
 		      void **drv_priv, char *force_ifname, u8 *if_addr,
-		      const char *bridge, int use_existing);
+		      const char *bridge, int use_existing, int setup_ap);
 
 	/**
 	 * if_remove - Remove a virtual interface
@@ -2967,7 +3035,6 @@
 	 * sched_scan - Request the driver to initiate scheduled scan
 	 * @priv: Private driver interface data
 	 * @params: Scan parameters
-	 * @interval: Interval between scan cycles in milliseconds
 	 * Returns: 0 on success, -1 on failure
 	 *
 	 * This operation should be used for scheduled scan offload to
@@ -2978,8 +3045,7 @@
 	 * and if not provided or if it returns -1, we fall back to
 	 * normal host-scheduled scans.
 	 */
-	int (*sched_scan)(void *priv, struct wpa_driver_scan_params *params,
-			  u32 interval);
+	int (*sched_scan)(void *priv, struct wpa_driver_scan_params *params);
 
 	/**
 	 * stop_sched_scan - Request the driver to stop a scheduled scan
@@ -3394,6 +3460,39 @@
 	 * Returns 0 on success, -1 on failure
 	 */
 	int (*set_band)(void *priv, enum set_band band);
+
+	/**
+	 * get_pref_freq_list - Get preferred frequency list for an interface
+	 * @priv: Private driver interface data
+	 * @if_type: Interface type
+	 * @num: Number of channels
+	 * @freq_list: Preferred channel frequency list encoded in MHz values
+	 * Returns 0 on success, -1 on failure
+	 *
+	 * This command can be used to query the preferred frequency list from
+	 * the driver specific to a particular interface type.
+	 */
+	int (*get_pref_freq_list)(void *priv, enum wpa_driver_if_type if_type,
+				  unsigned int *num, unsigned int *freq_list);
+
+	/**
+	 * set_prob_oper_freq - Indicate probable P2P operating channel
+	 * @priv: Private driver interface data
+	 * @freq: Channel frequency in MHz
+	 * Returns 0 on success, -1 on failure
+	 *
+	 * This command can be used to inform the driver of the operating
+	 * frequency that an ongoing P2P group formation is likely to come up
+	 * on. Local device is assuming P2P Client role.
+	 */
+	int (*set_prob_oper_freq)(void *priv, unsigned int freq);
+
+	/**
+	 * abort_scan - Request the driver to abort an ongoing scan
+	 * @priv: Private driver interface data
+	 * Returns 0 on success, -1 on failure
+	 */
+	int (*abort_scan)(void *priv);
 };
 
 
@@ -4053,6 +4152,12 @@
 		 * ptk_kek_len - The length of ptk_kek
 		 */
 		size_t ptk_kek_len;
+
+		/**
+		 * subnet_status - The subnet status:
+		 * 0 = unknown, 1 = unchanged, 2 = changed
+		 */
+		u8 subnet_status;
 	} assoc_info;
 
 	/**
@@ -4336,6 +4441,9 @@
 	 * @ssids: Scanned SSIDs (%NULL or zero-length SSID indicates wildcard
 	 *	SSID)
 	 * @num_ssids: Number of entries in ssids array
+	 * @external_scan: Whether the scan info is for an external scan
+	 * @nl_scan_event: 1 if the source of this scan event is a normal scan,
+	 * 	0 if the source of the scan event is a vendor scan
 	 */
 	struct scan_info {
 		int aborted;
@@ -4343,6 +4451,8 @@
 		size_t num_freqs;
 		struct wpa_driver_scan_ssid ssids[WPAS_MAX_SCAN_SSIDS];
 		size_t num_ssids;
+		int external_scan;
+		int nl_scan_event;
 	} scan_info;
 
 	/**
diff --git a/src/drivers/driver_atheros.c b/src/drivers/driver_atheros.c
index b721c32..1afeeb2 100644
--- a/src/drivers/driver_atheros.c
+++ b/src/drivers/driver_atheros.c
@@ -55,6 +55,10 @@
 #include "netlink.h"
 #include "linux_ioctl.h"
 
+#if defined(CONFIG_IEEE80211W) || defined(CONFIG_IEEE80211R) || defined(CONFIG_HS20) || defined(CONFIG_WNM) || defined(CONFIG_WPS)
+#define ATHEROS_USE_RAW_RECEIVE
+#endif
+
 
 struct atheros_driver_data {
 	struct hostapd_data *hapd;		/* back pointer */
@@ -430,7 +434,8 @@
 
 static int
 atheros_sta_set_flags(void *priv, const u8 *addr,
-		      int total_flags, int flags_or, int flags_and)
+		      unsigned int total_flags, unsigned int flags_or,
+		      unsigned int flags_and)
 {
 	/* For now, only support setting Authorized flag */
 	if (flags_or & WPA_STA_AUTHORIZED)
@@ -823,7 +828,7 @@
 	return 0;
 }
 
-#if defined(CONFIG_WPS) || defined(CONFIG_IEEE80211R) || defined(CONFIG_WNM) || defined(CONFIG_HS20)
+#ifdef ATHEROS_USE_RAW_RECEIVE
 static void atheros_raw_receive(void *ctx, const u8 *src_addr, const u8 *buf,
 				size_t len)
 {
@@ -911,7 +916,7 @@
 		break;
 	}
 }
-#endif
+#endif /* ATHEROS_USE_RAW_RECEIVE */
 
 static int atheros_receive_pkt(struct atheros_driver_data *drv)
 {
@@ -923,11 +928,11 @@
 #ifdef CONFIG_WPS
 	filt.app_filterype |= IEEE80211_FILTER_TYPE_PROBE_REQ;
 #endif /* CONFIG_WPS */
-#ifdef CONFIG_IEEE80211R
+#if defined(CONFIG_IEEE80211W) || defined(CONFIG_IEEE80211R)
 	filt.app_filterype |= (IEEE80211_FILTER_TYPE_ASSOC_REQ |
 			       IEEE80211_FILTER_TYPE_AUTH |
 			       IEEE80211_FILTER_TYPE_ACTION);
-#endif
+#endif /* CONFIG_IEEE80211R || CONFIG_IEEE80211W */
 #ifdef CONFIG_WNM
 	filt.app_filterype |= IEEE80211_FILTER_TYPE_ACTION;
 #endif /* CONFIG_WNM */
@@ -1026,7 +1031,7 @@
 #define atheros_set_ap_wps_ie NULL
 #endif /* CONFIG_WPS */
 
-#ifdef CONFIG_IEEE80211R
+#if defined(CONFIG_IEEE80211R) || defined(CONFIG_IEEE80211W)
 static int
 atheros_sta_auth(void *priv, const u8 *own_addr, const u8 *addr, u16 seq,
 		 u16 status_code, const u8 *ie, size_t len)
@@ -1102,7 +1107,7 @@
 	}
 	return ret;
 }
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R || CONFIG_IEEE80211W */
 
 static void
 atheros_new_sta(struct atheros_driver_data *drv, u8 addr[IEEE80211_ADDR_LEN])
@@ -1177,6 +1182,7 @@
 atheros_wireless_event_wireless_custom(struct atheros_driver_data *drv,
 				       char *custom, char *end)
 {
+#define MGMT_FRAM_TAG_SIZE 30 /* hardcoded in driver */
 	wpa_printf(MSG_DEBUG, "Custom wireless event: '%s'", custom);
 
 	if (strncmp(custom, "MLME-MICHAELMICFAILURE.indication", 33) == 0) {
@@ -1232,9 +1238,6 @@
 		 * so all are enabled for WPS... ugh.
 		 */
 		wpa_supplicant_event(drv->hapd, EVENT_WPS_BUTTON_PUSHED, NULL);
-#endif /* CONFIG_WPS */
-#if defined(CONFIG_WPS) || defined(CONFIG_IEEE80211R) || defined(CONFIG_HS20)
-#define MGMT_FRAM_TAG_SIZE 30 /* hardcoded in driver */
 	} else if (strncmp(custom, "Manage.prob_req ", 16) == 0) {
 		/*
 		 * Atheros driver uses a hack to pass Probe Request frames as a
@@ -1243,47 +1246,53 @@
 		 * Format: "Manage.prob_req <frame len>" | zero padding | frame
 		 */
 		int len = atoi(custom + 16);
-		if (len < 0 || custom + MGMT_FRAM_TAG_SIZE + len > end) {
+		if (len < 0 || MGMT_FRAM_TAG_SIZE + len > end - custom) {
 			wpa_printf(MSG_DEBUG, "Invalid Manage.prob_req event "
 				   "length %d", len);
 			return;
 		}
 		atheros_raw_receive(drv, NULL,
 				    (u8 *) custom + MGMT_FRAM_TAG_SIZE, len);
+#endif /* CONFIG_WPS */
+#if defined(CONFIG_IEEE80211R) || defined(CONFIG_IEEE80211W)
 	} else if (strncmp(custom, "Manage.assoc_req ", 17) == 0) {
 		/* Format: "Manage.assoc_req <frame len>" | zero padding |
 		 * frame */
 		int len = atoi(custom + 17);
-		if (len < 0 || custom + MGMT_FRAM_TAG_SIZE + len > end) {
-			wpa_printf(MSG_DEBUG, "Invalid Manage.prob_req/"
-				   "assoc_req/auth event length %d", len);
+		if (len < 0 || MGMT_FRAM_TAG_SIZE + len > end - custom) {
+			wpa_printf(MSG_DEBUG,
+				   "Invalid Manage.assoc_req event length %d",
+				   len);
 			return;
 		}
 		atheros_raw_receive(drv, NULL,
 				    (u8 *) custom + MGMT_FRAM_TAG_SIZE, len);
-	} else if (strncmp(custom, "Manage.action ", 14) == 0) {
-		/* Format: "Manage.assoc_req <frame len>" | zero padding |
-		 * frame */
-		int len = atoi(custom + 14);
-		if (len < 0 || custom + MGMT_FRAM_TAG_SIZE + len > end) {
-			wpa_printf(MSG_DEBUG, "Invalid Manage.prob_req/"
-				   "assoc_req/auth event length %d", len);
-			return;
-		}
-		atheros_raw_receive(drv, NULL,
-				    (u8 *) custom + MGMT_FRAM_TAG_SIZE, len);
-	} else if (strncmp(custom, "Manage.auth ", 12) == 0) {
-		/* Format: "Manage.auth <frame len>" | zero padding | frame
-		 */
+		} else if (strncmp(custom, "Manage.auth ", 12) == 0) {
+		/* Format: "Manage.auth <frame len>" | zero padding | frame */
 		int len = atoi(custom + 12);
-		if (len < 0 || custom + MGMT_FRAM_TAG_SIZE + len > end) {
-			wpa_printf(MSG_DEBUG, "Invalid Manage.prob_req/"
-				   "assoc_req/auth event length %d", len);
+			if (len < 0 ||
+			    MGMT_FRAM_TAG_SIZE + len > end - custom) {
+			wpa_printf(MSG_DEBUG,
+				   "Invalid Manage.auth event length %d", len);
 			return;
 		}
 		atheros_raw_receive(drv, NULL,
 				    (u8 *) custom + MGMT_FRAM_TAG_SIZE, len);
-#endif /* CONFIG_WPS or CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211W || CONFIG_IEEE80211R */
+#ifdef ATHEROS_USE_RAW_RECEIVE
+		} else if (strncmp(custom, "Manage.action ", 14) == 0) {
+		/* Format: "Manage.assoc_req <frame len>" | zero padding | frame
+		 */
+		int len = atoi(custom + 14);
+		if (len < 0 || MGMT_FRAM_TAG_SIZE + len > end - custom) {
+			wpa_printf(MSG_DEBUG,
+				   "Invalid Manage.action event length %d",
+				   len);
+			return;
+		}
+		atheros_raw_receive(drv, NULL,
+				    (u8 *) custom + MGMT_FRAM_TAG_SIZE, len);
+#endif /* ATHEROS_USE_RAW_RECEIVE */
 	}
 }
 
@@ -1375,7 +1384,7 @@
 
 static void
 atheros_wireless_event_wireless(struct atheros_driver_data *drv,
-				char *data, int len)
+				char *data, unsigned int len)
 {
 	struct iw_event iwe_buf, *iwe = &iwe_buf;
 	char *pos, *end, *custom, *buf;
@@ -1383,13 +1392,13 @@
 	pos = data;
 	end = data + len;
 
-	while (pos + IW_EV_LCP_LEN <= end) {
+	while ((size_t) (end - pos) >= IW_EV_LCP_LEN) {
 		/* Event data may be unaligned, so make a local, aligned copy
 		 * before processing. */
 		memcpy(&iwe_buf, pos, IW_EV_LCP_LEN);
 		wpa_printf(MSG_MSGDUMP, "Wireless event: cmd=0x%x len=%d",
 			   iwe->cmd, iwe->len);
-		if (iwe->len <= IW_EV_LCP_LEN)
+		if (iwe->len <= IW_EV_LCP_LEN || iwe->len > end - pos)
 			return;
 
 		custom = pos + IW_EV_POINT_LEN;
@@ -1421,7 +1430,7 @@
 			 * just like IWEVCUSTOM.
 			 */
 		case IWEVCUSTOM:
-			if (custom + iwe->u.data.length > end)
+			if (iwe->u.data.length > end - custom)
 				return;
 			buf = malloc(iwe->u.data.length + 1);
 			if (buf == NULL)
@@ -1695,10 +1704,10 @@
 	atheros_reset_appfilter(drv);
 
 	if (drv->wpa_ie || drv->wps_beacon_ie || drv->wps_probe_resp_ie) {
+		atheros_set_opt_ie(priv, NULL, 0);
 		wpabuf_free(drv->wpa_ie);
 		wpabuf_free(drv->wps_beacon_ie);
 		wpabuf_free(drv->wps_probe_resp_ie);
-		atheros_set_opt_ie(priv, NULL, 0);
 	}
 	netlink_deinit(drv->netlink);
 	(void) linux_set_iface_flags(drv->ioctl_sock, drv->iface, 0);
@@ -1836,10 +1845,11 @@
 }
 
 
-#ifdef CONFIG_IEEE80211R
+#if defined(CONFIG_IEEE80211R) || defined(CONFIG_IEEE80211W)
 
 static int atheros_send_mgmt(void *priv, const u8 *frm, size_t data_len,
-			     int noack, unsigned int freq)
+			     int noack, unsigned int freq,
+			     const u16 *csa_offs, size_t csa_offs_len)
 {
 	struct atheros_driver_data *drv = priv;
 	u8 buf[1510];
@@ -1861,8 +1871,11 @@
 	return set80211priv(drv, IEEE80211_IOCTL_SEND_MGMT, mgmt_frm,
 			    sizeof(struct ieee80211req_mgmtbuf) + data_len);
 }
+#endif /* CONFIG_IEEE80211R || CONFIG_IEEE80211W */
 
 
+#ifdef CONFIG_IEEE80211R
+
 static int atheros_add_tspec(void *priv, const u8 *addr, u8 *tspec_ie,
 			     size_t tspec_ielen)
 {
@@ -2142,10 +2155,12 @@
 	.set_ap_wps_ie		= atheros_set_ap_wps_ie,
 	.set_authmode		= atheros_set_authmode,
 	.set_ap			= atheros_set_ap,
-#ifdef CONFIG_IEEE80211R
+#if defined(CONFIG_IEEE80211R) || defined(CONFIG_IEEE80211W)
 	.sta_assoc              = atheros_sta_assoc,
 	.sta_auth               = atheros_sta_auth,
 	.send_mlme       	= atheros_send_mgmt,
+#endif /* CONFIG_IEEE80211R || CONFIG_IEEE80211W */
+#ifdef CONFIG_IEEE80211R
 	.add_tspec      	= atheros_add_tspec,
 	.add_sta_node    	= atheros_add_sta_node,
 #endif /* CONFIG_IEEE80211R */
diff --git a/src/drivers/driver_bsd.c b/src/drivers/driver_bsd.c
index 9ac87b4..bab1f03 100644
--- a/src/drivers/driver_bsd.c
+++ b/src/drivers/driver_bsd.c
@@ -894,7 +894,8 @@
 
 static int
 bsd_set_sta_authorized(void *priv, const u8 *addr,
-		       int total_flags, int flags_or, int flags_and)
+		       unsigned int total_flags, unsigned int flags_or,
+		       unsigned int flags_and)
 {
 	int authorized = -1;
 
diff --git a/src/drivers/driver_hostap.c b/src/drivers/driver_hostap.c
index 8835005..517a3bb 100644
--- a/src/drivers/driver_hostap.c
+++ b/src/drivers/driver_hostap.c
@@ -140,7 +140,7 @@
 static void handle_frame(struct hostap_driver_data *drv, u8 *buf, size_t len)
 {
 	struct ieee80211_hdr *hdr;
-	u16 fc, extra_len, type, stype;
+	u16 fc, type, stype;
 	size_t data_len = len;
 	int ver;
 	union wpa_event_data event;
@@ -165,19 +165,10 @@
 
 	ver = fc & WLAN_FC_PVER;
 
-	/* protocol version 3 is reserved for indicating extra data after the
-	 * payload, version 2 for indicating ACKed frame (TX callbacks), and
-	 * version 1 for indicating failed frame (no ACK, TX callbacks) */
-	if (ver == 3) {
-		u8 *pos = buf + len - 2;
-		extra_len = WPA_GET_LE16(pos);
-		printf("extra data in frame (elen=%d)\n", extra_len);
-		if ((size_t) extra_len + 2 > len) {
-			printf("  extra data overflow\n");
-			return;
-		}
-		len -= extra_len + 2;
-	} else if (ver == 1 || ver == 2) {
+	/* protocol version 2 is reserved for indicating ACKed frame (TX
+	 * callbacks), and version 1 for indicating failed frame (no ACK, TX
+	 * callbacks) */
+	if (ver == 1 || ver == 2) {
 		handle_tx_callback(drv, buf, data_len, ver == 2 ? 1 : 0);
 		return;
 	} else if (ver != 0) {
@@ -267,7 +258,8 @@
 
 
 static int hostap_send_mlme(void *priv, const u8 *msg, size_t len, int noack,
-			    unsigned int freq)
+			    unsigned int freq,
+			    const u16 *csa_offs, size_t csa_offs_len)
 {
 	struct hostap_driver_data *drv = priv;
 	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) msg;
@@ -316,7 +308,7 @@
 	pos += 2;
 	memcpy(pos, data, data_len);
 
-	res = hostap_send_mlme(drv, (u8 *) hdr, len, 0);
+	res = hostap_send_mlme(drv, (u8 *) hdr, len, 0, 0, NULL, 0);
 	if (res < 0) {
 		wpa_printf(MSG_ERROR, "hostap_send_eapol - packet len: %lu - "
 			   "failed: %d (%s)",
@@ -329,7 +321,8 @@
 
 
 static int hostap_sta_set_flags(void *priv, const u8 *addr,
-				int total_flags, int flags_or, int flags_and)
+				unsigned int total_flags, unsigned int flags_or,
+				unsigned int flags_and)
 {
 	struct hostap_driver_data *drv = priv;
 	struct prism2_hostapd_param param;
@@ -821,7 +814,7 @@
 
 
 static void hostapd_wireless_event_wireless(struct hostap_driver_data *drv,
-					    char *data, int len)
+					    char *data, unsigned int len)
 {
 	struct iw_event iwe_buf, *iwe = &iwe_buf;
 	char *pos, *end, *custom, *buf;
@@ -829,13 +822,13 @@
 	pos = data;
 	end = data + len;
 
-	while (pos + IW_EV_LCP_LEN <= end) {
+	while ((size_t) (end - pos) >= IW_EV_LCP_LEN) {
 		/* Event data may be unaligned, so make a local, aligned copy
 		 * before processing. */
 		memcpy(&iwe_buf, pos, IW_EV_LCP_LEN);
 		wpa_printf(MSG_DEBUG, "Wireless event: cmd=0x%x len=%d",
 			   iwe->cmd, iwe->len);
-		if (iwe->len <= IW_EV_LCP_LEN)
+		if (iwe->len <= IW_EV_LCP_LEN || iwe->len > end - pos)
 			return;
 
 		custom = pos + IW_EV_POINT_LEN;
@@ -854,7 +847,7 @@
 
 		switch (iwe->cmd) {
 		case IWEVCUSTOM:
-			if (custom + iwe->u.data.length > end)
+			if (iwe->u.data.length > end - custom)
 				return;
 			buf = malloc(iwe->u.data.length + 1);
 			if (buf == NULL)
@@ -1053,7 +1046,7 @@
 	memcpy(mgmt.bssid, own_addr, ETH_ALEN);
 	mgmt.u.deauth.reason_code = host_to_le16(reason);
 	return hostap_send_mlme(drv, (u8 *) &mgmt, IEEE80211_HDRLEN +
-				sizeof(mgmt.u.deauth), 0);
+				sizeof(mgmt.u.deauth), 0, 0, NULL, 0);
 }
 
 
@@ -1091,7 +1084,7 @@
 	memcpy(mgmt.bssid, own_addr, ETH_ALEN);
 	mgmt.u.disassoc.reason_code = host_to_le16(reason);
 	return  hostap_send_mlme(drv, (u8 *) &mgmt, IEEE80211_HDRLEN +
-				 sizeof(mgmt.u.disassoc), 0);
+				 sizeof(mgmt.u.disassoc), 0, 0, NULL, 0);
 }
 
 
@@ -1169,7 +1162,7 @@
 	os_memcpy(hdr.IEEE80211_BSSID_FROMDS, own_addr, ETH_ALEN);
 	os_memcpy(hdr.IEEE80211_SA_FROMDS, own_addr, ETH_ALEN);
 
-	hostap_send_mlme(priv, (u8 *)&hdr, sizeof(hdr), 0);
+	hostap_send_mlme(priv, (u8 *)&hdr, sizeof(hdr), 0, 0, NULL, 0);
 }
 
 
diff --git a/src/drivers/driver_ndis.c b/src/drivers/driver_ndis.c
index 669f1b8..fd32134 100644
--- a/src/drivers/driver_ndis.c
+++ b/src/drivers/driver_ndis.c
@@ -785,8 +785,8 @@
 	pos = (const u8 *) (res + 1);
 	end = pos + res->ie_len;
 
-	while (pos + 1 < end) {
-		if (pos + 2 + pos[1] > end)
+	while (end - pos > 1) {
+		if (2 + pos[1] > end - pos)
 			break;
 		if (pos[0] == ie)
 			return pos;
diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
index 2a05a3b..0fd836b 100644
--- a/src/drivers/driver_nl80211.c
+++ b/src/drivers/driver_nl80211.c
@@ -176,7 +176,8 @@
 static int nl80211_send_frame_cmd(struct i802_bss *bss,
 				  unsigned int freq, unsigned int wait,
 				  const u8 *buf, size_t buf_len, u64 *cookie,
-				  int no_cck, int no_ack, int offchanok);
+				  int no_cck, int no_ack, int offchanok,
+				  const u16 *csa_offs, size_t csa_offs_len);
 static int wpa_driver_nl80211_probe_req_report(struct i802_bss *bss,
 					       int report);
 
@@ -1536,6 +1537,10 @@
 			   "after rfkill unblock");
 		return;
 	}
+
+	if (is_p2p_net_interface(drv->nlmode))
+		nl80211_disable_11b_rates(drv, drv->ifindex, 1);
+
 	/* rtnetlink ifup handler will report interface as enabled */
 }
 
@@ -1878,6 +1883,11 @@
 			ret = -1;
 	}
 #endif /* CONFIG_TDLS */
+#ifdef CONFIG_FST
+	/* FST Action frames */
+	if (nl80211_register_action_frame(bss, (u8 *) "\x12", 1) < 0)
+		ret = -1;
+#endif /* CONFIG_FST */
 
 	/* FT Action frames */
 	if (nl80211_register_action_frame(bss, (u8 *) "\x06", 1) < 0)
@@ -2221,6 +2231,11 @@
 				   "interface '%s' UP", bss->ifname);
 			return ret;
 		}
+
+		if (is_p2p_net_interface(nlmode))
+			nl80211_disable_11b_rates(bss->drv,
+						  bss->drv->ifindex, 1);
+
 		if (nlmode == NL80211_IFTYPE_P2P_DEVICE)
 			return ret;
 	} else {
@@ -2463,6 +2478,7 @@
 }
 
 
+#ifdef CONFIG_DRIVER_NL80211_QCA
 static int issue_key_mgmt_set_key(struct wpa_driver_nl80211_data *drv,
 				  const u8 *key, size_t key_len)
 {
@@ -2490,6 +2506,7 @@
 
 	return ret;
 }
+#endif /* CONFIG_DRIVER_NL80211_QCA */
 
 
 static int wpa_driver_nl80211_set_key(const char *ifname, struct i802_bss *bss,
@@ -2520,6 +2537,7 @@
 	}
 #endif /* CONFIG_TDLS */
 
+#ifdef CONFIG_DRIVER_NL80211_QCA
 	if (alg == WPA_ALG_PMK &&
 	    (drv->capa.flags & WPA_DRIVER_FLAGS_KEY_MGMT_OFFLOAD)) {
 		wpa_printf(MSG_DEBUG, "%s: calling issue_key_mgmt_set_key",
@@ -2527,6 +2545,7 @@
 		ret = issue_key_mgmt_set_key(drv, key, key_len);
 		return ret;
 	}
+#endif /* CONFIG_DRIVER_NL80211_QCA */
 
 	if (alg == WPA_ALG_NONE) {
 		msg = nl80211_ifindex_msg(drv, ifindex, 0, NL80211_CMD_DEL_KEY);
@@ -3084,7 +3103,9 @@
 					 const void *data, size_t len,
 					 int encrypt, int noack,
 					 unsigned int freq, int no_cck,
-					 int offchanok, unsigned int wait_time)
+					 int offchanok, unsigned int wait_time,
+					 const u16 *csa_offs,
+					 size_t csa_offs_len)
 {
 	struct wpa_driver_nl80211_data *drv = bss->drv;
 	u64 cookie;
@@ -3110,7 +3131,8 @@
 
 	wpa_printf(MSG_DEBUG, "nl80211: send_frame -> send_frame_cmd");
 	res = nl80211_send_frame_cmd(bss, freq, wait_time, data, len,
-				     &cookie, no_cck, noack, offchanok);
+				     &cookie, no_cck, noack, offchanok,
+				     csa_offs, csa_offs_len);
 	if (res == 0 && !noack) {
 		const struct ieee80211_mgmt *mgmt;
 		u16 fc;
@@ -3136,7 +3158,9 @@
 					size_t data_len, int noack,
 					unsigned int freq, int no_cck,
 					int offchanok,
-					unsigned int wait_time)
+					unsigned int wait_time,
+					const u16 *csa_offs,
+					size_t csa_offs_len)
 {
 	struct wpa_driver_nl80211_data *drv = bss->drv;
 	struct ieee80211_mgmt *mgmt;
@@ -3166,7 +3190,7 @@
 		}
 		return nl80211_send_frame_cmd(bss, freq, 0,
 					      data, data_len, NULL, 1, noack,
-					      1);
+					      1, csa_offs, csa_offs_len);
 	}
 
 	if (drv->device_ap_sme && is_ap_interface(drv->nlmode)) {
@@ -3180,7 +3204,8 @@
 					      wait_time,
 					      data, data_len,
 					      &drv->send_action_cookie,
-					      no_cck, noack, offchanok);
+					      no_cck, noack, offchanok,
+					      csa_offs, csa_offs_len);
 	}
 
 	if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT &&
@@ -3200,7 +3225,8 @@
 	wpa_printf(MSG_DEBUG, "nl80211: send_mlme -> send_frame");
 	return wpa_driver_nl80211_send_frame(bss, data, data_len, encrypt,
 					     noack, freq, no_cck, offchanok,
-					     wait_time);
+					     wait_time, csa_offs,
+					     csa_offs_len);
 }
 
 
@@ -3253,7 +3279,7 @@
 	struct i802_bss *bss = priv;
 	struct wpa_driver_nl80211_data *drv = bss->drv;
 	struct nl_msg *msg;
-	struct nlattr *acl;
+	struct nl_msg *acl;
 	unsigned int i;
 	int ret;
 
@@ -3266,23 +3292,26 @@
 	wpa_printf(MSG_DEBUG, "nl80211: Set %s ACL (num_mac_acl=%u)",
 		   params->acl_policy ? "Accept" : "Deny", params->num_mac_acl);
 
-	if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_SET_MAC_ACL)) ||
-	    nla_put_u32(msg, NL80211_ATTR_ACL_POLICY, params->acl_policy ?
-			NL80211_ACL_POLICY_DENY_UNLESS_LISTED :
-			NL80211_ACL_POLICY_ACCEPT_UNLESS_LISTED) ||
-	    (acl = nla_nest_start(msg, NL80211_ATTR_MAC_ADDRS)) == NULL) {
-		nlmsg_free(msg);
+	acl = nlmsg_alloc();
+	if (!acl)
 		return -ENOMEM;
-	}
-
 	for (i = 0; i < params->num_mac_acl; i++) {
-		if (nla_put(msg, i + 1, ETH_ALEN, params->mac_acl[i].addr)) {
-			nlmsg_free(msg);
+		if (nla_put(acl, i + 1, ETH_ALEN, params->mac_acl[i].addr)) {
+			nlmsg_free(acl);
 			return -ENOMEM;
 		}
 	}
 
-	nla_nest_end(msg, acl);
+	if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_SET_MAC_ACL)) ||
+	    nla_put_u32(msg, NL80211_ATTR_ACL_POLICY, params->acl_policy ?
+			NL80211_ACL_POLICY_DENY_UNLESS_LISTED :
+			NL80211_ACL_POLICY_ACCEPT_UNLESS_LISTED) ||
+	    nla_put_nested(msg, NL80211_ATTR_MAC_ADDRS, acl)) {
+		nlmsg_free(msg);
+		nlmsg_free(acl);
+		return -ENOMEM;
+	}
+	nlmsg_free(acl);
 
 	ret = send_and_recv_msgs(drv, msg, NULL, NULL);
 	if (ret) {
@@ -4236,7 +4265,7 @@
 	memcpy(pos, data, data_len);
 
 	res = wpa_driver_nl80211_send_frame(bss, (u8 *) hdr, len, encrypt, 0,
-					    0, 0, 0, 0);
+					    0, 0, 0, 0, NULL, 0);
 	if (res < 0) {
 		wpa_printf(MSG_ERROR, "i802_send_eapol - packet len: %lu - "
 			   "failed: %d (%s)",
@@ -4249,8 +4278,9 @@
 
 
 static int wpa_driver_nl80211_sta_set_flags(void *priv, const u8 *addr,
-					    int total_flags,
-					    int flags_or, int flags_and)
+					    unsigned int total_flags,
+					    unsigned int flags_or,
+					    unsigned int flags_and)
 {
 	struct i802_bss *bss = priv;
 	struct nl_msg *msg;
@@ -4671,6 +4701,7 @@
 	int ret;
 	int algs;
 
+#ifdef CONFIG_DRIVER_NL80211_QCA
 	if (params->req_key_mgmt_offload && params->psk &&
 	    (params->key_mgmt_suite == WPA_KEY_MGMT_PSK ||
 	     params->key_mgmt_suite == WPA_KEY_MGMT_PSK_SHA256 ||
@@ -4680,6 +4711,7 @@
 		if (ret)
 			return ret;
 	}
+#endif /* CONFIG_DRIVER_NL80211_QCA */
 
 	wpa_printf(MSG_DEBUG, "nl80211: Connect (ifindex=%d)", drv->ifindex);
 	msg = nl80211_drv_msg(drv, 0, NL80211_CMD_CONNECT);
@@ -5424,7 +5456,7 @@
 	return wpa_driver_nl80211_send_mlme(bss, (u8 *) &mgmt,
 					    IEEE80211_HDRLEN +
 					    sizeof(mgmt.u.deauth), 0, 0, 0, 0,
-					    0);
+					    0, NULL, 0);
 }
 
 
@@ -5451,7 +5483,7 @@
 	return wpa_driver_nl80211_send_mlme(bss, (u8 *) &mgmt,
 					    IEEE80211_HDRLEN +
 					    sizeof(mgmt.u.disassoc), 0, 0, 0, 0,
-					    0);
+					    0, NULL, 0);
 }
 
 
@@ -5674,8 +5706,8 @@
 	struct wpa_driver_nl80211_data *drv;
 	struct i802_bss *bss;
 	size_t i;
-	char brname[IFNAMSIZ];
-	int ifindex, br_ifindex;
+	char master_ifname[IFNAMSIZ];
+	int ifindex, br_ifindex = 0;
 	int br_added = 0;
 
 	bss = wpa_driver_nl80211_drv_init(hapd, params->ifname,
@@ -5686,15 +5718,21 @@
 
 	drv = bss->drv;
 
-	if (linux_br_get(brname, params->ifname) == 0) {
+	if (linux_br_get(master_ifname, params->ifname) == 0) {
 		wpa_printf(MSG_DEBUG, "nl80211: Interface %s is in bridge %s",
-			   params->ifname, brname);
-		br_ifindex = if_nametoindex(brname);
-		os_strlcpy(bss->brname, brname, IFNAMSIZ);
+			   params->ifname, master_ifname);
+		br_ifindex = if_nametoindex(master_ifname);
+		os_strlcpy(bss->brname, master_ifname, IFNAMSIZ);
+	} else if ((params->num_bridge == 0 || !params->bridge[0]) &&
+		   linux_master_get(master_ifname, params->ifname) == 0) {
+		wpa_printf(MSG_DEBUG, "nl80211: Interface %s is in master %s",
+			params->ifname, master_ifname);
+		/* start listening for EAPOL on the master interface */
+		add_ifidx(drv, if_nametoindex(master_ifname));
 	} else {
-		brname[0] = '\0';
-		br_ifindex = 0;
+		master_ifname[0] = '\0';
 	}
+
 	bss->br_ifindex = br_ifindex;
 
 	for (i = 0; i < params->num_bridge; i++) {
@@ -5714,7 +5752,7 @@
 		if (i802_check_bridge(drv, bss, params->bridge[0],
 				      params->ifname) < 0)
 			goto failed;
-		if (os_strcmp(params->bridge[0], brname) != 0)
+		if (os_strcmp(params->bridge[0], master_ifname) != 0)
 			br_added = 1;
 	}
 
@@ -5792,8 +5830,9 @@
 		return NL80211_IFTYPE_P2P_DEVICE;
 	case WPA_IF_MESH:
 		return NL80211_IFTYPE_MESH_POINT;
+	default:
+		return -1;
 	}
-	return -1;
 }
 
 
@@ -5864,7 +5903,8 @@
 				     const char *ifname, const u8 *addr,
 				     void *bss_ctx, void **drv_priv,
 				     char *force_ifname, u8 *if_addr,
-				     const char *bridge, int use_existing)
+				     const char *bridge, int use_existing,
+				     int setup_ap)
 {
 	enum nl80211_iftype nlmode;
 	struct i802_bss *bss = priv;
@@ -5948,7 +5988,7 @@
 		os_memcpy(if_addr, new_addr, ETH_ALEN);
 	}
 
-	if (type == WPA_IF_AP_BSS) {
+	if (type == WPA_IF_AP_BSS && setup_ap) {
 		struct i802_bss *new_bss = os_zalloc(sizeof(*new_bss));
 		if (new_bss == NULL) {
 			if (added)
@@ -6103,7 +6143,8 @@
 				  unsigned int freq, unsigned int wait,
 				  const u8 *buf, size_t buf_len,
 				  u64 *cookie_out, int no_cck, int no_ack,
-				  int offchanok)
+				  int offchanok, const u16 *csa_offs,
+				  size_t csa_offs_len)
 {
 	struct wpa_driver_nl80211_data *drv = bss->drv;
 	struct nl_msg *msg;
@@ -6123,6 +6164,8 @@
 	     nla_put_flag(msg, NL80211_ATTR_OFFCHANNEL_TX_OK)) ||
 	    (no_cck && nla_put_flag(msg, NL80211_ATTR_TX_NO_CCK_RATE)) ||
 	    (no_ack && nla_put_flag(msg, NL80211_ATTR_DONT_WAIT_FOR_ACK)) ||
+	    (csa_offs && nla_put(msg, NL80211_ATTR_CSA_C_OFFSETS_TX,
+				 csa_offs_len * sizeof(u16), csa_offs)) ||
 	    nla_put(msg, NL80211_ATTR_FRAME, buf_len, buf))
 		goto fail;
 
@@ -6140,6 +6183,20 @@
 
 		if (cookie_out)
 			*cookie_out = no_ack ? (u64) -1 : cookie;
+
+		if (drv->num_send_action_cookies == MAX_SEND_ACTION_COOKIES) {
+			wpa_printf(MSG_DEBUG,
+				   "nl80211: Drop oldest pending send action cookie 0x%llx",
+				   (long long unsigned int)
+				   drv->send_action_cookies[0]);
+			os_memmove(&drv->send_action_cookies[0],
+				   &drv->send_action_cookies[1],
+				   (MAX_SEND_ACTION_COOKIES - 1) *
+				   sizeof(u64));
+			drv->num_send_action_cookies--;
+		}
+		drv->send_action_cookies[drv->num_send_action_cookies] = cookie;
+		drv->num_send_action_cookies++;
 	}
 
 fail:
@@ -6182,29 +6239,28 @@
 	     !drv->use_monitor))
 		ret = wpa_driver_nl80211_send_mlme(bss, buf, 24 + data_len,
 						   0, freq, no_cck, 1,
-						   wait_time);
+						   wait_time, NULL, 0);
 	else
 		ret = nl80211_send_frame_cmd(bss, freq, wait_time, buf,
 					     24 + data_len,
 					     &drv->send_action_cookie,
-					     no_cck, 0, 1);
+					     no_cck, 0, 1, NULL, 0);
 
 	os_free(buf);
 	return ret;
 }
 
 
-static void wpa_driver_nl80211_send_action_cancel_wait(void *priv)
+static void nl80211_frame_wait_cancel(struct i802_bss *bss, u64 cookie)
 {
-	struct i802_bss *bss = priv;
 	struct wpa_driver_nl80211_data *drv = bss->drv;
 	struct nl_msg *msg;
 	int ret;
 
 	wpa_printf(MSG_DEBUG, "nl80211: Cancel TX frame wait: cookie=0x%llx",
-		   (long long unsigned int) drv->send_action_cookie);
+		   (long long unsigned int) cookie);
 	if (!(msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_FRAME_WAIT_CANCEL)) ||
-	    nla_put_u64(msg, NL80211_ATTR_COOKIE, drv->send_action_cookie)) {
+	    nla_put_u64(msg, NL80211_ATTR_COOKIE, cookie)) {
 		nlmsg_free(msg);
 		return;
 	}
@@ -6216,6 +6272,30 @@
 }
 
 
+static void wpa_driver_nl80211_send_action_cancel_wait(void *priv)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	unsigned int i;
+	u64 cookie;
+
+	/* Cancel the last pending TX cookie */
+	nl80211_frame_wait_cancel(bss, drv->send_action_cookie);
+
+	/*
+	 * Cancel the other pending TX cookies, if any. This is needed since
+	 * the driver may keep a list of all pending offchannel TX operations
+	 * and free up the radio only once they have expired or cancelled.
+	 */
+	for (i = drv->num_send_action_cookies; i > 0; i--) {
+		cookie = drv->send_action_cookies[i - 1];
+		if (cookie != drv->send_action_cookie)
+			nl80211_frame_wait_cancel(bss, cookie);
+	}
+	drv->num_send_action_cookies = 0;
+}
+
+
 static int wpa_driver_nl80211_remain_on_channel(void *priv, unsigned int freq,
 						unsigned int duration)
 {
@@ -6438,9 +6518,13 @@
 static void wpa_driver_nl80211_resume(void *priv)
 {
 	struct i802_bss *bss = priv;
+	enum nl80211_iftype nlmode = nl80211_get_ifmode(bss);
 
 	if (i802_set_iface_flags(bss, 1))
 		wpa_printf(MSG_DEBUG, "nl80211: Failed to set interface up on resume event");
+
+	if (is_p2p_net_interface(nlmode))
+		nl80211_disable_11b_rates(bss->drv, bss->drv->ifindex, 1);
 }
 
 
@@ -6513,8 +6597,12 @@
 
 	os_memset(si, 0, sizeof(*si));
 	res = nl80211_get_link_signal(drv, si);
-	if (res != 0)
-		return res;
+	if (res) {
+		if (drv->nlmode != NL80211_IFTYPE_ADHOC &&
+		    drv->nlmode != NL80211_IFTYPE_MESH_POINT)
+			return res;
+		si->current_signal = 0;
+	}
 
 	res = nl80211_get_channel_width(drv, si);
 	if (res != 0)
@@ -6529,7 +6617,7 @@
 {
 	struct i802_bss *bss = priv;
 	return wpa_driver_nl80211_send_frame(bss, data, data_len, encrypt, 0,
-					     0, 0, 0, 0);
+					     0, 0, 0, 0, NULL, 0);
 }
 
 
@@ -6938,7 +7026,7 @@
 	os_memcpy(nulldata.hdr.IEEE80211_SA_FROMDS, own_addr, ETH_ALEN);
 
 	if (wpa_driver_nl80211_send_mlme(bss, (u8 *) &nulldata, size, 0, 0, 0,
-					 0, 0) < 0)
+					 0, 0, NULL, 0) < 0)
 		wpa_printf(MSG_DEBUG, "nl80211_send_null_frame: Failed to "
 			   "send poll frame");
 }
@@ -7208,6 +7296,19 @@
 				struct wpa_driver_scan_params *params)
 {
 	struct i802_bss *bss = priv;
+#ifdef CONFIG_DRIVER_NL80211_QCA
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+
+	/*
+	 * Do a vendor specific scan if possible. If only_new_results is
+	 * set, do a normal scan since a kernel (cfg80211) BSS cache flush
+	 * cannot be achieved through a vendor scan. The below condition may
+	 * need to be modified if new scan flags are added in the future whose
+	 * functionality can only be achieved through a normal scan.
+	 */
+	if (drv->scan_vendor_cmd_avail && !params->only_new_results)
+		return wpa_driver_nl80211_vendor_scan(bss, params);
+#endif /* CONFIG_DRIVER_NL80211_QCA */
 	return wpa_driver_nl80211_scan(bss, params);
 }
 
@@ -7245,11 +7346,13 @@
 
 static int driver_nl80211_send_mlme(void *priv, const u8 *data,
 				    size_t data_len, int noack,
-				    unsigned int freq)
+				    unsigned int freq,
+				    const u16 *csa_offs, size_t csa_offs_len)
 {
 	struct i802_bss *bss = priv;
 	return wpa_driver_nl80211_send_mlme(bss, data, data_len, noack,
-					    freq, 0, 0, 0);
+					    freq, 0, 0, 0, csa_offs,
+					    csa_offs_len);
 }
 
 
@@ -7476,7 +7579,13 @@
 				  "capa.max_acl_mac_addrs=%u\n"
 				  "capa.num_multichan_concurrent=%u\n"
 				  "capa.mac_addr_rand_sched_scan_supported=%d\n"
-				  "capa.mac_addr_rand_scan_supported=%d\n",
+				  "capa.mac_addr_rand_scan_supported=%d\n"
+				  "capa.conc_capab=%u\n"
+				  "capa.max_conc_chan_2_4=%u\n"
+				  "capa.max_conc_chan_5_0=%u\n"
+				  "capa.max_sched_scan_plans=%u\n"
+				  "capa.max_sched_scan_plan_interval=%u\n"
+				  "capa.max_sched_scan_plan_iterations=%u\n",
 				  drv->capa.key_mgmt,
 				  drv->capa.enc,
 				  drv->capa.auth,
@@ -7492,7 +7601,13 @@
 				  drv->capa.max_acl_mac_addrs,
 				  drv->capa.num_multichan_concurrent,
 				  drv->capa.mac_addr_rand_sched_scan_supported,
-				  drv->capa.mac_addr_rand_scan_supported);
+				  drv->capa.mac_addr_rand_scan_supported,
+				  drv->capa.conc_capab,
+				  drv->capa.max_conc_chan_2_4,
+				  drv->capa.max_conc_chan_5_0,
+				  drv->capa.max_sched_scan_plans,
+				  drv->capa.max_sched_scan_plan_interval,
+				  drv->capa.max_sched_scan_plan_iterations);
 		if (os_snprintf_error(end - pos, res))
 			return pos - buf;
 		pos += res;
@@ -7535,6 +7650,8 @@
 	struct wpa_driver_nl80211_data *drv = bss->drv;
 	struct nlattr *beacon_csa;
 	int ret = -ENOBUFS;
+	int csa_off_len = 0;
+	int i;
 
 	wpa_printf(MSG_DEBUG, "nl80211: Channel switch request (cs_count=%u block_tx=%u freq=%d width=%d cf1=%d cf2=%d)",
 		   settings->cs_count, settings->block_tx,
@@ -7551,20 +7668,56 @@
 	    (drv->nlmode != NL80211_IFTYPE_P2P_GO))
 		return -EOPNOTSUPP;
 
-	/* check settings validity */
-	if (!settings->beacon_csa.tail ||
-	    ((settings->beacon_csa.tail_len <=
-	      settings->counter_offset_beacon) ||
-	     (settings->beacon_csa.tail[settings->counter_offset_beacon] !=
-	      settings->cs_count)))
+	/*
+	 * Remove empty counters, assuming Probe Response and Beacon frame
+	 * counters match. This implementation assumes that there are only two
+	 * counters.
+	 */
+	if (settings->counter_offset_beacon[0] &&
+	    !settings->counter_offset_beacon[1]) {
+		csa_off_len = 1;
+	} else if (settings->counter_offset_beacon[1] &&
+		   !settings->counter_offset_beacon[0]) {
+		csa_off_len = 1;
+		settings->counter_offset_beacon[0] =
+			settings->counter_offset_beacon[1];
+		settings->counter_offset_presp[0] =
+			settings->counter_offset_presp[1];
+	} else if (settings->counter_offset_beacon[1] &&
+		   settings->counter_offset_beacon[0]) {
+		csa_off_len = 2;
+	} else {
+		wpa_printf(MSG_ERROR, "nl80211: No CSA counters provided");
+		return -EINVAL;
+	}
+
+	/* Check CSA counters validity */
+	if (drv->capa.max_csa_counters &&
+	    csa_off_len > drv->capa.max_csa_counters) {
+		wpa_printf(MSG_ERROR,
+			   "nl80211: Too many CSA counters provided");
+		return -EINVAL;
+	}
+
+	if (!settings->beacon_csa.tail)
 		return -EINVAL;
 
-	if (settings->beacon_csa.probe_resp &&
-	    ((settings->beacon_csa.probe_resp_len <=
-	      settings->counter_offset_presp) ||
-	     (settings->beacon_csa.probe_resp[settings->counter_offset_presp] !=
-	      settings->cs_count)))
-		return -EINVAL;
+	for (i = 0; i < csa_off_len; i++) {
+		u16 csa_c_off_bcn = settings->counter_offset_beacon[i];
+		u16 csa_c_off_presp = settings->counter_offset_presp[i];
+
+		if ((settings->beacon_csa.tail_len <= csa_c_off_bcn) ||
+		    (settings->beacon_csa.tail[csa_c_off_bcn] !=
+		     settings->cs_count))
+			return -EINVAL;
+
+		if (settings->beacon_csa.probe_resp &&
+		    ((settings->beacon_csa.probe_resp_len <=
+		      csa_c_off_presp) ||
+		     (settings->beacon_csa.probe_resp[csa_c_off_presp] !=
+		      settings->cs_count)))
+			return -EINVAL;
+	}
 
 	if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_CHANNEL_SWITCH)) ||
 	    nla_put_u32(msg, NL80211_ATTR_CH_SWITCH_COUNT,
@@ -7588,11 +7741,13 @@
 	if (ret)
 		goto error;
 
-	if (nla_put_u16(msg, NL80211_ATTR_CSA_C_OFF_BEACON,
-			settings->counter_offset_beacon) ||
+	if (nla_put(msg, NL80211_ATTR_CSA_C_OFF_BEACON,
+		    csa_off_len * sizeof(u16),
+		    settings->counter_offset_beacon) ||
 	    (settings->beacon_csa.probe_resp &&
-	     nla_put_u16(msg, NL80211_ATTR_CSA_C_OFF_PRESP,
-			 settings->counter_offset_presp)))
+	     nla_put(msg, NL80211_ATTR_CSA_C_OFF_PRESP,
+		     csa_off_len * sizeof(u16),
+		     settings->counter_offset_presp)))
 		goto fail;
 
 	nla_nest_end(msg, beacon_csa);
@@ -7838,6 +7993,7 @@
 }
 
 
+#ifdef CONFIG_DRIVER_NL80211_QCA
 static int nl80211_roaming(void *priv, int allowed, const u8 *bssid)
 {
 	struct i802_bss *bss = priv;
@@ -7870,6 +8026,7 @@
 
 	return send_and_recv_msgs(drv, msg, NULL, NULL);
 }
+#endif /* CONFIG_DRIVER_NL80211_QCA */
 
 
 static int nl80211_set_mac_addr(void *priv, const u8 *addr)
@@ -8354,6 +8511,8 @@
 }
 
 
+#ifdef CONFIG_DRIVER_NL80211_QCA
+
 static int hw_mode_to_qca_acs(enum hostapd_hw_mode hw_mode)
 {
 	switch (hw_mode) {
@@ -8373,6 +8532,26 @@
 }
 
 
+static int add_acs_freq_list(struct nl_msg *msg, const int *freq_list)
+{
+	int i, len, ret;
+	u32 *freqs;
+
+	if (!freq_list)
+		return 0;
+	len = int_array_len(freq_list);
+	freqs = os_malloc(sizeof(u32) * len);
+	if (!freqs)
+		return -1;
+	for (i = 0; i < len; i++)
+		freqs[i] = freq_list[i];
+	ret = nla_put(msg, QCA_WLAN_VENDOR_ATTR_ACS_FREQ_LIST,
+		      sizeof(u32) * len, freqs);
+	os_free(freqs);
+	return ret;
+}
+
+
 static int wpa_driver_do_acs(void *priv, struct drv_acs_params *params)
 {
 	struct i802_bss *bss = priv;
@@ -8402,7 +8581,8 @@
 			params->ch_width) ||
 	    (params->ch_list_len &&
 	     nla_put(msg, QCA_WLAN_VENDOR_ATTR_ACS_CH_LIST, params->ch_list_len,
-		     params->ch_list))) {
+		     params->ch_list)) ||
+	    add_acs_freq_list(msg, params->freq_list)) {
 		nlmsg_free(msg);
 		return -ENOBUFS;
 	}
@@ -8470,6 +8650,195 @@
 }
 
 
+struct nl80211_pcl {
+	unsigned int num;
+	unsigned int *freq_list;
+};
+
+static int preferred_freq_info_handler(struct nl_msg *msg, void *arg)
+{
+	struct nlattr *tb[NL80211_ATTR_MAX + 1];
+	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+	struct nl80211_pcl *param = arg;
+	struct nlattr *nl_vend, *attr;
+	enum qca_iface_type iface_type;
+	struct nlattr *tb_vendor[QCA_WLAN_VENDOR_ATTR_MAX + 1];
+	unsigned int num, max_num;
+	u32 *freqs;
+
+	nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+		  genlmsg_attrlen(gnlh, 0), NULL);
+
+	nl_vend = tb[NL80211_ATTR_VENDOR_DATA];
+	if (!nl_vend)
+		return NL_SKIP;
+
+	nla_parse(tb_vendor, QCA_WLAN_VENDOR_ATTR_MAX,
+		  nla_data(nl_vend), nla_len(nl_vend), NULL);
+
+	attr = tb_vendor[
+		QCA_WLAN_VENDOR_ATTR_GET_PREFERRED_FREQ_LIST_IFACE_TYPE];
+	if (!attr) {
+		wpa_printf(MSG_ERROR, "nl80211: iface_type couldn't be found");
+		param->num = 0;
+		return NL_SKIP;
+	}
+
+	iface_type = (enum qca_iface_type) nla_get_u32(attr);
+	wpa_printf(MSG_DEBUG, "nl80211: Driver returned iface_type=%d",
+		   iface_type);
+
+	attr = tb_vendor[QCA_WLAN_VENDOR_ATTR_GET_PREFERRED_FREQ_LIST];
+	if (!attr) {
+		wpa_printf(MSG_ERROR,
+			   "nl80211: preferred_freq_list couldn't be found");
+		param->num = 0;
+		return NL_SKIP;
+	}
+
+	/*
+	 * param->num has the maximum number of entries for which there
+	 * is room in the freq_list provided by the caller.
+	 */
+	freqs = nla_data(attr);
+	max_num = nla_len(attr) / sizeof(u32);
+	if (max_num > param->num)
+		max_num = param->num;
+	for (num = 0; num < max_num; num++)
+		param->freq_list[num] = freqs[num];
+	param->num = num;
+
+	return NL_SKIP;
+}
+
+
+static int nl80211_get_pref_freq_list(void *priv,
+				      enum wpa_driver_if_type if_type,
+				      unsigned int *num,
+				      unsigned int *freq_list)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	struct nl_msg *msg;
+	int ret;
+	unsigned int i;
+	struct nlattr *params;
+	struct nl80211_pcl param;
+	enum qca_iface_type iface_type;
+
+	if (!drv->get_pref_freq_list)
+		return -1;
+
+	switch (if_type) {
+	case WPA_IF_STATION:
+		iface_type = QCA_IFACE_TYPE_STA;
+		break;
+	case WPA_IF_AP_BSS:
+		iface_type = QCA_IFACE_TYPE_AP;
+		break;
+	case WPA_IF_P2P_GO:
+		iface_type = QCA_IFACE_TYPE_P2P_GO;
+		break;
+	case WPA_IF_P2P_CLIENT:
+		iface_type = QCA_IFACE_TYPE_P2P_CLIENT;
+		break;
+	case WPA_IF_IBSS:
+		iface_type = QCA_IFACE_TYPE_IBSS;
+		break;
+	case WPA_IF_TDLS:
+		iface_type = QCA_IFACE_TYPE_TDLS;
+		break;
+	default:
+		return -1;
+	}
+
+	param.num = *num;
+	param.freq_list = freq_list;
+
+	if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
+	    nla_put_u32(msg, NL80211_ATTR_IFINDEX, drv->ifindex) ||
+	    nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
+	    nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
+			QCA_NL80211_VENDOR_SUBCMD_GET_PREFERRED_FREQ_LIST) ||
+	    !(params = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA)) ||
+	    nla_put_u32(msg,
+			QCA_WLAN_VENDOR_ATTR_GET_PREFERRED_FREQ_LIST_IFACE_TYPE,
+			iface_type)) {
+		wpa_printf(MSG_ERROR,
+			   "%s: err in adding vendor_cmd and vendor_data",
+			   __func__);
+		nlmsg_free(msg);
+		return -1;
+	}
+	nla_nest_end(msg, params);
+
+	os_memset(freq_list, 0, *num * sizeof(freq_list[0]));
+	ret = send_and_recv_msgs(drv, msg, preferred_freq_info_handler, &param);
+	if (ret) {
+		wpa_printf(MSG_ERROR,
+			   "%s: err in send_and_recv_msgs", __func__);
+		return ret;
+	}
+
+	*num = param.num;
+
+	for (i = 0; i < *num; i++) {
+		wpa_printf(MSG_DEBUG, "nl80211: preferred_channel_list[%d]=%d",
+			   i, freq_list[i]);
+	}
+
+	return 0;
+}
+
+
+static int nl80211_set_prob_oper_freq(void *priv, unsigned int freq)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	struct nl_msg *msg;
+	int ret;
+	struct nlattr *params;
+
+	if (!drv->set_prob_oper_freq)
+		return -1;
+
+	wpa_printf(MSG_DEBUG,
+		   "nl80211: Set P2P probable operating freq %u for ifindex %d",
+		   freq, bss->ifindex);
+
+	if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
+	    nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
+	    nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
+			QCA_NL80211_VENDOR_SUBCMD_SET_PROBABLE_OPER_CHANNEL) ||
+	    !(params = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA)) ||
+	    nla_put_u32(msg,
+			QCA_WLAN_VENDOR_ATTR_PROBABLE_OPER_CHANNEL_IFACE_TYPE,
+			QCA_IFACE_TYPE_P2P_CLIENT) ||
+	    nla_put_u32(msg,
+			QCA_WLAN_VENDOR_ATTR_PROBABLE_OPER_CHANNEL_FREQ,
+			freq)) {
+		wpa_printf(MSG_ERROR,
+			   "%s: err in adding vendor_cmd and vendor_data",
+			   __func__);
+		nlmsg_free(msg);
+		return -1;
+	}
+	nla_nest_end(msg, params);
+
+	ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+	msg = NULL;
+	if (ret) {
+		wpa_printf(MSG_ERROR, "%s: err in send_and_recv_msgs",
+			   __func__);
+		return ret;
+	}
+	nlmsg_free(msg);
+	return 0;
+}
+
+#endif /* CONFIG_DRIVER_NL80211_QCA */
+
+
 const struct wpa_driver_ops wpa_driver_nl80211_ops = {
 	.name = "nl80211",
 	.desc = "Linux nl80211/cfg80211",
@@ -8480,6 +8849,7 @@
 	.sched_scan = wpa_driver_nl80211_sched_scan,
 	.stop_sched_scan = wpa_driver_nl80211_stop_sched_scan,
 	.get_scan_results2 = wpa_driver_nl80211_get_scan_results,
+	.abort_scan = wpa_driver_nl80211_abort_scan,
 	.deauthenticate = driver_nl80211_deauthenticate,
 	.authenticate = driver_nl80211_authenticate,
 	.associate = wpa_driver_nl80211_associate,
@@ -8563,7 +8933,9 @@
 	.vendor_cmd = nl80211_vendor_cmd,
 	.set_qos_map = nl80211_set_qos_map,
 	.set_wowlan = nl80211_set_wowlan,
+#ifdef CONFIG_DRIVER_NL80211_QCA
 	.roaming = nl80211_roaming,
+#endif /* CONFIG_DRIVER_NL80211_QCA */
 	.set_mac_addr = nl80211_set_mac_addr,
 #ifdef CONFIG_MESH
 	.init_mesh = wpa_driver_nl80211_init_mesh,
@@ -8576,6 +8948,10 @@
 	.br_set_net_param = wpa_driver_br_set_net_param,
 	.add_tx_ts = nl80211_add_ts,
 	.del_tx_ts = nl80211_del_ts,
+#ifdef CONFIG_DRIVER_NL80211_QCA
 	.do_acs = wpa_driver_do_acs,
 	.set_band = nl80211_set_band,
+	.get_pref_freq_list = nl80211_get_pref_freq_list,
+	.set_prob_oper_freq = nl80211_set_prob_oper_freq,
+#endif /* CONFIG_DRIVER_NL80211_QCA */
 };
diff --git a/src/drivers/driver_nl80211.h b/src/drivers/driver_nl80211.h
index 1536d2f..21c0b6d 100644
--- a/src/drivers/driver_nl80211.h
+++ b/src/drivers/driver_nl80211.h
@@ -146,9 +146,16 @@
 	unsigned int set_rekey_offload:1;
 	unsigned int p2p_go_ctwindow_supported:1;
 	unsigned int setband_vendor_cmd_avail:1;
+	unsigned int get_pref_freq_list:1;
+	unsigned int set_prob_oper_freq:1;
+	unsigned int scan_vendor_cmd_avail:1;
 
+	u64 vendor_scan_cookie;
 	u64 remain_on_chan_cookie;
 	u64 send_action_cookie;
+#define MAX_SEND_ACTION_COOKIES 20
+	u64 send_action_cookies[MAX_SEND_ACTION_COOKIES];
+	unsigned int num_send_action_cookies;
 
 	unsigned int last_mgmt_freq;
 
@@ -180,6 +187,13 @@
 	int auth_wep_tx_keyidx;
 	int auth_local_state_change;
 	int auth_p2p;
+
+	/*
+	 * Tells whether the last scan issued from wpa_supplicant was a normal
+	 * scan (NL80211_CMD_TRIGGER_SCAN) or a vendor scan
+	 * (NL80211_CMD_VENDOR). 0 if no pending scan request.
+	 */
+	int last_scan_cmd;
 };
 
 struct nl_msg;
@@ -265,11 +279,13 @@
 int wpa_driver_nl80211_scan(struct i802_bss *bss,
 			    struct wpa_driver_scan_params *params);
 int wpa_driver_nl80211_sched_scan(void *priv,
-				  struct wpa_driver_scan_params *params,
-				  u32 interval);
+				  struct wpa_driver_scan_params *params);
 int wpa_driver_nl80211_stop_sched_scan(void *priv);
 struct wpa_scan_results * wpa_driver_nl80211_get_scan_results(void *priv);
 void nl80211_dump_scan(struct wpa_driver_nl80211_data *drv);
+int wpa_driver_nl80211_abort_scan(void *priv);
 const u8 * nl80211_get_ie(const u8 *ies, size_t ies_len, u8 ie);
+int wpa_driver_nl80211_vendor_scan(struct i802_bss *bss,
+				   struct wpa_driver_scan_params *params);
 
 #endif /* DRIVER_NL80211_H */
diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c
index 4929cea..c74ed5f 100644
--- a/src/drivers/driver_nl80211_capa.c
+++ b/src/drivers/driver_nl80211_capa.c
@@ -499,6 +499,19 @@
 		capa->max_sched_scan_ssids =
 			nla_get_u8(tb[NL80211_ATTR_MAX_NUM_SCHED_SCAN_SSIDS]);
 
+	if (tb[NL80211_ATTR_MAX_NUM_SCHED_SCAN_PLANS] &&
+	    tb[NL80211_ATTR_MAX_SCAN_PLAN_INTERVAL] &&
+	    tb[NL80211_ATTR_MAX_SCAN_PLAN_ITERATIONS]) {
+		capa->max_sched_scan_plans =
+			nla_get_u32(tb[NL80211_ATTR_MAX_NUM_SCHED_SCAN_PLANS]);
+
+		capa->max_sched_scan_plan_interval =
+			nla_get_u32(tb[NL80211_ATTR_MAX_SCAN_PLAN_INTERVAL]);
+
+		capa->max_sched_scan_plan_iterations =
+			nla_get_u32(tb[NL80211_ATTR_MAX_SCAN_PLAN_ITERATIONS]);
+	}
+
 	if (tb[NL80211_ATTR_MAX_MATCH_SETS])
 		capa->max_match_sets =
 			nla_get_u8(tb[NL80211_ATTR_MAX_MATCH_SETS]);
@@ -580,6 +593,7 @@
 				case QCA_NL80211_VENDOR_SUBCMD_TEST:
 					drv->vendor_cmd_test_avail = 1;
 					break;
+#ifdef CONFIG_DRIVER_NL80211_QCA
 				case QCA_NL80211_VENDOR_SUBCMD_ROAMING:
 					drv->roaming_vendor_cmd_avail = 1;
 					break;
@@ -589,6 +603,12 @@
 				case QCA_NL80211_VENDOR_SUBCMD_GET_FEATURES:
 					drv->get_features_vendor_cmd_avail = 1;
 					break;
+				case QCA_NL80211_VENDOR_SUBCMD_GET_PREFERRED_FREQ_LIST:
+					drv->get_pref_freq_list = 1;
+					break;
+				case QCA_NL80211_VENDOR_SUBCMD_SET_PROBABLE_OPER_CHANNEL:
+					drv->set_prob_oper_freq = 1;
+					break;
 				case QCA_NL80211_VENDOR_SUBCMD_DO_ACS:
 					drv->capa.flags |=
 						WPA_DRIVER_FLAGS_ACS_OFFLOAD;
@@ -596,6 +616,10 @@
 				case QCA_NL80211_VENDOR_SUBCMD_SETBAND:
 					drv->setband_vendor_cmd_avail = 1;
 					break;
+				case QCA_NL80211_VENDOR_SUBCMD_TRIGGER_SCAN:
+					drv->scan_vendor_cmd_avail = 1;
+					break;
+#endif /* CONFIG_DRIVER_NL80211_QCA */
 				}
 			}
 
@@ -627,6 +651,10 @@
 		capa->max_stations =
 			nla_get_u32(tb[NL80211_ATTR_MAX_AP_ASSOC_STA]);
 
+	if (tb[NL80211_ATTR_MAX_CSA_COUNTERS])
+		capa->max_csa_counters =
+			nla_get_u8(tb[NL80211_ATTR_MAX_CSA_COUNTERS]);
+
 	return NL_SKIP;
 }
 
@@ -683,8 +711,6 @@
 	if (!drv->capa.max_remain_on_chan)
 		drv->capa.max_remain_on_chan = 5000;
 
-	if (info->channel_switch_supported)
-		drv->capa.flags |= WPA_DRIVER_FLAGS_AP_CSA;
 	drv->capa.wmm_ac_supported = info->wmm_ac_supported;
 
 	drv->capa.mac_addr_rand_sched_scan_supported =
@@ -692,10 +718,24 @@
 	drv->capa.mac_addr_rand_scan_supported =
 		info->mac_addr_rand_scan_supported;
 
+	if (info->channel_switch_supported) {
+		drv->capa.flags |= WPA_DRIVER_FLAGS_AP_CSA;
+		if (!drv->capa.max_csa_counters)
+			drv->capa.max_csa_counters = 1;
+	}
+
+	if (!drv->capa.max_sched_scan_plans) {
+		drv->capa.max_sched_scan_plans = 1;
+		drv->capa.max_sched_scan_plan_interval = UINT32_MAX;
+		drv->capa.max_sched_scan_plan_iterations = 0;
+	}
+
 	return 0;
 }
 
 
+#ifdef CONFIG_DRIVER_NL80211_QCA
+
 static int dfs_info_handler(struct nl_msg *msg, void *arg)
 {
 	struct nlattr *tb[NL80211_ATTR_MAX + 1];
@@ -751,6 +791,7 @@
 struct features_info {
 	u8 *flags;
 	size_t flags_len;
+	struct wpa_driver_capa *capa;
 };
 
 
@@ -776,6 +817,19 @@
 			info->flags = nla_data(attr);
 			info->flags_len = nla_len(attr);
 		}
+		attr = tb_vendor[QCA_WLAN_VENDOR_ATTR_CONCURRENCY_CAPA];
+		if (attr)
+			info->capa->conc_capab = nla_get_u32(attr);
+
+		attr = tb_vendor[
+			QCA_WLAN_VENDOR_ATTR_MAX_CONCURRENT_CHANNELS_2_4_BAND];
+		if (attr)
+			info->capa->max_conc_chan_2_4 = nla_get_u32(attr);
+
+		attr = tb_vendor[
+			QCA_WLAN_VENDOR_ATTR_MAX_CONCURRENT_CHANNELS_5_0_BAND];
+		if (attr)
+			info->capa->max_conc_chan_5_0 = nla_get_u32(attr);
 	}
 
 	return NL_SKIP;
@@ -810,6 +864,7 @@
 	}
 
 	os_memset(&info, 0, sizeof(info));
+	info.capa = &drv->capa;
 	ret = send_and_recv_msgs(drv, msg, features_info_handler, &info);
 	if (ret || !info.flags)
 		return;
@@ -819,8 +874,14 @@
 
 	if (check_feature(QCA_WLAN_VENDOR_FEATURE_SUPPORT_HW_MODE_ANY, &info))
 		drv->capa.flags |= WPA_DRIVER_FLAGS_SUPPORT_HW_MODE_ANY;
+
+	if (check_feature(QCA_WLAN_VENDOR_FEATURE_OFFCHANNEL_SIMULTANEOUS,
+			  &info))
+		drv->capa.flags |= WPA_DRIVER_FLAGS_OFFCHANNEL_SIMULTANEOUS;
 }
 
+#endif /* CONFIG_DRIVER_NL80211_QCA */
+
 
 int wpa_driver_nl80211_capa(struct wpa_driver_nl80211_data *drv)
 {
@@ -901,9 +962,21 @@
 	if (!drv->use_monitor && !info.data_tx_status)
 		drv->capa.flags &= ~WPA_DRIVER_FLAGS_EAPOL_TX_STATUS;
 
+#ifdef CONFIG_DRIVER_NL80211_QCA
 	qca_nl80211_check_dfs_capa(drv);
 	qca_nl80211_get_features(drv);
 
+	/*
+	 * To enable offchannel simultaneous support in wpa_supplicant, the
+	 * underlying driver needs to support the same along with offchannel TX.
+	 * Offchannel TX support is needed since remain_on_channel and
+	 * action_tx use some common data structures and hence cannot be
+	 * scheduled simultaneously.
+	 */
+	if (!(drv->capa.flags & WPA_DRIVER_FLAGS_OFFCHANNEL_TX))
+		drv->capa.flags &= ~WPA_DRIVER_FLAGS_OFFCHANNEL_SIMULTANEOUS;
+#endif /* CONFIG_DRIVER_NL80211_QCA */
+
 	return 0;
 }
 
@@ -912,6 +985,7 @@
 	u16 *num_modes;
 	struct hostapd_hw_modes *modes;
 	int last_mode, last_chan_idx;
+	int failed;
 };
 
 static void phy_info_ht_capa(struct hostapd_hw_modes *mode, struct nlattr *capa,
@@ -1029,7 +1103,7 @@
 				   mode->num_channels + new_channels,
 				   sizeof(struct hostapd_channel_data));
 	if (!channel)
-		return NL_SKIP;
+		return NL_STOP;
 
 	mode->channels = channel;
 	mode->num_channels += new_channels;
@@ -1075,7 +1149,7 @@
 
 	mode->rates = os_calloc(mode->num_rates, sizeof(int));
 	if (!mode->rates)
-		return NL_SKIP;
+		return NL_STOP;
 
 	idx = 0;
 
@@ -1104,8 +1178,10 @@
 		mode = os_realloc_array(phy_info->modes,
 					*phy_info->num_modes + 1,
 					sizeof(*mode));
-		if (!mode)
-			return NL_SKIP;
+		if (!mode) {
+			phy_info->failed = 1;
+			return NL_STOP;
+		}
 		phy_info->modes = mode;
 
 		mode = &phy_info->modes[*(phy_info->num_modes)];
@@ -1141,11 +1217,12 @@
 	phy_info_vht_capa(mode, tb_band[NL80211_BAND_ATTR_VHT_CAPA],
 			  tb_band[NL80211_BAND_ATTR_VHT_MCS_SET]);
 	ret = phy_info_freqs(phy_info, mode, tb_band[NL80211_BAND_ATTR_FREQS]);
-	if (ret != NL_OK)
+	if (ret == NL_OK)
+		ret = phy_info_rates(mode, tb_band[NL80211_BAND_ATTR_RATES]);
+	if (ret != NL_OK) {
+		phy_info->failed = 1;
 		return ret;
-	ret = phy_info_rates(mode, tb_band[NL80211_BAND_ATTR_RATES]);
-	if (ret != NL_OK)
-		return ret;
+	}
 
 	return NL_OK;
 }
@@ -1360,7 +1437,7 @@
 
 
 static void nl80211_set_vht_mode(struct hostapd_hw_modes *mode, int start,
-				 int end)
+				 int end, int max_bw)
 {
 	int c;
 
@@ -1377,6 +1454,32 @@
 
 		if (chan->freq - 70 >= start && chan->freq + 10 <= end)
 			chan->flag |= HOSTAPD_CHAN_VHT_70_10;
+
+		if (max_bw >= 160) {
+			if (chan->freq - 10 >= start && chan->freq + 150 <= end)
+				chan->flag |= HOSTAPD_CHAN_VHT_10_150;
+
+			if (chan->freq - 30 >= start && chan->freq + 130 <= end)
+				chan->flag |= HOSTAPD_CHAN_VHT_30_130;
+
+			if (chan->freq - 50 >= start && chan->freq + 110 <= end)
+				chan->flag |= HOSTAPD_CHAN_VHT_50_110;
+
+			if (chan->freq - 70 >= start && chan->freq + 90 <= end)
+				chan->flag |= HOSTAPD_CHAN_VHT_70_90;
+
+			if (chan->freq - 90 >= start && chan->freq + 70 <= end)
+				chan->flag |= HOSTAPD_CHAN_VHT_90_70;
+
+			if (chan->freq - 110 >= start && chan->freq + 50 <= end)
+				chan->flag |= HOSTAPD_CHAN_VHT_110_50;
+
+			if (chan->freq - 130 >= start && chan->freq + 30 <= end)
+				chan->flag |= HOSTAPD_CHAN_VHT_130_30;
+
+			if (chan->freq - 150 >= start && chan->freq + 10 <= end)
+				chan->flag |= HOSTAPD_CHAN_VHT_150_10;
+		}
 	}
 }
 
@@ -1407,7 +1510,7 @@
 		if (!results->modes[m].vht_capab)
 			continue;
 
-		nl80211_set_vht_mode(&results->modes[m], start, end);
+		nl80211_set_vht_mode(&results->modes[m], start, end, max_bw);
 	}
 }
 
@@ -1545,6 +1648,7 @@
 		.num_modes = num_modes,
 		.modes = NULL,
 		.last_mode = -1,
+		.failed = 0,
 	};
 
 	*num_modes = 0;
@@ -1561,6 +1665,16 @@
 
 	if (send_and_recv_msgs(drv, msg, phy_info_handler, &result) == 0) {
 		nl80211_set_regulatory_flags(drv, &result);
+		if (result.failed) {
+			int i;
+
+			for (i = 0; result.modes && i < *num_modes; i++) {
+				os_free(result.modes[i].channels);
+				os_free(result.modes[i].rates);
+			}
+			os_free(result.modes);
+			return NULL;
+		}
 		return wpa_driver_nl80211_postprocess_modes(result.modes,
 							    num_modes);
 	}
diff --git a/src/drivers/driver_nl80211_event.c b/src/drivers/driver_nl80211_event.c
index 7b0f721..4d7ac47 100644
--- a/src/drivers/driver_nl80211_event.c
+++ b/src/drivers/driver_nl80211_event.c
@@ -268,7 +268,8 @@
 			       struct nlattr *authorized,
 			       struct nlattr *key_replay_ctr,
 			       struct nlattr *ptk_kck,
-			       struct nlattr *ptk_kek)
+			       struct nlattr *ptk_kek,
+			       struct nlattr *subnet_status)
 {
 	union wpa_event_data event;
 	const u8 *ssid;
@@ -367,6 +368,17 @@
 		event.assoc_info.ptk_kek_len = nla_len(ptk_kek);
 	}
 
+	if (subnet_status) {
+		/*
+		 * At least for now, this is only available from
+		 * QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_SUBNET_STATUS and that
+		 * attribute has the same values 0, 1, 2 as are used in the
+		 * variable here, so no mapping between different values are
+		 * needed.
+		 */
+		event.assoc_info.subnet_status = nla_get_u8(subnet_status);
+	}
+
 	wpa_supplicant_event(drv->ctx, EVENT_ASSOC, &event);
 }
 
@@ -639,10 +651,21 @@
 			 * Avoid issues with some roaming cases where
 			 * disconnection event for the old AP may show up after
 			 * we have started connection with the new AP.
+			 * In case of locally generated event clear
+			 * ignore_next_local_deauth as well, to avoid next local
+			 * deauth event be wrongly ignored.
 			 */
-			wpa_printf(MSG_DEBUG, "nl80211: Ignore deauth/disassoc event from old AP " MACSTR " when already authenticating with " MACSTR,
-				   MAC2STR(bssid),
-				   MAC2STR(drv->auth_attempt_bssid));
+			if (!os_memcmp(mgmt->sa, drv->first_bss->addr,
+				       ETH_ALEN)) {
+				wpa_printf(MSG_DEBUG,
+					   "nl80211: Received a locally generated deauth event. Clear ignore_next_local_deauth flag");
+				drv->ignore_next_local_deauth = 0;
+			} else {
+				wpa_printf(MSG_DEBUG,
+					   "nl80211: Ignore deauth/disassoc event from old AP " MACSTR " when already authenticating with " MACSTR,
+					   MAC2STR(bssid),
+					   MAC2STR(drv->auth_attempt_bssid));
+			}
 			return;
 		}
 
@@ -679,13 +702,15 @@
 				mgmt->u.disassoc.variable;
 		}
 	} else {
+		event.deauth_info.locally_generated =
+			!os_memcmp(mgmt->sa, drv->first_bss->addr, ETH_ALEN);
 		if (drv->ignore_deauth_event) {
 			wpa_printf(MSG_DEBUG, "nl80211: Ignore deauth event due to previous forced deauth-during-auth");
 			drv->ignore_deauth_event = 0;
+			if (event.deauth_info.locally_generated)
+				drv->ignore_next_local_deauth = 0;
 			return;
 		}
-		event.deauth_info.locally_generated =
-			!os_memcmp(mgmt->sa, drv->first_bss->addr, ETH_ALEN);
 		if (drv->ignore_next_local_deauth) {
 			drv->ignore_next_local_deauth = 0;
 			if (event.deauth_info.locally_generated) {
@@ -968,7 +993,7 @@
 
 
 static void send_scan_event(struct wpa_driver_nl80211_data *drv, int aborted,
-			    struct nlattr *tb[])
+			    struct nlattr *tb[], int external_scan)
 {
 	union wpa_event_data event;
 	struct nlattr *nl;
@@ -978,7 +1003,7 @@
 	int freqs[MAX_REPORT_FREQS];
 	int num_freqs = 0;
 
-	if (drv->scan_for_auth) {
+	if (!external_scan && drv->scan_for_auth) {
 		drv->scan_for_auth = 0;
 		wpa_printf(MSG_DEBUG, "nl80211: Scan results for missing "
 			   "cfg80211 BSS entry");
@@ -989,6 +1014,8 @@
 	os_memset(&event, 0, sizeof(event));
 	info = &event.scan_info;
 	info->aborted = aborted;
+	info->external_scan = external_scan;
+	info->nl_scan_event = 1;
 
 	if (tb[NL80211_ATTR_SCAN_SSIDS]) {
 		nla_for_each_nested(nl, tb[NL80211_ATTR_SCAN_SSIDS], rem) {
@@ -1004,7 +1031,7 @@
 		}
 	}
 	if (tb[NL80211_ATTR_SCAN_FREQUENCIES]) {
-		char msg[200], *pos, *end;
+		char msg[300], *pos, *end;
 		int res;
 
 		pos = msg;
@@ -1444,6 +1471,8 @@
 }
 
 
+#ifdef CONFIG_DRIVER_NL80211_QCA
+
 static void qca_nl80211_avoid_freq(struct wpa_driver_nl80211_data *drv,
 				   const u8 *data, size_t len)
 {
@@ -1596,7 +1625,8 @@
 			   tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_AUTHORIZED],
 			   tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_KEY_REPLAY_CTR],
 			   tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_PTK_KCK],
-			   tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_PTK_KEK]);
+			   tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_PTK_KEK],
+			   tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_SUBNET_STATUS]);
 }
 
 
@@ -1686,6 +1716,140 @@
 }
 
 
+static void qca_nl80211_scan_trigger_event(struct wpa_driver_nl80211_data *drv,
+					   u8 *data, size_t len)
+{
+	struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_SCAN_MAX + 1];
+	u64 cookie = 0;
+	union wpa_event_data event;
+	struct scan_info *info;
+
+	if (nla_parse(tb, QCA_WLAN_VENDOR_ATTR_SCAN_MAX,
+		      (struct nlattr *) data, len, NULL) ||
+	    !tb[QCA_WLAN_VENDOR_ATTR_SCAN_COOKIE])
+		return;
+
+	cookie = nla_get_u64(tb[QCA_WLAN_VENDOR_ATTR_SCAN_COOKIE]);
+	if (cookie != drv->vendor_scan_cookie) {
+		/* External scan trigger event, ignore */
+		return;
+	}
+
+	/* Cookie match, own scan */
+	os_memset(&event, 0, sizeof(event));
+	info = &event.scan_info;
+	info->external_scan = 0;
+	info->nl_scan_event = 0;
+
+	drv->scan_state = SCAN_STARTED;
+	wpa_supplicant_event(drv->ctx, EVENT_SCAN_STARTED, &event);
+}
+
+
+static void send_vendor_scan_event(struct wpa_driver_nl80211_data *drv,
+				   int aborted, struct nlattr *tb[],
+				   int external_scan)
+{
+	union wpa_event_data event;
+	struct nlattr *nl;
+	int rem;
+	struct scan_info *info;
+	int freqs[MAX_REPORT_FREQS];
+	int num_freqs = 0;
+
+	os_memset(&event, 0, sizeof(event));
+	info = &event.scan_info;
+	info->aborted = aborted;
+	info->external_scan = external_scan;
+
+	if (tb[QCA_WLAN_VENDOR_ATTR_SCAN_SSIDS]) {
+		nla_for_each_nested(nl,
+				    tb[QCA_WLAN_VENDOR_ATTR_SCAN_SSIDS], rem) {
+			struct wpa_driver_scan_ssid *s =
+				&info->ssids[info->num_ssids];
+			s->ssid = nla_data(nl);
+			s->ssid_len = nla_len(nl);
+			wpa_printf(MSG_DEBUG,
+				   "nl80211: Scan probed for SSID '%s'",
+				   wpa_ssid_txt(s->ssid, s->ssid_len));
+			info->num_ssids++;
+			if (info->num_ssids == WPAS_MAX_SCAN_SSIDS)
+				break;
+		}
+	}
+
+	if (tb[QCA_WLAN_VENDOR_ATTR_SCAN_FREQUENCIES]) {
+		char msg[300], *pos, *end;
+		int res;
+
+		pos = msg;
+		end = pos + sizeof(msg);
+		*pos = '\0';
+
+		nla_for_each_nested(nl,
+				    tb[QCA_WLAN_VENDOR_ATTR_SCAN_FREQUENCIES],
+				    rem) {
+			freqs[num_freqs] = nla_get_u32(nl);
+			res = os_snprintf(pos, end - pos, " %d",
+					  freqs[num_freqs]);
+			if (!os_snprintf_error(end - pos, res))
+				pos += res;
+			num_freqs++;
+			if (num_freqs == MAX_REPORT_FREQS - 1)
+				break;
+		}
+
+		info->freqs = freqs;
+		info->num_freqs = num_freqs;
+		wpa_printf(MSG_DEBUG, "nl80211: Scan included frequencies:%s",
+			   msg);
+	}
+	wpa_supplicant_event(drv->ctx, EVENT_SCAN_RESULTS, &event);
+}
+
+
+static void qca_nl80211_scan_done_event(struct wpa_driver_nl80211_data *drv,
+					u8 *data, size_t len)
+{
+	struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_SCAN_MAX + 1];
+	u64 cookie = 0;
+	enum scan_status status;
+	int external_scan;
+
+	if (nla_parse(tb, QCA_WLAN_VENDOR_ATTR_SCAN_MAX,
+		      (struct nlattr *) data, len, NULL) ||
+	    !tb[QCA_WLAN_VENDOR_ATTR_SCAN_STATUS] ||
+	    !tb[QCA_WLAN_VENDOR_ATTR_SCAN_COOKIE])
+		return;
+
+	status = nla_get_u8(tb[QCA_WLAN_VENDOR_ATTR_SCAN_STATUS]);
+	if (status >= VENDOR_SCAN_STATUS_MAX)
+		return; /* invalid status */
+
+	cookie = nla_get_u64(tb[QCA_WLAN_VENDOR_ATTR_SCAN_COOKIE]);
+	if (cookie != drv->vendor_scan_cookie) {
+		/* Event from an external scan, get scan results */
+		external_scan = 1;
+	} else {
+		external_scan = 0;
+		if (status == VENDOR_SCAN_STATUS_NEW_RESULTS)
+			drv->scan_state = SCAN_COMPLETED;
+		else
+			drv->scan_state = SCAN_ABORTED;
+
+		eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, drv,
+				     drv->ctx);
+		drv->vendor_scan_cookie = 0;
+		drv->last_scan_cmd = 0;
+	}
+
+	send_vendor_scan_event(drv, (status == VENDOR_SCAN_STATUS_ABORTED), tb,
+			       external_scan);
+}
+
+#endif /* CONFIG_DRIVER_NL80211_QCA */
+
+
 static void nl80211_vendor_event_qca(struct wpa_driver_nl80211_data *drv,
 				     u32 subcmd, u8 *data, size_t len)
 {
@@ -1693,6 +1857,7 @@
 	case QCA_NL80211_VENDOR_SUBCMD_TEST:
 		wpa_hexdump(MSG_DEBUG, "nl80211: QCA test event", data, len);
 		break;
+#ifdef CONFIG_DRIVER_NL80211_QCA
 	case QCA_NL80211_VENDOR_SUBCMD_AVOID_FREQUENCY:
 		qca_nl80211_avoid_freq(drv, data, len);
 		break;
@@ -1709,6 +1874,13 @@
 	case QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_RADAR_DETECTED:
 		qca_nl80211_dfs_offload_radar_event(drv, subcmd, data, len);
 		break;
+	case QCA_NL80211_VENDOR_SUBCMD_TRIGGER_SCAN:
+		qca_nl80211_scan_trigger_event(drv, data, len);
+		break;
+	case QCA_NL80211_VENDOR_SUBCMD_SCAN_DONE:
+		qca_nl80211_scan_done_event(drv, data, len);
+		break;
+#endif /* CONFIG_DRIVER_NL80211_QCA */
 	default:
 		wpa_printf(MSG_DEBUG,
 			   "nl80211: Ignore unsupported QCA vendor event %u",
@@ -1831,6 +2003,7 @@
 {
 	struct wpa_driver_nl80211_data *drv = bss->drv;
 	union wpa_event_data data;
+	int external_scan_event = 0;
 
 	wpa_printf(MSG_DEBUG, "nl80211: Drv Event %d (%s) received for %s",
 		   cmd, nl80211_command_to_string(cmd), bss->ifname);
@@ -1883,28 +2056,38 @@
 	case NL80211_CMD_NEW_SCAN_RESULTS:
 		wpa_dbg(drv->ctx, MSG_DEBUG,
 			"nl80211: New scan results available");
-		drv->scan_state = SCAN_COMPLETED;
 		drv->scan_complete_events = 1;
-		eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, drv,
-				     drv->ctx);
-		send_scan_event(drv, 0, tb);
+		if (drv->last_scan_cmd == NL80211_CMD_TRIGGER_SCAN) {
+			drv->scan_state = SCAN_COMPLETED;
+			eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout,
+					     drv, drv->ctx);
+			drv->last_scan_cmd = 0;
+		} else {
+			external_scan_event = 1;
+		}
+		send_scan_event(drv, 0, tb, external_scan_event);
 		break;
 	case NL80211_CMD_SCHED_SCAN_RESULTS:
 		wpa_dbg(drv->ctx, MSG_DEBUG,
 			"nl80211: New sched scan results available");
 		drv->scan_state = SCHED_SCAN_RESULTS;
-		send_scan_event(drv, 0, tb);
+		send_scan_event(drv, 0, tb, 0);
 		break;
 	case NL80211_CMD_SCAN_ABORTED:
 		wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: Scan aborted");
-		drv->scan_state = SCAN_ABORTED;
-		/*
-		 * Need to indicate that scan results are available in order
-		 * not to make wpa_supplicant stop its scanning.
-		 */
-		eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, drv,
-				     drv->ctx);
-		send_scan_event(drv, 1, tb);
+		if (drv->last_scan_cmd == NL80211_CMD_TRIGGER_SCAN) {
+			drv->scan_state = SCAN_ABORTED;
+			/*
+			 * Need to indicate that scan results are available in
+			 * order not to make wpa_supplicant stop its scanning.
+			 */
+			eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout,
+					     drv, drv->ctx);
+			drv->last_scan_cmd = 0;
+		} else {
+			external_scan_event = 1;
+		}
+		send_scan_event(drv, 1, tb, external_scan_event);
 		break;
 	case NL80211_CMD_AUTHENTICATE:
 	case NL80211_CMD_ASSOCIATE:
@@ -1927,7 +2110,7 @@
 				   tb[NL80211_ATTR_MAC],
 				   tb[NL80211_ATTR_REQ_IE],
 				   tb[NL80211_ATTR_RESP_IE],
-				   NULL, NULL, NULL, NULL);
+				   NULL, NULL, NULL, NULL, NULL);
 		break;
 	case NL80211_CMD_CH_SWITCH_NOTIFY:
 		mlme_event_ch_switch(drv,
diff --git a/src/drivers/driver_nl80211_scan.c b/src/drivers/driver_nl80211_scan.c
index f3d45e5..2ff254e 100644
--- a/src/drivers/driver_nl80211_scan.c
+++ b/src/drivers/driver_nl80211_scan.c
@@ -1,5 +1,6 @@
 /*
  * Driver interaction with Linux nl80211/cfg80211 - Scanning
+ * Copyright(c) 2015 Intel Deutschland GmbH
  * Copyright (c) 2002-2014, Jouni Malinen <j@w1.fi>
  * Copyright (c) 2007, Johannes Berg <johannes@sipsolutions.net>
  * Copyright (c) 2009-2010, Atheros Communications
@@ -14,6 +15,7 @@
 #include "utils/common.h"
 #include "utils/eloop.h"
 #include "common/ieee802_11_defs.h"
+#include "common/qca-vendor.h"
 #include "driver_nl80211.h"
 
 
@@ -131,6 +133,8 @@
 				goto fail;
 		}
 		nla_nest_end(msg, ssids);
+	} else {
+		wpa_printf(MSG_DEBUG, "nl80211: Passive scan requested");
 	}
 
 	if (params->extra_ies) {
@@ -221,6 +225,9 @@
 	wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: scan request");
 	drv->scan_for_auth = 0;
 
+	if (TEST_FAIL())
+		return -1;
+
 	msg = nl80211_scan_common(bss, NL80211_CMD_TRIGGER_SCAN, params);
 	if (!msg)
 		return -1;
@@ -294,6 +301,7 @@
 	eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, drv, drv->ctx);
 	eloop_register_timeout(timeout, 0, wpa_driver_nl80211_scan_timeout,
 			       drv, drv->ctx);
+	drv->last_scan_cmd = NL80211_CMD_TRIGGER_SCAN;
 
 fail:
 	nlmsg_free(msg);
@@ -301,16 +309,82 @@
 }
 
 
+static int
+nl80211_sched_scan_add_scan_plans(struct wpa_driver_nl80211_data *drv,
+				  struct nl_msg *msg,
+				  struct wpa_driver_scan_params *params)
+{
+	struct nlattr *plans;
+	struct sched_scan_plan *scan_plans = params->sched_scan_plans;
+	unsigned int i;
+
+	plans = nla_nest_start(msg, NL80211_ATTR_SCHED_SCAN_PLANS);
+	if (!plans)
+		return -1;
+
+	for (i = 0; i < params->sched_scan_plans_num; i++) {
+		struct nlattr *plan = nla_nest_start(msg, i + 1);
+
+		if (!plan)
+			return -1;
+
+		if (!scan_plans[i].interval ||
+		    scan_plans[i].interval >
+		    drv->capa.max_sched_scan_plan_interval) {
+			wpa_printf(MSG_DEBUG,
+				   "nl80211: sched scan plan no. %u: Invalid interval: %u",
+				   i, scan_plans[i].interval);
+			return -1;
+		}
+
+		if (nla_put_u32(msg, NL80211_SCHED_SCAN_PLAN_INTERVAL,
+				scan_plans[i].interval))
+			return -1;
+
+		if (scan_plans[i].iterations >
+		    drv->capa.max_sched_scan_plan_iterations) {
+			wpa_printf(MSG_DEBUG,
+				   "nl80211: sched scan plan no. %u: Invalid number of iterations: %u",
+				   i, scan_plans[i].iterations);
+			return -1;
+		}
+
+		if (scan_plans[i].iterations &&
+		    nla_put_u32(msg, NL80211_SCHED_SCAN_PLAN_ITERATIONS,
+				scan_plans[i].iterations))
+			return -1;
+
+		nla_nest_end(msg, plan);
+
+		/*
+		 * All the scan plans must specify the number of iterations
+		 * except the last plan, which will run infinitely. So if the
+		 * number of iterations is not specified, this ought to be the
+		 * last scan plan.
+		 */
+		if (!scan_plans[i].iterations)
+			break;
+	}
+
+	if (i != params->sched_scan_plans_num - 1) {
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: All sched scan plans but the last must specify number of iterations");
+		return -1;
+	}
+
+	nla_nest_end(msg, plans);
+	return 0;
+}
+
+
 /**
  * wpa_driver_nl80211_sched_scan - Initiate a scheduled scan
  * @priv: Pointer to private driver data from wpa_driver_nl80211_init()
  * @params: Scan parameters
- * @interval: Interval between scan cycles in milliseconds
  * Returns: 0 on success, -1 on failure or if not supported
  */
 int wpa_driver_nl80211_sched_scan(void *priv,
-				  struct wpa_driver_scan_params *params,
-				  u32 interval)
+				  struct wpa_driver_scan_params *params)
 {
 	struct i802_bss *bss = priv;
 	struct wpa_driver_nl80211_data *drv = bss->drv;
@@ -325,11 +399,27 @@
 		return android_pno_start(bss, params);
 #endif /* ANDROID */
 
+	if (!params->sched_scan_plans_num ||
+	    params->sched_scan_plans_num > drv->capa.max_sched_scan_plans) {
+		wpa_printf(MSG_ERROR,
+			   "nl80211: Invalid number of sched scan plans: %u",
+			   params->sched_scan_plans_num);
+		return -1;
+	}
+
 	msg = nl80211_scan_common(bss, NL80211_CMD_START_SCHED_SCAN, params);
-	if (!msg ||
-	    nla_put_u32(msg, NL80211_ATTR_SCHED_SCAN_INTERVAL, interval))
+	if (!msg)
 		goto fail;
 
+	if (drv->capa.max_sched_scan_plan_iterations) {
+		if (nl80211_sched_scan_add_scan_plans(drv, msg, params))
+			goto fail;
+	} else {
+		if (nla_put_u32(msg, NL80211_ATTR_SCHED_SCAN_INTERVAL,
+				params->sched_scan_plans[0].interval * 1000))
+			goto fail;
+	}
+
 	if ((drv->num_filter_ssids &&
 	    (int) drv->num_filter_ssids <= drv->capa.max_match_sets) ||
 	    params->filter_rssi) {
@@ -392,8 +482,7 @@
 		goto fail;
 	}
 
-	wpa_printf(MSG_DEBUG, "nl80211: Sched scan requested (ret=%d) - "
-		   "scan interval %d msec", ret, interval);
+	wpa_printf(MSG_DEBUG, "nl80211: Sched scan requested (ret=%d)", ret);
 
 fail:
 	nlmsg_free(msg);
@@ -443,8 +532,8 @@
 	pos = ies;
 	end = ies + ies_len;
 
-	while (pos + 1 < end) {
-		if (pos + 2 + pos[1] > end)
+	while (end - pos > 1) {
+		if (2 + pos[1] > end - pos)
 			break;
 		if (pos[0] == ie)
 			return pos;
@@ -778,3 +867,213 @@
 
 	wpa_scan_results_free(res);
 }
+
+
+int wpa_driver_nl80211_abort_scan(void *priv)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	int ret;
+	struct nl_msg *msg;
+
+	wpa_printf(MSG_DEBUG, "nl80211: Abort scan");
+	msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_ABORT_SCAN);
+	ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+	if (ret) {
+		wpa_printf(MSG_DEBUG, "nl80211: Abort scan failed: ret=%d (%s)",
+			   ret, strerror(-ret));
+	}
+
+	return ret;
+}
+
+
+#ifdef CONFIG_DRIVER_NL80211_QCA
+
+static int scan_cookie_handler(struct nl_msg *msg, void *arg)
+{
+	struct nlattr *tb[NL80211_ATTR_MAX + 1];
+	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+	u64 *cookie = arg;
+
+	nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+		  genlmsg_attrlen(gnlh, 0), NULL);
+
+	if (tb[NL80211_ATTR_VENDOR_DATA]) {
+		struct nlattr *nl_vendor = tb[NL80211_ATTR_VENDOR_DATA];
+		struct nlattr *tb_vendor[QCA_WLAN_VENDOR_ATTR_SCAN_MAX + 1];
+
+		nla_parse(tb_vendor, QCA_WLAN_VENDOR_ATTR_SCAN_MAX,
+			  nla_data(nl_vendor), nla_len(nl_vendor), NULL);
+
+		if (tb_vendor[QCA_WLAN_VENDOR_ATTR_SCAN_COOKIE])
+			*cookie = nla_get_u64(
+				tb_vendor[QCA_WLAN_VENDOR_ATTR_SCAN_COOKIE]);
+	}
+
+	return NL_SKIP;
+}
+
+
+/**
+ * wpa_driver_nl80211_vendor_scan - Request the driver to initiate a vendor scan
+ * @bss: Pointer to private driver data from wpa_driver_nl80211_init()
+ * @params: Scan parameters
+ * Returns: 0 on success, -1 on failure
+ */
+int wpa_driver_nl80211_vendor_scan(struct i802_bss *bss,
+				   struct wpa_driver_scan_params *params)
+{
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	struct nl_msg *msg = NULL;
+	struct nlattr *attr;
+	size_t i;
+	u32 scan_flags = 0;
+	int ret = -1;
+	u64 cookie = 0;
+
+	wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: vendor scan request");
+	drv->scan_for_auth = 0;
+
+	if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
+	    nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
+	    nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
+			QCA_NL80211_VENDOR_SUBCMD_TRIGGER_SCAN) )
+		goto fail;
+
+	attr = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA);
+	if (attr == NULL)
+		goto fail;
+
+	if (params->num_ssids) {
+		struct nlattr *ssids;
+
+		ssids = nla_nest_start(msg, QCA_WLAN_VENDOR_ATTR_SCAN_SSIDS);
+		if (ssids == NULL)
+			goto fail;
+		for (i = 0; i < params->num_ssids; i++) {
+			wpa_hexdump_ascii(MSG_MSGDUMP, "nl80211: Scan SSID",
+					params->ssids[i].ssid,
+					params->ssids[i].ssid_len);
+			if (nla_put(msg, i + 1, params->ssids[i].ssid_len,
+				    params->ssids[i].ssid))
+				goto fail;
+		}
+		nla_nest_end(msg, ssids);
+	}
+
+	if (params->extra_ies) {
+		wpa_hexdump(MSG_MSGDUMP, "nl80211: Scan extra IEs",
+			    params->extra_ies, params->extra_ies_len);
+		if (nla_put(msg, QCA_WLAN_VENDOR_ATTR_SCAN_IE,
+			    params->extra_ies_len, params->extra_ies))
+			goto fail;
+	}
+
+	if (params->freqs) {
+		struct nlattr *freqs;
+
+		freqs = nla_nest_start(msg,
+				       QCA_WLAN_VENDOR_ATTR_SCAN_FREQUENCIES);
+		if (freqs == NULL)
+			goto fail;
+		for (i = 0; params->freqs[i]; i++) {
+			wpa_printf(MSG_MSGDUMP,
+				   "nl80211: Scan frequency %u MHz",
+				   params->freqs[i]);
+			if (nla_put_u32(msg, i + 1, params->freqs[i]))
+				goto fail;
+		}
+		nla_nest_end(msg, freqs);
+	}
+
+	os_free(drv->filter_ssids);
+	drv->filter_ssids = params->filter_ssids;
+	params->filter_ssids = NULL;
+	drv->num_filter_ssids = params->num_filter_ssids;
+
+	if (params->low_priority && drv->have_low_prio_scan) {
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: Add NL80211_SCAN_FLAG_LOW_PRIORITY");
+		scan_flags |= NL80211_SCAN_FLAG_LOW_PRIORITY;
+	}
+
+	if (params->mac_addr_rand) {
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: Add NL80211_SCAN_FLAG_RANDOM_ADDR");
+		scan_flags |= NL80211_SCAN_FLAG_RANDOM_ADDR;
+
+		if (params->mac_addr) {
+			wpa_printf(MSG_DEBUG, "nl80211: MAC address: " MACSTR,
+				   MAC2STR(params->mac_addr));
+			if (nla_put(msg, QCA_WLAN_VENDOR_ATTR_SCAN_MAC,
+				    ETH_ALEN, params->mac_addr))
+				goto fail;
+		}
+
+		if (params->mac_addr_mask) {
+			wpa_printf(MSG_DEBUG, "nl80211: MAC address mask: "
+				   MACSTR, MAC2STR(params->mac_addr_mask));
+			if (nla_put(msg, QCA_WLAN_VENDOR_ATTR_SCAN_MAC_MASK,
+				    ETH_ALEN, params->mac_addr_mask))
+				goto fail;
+		}
+	}
+
+	if (scan_flags &&
+	    nla_put_u32(msg, NL80211_ATTR_SCAN_FLAGS, scan_flags))
+		goto fail;
+
+	if (params->p2p_probe) {
+		struct nlattr *rates;
+
+		wpa_printf(MSG_DEBUG, "nl80211: P2P probe - mask SuppRates");
+
+		rates = nla_nest_start(msg,
+				       QCA_WLAN_VENDOR_ATTR_SCAN_SUPP_RATES);
+		if (rates == NULL)
+			goto fail;
+
+		/*
+		 * Remove 2.4 GHz rates 1, 2, 5.5, 11 Mbps from supported rates
+		 * by masking out everything else apart from the OFDM rates 6,
+		 * 9, 12, 18, 24, 36, 48, 54 Mbps from non-MCS rates. All 5 GHz
+		 * rates are left enabled.
+		 */
+		if (nla_put(msg, NL80211_BAND_2GHZ, 8,
+			    "\x0c\x12\x18\x24\x30\x48\x60\x6c"))
+			goto fail;
+		nla_nest_end(msg, rates);
+
+		if (nla_put_flag(msg, QCA_WLAN_VENDOR_ATTR_SCAN_TX_NO_CCK_RATE))
+			goto fail;
+	}
+
+	nla_nest_end(msg, attr);
+
+	ret = send_and_recv_msgs(drv, msg, scan_cookie_handler, &cookie);
+	msg = NULL;
+	if (ret) {
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: Vendor scan trigger failed: ret=%d (%s)",
+			   ret, strerror(-ret));
+		goto fail;
+	}
+
+	drv->vendor_scan_cookie = cookie;
+	drv->scan_state = SCAN_REQUESTED;
+
+	wpa_printf(MSG_DEBUG,
+		   "nl80211: Vendor scan requested (ret=%d) - scan timeout 30 seconds, scan cookie:0x%llx",
+		   ret, (long long unsigned int) cookie);
+	eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, drv, drv->ctx);
+	eloop_register_timeout(30, 0, wpa_driver_nl80211_scan_timeout,
+			       drv, drv->ctx);
+	drv->last_scan_cmd = NL80211_CMD_VENDOR;
+
+fail:
+	nlmsg_free(msg);
+	return ret;
+}
+
+#endif /* CONFIG_DRIVER_NL80211_QCA */
diff --git a/src/drivers/driver_privsep.c b/src/drivers/driver_privsep.c
index 1cfc15d..762c12f 100644
--- a/src/drivers/driver_privsep.c
+++ b/src/drivers/driver_privsep.c
@@ -161,11 +161,11 @@
 		return NULL;
 	}
 
-	while (results->num < (size_t) num && pos + sizeof(int) < end) {
+	while (results->num < (size_t) num && end - pos > sizeof(int)) {
 		int len;
 		os_memcpy(&len, pos, sizeof(int));
 		pos += sizeof(int);
-		if (len < 0 || len > 10000 || pos + len > end)
+		if (len < 0 || len > 10000 || len > end - pos)
 			break;
 
 		r = os_malloc(len);
@@ -220,6 +220,56 @@
 }
 
 
+static int wpa_driver_privsep_authenticate(
+	void *priv, struct wpa_driver_auth_params *params)
+{
+	struct wpa_driver_privsep_data *drv = priv;
+	struct privsep_cmd_authenticate *data;
+	int i, res;
+	size_t buflen;
+	u8 *pos;
+
+	wpa_printf(MSG_DEBUG, "%s: priv=%p freq=%d bssid=" MACSTR
+		   " auth_alg=%d local_state_change=%d p2p=%d",
+		   __func__, priv, params->freq, MAC2STR(params->bssid),
+		   params->auth_alg, params->local_state_change, params->p2p);
+
+	buflen = sizeof(*data) + params->ie_len + params->sae_data_len;
+	data = os_zalloc(buflen);
+	if (data == NULL)
+		return -1;
+
+	data->freq = params->freq;
+	os_memcpy(data->bssid, params->bssid, ETH_ALEN);
+	os_memcpy(data->ssid, params->ssid, params->ssid_len);
+	data->ssid_len = params->ssid_len;
+	data->auth_alg = params->auth_alg;
+	data->ie_len = params->ie_len;
+	for (i = 0; i < 4; i++) {
+		if (params->wep_key[i])
+			os_memcpy(data->wep_key[i], params->wep_key[i],
+				  params->wep_key_len[i]);
+		data->wep_key_len[i] = params->wep_key_len[i];
+	}
+	data->wep_tx_keyidx = params->wep_tx_keyidx;
+	data->local_state_change = params->local_state_change;
+	data->p2p = params->p2p;
+	pos = (u8 *) (data + 1);
+	if (params->ie_len) {
+		os_memcpy(pos, params->ie, params->ie_len);
+		pos += params->ie_len;
+	}
+	if (params->sae_data_len)
+		os_memcpy(pos, params->sae_data, params->sae_data_len);
+
+	res = wpa_priv_cmd(drv, PRIVSEP_CMD_AUTHENTICATE, data, buflen,
+			   NULL, NULL);
+	os_free(data);
+
+	return res;
+}
+
+
 static int wpa_driver_privsep_associate(
 	void *priv, struct wpa_driver_associate_params *params)
 {
@@ -309,6 +359,32 @@
 }
 
 
+static void wpa_driver_privsep_event_auth(void *ctx, u8 *buf, size_t len)
+{
+	union wpa_event_data data;
+	struct privsep_event_auth *auth;
+
+	os_memset(&data, 0, sizeof(data));
+	if (len < sizeof(*auth))
+		return;
+	auth = (struct privsep_event_auth *) buf;
+	if (len < sizeof(*auth) + auth->ies_len)
+		return;
+
+	os_memcpy(data.auth.peer, auth->peer, ETH_ALEN);
+	os_memcpy(data.auth.bssid, auth->bssid, ETH_ALEN);
+	data.auth.auth_type = auth->auth_type;
+	data.auth.auth_transaction = auth->auth_transaction;
+	data.auth.status_code = auth->status_code;
+	if (auth->ies_len) {
+		data.auth.ies = (u8 *) (auth + 1);
+		data.auth.ies_len = auth->ies_len;
+	}
+
+	wpa_supplicant_event(ctx, EVENT_AUTH, &data);
+}
+
+
 static void wpa_driver_privsep_event_assoc(void *ctx,
 					   enum wpa_event_type event,
 					   u8 *buf, size_t len)
@@ -468,6 +544,9 @@
 	case PRIVSEP_EVENT_SCAN_RESULTS:
 		wpa_supplicant_event(drv->ctx, EVENT_SCAN_RESULTS, NULL);
 		break;
+	case PRIVSEP_EVENT_SCAN_STARTED:
+		wpa_supplicant_event(drv->ctx, EVENT_SCAN_STARTED, NULL);
+		break;
 	case PRIVSEP_EVENT_ASSOC:
 		wpa_driver_privsep_event_assoc(drv->ctx, EVENT_ASSOC,
 					       event_buf, event_len);
@@ -503,6 +582,9 @@
 		wpa_driver_privsep_event_rx_eapol(drv->ctx, event_buf,
 						  event_len);
 		break;
+	case PRIVSEP_EVENT_AUTH:
+		wpa_driver_privsep_event_auth(drv->ctx, event_buf, event_len);
+		break;
 	}
 
 	os_free(buf);
@@ -703,6 +785,10 @@
 	res = wpa_priv_cmd(drv, PRIVSEP_CMD_GET_CAPA, NULL, 0, capa, &len);
 	if (res < 0 || len != sizeof(*capa))
 		return -1;
+	/* For now, no support for passing extended_capa pointers */
+	capa->extended_capa = NULL;
+	capa->extended_capa_mask = NULL;
+	capa->extended_capa_len = 0;
 	return 0;
 }
 
@@ -735,6 +821,7 @@
 	.set_param = wpa_driver_privsep_set_param,
 	.scan2 = wpa_driver_privsep_scan,
 	.deauthenticate = wpa_driver_privsep_deauthenticate,
+	.authenticate = wpa_driver_privsep_authenticate,
 	.associate = wpa_driver_privsep_associate,
 	.get_capa = wpa_driver_privsep_get_capa,
 	.get_mac_addr = wpa_driver_privsep_get_mac_addr,
diff --git a/src/drivers/driver_wext.c b/src/drivers/driver_wext.c
index 01defdf..791cd5d 100644
--- a/src/drivers/driver_wext.c
+++ b/src/drivers/driver_wext.c
@@ -422,7 +422,7 @@
 
 
 static void wpa_driver_wext_event_wireless(struct wpa_driver_wext_data *drv,
-					   char *data, int len)
+					   char *data, unsigned int len)
 {
 	struct iw_event iwe_buf, *iwe = &iwe_buf;
 	char *pos, *end, *custom, *buf;
@@ -430,13 +430,13 @@
 	pos = data;
 	end = data + len;
 
-	while (pos + IW_EV_LCP_LEN <= end) {
+	while ((size_t) (end - pos) >= IW_EV_LCP_LEN) {
 		/* Event data may be unaligned, so make a local, aligned copy
 		 * before processing. */
 		os_memcpy(&iwe_buf, pos, IW_EV_LCP_LEN);
 		wpa_printf(MSG_DEBUG, "Wireless event: cmd=0x%x len=%d",
 			   iwe->cmd, iwe->len);
-		if (iwe->len <= IW_EV_LCP_LEN)
+		if (iwe->len <= IW_EV_LCP_LEN || iwe->len > end - pos)
 			return;
 
 		custom = pos + IW_EV_POINT_LEN;
@@ -480,7 +480,7 @@
 			}
 			break;
 		case IWEVMICHAELMICFAILURE:
-			if (custom + iwe->u.data.length > end) {
+			if (iwe->u.data.length > end - custom) {
 				wpa_printf(MSG_DEBUG, "WEXT: Invalid "
 					   "IWEVMICHAELMICFAILURE length");
 				return;
@@ -489,7 +489,7 @@
 				drv->ctx, custom, iwe->u.data.length);
 			break;
 		case IWEVCUSTOM:
-			if (custom + iwe->u.data.length > end) {
+			if (iwe->u.data.length > end - custom) {
 				wpa_printf(MSG_DEBUG, "WEXT: Invalid "
 					   "IWEVCUSTOM length");
 				return;
@@ -508,7 +508,7 @@
 					     NULL);
 			break;
 		case IWEVASSOCREQIE:
-			if (custom + iwe->u.data.length > end) {
+			if (iwe->u.data.length > end - custom) {
 				wpa_printf(MSG_DEBUG, "WEXT: Invalid "
 					   "IWEVASSOCREQIE length");
 				return;
@@ -517,7 +517,7 @@
 				drv, custom, iwe->u.data.length);
 			break;
 		case IWEVASSOCRESPIE:
-			if (custom + iwe->u.data.length > end) {
+			if (iwe->u.data.length > end - custom) {
 				wpa_printf(MSG_DEBUG, "WEXT: Invalid "
 					   "IWEVASSOCRESPIE length");
 				return;
@@ -526,7 +526,7 @@
 				drv, custom, iwe->u.data.length);
 			break;
 		case IWEVPMKIDCAND:
-			if (custom + iwe->u.data.length > end) {
+			if (iwe->u.data.length > end - custom) {
 				wpa_printf(MSG_DEBUG, "WEXT: Invalid "
 					   "IWEVPMKIDCAND length");
 				return;
@@ -1220,7 +1220,7 @@
 			       char *end)
 {
 	int ssid_len = iwe->u.essid.length;
-	if (custom + ssid_len > end)
+	if (ssid_len > end - custom)
 		return;
 	if (iwe->u.essid.flags &&
 	    ssid_len > 0 &&
@@ -1316,7 +1316,7 @@
 	size_t clen;
 
 	clen = iwe->len;
-	if (custom + clen > end)
+	if (clen > (size_t) (end - custom))
 		return;
 	maxrate = 0;
 	while (((ssize_t) clen) >= (ssize_t) sizeof(struct iw_param)) {
@@ -1369,7 +1369,7 @@
 	u8 *tmp;
 
 	clen = iwe->u.data.length;
-	if (custom + clen > end)
+	if (clen > (size_t) (end - custom))
 		return;
 
 	if (clen > 7 && os_strncmp(custom, "wpa_ie=", 7) == 0) {
@@ -1441,8 +1441,8 @@
 	/* Figure out whether we need to fake any IEs */
 	pos = data->ie;
 	end = pos + data->ie_len;
-	while (pos && pos + 1 < end) {
-		if (pos + 2 + pos[1] > end)
+	while (pos && end - pos > 1) {
+		if (2 + pos[1] > end - pos)
 			break;
 		if (pos[0] == WLAN_EID_SSID)
 			ssid_ie = pos;
@@ -1530,11 +1530,11 @@
 	end = (char *) res_buf + len;
 	os_memset(&data, 0, sizeof(data));
 
-	while (pos + IW_EV_LCP_LEN <= end) {
+	while ((size_t) (end - pos) >= IW_EV_LCP_LEN) {
 		/* Event data may be unaligned, so make a local, aligned copy
 		 * before processing. */
 		os_memcpy(&iwe_buf, pos, IW_EV_LCP_LEN);
-		if (iwe->len <= IW_EV_LCP_LEN)
+		if (iwe->len <= IW_EV_LCP_LEN || iwe->len > end - pos)
 			break;
 
 		custom = pos + IW_EV_POINT_LEN;
diff --git a/src/drivers/drivers.mak b/src/drivers/drivers.mak
index 9434078..c4f5f97 100644
--- a/src/drivers/drivers.mak
+++ b/src/drivers/drivers.mak
@@ -30,6 +30,9 @@
 DRV_OBJS += ../src/drivers/driver_nl80211_monitor.o
 DRV_OBJS += ../src/drivers/driver_nl80211_scan.o
 DRV_OBJS += ../src/utils/radiotap.o
+ifdef CONFIG_DRIVER_NL80211_QCA
+DRV_CFLAGS += -DCONFIG_DRIVER_NL80211_QCA
+endif
 NEED_SME=y
 NEED_AP_MLME=y
 NEED_NETLINK=y
@@ -54,7 +57,9 @@
   ifdef CONFIG_LIBNL_TINY
     DRV_LIBS += -lnl-tiny
   else
-    DRV_LIBS += -lnl
+    ifndef CONFIG_OSX
+      DRV_LIBS += -lnl
+    endif
   endif
 
   ifdef CONFIG_LIBNL20
diff --git a/src/drivers/drivers.mk b/src/drivers/drivers.mk
index 8da4c53..0a05a24 100644
--- a/src/drivers/drivers.mk
+++ b/src/drivers/drivers.mk
@@ -26,6 +26,9 @@
 DRV_OBJS += src/drivers/driver_nl80211_monitor.c
 DRV_OBJS += src/drivers/driver_nl80211_scan.c
 DRV_OBJS += src/utils/radiotap.c
+ifdef CONFIG_DRIVER_NL80211_QCA
+DRV_CFLAGS += -DCONFIG_DRIVER_NL80211_QCA
+endif
 NEED_SME=y
 NEED_AP_MLME=y
 NEED_NETLINK=y
diff --git a/src/drivers/linux_ioctl.c b/src/drivers/linux_ioctl.c
index 837971d..e21147a 100644
--- a/src/drivers/linux_ioctl.c
+++ b/src/drivers/linux_ioctl.c
@@ -219,3 +219,26 @@
 	os_strlcpy(brname, pos, IFNAMSIZ);
 	return 0;
 }
+
+
+int linux_master_get(char *master_ifname, const char *ifname)
+{
+	char buf[128], masterlink[128], *pos;
+	ssize_t res;
+
+	/* check whether there is a master */
+	os_snprintf(buf, sizeof(buf), "/sys/class/net/%s/master", ifname);
+
+	res = readlink(buf, masterlink, sizeof(masterlink));
+	if (res < 0 || (size_t) res >= sizeof(masterlink))
+		return -1;
+
+	masterlink[res] = '\0';
+
+	pos = os_strrchr(masterlink, '/');
+	if (pos == NULL)
+		return -1;
+	pos++;
+	os_strlcpy(master_ifname, pos, IFNAMSIZ);
+	return 0;
+}
diff --git a/src/drivers/linux_ioctl.h b/src/drivers/linux_ioctl.h
index c03fe6e..6de4d9b 100644
--- a/src/drivers/linux_ioctl.h
+++ b/src/drivers/linux_ioctl.h
@@ -18,5 +18,6 @@
 int linux_br_add_if(int sock, const char *brname, const char *ifname);
 int linux_br_del_if(int sock, const char *brname, const char *ifname);
 int linux_br_get(char *brname, const char *ifname);
+int linux_master_get(char *master_ifname, const char *ifname);
 
 #endif /* LINUX_IOCTL_H */
diff --git a/src/drivers/nl80211_copy.h b/src/drivers/nl80211_copy.h
index ae16ba9..5b7b5eb 100644
--- a/src/drivers/nl80211_copy.h
+++ b/src/drivers/nl80211_copy.h
@@ -10,6 +10,7 @@
  * Copyright 2008, 2009 Luis R. Rodriguez <lrodriguez@atheros.com>
  * Copyright 2008 Jouni Malinen <jouni.malinen@atheros.com>
  * Copyright 2008 Colin McCabe <colin@cozybit.com>
+ * Copyright 2015	Intel Deutschland GmbH
  *
  * Permission to use, copy, modify, and/or distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
@@ -328,7 +329,15 @@
  *	partial scan results may be available
  *
  * @NL80211_CMD_START_SCHED_SCAN: start a scheduled scan at certain
- *	intervals, as specified by %NL80211_ATTR_SCHED_SCAN_INTERVAL.
+ *	intervals and certain number of cycles, as specified by
+ *	%NL80211_ATTR_SCHED_SCAN_PLANS. If %NL80211_ATTR_SCHED_SCAN_PLANS is
+ *	not specified and only %NL80211_ATTR_SCHED_SCAN_INTERVAL is specified,
+ *	scheduled scan will run in an infinite loop with the specified interval.
+ *	These attributes are mutually exculsive,
+ *	i.e. NL80211_ATTR_SCHED_SCAN_INTERVAL must not be passed if
+ *	NL80211_ATTR_SCHED_SCAN_PLANS is defined.
+ *	If for some reason scheduled scan is aborted by the driver, all scan
+ *	plans are canceled (including scan plans that did not start yet).
  *	Like with normal scans, if SSIDs (%NL80211_ATTR_SCAN_SSIDS)
  *	are passed, they are used in the probe requests.  For
  *	broadcast, a broadcast SSID must be passed (ie. an empty
@@ -811,6 +820,10 @@
  *	as an event to indicate changes for devices with wiphy-specific regdom
  *	management.
  *
+ * @NL80211_CMD_ABORT_SCAN: Stop an ongoing scan. Returns -ENOENT if a scan is
+ *	not running. The driver indicates the status of the scan through
+ *	cfg80211_scan_done().
+ *
  * @NL80211_CMD_MAX: highest used command number
  * @__NL80211_CMD_AFTER_LAST: internal use
  */
@@ -997,6 +1010,8 @@
 
 	NL80211_CMD_WIPHY_REG_CHANGE,
 
+	NL80211_CMD_ABORT_SCAN,
+
 	/* add new commands above here */
 
 	/* used to define NL80211_CMD_MAX below */
@@ -1754,12 +1769,27 @@
  *	should be contained in the result as the sum of the respective counters
  *	over all channels.
  *
- * @NL80211_ATTR_SCHED_SCAN_DELAY: delay before a scheduled scan (or a
- *	WoWLAN net-detect scan) is started, u32 in seconds.
+ * @NL80211_ATTR_SCHED_SCAN_DELAY: delay before the first cycle of a
+ *	scheduled scan is started.  Or the delay before a WoWLAN
+ *	net-detect scan is started, counting from the moment the
+ *	system is suspended.  This value is a u32, in seconds.
 
  * @NL80211_ATTR_REG_INDOOR: flag attribute, if set indicates that the device
  *      is operating in an indoor environment.
  *
+ * @NL80211_ATTR_MAX_NUM_SCHED_SCAN_PLANS: maximum number of scan plans for
+ *	scheduled scan supported by the device (u32), a wiphy attribute.
+ * @NL80211_ATTR_MAX_SCAN_PLAN_INTERVAL: maximum interval (in seconds) for
+ *	a scan plan (u32), a wiphy attribute.
+ * @NL80211_ATTR_MAX_SCAN_PLAN_ITERATIONS: maximum number of iterations in
+ *	a scan plan (u32), a wiphy attribute.
+ * @NL80211_ATTR_SCHED_SCAN_PLANS: a list of scan plans for scheduled scan.
+ *	Each scan plan defines the number of scan iterations and the interval
+ *	between scans. The last scan plan will always run infinitely,
+ *	thus it must not specify the number of iterations, only the interval
+ *	between scans. The scan plans are executed sequentially.
+ *	Each scan plan is a nested attribute of &enum nl80211_sched_scan_plan.
+ *
  * @NUM_NL80211_ATTR: total number of nl80211_attrs available
  * @NL80211_ATTR_MAX: highest attribute number currently defined
  * @__NL80211_ATTR_AFTER_LAST: internal use
@@ -2129,6 +2159,11 @@
 
 	NL80211_ATTR_REG_INDOOR,
 
+	NL80211_ATTR_MAX_NUM_SCHED_SCAN_PLANS,
+	NL80211_ATTR_MAX_SCAN_PLAN_INTERVAL,
+	NL80211_ATTR_MAX_SCAN_PLAN_ITERATIONS,
+	NL80211_ATTR_SCHED_SCAN_PLANS,
+
 	/* add attributes here, update the policy in nl80211.c */
 
 	__NL80211_ATTR_AFTER_LAST,
@@ -2619,16 +2654,17 @@
  *	an indoor surroundings, i.e., it is connected to AC power (and not
  *	through portable DC inverters) or is under the control of a master
  *	that is acting as an AP and is connected to AC power.
- * @NL80211_FREQUENCY_ATTR_GO_CONCURRENT: GO operation is allowed on this
+ * @NL80211_FREQUENCY_ATTR_IR_CONCURRENT: IR operation is allowed on this
  *	channel if it's connected concurrently to a BSS on the same channel on
  *	the 2 GHz band or to a channel in the same UNII band (on the 5 GHz
- *	band), and IEEE80211_CHAN_RADAR is not set. Instantiating a GO on a
- *	channel that has the GO_CONCURRENT attribute set can be done when there
- *	is a clear assessment that the device is operating under the guidance of
- *	an authorized master, i.e., setting up a GO while the device is also
- *	connected to an AP with DFS and radar detection on the UNII band (it is
- *	up to user-space, i.e., wpa_supplicant to perform the required
- *	verifications)
+ *	band), and IEEE80211_CHAN_RADAR is not set. Instantiating a GO or TDLS
+ *	off-channel on a channel that has the IR_CONCURRENT attribute set can be
+ *	done when there is a clear assessment that the device is operating under
+ *	the guidance of an authorized master, i.e., setting up a GO or TDLS
+ *	off-channel while the device is also connected to an AP with DFS and
+ *	radar detection on the UNII band (it is up to user-space, i.e.,
+ *	wpa_supplicant to perform the required verifications). Using this
+ *	attribute for IR is disallowed for master interfaces (IBSS, AP).
  * @NL80211_FREQUENCY_ATTR_NO_20MHZ: 20 MHz operation is not allowed
  *	on this channel in current regulatory domain.
  * @NL80211_FREQUENCY_ATTR_NO_10MHZ: 10 MHz operation is not allowed
@@ -2640,7 +2676,7 @@
  * See https://apps.fcc.gov/eas/comments/GetPublishedDocument.html?id=327&tn=528122
  * for more information on the FCC description of the relaxations allowed
  * by NL80211_FREQUENCY_ATTR_INDOOR_ONLY and
- * NL80211_FREQUENCY_ATTR_GO_CONCURRENT.
+ * NL80211_FREQUENCY_ATTR_IR_CONCURRENT.
  */
 enum nl80211_frequency_attr {
 	__NL80211_FREQUENCY_ATTR_INVALID,
@@ -2658,7 +2694,7 @@
 	NL80211_FREQUENCY_ATTR_NO_160MHZ,
 	NL80211_FREQUENCY_ATTR_DFS_CAC_TIME,
 	NL80211_FREQUENCY_ATTR_INDOOR_ONLY,
-	NL80211_FREQUENCY_ATTR_GO_CONCURRENT,
+	NL80211_FREQUENCY_ATTR_IR_CONCURRENT,
 	NL80211_FREQUENCY_ATTR_NO_20MHZ,
 	NL80211_FREQUENCY_ATTR_NO_10MHZ,
 
@@ -2671,6 +2707,8 @@
 #define NL80211_FREQUENCY_ATTR_PASSIVE_SCAN	NL80211_FREQUENCY_ATTR_NO_IR
 #define NL80211_FREQUENCY_ATTR_NO_IBSS		NL80211_FREQUENCY_ATTR_NO_IR
 #define NL80211_FREQUENCY_ATTR_NO_IR		NL80211_FREQUENCY_ATTR_NO_IR
+#define NL80211_FREQUENCY_ATTR_GO_CONCURRENT \
+					NL80211_FREQUENCY_ATTR_IR_CONCURRENT
 
 /**
  * enum nl80211_bitrate_attr - bitrate attributes
@@ -2829,7 +2867,7 @@
  * @NL80211_RRF_AUTO_BW: maximum available bandwidth should be calculated
  *	base on contiguous rules and wider channels will be allowed to cross
  *	multiple contiguous/overlapping frequency ranges.
- * @NL80211_RRF_GO_CONCURRENT: See &NL80211_FREQUENCY_ATTR_GO_CONCURRENT
+ * @NL80211_RRF_IR_CONCURRENT: See &NL80211_FREQUENCY_ATTR_IR_CONCURRENT
  * @NL80211_RRF_NO_HT40MINUS: channels can't be used in HT40- operation
  * @NL80211_RRF_NO_HT40PLUS: channels can't be used in HT40+ operation
  * @NL80211_RRF_NO_80MHZ: 80MHz operation not allowed
@@ -2846,7 +2884,7 @@
 	NL80211_RRF_NO_IR		= 1<<7,
 	__NL80211_RRF_NO_IBSS		= 1<<8,
 	NL80211_RRF_AUTO_BW		= 1<<11,
-	NL80211_RRF_GO_CONCURRENT	= 1<<12,
+	NL80211_RRF_IR_CONCURRENT	= 1<<12,
 	NL80211_RRF_NO_HT40MINUS	= 1<<13,
 	NL80211_RRF_NO_HT40PLUS		= 1<<14,
 	NL80211_RRF_NO_80MHZ		= 1<<15,
@@ -2858,6 +2896,7 @@
 #define NL80211_RRF_NO_IR		NL80211_RRF_NO_IR
 #define NL80211_RRF_NO_HT40		(NL80211_RRF_NO_HT40MINUS |\
 					 NL80211_RRF_NO_HT40PLUS)
+#define NL80211_RRF_GO_CONCURRENT	NL80211_RRF_IR_CONCURRENT
 
 /* For backport compatibility with older userspace */
 #define NL80211_RRF_NO_IR_ALL		(NL80211_RRF_NO_IR | __NL80211_RRF_NO_IBSS)
@@ -3359,6 +3398,9 @@
  *	(not present if no beacon frame has been received yet)
  * @NL80211_BSS_PRESP_DATA: the data in @NL80211_BSS_INFORMATION_ELEMENTS and
  *	@NL80211_BSS_TSF is known to be from a probe response (flag attribute)
+ * @NL80211_BSS_LAST_SEEN_BOOTTIME: CLOCK_BOOTTIME timestamp when this entry
+ *	was last updated by a received frame. The value is expected to be
+ *	accurate to about 10ms. (u64, nanoseconds)
  * @__NL80211_BSS_AFTER_LAST: internal
  * @NL80211_BSS_MAX: highest BSS attribute
  */
@@ -3378,6 +3420,7 @@
 	NL80211_BSS_CHAN_WIDTH,
 	NL80211_BSS_BEACON_TSF,
 	NL80211_BSS_PRESP_DATA,
+	NL80211_BSS_LAST_SEEN_BOOTTIME,
 
 	/* keep last */
 	__NL80211_BSS_AFTER_LAST,
@@ -4584,4 +4627,28 @@
 	NL80211_TDLS_PEER_WMM = 1<<2,
 };
 
+/**
+ * enum nl80211_sched_scan_plan - scanning plan for scheduled scan
+ * @__NL80211_SCHED_SCAN_PLAN_INVALID: attribute number 0 is reserved
+ * @NL80211_SCHED_SCAN_PLAN_INTERVAL: interval between scan iterations. In
+ *	seconds (u32).
+ * @NL80211_SCHED_SCAN_PLAN_ITERATIONS: number of scan iterations in this
+ *	scan plan (u32). The last scan plan must not specify this attribute
+ *	because it will run infinitely. A value of zero is invalid as it will
+ *	make the scan plan meaningless.
+ * @NL80211_SCHED_SCAN_PLAN_MAX: highest scheduled scan plan attribute number
+ *	currently defined
+ * @__NL80211_SCHED_SCAN_PLAN_AFTER_LAST: internal use
+ */
+enum nl80211_sched_scan_plan {
+	__NL80211_SCHED_SCAN_PLAN_INVALID,
+	NL80211_SCHED_SCAN_PLAN_INTERVAL,
+	NL80211_SCHED_SCAN_PLAN_ITERATIONS,
+
+	/* keep last */
+	__NL80211_SCHED_SCAN_PLAN_AFTER_LAST,
+	NL80211_SCHED_SCAN_PLAN_MAX =
+		__NL80211_SCHED_SCAN_PLAN_AFTER_LAST - 1
+};
+
 #endif /* __LINUX_NL80211_H */
diff --git a/src/eap_common/eap_gpsk_common.c b/src/eap_common/eap_gpsk_common.c
index 8c7ae27..b081879 100644
--- a/src/eap_common/eap_gpsk_common.c
+++ b/src/eap_common/eap_gpsk_common.c
@@ -92,7 +92,8 @@
 	n = (len + hashlen - 1) / hashlen;
 	for (i = 1; i <= n; i++) {
 		WPA_PUT_BE16(ibuf, i);
-		hmac_sha256_vector(psk, 32, 2, addr, vlen, hash);
+		if (hmac_sha256_vector(psk, 32, 2, addr, vlen, hash))
+			return -1;
 		clen = left > hashlen ? hashlen : left;
 		os_memcpy(opos, hash, clen);
 		opos += clen;
@@ -534,8 +535,7 @@
 		break;
 #ifdef EAP_GPSK_SHA256
 	case EAP_GPSK_CIPHER_SHA256:
-		hmac_sha256(sk, sk_len, data, len, mic);
-		ret = 0;
+		ret = hmac_sha256(sk, sk_len, data, len, mic);
 		break;
 #endif /* EAP_GPSK_SHA256 */
 	default:
@@ -545,5 +545,8 @@
 		break;
 	}
 
+	if (ret)
+		wpa_printf(MSG_DEBUG, "EAP-GPSK: Could not compute MIC");
+
 	return ret;
 }
diff --git a/src/eap_common/eap_pwd_common.c b/src/eap_common/eap_pwd_common.c
index 4d27623..67f8f70 100644
--- a/src/eap_common/eap_pwd_common.c
+++ b/src/eap_common/eap_pwd_common.c
@@ -115,6 +115,26 @@
         case 26:
 		nid = NID_secp224r1;
 		break;
+#ifdef NID_brainpoolP224r1
+	case 27:
+		nid = NID_brainpoolP224r1;
+		break;
+#endif /* NID_brainpoolP224r1 */
+#ifdef NID_brainpoolP256r1
+	case 28:
+		nid = NID_brainpoolP256r1;
+		break;
+#endif /* NID_brainpoolP256r1 */
+#ifdef NID_brainpoolP384r1
+	case 29:
+		nid = NID_brainpoolP384r1;
+		break;
+#endif /* NID_brainpoolP384r1 */
+#ifdef NID_brainpoolP512r1
+	case 30:
+		nid = NID_brainpoolP512r1;
+		break;
+#endif /* NID_brainpoolP512r1 */
         default:
 		wpa_printf(MSG_INFO, "EAP-pwd: unsupported group %d", num);
 		return -1;
diff --git a/src/eap_common/eap_sake_common.c b/src/eap_common/eap_sake_common.c
index c22e43e..8819541 100644
--- a/src/eap_common/eap_sake_common.c
+++ b/src/eap_common/eap_sake_common.c
@@ -121,7 +121,7 @@
 		attr->next_tmpid_len = len;
 		break;
 	case EAP_SAKE_AT_MSK_LIFE:
-		wpa_printf(MSG_DEBUG, "EAP-SAKE: Parse: AT_IV");
+		wpa_printf(MSG_DEBUG, "EAP-SAKE: Parse: AT_MSK_LIFE");
 		if (len != 4) {
 			wpa_printf(MSG_DEBUG, "EAP-SAKE: Invalid "
 				   "AT_MSK_LIFE payload length %d", len);
diff --git a/src/eap_peer/eap.c b/src/eap_peer/eap.c
index 1dbe003..28d5116 100644
--- a/src/eap_peer/eap.c
+++ b/src/eap_peer/eap.c
@@ -188,6 +188,14 @@
 	 */
 	eapol_set_bool(sm, EAPOL_eapResp, FALSE);
 	eapol_set_bool(sm, EAPOL_eapNoResp, FALSE);
+	/*
+	 * RFC 4137 does not reset ignore here, but since it is possible for
+	 * some method code paths to end up not setting ignore=FALSE, clear the
+	 * value here to avoid issues if a previous authentication attempt
+	 * failed with ignore=TRUE being left behind in the last
+	 * m.check(eapReqData) operation.
+	 */
+	sm->ignore = 0;
 	sm->num_rounds = 0;
 	sm->prev_failure = 0;
 	sm->expected_failure = 0;
@@ -584,7 +592,7 @@
 	wpa_printf(MSG_DEBUG, "EAP: Valid ERP key found %s (SEQ=%u)",
 		   erp->keyname_nai, erp->next_seq);
 
-	msg = eap_msg_alloc(EAP_VENDOR_IETF, EAP_ERP_TYPE_REAUTH,
+	msg = eap_msg_alloc(EAP_VENDOR_IETF, (EapType) EAP_ERP_TYPE_REAUTH,
 			    1 + 2 + 2 + os_strlen(erp->keyname_nai) + 1 + 16,
 			    EAP_CODE_INITIATE, hdr->identifier);
 	if (msg == NULL)
@@ -708,7 +716,7 @@
 	wpabuf_free(sm->lastRespData);
 	if (sm->eapRespData) {
 		if (sm->workaround)
-			os_memcpy(sm->last_md5, sm->req_md5, 16);
+			os_memcpy(sm->last_sha1, sm->req_sha1, 20);
 		sm->lastId = sm->reqId;
 		sm->lastRespData = wpabuf_dup(sm->eapRespData);
 		eapol_set_bool(sm, EAPOL_eapResp, TRUE);
@@ -914,12 +922,12 @@
 
 	duplicate = (sm->reqId == sm->lastId) && sm->rxReq;
 	if (sm->workaround && duplicate &&
-	    os_memcmp(sm->req_md5, sm->last_md5, 16) != 0) {
+	    os_memcmp(sm->req_sha1, sm->last_sha1, 20) != 0) {
 		/*
 		 * RFC 4137 uses (reqId == lastId) as the only verification for
 		 * duplicate EAP requests. However, this misses cases where the
 		 * AS is incorrectly using the same id again; and
-		 * unfortunately, such implementations exist. Use MD5 hash as
+		 * unfortunately, such implementations exist. Use SHA1 hash as
 		 * an extra verification for the packets being duplicate to
 		 * workaround these issues.
 		 */
@@ -1765,7 +1773,7 @@
 	if (sm->workaround) {
 		const u8 *addr[1];
 		addr[0] = wpabuf_head(req);
-		md5_vector(1, addr, &plen, sm->req_md5);
+		sha1_vector(1, addr, &plen, sm->req_sha1);
 	}
 
 	switch (hdr->code) {
diff --git a/src/eap_peer/eap_eke.c b/src/eap_peer/eap_eke.c
index dfbda56..1fd4970 100644
--- a/src/eap_peer/eap_eke.c
+++ b/src/eap_peer/eap_eke.c
@@ -452,6 +452,7 @@
 	/* DHComponent_P = Encr(key, y_p) */
 	rpos = wpabuf_put(resp, data->sess.dhcomp_len);
 	if (eap_eke_dhcomp(&data->sess, key, pub, rpos) < 0) {
+		wpabuf_free(resp);
 		wpa_printf(MSG_INFO, "EAP-EKE: Failed to build DHComponent_P");
 		os_memset(key, 0, sizeof(key));
 		return eap_eke_build_fail(data, ret, id,
diff --git a/src/eap_peer/eap_fast.c b/src/eap_peer/eap_fast.c
index f636e74..833dcb6 100644
--- a/src/eap_peer/eap_fast.c
+++ b/src/eap_peer/eap_fast.c
@@ -1096,7 +1096,7 @@
 	/* Parse TLVs from the decrypted Phase 2 data */
 	pos = wpabuf_mhead(decrypted);
 	end = pos + wpabuf_len(decrypted);
-	while (pos + 4 < end) {
+	while (end - pos > 4) {
 		mandatory = pos[0] & 0x80;
 		tlv_type = WPA_GET_BE16(pos) & 0x3fff;
 		pos += 2;
@@ -1572,6 +1572,13 @@
 						  EAP_TYPE_FAST,
 						  data->fast_version, id, &msg,
 						  &resp);
+		if (res < 0) {
+			wpa_printf(MSG_DEBUG,
+				   "EAP-FAST: TLS processing failed");
+			ret->methodState = METHOD_DONE;
+			ret->decision = DECISION_FAIL;
+			return resp;
+		}
 
 		if (tls_connection_established(sm->ssl_ctx, data->ssl.conn)) {
 			char cipher[80];
diff --git a/src/eap_peer/eap_fast_pac.c b/src/eap_peer/eap_fast_pac.c
index 89e604e..c0986b3 100644
--- a/src/eap_peer/eap_fast_pac.c
+++ b/src/eap_peer/eap_fast_pac.c
@@ -709,7 +709,7 @@
 	pos = pac->pac_info;
 	end = pos + pac->pac_info_len;
 
-	while (pos + 4 < end) {
+	while (end - pos > 4) {
 		type = WPA_GET_BE16(pos);
 		pos += 2;
 		len = WPA_GET_BE16(pos);
diff --git a/src/eap_peer/eap_i.h b/src/eap_peer/eap_i.h
index 5f8b5fa..99b44da 100644
--- a/src/eap_peer/eap_i.h
+++ b/src/eap_peer/eap_i.h
@@ -338,9 +338,9 @@
 	Boolean rxResp /* LEAP only */;
 	Boolean leap_done;
 	Boolean peap_done;
-	u8 req_md5[16]; /* MD5() of the current EAP packet */
-	u8 last_md5[16]; /* MD5() of the previously received EAP packet; used
-			  * in duplicate request detection. */
+	u8 req_sha1[20]; /* SHA1() of the current EAP packet */
+	u8 last_sha1[20]; /* SHA1() of the previously received EAP packet; used
+			   * in duplicate request detection. */
 
 	void *msg_ctx;
 	void *scard_ctx;
diff --git a/src/eap_peer/eap_mschapv2.c b/src/eap_peer/eap_mschapv2.c
index 9e486e7..6acf1e8 100644
--- a/src/eap_peer/eap_mschapv2.c
+++ b/src/eap_peer/eap_mschapv2.c
@@ -511,6 +511,11 @@
 	struct eap_sm *sm, struct eap_mschapv2_data *data,
 	struct eap_method_ret *ret, const struct eap_mschapv2_hdr *req, u8 id)
 {
+#ifdef CONFIG_NO_RC4
+	wpa_printf(MSG_ERROR,
+		"EAP-MSCHAPV2: RC4 not support in the build - cannot change password");
+	return NULL;
+#else /* CONFIG_NO_RC4 */
 	struct wpabuf *resp;
 	int ms_len;
 	const u8 *username, *password, *new_password;
@@ -628,6 +633,7 @@
 fail:
 	wpabuf_free(resp);
 	return NULL;
+#endif /* CONFIG_NO_RC4 */
 }
 
 
diff --git a/src/eap_peer/eap_peap.c b/src/eap_peer/eap_peap.c
index 4f68fce..98a48a6 100644
--- a/src/eap_peer/eap_peap.c
+++ b/src/eap_peer/eap_peap.c
@@ -1011,6 +1011,13 @@
 						  data->peap_version, id, &msg,
 						  &resp);
 
+		if (res < 0) {
+			wpa_printf(MSG_DEBUG,
+				   "EAP-PEAP: TLS processing failed");
+			ret->methodState = METHOD_DONE;
+			ret->decision = DECISION_FAIL;
+			return resp;
+		}
 		if (tls_connection_established(sm->ssl_ctx, data->ssl.conn)) {
 			char *label;
 			wpa_printf(MSG_DEBUG,
diff --git a/src/eap_peer/eap_pwd.c b/src/eap_peer/eap_pwd.c
index cd7a2ab..892b590 100644
--- a/src/eap_peer/eap_pwd.c
+++ b/src/eap_peer/eap_pwd.c
@@ -288,6 +288,12 @@
 	}
 
 	if (id->prep == EAP_PWD_PREP_MS) {
+#ifdef CONFIG_FIPS
+		wpa_printf(MSG_ERROR,
+			   "EAP-PWD (peer): MS password hash not supported in FIPS mode");
+		eap_pwd_state(data, FAILURE);
+		return;
+#else /* CONFIG_FIPS */
 		if (data->password_hash) {
 			res = hash_nt_password_hash(data->password, pwhashhash);
 		} else {
@@ -307,6 +313,7 @@
 
 		password = pwhashhash;
 		password_len = sizeof(pwhashhash);
+#endif /* CONFIG_FIPS */
 	} else {
 		password = data->password;
 		password_len = data->password_len;
@@ -767,7 +774,8 @@
 	wpabuf_put_data(data->outbuf, conf, SHA256_MAC_LEN);
 
 fin:
-	bin_clear_free(cruft, BN_num_bytes(data->grp->prime));
+	if (data->grp)
+		bin_clear_free(cruft, BN_num_bytes(data->grp->prime));
 	BN_clear_free(x);
 	BN_clear_free(y);
 	if (data->outbuf == NULL) {
diff --git a/src/eap_peer/eap_sim.c b/src/eap_peer/eap_sim.c
index 99a2816..cbf7461 100644
--- a/src/eap_peer/eap_sim.c
+++ b/src/eap_peer/eap_sim.c
@@ -1135,7 +1135,7 @@
 	if (random_get_bytes(data->nonce_mt, EAP_SIM_NONCE_MT_LEN)) {
 		wpa_printf(MSG_WARNING, "EAP-SIM: Failed to get random data "
 			   "for NONCE_MT");
-		os_free(data);
+		eap_sim_deinit(sm, data);
 		return NULL;
 	}
 	data->num_id_req = 0;
diff --git a/src/eap_peer/eap_tls.c b/src/eap_peer/eap_tls.c
index d81b1cf..66a027a 100644
--- a/src/eap_peer/eap_tls.c
+++ b/src/eap_peer/eap_tls.c
@@ -156,20 +156,6 @@
 	ret->methodState = METHOD_DONE;
 	ret->decision = DECISION_FAIL;
 
-	if (res == -1) {
-		struct eap_peer_config *config = eap_get_config(sm);
-		if (config) {
-			/*
-			 * The TLS handshake failed. So better forget the old
-			 * PIN. It may be wrong, we cannot be sure but trying
-			 * the wrong one again might block it on the card--so
-			 * better ask the user again.
-			 */
-			os_free(config->pin);
-			config->pin = NULL;
-		}
-	}
-
 	if (resp) {
 		/*
 		 * This is likely an alert message, so send it instead of just
diff --git a/src/eap_peer/eap_tls_common.c b/src/eap_peer/eap_tls_common.c
index fef7fdb..af2b754 100644
--- a/src/eap_peer/eap_tls_common.c
+++ b/src/eap_peer/eap_tls_common.c
@@ -68,6 +68,10 @@
 		params->flags |= TLS_CONN_DISABLE_SESSION_TICKET;
 	if (os_strstr(txt, "tls_disable_session_ticket=0"))
 		params->flags &= ~TLS_CONN_DISABLE_SESSION_TICKET;
+	if (os_strstr(txt, "tls_disable_tlsv1_0=1"))
+		params->flags |= TLS_CONN_DISABLE_TLSv1_0;
+	if (os_strstr(txt, "tls_disable_tlsv1_0=0"))
+		params->flags &= ~TLS_CONN_DISABLE_TLSv1_0;
 	if (os_strstr(txt, "tls_disable_tlsv1_1=1"))
 		params->flags |= TLS_CONN_DISABLE_TLSv1_1;
 	if (os_strstr(txt, "tls_disable_tlsv1_1=0"))
@@ -343,10 +347,10 @@
 				    struct eap_ssl_data *data, u8 eap_type,
 				    size_t *len)
 {
-	struct tls_keys keys;
+	struct tls_random keys;
 	u8 *out;
 
-	if (tls_connection_get_keys(sm->ssl_ctx, data->conn, &keys))
+	if (tls_connection_get_random(sm->ssl_ctx, data->conn, &keys))
 		return NULL;
 
 	if (keys.client_random == NULL || keys.server_random == NULL)
@@ -678,12 +682,18 @@
 	if (tls_connection_get_failed(data->ssl_ctx, data->conn)) {
 		/* TLS processing has failed - return error */
 		wpa_printf(MSG_DEBUG, "SSL: Failed - tls_out available to "
-			   "report error");
+			   "report error (len=%u)",
+			   (unsigned int) wpabuf_len(data->tls_out));
 		ret = -1;
 		/* TODO: clean pin if engine used? */
+		if (wpabuf_len(data->tls_out) == 0) {
+			wpabuf_free(data->tls_out);
+			data->tls_out = NULL;
+			return -1;
+		}
 	}
 
-	if (data->tls_out == NULL || wpabuf_len(data->tls_out) == 0) {
+	if (wpabuf_len(data->tls_out) == 0) {
 		/*
 		 * TLS negotiation should now be complete since all other cases
 		 * needing more data should have been caught above based on
@@ -749,20 +759,24 @@
 int eap_peer_tls_status(struct eap_sm *sm, struct eap_ssl_data *data,
 			char *buf, size_t buflen, int verbose)
 {
-	char name[128];
+	char version[20], name[128];
 	int len = 0, ret;
 
-	if (tls_get_cipher(data->ssl_ctx, data->conn, name, sizeof(name)) == 0)
-	{
-		ret = os_snprintf(buf + len, buflen - len,
-				  "EAP TLS cipher=%s\n"
-				  "tls_session_reused=%d\n",
-				  name, tls_connection_resumed(data->ssl_ctx,
-							       data->conn));
-		if (os_snprintf_error(buflen - len, ret))
-			return len;
-		len += ret;
-	}
+	if (tls_get_version(data->ssl_ctx, data->conn, version,
+			    sizeof(version)) < 0)
+		version[0] = '\0';
+	if (tls_get_cipher(data->ssl_ctx, data->conn, name, sizeof(name)) < 0)
+		name[0] = '\0';
+
+	ret = os_snprintf(buf + len, buflen - len,
+			  "eap_tls_version=%s\n"
+			  "EAP TLS cipher=%s\n"
+			  "tls_session_reused=%d\n",
+			  version, name,
+			  tls_connection_resumed(data->ssl_ctx, data->conn));
+	if (os_snprintf_error(buflen - len, ret))
+		return len;
+	len += ret;
 
 	return len;
 }
diff --git a/src/eap_peer/eap_ttls.c b/src/eap_peer/eap_ttls.c
index 25e3cba..b186c91 100644
--- a/src/eap_peer/eap_ttls.c
+++ b/src/eap_peer/eap_ttls.c
@@ -175,7 +175,8 @@
 	}
 
 	avp->avp_code = host_to_be32(avp_code);
-	avp->avp_length = host_to_be32((flags << 24) | (u32) (hdrlen + len));
+	avp->avp_length = host_to_be32(((u32) flags << 24) |
+				       (u32) (hdrlen + len));
 
 	return avphdr + hdrlen;
 }
@@ -253,11 +254,13 @@
 }
 
 
+#ifndef CONFIG_FIPS
 static u8 * eap_ttls_implicit_challenge(struct eap_sm *sm,
 					struct eap_ttls_data *data, size_t len)
 {
 	return eap_peer_tls_derive_key(sm, &data->ssl, "ttls challenge", len);
 }
+#endif /* CONFIG_FIPS */
 
 
 static void eap_ttls_phase2_select_eap_method(struct eap_ttls_data *data,
@@ -428,6 +431,10 @@
 					    struct eap_method_ret *ret,
 					    struct wpabuf **resp)
 {
+#ifdef CONFIG_FIPS
+	wpa_printf(MSG_ERROR, "EAP-TTLS: MSCHAPV2 not supported in FIPS build");
+	return -1;
+#else /* CONFIG_FIPS */
 #ifdef EAP_MSCHAPv2
 	struct wpabuf *msg;
 	u8 *buf, *pos, *challenge, *peer_challenge;
@@ -510,6 +517,7 @@
 	wpa_printf(MSG_ERROR, "EAP-TTLS: MSCHAPv2 not included in the build");
 	return -1;
 #endif /* EAP_MSCHAPv2 */
+#endif /* CONFIG_FIPS */
 }
 
 
@@ -518,6 +526,10 @@
 					  struct eap_method_ret *ret,
 					  struct wpabuf **resp)
 {
+#ifdef CONFIG_FIPS
+	wpa_printf(MSG_ERROR, "EAP-TTLS: MSCHAP not supported in FIPS build");
+	return -1;
+#else /* CONFIG_FIPS */
 	struct wpabuf *msg;
 	u8 *buf, *pos, *challenge;
 	const u8 *identity, *password;
@@ -592,6 +604,7 @@
 	ret->decision = DECISION_COND_SUCC;
 
 	return 0;
+#endif /* CONFIG_FIPS */
 }
 
 
@@ -654,6 +667,10 @@
 					struct eap_method_ret *ret,
 					struct wpabuf **resp)
 {
+#ifdef CONFIG_FIPS
+	wpa_printf(MSG_ERROR, "EAP-TTLS: CHAP not supported in FIPS build");
+	return -1;
+#else /* CONFIG_FIPS */
 	struct wpabuf *msg;
 	u8 *buf, *pos, *challenge;
 	const u8 *identity, *password;
@@ -722,6 +739,7 @@
 	ret->decision = DECISION_COND_SUCC;
 
 	return 0;
+#endif /* CONFIG_FIPS */
 }
 
 
@@ -1393,6 +1411,12 @@
 	res = eap_peer_tls_process_helper(sm, &data->ssl, EAP_TYPE_TTLS,
 					  data->ttls_version, identifier,
 					  in_data, out_data);
+	if (res < 0) {
+		wpa_printf(MSG_DEBUG, "EAP-TTLS: TLS processing failed");
+		ret->methodState = METHOD_DONE;
+		ret->decision = DECISION_FAIL;
+		return -1;
+	}
 
 	if (tls_connection_established(sm->ssl_ctx, data->ssl.conn)) {
 		wpa_printf(MSG_DEBUG, "EAP-TTLS: TLS done, proceed to "
diff --git a/src/eap_peer/eap_wsc.c b/src/eap_peer/eap_wsc.c
index 7ce0a53..7ac99c7 100644
--- a/src/eap_peer/eap_wsc.c
+++ b/src/eap_peer/eap_wsc.c
@@ -557,6 +557,9 @@
 		if (data->out_buf == NULL) {
 			wpa_printf(MSG_DEBUG, "EAP-WSC: Failed to receive "
 				   "message from WPS");
+			eap_wsc_state(data, FAIL);
+			ret->methodState = METHOD_DONE;
+			ret->decision = DECISION_FAIL;
 			return NULL;
 		}
 		data->out_used = 0;
diff --git a/src/eap_peer/ikev2.c b/src/eap_peer/ikev2.c
index 55ab72a..ca6502e 100644
--- a/src/eap_peer/ikev2.c
+++ b/src/eap_peer/ikev2.c
@@ -128,7 +128,7 @@
 
 	t = (const struct ikev2_transform *) pos;
 	transform_len = WPA_GET_BE16(t->transform_length);
-	if (transform_len < (int) sizeof(*t) || pos + transform_len > end) {
+	if (transform_len < (int) sizeof(*t) || transform_len > end - pos) {
 		wpa_printf(MSG_INFO, "IKEV2: Invalid transform length %d",
 			   transform_len);
 		return -1;
@@ -248,7 +248,7 @@
 
 	ppos = (const u8 *) (p + 1);
 	pend = pos + proposal_len;
-	if (ppos + p->spi_size > pend) {
+	if (p->spi_size > pend - ppos) {
 		wpa_printf(MSG_INFO, "IKEV2: Not enough room for SPI "
 			   "in proposal");
 		return -1;
diff --git a/src/eap_server/eap.h b/src/eap_server/eap.h
index 09be581..69eaab8 100644
--- a/src/eap_server/eap.h
+++ b/src/eap_server/eap.h
@@ -131,6 +131,7 @@
 	const u8 *server_id;
 	size_t server_id_len;
 	int erp;
+	unsigned int tls_session_lifetime;
 
 #ifdef CONFIG_TESTING_OPTIONS
 	u32 tls_test_flags;
diff --git a/src/eap_server/eap_i.h b/src/eap_server/eap_i.h
index 978c879..c90443d 100644
--- a/src/eap_server/eap_i.h
+++ b/src/eap_server/eap_i.h
@@ -210,6 +210,7 @@
 	Boolean initiate_reauth_start_sent;
 	Boolean try_initiate_reauth;
 	int erp;
+	unsigned int tls_session_lifetime;
 
 #ifdef CONFIG_TESTING_OPTIONS
 	u32 tls_test_flags;
diff --git a/src/eap_server/eap_server.c b/src/eap_server/eap_server.c
index 6651229..84ecafc 100644
--- a/src/eap_server/eap_server.c
+++ b/src/eap_server/eap_server.c
@@ -96,7 +96,8 @@
 		plen += 2 + domain_len;
 	}
 
-	msg = eap_msg_alloc(EAP_VENDOR_IETF, EAP_ERP_TYPE_REAUTH_START, plen,
+	msg = eap_msg_alloc(EAP_VENDOR_IETF,
+			    (EapType) EAP_ERP_TYPE_REAUTH_START, plen,
 			    EAP_CODE_INITIATE, id);
 	if (msg == NULL)
 		return NULL;
@@ -714,8 +715,8 @@
 	plen = 1 + 2 + 2 + os_strlen(nai);
 	if (hash_len)
 		plen += 1 + hash_len;
-	msg = eap_msg_alloc(EAP_VENDOR_IETF, EAP_ERP_TYPE_REAUTH, plen,
-			    EAP_CODE_FINISH, id);
+	msg = eap_msg_alloc(EAP_VENDOR_IETF, (EapType) EAP_ERP_TYPE_REAUTH,
+			    plen, EAP_CODE_FINISH, id);
 	if (msg == NULL)
 		return;
 	wpabuf_put_u8(msg, flags);
@@ -799,7 +800,7 @@
 
 	sm->rxInitiate = FALSE;
 
-	pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_ERP_TYPE_REAUTH,
+	pos = eap_hdr_validate(EAP_VENDOR_IETF, (EapType) EAP_ERP_TYPE_REAUTH,
 			       sm->eap_if.eapRespData, &len);
 	if (pos == NULL) {
 		wpa_printf(MSG_INFO, "EAP-Initiate: Invalid frame");
@@ -1246,6 +1247,17 @@
 			break;
 		}
 		SM_ENTER(EAP, SEND_REQUEST);
+		if (sm->eap_if.eapNoReq && !sm->eap_if.eapReq) {
+			/*
+			 * This transition is not mentioned in RFC 4137, but it
+			 * is needed to handle cleanly a case where EAP method
+			 * buildReq fails.
+			 */
+			wpa_printf(MSG_DEBUG,
+				   "EAP: Method did not return a request");
+			SM_ENTER(EAP, FAILURE);
+			break;
+		}
 		break;
 	case EAP_METHOD_RESPONSE:
 		/*
@@ -1853,6 +1865,7 @@
 	sm->server_id = conf->server_id;
 	sm->server_id_len = conf->server_id_len;
 	sm->erp = conf->erp;
+	sm->tls_session_lifetime = conf->tls_session_lifetime;
 
 #ifdef CONFIG_TESTING_OPTIONS
 	sm->tls_test_flags = conf->tls_test_flags;
diff --git a/src/eap_server/eap_server_fast.c b/src/eap_server/eap_server_fast.c
index 6745100..47210e1 100644
--- a/src/eap_server/eap_server_fast.c
+++ b/src/eap_server/eap_server_fast.c
@@ -180,42 +180,47 @@
 			buf, end - buf);
 
 	pos = buf;
-	while (pos + 1 < end) {
-		if (pos + 2 + pos[1] > end)
+	while (end - pos > 1) {
+		u8 id, elen;
+
+		id = *pos++;
+		elen = *pos++;
+		if (elen > end - pos)
 			break;
 
-		switch (*pos) {
+		switch (id) {
 		case PAC_OPAQUE_TYPE_PAD:
 			goto done;
 		case PAC_OPAQUE_TYPE_KEY:
-			if (pos[1] != EAP_FAST_PAC_KEY_LEN) {
-				wpa_printf(MSG_DEBUG, "EAP-FAST: Invalid "
-					   "PAC-Key length %d", pos[1]);
+			if (elen != EAP_FAST_PAC_KEY_LEN) {
+				wpa_printf(MSG_DEBUG,
+					   "EAP-FAST: Invalid PAC-Key length %d",
+					   elen);
 				os_free(buf);
 				return -1;
 			}
-			pac_key = pos + 2;
+			pac_key = pos;
 			wpa_hexdump_key(MSG_DEBUG, "EAP-FAST: PAC-Key from "
 					"decrypted PAC-Opaque",
 					pac_key, EAP_FAST_PAC_KEY_LEN);
 			break;
 		case PAC_OPAQUE_TYPE_LIFETIME:
-			if (pos[1] != 4) {
+			if (elen != 4) {
 				wpa_printf(MSG_DEBUG, "EAP-FAST: Invalid "
 					   "PAC-Key lifetime length %d",
-					   pos[1]);
+					   elen);
 				os_free(buf);
 				return -1;
 			}
-			lifetime = WPA_GET_BE32(pos + 2);
+			lifetime = WPA_GET_BE32(pos);
 			break;
 		case PAC_OPAQUE_TYPE_IDENTITY:
-			identity = pos + 2;
-			identity_len = pos[1];
+			identity = pos;
+			identity_len = elen;
 			break;
 		}
 
-		pos += 2 + pos[1];
+		pos += elen;
 	}
 done:
 
@@ -428,7 +433,7 @@
 	}
 	data->state = START;
 
-	if (eap_server_tls_ssl_init(sm, &data->ssl, 0)) {
+	if (eap_server_tls_ssl_init(sm, &data->ssl, 0, EAP_TYPE_FAST)) {
 		wpa_printf(MSG_INFO, "EAP-FAST: Failed to initialize SSL.");
 		eap_fast_reset(sm, data);
 		return NULL;
@@ -1134,7 +1139,7 @@
 
 	pos = wpabuf_mhead(data);
 	end = pos + wpabuf_len(data);
-	while (pos + 4 < end) {
+	while (end - pos > 4) {
 		mandatory = pos[0] & 0x80;
 		tlv_type = WPA_GET_BE16(pos) & 0x3fff;
 		pos += 2;
diff --git a/src/eap_server/eap_server_peap.c b/src/eap_server/eap_server_peap.c
index 3848f30..51062b0 100644
--- a/src/eap_server/eap_server_peap.c
+++ b/src/eap_server/eap_server_peap.c
@@ -95,6 +95,37 @@
 		   eap_peap_state_txt(data->state),
 		   eap_peap_state_txt(state));
 	data->state = state;
+	if (state == FAILURE || state == FAILURE_REQ)
+		tls_connection_remove_session(data->ssl.conn);
+}
+
+
+static void eap_peap_valid_session(struct eap_sm *sm,
+				   struct eap_peap_data *data)
+{
+	struct wpabuf *buf;
+
+	if (!sm->tls_session_lifetime ||
+	    tls_connection_resumed(sm->ssl_ctx, data->ssl.conn))
+		return;
+
+	buf = wpabuf_alloc(1 + 1 + sm->identity_len);
+	if (!buf)
+		return;
+	wpabuf_put_u8(buf, EAP_TYPE_PEAP);
+	if (sm->identity) {
+		u8 id_len;
+
+		if (sm->identity_len <= 255)
+			id_len = sm->identity_len;
+		else
+			id_len = 255;
+		wpabuf_put_u8(buf, id_len);
+		wpabuf_put_data(buf, sm->identity, id_len);
+	} else {
+		wpabuf_put_u8(buf, 0);
+	}
+	tls_connection_set_success_data(data->ssl.conn, buf);
 }
 
 
@@ -151,7 +182,7 @@
 	data->state = START;
 	data->crypto_binding = OPTIONAL_BINDING;
 
-	if (eap_server_tls_ssl_init(sm, &data->ssl, 0)) {
+	if (eap_server_tls_ssl_init(sm, &data->ssl, 0, EAP_TYPE_PEAP)) {
 		wpa_printf(MSG_INFO, "EAP-PEAP: Failed to initialize SSL.");
 		eap_peap_reset(sm, data);
 		return NULL;
@@ -708,10 +739,12 @@
 		if (status == EAP_TLV_RESULT_SUCCESS) {
 			wpa_printf(MSG_INFO, "EAP-PEAP: TLV Result - Success "
 				   "- requested %s", requested);
-			if (data->tlv_request == TLV_REQ_SUCCESS)
+			if (data->tlv_request == TLV_REQ_SUCCESS) {
 				eap_peap_state(data, SUCCESS);
-			else
+				eap_peap_valid_session(sm, data);
+			} else {
 				eap_peap_state(data, FAILURE);
+			}
 			
 		} else if (status == EAP_TLV_RESULT_FAILURE) {
 			wpa_printf(MSG_INFO, "EAP-PEAP: TLV Result - Failure "
@@ -1094,6 +1127,7 @@
 		wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase 2 Success");
 		if (data->state == SUCCESS_REQ) {
 			eap_peap_state(data, SUCCESS);
+			eap_peap_valid_session(sm, data);
 		}
 		break;
 	case EAP_CODE_FAILURE:
@@ -1159,6 +1193,7 @@
 		break;
 	case SUCCESS_REQ:
 		eap_peap_state(data, SUCCESS);
+		eap_peap_valid_session(sm, data);
 		break;
 	case FAILURE_REQ:
 		eap_peap_state(data, FAILURE);
@@ -1175,10 +1210,65 @@
 			     struct wpabuf *respData)
 {
 	struct eap_peap_data *data = priv;
+	const struct wpabuf *buf;
+	const u8 *pos;
+	u8 id_len;
+
 	if (eap_server_tls_process(sm, &data->ssl, respData, data,
 				   EAP_TYPE_PEAP, eap_peap_process_version,
-				   eap_peap_process_msg) < 0)
+				   eap_peap_process_msg) < 0) {
 		eap_peap_state(data, FAILURE);
+		return;
+	}
+
+	if (data->state == SUCCESS ||
+	    !tls_connection_established(sm->ssl_ctx, data->ssl.conn) ||
+	    !tls_connection_resumed(sm->ssl_ctx, data->ssl.conn))
+		return;
+
+	buf = tls_connection_get_success_data(data->ssl.conn);
+	if (!buf || wpabuf_len(buf) < 2) {
+		wpa_printf(MSG_DEBUG,
+			   "EAP-PEAP: No success data in resumed session - reject attempt");
+		eap_peap_state(data, FAILURE);
+		return;
+	}
+
+	pos = wpabuf_head(buf);
+	if (*pos != EAP_TYPE_PEAP) {
+		wpa_printf(MSG_DEBUG,
+			   "EAP-PEAP: Resumed session for another EAP type (%u) - reject attempt",
+			   *pos);
+		eap_peap_state(data, FAILURE);
+		return;
+	}
+
+	pos++;
+	id_len = *pos++;
+	wpa_hexdump_ascii(MSG_DEBUG, "EAP-PEAP: Identity from cached session",
+			  pos, id_len);
+	os_free(sm->identity);
+	sm->identity = os_malloc(id_len ? id_len : 1);
+	if (!sm->identity) {
+		sm->identity_len = 0;
+		eap_peap_state(data, FAILURE);
+		return;
+	}
+
+	os_memcpy(sm->identity, pos, id_len);
+	sm->identity_len = id_len;
+
+	if (eap_user_get(sm, sm->identity, sm->identity_len, 1) != 0) {
+		wpa_hexdump_ascii(MSG_DEBUG, "EAP-PEAP: Phase2 Identity not found in the user database",
+				  sm->identity, sm->identity_len);
+		eap_peap_state(data, FAILURE);
+		return;
+	}
+
+	wpa_printf(MSG_DEBUG,
+		   "EAP-PEAP: Resuming previous session - skip Phase2");
+	eap_peap_state(data, SUCCESS_REQ);
+	tls_connection_set_success_data_resumed(data->ssl.conn);
 }
 
 
diff --git a/src/eap_server/eap_server_tls.c b/src/eap_server/eap_server_tls.c
index 58cfe8a..bd18a4b 100644
--- a/src/eap_server/eap_server_tls.c
+++ b/src/eap_server/eap_server_tls.c
@@ -48,6 +48,23 @@
 		   eap_tls_state_txt(data->state),
 		   eap_tls_state_txt(state));
 	data->state = state;
+	if (state == FAILURE)
+		tls_connection_remove_session(data->ssl.conn);
+}
+
+
+static void eap_tls_valid_session(struct eap_sm *sm, struct eap_tls_data *data)
+{
+	struct wpabuf *buf;
+
+	if (!sm->tls_session_lifetime)
+		return;
+
+	buf = wpabuf_alloc(1);
+	if (!buf)
+		return;
+	wpabuf_put_u8(buf, data->eap_type);
+	tls_connection_set_success_data(data->ssl.conn, buf);
 }
 
 
@@ -60,7 +77,7 @@
 		return NULL;
 	data->state = START;
 
-	if (eap_server_tls_ssl_init(sm, &data->ssl, 1)) {
+	if (eap_server_tls_ssl_init(sm, &data->ssl, 1, EAP_TYPE_TLS)) {
 		wpa_printf(MSG_INFO, "EAP-TLS: Failed to initialize SSL.");
 		eap_tls_reset(sm, data);
 		return NULL;
@@ -82,7 +99,7 @@
 		return NULL;
 	data->state = START;
 
-	if (eap_server_tls_ssl_init(sm, &data->ssl, 0)) {
+	if (eap_server_tls_ssl_init(sm, &data->ssl, 0, EAP_UNAUTH_TLS_TYPE)) {
 		wpa_printf(MSG_INFO, "EAP-TLS: Failed to initialize SSL.");
 		eap_tls_reset(sm, data);
 		return NULL;
@@ -104,7 +121,8 @@
 		return NULL;
 	data->state = START;
 
-	if (eap_server_tls_ssl_init(sm, &data->ssl, 0)) {
+	if (eap_server_tls_ssl_init(sm, &data->ssl, 0,
+				    EAP_WFA_UNAUTH_TLS_TYPE)) {
 		wpa_printf(MSG_INFO, "EAP-TLS: Failed to initialize SSL.");
 		eap_tls_reset(sm, data);
 		return NULL;
@@ -183,6 +201,7 @@
 		 * fragments waiting to be sent out. */
 		wpa_printf(MSG_DEBUG, "EAP-TLS: Done");
 		eap_tls_state(data, SUCCESS);
+		eap_tls_valid_session(sm, data);
 	}
 
 	return res;
@@ -234,10 +253,41 @@
 			    struct wpabuf *respData)
 {
 	struct eap_tls_data *data = priv;
+	const struct wpabuf *buf;
+	const u8 *pos;
+
 	if (eap_server_tls_process(sm, &data->ssl, respData, data,
 				   data->eap_type, NULL, eap_tls_process_msg) <
-	    0)
+	    0) {
 		eap_tls_state(data, FAILURE);
+		return;
+	}
+
+	if (!tls_connection_established(sm->ssl_ctx, data->ssl.conn) ||
+	    !tls_connection_resumed(sm->ssl_ctx, data->ssl.conn))
+		return;
+
+	buf = tls_connection_get_success_data(data->ssl.conn);
+	if (!buf || wpabuf_len(buf) < 1) {
+		wpa_printf(MSG_DEBUG,
+			   "EAP-TLS: No success data in resumed session - reject attempt");
+		eap_tls_state(data, FAILURE);
+		return;
+	}
+
+	pos = wpabuf_head(buf);
+	if (*pos != data->eap_type) {
+		wpa_printf(MSG_DEBUG,
+			   "EAP-TLS: Resumed session for another EAP type (%u) - reject attempt",
+			   *pos);
+		eap_tls_state(data, FAILURE);
+		return;
+	}
+
+	wpa_printf(MSG_DEBUG,
+		   "EAP-TLS: Resuming previous session");
+	eap_tls_state(data, SUCCESS);
+	tls_connection_set_success_data_resumed(data->ssl.conn);
 }
 
 
diff --git a/src/eap_server/eap_server_tls_common.c b/src/eap_server/eap_server_tls_common.c
index 23498c9..05677b7 100644
--- a/src/eap_server/eap_server_tls_common.c
+++ b/src/eap_server/eap_server_tls_common.c
@@ -44,8 +44,11 @@
 
 
 int eap_server_tls_ssl_init(struct eap_sm *sm, struct eap_ssl_data *data,
-			    int verify_peer)
+			    int verify_peer, int eap_type)
 {
+	u8 session_ctx[8];
+	unsigned int flags = 0;
+
 	if (sm->ssl_ctx == NULL) {
 		wpa_printf(MSG_ERROR, "TLS context not initialized - cannot use TLS-based EAP method");
 		return -1;
@@ -68,7 +71,13 @@
 #endif /* CONFIG_TESTING_OPTIONS */
 #endif /* CONFIG_TLS_INTERNAL */
 
-	if (tls_connection_set_verify(sm->ssl_ctx, data->conn, verify_peer)) {
+	if (eap_type != EAP_TYPE_FAST)
+		flags |= TLS_CONN_DISABLE_SESSION_TICKET;
+	os_memcpy(session_ctx, "hostapd", 7);
+	session_ctx[7] = (u8) eap_type;
+	if (tls_connection_set_verify(sm->ssl_ctx, data->conn, verify_peer,
+				      flags, session_ctx,
+				      sizeof(session_ctx))) {
 		wpa_printf(MSG_INFO, "SSL: Failed to configure verification "
 			   "of TLS peer certificate");
 		tls_connection_deinit(sm->ssl_ctx, data->conn);
@@ -133,10 +142,10 @@
 				      struct eap_ssl_data *data, u8 eap_type,
 				      size_t *len)
 {
-	struct tls_keys keys;
+	struct tls_random keys;
 	u8 *out;
 
-	if (tls_connection_get_keys(sm->ssl_ctx, data->conn, &keys))
+	if (tls_connection_get_random(sm->ssl_ctx, data->conn, &keys))
 		return NULL;
 
 	if (keys.client_random == NULL || keys.server_random == NULL)
diff --git a/src/eap_server/eap_server_ttls.c b/src/eap_server/eap_server_ttls.c
index 31c67e8..53ffa1e 100644
--- a/src/eap_server/eap_server_ttls.c
+++ b/src/eap_server/eap_server_ttls.c
@@ -71,6 +71,36 @@
 		   eap_ttls_state_txt(data->state),
 		   eap_ttls_state_txt(state));
 	data->state = state;
+	if (state == FAILURE)
+		tls_connection_remove_session(data->ssl.conn);
+}
+
+
+static void eap_ttls_valid_session(struct eap_sm *sm,
+				   struct eap_ttls_data *data)
+{
+	struct wpabuf *buf;
+
+	if (!sm->tls_session_lifetime)
+		return;
+
+	buf = wpabuf_alloc(1 + 1 + sm->identity_len);
+	if (!buf)
+		return;
+	wpabuf_put_u8(buf, EAP_TYPE_TTLS);
+	if (sm->identity) {
+		u8 id_len;
+
+		if (sm->identity_len <= 255)
+			id_len = sm->identity_len;
+		else
+			id_len = 255;
+		wpabuf_put_u8(buf, id_len);
+		wpabuf_put_data(buf, sm->identity, id_len);
+	} else {
+		wpabuf_put_u8(buf, 0);
+	}
+	tls_connection_set_success_data(data->ssl.conn, buf);
 }
 
 
@@ -317,7 +347,7 @@
 	data->ttls_version = EAP_TTLS_VERSION;
 	data->state = START;
 
-	if (eap_server_tls_ssl_init(sm, &data->ssl, 0)) {
+	if (eap_server_tls_ssl_init(sm, &data->ssl, 0, EAP_TYPE_TTLS)) {
 		wpa_printf(MSG_INFO, "EAP-TTLS: Failed to initialize SSL.");
 		eap_ttls_reset(sm, data);
 		return NULL;
@@ -518,6 +548,7 @@
 
 	wpa_printf(MSG_DEBUG, "EAP-TTLS/PAP: Correct user password");
 	eap_ttls_state(data, SUCCESS);
+	eap_ttls_valid_session(sm, data);
 }
 
 
@@ -576,6 +607,7 @@
 	    0) {
 		wpa_printf(MSG_DEBUG, "EAP-TTLS/CHAP: Correct user password");
 		eap_ttls_state(data, SUCCESS);
+		eap_ttls_valid_session(sm, data);
 	} else {
 		wpa_printf(MSG_DEBUG, "EAP-TTLS/CHAP: Invalid user password");
 		eap_ttls_state(data, FAILURE);
@@ -643,6 +675,7 @@
 	if (os_memcmp_const(nt_response, response + 2 + 24, 24) == 0) {
 		wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAP: Correct response");
 		eap_ttls_state(data, SUCCESS);
+		eap_ttls_valid_session(sm, data);
 	} else {
 		wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAP: Invalid NT-Response");
 		wpa_hexdump(MSG_MSGDUMP, "EAP-TTLS/MSCHAP: Received",
@@ -906,6 +939,7 @@
 		break;
 	case PHASE2_METHOD:
 		eap_ttls_state(data, SUCCESS);
+		eap_ttls_valid_session(sm, data);
 		break;
 	case FAILURE:
 		break;
@@ -1129,6 +1163,7 @@
 			wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: Peer "
 				   "acknowledged response");
 			eap_ttls_state(data, SUCCESS);
+			eap_ttls_valid_session(sm, data);
 		} else if (!data->mschapv2_resp_ok) {
 			wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: Peer "
 				   "acknowledged error");
@@ -1155,10 +1190,64 @@
 			     struct wpabuf *respData)
 {
 	struct eap_ttls_data *data = priv;
+	const struct wpabuf *buf;
+	const u8 *pos;
+	u8 id_len;
+
 	if (eap_server_tls_process(sm, &data->ssl, respData, data,
 				   EAP_TYPE_TTLS, eap_ttls_process_version,
-				   eap_ttls_process_msg) < 0)
+				   eap_ttls_process_msg) < 0) {
 		eap_ttls_state(data, FAILURE);
+		return;
+	}
+
+	if (!tls_connection_established(sm->ssl_ctx, data->ssl.conn) ||
+	    !tls_connection_resumed(sm->ssl_ctx, data->ssl.conn))
+		return;
+
+	buf = tls_connection_get_success_data(data->ssl.conn);
+	if (!buf || wpabuf_len(buf) < 1) {
+		wpa_printf(MSG_DEBUG,
+			   "EAP-TTLS: No success data in resumed session - reject attempt");
+		eap_ttls_state(data, FAILURE);
+		return;
+	}
+
+	pos = wpabuf_head(buf);
+	if (*pos != EAP_TYPE_TTLS) {
+		wpa_printf(MSG_DEBUG,
+			   "EAP-TTLS: Resumed session for another EAP type (%u) - reject attempt",
+			   *pos);
+		eap_ttls_state(data, FAILURE);
+		return;
+	}
+
+	pos++;
+	id_len = *pos++;
+	wpa_hexdump_ascii(MSG_DEBUG, "EAP-TTLS: Identity from cached session",
+			  pos, id_len);
+	os_free(sm->identity);
+	sm->identity = os_malloc(id_len ? id_len : 1);
+	if (!sm->identity) {
+		sm->identity_len = 0;
+		eap_ttls_state(data, FAILURE);
+		return;
+	}
+
+	os_memcpy(sm->identity, pos, id_len);
+	sm->identity_len = id_len;
+
+	if (eap_user_get(sm, sm->identity, sm->identity_len, 1) != 0) {
+		wpa_hexdump_ascii(MSG_DEBUG, "EAP-TTLS: Phase2 Identity not found in the user database",
+				  sm->identity, sm->identity_len);
+		eap_ttls_state(data, FAILURE);
+		return;
+	}
+
+	wpa_printf(MSG_DEBUG,
+		   "EAP-TTLS: Resuming previous session - skip Phase2");
+	eap_ttls_state(data, SUCCESS);
+	tls_connection_set_success_data_resumed(data->ssl.conn);
 }
 
 
diff --git a/src/eap_server/eap_sim_db.c b/src/eap_server/eap_sim_db.c
index acf5435..d84c3d2 100644
--- a/src/eap_server/eap_sim_db.c
+++ b/src/eap_server/eap_sim_db.c
@@ -66,6 +66,7 @@
 	struct eap_sim_pseudonym *pseudonyms;
 	struct eap_sim_reauth *reauths;
 	struct eap_sim_db_pending *pending;
+	unsigned int eap_sim_db_timeout;
 #ifdef CONFIG_SQLITE
 	sqlite3 *sqlite_db;
 	char db_tmp_identity[100];
@@ -76,6 +77,10 @@
 };
 
 
+static void eap_sim_db_del_timeout(void *eloop_ctx, void *user_ctx);
+static void eap_sim_db_query_timeout(void *eloop_ctx, void *user_ctx);
+
+
 #ifdef CONFIG_SQLITE
 
 static int db_table_exists(sqlite3 *db, const char *name)
@@ -397,6 +402,57 @@
 }
 
 
+static void eap_sim_db_free_pending(struct eap_sim_db_data *data,
+				    struct eap_sim_db_pending *entry)
+{
+	eloop_cancel_timeout(eap_sim_db_query_timeout, data, entry);
+	eloop_cancel_timeout(eap_sim_db_del_timeout, data, entry);
+	os_free(entry);
+}
+
+
+static void eap_sim_db_del_pending(struct eap_sim_db_data *data,
+				   struct eap_sim_db_pending *entry)
+{
+	struct eap_sim_db_pending **pp = &data->pending;
+
+	while (*pp != NULL) {
+		if (*pp == entry) {
+			*pp = entry->next;
+			eap_sim_db_free_pending(data, entry);
+			return;
+		}
+		pp = &(*pp)->next;
+	}
+}
+
+
+static void eap_sim_db_del_timeout(void *eloop_ctx, void *user_ctx)
+{
+	struct eap_sim_db_data *data = eloop_ctx;
+	struct eap_sim_db_pending *entry = user_ctx;
+
+	wpa_printf(MSG_DEBUG, "EAP-SIM DB: Delete query timeout for %p", entry);
+	eap_sim_db_del_pending(data, entry);
+}
+
+
+static void eap_sim_db_query_timeout(void *eloop_ctx, void *user_ctx)
+{
+	struct eap_sim_db_data *data = eloop_ctx;
+	struct eap_sim_db_pending *entry = user_ctx;
+
+	/*
+	 * Report failure and allow some time for EAP server to process it
+	 * before deleting the query.
+	 */
+	wpa_printf(MSG_DEBUG, "EAP-SIM DB: Query timeout for %p", entry);
+	entry->state = FAILURE;
+	data->get_complete_cb(data->ctx, entry->cb_session_ctx);
+	eloop_register_timeout(1, 0, eap_sim_db_del_timeout, data, entry);
+}
+
+
 static void eap_sim_db_sim_resp_auth(struct eap_sim_db_data *data,
 				     const char *imsi, char *buf)
 {
@@ -472,7 +528,7 @@
 
 parse_fail:
 	wpa_printf(MSG_DEBUG, "EAP-SIM DB: Failed to parse response string");
-	os_free(entry);
+	eap_sim_db_free_pending(data, entry);
 }
 
 
@@ -563,7 +619,7 @@
 
 parse_fail:
 	wpa_printf(MSG_DEBUG, "EAP-SIM DB: Failed to parse response string");
-	os_free(entry);
+	eap_sim_db_free_pending(data, entry);
 }
 
 
@@ -690,12 +746,13 @@
 /**
  * eap_sim_db_init - Initialize EAP-SIM DB / authentication gateway interface
  * @config: Configuration data (e.g., file name)
+ * @db_timeout: Database lookup timeout
  * @get_complete_cb: Callback function for reporting availability of triplets
  * @ctx: Context pointer for get_complete_cb
  * Returns: Pointer to a private data structure or %NULL on failure
  */
 struct eap_sim_db_data *
-eap_sim_db_init(const char *config,
+eap_sim_db_init(const char *config, unsigned int db_timeout,
 		void (*get_complete_cb)(void *ctx, void *session_ctx),
 		void *ctx)
 {
@@ -709,6 +766,7 @@
 	data->sock = -1;
 	data->get_complete_cb = get_complete_cb;
 	data->ctx = ctx;
+	data->eap_sim_db_timeout = db_timeout;
 	data->fname = os_strdup(config);
 	if (data->fname == NULL)
 		goto fail;
@@ -796,7 +854,7 @@
 	while (pending) {
 		prev_pending = pending;
 		pending = pending->next;
-		os_free(prev_pending);
+		eap_sim_db_free_pending(data, prev_pending);
 	}
 
 	os_free(data);
@@ -833,11 +891,11 @@
 }
 
 
-static void eap_sim_db_expire_pending(struct eap_sim_db_data *data)
+static void eap_sim_db_expire_pending(struct eap_sim_db_data *data,
+				      struct eap_sim_db_pending *entry)
 {
-	/* TODO: add limit for maximum length for pending list; remove latest
-	 * (i.e., last) entry from the list if the limit is reached; could also
-	 * use timeout to expire pending entries */
+	eloop_register_timeout(data->eap_sim_db_timeout, 0,
+			       eap_sim_db_query_timeout, data, entry);
 }
 
 
@@ -891,7 +949,7 @@
 		if (entry->state == FAILURE) {
 			wpa_printf(MSG_DEBUG, "EAP-SIM DB: Pending entry -> "
 				   "failure");
-			os_free(entry);
+			eap_sim_db_free_pending(data, entry);
 			return EAP_SIM_DB_FAILURE;
 		}
 
@@ -911,7 +969,7 @@
 		os_memcpy(sres, entry->u.sim.sres,
 			  num_chal * EAP_SIM_SRES_LEN);
 		os_memcpy(kc, entry->u.sim.kc, num_chal * EAP_SIM_KC_LEN);
-		os_free(entry);
+		eap_sim_db_free_pending(data, entry);
 		return num_chal;
 	}
 
@@ -945,7 +1003,8 @@
 	entry->cb_session_ctx = cb_session_ctx;
 	entry->state = PENDING;
 	eap_sim_db_add_pending(data, entry);
-	eap_sim_db_expire_pending(data);
+	eap_sim_db_expire_pending(data, entry);
+	wpa_printf(MSG_DEBUG, "EAP-SIM DB: Added query %p", entry);
 
 	return EAP_SIM_DB_PENDING;
 }
@@ -1356,7 +1415,7 @@
 	entry = eap_sim_db_get_pending(data, imsi, 1);
 	if (entry) {
 		if (entry->state == FAILURE) {
-			os_free(entry);
+			eap_sim_db_free_pending(data, entry);
 			wpa_printf(MSG_DEBUG, "EAP-SIM DB: Failure");
 			return EAP_SIM_DB_FAILURE;
 		}
@@ -1375,7 +1434,7 @@
 		os_memcpy(ck, entry->u.aka.ck, EAP_AKA_CK_LEN);
 		os_memcpy(res, entry->u.aka.res, EAP_AKA_RES_MAX_LEN);
 		*res_len = entry->u.aka.res_len;
-		os_free(entry);
+		eap_sim_db_free_pending(data, entry);
 		return 0;
 	}
 
@@ -1406,7 +1465,8 @@
 	entry->cb_session_ctx = cb_session_ctx;
 	entry->state = PENDING;
 	eap_sim_db_add_pending(data, entry);
-	eap_sim_db_expire_pending(data);
+	eap_sim_db_expire_pending(data, entry);
+	wpa_printf(MSG_DEBUG, "EAP-SIM DB: Added query %p", entry);
 
 	return EAP_SIM_DB_PENDING;
 }
diff --git a/src/eap_server/eap_sim_db.h b/src/eap_server/eap_sim_db.h
index 53a1a7c..ca900b9 100644
--- a/src/eap_server/eap_sim_db.h
+++ b/src/eap_server/eap_sim_db.h
@@ -31,7 +31,7 @@
 struct eap_sim_db_data;
 
 struct eap_sim_db_data *
-eap_sim_db_init(const char *config,
+eap_sim_db_init(const char *config, unsigned int db_timeout,
 		void (*get_complete_cb)(void *ctx, void *session_ctx),
 		void *ctx);
 
diff --git a/src/eap_server/eap_tls_common.h b/src/eap_server/eap_tls_common.h
index ddf90b8..dc943eb 100644
--- a/src/eap_server/eap_tls_common.h
+++ b/src/eap_server/eap_tls_common.h
@@ -70,7 +70,7 @@
 struct wpabuf * eap_tls_msg_alloc(EapType type, size_t payload_len,
 				  u8 code, u8 identifier);
 int eap_server_tls_ssl_init(struct eap_sm *sm, struct eap_ssl_data *data,
-			    int verify_peer);
+			    int verify_peer, int eap_type);
 void eap_server_tls_ssl_deinit(struct eap_sm *sm, struct eap_ssl_data *data);
 u8 * eap_server_tls_derive_key(struct eap_sm *sm, struct eap_ssl_data *data,
 			       char *label, size_t len);
diff --git a/src/eap_server/ikev2.c b/src/eap_server/ikev2.c
index 632598f..5385cd8 100644
--- a/src/eap_server/ikev2.c
+++ b/src/eap_server/ikev2.c
@@ -133,7 +133,7 @@
 
 	t = (const struct ikev2_transform *) pos;
 	transform_len = WPA_GET_BE16(t->transform_length);
-	if (transform_len < (int) sizeof(*t) || pos + transform_len > end) {
+	if (transform_len < (int) sizeof(*t) || transform_len > end - pos) {
 		wpa_printf(MSG_INFO, "IKEV2: Invalid transform length %d",
 			   transform_len);
 		return -1;
@@ -221,7 +221,7 @@
 
 	p = (const struct ikev2_proposal *) pos;
 	proposal_len = WPA_GET_BE16(p->proposal_length);
-	if (proposal_len < (int) sizeof(*p) || pos + proposal_len > end) {
+	if (proposal_len < (int) sizeof(*p) || proposal_len > end - pos) {
 		wpa_printf(MSG_INFO, "IKEV2: Invalid proposal length %d",
 			   proposal_len);
 		return -1;
@@ -256,7 +256,7 @@
 
 	ppos = (const u8 *) (p + 1);
 	pend = pos + proposal_len;
-	if (ppos + p->spi_size > pend) {
+	if (p->spi_size > pend - ppos) {
 		wpa_printf(MSG_INFO, "IKEV2: Not enough room for SPI "
 			   "in proposal");
 		return -1;
diff --git a/src/eapol_auth/eapol_auth_sm.c b/src/eapol_auth/eapol_auth_sm.c
index 3b0c2e4..ff33d28 100644
--- a/src/eapol_auth/eapol_auth_sm.c
+++ b/src/eapol_auth/eapol_auth_sm.c
@@ -1,6 +1,6 @@
 /*
  * IEEE 802.1X-2004 Authenticator - EAPOL state machine
- * Copyright (c) 2002-2014, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2002-2015, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -198,6 +198,18 @@
 {
 	SM_ENTRY_MA(AUTH_PAE, INITIALIZE, auth_pae);
 	sm->portMode = Auto;
+
+	/*
+	 * Clearing keyRun here is not specified in IEEE Std 802.1X-2004, but
+	 * it looks like this would be logical thing to do here since the
+	 * EAPOL-Key exchange is not possible in this state. It is possible to
+	 * get here on disconnection event without advancing to the
+	 * AUTHENTICATING state to clear keyRun before the IEEE 802.11 RSN
+	 * authenticator state machine runs and that may advance from
+	 * AUTHENTICATION2 to INITPMK if keyRun = TRUE has been left from the
+	 * last association. This can be avoided by clearing keyRun here.
+	 */
+	sm->keyRun = FALSE;
 }
 
 
@@ -835,6 +847,7 @@
 	eap_conf.server_id = eapol->conf.server_id;
 	eap_conf.server_id_len = eapol->conf.server_id_len;
 	eap_conf.erp = eapol->conf.erp;
+	eap_conf.tls_session_lifetime = eapol->conf.tls_session_lifetime;
 	sm->eap = eap_server_sm_init(sm, &eapol_cb, &eap_conf);
 	if (sm->eap == NULL) {
 		eapol_auth_free(sm);
@@ -1080,6 +1093,87 @@
 }
 
 
+void eapol_auth_reauthenticate(struct eapol_state_machine *sm)
+{
+	wpa_printf(MSG_DEBUG, "EAPOL: External reauthentication trigger for "
+		   MACSTR, MAC2STR(sm->addr));
+	sm->reAuthenticate = TRUE;
+	eapol_auth_step(sm);
+}
+
+
+int eapol_auth_set_conf(struct eapol_state_machine *sm, const char *param,
+			const char *value)
+{
+	wpa_printf(MSG_DEBUG, "EAPOL: External configuration operation for "
+		   MACSTR " - param=%s value=%s",
+		   MAC2STR(sm->addr), param, value);
+
+	if (os_strcasecmp(param, "AdminControlledDirections") == 0) {
+		if (os_strcmp(value, "Both") == 0)
+			sm->adminControlledDirections = Both;
+		else if (os_strcmp(value, "In") == 0)
+			sm->adminControlledDirections = In;
+		else
+			return -1;
+		eapol_auth_step(sm);
+		return 0;
+	}
+
+	if (os_strcasecmp(param, "AdminControlledPortControl") == 0) {
+		if (os_strcmp(value, "ForceAuthorized") == 0)
+			sm->portControl = ForceAuthorized;
+		else if (os_strcmp(value, "ForceUnauthorized") == 0)
+			sm->portControl = ForceUnauthorized;
+		else if (os_strcmp(value, "Auto") == 0)
+			sm->portControl = Auto;
+		else
+			return -1;
+		eapol_auth_step(sm);
+		return 0;
+	}
+
+	if (os_strcasecmp(param, "quietPeriod") == 0) {
+		sm->quietPeriod = atoi(value);
+		return 0;
+	}
+
+	if (os_strcasecmp(param, "serverTimeout") == 0) {
+		sm->serverTimeout = atoi(value);
+		return 0;
+	}
+
+	if (os_strcasecmp(param, "reAuthPeriod") == 0) {
+		sm->reAuthPeriod = atoi(value);
+		return 0;
+	}
+
+	if (os_strcasecmp(param, "reAuthEnabled") == 0) {
+		if (os_strcmp(value, "TRUE") == 0)
+			sm->reAuthEnabled = TRUE;
+		else if (os_strcmp(value, "FALSE") == 0)
+			sm->reAuthEnabled = FALSE;
+		else
+			return -1;
+		eapol_auth_step(sm);
+		return 0;
+	}
+
+	if (os_strcasecmp(param, "KeyTransmissionEnabled") == 0) {
+		if (os_strcmp(value, "TRUE") == 0)
+			sm->keyTxEnabled = TRUE;
+		else if (os_strcmp(value, "FALSE") == 0)
+			sm->keyTxEnabled = FALSE;
+		else
+			return -1;
+		eapol_auth_step(sm);
+		return 0;
+	}
+
+	return -1;
+}
+
+
 static int eapol_auth_conf_clone(struct eapol_auth_config *dst,
 				 struct eapol_auth_config *src)
 {
@@ -1148,6 +1242,7 @@
 	}
 	dst->erp_send_reauth_start = src->erp_send_reauth_start;
 	dst->erp = src->erp;
+	dst->tls_session_lifetime = src->tls_session_lifetime;
 
 	return 0;
 
diff --git a/src/eapol_auth/eapol_auth_sm.h b/src/eapol_auth/eapol_auth_sm.h
index ebed19a..e1974e4 100644
--- a/src/eapol_auth/eapol_auth_sm.h
+++ b/src/eapol_auth/eapol_auth_sm.h
@@ -1,6 +1,6 @@
 /*
  * IEEE 802.1X-2004 Authenticator - EAPOL state machine
- * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2002-2015, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -27,6 +27,7 @@
 	int erp_send_reauth_start;
 	char *erp_domain; /* a copy of this will be allocated */
 	int erp; /* Whether ERP is enabled on authentication server */
+	unsigned int tls_session_lifetime;
 	u8 *pac_opaque_encr_key;
 	u8 *eap_fast_a_id;
 	size_t eap_fast_a_id_len;
@@ -94,5 +95,8 @@
 int eapol_auth_dump_state(struct eapol_state_machine *sm, char *buf,
 			  size_t buflen);
 int eapol_auth_eap_pending_cb(struct eapol_state_machine *sm, void *ctx);
+void eapol_auth_reauthenticate(struct eapol_state_machine *sm);
+int eapol_auth_set_conf(struct eapol_state_machine *sm, const char *param,
+			const char *value);
 
 #endif /* EAPOL_AUTH_SM_H */
diff --git a/src/eapol_auth/eapol_auth_sm_i.h b/src/eapol_auth/eapol_auth_sm_i.h
index a29b49c..aa3e117 100644
--- a/src/eapol_auth/eapol_auth_sm_i.h
+++ b/src/eapol_auth/eapol_auth_sm_i.h
@@ -162,12 +162,6 @@
 	struct radius_class_data radius_class;
 	struct wpabuf *radius_cui; /* Chargeable-User-Identity */
 
-	/* Keys for encrypting and signing EAPOL-Key frames */
-	u8 *eapol_key_sign;
-	size_t eapol_key_sign_len;
-	u8 *eapol_key_crypt;
-	size_t eapol_key_crypt_len;
-
 	struct eap_sm *eap;
 
 	Boolean initializing; /* in process of initializing state machines */
diff --git a/src/eapol_supp/eapol_supp_sm.c b/src/eapol_supp/eapol_supp_sm.c
index eb8c5bb..65460fc 100644
--- a/src/eapol_supp/eapol_supp_sm.c
+++ b/src/eapol_supp/eapol_supp_sm.c
@@ -244,7 +244,8 @@
 
 SM_STATE(SUPP_PAE, CONNECTING)
 {
-	int send_start = sm->SUPP_PAE_state == SUPP_PAE_CONNECTING;
+	int send_start = sm->SUPP_PAE_state == SUPP_PAE_CONNECTING ||
+		sm->SUPP_PAE_state == SUPP_PAE_HELD;
 	SM_ENTRY(SUPP_PAE, CONNECTING);
 
 	if (sm->eapTriggerStart)
@@ -313,6 +314,16 @@
 {
 	SM_ENTRY(SUPP_PAE, RESTART);
 	sm->eapRestart = TRUE;
+	if (sm->altAccept) {
+		/*
+		 * Prevent EAP peer state machine from failing due to prior
+		 * external EAP success notification (altSuccess=TRUE in the
+		 * IDLE state could result in a transition to the FAILURE state.
+		 */
+		wpa_printf(MSG_DEBUG, "EAPOL: Clearing prior altAccept TRUE");
+		sm->eapSuccess = FALSE;
+		sm->altAccept = FALSE;
+	}
 }
 
 
@@ -653,7 +664,9 @@
 	struct ieee802_1x_eapol_key *key;
 	struct eap_key_data keydata;
 	u8 orig_key_sign[IEEE8021X_KEY_SIGN_LEN], datakey[32];
+#ifndef CONFIG_NO_RC4
 	u8 ekey[IEEE8021X_KEY_IV_LEN + IEEE8021X_ENCR_KEY_LEN];
+#endif /* CONFIG_NO_RC4 */
 	int key_len, res, sign_key_len, encr_key_len;
 	u16 rx_key_length;
 	size_t plen;
@@ -747,6 +760,13 @@
 		return;
 	}
 	if (key_len == rx_key_length) {
+#ifdef CONFIG_NO_RC4
+		if (encr_key_len) {
+			/* otherwise unused */
+		}
+		wpa_printf(MSG_ERROR, "EAPOL: RC4 not supported in the build");
+		return;
+#else /* CONFIG_NO_RC4 */
 		os_memcpy(ekey, key->key_iv, IEEE8021X_KEY_IV_LEN);
 		os_memcpy(ekey + IEEE8021X_KEY_IV_LEN, keydata.encr_key,
 			  encr_key_len);
@@ -755,6 +775,7 @@
 			 datakey, key_len);
 		wpa_hexdump_key(MSG_DEBUG, "EAPOL: Decrypted(RC4) key",
 				datakey, key_len);
+#endif /* CONFIG_NO_RC4 */
 	} else if (key_len == 0) {
 		/*
 		 * IEEE 802.1X-2004 specifies that least significant Key Length
diff --git a/src/fst/Makefile b/src/fst/Makefile
new file mode 100644
index 0000000..9c41962
--- /dev/null
+++ b/src/fst/Makefile
@@ -0,0 +1,8 @@
+all:
+	@echo Nothing to be made.
+
+clean:
+	rm -f *~ *.o *.d
+
+install:
+	@echo Nothing to be made.
diff --git a/src/fst/fst.c b/src/fst/fst.c
new file mode 100644
index 0000000..40430e2
--- /dev/null
+++ b/src/fst/fst.c
@@ -0,0 +1,225 @@
+/*
+ * FST module implementation
+ * Copyright (c) 2014, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "fst/fst.h"
+#include "fst/fst_internal.h"
+#include "fst/fst_defs.h"
+#include "fst/fst_ctrl_iface.h"
+
+struct dl_list fst_global_ctrls_list;
+
+
+static void fst_ctrl_iface_notify_peer_state_change(struct fst_iface *iface,
+						    Boolean connected,
+						    const u8 *peer_addr)
+{
+	union fst_event_extra extra;
+
+	extra.peer_state.connected = connected;
+	os_strlcpy(extra.peer_state.ifname, fst_iface_get_name(iface),
+		   sizeof(extra.peer_state.ifname));
+	os_memcpy(extra.peer_state.addr, peer_addr, ETH_ALEN);
+
+	foreach_fst_ctrl_call(on_event, EVENT_PEER_STATE_CHANGED,
+			      iface, NULL, &extra);
+}
+
+
+struct fst_iface * fst_attach(const char *ifname, const u8 *own_addr,
+			      const struct fst_wpa_obj *iface_obj,
+			      const struct fst_iface_cfg *cfg)
+{
+	struct fst_group *g;
+	struct fst_group *group = NULL;
+	struct fst_iface *iface = NULL;
+	Boolean new_group = FALSE;
+
+	WPA_ASSERT(ifname != NULL);
+	WPA_ASSERT(iface_obj != NULL);
+	WPA_ASSERT(cfg != NULL);
+
+	foreach_fst_group(g) {
+		if (os_strcmp(cfg->group_id, fst_group_get_id(g)) == 0) {
+			group = g;
+			break;
+		}
+	}
+
+	if (!group) {
+		group = fst_group_create(cfg->group_id);
+		if (!group) {
+			fst_printf(MSG_ERROR, "%s: FST group cannot be created",
+				   cfg->group_id);
+			return NULL;
+		}
+		new_group = TRUE;
+	}
+
+	iface = fst_iface_create(group, ifname, own_addr, iface_obj, cfg);
+	if (!iface) {
+		fst_printf_group(group, MSG_ERROR, "cannot create iface for %s",
+				 ifname);
+		if (new_group)
+			fst_group_delete(group);
+		return NULL;
+	}
+
+	fst_group_attach_iface(group, iface);
+	fst_group_update_ie(group);
+
+	foreach_fst_ctrl_call(on_iface_added, iface);
+
+	fst_printf_iface(iface, MSG_DEBUG,
+			 "iface attached to group %s (prio=%d, llt=%d)",
+			 cfg->group_id, cfg->priority, cfg->llt);
+
+	return iface;
+}
+
+
+void fst_detach(struct fst_iface *iface)
+{
+	struct fst_group *group = fst_iface_get_group(iface);
+
+	fst_printf_iface(iface, MSG_DEBUG, "iface detached from group %s",
+			 fst_group_get_id(group));
+	fst_session_global_on_iface_detached(iface);
+	foreach_fst_ctrl_call(on_iface_removed, iface);
+	fst_group_detach_iface(group, iface);
+	fst_iface_delete(iface);
+	fst_group_update_ie(group);
+	fst_group_delete_if_empty(group);
+}
+
+
+int fst_global_init(void)
+{
+	dl_list_init(&fst_global_groups_list);
+	dl_list_init(&fst_global_ctrls_list);
+	fst_session_global_init();
+	return 0;
+}
+
+
+void fst_global_deinit(void)
+{
+	struct fst_group *group;
+	struct fst_ctrl_handle *h;
+
+	fst_session_global_deinit();
+	while ((group = fst_first_group()) != NULL)
+		fst_group_delete(group);
+	while ((h = dl_list_first(&fst_global_ctrls_list,
+				  struct fst_ctrl_handle,
+				  global_ctrls_lentry)))
+		fst_global_del_ctrl(h);
+}
+
+
+struct fst_ctrl_handle * fst_global_add_ctrl(const struct fst_ctrl *ctrl)
+{
+	struct fst_ctrl_handle *h;
+
+	if (!ctrl)
+		return NULL;
+
+	h = os_zalloc(sizeof(*h));
+	if (!h)
+		return NULL;
+
+	if (ctrl->init && ctrl->init()) {
+		os_free(h);
+		return NULL;
+	}
+
+	h->ctrl = *ctrl;
+	dl_list_add_tail(&fst_global_ctrls_list, &h->global_ctrls_lentry);
+
+	return h;
+}
+
+
+void fst_global_del_ctrl(struct fst_ctrl_handle *h)
+{
+	dl_list_del(&h->global_ctrls_lentry);
+	if (h->ctrl.deinit)
+		h->ctrl.deinit();
+	os_free(h);
+}
+
+
+void fst_rx_action(struct fst_iface *iface, const struct ieee80211_mgmt *mgmt,
+		   size_t len)
+{
+	if (fst_iface_is_connected(iface, mgmt->sa, FALSE))
+		fst_session_on_action_rx(iface, mgmt, len);
+	else
+		wpa_printf(MSG_DEBUG,
+			   "FST: Ignore FST Action frame - no FST connection with "
+			   MACSTR, MAC2STR(mgmt->sa));
+}
+
+
+void fst_notify_peer_connected(struct fst_iface *iface, const u8 *addr)
+{
+	if (is_zero_ether_addr(addr))
+		return;
+
+#ifndef HOSTAPD
+	fst_group_update_ie(fst_iface_get_group(iface));
+#endif /* HOSTAPD */
+
+	fst_printf_iface(iface, MSG_DEBUG, MACSTR " became connected",
+			 MAC2STR(addr));
+
+	fst_ctrl_iface_notify_peer_state_change(iface, TRUE, addr);
+}
+
+
+void fst_notify_peer_disconnected(struct fst_iface *iface, const u8 *addr)
+{
+	if (is_zero_ether_addr(addr))
+		return;
+
+#ifndef HOSTAPD
+	fst_group_update_ie(fst_iface_get_group(iface));
+#endif /* HOSTAPD */
+
+	fst_printf_iface(iface, MSG_DEBUG, MACSTR " became disconnected",
+			 MAC2STR(addr));
+
+	fst_ctrl_iface_notify_peer_state_change(iface, FALSE, addr);
+}
+
+
+Boolean fst_are_ifaces_aggregated(struct fst_iface *iface1,
+				  struct fst_iface *iface2)
+{
+	return fst_iface_get_group(iface1) == fst_iface_get_group(iface2);
+}
+
+
+enum mb_band_id fst_hw_mode_to_band(enum hostapd_hw_mode mode)
+{
+	switch (mode) {
+	case HOSTAPD_MODE_IEEE80211B:
+	case HOSTAPD_MODE_IEEE80211G:
+		return MB_BAND_ID_WIFI_2_4GHZ;
+	case HOSTAPD_MODE_IEEE80211A:
+		return MB_BAND_ID_WIFI_5GHZ;
+	case HOSTAPD_MODE_IEEE80211AD:
+		return MB_BAND_ID_WIFI_60GHZ;
+	default:
+		WPA_ASSERT(0);
+		return MB_BAND_ID_WIFI_2_4GHZ;
+	}
+}
diff --git a/src/fst/fst.h b/src/fst/fst.h
new file mode 100644
index 0000000..0c0e435
--- /dev/null
+++ b/src/fst/fst.h
@@ -0,0 +1,296 @@
+/*
+ * FST module - interface definitions
+ * Copyright (c) 2014, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef FST_H
+#define FST_H
+
+#ifdef CONFIG_FST
+
+#include "common/defs.h"
+#include "fst/fst_ctrl_iface.h"
+
+/* FST module hostap integration API */
+
+#define US_IN_MS           1000
+#define LLT_UNIT_US        32 /* See 10.32.2.2  Transitioning between states */
+
+#define FST_LLT_MS_TO_VAL(m) (((u32) (m)) * US_IN_MS / LLT_UNIT_US)
+#define FST_LLT_VAL_TO_MS(v) (((u32) (v)) * LLT_UNIT_US / US_IN_MS)
+
+#define FST_MAX_LLT_MS       FST_LLT_VAL_TO_MS(-1)
+#define FST_MAX_PRIO_VALUE   ((u8) -1)
+#define FST_MAX_GROUP_ID_LEN IFNAMSIZ
+
+#define FST_DEFAULT_LLT_CFG_VALUE 50
+
+struct hostapd_hw_modes;
+struct ieee80211_mgmt;
+struct fst_iface;
+struct fst_group;
+struct fst_session;
+struct fst_get_peer_ctx;
+struct fst_ctrl_handle;
+
+struct fst_wpa_obj {
+	void *ctx;
+
+	/**
+	 * get_bssid - Get BSSID of the interface
+	 * @ctx: User context %ctx
+	 * Returns: BSSID for success, %NULL for failure.
+	 *
+	 * NOTE: For AP it returns the own BSSID, while for STA - the BSSID of
+	 * the associated AP.
+	 */
+	const u8 * (*get_bssid)(void *ctx);
+
+	/**
+	 * get_channel_info - Get current channel info
+	 * @ctx: User context %ctx
+	 * @hw_mode: OUT, current HW mode
+	 * @channel: OUT, current channel
+	 */
+	void (*get_channel_info)(void *ctx, enum hostapd_hw_mode *hw_mode,
+				 u8 *channel);
+
+	/**
+	 * get_hw_modes - Get hardware modes
+	 * @ctx: User context %ctx
+	 * @modes: OUT, pointer on array of hw modes
+	 *
+	 * Returns: Number of hw modes available.
+	 */
+	int (*get_hw_modes)(void *ctx, struct hostapd_hw_modes **modes);
+
+	/**
+	 * set_ies - Set interface's MB IE
+	 * @ctx: User context %ctx
+	 * @fst_ies: MB IE buffer (owned by FST module)
+	 */
+	void (*set_ies)(void *ctx, const struct wpabuf *fst_ies);
+
+	/**
+	 * send_action - Send FST Action frame via the interface
+	 * @ctx: User context %ctx
+	 * @addr: Address of the destination STA
+	 * @data: Action frame buffer
+	 * Returns: 0 for success, negative error code for failure.
+	 */
+	int (*send_action)(void *ctx, const u8 *addr, struct wpabuf *data);
+
+	/**
+	 * get_mb_ie - Get last MB IE received from STA
+	 * @ctx: User context %ctx
+	 * @addr: Address of the STA
+	 * Returns: MB IE buffer, %NULL if no MB IE received from the STA
+	 */
+	const struct wpabuf * (*get_mb_ie)(void *ctx, const u8 *addr);
+
+	/**
+	 * update_mb_ie - Update last MB IE received from STA
+	 * @ctx: User context %ctx
+	 * @addr: Address of the STA
+	 * @buf: Buffer that contains the MB IEs data
+	 * @size: Size of data in %buf
+	 */
+	void (*update_mb_ie)(void *ctx, const u8 *addr,
+			     const u8 *buf, size_t size);
+
+	/**
+	 * get_peer_first - Get MAC address of the 1st connected STA
+	 * @ctx: User context %ctx
+	 * @get_ctx: Context to be used for %get_peer_next call
+	 * @mb_only: %TRUE if only multi-band capable peer should be reported
+	 * Returns: Address of the 1st connected STA, %NULL if no STAs connected
+	 */
+	const u8 * (*get_peer_first)(void *ctx,
+				     struct fst_get_peer_ctx **get_ctx,
+				     Boolean mb_only);
+	/**
+	 * get_peer_next - Get MAC address of the next connected STA
+	 * @ctx: User context %ctx
+	 * @get_ctx: Context received from %get_peer_first or previous
+	 *           %get_peer_next call
+	 * @mb_only: %TRUE if only multi-band capable peer should be reported
+	 * Returns: Address of the next connected STA, %NULL if no more STAs
+	 *          connected
+	 */
+	const u8 * (*get_peer_next)(void *ctx,
+				    struct fst_get_peer_ctx **get_ctx,
+				    Boolean mb_only);
+};
+
+/**
+ * fst_global_init - Global FST module initiator
+ * Returns: 0 for success, negative error code for failure.
+ * Note: The purpose of this function is to allocate and initiate global
+ *       FST module data structures (linked lists, static data etc.)
+ *       This function should be called prior to the 1st %fst_attach call.
+ */
+int fst_global_init(void);
+
+/**
+ * fst_global_deinit - Global FST module de-initiator
+ * Note: The purpose of this function is to deallocate and de-initiate global
+ *       FST module data structures (linked lists, static data etc.)
+ */
+void fst_global_deinit(void);
+
+/**
+ * struct fst_ctrl - Notification interface for FST module
+ */
+struct fst_ctrl {
+	/**
+	 * init - Initialize the notification interface
+	 * Returns: 0 for success, negative error code for failure.
+	 */
+	int (*init)(void);
+
+	/**
+	 * deinit - Deinitialize the notification interface
+	 */
+	void (*deinit)(void);
+
+	/**
+	 * on_group_created - Notify about FST group creation
+	 * Returns: 0 for success, negative error code for failure.
+	 */
+	int (*on_group_created)(struct fst_group *g);
+
+	/**
+	 * on_group_deleted - Notify about FST group deletion
+	 */
+	void (*on_group_deleted)(struct fst_group *g);
+
+	/**
+	 * on_iface_added - Notify about interface addition
+	 * Returns: 0 for success, negative error code for failure.
+	 */
+	int (*on_iface_added)(struct fst_iface *i);
+
+	/**
+	 * on_iface_removed - Notify about interface removal
+	 */
+	void (*on_iface_removed)(struct fst_iface *i);
+
+	/**
+	 * on_session_added - Notify about FST session addition
+	 * Returns: 0 for success, negative error code for failure.
+	 */
+	int (*on_session_added)(struct fst_session *s);
+
+	/**
+	 * on_session_removed - Notify about FST session removal
+	 */
+	void (*on_session_removed)(struct fst_session *s);
+
+	/**
+	 * on_event - Notify about FST event
+	 * @event_type: Event type
+	 * @i: Interface object that relates to the event or NULL
+	 * @g: Group object that relates to the event or NULL
+	 * @extra - Event specific data (see fst_ctrl_iface.h for more info)
+	 */
+	void (*on_event)(enum fst_event_type event_type, struct fst_iface *i,
+			 struct fst_session *s,
+			 const union fst_event_extra *extra);
+};
+
+struct fst_ctrl_handle * fst_global_add_ctrl(const struct fst_ctrl *ctrl);
+void fst_global_del_ctrl(struct fst_ctrl_handle *h);
+
+/**
+ * NOTE: These values have to be read from configuration file
+ */
+struct fst_iface_cfg {
+	char group_id[FST_MAX_GROUP_ID_LEN + 1];
+	u8 priority;
+	u32 llt;
+};
+
+/**
+ * fst_attach - Attach interface to an FST group according to configuration read
+ * @ifname: Interface name
+ * @own_addr: Own interface MAC address
+ * @iface_obj: Callbacks to be used by FST module to communicate with
+ *             hostapd/wpa_supplicant
+ * @cfg: FST-related interface configuration read from the configuration file
+ * Returns: FST interface object for success, %NULL for failure.
+ */
+struct fst_iface * fst_attach(const char *ifname,
+			      const u8 *own_addr,
+			      const struct fst_wpa_obj *iface_obj,
+			      const struct fst_iface_cfg *cfg);
+
+/**
+ * fst_detach - Detach an interface
+ * @iface: FST interface object
+ */
+void fst_detach(struct fst_iface *iface);
+
+/* FST module inputs */
+/**
+ * fst_rx_action - FST Action frames handler
+ * @iface: FST interface object
+ * @mgmt: Action frame arrived
+ * @len: Action frame length
+ */
+void fst_rx_action(struct fst_iface *iface, const struct ieee80211_mgmt *mgmt,
+		   size_t len);
+
+/**
+ * fst_notify_peer_connected - FST STA connect handler
+ * @iface: FST interface object
+ * @addr: Address of the connected STA
+ */
+void fst_notify_peer_connected(struct fst_iface *iface, const u8 *addr);
+
+/**
+ * fst_notify_peer_disconnected - FST STA disconnect handler
+ * @iface: FST interface object
+ * @addr: Address of the disconnected STA
+ */
+void fst_notify_peer_disconnected(struct fst_iface *iface, const u8 *addr);
+
+/* FST module auxiliary routines */
+
+/**
+ * fst_are_ifaces_aggregated - Determines whether 2 interfaces belong to the
+ *                             same FST group
+ * @iface1: 1st FST interface object
+ * @iface1: 2nd FST interface object
+ *
+ * Returns: %TRUE if the interfaces belong to the same FST group,
+ *          %FALSE otherwise
+ */
+Boolean fst_are_ifaces_aggregated(struct fst_iface *iface1,
+				  struct fst_iface *iface2);
+
+#else /* CONFIG_FST */
+
+static inline int fst_global_init(void)
+{
+	return 0;
+}
+
+static inline int fst_global_start(void)
+{
+	return 0;
+}
+
+static inline void fst_global_stop(void)
+{
+}
+
+static inline void fst_global_deinit(void)
+{
+}
+
+#endif /* CONFIG_FST */
+
+#endif /* FST_H */
diff --git a/src/fst/fst_ctrl_aux.c b/src/fst/fst_ctrl_aux.c
new file mode 100644
index 0000000..dc7b2a7
--- /dev/null
+++ b/src/fst/fst_ctrl_aux.c
@@ -0,0 +1,69 @@
+/*
+ * FST module implementation
+ * Copyright (c) 2014, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+#include "utils/common.h"
+#include "common/defs.h"
+#include "fst_ctrl_defs.h"
+#include "fst_ctrl_aux.h"
+
+
+static const char *session_event_names[] = {
+	[EVENT_FST_ESTABLISHED] FST_PVAL_EVT_TYPE_ESTABLISHED,
+	[EVENT_FST_SETUP] FST_PVAL_EVT_TYPE_SETUP,
+	[EVENT_FST_SESSION_STATE_CHANGED] FST_PVAL_EVT_TYPE_SESSION_STATE,
+};
+
+static const char *reason_names[] = {
+	[REASON_TEARDOWN] FST_CS_PVAL_REASON_TEARDOWN,
+	[REASON_SETUP] FST_CS_PVAL_REASON_SETUP,
+	[REASON_SWITCH] FST_CS_PVAL_REASON_SWITCH,
+	[REASON_STT] FST_CS_PVAL_REASON_STT,
+	[REASON_REJECT] FST_CS_PVAL_REASON_REJECT,
+	[REASON_ERROR_PARAMS] FST_CS_PVAL_REASON_ERROR_PARAMS,
+	[REASON_RESET] FST_CS_PVAL_REASON_RESET,
+	[REASON_DETACH_IFACE] FST_CS_PVAL_REASON_DETACH_IFACE,
+};
+
+static const char *session_state_names[] = {
+	[FST_SESSION_STATE_INITIAL] FST_CS_PVAL_STATE_INITIAL,
+	[FST_SESSION_STATE_SETUP_COMPLETION] FST_CS_PVAL_STATE_SETUP_COMPLETION,
+	[FST_SESSION_STATE_TRANSITION_DONE] FST_CS_PVAL_STATE_TRANSITION_DONE,
+	[FST_SESSION_STATE_TRANSITION_CONFIRMED]
+	FST_CS_PVAL_STATE_TRANSITION_CONFIRMED,
+};
+
+
+/* helpers */
+const char * fst_get_str_name(unsigned index, const char *names[],
+			      size_t names_size)
+{
+	if (index >= names_size || !names[index])
+		return FST_NAME_UNKNOWN;
+	return names[index];
+}
+
+
+const char * fst_session_event_type_name(enum fst_event_type event)
+{
+	return fst_get_str_name(event, session_event_names,
+				ARRAY_SIZE(session_event_names));
+}
+
+
+const char * fst_reason_name(enum fst_reason reason)
+{
+	return fst_get_str_name(reason, reason_names, ARRAY_SIZE(reason_names));
+}
+
+
+const char * fst_session_state_name(enum fst_session_state state)
+{
+	return fst_get_str_name(state, session_state_names,
+				ARRAY_SIZE(session_state_names));
+}
diff --git a/src/fst/fst_ctrl_aux.h b/src/fst/fst_ctrl_aux.h
new file mode 100644
index 0000000..e2133f5
--- /dev/null
+++ b/src/fst/fst_ctrl_aux.h
@@ -0,0 +1,91 @@
+/*
+ * FST module - miscellaneous definitions
+ * Copyright (c) 2014, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef FST_CTRL_AUX_H
+#define FST_CTRL_AUX_H
+
+#include "common/defs.h"
+
+/* FST module control interface API */
+#define FST_INVALID_SESSION_ID ((u32) -1)
+#define FST_MAX_GROUP_ID_SIZE   32
+#define FST_MAX_INTERFACE_SIZE  32
+
+enum fst_session_state {
+	FST_SESSION_STATE_INITIAL,
+	FST_SESSION_STATE_SETUP_COMPLETION,
+	FST_SESSION_STATE_TRANSITION_DONE,
+	FST_SESSION_STATE_TRANSITION_CONFIRMED,
+	FST_SESSION_STATE_LAST
+};
+
+enum fst_event_type {
+	EVENT_FST_IFACE_STATE_CHANGED,  /* An interface has been either attached
+					 * to or detached from an FST group */
+	EVENT_FST_ESTABLISHED,          /* FST Session has been established */
+	EVENT_FST_SETUP,                /* FST Session request received */
+	EVENT_FST_SESSION_STATE_CHANGED,/* FST Session state has been changed */
+	EVENT_PEER_STATE_CHANGED        /* FST related generic event occurred,
+					 * see struct fst_hostap_event_data for
+					 *  more info */
+};
+
+enum fst_initiator {
+	FST_INITIATOR_UNDEFINED,
+	FST_INITIATOR_LOCAL,
+	FST_INITIATOR_REMOTE,
+};
+
+union fst_event_extra {
+	struct fst_event_extra_iface_state {
+		Boolean attached;
+		char ifname[FST_MAX_INTERFACE_SIZE];
+		char group_id[FST_MAX_GROUP_ID_SIZE];
+	} iface_state; /* for EVENT_FST_IFACE_STATE_CHANGED */
+	struct fst_event_extra_peer_state {
+		Boolean connected;
+		char ifname[FST_MAX_INTERFACE_SIZE];
+		u8 addr[ETH_ALEN];
+	} peer_state; /* for EVENT_PEER_STATE_CHANGED */
+	struct fst_event_extra_session_state {
+		enum fst_session_state old_state;
+		enum fst_session_state new_state;
+		union fst_session_state_switch_extra {
+			struct {
+				enum fst_reason {
+					REASON_TEARDOWN,
+					REASON_SETUP,
+					REASON_SWITCH,
+					REASON_STT,
+					REASON_REJECT,
+					REASON_ERROR_PARAMS,
+					REASON_RESET,
+					REASON_DETACH_IFACE,
+				} reason;
+				u8 reject_code; /* REASON_REJECT */
+				/* REASON_SWITCH,
+				 * REASON_TEARDOWN,
+				 * REASON_REJECT
+				 */
+				enum fst_initiator initiator;
+			} to_initial;
+		} extra;
+	} session_state; /* for EVENT_FST_SESSION_STATE_CHANGED */
+};
+
+/* helpers - prints enum in string form */
+#define FST_NAME_UNKNOWN "UNKNOWN"
+
+const char * fst_get_str_name(unsigned index, const char *names[],
+			      size_t names_size);
+
+const char * fst_session_event_type_name(enum fst_event_type);
+const char * fst_reason_name(enum fst_reason reason);
+const char * fst_session_state_name(enum fst_session_state state);
+
+#endif /* FST_CTRL_AUX_H */
diff --git a/src/fst/fst_ctrl_defs.h b/src/fst/fst_ctrl_defs.h
new file mode 100644
index 0000000..6735389
--- /dev/null
+++ b/src/fst/fst_ctrl_defs.h
@@ -0,0 +1,109 @@
+/*
+ * FST module - shared Control interface definitions
+ * Copyright (c) 2014, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef FST_CTRL_DEFS_H
+#define FST_CTRL_DEFS_H
+
+/* Undefined value */
+#define FST_CTRL_PVAL_NONE                     "NONE"
+
+/* FST-ATTACH parameters */
+#define FST_ATTACH_CMD_PNAME_LLT      "llt"      /* pval = desired LLT */
+#define FST_ATTACH_CMD_PNAME_PRIORITY "priority" /* pval = desired priority */
+
+/* FST-MANAGER parameters */
+/* FST Session states */
+#define FST_CS_PVAL_STATE_INITIAL              "INITIAL"
+#define FST_CS_PVAL_STATE_SETUP_COMPLETION     "SETUP_COMPLETION"
+#define FST_CS_PVAL_STATE_TRANSITION_DONE      "TRANSITION_DONE"
+#define FST_CS_PVAL_STATE_TRANSITION_CONFIRMED "TRANSITION_CONFIRMED"
+
+/* FST Session reset reasons */
+#define FST_CS_PVAL_REASON_TEARDOWN     "REASON_TEARDOWN"
+#define FST_CS_PVAL_REASON_SETUP        "REASON_SETUP"
+#define FST_CS_PVAL_REASON_SWITCH       "REASON_SWITCH"
+#define FST_CS_PVAL_REASON_STT          "REASON_STT"
+#define FST_CS_PVAL_REASON_REJECT       "REASON_REJECT"
+#define FST_CS_PVAL_REASON_ERROR_PARAMS "REASON_ERROR_PARAMS"
+#define FST_CS_PVAL_REASON_RESET        "REASON_RESET"
+#define FST_CS_PVAL_REASON_DETACH_IFACE "REASON_DETACH_IFACE"
+
+/* FST Session responses */
+#define FST_CS_PVAL_RESPONSE_ACCEPT "ACCEPT"
+#define FST_CS_PVAL_RESPONSE_REJECT "REJECT"
+
+/* FST Session action initiator */
+#define FST_CS_PVAL_INITIATOR_LOCAL  "LOCAL"
+#define FST_CS_PVAL_INITIATOR_REMOTE "REMOTE"
+
+/* FST-CLI subcommands and parameter names */
+#define FST_CMD_LIST_GROUPS      "list_groups"
+#define FST_CMD_LIST_IFACES      "list_ifaces"
+#define FST_CMD_IFACE_PEERS      "iface_peers"
+#define FST_CMD_GET_PEER_MBIES   "get_peer_mbies"
+#define FST_CMD_LIST_SESSIONS    "list_sessions"
+#define FST_CMD_SESSION_ADD      "session_add"
+#define FST_CMD_SESSION_REMOVE   "session_remove"
+#define FST_CMD_SESSION_GET      "session_get"
+#define FST_CSG_PNAME_OLD_PEER_ADDR  "old_peer_addr" /* pval = address string */
+#define FST_CSG_PNAME_NEW_PEER_ADDR  "new_peer_addr" /* pval = address string */
+#define FST_CSG_PNAME_OLD_IFNAME "old_ifname" /* pval = ifname */
+#define FST_CSG_PNAME_NEW_IFNAME "new_ifname" /* pval = ifname */
+#define FST_CSG_PNAME_LLT        "llt"        /* pval = numeric llt value */
+#define FST_CSG_PNAME_STATE      "state"      /* pval = FST_CS_PVAL_STATE_... */
+#define FST_CMD_SESSION_SET      "session_set"
+#define FST_CSS_PNAME_OLD_PEER_ADDR  FST_CSG_PNAME_OLD_PEER_ADDR
+#define FST_CSS_PNAME_NEW_PEER_ADDR  FST_CSG_PNAME_NEW_PEER_ADDR
+#define FST_CSS_PNAME_OLD_IFNAME     FST_CSG_PNAME_OLD_IFNAME
+#define FST_CSS_PNAME_NEW_IFNAME     FST_CSG_PNAME_NEW_IFNAME
+#define FST_CSS_PNAME_LLT            FST_CSG_PNAME_LLT
+#define FST_CMD_SESSION_INITIATE "session_initiate"
+#define FST_CMD_SESSION_RESPOND  "session_respond"
+#define FST_CMD_SESSION_TRANSFER "session_transfer"
+#define FST_CMD_SESSION_TEARDOWN "session_teardown"
+
+#ifdef CONFIG_FST_TEST
+#define FST_CTR_PVAL_BAD_NEW_BAND        "bad_new_band"
+
+#define FST_CMD_TEST_REQUEST    "test_request"
+#define FST_CTR_IS_SUPPORTED        "is_supported"
+#define FST_CTR_SEND_SETUP_REQUEST  "send_setup_request"
+#define FST_CTR_SEND_SETUP_RESPONSE "send_setup_response"
+#define FST_CTR_SEND_ACK_REQUEST    "send_ack_request"
+#define FST_CTR_SEND_ACK_RESPONSE   "send_ack_response"
+#define FST_CTR_SEND_TEAR_DOWN      "send_tear_down"
+#define FST_CTR_GET_FSTS_ID         "get_fsts_id"
+#define FST_CTR_GET_LOCAL_MBIES     "get_local_mbies"
+#endif /* CONFIG_FST_TEST */
+
+/* Events */
+#define FST_CTRL_EVENT_IFACE "FST-EVENT-IFACE"
+#define FST_CEI_PNAME_IFNAME       "ifname"
+#define FST_CEI_PNAME_GROUP        "group"
+#define FST_CEI_PNAME_ATTACHED     "attached"
+#define FST_CEI_PNAME_DETACHED     "detached"
+#define FST_CTRL_EVENT_PEER "FST-EVENT-PEER"
+#define FST_CEP_PNAME_IFNAME       "ifname"
+#define FST_CEP_PNAME_ADDR         "peer_addr"
+#define FST_CEP_PNAME_CONNECTED    "connected"
+#define FST_CEP_PNAME_DISCONNECTED "disconnected"
+#define FST_CTRL_EVENT_SESSION "FST-EVENT-SESSION"
+#define FST_CES_PNAME_SESSION_ID "session_id"
+#define FST_CES_PNAME_EVT_TYPE   "event_type"
+#define FST_PVAL_EVT_TYPE_SESSION_STATE "EVENT_FST_SESSION_STATE"
+/* old_state/new_state: pval = FST_CS_PVAL_STATE_... */
+#define FST_CES_PNAME_OLD_STATE   "old_state"
+#define FST_CES_PNAME_NEW_STATE   "new_state"
+#define FST_CES_PNAME_REASON      "reason" /* pval = FST_CS_PVAL_REASON_... */
+#define FST_CES_PNAME_REJECT_CODE "reject_code" /* pval = u8 code */
+/* pval = FST_CS_PVAL_INITIATOR_... */
+#define FST_CES_PNAME_INITIATOR   "initiator"
+#define FST_PVAL_EVT_TYPE_ESTABLISHED "EVENT_FST_ESTABLISHED"
+#define FST_PVAL_EVT_TYPE_SETUP "EVENT_FST_SETUP"
+
+#endif /* FST_CTRL_DEFS_H */
diff --git a/src/fst/fst_ctrl_iface.c b/src/fst/fst_ctrl_iface.c
new file mode 100644
index 0000000..98ece9f
--- /dev/null
+++ b/src/fst/fst_ctrl_iface.c
@@ -0,0 +1,948 @@
+/*
+ * FST module - Control Interface implementation
+ * Copyright (c) 2014, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+#include "utils/common.h"
+#include "common/defs.h"
+#include "list.h"
+#include "fst/fst.h"
+#include "fst/fst_internal.h"
+#include "fst_ctrl_defs.h"
+#include "fst_ctrl_iface.h"
+
+
+static struct fst_group * get_fst_group_by_id(const char *id)
+{
+	struct fst_group *g;
+
+	foreach_fst_group(g) {
+		const char *group_id = fst_group_get_id(g);
+
+		if (os_strncmp(group_id, id, os_strlen(group_id)) == 0)
+			return g;
+	}
+
+	return NULL;
+}
+
+
+/* notifications */
+static Boolean format_session_state_extra(const union fst_event_extra *extra,
+					  char *buffer, size_t size)
+{
+	int len;
+	char reject_str[32] = FST_CTRL_PVAL_NONE;
+	const char *initiator = FST_CTRL_PVAL_NONE;
+	const struct fst_event_extra_session_state *ss;
+
+	ss = &extra->session_state;
+	if (ss->new_state != FST_SESSION_STATE_INITIAL)
+		return TRUE;
+
+	switch (ss->extra.to_initial.reason) {
+	case REASON_REJECT:
+		if (ss->extra.to_initial.reject_code != WLAN_STATUS_SUCCESS)
+			os_snprintf(reject_str, sizeof(reject_str), "%u",
+				    ss->extra.to_initial.reject_code);
+		/* no break */
+	case REASON_TEARDOWN:
+	case REASON_SWITCH:
+		switch (ss->extra.to_initial.initiator) {
+		case FST_INITIATOR_LOCAL:
+			initiator = FST_CS_PVAL_INITIATOR_LOCAL;
+			break;
+		case FST_INITIATOR_REMOTE:
+			initiator = FST_CS_PVAL_INITIATOR_REMOTE;
+			break;
+		default:
+			break;
+		}
+		break;
+	default:
+		break;
+	}
+
+	len = os_snprintf(buffer, size,
+			  FST_CES_PNAME_REASON "=%s "
+			  FST_CES_PNAME_REJECT_CODE "=%s "
+			  FST_CES_PNAME_INITIATOR "=%s",
+			  fst_reason_name(ss->extra.to_initial.reason),
+			  reject_str, initiator);
+
+	return !os_snprintf_error(size, len);
+}
+
+
+static void fst_ctrl_iface_notify(struct fst_iface *f, u32 session_id,
+				  enum fst_event_type event_type,
+				  const union fst_event_extra *extra)
+{
+	struct fst_group *g;
+	char extra_str[128] = "";
+	const struct fst_event_extra_session_state *ss;
+	const struct fst_event_extra_iface_state *is;
+	const struct fst_event_extra_peer_state *ps;
+
+	/*
+	 * FST can use any of interface objects as it only sends messages
+	 * on global Control Interface, so we just pick the 1st one.
+	 */
+
+	if (!f) {
+		foreach_fst_group(g) {
+			f = fst_group_first_iface(g);
+			if (f)
+				break;
+		}
+		if (!f)
+			return;
+	}
+
+	WPA_ASSERT(f->iface_obj.ctx);
+
+	switch (event_type) {
+	case EVENT_FST_IFACE_STATE_CHANGED:
+		if (!extra)
+			return;
+		is = &extra->iface_state;
+		wpa_msg_global_only(f->iface_obj.ctx, MSG_INFO,
+				    FST_CTRL_EVENT_IFACE " %s "
+				    FST_CEI_PNAME_IFNAME "=%s "
+				    FST_CEI_PNAME_GROUP "=%s",
+				    is->attached ? FST_CEI_PNAME_ATTACHED :
+				    FST_CEI_PNAME_DETACHED,
+				    is->ifname, is->group_id);
+		break;
+	case EVENT_PEER_STATE_CHANGED:
+		if (!extra)
+			return;
+		ps = &extra->peer_state;
+		wpa_msg_global_only(fst_iface_get_wpa_obj_ctx(f), MSG_INFO,
+				    FST_CTRL_EVENT_PEER " %s "
+				    FST_CEP_PNAME_IFNAME "=%s "
+				    FST_CEP_PNAME_ADDR "=" MACSTR,
+				    ps->connected ? FST_CEP_PNAME_CONNECTED :
+				    FST_CEP_PNAME_DISCONNECTED,
+				    ps->ifname, MAC2STR(ps->addr));
+		break;
+	case EVENT_FST_SESSION_STATE_CHANGED:
+		if (!extra)
+			return;
+		if (!format_session_state_extra(extra, extra_str,
+						sizeof(extra_str))) {
+			fst_printf(MSG_ERROR,
+				   "CTRL: Cannot format STATE_CHANGE extra");
+			extra_str[0] = 0;
+		}
+		ss = &extra->session_state;
+		wpa_msg_global_only(fst_iface_get_wpa_obj_ctx(f), MSG_INFO,
+				    FST_CTRL_EVENT_SESSION " "
+				    FST_CES_PNAME_SESSION_ID "=%u "
+				    FST_CES_PNAME_EVT_TYPE "=%s "
+				    FST_CES_PNAME_OLD_STATE "=%s "
+				    FST_CES_PNAME_NEW_STATE "=%s %s",
+				    session_id,
+				    fst_session_event_type_name(event_type),
+				    fst_session_state_name(ss->old_state),
+				    fst_session_state_name(ss->new_state),
+				    extra_str);
+		break;
+	case EVENT_FST_ESTABLISHED:
+	case EVENT_FST_SETUP:
+		wpa_msg_global_only(fst_iface_get_wpa_obj_ctx(f), MSG_INFO,
+				    FST_CTRL_EVENT_SESSION " "
+				    FST_CES_PNAME_SESSION_ID "=%u "
+				    FST_CES_PNAME_EVT_TYPE "=%s",
+				    session_id,
+				    fst_session_event_type_name(event_type));
+		break;
+	}
+}
+
+
+/* command processors */
+
+/* fst session_get */
+static int session_get(const char *session_id, char *buf, size_t buflen)
+{
+	struct fst_session *s;
+	struct fst_iface *new_iface, *old_iface;
+	const u8 *old_peer_addr, *new_peer_addr;
+	u32 id;
+
+	id = strtoul(session_id, NULL, 0);
+
+	s = fst_session_get_by_id(id);
+	if (!s) {
+		fst_printf(MSG_WARNING, "CTRL: Cannot find session %u", id);
+		return os_snprintf(buf, buflen, "FAIL\n");
+	}
+
+	old_peer_addr = fst_session_get_peer_addr(s, TRUE);
+	new_peer_addr = fst_session_get_peer_addr(s, FALSE);
+	new_iface = fst_session_get_iface(s, FALSE);
+	old_iface = fst_session_get_iface(s, TRUE);
+
+	return os_snprintf(buf, buflen,
+			   FST_CSG_PNAME_OLD_PEER_ADDR "=" MACSTR "\n"
+			   FST_CSG_PNAME_NEW_PEER_ADDR "=" MACSTR "\n"
+			   FST_CSG_PNAME_NEW_IFNAME "=%s\n"
+			   FST_CSG_PNAME_OLD_IFNAME "=%s\n"
+			   FST_CSG_PNAME_LLT "=%u\n"
+			   FST_CSG_PNAME_STATE "=%s\n",
+			   MAC2STR(old_peer_addr),
+			   MAC2STR(new_peer_addr),
+			   new_iface ? fst_iface_get_name(new_iface) :
+			   FST_CTRL_PVAL_NONE,
+			   old_iface ? fst_iface_get_name(old_iface) :
+			   FST_CTRL_PVAL_NONE,
+			   fst_session_get_llt(s),
+			   fst_session_state_name(fst_session_get_state(s)));
+}
+
+
+/* fst session_set */
+static int session_set(const char *session_id, char *buf, size_t buflen)
+{
+	struct fst_session *s;
+	char *p, *q;
+	u32 id;
+	int ret;
+
+	id = strtoul(session_id, &p, 0);
+
+	s = fst_session_get_by_id(id);
+	if (!s) {
+		fst_printf(MSG_WARNING, "CTRL: Cannot find session %u", id);
+		return os_snprintf(buf, buflen, "FAIL\n");
+	}
+
+	if (*p != ' ' || !(q = os_strchr(p + 1, '=')))
+		return os_snprintf(buf, buflen, "FAIL\n");
+	p++;
+
+	if (os_strncasecmp(p, FST_CSS_PNAME_OLD_IFNAME, q - p) == 0) {
+		ret = fst_session_set_str_ifname(s, q + 1, TRUE);
+	} else if (os_strncasecmp(p, FST_CSS_PNAME_NEW_IFNAME, q - p) == 0) {
+		ret = fst_session_set_str_ifname(s, q + 1, FALSE);
+	} else if (os_strncasecmp(p, FST_CSS_PNAME_OLD_PEER_ADDR, q - p) == 0) {
+		ret = fst_session_set_str_peer_addr(s, q + 1, TRUE);
+	} else if (os_strncasecmp(p, FST_CSS_PNAME_NEW_PEER_ADDR, q - p) == 0) {
+		ret = fst_session_set_str_peer_addr(s, q + 1, FALSE);
+	} else if (os_strncasecmp(p, FST_CSS_PNAME_LLT, q - p) == 0) {
+		ret = fst_session_set_str_llt(s, q + 1);
+	} else {
+		fst_printf(MSG_ERROR, "CTRL: Unknown parameter: %s", p);
+		return os_snprintf(buf, buflen, "FAIL\n");
+	}
+
+	return os_snprintf(buf, buflen, "%s\n", ret ? "FAIL" : "OK");
+}
+
+
+/* fst session_add/remove */
+static int session_add(const char *group_id, char *buf, size_t buflen)
+{
+	struct fst_group *g;
+	struct fst_session *s;
+
+	g = get_fst_group_by_id(group_id);
+	if (!g) {
+		fst_printf(MSG_WARNING, "CTRL: Cannot find group '%s'",
+			   group_id);
+		return os_snprintf(buf, buflen, "FAIL\n");
+	}
+
+	s = fst_session_create(g);
+	if (!s) {
+		fst_printf(MSG_ERROR,
+			   "CTRL: Cannot create session for group '%s'",
+			   group_id);
+		return os_snprintf(buf, buflen, "FAIL\n");
+	}
+
+	return os_snprintf(buf, buflen, "%u\n", fst_session_get_id(s));
+}
+
+
+static int session_remove(const char *session_id, char *buf, size_t buflen)
+{
+	struct fst_session *s;
+	struct fst_group *g;
+	u32 id;
+
+	id = strtoul(session_id, NULL, 0);
+
+	s = fst_session_get_by_id(id);
+	if (!s) {
+		fst_printf(MSG_WARNING, "CTRL: Cannot find session %u", id);
+		return os_snprintf(buf, buflen, "FAIL\n");
+	}
+
+	g = fst_session_get_group(s);
+	fst_session_reset(s);
+	fst_session_delete(s);
+	fst_group_delete_if_empty(g);
+
+	return os_snprintf(buf, buflen, "OK\n");
+}
+
+
+/* fst session_initiate */
+static int session_initiate(const char *session_id, char *buf, size_t buflen)
+{
+	struct fst_session *s;
+	u32 id;
+
+	id = strtoul(session_id, NULL, 0);
+
+	s = fst_session_get_by_id(id);
+	if (!s) {
+		fst_printf(MSG_WARNING, "CTRL: Cannot find session %u", id);
+		return os_snprintf(buf, buflen, "FAIL\n");
+	}
+
+	if (fst_session_initiate_setup(s)) {
+		fst_printf(MSG_WARNING, "CTRL: Cannot initiate session %u", id);
+		return os_snprintf(buf, buflen, "FAIL\n");
+	}
+
+	return os_snprintf(buf, buflen, "OK\n");
+}
+
+
+/* fst session_respond */
+static int session_respond(const char *session_id, char *buf, size_t buflen)
+{
+	struct fst_session *s;
+	char *p;
+	u32 id;
+	u8 status_code;
+
+	id = strtoul(session_id, &p, 0);
+
+	s = fst_session_get_by_id(id);
+	if (!s) {
+		fst_printf(MSG_WARNING, "CTRL: Cannot find session %u", id);
+		return os_snprintf(buf, buflen, "FAIL\n");
+	}
+
+	if (*p != ' ')
+		return os_snprintf(buf, buflen, "FAIL\n");
+	p++;
+
+	if (!os_strcasecmp(p, FST_CS_PVAL_RESPONSE_ACCEPT)) {
+		status_code = WLAN_STATUS_SUCCESS;
+	} else if (!os_strcasecmp(p, FST_CS_PVAL_RESPONSE_REJECT)) {
+		status_code = WLAN_STATUS_PENDING_ADMITTING_FST_SESSION;
+	} else {
+		fst_printf(MSG_WARNING,
+			   "CTRL: session %u: unknown response status: %s",
+			   id, p);
+		return os_snprintf(buf, buflen, "FAIL\n");
+	}
+
+	if (fst_session_respond(s, status_code)) {
+		fst_printf(MSG_WARNING, "CTRL: Cannot respond to session %u",
+			   id);
+		return os_snprintf(buf, buflen, "FAIL\n");
+	}
+
+	fst_printf(MSG_INFO, "CTRL: session %u responded", id);
+
+	return os_snprintf(buf, buflen, "OK\n");
+}
+
+
+/* fst session_transfer */
+static int session_transfer(const char *session_id, char *buf, size_t buflen)
+{
+	struct fst_session *s;
+	u32 id;
+
+	id = strtoul(session_id, NULL, 0);
+
+	s = fst_session_get_by_id(id);
+	if (!s) {
+		fst_printf(MSG_WARNING, "CTRL: Cannot find session %u", id);
+		return os_snprintf(buf, buflen, "FAIL\n");
+	}
+
+	if (fst_session_initiate_switch(s)) {
+		fst_printf(MSG_WARNING,
+			   "CTRL: Cannot initiate ST for session %u", id);
+		return os_snprintf(buf, buflen, "FAIL\n");
+	}
+
+	return os_snprintf(buf, buflen, "OK\n");
+}
+
+
+/* fst session_teardown */
+static int session_teardown(const char *session_id, char *buf, size_t buflen)
+{
+	struct fst_session *s;
+	u32 id;
+
+	id = strtoul(session_id, NULL, 0);
+
+	s = fst_session_get_by_id(id);
+	if (!s) {
+		fst_printf(MSG_WARNING, "CTRL: Cannot find session %u", id);
+		return os_snprintf(buf, buflen, "FAIL\n");
+	}
+
+	if (fst_session_tear_down_setup(s)) {
+		fst_printf(MSG_WARNING, "CTRL: Cannot tear down session %u",
+			   id);
+		return os_snprintf(buf, buflen, "FAIL\n");
+	}
+
+	return os_snprintf(buf, buflen, "OK\n");
+}
+
+
+#ifdef CONFIG_FST_TEST
+/* fst test_request */
+static int test_request(const char *request, char *buf, size_t buflen)
+{
+	const char *p = request;
+	int ret;
+
+	if (!os_strncasecmp(p, FST_CTR_SEND_SETUP_REQUEST,
+			    os_strlen(FST_CTR_SEND_SETUP_REQUEST))) {
+		ret = fst_test_req_send_fst_request(
+			p + os_strlen(FST_CTR_SEND_SETUP_REQUEST));
+	} else if (!os_strncasecmp(p, FST_CTR_SEND_SETUP_RESPONSE,
+				   os_strlen(FST_CTR_SEND_SETUP_RESPONSE))) {
+		ret = fst_test_req_send_fst_response(
+			p + os_strlen(FST_CTR_SEND_SETUP_RESPONSE));
+	} else if (!os_strncasecmp(p, FST_CTR_SEND_ACK_REQUEST,
+				   os_strlen(FST_CTR_SEND_ACK_REQUEST))) {
+		ret = fst_test_req_send_ack_request(
+			p + os_strlen(FST_CTR_SEND_ACK_REQUEST));
+	} else if (!os_strncasecmp(p, FST_CTR_SEND_ACK_RESPONSE,
+				   os_strlen(FST_CTR_SEND_ACK_RESPONSE))) {
+		ret = fst_test_req_send_ack_response(
+			p + os_strlen(FST_CTR_SEND_ACK_RESPONSE));
+	} else if (!os_strncasecmp(p, FST_CTR_SEND_TEAR_DOWN,
+				   os_strlen(FST_CTR_SEND_TEAR_DOWN))) {
+		ret = fst_test_req_send_tear_down(
+			p + os_strlen(FST_CTR_SEND_TEAR_DOWN));
+	} else if (!os_strncasecmp(p, FST_CTR_GET_FSTS_ID,
+				   os_strlen(FST_CTR_GET_FSTS_ID))) {
+		u32 fsts_id = fst_test_req_get_fsts_id(
+			p + os_strlen(FST_CTR_GET_FSTS_ID));
+		if (fsts_id != FST_FSTS_ID_NOT_FOUND)
+			return os_snprintf(buf, buflen, "%u\n", fsts_id);
+		return os_snprintf(buf, buflen, "FAIL\n");
+	} else if (!os_strncasecmp(p, FST_CTR_GET_LOCAL_MBIES,
+				   os_strlen(FST_CTR_GET_LOCAL_MBIES))) {
+		return fst_test_req_get_local_mbies(
+			p + os_strlen(FST_CTR_GET_LOCAL_MBIES), buf, buflen);
+	} else if (!os_strncasecmp(p, FST_CTR_IS_SUPPORTED,
+				   os_strlen(FST_CTR_IS_SUPPORTED))) {
+		ret = 0;
+	} else {
+		fst_printf(MSG_ERROR, "CTRL: Unknown parameter: %s", p);
+		return os_snprintf(buf, buflen, "FAIL\n");
+	}
+
+	return os_snprintf(buf, buflen, "%s\n", ret ? "FAIL" : "OK");
+}
+#endif /* CONFIG_FST_TEST */
+
+
+/* fst list_sessions */
+struct list_sessions_cb_ctx {
+	char *buf;
+	size_t buflen;
+	size_t reply_len;
+};
+
+
+static void list_session_enum_cb(struct fst_group *g, struct fst_session *s,
+				 void *ctx)
+{
+	struct list_sessions_cb_ctx *c = ctx;
+	int ret;
+
+	ret = os_snprintf(c->buf, c->buflen, " %u", fst_session_get_id(s));
+
+	c->buf += ret;
+	c->buflen -= ret;
+	c->reply_len += ret;
+}
+
+
+static int list_sessions(const char *group_id, char *buf, size_t buflen)
+{
+	struct list_sessions_cb_ctx ctx;
+	struct fst_group *g;
+
+	g = get_fst_group_by_id(group_id);
+	if (!g) {
+		fst_printf(MSG_WARNING, "CTRL: Cannot find group '%s'",
+			   group_id);
+		return os_snprintf(buf, buflen, "FAIL\n");
+	}
+
+	ctx.buf = buf;
+	ctx.buflen = buflen;
+	ctx.reply_len = 0;
+
+	fst_session_enum(g, list_session_enum_cb, &ctx);
+
+	ctx.reply_len += os_snprintf(buf + ctx.reply_len, ctx.buflen, "\n");
+
+	return ctx.reply_len;
+}
+
+
+/* fst iface_peers */
+static int iface_peers(const char *group_id, char *buf, size_t buflen)
+{
+	const char *ifname;
+	struct fst_group *g;
+	struct fst_iface *f;
+	struct fst_get_peer_ctx *ctx;
+	const u8 *addr;
+	unsigned found = 0;
+	int ret = 0;
+
+	g = get_fst_group_by_id(group_id);
+	if (!g) {
+		fst_printf(MSG_WARNING, "CTRL: Cannot find group '%s'",
+			   group_id);
+		return os_snprintf(buf, buflen, "FAIL\n");
+	}
+
+	ifname = os_strchr(group_id, ' ');
+	if (!ifname)
+		return os_snprintf(buf, buflen, "FAIL\n");
+	ifname++;
+
+	foreach_fst_group_iface(g, f) {
+		const char *in = fst_iface_get_name(f);
+
+		if (os_strncmp(ifname, in, os_strlen(in)) == 0) {
+			found = 1;
+			break;
+		}
+	}
+
+	if (!found)
+		return os_snprintf(buf, buflen, "FAIL\n");
+
+	addr = fst_iface_get_peer_first(f, &ctx, FALSE);
+	for (; addr != NULL; addr = fst_iface_get_peer_next(f, &ctx, FALSE)) {
+		int res;
+
+		res = os_snprintf(buf + ret, buflen - ret, MACSTR "\n",
+				  MAC2STR(addr));
+		if (os_snprintf_error(buflen - ret, res))
+			break;
+		ret += res;
+	}
+
+	return ret;
+}
+
+
+static int get_peer_mbies(const char *params, char *buf, size_t buflen)
+{
+	char *endp;
+	char ifname[FST_MAX_INTERFACE_SIZE];
+	u8 peer_addr[ETH_ALEN];
+	struct fst_group *g;
+	struct fst_iface *iface = NULL;
+	const struct wpabuf *mbies;
+
+	if (fst_read_next_text_param(params, ifname, sizeof(ifname), &endp) ||
+	    !*ifname)
+		goto problem;
+
+	while (isspace(*endp))
+		endp++;
+	if (fst_read_peer_addr(endp, peer_addr))
+		goto problem;
+
+	foreach_fst_group(g) {
+		iface = fst_group_get_iface_by_name(g, ifname);
+		if (iface)
+			break;
+	}
+	if (!iface)
+		goto problem;
+
+	mbies = fst_iface_get_peer_mb_ie(iface, peer_addr);
+	if (!mbies)
+		goto problem;
+
+	return wpa_snprintf_hex(buf, buflen, wpabuf_head(mbies),
+				wpabuf_len(mbies));
+
+problem:
+	return os_snprintf(buf, buflen, "FAIL\n");
+}
+
+
+/* fst list_ifaces */
+static int list_ifaces(const char *group_id, char *buf, size_t buflen)
+{
+	struct fst_group *g;
+	struct fst_iface *f;
+	int ret = 0;
+
+	g = get_fst_group_by_id(group_id);
+	if (!g) {
+		fst_printf(MSG_WARNING, "CTRL: Cannot find group '%s'",
+			   group_id);
+		return os_snprintf(buf, buflen, "FAIL\n");
+	}
+
+	foreach_fst_group_iface(g, f) {
+		int res;
+		const u8 *iface_addr = fst_iface_get_addr(f);
+
+		res = os_snprintf(buf + ret, buflen - ret,
+				  "%s|" MACSTR "|%u|%u\n",
+				  fst_iface_get_name(f),
+				  MAC2STR(iface_addr),
+				  fst_iface_get_priority(f),
+				  fst_iface_get_llt(f));
+		if (os_snprintf_error(buflen - ret, res))
+			break;
+		ret += res;
+	}
+
+	return ret;
+}
+
+
+/* fst list_groups */
+static int list_groups(const char *cmd, char *buf, size_t buflen)
+{
+	struct fst_group *g;
+	int ret = 0;
+
+	foreach_fst_group(g) {
+		int res;
+
+		res = os_snprintf(buf + ret, buflen - ret, "%s\n",
+				  fst_group_get_id(g));
+		if (os_snprintf_error(buflen - ret, res))
+			break;
+		ret += res;
+	}
+
+	return ret;
+}
+
+
+static const char * band_freq(enum mb_band_id band)
+{
+	static const char *band_names[] = {
+		[MB_BAND_ID_WIFI_2_4GHZ] "2.4GHZ",
+		[MB_BAND_ID_WIFI_5GHZ] "5GHZ",
+		[MB_BAND_ID_WIFI_60GHZ] "60GHZ",
+	};
+
+	return fst_get_str_name(band, band_names, ARRAY_SIZE(band_names));
+}
+
+
+static int print_band(unsigned num, struct fst_iface *iface, const u8 *addr,
+		      char *buf, size_t buflen)
+{
+	const struct wpabuf *wpabuf;
+	enum hostapd_hw_mode hw_mode;
+	u8 channel;
+	int ret = 0;
+
+	fst_iface_get_channel_info(iface, &hw_mode, &channel);
+
+	ret += os_snprintf(buf + ret, buflen - ret, "band%u_frequency=%s\n",
+			   num, band_freq(fst_hw_mode_to_band(hw_mode)));
+	ret += os_snprintf(buf + ret, buflen - ret, "band%u_iface=%s\n",
+			   num, fst_iface_get_name(iface));
+	wpabuf = fst_iface_get_peer_mb_ie(iface, addr);
+	if (wpabuf) {
+		ret += os_snprintf(buf + ret, buflen - ret, "band%u_mb_ies=",
+				   num);
+		ret += wpa_snprintf_hex(buf + ret, buflen - ret,
+					wpabuf_head(wpabuf),
+					wpabuf_len(wpabuf));
+		ret += os_snprintf(buf + ret, buflen - ret, "\n");
+	}
+	ret += os_snprintf(buf + ret, buflen - ret, "band%u_fst_group_id=%s\n",
+			   num, fst_iface_get_group_id(iface));
+	ret += os_snprintf(buf + ret, buflen - ret, "band%u_fst_priority=%u\n",
+			   num, fst_iface_get_priority(iface));
+	ret += os_snprintf(buf + ret, buflen - ret, "band%u_fst_llt=%u\n",
+			   num, fst_iface_get_llt(iface));
+
+	return ret;
+}
+
+
+static void fst_ctrl_iface_on_iface_state_changed(struct fst_iface *i,
+						  Boolean attached)
+{
+	union fst_event_extra extra;
+
+	os_memset(&extra, 0, sizeof(extra));
+	extra.iface_state.attached = attached;
+	os_strlcpy(extra.iface_state.ifname, fst_iface_get_name(i),
+		   sizeof(extra.iface_state.ifname));
+	os_strlcpy(extra.iface_state.group_id, fst_iface_get_group_id(i),
+		   sizeof(extra.iface_state.group_id));
+
+	fst_ctrl_iface_notify(i, FST_INVALID_SESSION_ID,
+			      EVENT_FST_IFACE_STATE_CHANGED, &extra);
+}
+
+
+static int fst_ctrl_iface_on_iface_added(struct fst_iface *i)
+{
+	fst_ctrl_iface_on_iface_state_changed(i, TRUE);
+	return 0;
+}
+
+
+static void fst_ctrl_iface_on_iface_removed(struct fst_iface *i)
+{
+	fst_ctrl_iface_on_iface_state_changed(i, FALSE);
+}
+
+
+static void fst_ctrl_iface_on_event(enum fst_event_type event_type,
+				    struct fst_iface *i, struct fst_session *s,
+				    const union fst_event_extra *extra)
+{
+	u32 session_id = s ? fst_session_get_id(s) : FST_INVALID_SESSION_ID;
+
+	fst_ctrl_iface_notify(i, session_id, event_type, extra);
+}
+
+
+static const struct fst_ctrl ctrl_cli = {
+	.on_iface_added = fst_ctrl_iface_on_iface_added,
+	.on_iface_removed =  fst_ctrl_iface_on_iface_removed,
+	.on_event = fst_ctrl_iface_on_event,
+};
+
+const struct fst_ctrl *fst_ctrl_cli = &ctrl_cli;
+
+
+int fst_ctrl_iface_mb_info(const u8 *addr, char *buf, size_t buflen)
+{
+	struct fst_group *g;
+	struct fst_iface *f;
+	unsigned num = 0;
+	int ret = 0;
+
+	foreach_fst_group(g) {
+		foreach_fst_group_iface(g, f) {
+			if (fst_iface_is_connected(f, addr, TRUE)) {
+				ret += print_band(num++, f, addr,
+						  buf + ret, buflen - ret);
+			}
+		}
+	}
+
+	return ret;
+}
+
+
+/* fst ctrl processor */
+int fst_ctrl_iface_receive(const char *cmd, char *reply, size_t reply_size)
+{
+	static const struct fst_command {
+		const char *name;
+		unsigned has_param;
+		int (*process)(const char *group_id, char *buf, size_t buflen);
+	} commands[] = {
+		{ FST_CMD_LIST_GROUPS, 0, list_groups},
+		{ FST_CMD_LIST_IFACES, 1, list_ifaces},
+		{ FST_CMD_IFACE_PEERS, 1, iface_peers},
+		{ FST_CMD_GET_PEER_MBIES, 1, get_peer_mbies},
+		{ FST_CMD_LIST_SESSIONS, 1, list_sessions},
+		{ FST_CMD_SESSION_ADD, 1, session_add},
+		{ FST_CMD_SESSION_REMOVE, 1, session_remove},
+		{ FST_CMD_SESSION_GET, 1, session_get},
+		{ FST_CMD_SESSION_SET, 1, session_set},
+		{ FST_CMD_SESSION_INITIATE, 1, session_initiate},
+		{ FST_CMD_SESSION_RESPOND, 1, session_respond},
+		{ FST_CMD_SESSION_TRANSFER, 1, session_transfer},
+		{ FST_CMD_SESSION_TEARDOWN, 1, session_teardown},
+#ifdef CONFIG_FST_TEST
+		{ FST_CMD_TEST_REQUEST, 1, test_request },
+#endif /* CONFIG_FST_TEST */
+		{ NULL, 0, NULL }
+	};
+	const struct fst_command *c;
+	const char *p;
+	const char *temp;
+	Boolean non_spaces_found;
+
+	for (c = commands; c->name; c++) {
+		if (os_strncasecmp(cmd, c->name, os_strlen(c->name)) != 0)
+			continue;
+		p = cmd + os_strlen(c->name);
+		if (c->has_param) {
+			if (!isspace(p[0]))
+				return os_snprintf(reply, reply_size, "FAIL\n");
+			p++;
+			temp = p;
+			non_spaces_found = FALSE;
+			while (*temp) {
+				if (!isspace(*temp)) {
+					non_spaces_found = TRUE;
+					break;
+				}
+				temp++;
+			}
+			if (!non_spaces_found)
+				return os_snprintf(reply, reply_size, "FAIL\n");
+		}
+		return c->process(p, reply, reply_size);
+	}
+
+	return os_snprintf(reply, reply_size, "UNKNOWN FST COMMAND\n");
+}
+
+
+int fst_read_next_int_param(const char *params, Boolean *valid, char **endp)
+{
+	int ret = -1;
+	const char *curp;
+
+	*valid = FALSE;
+	*endp = (char *) params;
+	curp = params;
+	if (*curp) {
+		ret = (int) strtol(curp, endp, 0);
+		if (!**endp || isspace(**endp))
+			*valid = TRUE;
+	}
+
+	return ret;
+}
+
+
+int fst_read_next_text_param(const char *params, char *buf, size_t buflen,
+			     char **endp)
+{
+	size_t max_chars_to_copy;
+	char *cur_dest;
+
+	*endp = (char *) params;
+	while (isspace(**endp))
+		(*endp)++;
+	if (!**endp || buflen <= 1)
+		return -EINVAL;
+
+	max_chars_to_copy = buflen - 1;
+	/* We need 1 byte for the terminating zero */
+	cur_dest = buf;
+	while (**endp && !isspace(**endp) && max_chars_to_copy > 0) {
+		*cur_dest = **endp;
+		(*endp)++;
+		cur_dest++;
+		max_chars_to_copy--;
+	}
+	*cur_dest = 0;
+
+	return 0;
+}
+
+
+int fst_read_peer_addr(const char *mac, u8 *peer_addr)
+{
+	if (hwaddr_aton(mac, peer_addr)) {
+		fst_printf(MSG_WARNING, "Bad peer_mac %s: invalid addr string",
+			   mac);
+		return -1;
+	}
+
+	if (is_zero_ether_addr(peer_addr) ||
+	    is_multicast_ether_addr(peer_addr)) {
+		fst_printf(MSG_WARNING, "Bad peer_mac %s: not a unicast addr",
+			   mac);
+		return -1;
+	}
+
+	return 0;
+}
+
+
+int fst_parse_attach_command(const char *cmd, char *ifname, size_t ifname_size,
+			     struct fst_iface_cfg *cfg)
+{
+	char *pos;
+	char *endp;
+	Boolean is_valid;
+	int val;
+
+	if (fst_read_next_text_param(cmd, ifname, ifname_size, &endp) ||
+	    fst_read_next_text_param(endp, cfg->group_id, sizeof(cfg->group_id),
+				     &endp))
+		return -EINVAL;
+
+	cfg->llt = FST_DEFAULT_LLT_CFG_VALUE;
+	cfg->priority = 0;
+	pos = os_strstr(endp, FST_ATTACH_CMD_PNAME_LLT);
+	if (pos) {
+		pos += os_strlen(FST_ATTACH_CMD_PNAME_LLT);
+		if (*pos == '=') {
+			val = fst_read_next_int_param(pos + 1, &is_valid,
+						      &endp);
+			if (is_valid)
+				cfg->llt = val;
+		}
+	}
+	pos = os_strstr(endp, FST_ATTACH_CMD_PNAME_PRIORITY);
+	if (pos) {
+		pos += os_strlen(FST_ATTACH_CMD_PNAME_PRIORITY);
+		if (*pos == '=') {
+			val = fst_read_next_int_param(pos + 1, &is_valid,
+						      &endp);
+			if (is_valid)
+				cfg->priority = (u8) val;
+		}
+	}
+
+	return 0;
+}
+
+
+int fst_parse_detach_command(const char *cmd, char *ifname, size_t ifname_size)
+{
+	char *endp;
+
+	return fst_read_next_text_param(cmd, ifname, ifname_size, &endp);
+}
+
+
+int fst_iface_detach(const char *ifname)
+{
+	struct fst_group *g;
+
+	foreach_fst_group(g) {
+		struct fst_iface *f;
+
+		f = fst_group_get_iface_by_name(g, ifname);
+		if (f) {
+			fst_detach(f);
+			return 0;
+		}
+	}
+
+	return -EINVAL;
+}
diff --git a/src/fst/fst_ctrl_iface.h b/src/fst/fst_ctrl_iface.h
new file mode 100644
index 0000000..4d0cd9f
--- /dev/null
+++ b/src/fst/fst_ctrl_iface.h
@@ -0,0 +1,45 @@
+/*
+ * FST module - internal Control interface definitions
+ * Copyright (c) 2014, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef FST_CTRL_IFACE_H
+#define FST_CTRL_IFACE_H
+
+#include "fst/fst_ctrl_aux.h"
+
+#ifdef CONFIG_FST
+
+/* receiver */
+int fst_ctrl_iface_mb_info(const u8 *addr, char *buf, size_t buflen);
+
+int fst_ctrl_iface_receive(const char *txtaddr, char *buf, size_t buflen);
+
+extern const struct fst_ctrl *fst_ctrl_cli;
+
+#else /* CONFIG_FST */
+
+static inline int
+fst_ctrl_iface_mb_info(const u8 *addr, char *buf, size_t buflen)
+{
+	return 0;
+}
+
+#endif /* CONFIG_FST */
+
+int fst_read_next_int_param(const char *params, Boolean *valid, char **endp);
+int fst_read_next_text_param(const char *params, char *buf, size_t buflen,
+			     char **endp);
+int fst_read_peer_addr(const char *mac, u8 *peer_addr);
+
+struct fst_iface_cfg;
+
+int fst_parse_attach_command(const char *cmd, char *ifname, size_t ifname_size,
+			     struct fst_iface_cfg *cfg);
+int fst_parse_detach_command(const char *cmd, char *ifname, size_t ifname_size);
+int fst_iface_detach(const char *ifname);
+
+#endif /* CTRL_IFACE_FST_H */
diff --git a/src/fst/fst_defs.h b/src/fst/fst_defs.h
new file mode 100644
index 0000000..8ddcc61
--- /dev/null
+++ b/src/fst/fst_defs.h
@@ -0,0 +1,87 @@
+/*
+ * FST module - FST related definitions
+ * Copyright (c) 2014, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef IEEE_80211_FST_DEFS_H
+#define IEEE_80211_FST_DEFS_H
+
+/* IEEE Std 802.11ad */
+
+#define MB_STA_CHANNEL_ALL 0
+
+enum session_type {
+	SESSION_TYPE_BSS = 0, /*  Infrastructure BSS */
+	SESSION_TYPE_IBSS = 1,
+	SESSION_TYPE_DLS = 2,
+	SESSION_TYPE_TDLS = 3,
+	SESSION_TYPE_PBSS = 4
+};
+
+#define SESSION_CONTROL(session_type, switch_intent) \
+	(((u8) ((session_type) & 0x7)) | ((switch_intent) ? 0x10 : 0x00))
+
+#define GET_SESSION_CONTROL_TYPE(session_control) \
+	((u8) ((session_control) & 0x7))
+
+#define GET_SESSION_CONTROL_SWITCH_INTENT(session_control) \
+	(((session_control) & 0x10) >> 4)
+
+/* 8.4.2.147  Session Transition element */
+struct session_transition_ie {
+	u8 element_id;
+	u8 length;
+	u32 fsts_id;
+	u8 session_control;
+	u8 new_band_id;
+	u8 new_band_setup;
+	u8 new_band_op;
+	u8 old_band_id;
+	u8 old_band_setup;
+	u8 old_band_op;
+} STRUCT_PACKED;
+
+struct fst_setup_req {
+	u8 action;
+	u8 dialog_token;
+	u32 llt;
+	struct session_transition_ie stie;
+	/* Multi-band (optional) */
+	/* Wakeup Schedule (optional) */
+	/* Awake Window (optional) */
+	/* Switching Stream (optional) */
+} STRUCT_PACKED;
+
+struct fst_setup_res {
+	u8 action;
+	u8 dialog_token;
+	u8 status_code;
+	struct session_transition_ie stie;
+	/* Multi-band (optional) */
+	/* Wakeup Schedule (optional) */
+	/* Awake Window (optional) */
+	/* Switching Stream (optional) */
+	/* Timeout Interval (optional) */
+} STRUCT_PACKED;
+
+struct fst_ack_req {
+	u8 action;
+	u8 dialog_token;
+	u32 fsts_id;
+} STRUCT_PACKED;
+
+struct fst_ack_res {
+	u8 action;
+	u8 dialog_token;
+	u32 fsts_id;
+} STRUCT_PACKED;
+
+struct fst_tear_down {
+	u8 action;
+	u32 fsts_id;
+} STRUCT_PACKED;
+
+#endif /* IEEE_80211_FST_DEFS_H */
diff --git a/src/fst/fst_group.c b/src/fst/fst_group.c
new file mode 100644
index 0000000..e0c055f
--- /dev/null
+++ b/src/fst/fst_group.c
@@ -0,0 +1,437 @@
+/*
+ * FST module - FST group object implementation
+ * Copyright (c) 2014, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+#include "utils/common.h"
+#include "common/defs.h"
+#include "common/ieee802_11_defs.h"
+#include "common/ieee802_11_common.h"
+#include "drivers/driver.h"
+#include "fst/fst_internal.h"
+#include "fst/fst_defs.h"
+
+
+struct dl_list fst_global_groups_list;
+
+
+static void fst_dump_mb_ies(const char *group_id, const char *ifname,
+			    struct wpabuf *mbies)
+{
+	const u8 *p = wpabuf_head(mbies);
+	size_t s = wpabuf_len(mbies);
+
+	while (s >= 2) {
+		const struct multi_band_ie *mbie =
+			(const struct multi_band_ie *) p;
+		WPA_ASSERT(mbie->eid == WLAN_EID_MULTI_BAND);
+		WPA_ASSERT(2 + mbie->len >= sizeof(*mbie));
+
+		fst_printf(MSG_WARNING,
+			   "%s: %s: mb_ctrl=%u band_id=%u op_class=%u chan=%u bssid="
+			   MACSTR
+			   " beacon_int=%u tsf_offs=[%u %u %u %u %u %u %u %u] mb_cc=0x%02x tmout=%u",
+			   group_id, ifname,
+			   mbie->mb_ctrl, mbie->band_id, mbie->op_class,
+			   mbie->chan, MAC2STR(mbie->bssid), mbie->beacon_int,
+			   mbie->tsf_offs[0], mbie->tsf_offs[1],
+			   mbie->tsf_offs[2], mbie->tsf_offs[3],
+			   mbie->tsf_offs[4], mbie->tsf_offs[5],
+			   mbie->tsf_offs[6], mbie->tsf_offs[7],
+			   mbie->mb_connection_capability,
+			   mbie->fst_session_tmout);
+
+		p += 2 + mbie->len;
+		s -= 2 + mbie->len;
+	}
+}
+
+
+static void fst_fill_mb_ie(struct wpabuf *buf, const u8 *bssid,
+			   const u8 *own_addr, enum mb_band_id band, u8 channel)
+{
+	struct multi_band_ie *mbie;
+	size_t len = sizeof(*mbie);
+
+	if (own_addr)
+		len += ETH_ALEN;
+
+	mbie = wpabuf_put(buf, len);
+
+	os_memset(mbie, 0, len);
+
+	mbie->eid = WLAN_EID_MULTI_BAND;
+	mbie->len = len - 2;
+#ifdef HOSTAPD
+	mbie->mb_ctrl = MB_STA_ROLE_AP;
+	mbie->mb_connection_capability = MB_CONNECTION_CAPABILITY_AP;
+#else /* HOSTAPD */
+	mbie->mb_ctrl = MB_STA_ROLE_NON_PCP_NON_AP;
+	mbie->mb_connection_capability = 0;
+#endif /* HOSTAPD */
+	if (bssid)
+		os_memcpy(mbie->bssid, bssid, ETH_ALEN);
+	mbie->band_id = band;
+	mbie->op_class = 0;  /* means all */
+	mbie->chan = channel;
+	mbie->fst_session_tmout = FST_DEFAULT_SESSION_TIMEOUT_TU;
+
+	if (own_addr) {
+		mbie->mb_ctrl |= MB_CTRL_STA_MAC_PRESENT;
+		os_memcpy(&mbie[1], own_addr, ETH_ALEN);
+	}
+}
+
+
+static unsigned fst_fill_iface_mb_ies(struct fst_iface *f, struct wpabuf *buf)
+{
+	const  u8 *bssid;
+
+	bssid = fst_iface_get_bssid(f);
+	if (bssid) {
+		enum hostapd_hw_mode hw_mode;
+		u8 channel;
+
+		if (buf) {
+			fst_iface_get_channel_info(f, &hw_mode, &channel);
+			fst_fill_mb_ie(buf, bssid, fst_iface_get_addr(f),
+				       fst_hw_mode_to_band(hw_mode), channel);
+		}
+		return 1;
+	} else {
+		unsigned bands[MB_BAND_ID_WIFI_60GHZ + 1] = {};
+		struct hostapd_hw_modes *modes;
+		enum mb_band_id b;
+		int num_modes = fst_iface_get_hw_modes(f, &modes);
+		int ret = 0;
+
+		while (num_modes--) {
+			b = fst_hw_mode_to_band(modes->mode);
+			modes++;
+			if (b >= ARRAY_SIZE(bands) || bands[b]++)
+				continue;
+			ret++;
+			if (buf)
+				fst_fill_mb_ie(buf, NULL, fst_iface_get_addr(f),
+					       b, MB_STA_CHANNEL_ALL);
+		}
+		return ret;
+	}
+}
+
+
+static struct wpabuf * fst_group_create_mb_ie(struct fst_group *g,
+					      struct fst_iface *i)
+{
+	struct wpabuf *buf;
+	struct fst_iface *f;
+	unsigned int nof_mbies = 0;
+	unsigned int nof_ifaces_added = 0;
+
+	foreach_fst_group_iface(g, f) {
+		if (f == i)
+			continue;
+		nof_mbies += fst_fill_iface_mb_ies(f, NULL);
+	}
+
+	buf = wpabuf_alloc(nof_mbies *
+			   (sizeof(struct multi_band_ie) + ETH_ALEN));
+	if (!buf) {
+		fst_printf_iface(i, MSG_ERROR,
+				 "cannot allocate mem for %u MB IEs",
+				 nof_mbies);
+		return NULL;
+	}
+
+	/* The list is sorted in descending order by priorities, so MB IEs will
+	 * be arranged in the same order, as required by spec (see corresponding
+	 * comment in.fst_attach().
+	 */
+	foreach_fst_group_iface(g, f) {
+		if (f == i)
+			continue;
+
+		fst_fill_iface_mb_ies(f, buf);
+		++nof_ifaces_added;
+
+		fst_printf_iface(i, MSG_DEBUG, "added to MB IE");
+	}
+
+	if (!nof_ifaces_added) {
+		wpabuf_free(buf);
+		buf = NULL;
+		fst_printf_iface(i, MSG_INFO,
+				 "cannot add MB IE: no backup ifaces");
+	} else {
+		fst_dump_mb_ies(fst_group_get_id(g), fst_iface_get_name(i),
+				buf);
+	}
+
+	return buf;
+}
+
+
+static const u8 * fst_mbie_get_peer_addr(const struct multi_band_ie *mbie)
+{
+	const u8 *peer_addr = NULL;
+
+	switch (MB_CTRL_ROLE(mbie->mb_ctrl)) {
+	case MB_STA_ROLE_AP:
+		peer_addr = mbie->bssid;
+		break;
+	case MB_STA_ROLE_NON_PCP_NON_AP:
+		if (mbie->mb_ctrl & MB_CTRL_STA_MAC_PRESENT &&
+		    (size_t) 2 + mbie->len >= sizeof(*mbie) + ETH_ALEN)
+			peer_addr = (const u8 *) &mbie[1];
+		break;
+	default:
+		break;
+	}
+
+	return peer_addr;
+}
+
+
+static struct fst_iface *
+fst_group_get_new_iface_by_mbie_and_band_id(struct fst_group *g,
+					    const u8 *mb_ies_buff,
+					    size_t mb_ies_size,
+					    u8 band_id,
+					    u8 *iface_peer_addr)
+{
+	while (mb_ies_size >= 2) {
+		const struct multi_band_ie *mbie =
+			(const struct multi_band_ie *) mb_ies_buff;
+
+		if (mbie->eid != WLAN_EID_MULTI_BAND ||
+		    (size_t) 2 + mbie->len < sizeof(*mbie))
+			break;
+
+		if (mbie->band_id == band_id) {
+			struct fst_iface *iface;
+
+			foreach_fst_group_iface(g, iface) {
+				const u8 *peer_addr =
+					fst_mbie_get_peer_addr(mbie);
+
+				if (peer_addr &&
+				    fst_iface_is_connected(iface, peer_addr,
+							   TRUE) &&
+				    band_id == fst_iface_get_band_id(iface)) {
+					os_memcpy(iface_peer_addr, peer_addr,
+						  ETH_ALEN);
+					return iface;
+				}
+			}
+			break;
+		}
+
+		mb_ies_buff += 2 + mbie->len;
+		mb_ies_size -= 2 + mbie->len;
+	}
+
+	return NULL;
+}
+
+
+struct fst_iface * fst_group_get_iface_by_name(struct fst_group *g,
+					       const char *ifname)
+{
+	struct fst_iface *f;
+
+	foreach_fst_group_iface(g, f) {
+		const char *in = fst_iface_get_name(f);
+
+		if (os_strncmp(in, ifname, os_strlen(in)) == 0)
+			return f;
+	}
+
+	return NULL;
+}
+
+
+u8 fst_group_assign_dialog_token(struct fst_group *g)
+{
+	g->dialog_token++;
+	if (g->dialog_token == 0)
+		g->dialog_token++;
+	return g->dialog_token;
+}
+
+
+u32 fst_group_assign_fsts_id(struct fst_group *g)
+{
+	g->fsts_id++;
+	return g->fsts_id;
+}
+
+
+static Boolean
+fst_group_does_iface_appear_in_other_mbies(struct fst_group *g,
+					   struct fst_iface *iface,
+					   struct fst_iface *other,
+					   u8 *peer_addr)
+{
+	struct fst_get_peer_ctx *ctx;
+	const u8 *addr;
+	const u8 *iface_addr;
+	enum mb_band_id  iface_band_id;
+
+	WPA_ASSERT(g == fst_iface_get_group(iface));
+	WPA_ASSERT(g == fst_iface_get_group(other));
+
+	iface_addr = fst_iface_get_addr(iface);
+	iface_band_id = fst_iface_get_band_id(iface);
+
+	addr = fst_iface_get_peer_first(other, &ctx, TRUE);
+	for (; addr; addr = fst_iface_get_peer_next(other, &ctx, TRUE)) {
+		const struct wpabuf *mbies;
+		u8 other_iface_peer_addr[ETH_ALEN];
+		struct fst_iface *other_new_iface;
+
+		mbies = fst_iface_get_peer_mb_ie(other, addr);
+		if (!mbies)
+			continue;
+
+		other_new_iface = fst_group_get_new_iface_by_mbie_and_band_id(
+			g, wpabuf_head(mbies), wpabuf_len(mbies),
+			iface_band_id, other_iface_peer_addr);
+		if (other_new_iface == iface &&
+		    os_memcmp(iface_addr, other_iface_peer_addr,
+			      ETH_ALEN) != 0) {
+			os_memcpy(peer_addr, addr, ETH_ALEN);
+			return TRUE;
+		}
+	}
+
+	return FALSE;
+}
+
+
+struct fst_iface *
+fst_group_find_new_iface_by_stie(struct fst_group *g,
+				 struct fst_iface *iface,
+				 const u8 *peer_addr,
+				 const struct session_transition_ie *stie,
+				 u8 *iface_peer_addr)
+{
+	struct fst_iface *i;
+
+	foreach_fst_group_iface(g, i) {
+		if (i == iface ||
+		    stie->new_band_id != fst_iface_get_band_id(i))
+			continue;
+		if (fst_group_does_iface_appear_in_other_mbies(g, iface, i,
+			iface_peer_addr))
+			return i;
+		break;
+	}
+	return NULL;
+}
+
+
+struct fst_iface *
+fst_group_get_new_iface_by_stie_and_mbie(
+	struct fst_group *g, const u8 *mb_ies_buff, size_t mb_ies_size,
+	const struct session_transition_ie *stie, u8 *iface_peer_addr)
+{
+	return fst_group_get_new_iface_by_mbie_and_band_id(
+		g, mb_ies_buff, mb_ies_size, stie->new_band_id,
+		iface_peer_addr);
+}
+
+
+struct fst_group * fst_group_create(const char *group_id)
+{
+	struct fst_group *g;
+
+	g = os_zalloc(sizeof(*g));
+	if (g == NULL) {
+		fst_printf(MSG_ERROR, "%s: Cannot alloc group", group_id);
+		return NULL;
+	}
+
+	dl_list_init(&g->ifaces);
+	os_strlcpy(g->group_id, group_id, sizeof(g->group_id));
+
+	dl_list_add_tail(&fst_global_groups_list, &g->global_groups_lentry);
+	fst_printf_group(g, MSG_DEBUG, "instance created");
+
+	foreach_fst_ctrl_call(on_group_created, g);
+
+	return g;
+}
+
+
+void fst_group_attach_iface(struct fst_group *g, struct fst_iface *i)
+{
+	struct dl_list *list = &g->ifaces;
+	struct fst_iface *f;
+
+	/*
+	 * Add new interface to the list.
+	 * The list is sorted in descending order by priority to allow
+	 * multiple MB IEs creation according to the spec (see 10.32 Multi-band
+	 * operation, 10.32.1 General), as they should be ordered according to
+	 * priorities.
+	 */
+	foreach_fst_group_iface(g, f) {
+		if (fst_iface_get_priority(f) < fst_iface_get_priority(i))
+			break;
+		list = &f->group_lentry;
+	}
+	dl_list_add(list, &i->group_lentry);
+}
+
+
+void fst_group_detach_iface(struct fst_group *g, struct fst_iface *i)
+{
+	dl_list_del(&i->group_lentry);
+}
+
+
+void fst_group_delete(struct fst_group *group)
+{
+	struct fst_session *s;
+
+	dl_list_del(&group->global_groups_lentry);
+	WPA_ASSERT(dl_list_empty(&group->ifaces));
+	foreach_fst_ctrl_call(on_group_deleted, group);
+	fst_printf_group(group, MSG_DEBUG, "instance deleted");
+	while ((s = fst_session_global_get_first_by_group(group)) != NULL)
+		fst_session_delete(s);
+	os_free(group);
+}
+
+
+Boolean fst_group_delete_if_empty(struct fst_group *group)
+{
+	Boolean is_empty = !fst_group_has_ifaces(group) &&
+		!fst_session_global_get_first_by_group(group);
+
+	if (is_empty)
+		fst_group_delete(group);
+
+	return is_empty;
+}
+
+
+void fst_group_update_ie(struct fst_group *g)
+{
+	struct fst_iface *i;
+
+	foreach_fst_group_iface(g, i) {
+		struct wpabuf *mbie = fst_group_create_mb_ie(g, i);
+
+		if (!mbie)
+			fst_printf_iface(i, MSG_WARNING, "cannot create MB IE");
+
+		fst_iface_attach_mbie(i, mbie);
+		fst_iface_set_ies(i, mbie);
+		fst_printf_iface(i, MSG_DEBUG, "multi-band IE set to %p", mbie);
+	}
+}
diff --git a/src/fst/fst_group.h b/src/fst/fst_group.h
new file mode 100644
index 0000000..3a87c0b
--- /dev/null
+++ b/src/fst/fst_group.h
@@ -0,0 +1,75 @@
+/*
+ * FST module - FST group object definitions
+ * Copyright (c) 2014, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef FST_GROUP_H
+#define FST_GROUP_H
+
+struct fst_group {
+	char group_id[IFNAMSIZ + 1];
+	struct dl_list ifaces;
+	u8 dialog_token;
+	u32 fsts_id;
+	struct dl_list global_groups_lentry;
+};
+
+struct session_transition_ie;
+
+#define foreach_fst_group_iface(g, i) \
+	dl_list_for_each((i), &(g)->ifaces, struct fst_iface, group_lentry)
+
+struct fst_group * fst_group_create(const char *group_id);
+void fst_group_attach_iface(struct fst_group *g, struct fst_iface *i);
+void fst_group_detach_iface(struct fst_group *g, struct fst_iface *i);
+void fst_group_delete(struct fst_group *g);
+
+void fst_group_update_ie(struct fst_group *g);
+
+static inline Boolean fst_group_has_ifaces(struct fst_group *g)
+{
+	return !dl_list_empty(&g->ifaces);
+}
+
+static inline struct fst_iface * fst_group_first_iface(struct fst_group *g)
+{
+	return dl_list_first(&g->ifaces, struct fst_iface, group_lentry);
+}
+
+static inline const char * fst_group_get_id(struct fst_group *g)
+{
+	return g->group_id;
+}
+
+Boolean fst_group_delete_if_empty(struct fst_group *group);
+struct fst_iface * fst_group_get_iface_by_name(struct fst_group *g,
+					       const char *ifname);
+struct fst_iface *
+fst_group_find_new_iface_by_stie(struct fst_group *g,
+				 struct fst_iface *iface,
+				 const u8 *peer_addr,
+				 const struct session_transition_ie *stie,
+				 u8 *iface_peer_addr);
+struct fst_iface *
+fst_group_get_new_iface_by_stie_and_mbie(
+	struct fst_group *g, const u8 *mb_ies_buff, size_t mb_ies_size,
+	const struct session_transition_ie *stie, u8 *iface_peer_addr);
+u8  fst_group_assign_dialog_token(struct fst_group *g);
+u32 fst_group_assign_fsts_id(struct fst_group *g);
+
+extern struct dl_list fst_global_groups_list;
+
+#define foreach_fst_group(g) \
+	dl_list_for_each((g), &fst_global_groups_list, \
+			 struct fst_group, global_groups_lentry)
+
+static inline struct fst_group * fst_first_group(void)
+{
+	return dl_list_first(&fst_global_groups_list, struct fst_group,
+			     global_groups_lentry);
+}
+
+#endif /* FST_GROUP_H */
diff --git a/src/fst/fst_iface.c b/src/fst/fst_iface.c
new file mode 100644
index 0000000..35e83cb
--- /dev/null
+++ b/src/fst/fst_iface.c
@@ -0,0 +1,80 @@
+/*
+ * FST module - FST interface object implementation
+ * Copyright (c) 2014, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+#include "utils/common.h"
+#include "fst/fst_internal.h"
+#include "fst/fst_defs.h"
+
+
+struct fst_iface * fst_iface_create(struct fst_group *g, const char *ifname,
+				    const u8 *own_addr,
+				    const struct fst_wpa_obj *iface_obj,
+				    const struct fst_iface_cfg *cfg)
+{
+	struct fst_iface *i;
+
+	i = os_zalloc(sizeof(*i));
+	if (!i) {
+		fst_printf_group(g, MSG_ERROR, "cannot allocate iface for %s",
+				ifname);
+		return NULL;
+	}
+
+	i->cfg = *cfg;
+	i->iface_obj = *iface_obj;
+	i->group = g;
+	os_strlcpy(i->ifname, ifname, sizeof(i->ifname));
+	os_memcpy(i->own_addr, own_addr, sizeof(i->own_addr));
+
+	if (!i->cfg.llt) {
+		fst_printf_iface(i, MSG_WARNING, "Zero llt adjusted");
+		i->cfg.llt = FST_DEFAULT_LLT_CFG_VALUE;
+	}
+
+	return i;
+}
+
+
+void fst_iface_delete(struct fst_iface *i)
+{
+	fst_iface_set_ies(i, NULL);
+	wpabuf_free(i->mb_ie);
+	os_free(i);
+}
+
+
+Boolean fst_iface_is_connected(struct fst_iface *iface, const u8 *addr,
+			       Boolean mb_only)
+{
+	struct fst_get_peer_ctx *ctx;
+	const u8 *a = fst_iface_get_peer_first(iface, &ctx, mb_only);
+
+	for (; a != NULL; a = fst_iface_get_peer_next(iface, &ctx, mb_only))
+		if (os_memcmp(addr, a, ETH_ALEN) == 0)
+			return TRUE;
+
+	return FALSE;
+}
+
+
+void fst_iface_attach_mbie(struct fst_iface *i, struct wpabuf *mbie)
+{
+	wpabuf_free(i->mb_ie);
+	i->mb_ie = mbie;
+}
+
+
+enum mb_band_id fst_iface_get_band_id(struct fst_iface *i)
+{
+	enum hostapd_hw_mode hw_mode;
+	u8 channel;
+
+	fst_iface_get_channel_info(i, &hw_mode, &channel);
+	return fst_hw_mode_to_band(hw_mode);
+}
diff --git a/src/fst/fst_iface.h b/src/fst/fst_iface.h
new file mode 100644
index 0000000..0eb2732
--- /dev/null
+++ b/src/fst/fst_iface.h
@@ -0,0 +1,136 @@
+/*
+ * FST module - FST interface object definitions
+ * Copyright (c) 2014, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+
+#ifndef FST_IFACE_H
+#define FST_IFACE_H
+
+#include "utils/includes.h"
+#include "utils/common.h"
+#include "list.h"
+#include "fst.h"
+
+struct fst_iface {
+	struct fst_group *group;
+	struct fst_wpa_obj iface_obj;
+	u8 own_addr[ETH_ALEN];
+	struct wpabuf *mb_ie;
+	char ifname[IFNAMSIZ + 1];
+	struct fst_iface_cfg cfg;
+	struct dl_list group_lentry;
+};
+
+struct fst_iface * fst_iface_create(struct fst_group *g, const char *ifname,
+				    const u8 *own_addr,
+				    const struct fst_wpa_obj *iface_obj,
+				    const struct fst_iface_cfg *cfg);
+void fst_iface_delete(struct fst_iface *i);
+
+static inline struct fst_group * fst_iface_get_group(struct fst_iface *i)
+{
+	return i->group;
+}
+
+static inline const char * fst_iface_get_name(struct fst_iface *i)
+{
+	return i->ifname;
+}
+
+static inline const u8 * fst_iface_get_addr(struct fst_iface *i)
+{
+	return i->own_addr;
+}
+
+static inline const char * fst_iface_get_group_id(struct fst_iface *i)
+{
+	return i->cfg.group_id;
+}
+
+static inline u8 fst_iface_get_priority(struct fst_iface *i)
+{
+	return i->cfg.priority;
+}
+
+static inline u32 fst_iface_get_llt(struct fst_iface *i)
+{
+	return i->cfg.llt;
+}
+
+static inline const struct wpabuf * fst_iface_get_mbie(struct fst_iface *i)
+{
+	return i->mb_ie;
+}
+
+static inline const u8 * fst_iface_get_bssid(struct fst_iface *i)
+{
+	return i->iface_obj.get_bssid(i->iface_obj.ctx);
+}
+
+static inline void fst_iface_get_channel_info(struct fst_iface *i,
+					      enum hostapd_hw_mode *hw_mode,
+					      u8 *channel)
+{
+	i->iface_obj.get_channel_info(i->iface_obj.ctx, hw_mode, channel);
+}
+
+static inline int fst_iface_get_hw_modes(struct fst_iface *i,
+					 struct hostapd_hw_modes **modes)
+{
+	return i->iface_obj.get_hw_modes(i->iface_obj.ctx, modes);
+}
+
+static inline void fst_iface_set_ies(struct fst_iface *i,
+				     const struct wpabuf *fst_ies)
+{
+	i->iface_obj.set_ies(i->iface_obj.ctx, fst_ies);
+}
+
+static inline int fst_iface_send_action(struct fst_iface *i,
+					const u8 *addr, struct wpabuf *data)
+{
+	return i->iface_obj.send_action(i->iface_obj.ctx, addr, data);
+}
+
+static inline const struct wpabuf *
+fst_iface_get_peer_mb_ie(struct fst_iface *i, const u8 *addr)
+{
+	return i->iface_obj.get_mb_ie(i->iface_obj.ctx, addr);
+}
+
+static inline void fst_iface_update_mb_ie(struct fst_iface *i,
+					  const u8 *addr,
+					  const u8 *buf, size_t size)
+{
+	return i->iface_obj.update_mb_ie(i->iface_obj.ctx, addr, buf, size);
+}
+
+static inline const u8 * fst_iface_get_peer_first(struct fst_iface *i,
+						  struct fst_get_peer_ctx **ctx,
+						  Boolean mb_only)
+{
+	return i->iface_obj.get_peer_first(i->iface_obj.ctx, ctx, mb_only);
+}
+
+static inline const u8 * fst_iface_get_peer_next(struct fst_iface *i,
+						 struct fst_get_peer_ctx **ctx,
+						 Boolean mb_only)
+{
+	return i->iface_obj.get_peer_next(i->iface_obj.ctx, ctx, mb_only);
+}
+
+Boolean fst_iface_is_connected(struct fst_iface *iface, const u8 *addr,
+			       Boolean mb_only);
+void fst_iface_attach_mbie(struct fst_iface *i, struct wpabuf *mbie);
+enum mb_band_id fst_iface_get_band_id(struct fst_iface *i);
+
+static inline void * fst_iface_get_wpa_obj_ctx(struct fst_iface *i)
+{
+	return i->iface_obj.ctx;
+}
+
+#endif /* FST_IFACE_H */
diff --git a/src/fst/fst_internal.h b/src/fst/fst_internal.h
new file mode 100644
index 0000000..9fe32b8
--- /dev/null
+++ b/src/fst/fst_internal.h
@@ -0,0 +1,49 @@
+/*
+ * FST module - auxiliary definitions
+ * Copyright (c) 2014, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef FST_INTERNAL_H
+#define FST_INTERNAL_H
+
+#include "utils/includes.h"
+#include "utils/common.h"
+#include "common/defs.h"
+#include "common/ieee802_11_defs.h"
+#include "fst/fst_iface.h"
+#include "fst/fst_group.h"
+#include "fst/fst_session.h"
+
+#define fst_printf(level, format, ...) \
+	wpa_printf((level), "FST: " format, ##__VA_ARGS__)
+
+#define fst_printf_group(group, level, format, ...) \
+	wpa_printf((level), "FST: %s: " format, \
+		   fst_group_get_id(group), ##__VA_ARGS__)
+
+#define fst_printf_iface(iface, level, format, ...) \
+	fst_printf_group(fst_iface_get_group(iface), (level), "%s: " format, \
+			 fst_iface_get_name(iface), ##__VA_ARGS__)
+
+enum mb_band_id fst_hw_mode_to_band(enum hostapd_hw_mode mode);
+
+struct fst_ctrl_handle {
+	struct fst_ctrl ctrl;
+	struct dl_list global_ctrls_lentry;
+};
+
+extern struct dl_list fst_global_ctrls_list;
+
+#define foreach_fst_ctrl_call(clb, ...) \
+	do { \
+		struct fst_ctrl_handle *__fst_ctrl_h; \
+		dl_list_for_each(__fst_ctrl_h, &fst_global_ctrls_list, \
+			struct fst_ctrl_handle, global_ctrls_lentry) \
+			if (__fst_ctrl_h->ctrl.clb) \
+				__fst_ctrl_h->ctrl.clb(__VA_ARGS__);\
+	} while (0)
+
+#endif /* FST_INTERNAL_H */
diff --git a/src/fst/fst_session.c b/src/fst/fst_session.c
new file mode 100644
index 0000000..f804b12
--- /dev/null
+++ b/src/fst/fst_session.c
@@ -0,0 +1,1623 @@
+/*
+ * FST module - FST Session implementation
+ * Copyright (c) 2014, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "common/defs.h"
+#include "fst/fst_internal.h"
+#include "fst/fst_defs.h"
+#include "fst/fst_ctrl_iface.h"
+#ifdef CONFIG_FST_TEST
+#include "fst/fst_ctrl_defs.h"
+#endif /* CONFIG_FST_TEST */
+
+#define US_80211_TU 1024
+
+#define US_TO_TU(m) ((m) * / US_80211_TU)
+#define TU_TO_US(m) ((m) * US_80211_TU)
+
+#define FST_LLT_SWITCH_IMMEDIATELY 0
+
+#define fst_printf_session(s, level, format, ...) \
+	fst_printf((level), "%u (0x%08x): [" MACSTR "," MACSTR "] :" format, \
+		   (s)->id, (s)->data.fsts_id, \
+		   MAC2STR((s)->data.old_peer_addr), \
+		   MAC2STR((s)->data.new_peer_addr), \
+		   ##__VA_ARGS__)
+
+#define fst_printf_siface(s, iface, level, format, ...) \
+	fst_printf_session((s), (level), "%s: " format, \
+			   fst_iface_get_name(iface), ##__VA_ARGS__)
+
+#define fst_printf_sframe(s, is_old, level, format, ...) \
+	fst_printf_siface((s), \
+		(is_old) ? (s)->data.old_iface : (s)->data.new_iface, \
+		(level), format, ##__VA_ARGS__)
+
+#define FST_LLT_MS_DEFAULT 50
+#define FST_ACTION_MAX_SUPPORTED   FST_ACTION_ON_CHANNEL_TUNNEL
+
+const char * const fst_action_names[] = {
+	[FST_ACTION_SETUP_REQUEST]     = "Setup Request",
+	[FST_ACTION_SETUP_RESPONSE]    = "Setup Response",
+	[FST_ACTION_TEAR_DOWN]         = "Tear Down",
+	[FST_ACTION_ACK_REQUEST]       = "Ack Request",
+	[FST_ACTION_ACK_RESPONSE]      = "Ack Response",
+	[FST_ACTION_ON_CHANNEL_TUNNEL] = "On Channel Tunnel",
+};
+
+struct fst_session {
+	struct {
+		/* Session configuration that can be zeroed on reset */
+		u8 old_peer_addr[ETH_ALEN];
+		u8 new_peer_addr[ETH_ALEN];
+		struct fst_iface *new_iface;
+		struct fst_iface *old_iface;
+		u32 llt_ms;
+		u8 pending_setup_req_dlgt;
+		u32 fsts_id; /* FSTS ID, see spec, 8.4.2.147
+			      * Session Transition element */
+	} data;
+	/* Session object internal fields which won't be zeroed on reset */
+	struct dl_list global_sessions_lentry;
+	u32 id; /* Session object ID used to identify
+		 * specific session object */
+	struct fst_group *group;
+	enum fst_session_state state;
+	Boolean stt_armed;
+};
+
+static struct dl_list global_sessions_list;
+static u32 global_session_id = 0;
+
+#define foreach_fst_session(s) \
+	dl_list_for_each((s), &global_sessions_list, \
+			 struct fst_session, global_sessions_lentry)
+
+#define foreach_fst_session_safe(s, temp) \
+	dl_list_for_each_safe((s), (temp), &global_sessions_list, \
+			      struct fst_session, global_sessions_lentry)
+
+
+static void fst_session_global_inc_id(void)
+{
+	global_session_id++;
+	if (global_session_id == FST_INVALID_SESSION_ID)
+		global_session_id++;
+}
+
+
+int fst_session_global_init(void)
+{
+	dl_list_init(&global_sessions_list);
+	return 0;
+}
+
+
+void fst_session_global_deinit(void)
+{
+	WPA_ASSERT(dl_list_empty(&global_sessions_list));
+}
+
+
+static inline void fst_session_notify_ctrl(struct fst_session *s,
+					   enum fst_event_type event_type,
+					   union fst_event_extra *extra)
+{
+	foreach_fst_ctrl_call(on_event, event_type, NULL, s, extra);
+}
+
+
+static void fst_session_set_state(struct fst_session *s,
+				  enum fst_session_state state,
+				  union fst_session_state_switch_extra *extra)
+{
+	if (s->state != state) {
+		union fst_event_extra evext = {
+			.session_state = {
+				.old_state = s->state,
+				.new_state = state,
+			},
+		};
+
+		if (extra)
+			evext.session_state.extra = *extra;
+		fst_session_notify_ctrl(s, EVENT_FST_SESSION_STATE_CHANGED,
+					&evext);
+		fst_printf_session(s, MSG_INFO, "State: %s => %s",
+				   fst_session_state_name(s->state),
+				   fst_session_state_name(state));
+		s->state = state;
+	}
+}
+
+
+static u32 fst_find_free_session_id(void)
+{
+	u32 i, id = FST_INVALID_SESSION_ID;
+	struct fst_session *s;
+
+	for (i = 0; i < (u32) -1; i++) {
+		Boolean in_use = FALSE;
+
+		foreach_fst_session(s) {
+			if (s->id == global_session_id) {
+				fst_session_global_inc_id();
+				in_use = TRUE;
+				break;
+			}
+		}
+		if (!in_use) {
+			id = global_session_id;
+			fst_session_global_inc_id();
+			break;
+		}
+	}
+
+	return id;
+}
+
+
+static void fst_session_timeout_handler(void *eloop_data, void *user_ctx)
+{
+	struct fst_session *s = user_ctx;
+	union fst_session_state_switch_extra extra = {
+		.to_initial = {
+			.reason = REASON_STT,
+		},
+	};
+
+	fst_printf_session(s, MSG_WARNING, "Session State Timeout");
+	fst_session_set_state(s, FST_SESSION_STATE_INITIAL, &extra);
+}
+
+
+static void fst_session_stt_arm(struct fst_session *s)
+{
+	eloop_register_timeout(0, TU_TO_US(FST_DEFAULT_SESSION_TIMEOUT_TU),
+			       fst_session_timeout_handler, NULL, s);
+	s->stt_armed = TRUE;
+}
+
+
+static void fst_session_stt_disarm(struct fst_session *s)
+{
+	if (s->stt_armed) {
+		eloop_cancel_timeout(fst_session_timeout_handler, NULL, s);
+		s->stt_armed = FALSE;
+	}
+}
+
+
+static Boolean fst_session_is_in_transition(struct fst_session *s)
+{
+	/* See spec, 10.32.2.2  Transitioning between states */
+	return s->stt_armed;
+}
+
+
+static int fst_session_is_in_progress(struct fst_session *s)
+{
+	return s->state != FST_SESSION_STATE_INITIAL;
+}
+
+
+static int fst_session_is_ready_pending(struct fst_session *s)
+{
+	return s->state == FST_SESSION_STATE_SETUP_COMPLETION &&
+		fst_session_is_in_transition(s);
+}
+
+
+static int fst_session_is_ready(struct fst_session *s)
+{
+	return s->state == FST_SESSION_STATE_SETUP_COMPLETION &&
+		!fst_session_is_in_transition(s);
+}
+
+
+static int fst_session_is_switch_requested(struct fst_session *s)
+{
+	return s->state == FST_SESSION_STATE_TRANSITION_DONE &&
+		fst_session_is_in_transition(s);
+}
+
+
+static struct fst_session *
+fst_find_session_in_progress(const u8 *peer_addr, struct fst_group *g)
+{
+	struct fst_session *s;
+
+	foreach_fst_session(s) {
+		if (s->group == g &&
+		    (os_memcmp(s->data.old_peer_addr, peer_addr,
+			       ETH_ALEN) == 0 ||
+		     os_memcmp(s->data.new_peer_addr, peer_addr,
+			       ETH_ALEN) == 0) &&
+		    fst_session_is_in_progress(s))
+			return s;
+	}
+
+	return NULL;
+}
+
+
+static void fst_session_reset_ex(struct fst_session *s, enum fst_reason reason)
+{
+	union fst_session_state_switch_extra evext = {
+		.to_initial = {
+			.reason = reason,
+		},
+	};
+
+	if (s->state == FST_SESSION_STATE_SETUP_COMPLETION ||
+	    s->state == FST_SESSION_STATE_TRANSITION_DONE)
+		fst_session_tear_down_setup(s);
+	fst_session_stt_disarm(s);
+	os_memset(&s->data, 0, sizeof(s->data));
+	fst_session_set_state(s, FST_SESSION_STATE_INITIAL, &evext);
+}
+
+
+static int fst_session_send_action(struct fst_session *s, Boolean old_iface,
+				   const void *payload, size_t size,
+				   const struct wpabuf *extra_buf)
+{
+	size_t len;
+	int res;
+	struct wpabuf *buf;
+	u8 action;
+	struct fst_iface *iface =
+		old_iface ? s->data.old_iface : s->data.new_iface;
+
+	WPA_ASSERT(payload != NULL);
+	WPA_ASSERT(size != 0);
+
+	action = *(const u8 *) payload;
+
+	WPA_ASSERT(action <= FST_ACTION_MAX_SUPPORTED);
+
+	if (!iface) {
+		fst_printf_session(s, MSG_ERROR,
+				   "no %s interface for FST Action '%s' sending",
+				   old_iface ? "old" : "new",
+				   fst_action_names[action]);
+		return -1;
+	}
+
+	len = sizeof(u8) /* category */ + size;
+	if (extra_buf)
+		len += wpabuf_size(extra_buf);
+
+	buf = wpabuf_alloc(len);
+	if (!buf) {
+		fst_printf_session(s, MSG_ERROR,
+				   "cannot allocate buffer of %zu bytes for FST Action '%s' sending",
+				   len, fst_action_names[action]);
+		return -1;
+	}
+
+	wpabuf_put_u8(buf, WLAN_ACTION_FST);
+	wpabuf_put_data(buf, payload, size);
+	if (extra_buf)
+		wpabuf_put_buf(buf, extra_buf);
+
+	res = fst_iface_send_action(iface,
+				    old_iface ? s->data.old_peer_addr :
+				    s->data.new_peer_addr, buf);
+	if (res < 0)
+		fst_printf_siface(s, iface, MSG_ERROR,
+				  "failed to send FST Action '%s'",
+				  fst_action_names[action]);
+	else
+		fst_printf_siface(s, iface, MSG_DEBUG, "FST Action '%s' sent",
+				  fst_action_names[action]);
+	wpabuf_free(buf);
+
+	return res;
+}
+
+
+static int fst_session_send_tear_down(struct fst_session *s)
+{
+	struct fst_tear_down td;
+	int res;
+
+	if (!fst_session_is_in_progress(s)) {
+		fst_printf_session(s, MSG_ERROR, "No FST setup to tear down");
+		return -1;
+	}
+
+	WPA_ASSERT(s->data.old_iface != NULL);
+	WPA_ASSERT(s->data.new_iface != NULL);
+
+	os_memset(&td, 0, sizeof(td));
+
+	td.action = FST_ACTION_TEAR_DOWN;
+	td.fsts_id = host_to_le32(s->data.fsts_id);
+
+	res = fst_session_send_action(s, TRUE, &td, sizeof(td), NULL);
+	if (!res)
+		fst_printf_sframe(s, TRUE, MSG_INFO, "FST TearDown sent");
+	else
+		fst_printf_sframe(s, TRUE, MSG_ERROR,
+				  "failed to send FST TearDown");
+
+	return res;
+}
+
+
+static void fst_session_handle_setup_request(struct fst_iface *iface,
+					     const struct ieee80211_mgmt *mgmt,
+					     size_t frame_len)
+{
+	struct fst_session *s;
+	const struct fst_setup_req *req;
+	struct fst_iface *new_iface = NULL;
+	struct fst_group *g;
+	u8 new_iface_peer_addr[ETH_ALEN];
+	const struct wpabuf *peer_mbies;
+	size_t plen;
+
+	if (frame_len < IEEE80211_HDRLEN + 1 + sizeof(*req))  {
+		fst_printf_iface(iface, MSG_WARNING,
+				 "FST Request dropped: too short (%zu < %zu)",
+				 frame_len,
+				 IEEE80211_HDRLEN + 1 + sizeof(*req));
+		return;
+	}
+	plen = frame_len - IEEE80211_HDRLEN - 1;
+	req = (const struct fst_setup_req *)
+		(((const u8 *) mgmt) + IEEE80211_HDRLEN + 1);
+	if (req->stie.element_id != WLAN_EID_SESSION_TRANSITION ||
+	    req->stie.length < 11) {
+		fst_printf_iface(iface, MSG_WARNING,
+				 "FST Request dropped: invalid STIE");
+		return;
+	}
+
+	if (req->stie.new_band_id == req->stie.old_band_id) {
+		fst_printf_iface(iface, MSG_WARNING,
+				 "FST Request dropped: new and old band IDs are the same");
+		return;
+	}
+
+	g = fst_iface_get_group(iface);
+
+	if (plen > sizeof(*req)) {
+		fst_iface_update_mb_ie(iface, mgmt->sa, (const u8 *) (req + 1),
+				       plen - sizeof(*req));
+		fst_printf_iface(iface, MSG_INFO,
+				 "FST Request: MB IEs updated for " MACSTR,
+				 MAC2STR(mgmt->sa));
+	}
+
+	peer_mbies = fst_iface_get_peer_mb_ie(iface, mgmt->sa);
+	if (peer_mbies) {
+		new_iface = fst_group_get_new_iface_by_stie_and_mbie(
+			g, wpabuf_head(peer_mbies), wpabuf_len(peer_mbies),
+			&req->stie, new_iface_peer_addr);
+		if (new_iface)
+			fst_printf_iface(iface, MSG_INFO,
+					 "FST Request: new iface (%s:" MACSTR
+					 ") found by MB IEs",
+					 fst_iface_get_name(new_iface),
+					 MAC2STR(new_iface_peer_addr));
+	}
+
+	if (!new_iface) {
+		new_iface = fst_group_find_new_iface_by_stie(
+			g, iface, mgmt->sa, &req->stie,
+			new_iface_peer_addr);
+		if (new_iface)
+			fst_printf_iface(iface, MSG_INFO,
+					 "FST Request: new iface (%s:" MACSTR
+					 ") found by others",
+					 fst_iface_get_name(new_iface),
+					 MAC2STR(new_iface_peer_addr));
+	}
+
+	if (!new_iface) {
+		fst_printf_iface(iface, MSG_WARNING,
+				 "FST Request dropped: new iface not found");
+		return;
+	}
+
+	s = fst_find_session_in_progress(mgmt->sa, g);
+	if (s) {
+		union fst_session_state_switch_extra evext = {
+			.to_initial = {
+				.reason = REASON_SETUP,
+			},
+		};
+
+		/*
+		 * 10.32.2.2  Transitioning between states:
+		 * Upon receipt of an FST Setup Request frame, the responder
+		 * shall respond with an FST Setup Response frame unless it has
+		 * a pending FST Setup Request frame addressed to the initiator
+		 * and the responder has a numerically larger MAC address than
+		 * the initiator’s MAC address, in which case, the responder
+		 * shall delete the received FST Setup Request.
+		 */
+		if (os_memcmp(mgmt->da, mgmt->sa, ETH_ALEN) > 0) {
+			fst_printf_session(s, MSG_WARNING,
+					   "FST Request dropped due to MAC comparison (our MAC is "
+					   MACSTR ")",
+					   MAC2STR(mgmt->da));
+			return;
+		}
+
+		if (!fst_session_is_ready_pending(s)) {
+			fst_printf_session(s, MSG_WARNING,
+					   "FST Request from " MACSTR
+					   " dropped due to inappropriate state %s",
+					   MAC2STR(mgmt->da),
+					   fst_session_state_name(s->state));
+			return;
+		}
+
+
+		/*
+		 * If FST Setup Request arrived with the same FSTS ID as one we
+		 * initialized before, it means the other side either didn't
+		 * receive our FST Request or skipped it for some reason (for
+		 * example, due to numerical MAC comparison).
+		 *
+		 * In this case, there's no need to tear down the session.
+		 * Moreover, as FSTS ID is the same, the other side will
+		 * associate this tear down with the session it initiated that
+		 * will break the sync.
+		 */
+		if (le_to_host32(req->stie.fsts_id) != s->data.fsts_id)
+			fst_session_send_tear_down(s);
+		else
+			fst_printf_session(s, MSG_WARNING,
+					   "Skipping TearDown as the FST request has the same FSTS ID as initiated");
+		fst_session_set_state(s, FST_SESSION_STATE_INITIAL, &evext);
+		fst_session_stt_disarm(s);
+		fst_printf_session(s, MSG_WARNING, "reset due to FST request");
+	}
+
+	s = fst_session_create(g);
+	if (!s) {
+		fst_printf(MSG_WARNING,
+			   "FST Request dropped: cannot create session for %s and %s",
+			   fst_iface_get_name(iface),
+			   fst_iface_get_name(new_iface));
+		return;
+	}
+
+	fst_session_set_iface(s, iface, TRUE);
+	fst_session_set_peer_addr(s, mgmt->sa, TRUE);
+	fst_session_set_iface(s, new_iface, FALSE);
+	fst_session_set_peer_addr(s, new_iface_peer_addr, FALSE);
+	fst_session_set_llt(s, FST_LLT_VAL_TO_MS(le_to_host32(req->llt)));
+	s->data.pending_setup_req_dlgt = req->dialog_token;
+	s->data.fsts_id = le_to_host32(req->stie.fsts_id);
+
+	fst_session_stt_arm(s);
+
+	fst_session_notify_ctrl(s, EVENT_FST_SETUP, NULL);
+
+	fst_session_set_state(s, FST_SESSION_STATE_SETUP_COMPLETION, NULL);
+}
+
+
+static void fst_session_handle_setup_response(struct fst_session *s,
+					      struct fst_iface *iface,
+					      const struct ieee80211_mgmt *mgmt,
+					      size_t frame_len)
+{
+	const struct fst_setup_res *res;
+	size_t plen = frame_len - IEEE80211_HDRLEN - 1;
+	enum hostapd_hw_mode hw_mode;
+	u8 channel;
+	union fst_session_state_switch_extra evext = {
+		.to_initial = {0},
+	};
+
+	if (iface != s->data.old_iface) {
+		fst_printf_session(s, MSG_WARNING,
+				   "FST Response dropped: %s is not the old iface",
+				   fst_iface_get_name(iface));
+		return;
+	}
+
+	if (!fst_session_is_ready_pending(s)) {
+		fst_printf_session(s, MSG_WARNING,
+				   "FST Response dropped due to wrong state: %s",
+				   fst_session_state_name(s->state));
+		return;
+	}
+
+	if (plen < sizeof(*res)) {
+		fst_printf_session(s, MSG_WARNING,
+				   "Too short FST Response dropped");
+		return;
+	}
+	res = (const struct fst_setup_res *)
+		(((const u8 *) mgmt) + IEEE80211_HDRLEN + 1);
+	if (res->stie.element_id != WLAN_EID_SESSION_TRANSITION ||
+	    res->stie.length < 11) {
+		fst_printf_iface(iface, MSG_WARNING,
+				 "FST Response dropped: invalid STIE");
+		return;
+	}
+
+	if (res->dialog_token != s->data.pending_setup_req_dlgt)  {
+		fst_printf_session(s, MSG_WARNING,
+				   "FST Response dropped due to wrong dialog token (%u != %u)",
+				   s->data.pending_setup_req_dlgt,
+				   res->dialog_token);
+		return;
+	}
+
+	if (res->status_code == WLAN_STATUS_SUCCESS &&
+	    le_to_host32(res->stie.fsts_id) != s->data.fsts_id) {
+		fst_printf_session(s, MSG_WARNING,
+				   "FST Response dropped due to wrong FST Session ID (%u)",
+				   le_to_host32(res->stie.fsts_id));
+		return;
+	}
+
+	fst_session_stt_disarm(s);
+
+	if (res->status_code != WLAN_STATUS_SUCCESS) {
+		/*
+		 * 10.32.2.2  Transitioning between states
+		 * The initiator shall set the STT to the value of the
+		 * FSTSessionTimeOut field at ... and at each ACK frame sent in
+		 * response to a received FST Setup Response with the Status
+		 * Code field equal to PENDING_ADMITTING_FST_SESSION or
+		 * PENDING_GAP_IN_BA_WINDOW.
+		 */
+		evext.to_initial.reason = REASON_REJECT;
+		evext.to_initial.reject_code = res->status_code;
+		evext.to_initial.initiator = FST_INITIATOR_REMOTE;
+		fst_session_set_state(s, FST_SESSION_STATE_INITIAL, &evext);
+		fst_printf_session(s, MSG_WARNING,
+				   "FST Setup rejected by remote side with status %u",
+				   res->status_code);
+		return;
+	}
+
+	fst_iface_get_channel_info(s->data.new_iface, &hw_mode, &channel);
+
+	if (fst_hw_mode_to_band(hw_mode) != res->stie.new_band_id) {
+		evext.to_initial.reason = REASON_ERROR_PARAMS;
+		fst_session_set_state(s, FST_SESSION_STATE_INITIAL, &evext);
+		fst_printf_session(s, MSG_WARNING,
+				   "invalid FST Setup parameters");
+		fst_session_tear_down_setup(s);
+		return;
+	}
+
+	fst_printf_session(s, MSG_INFO,
+			   "%s: FST Setup established for %s (llt=%u)",
+			   fst_iface_get_name(s->data.old_iface),
+			   fst_iface_get_name(s->data.new_iface),
+			   s->data.llt_ms);
+
+	fst_session_notify_ctrl(s, EVENT_FST_ESTABLISHED, NULL);
+
+	if (s->data.llt_ms == FST_LLT_SWITCH_IMMEDIATELY)
+		fst_session_initiate_switch(s);
+}
+
+
+static void fst_session_handle_tear_down(struct fst_session *s,
+					 struct fst_iface *iface,
+					 const struct ieee80211_mgmt *mgmt,
+					 size_t frame_len)
+{
+	const struct fst_tear_down *td;
+	size_t plen = frame_len - IEEE80211_HDRLEN - 1;
+	union fst_session_state_switch_extra evext = {
+		.to_initial = {
+			.reason = REASON_TEARDOWN,
+			.initiator = FST_INITIATOR_REMOTE,
+		},
+	};
+
+	if (plen < sizeof(*td)) {
+		fst_printf_session(s, MSG_WARNING,
+				   "Too short FST Tear Down dropped");
+		return;
+	}
+	td = (const struct fst_tear_down *)
+		(((const u8 *) mgmt) + IEEE80211_HDRLEN + 1);
+
+	if (le_to_host32(td->fsts_id) != s->data.fsts_id) {
+		fst_printf_siface(s, iface, MSG_WARNING,
+				  "tear down for wrong FST Setup ID (%u)",
+				  le_to_host32(td->fsts_id));
+		return;
+	}
+
+	fst_session_stt_disarm(s);
+
+	fst_session_set_state(s, FST_SESSION_STATE_INITIAL, &evext);
+}
+
+
+static void fst_session_handle_ack_request(struct fst_session *s,
+					   struct fst_iface *iface,
+					   const struct ieee80211_mgmt *mgmt,
+					   size_t frame_len)
+{
+	const struct fst_ack_req *req;
+	size_t plen = frame_len - IEEE80211_HDRLEN - 1;
+	struct fst_ack_res res;
+	union fst_session_state_switch_extra evext = {
+		.to_initial = {
+			.reason = REASON_SWITCH,
+			.initiator = FST_INITIATOR_REMOTE,
+		},
+	};
+
+	if (!fst_session_is_ready(s) && !fst_session_is_switch_requested(s)) {
+		fst_printf_siface(s, iface, MSG_ERROR,
+				  "cannot initiate switch due to wrong session state (%s)",
+				  fst_session_state_name(s->state));
+		return;
+	}
+
+	WPA_ASSERT(s->data.new_iface != NULL);
+
+	if (iface != s->data.new_iface) {
+		fst_printf_siface(s, iface, MSG_ERROR,
+				  "Ack received on wrong interface");
+		return;
+	}
+
+	if (plen < sizeof(*req)) {
+		fst_printf_session(s, MSG_WARNING,
+				   "Too short FST Ack Request dropped");
+		return;
+	}
+	req = (const struct fst_ack_req *)
+		(((const u8 *) mgmt) + IEEE80211_HDRLEN + 1);
+
+	if (le_to_host32(req->fsts_id) != s->data.fsts_id) {
+		fst_printf_siface(s, iface, MSG_WARNING,
+				  "Ack for wrong FST Setup ID (%u)",
+				  le_to_host32(req->fsts_id));
+		return;
+	}
+
+	os_memset(&res, 0, sizeof(res));
+
+	res.action = FST_ACTION_ACK_RESPONSE;
+	res.dialog_token = req->dialog_token;
+	res.fsts_id = req->fsts_id;
+
+	if (!fst_session_send_action(s, FALSE, &res, sizeof(res), NULL)) {
+		fst_printf_sframe(s, FALSE, MSG_INFO, "FST Ack Response sent");
+		fst_session_stt_disarm(s);
+		fst_session_set_state(s, FST_SESSION_STATE_TRANSITION_DONE,
+				      NULL);
+		fst_session_set_state(s, FST_SESSION_STATE_TRANSITION_CONFIRMED,
+				      NULL);
+		fst_session_set_state(s, FST_SESSION_STATE_INITIAL, &evext);
+	}
+}
+
+
+static void
+fst_session_handle_ack_response(struct fst_session *s,
+				struct fst_iface *iface,
+				const struct ieee80211_mgmt *mgmt,
+				size_t frame_len)
+{
+	const struct fst_ack_res *res;
+	size_t plen = frame_len - IEEE80211_HDRLEN - 1;
+	union fst_session_state_switch_extra evext = {
+		.to_initial = {
+			.reason = REASON_SWITCH,
+			.initiator = FST_INITIATOR_LOCAL,
+		},
+	};
+
+	if (!fst_session_is_switch_requested(s)) {
+		fst_printf_siface(s, iface, MSG_ERROR,
+				  "Ack Response in inappropriate session state (%s)",
+				  fst_session_state_name(s->state));
+		return;
+	}
+
+	WPA_ASSERT(s->data.new_iface != NULL);
+
+	if (iface != s->data.new_iface) {
+		fst_printf_siface(s, iface, MSG_ERROR,
+				  "Ack response received on wrong interface");
+		return;
+	}
+
+	if (plen < sizeof(*res)) {
+		fst_printf_session(s, MSG_WARNING,
+				   "Too short FST Ack Response dropped");
+		return;
+	}
+	res = (const struct fst_ack_res *)
+		(((const u8 *) mgmt) + IEEE80211_HDRLEN + 1);
+
+	if (le_to_host32(res->fsts_id) != s->data.fsts_id) {
+		fst_printf_siface(s, iface, MSG_ERROR,
+				  "Ack response for wrong FST Setup ID (%u)",
+				  le_to_host32(res->fsts_id));
+		return;
+	}
+
+	fst_session_set_state(s, FST_SESSION_STATE_TRANSITION_CONFIRMED, NULL);
+	fst_session_set_state(s, FST_SESSION_STATE_INITIAL, &evext);
+
+	fst_session_stt_disarm(s);
+}
+
+
+struct fst_session * fst_session_create(struct fst_group *g)
+{
+	struct fst_session *s;
+	u32 id;
+
+	WPA_ASSERT(!is_zero_ether_addr(own_addr));
+
+	id = fst_find_free_session_id();
+	if (id == FST_INVALID_SESSION_ID) {
+		fst_printf(MSG_ERROR, "Cannot assign new session ID");
+		return NULL;
+	}
+
+	s = os_zalloc(sizeof(*s));
+	if (!s) {
+		fst_printf(MSG_ERROR, "Cannot allocate new session object");
+		return NULL;
+	}
+
+	s->id = id;
+	s->group = g;
+	s->state = FST_SESSION_STATE_INITIAL;
+
+	s->data.llt_ms = FST_LLT_MS_DEFAULT;
+
+	fst_printf(MSG_INFO, "Session %u created", s->id);
+
+	dl_list_add_tail(&global_sessions_list, &s->global_sessions_lentry);
+
+	foreach_fst_ctrl_call(on_session_added, s);
+
+	return s;
+}
+
+
+void fst_session_set_iface(struct fst_session *s, struct fst_iface *iface,
+			   Boolean is_old)
+{
+	if (is_old)
+		s->data.old_iface = iface;
+	else
+		s->data.new_iface = iface;
+
+}
+
+
+void fst_session_set_llt(struct fst_session *s, u32 llt)
+{
+	s->data.llt_ms = llt;
+}
+
+
+void fst_session_set_peer_addr(struct fst_session *s, const u8 *addr,
+			       Boolean is_old)
+{
+	u8 *a = is_old ? s->data.old_peer_addr : s->data.new_peer_addr;
+
+	os_memcpy(a, addr, ETH_ALEN);
+}
+
+
+int fst_session_initiate_setup(struct fst_session *s)
+{
+	struct fst_setup_req req;
+	int res;
+	u32 fsts_id;
+	u8 dialog_token;
+	struct fst_session *_s;
+
+	if (fst_session_is_in_progress(s)) {
+		fst_printf_session(s, MSG_ERROR, "Session in progress");
+		return -EINVAL;
+	}
+
+	if (is_zero_ether_addr(s->data.old_peer_addr)) {
+		fst_printf_session(s, MSG_ERROR, "No old peer MAC address");
+		return -EINVAL;
+	}
+
+	if (is_zero_ether_addr(s->data.new_peer_addr)) {
+		fst_printf_session(s, MSG_ERROR, "No new peer MAC address");
+		return -EINVAL;
+	}
+
+	if (!s->data.old_iface) {
+		fst_printf_session(s, MSG_ERROR, "No old interface defined");
+		return -EINVAL;
+	}
+
+	if (!s->data.new_iface) {
+		fst_printf_session(s, MSG_ERROR, "No new interface defined");
+		return -EINVAL;
+	}
+
+	if (s->data.new_iface == s->data.old_iface) {
+		fst_printf_session(s, MSG_ERROR,
+				   "Same interface set as old and new");
+		return -EINVAL;
+	}
+
+	if (!fst_iface_is_connected(s->data.old_iface, s->data.old_peer_addr,
+				    FALSE)) {
+		fst_printf_session(s, MSG_ERROR,
+				   "The preset old peer address is not connected");
+		return -EINVAL;
+	}
+
+	if (!fst_iface_is_connected(s->data.new_iface, s->data.new_peer_addr,
+				    FALSE)) {
+		fst_printf_session(s, MSG_ERROR,
+				   "The preset new peer address is not connected");
+		return -EINVAL;
+	}
+
+	_s = fst_find_session_in_progress(s->data.old_peer_addr, s->group);
+	if (_s) {
+		fst_printf_session(s, MSG_ERROR,
+				   "There is another session in progress (old): %u",
+				   _s->id);
+		return -EINVAL;
+	}
+
+	_s = fst_find_session_in_progress(s->data.new_peer_addr, s->group);
+	if (_s) {
+		fst_printf_session(s, MSG_ERROR,
+				   "There is another session in progress (new): %u",
+				   _s->id);
+		return -EINVAL;
+	}
+
+	dialog_token = fst_group_assign_dialog_token(s->group);
+	fsts_id = fst_group_assign_fsts_id(s->group);
+
+	os_memset(&req, 0, sizeof(req));
+
+	fst_printf_siface(s, s->data.old_iface, MSG_INFO,
+		"initiating FST setup for %s (llt=%u ms)",
+		fst_iface_get_name(s->data.new_iface), s->data.llt_ms);
+
+	req.action = FST_ACTION_SETUP_REQUEST;
+	req.dialog_token = dialog_token;
+	req.llt = host_to_le32(FST_LLT_MS_TO_VAL(s->data.llt_ms));
+	/* 8.4.2.147 Session Transition element */
+	req.stie.element_id = WLAN_EID_SESSION_TRANSITION;
+	req.stie.length = sizeof(req.stie) - 2;
+	req.stie.fsts_id = host_to_le32(fsts_id);
+	req.stie.session_control = SESSION_CONTROL(SESSION_TYPE_BSS, 0);
+
+	req.stie.new_band_id = fst_iface_get_band_id(s->data.new_iface);
+	req.stie.new_band_op = 1;
+	req.stie.new_band_setup = 0;
+
+	req.stie.old_band_id = fst_iface_get_band_id(s->data.old_iface);
+	req.stie.old_band_op = 1;
+	req.stie.old_band_setup = 0;
+
+	res = fst_session_send_action(s, TRUE, &req, sizeof(req),
+				      fst_iface_get_mbie(s->data.old_iface));
+	if (!res) {
+		s->data.fsts_id = fsts_id;
+		s->data.pending_setup_req_dlgt = dialog_token;
+		fst_printf_sframe(s, TRUE, MSG_INFO, "FST Setup Request sent");
+		fst_session_set_state(s, FST_SESSION_STATE_SETUP_COMPLETION,
+				      NULL);
+
+		fst_session_stt_arm(s);
+	}
+
+	return res;
+}
+
+
+int fst_session_respond(struct fst_session *s, u8 status_code)
+{
+	struct fst_setup_res res;
+	enum hostapd_hw_mode hw_mode;
+	u8 channel;
+
+	if (!fst_session_is_ready_pending(s)) {
+		fst_printf_session(s, MSG_ERROR, "incorrect state: %s",
+				   fst_session_state_name(s->state));
+		return -EINVAL;
+	}
+
+	if (is_zero_ether_addr(s->data.old_peer_addr)) {
+		fst_printf_session(s, MSG_ERROR, "No peer MAC address");
+		return -EINVAL;
+	}
+
+	if (!s->data.old_iface) {
+		fst_printf_session(s, MSG_ERROR, "No old interface defined");
+		return -EINVAL;
+	}
+
+	if (!s->data.new_iface) {
+		fst_printf_session(s, MSG_ERROR, "No new interface defined");
+		return -EINVAL;
+	}
+
+	if (s->data.new_iface == s->data.old_iface) {
+		fst_printf_session(s, MSG_ERROR,
+				   "Same interface set as old and new");
+		return -EINVAL;
+	}
+
+	if (!fst_iface_is_connected(s->data.old_iface,
+				    s->data.old_peer_addr, FALSE)) {
+		fst_printf_session(s, MSG_ERROR,
+				   "The preset peer address is not in the peer list");
+		return -EINVAL;
+	}
+
+	fst_session_stt_disarm(s);
+
+	os_memset(&res, 0, sizeof(res));
+
+	res.action = FST_ACTION_SETUP_RESPONSE;
+	res.dialog_token = s->data.pending_setup_req_dlgt;
+	res.status_code = status_code;
+
+	res.stie.element_id = WLAN_EID_SESSION_TRANSITION;
+	res.stie.length = sizeof(res.stie) - 2;
+
+	if (status_code == WLAN_STATUS_SUCCESS) {
+		res.stie.fsts_id = s->data.fsts_id;
+		res.stie.session_control = SESSION_CONTROL(SESSION_TYPE_BSS, 0);
+
+		fst_iface_get_channel_info(s->data.new_iface, &hw_mode,
+					   &channel);
+		res.stie.new_band_id = fst_hw_mode_to_band(hw_mode);
+		res.stie.new_band_op = 1;
+		res.stie.new_band_setup = 0;
+
+		fst_iface_get_channel_info(s->data.old_iface, &hw_mode,
+					   &channel);
+		res.stie.old_band_id = fst_hw_mode_to_band(hw_mode);
+		res.stie.old_band_op = 1;
+		res.stie.old_band_setup = 0;
+
+		fst_printf_session(s, MSG_INFO,
+				   "%s: FST Setup Request accepted for %s (llt=%u)",
+				   fst_iface_get_name(s->data.old_iface),
+				   fst_iface_get_name(s->data.new_iface),
+				   s->data.llt_ms);
+	} else {
+		fst_printf_session(s, MSG_WARNING,
+				   "%s: FST Setup Request rejected with code %d",
+				   fst_iface_get_name(s->data.old_iface),
+				   status_code);
+	}
+
+	if (fst_session_send_action(s, TRUE, &res, sizeof(res),
+				    fst_iface_get_mbie(s->data.old_iface))) {
+		fst_printf_sframe(s, TRUE, MSG_ERROR,
+				  "cannot send FST Setup Response with code %d",
+				  status_code);
+		return -EINVAL;
+	}
+
+	fst_printf_sframe(s, TRUE, MSG_INFO, "FST Setup Response sent");
+
+	if (status_code != WLAN_STATUS_SUCCESS) {
+		union fst_session_state_switch_extra evext = {
+			.to_initial = {
+				.reason = REASON_REJECT,
+				.reject_code = status_code,
+				.initiator = FST_INITIATOR_LOCAL,
+			},
+		};
+		fst_session_set_state(s, FST_SESSION_STATE_INITIAL, &evext);
+	}
+
+	return 0;
+}
+
+
+int fst_session_initiate_switch(struct fst_session *s)
+{
+	struct fst_ack_req req;
+	int res;
+	u8 dialog_token;
+
+	if (!fst_session_is_ready(s)) {
+		fst_printf_session(s, MSG_ERROR,
+				   "cannot initiate switch due to wrong setup state (%d)",
+				   s->state);
+		return -1;
+	}
+
+	dialog_token = fst_group_assign_dialog_token(s->group);
+
+	WPA_ASSERT(s->data.new_iface != NULL);
+	WPA_ASSERT(s->data.old_iface != NULL);
+
+	fst_printf_session(s, MSG_INFO, "initiating FST switch: %s => %s",
+			   fst_iface_get_name(s->data.old_iface),
+			   fst_iface_get_name(s->data.new_iface));
+
+	os_memset(&req, 0, sizeof(req));
+
+	req.action = FST_ACTION_ACK_REQUEST;
+	req.dialog_token = dialog_token;
+	req.fsts_id = host_to_le32(s->data.fsts_id);
+
+	res = fst_session_send_action(s, FALSE, &req, sizeof(req), NULL);
+	if (!res) {
+		fst_printf_sframe(s, FALSE, MSG_INFO, "FST Ack Request sent");
+		fst_session_set_state(s, FST_SESSION_STATE_TRANSITION_DONE,
+				      NULL);
+		fst_session_stt_arm(s);
+	} else {
+		fst_printf_sframe(s, FALSE, MSG_ERROR,
+				  "Cannot send FST Ack Request");
+	}
+
+	return res;
+}
+
+
+void fst_session_handle_action(struct fst_session *s,
+			       struct fst_iface *iface,
+			       const struct ieee80211_mgmt *mgmt,
+			       size_t frame_len)
+{
+	switch (mgmt->u.action.u.fst_action.action) {
+	case FST_ACTION_SETUP_REQUEST:
+		WPA_ASSERT(0);
+		break;
+	case FST_ACTION_SETUP_RESPONSE:
+		fst_session_handle_setup_response(s, iface, mgmt, frame_len);
+		break;
+	case FST_ACTION_TEAR_DOWN:
+		fst_session_handle_tear_down(s, iface, mgmt, frame_len);
+		break;
+	case FST_ACTION_ACK_REQUEST:
+		fst_session_handle_ack_request(s, iface, mgmt, frame_len);
+		break;
+	case FST_ACTION_ACK_RESPONSE:
+		fst_session_handle_ack_response(s, iface, mgmt, frame_len);
+		break;
+	case FST_ACTION_ON_CHANNEL_TUNNEL:
+	default:
+		fst_printf_sframe(s, FALSE, MSG_ERROR,
+				  "Unsupported FST Action frame");
+		break;
+	}
+}
+
+
+int fst_session_tear_down_setup(struct fst_session *s)
+{
+	int res;
+	union fst_session_state_switch_extra evext = {
+		.to_initial = {
+			.reason = REASON_TEARDOWN,
+			.initiator = FST_INITIATOR_LOCAL,
+		},
+	};
+
+	res = fst_session_send_tear_down(s);
+
+	fst_session_set_state(s, FST_SESSION_STATE_INITIAL, &evext);
+
+	return res;
+}
+
+
+void fst_session_reset(struct fst_session *s)
+{
+	fst_session_reset_ex(s, REASON_RESET);
+}
+
+
+void fst_session_delete(struct fst_session *s)
+{
+	fst_printf(MSG_INFO, "Session %u deleted", s->id);
+	dl_list_del(&s->global_sessions_lentry);
+	foreach_fst_ctrl_call(on_session_removed, s);
+	os_free(s);
+}
+
+
+struct fst_group * fst_session_get_group(struct fst_session *s)
+{
+	return s->group;
+}
+
+
+struct fst_iface * fst_session_get_iface(struct fst_session *s, Boolean is_old)
+{
+	return is_old ? s->data.old_iface : s->data.new_iface;
+}
+
+
+u32 fst_session_get_id(struct fst_session *s)
+{
+	return s->id;
+}
+
+
+const u8 * fst_session_get_peer_addr(struct fst_session *s, Boolean is_old)
+{
+	return is_old ? s->data.old_peer_addr : s->data.new_peer_addr;
+}
+
+
+u32 fst_session_get_llt(struct fst_session *s)
+{
+	return s->data.llt_ms;
+}
+
+
+enum fst_session_state fst_session_get_state(struct fst_session *s)
+{
+	return s->state;
+}
+
+
+struct fst_session * fst_session_get_by_id(u32 id)
+{
+	struct fst_session *s;
+
+	foreach_fst_session(s) {
+		if (id == s->id)
+			return s;
+	}
+
+	return NULL;
+}
+
+
+void fst_session_enum(struct fst_group *g, fst_session_enum_clb clb, void *ctx)
+{
+	struct fst_session *s;
+
+	foreach_fst_session(s) {
+		if (!g || s->group == g)
+			clb(s->group, s, ctx);
+	}
+}
+
+
+void fst_session_on_action_rx(struct fst_iface *iface,
+			      const struct ieee80211_mgmt *mgmt,
+			      size_t len)
+{
+	struct fst_session *s;
+
+	if (len < IEEE80211_HDRLEN + 2 ||
+	    mgmt->u.action.category != WLAN_ACTION_FST) {
+		fst_printf_iface(iface, MSG_ERROR,
+				 "invalid Action frame received");
+		return;
+	}
+
+	if (mgmt->u.action.u.fst_action.action <= FST_ACTION_MAX_SUPPORTED) {
+		fst_printf_iface(iface, MSG_DEBUG,
+				 "FST Action '%s' received!",
+				 fst_action_names[mgmt->u.action.u.fst_action.action]);
+	} else {
+		fst_printf_iface(iface, MSG_WARNING,
+				 "unknown FST Action (%u) received!",
+				 mgmt->u.action.u.fst_action.action);
+		return;
+	}
+
+	if (mgmt->u.action.u.fst_action.action == FST_ACTION_SETUP_REQUEST) {
+		fst_session_handle_setup_request(iface, mgmt, len);
+		return;
+	}
+
+	s = fst_find_session_in_progress(mgmt->sa, fst_iface_get_group(iface));
+	if (s) {
+		fst_session_handle_action(s, iface, mgmt, len);
+	} else {
+		fst_printf_iface(iface, MSG_WARNING,
+				 "FST Action '%s' dropped: no session in progress found",
+				 fst_action_names[mgmt->u.action.u.fst_action.action]);
+	}
+}
+
+
+int fst_session_set_str_ifname(struct fst_session *s, const char *ifname,
+			       Boolean is_old)
+{
+	struct fst_group *g = fst_session_get_group(s);
+	struct fst_iface *i;
+
+	i = fst_group_get_iface_by_name(g, ifname);
+	if (!i) {
+		fst_printf_session(s, MSG_WARNING,
+				   "Cannot set iface %s: no such iface within group '%s'",
+				   ifname, fst_group_get_id(g));
+		return -1;
+	}
+
+	fst_session_set_iface(s, i, is_old);
+
+	return 0;
+}
+
+
+int fst_session_set_str_peer_addr(struct fst_session *s, const char *mac,
+				  Boolean is_old)
+{
+	u8 peer_addr[ETH_ALEN];
+	int res = fst_read_peer_addr(mac, peer_addr);
+
+	if (res)
+		return res;
+
+	fst_session_set_peer_addr(s, peer_addr, is_old);
+
+	return 0;
+}
+
+
+int fst_session_set_str_llt(struct fst_session *s, const char *llt_str)
+{
+	char *endp;
+	long int llt = strtol(llt_str, &endp, 0);
+
+	if (*endp || llt < 0 || (unsigned long int) llt > FST_MAX_LLT_MS) {
+		fst_printf_session(s, MSG_WARNING,
+				   "Cannot set llt %s: Invalid llt value (1..%u expected)",
+				   llt_str, FST_MAX_LLT_MS);
+		return -1;
+	}
+	fst_session_set_llt(s, (u32) llt);
+
+	return 0;
+}
+
+
+void fst_session_global_on_iface_detached(struct fst_iface *iface)
+{
+	struct fst_session *s;
+
+	foreach_fst_session(s) {
+		if (fst_session_is_in_progress(s) &&
+		    (s->data.new_iface == iface ||
+		     s->data.old_iface == iface))
+			fst_session_reset_ex(s, REASON_DETACH_IFACE);
+	}
+}
+
+
+struct fst_session * fst_session_global_get_first_by_group(struct fst_group *g)
+{
+	struct fst_session *s;
+
+	foreach_fst_session(s) {
+		if (s->group == g)
+			return s;
+	}
+
+	return NULL;
+}
+
+
+#ifdef CONFIG_FST_TEST
+
+static int get_group_fill_session(struct fst_group **g, struct fst_session *s)
+{
+	const u8 *old_addr, *new_addr;
+	struct fst_get_peer_ctx *ctx;
+
+	os_memset(s, 0, sizeof(*s));
+	foreach_fst_group(*g) {
+		s->data.new_iface = fst_group_first_iface(*g);
+		if (s->data.new_iface)
+			break;
+	}
+	if (!s->data.new_iface)
+		return -EINVAL;
+
+	s->data.old_iface = dl_list_entry(s->data.new_iface->group_lentry.next,
+					  struct fst_iface, group_lentry);
+	if (!s->data.old_iface)
+		return -EINVAL;
+
+	old_addr = fst_iface_get_peer_first(s->data.old_iface, &ctx, TRUE);
+	if (!old_addr)
+		return -EINVAL;
+
+	new_addr = fst_iface_get_peer_first(s->data.new_iface, &ctx, TRUE);
+	if (!new_addr)
+		return -EINVAL;
+
+	os_memcpy(s->data.old_peer_addr, old_addr, ETH_ALEN);
+	os_memcpy(s->data.new_peer_addr, new_addr, ETH_ALEN);
+
+	return 0;
+}
+
+
+#define FST_MAX_COMMAND_WORD_NAME_LENGTH 16
+
+int fst_test_req_send_fst_request(const char *params)
+{
+	int fsts_id;
+	Boolean is_valid;
+	char *endp;
+	struct fst_setup_req req;
+	struct fst_session s;
+	struct fst_group *g;
+	enum hostapd_hw_mode hw_mode;
+	u8 channel;
+	char additional_param[FST_MAX_COMMAND_WORD_NAME_LENGTH];
+
+	if (params[0] != ' ')
+		return -EINVAL;
+	params++;
+	fsts_id = fst_read_next_int_param(params, &is_valid, &endp);
+	if (!is_valid)
+		return -EINVAL;
+
+	if (get_group_fill_session(&g, &s))
+		return -EINVAL;
+
+	req.action = FST_ACTION_SETUP_REQUEST;
+	req.dialog_token = g->dialog_token;
+	req.llt = host_to_le32(FST_LLT_MS_DEFAULT);
+	/* 8.4.2.147 Session Transition element */
+	req.stie.element_id = WLAN_EID_SESSION_TRANSITION;
+	req.stie.length = sizeof(req.stie) - 2;
+	req.stie.fsts_id = host_to_le32(fsts_id);
+	req.stie.session_control = SESSION_CONTROL(SESSION_TYPE_BSS, 0);
+
+	fst_iface_get_channel_info(s.data.new_iface, &hw_mode, &channel);
+	req.stie.new_band_id = fst_hw_mode_to_band(hw_mode);
+	req.stie.new_band_op = 1;
+	req.stie.new_band_setup = 0;
+
+	fst_iface_get_channel_info(s.data.old_iface, &hw_mode, &channel);
+	req.stie.old_band_id = fst_hw_mode_to_band(hw_mode);
+	req.stie.old_band_op = 1;
+	req.stie.old_band_setup = 0;
+
+	if (!fst_read_next_text_param(endp, additional_param,
+				       sizeof(additional_param), &endp)) {
+		if (!os_strcasecmp(additional_param, FST_CTR_PVAL_BAD_NEW_BAND))
+			req.stie.new_band_id = req.stie.old_band_id;
+	}
+
+	return fst_session_send_action(&s, TRUE, &req, sizeof(req),
+				       s.data.old_iface->mb_ie);
+}
+
+
+int fst_test_req_send_fst_response(const char *params)
+{
+	int fsts_id;
+	Boolean is_valid;
+	char *endp;
+	struct fst_setup_res res;
+	struct fst_session s;
+	struct fst_group *g;
+	enum hostapd_hw_mode hw_mode;
+	u8 status_code;
+	u8 channel;
+	char response[FST_MAX_COMMAND_WORD_NAME_LENGTH];
+	struct fst_session *_s;
+
+	if (params[0] != ' ')
+		return -EINVAL;
+	params++;
+	fsts_id = fst_read_next_int_param(params, &is_valid, &endp);
+	if (!is_valid)
+		return -EINVAL;
+
+	if (get_group_fill_session(&g, &s))
+		return -EINVAL;
+
+	status_code = WLAN_STATUS_SUCCESS;
+	if (!fst_read_next_text_param(endp, response, sizeof(response),
+				      &endp)) {
+		if (!os_strcasecmp(response, FST_CS_PVAL_RESPONSE_REJECT))
+			status_code = WLAN_STATUS_PENDING_ADMITTING_FST_SESSION;
+	}
+
+	os_memset(&res, 0, sizeof(res));
+
+	res.action = FST_ACTION_SETUP_RESPONSE;
+	/*
+	 * If some session has just received an FST Setup Request, then
+	 * use the correct dialog token copied from this request.
+	 */
+	_s = fst_find_session_in_progress(fst_session_get_peer_addr(&s, TRUE),
+					  g);
+	res.dialog_token = (_s && fst_session_is_ready_pending(_s)) ?
+		_s->data.pending_setup_req_dlgt : g->dialog_token;
+	res.status_code  = status_code;
+
+	res.stie.element_id = WLAN_EID_SESSION_TRANSITION;
+	res.stie.length = sizeof(res.stie) - 2;
+
+	if (res.status_code == WLAN_STATUS_SUCCESS) {
+		res.stie.fsts_id = fsts_id;
+		res.stie.session_control = SESSION_CONTROL(SESSION_TYPE_BSS, 0);
+
+		fst_iface_get_channel_info(s.data.new_iface, &hw_mode,
+					    &channel);
+		res.stie.new_band_id = fst_hw_mode_to_band(hw_mode);
+		res.stie.new_band_op = 1;
+		res.stie.new_band_setup = 0;
+
+		fst_iface_get_channel_info(s.data.old_iface, &hw_mode,
+					   &channel);
+		res.stie.old_band_id = fst_hw_mode_to_band(hw_mode);
+		res.stie.old_band_op = 1;
+		res.stie.old_band_setup = 0;
+	}
+
+	if (!fst_read_next_text_param(endp, response, sizeof(response),
+				      &endp)) {
+		if (!os_strcasecmp(response, FST_CTR_PVAL_BAD_NEW_BAND))
+			res.stie.new_band_id = res.stie.old_band_id;
+	}
+
+	return fst_session_send_action(&s, TRUE, &res, sizeof(res),
+				       s.data.old_iface->mb_ie);
+}
+
+
+int fst_test_req_send_ack_request(const char *params)
+{
+	int fsts_id;
+	Boolean is_valid;
+	char *endp;
+	struct fst_ack_req req;
+	struct fst_session s;
+	struct fst_group *g;
+
+	if (params[0] != ' ')
+		return -EINVAL;
+	params++;
+	fsts_id = fst_read_next_int_param(params, &is_valid, &endp);
+	if (!is_valid)
+		return -EINVAL;
+
+	if (get_group_fill_session(&g, &s))
+		return -EINVAL;
+
+	os_memset(&req, 0, sizeof(req));
+	req.action = FST_ACTION_ACK_REQUEST;
+	req.dialog_token = g->dialog_token;
+	req.fsts_id = fsts_id;
+
+	return fst_session_send_action(&s, FALSE, &req, sizeof(req), NULL);
+}
+
+
+int fst_test_req_send_ack_response(const char *params)
+{
+	int fsts_id;
+	Boolean is_valid;
+	char *endp;
+	struct fst_ack_res res;
+	struct fst_session s;
+	struct fst_group *g;
+
+	if (params[0] != ' ')
+		return -EINVAL;
+	params++;
+	fsts_id = fst_read_next_int_param(params, &is_valid, &endp);
+	if (!is_valid)
+		return -EINVAL;
+
+	if (get_group_fill_session(&g, &s))
+		return -EINVAL;
+
+	os_memset(&res, 0, sizeof(res));
+	res.action = FST_ACTION_ACK_RESPONSE;
+	res.dialog_token = g->dialog_token;
+	res.fsts_id = fsts_id;
+
+	return fst_session_send_action(&s, FALSE, &res, sizeof(res), NULL);
+}
+
+
+int fst_test_req_send_tear_down(const char *params)
+{
+	int fsts_id;
+	Boolean is_valid;
+	char *endp;
+	struct fst_tear_down td;
+	struct fst_session s;
+	struct fst_group *g;
+
+	if (params[0] != ' ')
+		return -EINVAL;
+	params++;
+	fsts_id = fst_read_next_int_param(params, &is_valid, &endp);
+	if (!is_valid)
+		return -EINVAL;
+
+	if (get_group_fill_session(&g, &s))
+		return -EINVAL;
+
+	os_memset(&td, 0, sizeof(td));
+	td.action = FST_ACTION_TEAR_DOWN;
+	td.fsts_id = fsts_id;
+
+	return fst_session_send_action(&s, TRUE, &td, sizeof(td), NULL);
+}
+
+
+u32 fst_test_req_get_fsts_id(const char *params)
+{
+	int sid;
+	Boolean is_valid;
+	char *endp;
+	struct fst_session *s;
+
+	if (params[0] != ' ')
+		return FST_FSTS_ID_NOT_FOUND;
+	params++;
+	sid = fst_read_next_int_param(params, &is_valid, &endp);
+	if (!is_valid)
+		return FST_FSTS_ID_NOT_FOUND;
+
+	s = fst_session_get_by_id(sid);
+	if (!s)
+		return FST_FSTS_ID_NOT_FOUND;
+
+	return s->data.fsts_id;
+}
+
+
+int fst_test_req_get_local_mbies(const char *request, char *buf, size_t buflen)
+{
+	char *endp;
+	char ifname[FST_MAX_COMMAND_WORD_NAME_LENGTH];
+	struct fst_group *g;
+	struct fst_iface *iface;
+
+	if (request[0] != ' ')
+		return -EINVAL;
+	request++;
+	if (fst_read_next_text_param(request, ifname, sizeof(ifname), &endp) ||
+	    !*ifname)
+		goto problem;
+	g = dl_list_first(&fst_global_groups_list, struct fst_group,
+			  global_groups_lentry);
+	if (!g)
+		goto problem;
+	iface = fst_group_get_iface_by_name(g, ifname);
+	if (!iface || !iface->mb_ie)
+		goto problem;
+	return wpa_snprintf_hex(buf, buflen, wpabuf_head(iface->mb_ie),
+				wpabuf_len(iface->mb_ie));
+
+problem:
+	return os_snprintf(buf, buflen, "FAIL\n");
+}
+
+#endif /* CONFIG_FST_TEST */
diff --git a/src/fst/fst_session.h b/src/fst/fst_session.h
new file mode 100644
index 0000000..1162de4
--- /dev/null
+++ b/src/fst/fst_session.h
@@ -0,0 +1,80 @@
+/*
+ * FST module - FST Session related definitions
+ * Copyright (c) 2014, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef FST_SESSION_H
+#define FST_SESSION_H
+
+#define FST_DEFAULT_SESSION_TIMEOUT_TU 255 /* u8 */
+
+struct fst_iface;
+struct fst_group;
+struct fst_session;
+enum fst_session_state;
+
+int  fst_session_global_init(void);
+void fst_session_global_deinit(void);
+void fst_session_global_on_iface_detached(struct fst_iface *iface);
+struct fst_session *
+fst_session_global_get_first_by_group(struct fst_group *g);
+
+struct fst_session * fst_session_create(struct fst_group *g);
+void fst_session_set_iface(struct fst_session *s, struct fst_iface *iface,
+			   Boolean is_old);
+void fst_session_set_llt(struct fst_session *s, u32 llt);
+void fst_session_set_peer_addr(struct fst_session *s, const u8 *addr,
+			       Boolean is_old);
+int fst_session_initiate_setup(struct fst_session *s);
+int fst_session_respond(struct fst_session *s, u8 status_code);
+int fst_session_initiate_switch(struct fst_session *s);
+void fst_session_handle_action(struct fst_session *s, struct fst_iface *iface,
+			       const struct ieee80211_mgmt *mgmt,
+			       size_t frame_len);
+int fst_session_tear_down_setup(struct fst_session *s);
+void fst_session_reset(struct fst_session *s);
+void fst_session_delete(struct fst_session *s);
+
+struct fst_group * fst_session_get_group(struct fst_session *s);
+struct fst_iface * fst_session_get_iface(struct fst_session *s, Boolean is_old);
+const u8 * fst_session_get_peer_addr(struct fst_session *s, Boolean is_old);
+u32 fst_session_get_id(struct fst_session *s);
+u32 fst_session_get_llt(struct fst_session *s);
+enum fst_session_state fst_session_get_state(struct fst_session *s);
+
+struct fst_session *fst_session_get_by_id(u32 id);
+
+typedef void (*fst_session_enum_clb)(struct fst_group *g, struct fst_session *s,
+				     void *ctx);
+
+void fst_session_enum(struct fst_group *g, fst_session_enum_clb clb, void *ctx);
+
+void fst_session_on_action_rx(struct fst_iface *iface,
+			      const struct ieee80211_mgmt *mgmt, size_t len);
+
+
+int fst_session_set_str_ifname(struct fst_session *s, const char *ifname,
+			       Boolean is_old);
+int fst_session_set_str_peer_addr(struct fst_session *s, const char *mac,
+				  Boolean is_old);
+int fst_session_set_str_llt(struct fst_session *s, const char *llt_str);
+
+#ifdef CONFIG_FST_TEST
+
+#define FST_FSTS_ID_NOT_FOUND ((u32) -1)
+
+int fst_test_req_send_fst_request(const char *params);
+int fst_test_req_send_fst_response(const char *params);
+int fst_test_req_send_ack_request(const char *params);
+int fst_test_req_send_ack_response(const char *params);
+int fst_test_req_send_tear_down(const char *params);
+u32 fst_test_req_get_fsts_id(const char *params);
+int fst_test_req_get_local_mbies(const char *request, char *buf,
+				 size_t buflen);
+
+#endif /* CONFIG_FST_TEST */
+
+#endif /* FST_SESSION_H */
diff --git a/src/l2_packet/l2_packet_linux.c b/src/l2_packet/l2_packet_linux.c
index 41de2f8..f52f7a2 100644
--- a/src/l2_packet/l2_packet_linux.c
+++ b/src/l2_packet/l2_packet_linux.c
@@ -30,11 +30,13 @@
 	int l2_hdr; /* whether to include layer 2 (Ethernet) header data
 		     * buffers */
 
+#ifndef CONFIG_NO_LINUX_PACKET_SOCKET_WAR
 	/* For working around Linux packet socket behavior and regression. */
 	int fd_br_rx;
 	int last_from_br;
 	u8 last_hash[SHA1_MAC_LEN];
-	unsigned int num_rx, num_rx_br;
+	unsigned int num_rx_br;
+#endif /* CONFIG_NO_LINUX_PACKET_SOCKET_WAR */
 };
 
 /* Generated by 'sudo tcpdump -s 3000 -dd greater 278 and ip and udp and
@@ -127,7 +129,6 @@
 	struct sockaddr_ll ll;
 	socklen_t fromlen;
 
-	l2->num_rx++;
 	os_memset(&ll, 0, sizeof(ll));
 	fromlen = sizeof(ll);
 	res = recvfrom(sock, buf, sizeof(buf), 0, (struct sockaddr *) &ll,
@@ -141,6 +142,7 @@
 	wpa_printf(MSG_DEBUG, "%s: src=" MACSTR " len=%d",
 		   __func__, MAC2STR(ll.sll_addr), (int) res);
 
+#ifndef CONFIG_NO_LINUX_PACKET_SOCKET_WAR
 	if (l2->fd_br_rx >= 0) {
 		u8 hash[SHA1_MAC_LEN];
 		const u8 *addr[1];
@@ -173,10 +175,12 @@
 	}
 
 	l2->last_from_br = 0;
+#endif /* CONFIG_NO_LINUX_PACKET_SOCKET_WAR */
 	l2->rx_callback(l2->rx_callback_ctx, ll.sll_addr, buf, res);
 }
 
 
+#ifndef CONFIG_NO_LINUX_PACKET_SOCKET_WAR
 static void l2_packet_receive_br(int sock, void *eloop_ctx, void *sock_ctx)
 {
 	struct l2_packet_data *l2 = eloop_ctx;
@@ -214,6 +218,7 @@
 	os_memcpy(l2->last_hash, hash, SHA1_MAC_LEN);
 	l2->rx_callback(l2->rx_callback_ctx, ll.sll_addr, buf, res);
 }
+#endif /* CONFIG_NO_LINUX_PACKET_SOCKET_WAR */
 
 
 struct l2_packet_data * l2_packet_init(
@@ -233,7 +238,9 @@
 	l2->rx_callback = rx_callback;
 	l2->rx_callback_ctx = rx_callback_ctx;
 	l2->l2_hdr = l2_hdr;
+#ifndef CONFIG_NO_LINUX_PACKET_SOCKET_WAR
 	l2->fd_br_rx = -1;
+#endif /* CONFIG_NO_LINUX_PACKET_SOCKET_WAR */
 
 	l2->fd = socket(PF_PACKET, l2_hdr ? SOCK_RAW : SOCK_DGRAM,
 			htons(protocol));
@@ -289,6 +296,7 @@
 	void *rx_callback_ctx, int l2_hdr)
 {
 	struct l2_packet_data *l2;
+#ifndef CONFIG_NO_LINUX_PACKET_SOCKET_WAR
 	struct sock_filter ethertype_sock_filter_insns[] = {
 		/* Load ethertype */
 		BPF_STMT(BPF_LD | BPF_H | BPF_ABS, 2 * ETH_ALEN),
@@ -304,12 +312,14 @@
 		.filter = ethertype_sock_filter_insns,
 	};
 	struct sockaddr_ll ll;
+#endif /* CONFIG_NO_LINUX_PACKET_SOCKET_WAR */
 
 	l2 = l2_packet_init(br_ifname, own_addr, protocol, rx_callback,
 			    rx_callback_ctx, l2_hdr);
 	if (!l2)
 		return NULL;
 
+#ifndef CONFIG_NO_LINUX_PACKET_SOCKET_WAR
 	/*
 	 * The Linux packet socket behavior has changed over the years and there
 	 * is an inconvenient regression in it that breaks RX for a specific
@@ -357,6 +367,7 @@
 	}
 
 	eloop_register_read_sock(l2->fd_br_rx, l2_packet_receive_br, l2, NULL);
+#endif /* CONFIG_NO_LINUX_PACKET_SOCKET_WAR */
 
 	return l2;
 }
@@ -372,10 +383,12 @@
 		close(l2->fd);
 	}
 
+#ifndef CONFIG_NO_LINUX_PACKET_SOCKET_WAR
 	if (l2->fd_br_rx >= 0) {
 		eloop_unregister_read_sock(l2->fd_br_rx);
 		close(l2->fd_br_rx);
 	}
+#endif /* CONFIG_NO_LINUX_PACKET_SOCKET_WAR */
 
 	os_free(l2);
 }
diff --git a/src/p2p/p2p.c b/src/p2p/p2p.c
index b87ff96..d901169 100644
--- a/src/p2p/p2p.c
+++ b/src/p2p/p2p.c
@@ -48,9 +48,8 @@
 #define P2P_PEER_EXPIRATION_AGE 60
 #endif /* P2P_PEER_EXPIRATION_AGE */
 
-#define P2P_PEER_EXPIRATION_INTERVAL (P2P_PEER_EXPIRATION_AGE / 2)
 
-static void p2p_expire_peers(struct p2p_data *p2p)
+void p2p_expire_peers(struct p2p_data *p2p)
 {
 	struct p2p_device *dev, *n;
 	struct os_reltime now;
@@ -103,15 +102,6 @@
 }
 
 
-static void p2p_expiration_timeout(void *eloop_ctx, void *timeout_ctx)
-{
-	struct p2p_data *p2p = eloop_ctx;
-	p2p_expire_peers(p2p);
-	eloop_register_timeout(P2P_PEER_EXPIRATION_INTERVAL, 0,
-			       p2p_expiration_timeout, p2p, NULL);
-}
-
-
 static const char * p2p_state_txt(int state)
 {
 	switch (state) {
@@ -455,8 +445,9 @@
 static void p2p_copy_client_info(struct p2p_device *dev,
 				 struct p2p_client_info *cli)
 {
-	os_memcpy(dev->info.device_name, cli->dev_name, cli->dev_name_len);
-	dev->info.device_name[cli->dev_name_len] = '\0';
+	p2p_copy_filter_devname(dev->info.device_name,
+				sizeof(dev->info.device_name),
+				cli->dev_name, cli->dev_name_len);
 	dev->info.dev_capab = cli->dev_capab;
 	dev->info.config_methods = cli->config_methods;
 	os_memcpy(dev->info.pri_dev_type, cli->pri_dev_type, 8);
@@ -646,11 +637,11 @@
 
 	end = ies + ies_len;
 
-	for (pos = ies; pos + 1 < end; pos += len) {
+	for (pos = ies; end - pos > 1; pos += len) {
 		id = *pos++;
 		len = *pos++;
 
-		if (pos + len > end)
+		if (len > end - pos)
 			break;
 
 		if (id != WLAN_EID_VENDOR_SPECIFIC || len < 3)
@@ -1142,7 +1133,7 @@
 	if (adv_len >= sizeof(str_buf))
 		return 0;
 
-	for (i = 0; str[i] && i < adv_len; i++) {
+	for (i = 0; i < adv_len; i++) {
 		if (str[i] >= 'A' && str[i] <= 'Z')
 			str_buf[i] = str[i] - 'A' + 'a';
 		else
@@ -1469,7 +1460,7 @@
 
 
 /**
- * p2p_prepare_channel - Select operating channel for GO Negotiation
+ * p2p_prepare_channel - Select operating channel for GO Negotiation or P2PS PD
  * @p2p: P2P module context from p2p_init()
  * @dev: Selected peer device
  * @force_freq: Forced frequency in MHz or 0 if not forced
@@ -1478,9 +1469,9 @@
  * Returns: 0 on success, -1 on failure (channel not supported for P2P)
  *
  * This function is used to do initial operating channel selection for GO
- * Negotiation prior to having received peer information. The selected channel
- * may be further optimized in p2p_reselect_channel() once the peer information
- * is available.
+ * Negotiation prior to having received peer information or for P2PS PD
+ * signalling. The selected channel may be further optimized in
+ * p2p_reselect_channel() once the peer information is available.
  */
 int p2p_prepare_channel(struct p2p_data *p2p, struct p2p_device *dev,
 			unsigned int force_freq, unsigned int pref_freq, int go)
@@ -2689,13 +2680,14 @@
 
 int p2p_service_add_asp(struct p2p_data *p2p, int auto_accept, u32 adv_id,
 			const char *adv_str, u8 svc_state, u16 config_methods,
-			const char *svc_info)
+			const char *svc_info, const u8 *cpt_priority)
 {
 	struct p2ps_advertisement *adv_data, *tmp, **prev;
 	u8 buf[P2PS_HASH_LEN];
 	size_t adv_data_len, adv_len, info_len = 0;
+	int i;
 
-	if (!p2p || !adv_str || !adv_str[0])
+	if (!p2p || !adv_str || !adv_str[0] || !cpt_priority)
 		return -1;
 
 	if (!(config_methods & p2p->cfg->config_methods)) {
@@ -2724,6 +2716,11 @@
 	adv_data->auto_accept = (u8) auto_accept;
 	os_memcpy(adv_data->svc_name, adv_str, adv_len);
 
+	for (i = 0; cpt_priority[i] && i < P2PS_FEATURE_CAPAB_CPT_MAX; i++) {
+		adv_data->cpt_priority[i] = cpt_priority[i];
+		adv_data->cpt_mask |= cpt_priority[i];
+	}
+
 	if (svc_info && info_len) {
 		adv_data->svc_info = &adv_data->svc_name[adv_len + 1];
 		os_memcpy(adv_data->svc_info, svc_info, info_len);
@@ -2762,8 +2759,9 @@
 
 inserted:
 	p2p_dbg(p2p,
-		"Added ASP advertisement adv_id=0x%x config_methods=0x%x svc_state=0x%x adv_str='%s'",
-		adv_id, adv_data->config_methods, svc_state, adv_str);
+		"Added ASP advertisement adv_id=0x%x config_methods=0x%x svc_state=0x%x adv_str='%s' cpt_mask=0x%x",
+		adv_id, adv_data->config_methods, svc_state, adv_str,
+		adv_data->cpt_mask);
 
 	return 0;
 }
@@ -2919,9 +2917,6 @@
 
 	dl_list_init(&p2p->devices);
 
-	eloop_register_timeout(P2P_PEER_EXPIRATION_INTERVAL, 0,
-			       p2p_expiration_timeout, p2p, NULL);
-
 	p2p->go_timeout = 100;
 	p2p->client_timeout = 20;
 	p2p->num_p2p_sd_queries = 0;
@@ -2950,8 +2945,6 @@
 	wpabuf_free(p2p->wfd_coupled_sink_info);
 #endif /* CONFIG_WIFI_DISPLAY */
 
-	eloop_cancel_timeout(p2p_expiration_timeout, p2p, NULL);
-	eloop_cancel_timeout(p2p_ext_listen_timeout, p2p, NULL);
 	eloop_cancel_timeout(p2p_scan_timeout, p2p, NULL);
 	eloop_cancel_timeout(p2p_go_neg_start, p2p, NULL);
 	eloop_cancel_timeout(p2p_go_neg_wait_timeout, p2p, NULL);
@@ -2978,6 +2971,8 @@
 void p2p_flush(struct p2p_data *p2p)
 {
 	struct p2p_device *dev, *prev;
+
+	p2p_ext_listen(p2p, 0, 0);
 	p2p_stop_find(p2p);
 	dl_list_for_each_safe(dev, prev, &p2p->devices, struct p2p_device,
 			      list) {
@@ -2987,6 +2982,7 @@
 	p2p_free_sd_queries(p2p);
 	os_free(p2p->after_scan_tx);
 	p2p->after_scan_tx = NULL;
+	p2p->ssid_set = 0;
 }
 
 
@@ -3332,6 +3328,43 @@
 	}
 
 	/*
+	 * If after PD Request the peer doesn't expect to receive PD Response
+	 * the PD Request ACK indicates a completion of the current PD. This
+	 * happens only on the advertiser side sending the follow-on PD Request
+	 * with the status different than 12 (Success: accepted by user).
+	 */
+	if (p2p->p2ps_prov && !p2p->p2ps_prov->pd_seeker &&
+	    p2p->p2ps_prov->status != P2P_SC_SUCCESS_DEFERRED) {
+		p2p_dbg(p2p, "P2PS PD completion on Follow-on PD Request ACK");
+
+		if (p2p->send_action_in_progress) {
+			p2p->send_action_in_progress = 0;
+			p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
+		}
+
+		p2p->pending_action_state = P2P_NO_PENDING_ACTION;
+
+		if (p2p->cfg->p2ps_prov_complete) {
+			p2p->cfg->p2ps_prov_complete(
+				p2p->cfg->cb_ctx,
+				p2p->p2ps_prov->status,
+				p2p->p2ps_prov->adv_mac,
+				p2p->p2ps_prov->adv_mac,
+				p2p->p2ps_prov->session_mac,
+				NULL, p2p->p2ps_prov->adv_id,
+				p2p->p2ps_prov->session_id,
+				0, 0, NULL, 0, 0, 0,
+				NULL, NULL, 0, 0);
+		}
+
+		if (p2p->user_initiated_pd)
+			p2p_reset_pending_pd(p2p);
+
+		p2ps_prov_free(p2p);
+		return;
+	}
+
+	/*
 	 * This postponing, of resetting pending_action_state, needs to be
 	 * done only for user initiated PD requests and not internal ones.
 	 */
@@ -5389,3 +5422,20 @@
 		"Timeout on waiting peer to become ready for GO Negotiation");
 	p2p_go_neg_failed(p2p, -1);
 }
+
+
+void p2p_set_own_pref_freq_list(struct p2p_data *p2p,
+				const unsigned int *pref_freq_list,
+				unsigned int size)
+{
+	unsigned int i;
+
+	if (size > P2P_MAX_PREF_CHANNELS)
+		size = P2P_MAX_PREF_CHANNELS;
+	p2p->num_pref_freq = size;
+	for (i = 0; i < size; i++) {
+		p2p->pref_freq_list[i] = pref_freq_list[i];
+		p2p_dbg(p2p, "Own preferred frequency list[%u]=%u MHz",
+			i, p2p->pref_freq_list[i]);
+	}
+}
diff --git a/src/p2p/p2p.h b/src/p2p/p2p.h
index 67b8bdb..bfdb2c9 100644
--- a/src/p2p/p2p.h
+++ b/src/p2p/p2p.h
@@ -21,11 +21,17 @@
 #define P2PS_WILD_HASH_STR "org.wi-fi.wfds"
 #define P2PS_HASH_LEN 6
 #define P2P_MAX_QUERY_HASH 6
+#define P2PS_FEATURE_CAPAB_CPT_MAX 2
+
+/**
+ * P2P_MAX_PREF_CHANNELS - Maximum number of preferred channels
+ */
+#define P2P_MAX_PREF_CHANNELS 100
 
 /**
  * P2P_MAX_REG_CLASSES - Maximum number of regulatory classes
  */
-#define P2P_MAX_REG_CLASSES 10
+#define P2P_MAX_REG_CLASSES 15
 
 /**
  * P2P_MAX_REG_CLASS_CHANNELS - Maximum number of channels per regulatory class
@@ -93,6 +99,10 @@
 
 	int vht;
 
+	u8 max_oper_chwidth;
+
+	unsigned int vht_center_freq2;
+
 	/**
 	 * ssid - SSID of the group
 	 */
@@ -156,6 +166,11 @@
 
 struct p2ps_provision {
 	/**
+	 * pd_seeker - P2PS provision discovery seeker role
+	 */
+	unsigned int pd_seeker:1;
+
+	/**
 	 * status - Remote returned provisioning status code
 	 */
 	int status;
@@ -196,6 +211,33 @@
 	u8 adv_mac[ETH_ALEN];
 
 	/**
+	 * cpt_mask - Supported Coordination Protocol Transport mask
+	 *
+	 * A bitwise mask of supported ASP Coordination Protocol Transports.
+	 * This property is set together and corresponds with cpt_priority.
+	 */
+	u8 cpt_mask;
+
+	/**
+	 * cpt_priority - Coordination Protocol Transport priority list
+	 *
+	 * Priorities of supported ASP Coordination Protocol Transports.
+	 * This property is set together and corresponds with cpt_mask.
+	 * The CPT priority list is 0 terminated.
+	 */
+	u8 cpt_priority[P2PS_FEATURE_CAPAB_CPT_MAX + 1];
+
+	/**
+	 * force_freq - The only allowed channel frequency in MHz or 0.
+	 */
+	unsigned int force_freq;
+
+	/**
+	 * pref_freq - Preferred operating frequency in MHz or 0.
+	 */
+	unsigned int pref_freq;
+
+	/**
 	 * info - Vendor defined extra Provisioning information
 	 */
 	char info[0];
@@ -235,6 +277,23 @@
 	u8 hash[P2PS_HASH_LEN];
 
 	/**
+	 * cpt_mask - supported Coordination Protocol Transport mask
+	 *
+	 * A bitwise mask of supported ASP Coordination Protocol Transports.
+	 * This property is set together and corresponds with cpt_priority.
+	 */
+	u8 cpt_mask;
+
+	/**
+	 * cpt_priority - Coordination Protocol Transport priority list
+	 *
+	 * Priorities of supported ASP Coordinatin Protocol Transports.
+	 * This property is set together and corresponds with cpt_mask.
+	 * The CPT priority list is 0 terminated.
+	 */
+	u8 cpt_priority[P2PS_FEATURE_CAPAB_CPT_MAX + 1];
+
+	/**
 	 * svc_name - NULL Terminated UTF-8 Service Name, and svc_info storage
 	 */
 	char svc_name[0];
@@ -955,18 +1014,21 @@
 
 	/**
 	 * Determine if we have a persistent group we share with remote peer
+	 * and allocate interface for this group if needed
 	 * @ctx: Callback context from cb_ctx
 	 * @addr: Peer device address to search for
 	 * @ssid: Persistent group SSID or %NULL if any
 	 * @ssid_len: Length of @ssid
-	 * @go_dev_addr: Buffer for returning intended GO P2P Device Address
+	 * @go_dev_addr: Buffer for returning GO P2P Device Address
 	 * @ret_ssid: Buffer for returning group SSID
 	 * @ret_ssid_len: Buffer for returning length of @ssid
+	 * @intended_iface_addr: Buffer for returning intended iface address
 	 * Returns: 1 if a matching persistent group was found, 0 otherwise
 	 */
 	int (*get_persistent_group)(void *ctx, const u8 *addr, const u8 *ssid,
 				    size_t ssid_len, u8 *go_dev_addr,
-				    u8 *ret_ssid, size_t *ret_ssid_len);
+				    u8 *ret_ssid, size_t *ret_ssid_len,
+				    u8 *intended_iface_addr);
 
 	/**
 	 * Get information about a possible local GO role
@@ -976,6 +1038,8 @@
 	 * @ssid_len: Buffer for returning length of @ssid
 	 * @group_iface: Buffer for returning whether a separate group interface
 	 *	would be used
+	 * @freq: Variable for returning the current operating frequency of a
+	 *	currently running P2P GO.
 	 * Returns: 1 if GO info found, 0 otherwise
 	 *
 	 * This is used to compose New Group settings (SSID, and intended
@@ -983,7 +1047,8 @@
 	 * result in our being an autonomous GO.
 	 */
 	int (*get_go_info)(void *ctx, u8 *intended_addr,
-			   u8 *ssid, size_t *ssid_len, int *group_iface);
+			   u8 *ssid, size_t *ssid_len, int *group_iface,
+			   unsigned int *freq);
 
 	/**
 	 * remove_stale_groups - Remove stale P2PS groups
@@ -1007,7 +1072,9 @@
 				   u8 conncap, int passwd_id,
 				   const u8 *persist_ssid,
 				   size_t persist_ssid_size, int response_done,
-				   int prov_start, const char *session_info);
+				   int prov_start, const char *session_info,
+				   const u8 *feat_cap, size_t feat_cap_len,
+				   unsigned int freq);
 
 	/**
 	 * prov_disc_resp_cb - Callback for indicating completion of PD Response
@@ -1021,14 +1088,34 @@
 
 	/**
 	 * p2ps_group_capability - Determine group capability
+	 * @ctx: Callback context from cb_ctx
+	 * @incoming: Peer requested roles, expressed with P2PS_SETUP_* bitmap.
+	 * @role: Local roles, expressed with P2PS_SETUP_* bitmap.
+	 * @force_freq: Variable for returning forced frequency for the group.
+	 * @pref_freq: Variable for returning preferred frequency for the group.
+	 * Returns: P2PS_SETUP_* bitmap of group capability result.
 	 *
-	 * This function can be used to determine group capability based on
-	 * information from P2PS PD exchange and the current state of ongoing
-	 * groups and driver capabilities.
-	 *
-	 * P2PS_SETUP_* bitmap is used as the parameters and return value.
+	 * This function can be used to determine group capability and
+	 * frequencies based on information from P2PS PD exchange and the
+	 * current state of ongoing groups and driver capabilities.
 	 */
-	u8 (*p2ps_group_capability)(void *ctx, u8 incoming, u8 role);
+	u8 (*p2ps_group_capability)(void *ctx, u8 incoming, u8 role,
+				    unsigned int *force_freq,
+				    unsigned int *pref_freq);
+
+	/**
+	 * get_pref_freq_list - Get preferred frequency list for an interface
+	 * @ctx: Callback context from cb_ctx
+	 * @go: Whether the use if for GO role
+	 * @len: Length of freq_list in entries (both IN and OUT)
+	 * @freq_list: Buffer for returning the preferred frequencies (MHz)
+	 * Returns: 0 on success, -1 on failure
+	 *
+	 * This function can be used to query the preferred frequency list from
+	 * the driver specific to a particular interface type.
+	 */
+	int (*get_pref_freq_list)(void *ctx, int go,
+				  unsigned int *len, unsigned int *freq_list);
 };
 
 
@@ -2248,9 +2335,33 @@
 p2p_service_p2ps_id(struct p2p_data *p2p, u32 adv_id);
 int p2p_service_add_asp(struct p2p_data *p2p, int auto_accept, u32 adv_id,
 			const char *adv_str, u8 svc_state,
-			u16 config_methods, const char *svc_info);
+			u16 config_methods, const char *svc_info,
+			const u8 *cpt_priority);
 int p2p_service_del_asp(struct p2p_data *p2p, u32 adv_id);
 void p2p_service_flush_asp(struct p2p_data *p2p);
 struct p2ps_advertisement * p2p_get_p2ps_adv_list(struct p2p_data *p2p);
 
+/**
+ * p2p_expire_peers - Periodic cleanup function to expire peers
+ * @p2p: P2P module context from p2p_init()
+ *
+ * This is a cleanup function that the entity calling p2p_init() is
+ * expected to call periodically to clean up expired peer entries.
+ */
+void p2p_expire_peers(struct p2p_data *p2p);
+
+void p2p_set_own_pref_freq_list(struct p2p_data *p2p,
+				const unsigned int *pref_freq_list,
+				unsigned int size);
+
+/**
+ * p2p_group_get_common_freqs - Get the group common frequencies
+ * @group: P2P group context from p2p_group_init()
+ * @common_freqs: On return will hold the group common frequencies
+ * @num: On return will hold the number of group common frequencies
+ * Returns: 0 on success, -1 otherwise
+ */
+int p2p_group_get_common_freqs(struct p2p_group *group, int *common_freqs,
+			       unsigned int *num);
+
 #endif /* P2P_H */
diff --git a/src/p2p/p2p_build.c b/src/p2p/p2p_build.c
index c733543..793d28b 100644
--- a/src/p2p/p2p_build.c
+++ b/src/p2p/p2p_build.c
@@ -10,6 +10,7 @@
 
 #include "common.h"
 #include "common/ieee802_11_defs.h"
+#include "common/qca-vendor.h"
 #include "wps/wps_i.h"
 #include "p2p_i.h"
 
@@ -109,6 +110,44 @@
 }
 
 
+void p2p_buf_add_pref_channel_list(struct wpabuf *buf,
+				   const u32 *preferred_freq_list,
+				   unsigned int size)
+{
+	unsigned int i, count = 0;
+	u8 op_class, op_channel;
+
+	if (!size)
+		return;
+
+	/*
+	 * First, determine the number of P2P supported channels in the
+	 * pref_freq_list returned from driver. This is needed for calculations
+	 * of the vendor IE size.
+	 */
+	for (i = 0; i < size; i++) {
+		if (p2p_freq_to_channel(preferred_freq_list[i], &op_class,
+					&op_channel) == 0)
+			count++;
+	}
+
+	wpabuf_put_u8(buf, WLAN_EID_VENDOR_SPECIFIC);
+	wpabuf_put_u8(buf, 4 + count * sizeof(u16));
+	wpabuf_put_be24(buf, OUI_QCA);
+	wpabuf_put_u8(buf, QCA_VENDOR_ELEM_P2P_PREF_CHAN_LIST);
+	for (i = 0; i < size; i++) {
+		if (p2p_freq_to_channel(preferred_freq_list[i], &op_class,
+					&op_channel) < 0) {
+			wpa_printf(MSG_DEBUG, "Unsupported frequency %u MHz",
+				   preferred_freq_list[i]);
+			continue;
+		}
+		wpabuf_put_u8(buf, op_class);
+		wpabuf_put_u8(buf, op_channel);
+	}
+}
+
+
 void p2p_buf_add_channel_list(struct wpabuf *buf, const char *country,
 			      struct p2p_channels *chan)
 {
diff --git a/src/p2p/p2p_go_neg.c b/src/p2p/p2p_go_neg.c
index 19f1daa..049ce6e 100644
--- a/src/p2p/p2p_go_neg.c
+++ b/src/p2p/p2p_go_neg.c
@@ -38,7 +38,7 @@
 {
 	const u8 *pos, *end;
 	struct p2p_channels *ch;
-	size_t channels;
+	u8 channels;
 	struct p2p_channels intersection;
 
 	ch = &dev->channels;
@@ -58,14 +58,14 @@
 	}
 	pos += 3;
 
-	while (pos + 2 < end) {
+	while (end - pos > 2) {
 		struct p2p_reg_class *cl = &ch->reg_class[ch->reg_classes];
 		cl->reg_class = *pos++;
-		if (pos + 1 + pos[0] > end) {
+		channels = *pos++;
+		if (channels > end - pos) {
 			p2p_info(p2p, "Invalid peer Channel List");
 			return -1;
 		}
-		channels = *pos++;
 		cl->channels = channels > P2P_MAX_REG_CLASS_CHANNELS ?
 			P2P_MAX_REG_CLASS_CHANNELS : channels;
 		os_memcpy(cl->channel, pos, cl->channels);
@@ -185,6 +185,9 @@
 				      p2p->op_reg_class, p2p->op_channel);
 	p2p_buf_update_ie_hdr(buf, len);
 
+	p2p_buf_add_pref_channel_list(buf, p2p->pref_freq_list,
+				      p2p->num_pref_freq);
+
 	/* WPS IE with Device Password ID attribute */
 	pw_id = p2p_wps_method_pw_id(peer->wps_method);
 	if (peer->oob_pw_id)
@@ -312,7 +315,7 @@
 			       group_capab);
 	p2p_buf_add_go_intent(buf, (p2p->go_intent << 1) | tie_breaker);
 	p2p_buf_add_config_timeout(buf, p2p->go_timeout, p2p->client_timeout);
-	if (peer && peer->go_state == REMOTE_GO) {
+	if (peer && peer->go_state == REMOTE_GO && !p2p->num_pref_freq) {
 		p2p_dbg(p2p, "Omit Operating Channel attribute");
 	} else {
 		p2p_buf_add_operating_channel(buf, p2p->cfg->country,
@@ -381,7 +384,7 @@
 	unsigned int i;
 	const int op_classes_5ghz[] = { 124, 125, 115, 0 };
 	const int op_classes_ht40[] = { 126, 127, 116, 117, 0 };
-	const int op_classes_vht[] = { 128, 0 };
+	const int op_classes_vht[] = { 128, 129, 130, 0 };
 
 	if (p2p->own_freq_preference > 0 &&
 	    p2p_freq_to_channel(p2p->own_freq_preference,
@@ -542,6 +545,195 @@
 }
 
 
+static void p2p_check_pref_chan_no_recv(struct p2p_data *p2p, int go,
+					struct p2p_device *dev,
+					struct p2p_message *msg,
+					unsigned freq_list[], unsigned int size)
+{
+	u8 op_class, op_channel;
+	unsigned int oper_freq = 0, i, j;
+	int found = 0;
+
+	p2p_dbg(p2p,
+		"Peer didn't provide a preferred frequency list, see if any of our preferred channels are supported by peer device");
+
+	/*
+	 * Search for a common channel in our preferred frequency list which is
+	 * also supported by the peer device.
+	 */
+	for (i = 0; i < size && !found; i++) {
+		/*
+		 * Make sure that the common frequency is:
+		 * 1. Supported by peer
+		 * 2. Allowed for P2P use.
+		 */
+		oper_freq = freq_list[i];
+		if (p2p_freq_to_channel(oper_freq, &op_class,
+					&op_channel) < 0) {
+			p2p_dbg(p2p, "Unsupported frequency %u MHz", oper_freq);
+			continue;
+		}
+		if (!p2p_channels_includes(&p2p->cfg->channels,
+					   op_class, op_channel) &&
+		    (go || !p2p_channels_includes(&p2p->cfg->cli_channels,
+						  op_class, op_channel))) {
+			p2p_dbg(p2p,
+				"Freq %u MHz (oper_class %u channel %u) not allowed for P2P",
+				oper_freq, op_class, op_channel);
+			break;
+		}
+		for (j = 0; j < msg->channel_list_len; j++) {
+
+			if (op_channel != msg->channel_list[j])
+				continue;
+
+			p2p->op_reg_class = op_class;
+			p2p->op_channel = op_channel;
+			os_memcpy(&p2p->channels, &p2p->cfg->channels,
+				  sizeof(struct p2p_channels));
+			found = 1;
+			break;
+		}
+	}
+
+	if (found) {
+		p2p_dbg(p2p,
+			"Freq %d MHz is a preferred channel and is also supported by peer, use it as the operating channel",
+			oper_freq);
+	} else {
+		p2p_dbg(p2p,
+			"None of our preferred channels are supported by peer!. Use: %d MHz for oper_channel",
+			dev->oper_freq);
+	}
+}
+
+
+static void p2p_check_pref_chan_recv(struct p2p_data *p2p, int go,
+				     struct p2p_device *dev,
+				     struct p2p_message *msg,
+				     unsigned freq_list[], unsigned int size)
+{
+	u8 op_class, op_channel;
+	unsigned int oper_freq = 0, i, j;
+	int found = 0;
+
+	/*
+	 * Peer device supports a Preferred Frequency List.
+	 * Search for a common channel in the preferred frequency lists
+	 * of both peer and local devices.
+	 */
+	for (i = 0; i < size && !found; i++) {
+		for (j = 2; j < (msg->pref_freq_list_len / 2); j++) {
+			oper_freq = p2p_channel_to_freq(
+				msg->pref_freq_list[2 * j],
+				msg->pref_freq_list[2 * j + 1]);
+			if (freq_list[i] != oper_freq)
+				continue;
+
+			/*
+			 * Make sure that the found frequency is:
+			 * 1. Supported
+			 * 2. Allowed for P2P use.
+			 */
+			if (p2p_freq_to_channel(oper_freq, &op_class,
+						&op_channel) < 0) {
+				p2p_dbg(p2p, "Unsupported frequency %u MHz",
+					oper_freq);
+				continue;
+			}
+
+			if (!p2p_channels_includes(&p2p->cfg->channels,
+						   op_class, op_channel) &&
+			    (go ||
+			     !p2p_channels_includes(&p2p->cfg->cli_channels,
+						    op_class, op_channel))) {
+				p2p_dbg(p2p,
+					"Freq %u MHz (oper_class %u channel %u) not allowed for P2P",
+					oper_freq, op_class, op_channel);
+				break;
+			}
+			p2p->op_reg_class = op_class;
+			p2p->op_channel = op_channel;
+			os_memcpy(&p2p->channels, &p2p->cfg->channels,
+				  sizeof(struct p2p_channels));
+			found = 1;
+			break;
+		}
+	}
+
+	if (found) {
+		p2p_dbg(p2p,
+			"Freq %d MHz is a common preferred channel for both peer and local, use it as operating channel",
+			oper_freq);
+	} else {
+		p2p_dbg(p2p,
+			"No common preferred channels found! Use: %d MHz for oper_channel",
+			dev->oper_freq);
+	}
+}
+
+
+void p2p_check_pref_chan(struct p2p_data *p2p, int go,
+			 struct p2p_device *dev, struct p2p_message *msg)
+{
+	unsigned int freq_list[P2P_MAX_PREF_CHANNELS], size;
+	unsigned int i;
+	u8 op_class, op_channel;
+
+	/*
+	 * Use the preferred channel list from the driver only if there is no
+	 * forced_freq, e.g., P2P_CONNECT freq=..., and no preferred operating
+	 * channel hardcoded in the configuration file.
+	 */
+	if (!p2p->cfg->get_pref_freq_list || p2p->cfg->num_pref_chan ||
+	    (dev->flags & P2P_DEV_FORCE_FREQ) || p2p->cfg->cfg_op_channel)
+		return;
+
+	/* Obtain our preferred frequency list from driver based on P2P role. */
+	size = P2P_MAX_PREF_CHANNELS;
+	if (p2p->cfg->get_pref_freq_list(p2p->cfg->cb_ctx, go, &size,
+					 freq_list))
+		return;
+
+	/*
+	 * Check if peer's preference of operating channel is in
+	 * our preferred channel list.
+	 */
+	for (i = 0; i < size; i++) {
+		if (freq_list[i] == (unsigned int) dev->oper_freq)
+			break;
+	}
+	if (i != size) {
+		/* Peer operating channel preference matches our preference */
+		if (p2p_freq_to_channel(freq_list[i], &op_class, &op_channel) <
+		    0) {
+			p2p_dbg(p2p,
+				"Peer operating channel preference is unsupported frequency %u MHz",
+				freq_list[i]);
+		} else {
+			p2p->op_reg_class = op_class;
+			p2p->op_channel = op_channel;
+			os_memcpy(&p2p->channels, &p2p->cfg->channels,
+				  sizeof(struct p2p_channels));
+			return;
+		}
+	}
+
+	p2p_dbg(p2p,
+		"Peer operating channel preference: %d MHz is not in our preferred channel list",
+		dev->oper_freq);
+
+	/*
+	  Check if peer's preferred channel list is
+	  * _not_ included in the GO Negotiation Request or Invitation Request.
+	  */
+	if (msg->pref_freq_list_len == 0)
+		p2p_check_pref_chan_no_recv(p2p, go, dev, msg, freq_list, size);
+	else
+		p2p_check_pref_chan_recv(p2p, go, dev, msg, freq_list, size);
+}
+
+
 void p2p_process_go_neg_req(struct p2p_data *p2p, const u8 *sa,
 			    const u8 *data, size_t len, int rx_freq)
 {
@@ -709,6 +901,14 @@
 			return;
 		}
 
+		if (dev->go_neg_req_sent &&
+		    (dev->flags & P2P_DEV_PEER_WAITING_RESPONSE)) {
+			p2p_dbg(p2p,
+				"Do not reply since peer is waiting for us to start a new GO Negotiation and GO Neg Request already sent");
+			p2p_parse_free(&msg);
+			return;
+		}
+
 		go = p2p_go_det(p2p->go_intent, *msg.go_intent);
 		if (go < 0) {
 			p2p_dbg(p2p, "Incompatible GO Intent");
@@ -799,6 +999,12 @@
 		p2p_dbg(p2p, "Peer operating channel preference: %d MHz",
 			dev->oper_freq);
 
+		/*
+		 * Use the driver preferred frequency list extension if
+		 * supported.
+		 */
+		p2p_check_pref_chan(p2p, go, dev, &msg);
+
 		if (msg.config_timeout) {
 			dev->go_timeout = msg.config_timeout[0];
 			dev->client_timeout = msg.config_timeout[1];
@@ -1150,6 +1356,13 @@
 	if (go && p2p_go_select_channel(p2p, dev, &status) < 0)
 		goto fail;
 
+	/*
+	 * Use the driver preferred frequency list extension if local device is
+	 * GO.
+	 */
+	if (go)
+		p2p_check_pref_chan(p2p, go, dev, &msg);
+
 	p2p_set_state(p2p, P2P_GO_NEG);
 	p2p_clear_timeout(p2p);
 
diff --git a/src/p2p/p2p_group.c b/src/p2p/p2p_group.c
index 41ca99f..2cf2450 100644
--- a/src/p2p/p2p_group.c
+++ b/src/p2p/p2p_group.c
@@ -296,14 +296,14 @@
 	os_memset(zero_addr, 0, ETH_ALEN);
 	pos = wpabuf_head_u8(m->wfd_ie);
 	end = pos + wpabuf_len(m->wfd_ie);
-	while (pos + 1 < end) {
+	while (end - pos >= 3) {
 		u8 id;
 		u16 len;
 
 		id = *pos++;
 		len = WPA_GET_BE16(pos);
 		pos += 2;
-		if (pos + len > end)
+		if (len > end - pos)
 			break;
 
 		switch (id) {
@@ -1071,3 +1071,43 @@
 			break;
 	}
 }
+
+
+int p2p_group_get_common_freqs(struct p2p_group *group, int *common_freqs,
+			       unsigned int *num)
+
+{
+	struct p2p_channels intersect, res;
+	struct p2p_group_member *m;
+
+	if (!group || !common_freqs || !num)
+		return -1;
+
+	os_memset(&intersect, 0, sizeof(intersect));
+	os_memset(&res, 0, sizeof(res));
+
+	p2p_channels_union(&intersect, &group->p2p->cfg->channels,
+			   &intersect);
+
+	p2p_channels_dump(group->p2p,
+			  "Group common freqs before iterating members",
+			  &intersect);
+
+	for (m = group->members; m; m = m->next) {
+		struct p2p_device *dev;
+
+		dev = p2p_get_device(group->p2p, m->dev_addr);
+		if (!dev)
+			continue;
+
+		p2p_channels_intersect(&intersect, &dev->channels, &res);
+		intersect = res;
+	}
+
+	p2p_channels_dump(group->p2p, "Group common channels", &intersect);
+
+	os_memset(common_freqs, 0, *num * sizeof(int));
+	*num = p2p_channels_to_freqs(&intersect, common_freqs, *num);
+
+	return 0;
+}
diff --git a/src/p2p/p2p_i.h b/src/p2p/p2p_i.h
index a1042d2..7f65ace 100644
--- a/src/p2p/p2p_i.h
+++ b/src/p2p/p2p_i.h
@@ -53,6 +53,9 @@
 	 * from Beacon/Probe Response), the interface address is stored here.
 	 * p2p_device_addr must still be set in such a case to the unique
 	 * identifier for the P2P Device.
+	 *
+	 * This field is also used during P2PS PD to store the intended GO
+	 * address of the peer.
 	 */
 	u8 interface_addr[ETH_ALEN];
 
@@ -535,6 +538,9 @@
 	u16 authorized_oob_dev_pw_id;
 
 	struct wpabuf **vendor_elem;
+
+	unsigned int pref_freq_list[P2P_MAX_PREF_CHANNELS];
+	unsigned int num_pref_freq;
 };
 
 /**
@@ -637,6 +643,9 @@
 	const u8 *persistent_dev;
 	const u8 *persistent_ssid;
 	size_t persistent_ssid_len;
+
+	const u8 *pref_freq_list;
+	size_t pref_freq_list_len;
 };
 
 
@@ -682,6 +691,8 @@
 			      u8 *op_channel);
 
 /* p2p_parse.c */
+void p2p_copy_filter_devname(char *dst, size_t dst_len,
+			     const void *src, size_t src_len);
 int p2p_parse_p2p_ie(const struct wpabuf *buf, struct p2p_message *msg);
 int p2p_parse_ies(const u8 *data, size_t len, struct p2p_message *msg);
 int p2p_parse(const u8 *data, size_t len, struct p2p_message *msg);
@@ -763,6 +774,8 @@
 				       const u8 *ssid, size_t ssid_len);
 int p2p_build_wps_ie(struct p2p_data *p2p, struct wpabuf *buf, int pw_id,
 		     int all_attr);
+void p2p_buf_add_pref_channel_list(struct wpabuf *buf,
+				   const u32 *preferred_freq_list, u32 size);
 
 /* p2p_sd.c */
 struct p2p_sd_query * p2p_pending_sd_req(struct p2p_data *p2p,
@@ -792,6 +805,8 @@
 u16 p2p_wps_method_pw_id(enum p2p_wps_method wps_method);
 void p2p_reselect_channel(struct p2p_data *p2p,
 			  struct p2p_channels *intersection);
+void p2p_check_pref_chan(struct p2p_data *p2p, int go,
+			 struct p2p_device *dev, struct p2p_message *msg);
 
 /* p2p_pd.c */
 void p2p_process_prov_disc_req(struct p2p_data *p2p, const u8 *sa,
diff --git a/src/p2p/p2p_invitation.c b/src/p2p/p2p_invitation.c
index f5454f7..70da15a 100644
--- a/src/p2p/p2p_invitation.c
+++ b/src/p2p/p2p_invitation.c
@@ -85,6 +85,9 @@
 	p2p_buf_add_device_info(buf, p2p, peer);
 	p2p_buf_update_ie_hdr(buf, len);
 
+	p2p_buf_add_pref_channel_list(buf, p2p->pref_freq_list,
+				      p2p->num_pref_freq);
+
 #ifdef CONFIG_WIFI_DISPLAY
 	if (wfd_ie)
 		wpabuf_put_buf(buf, wfd_ie);
@@ -281,7 +284,7 @@
 
 		if (!p2p_channels_includes(&intersection, reg_class, channel))
 		{
-			p2p_dbg(p2p, "forced freq %d MHz not in the supported channels interaction",
+			p2p_dbg(p2p, "forced freq %d MHz not in the supported channels intersection",
 				op_freq);
 			status = P2P_SC_FAIL_NO_COMMON_CHANNELS;
 			goto fail;
@@ -343,6 +346,12 @@
 			p2p_reselect_channel(p2p, &intersection);
 		}
 
+		/*
+		 * Use the driver preferred frequency list extension if
+		 * supported.
+		 */
+		p2p_check_pref_chan(p2p, go, dev, &msg);
+
 		op_freq = p2p_channel_to_freq(p2p->op_reg_class,
 					      p2p->op_channel);
 		if (op_freq < 0) {
@@ -534,6 +543,12 @@
 				peer_oper_freq = 0;
 		}
 
+		/*
+		 * Use the driver preferred frequency list extension if
+		 * supported.
+		 */
+		p2p_check_pref_chan(p2p, 0, dev, &msg);
+
 		p2p->cfg->invitation_result(p2p->cfg->cb_ctx, *msg.status,
 					    msg.group_bssid, channels, sa,
 					    freq, peer_oper_freq);
diff --git a/src/p2p/p2p_parse.c b/src/p2p/p2p_parse.c
index 980dddf..5d2299c 100644
--- a/src/p2p/p2p_parse.c
+++ b/src/p2p/p2p_parse.c
@@ -15,11 +15,29 @@
 #include "p2p_i.h"
 
 
+void p2p_copy_filter_devname(char *dst, size_t dst_len,
+			     const void *src, size_t src_len)
+{
+	size_t i;
+
+	if (src_len >= dst_len)
+		src_len = dst_len - 1;
+	os_memcpy(dst, src, src_len);
+	dst[src_len] = '\0';
+	for (i = 0; i < src_len; i++) {
+		if (dst[i] == '\0')
+			break;
+		if (is_ctrl_char(dst[i]))
+			dst[i] = '_';
+	}
+}
+
+
 static int p2p_parse_attribute(u8 id, const u8 *data, u16 len,
 			       struct p2p_message *msg)
 {
 	const u8 *pos;
-	size_t i, nlen;
+	u16 nlen;
 	char devtype[WPS_DEV_TYPE_BUFSIZE];
 
 	switch (id) {
@@ -149,21 +167,14 @@
 		pos += 2;
 		nlen = WPA_GET_BE16(pos);
 		pos += 2;
-		if (data + len - pos < (int) nlen ||
-		    nlen > WPS_DEV_NAME_MAX_LEN) {
+		if (nlen > data + len - pos || nlen > WPS_DEV_NAME_MAX_LEN) {
 			wpa_printf(MSG_DEBUG, "P2P: Invalid Device Name "
-				   "length %d (buf len %d)", (int) nlen,
+				   "length %u (buf len %d)", nlen,
 				   (int) (data + len - pos));
 			return -1;
 		}
-		os_memcpy(msg->device_name, pos, nlen);
-		msg->device_name[nlen] = '\0';
-		for (i = 0; i < nlen; i++) {
-			if (msg->device_name[i] == '\0')
-				break;
-			if (is_ctrl_char(msg->device_name[i]))
-				msg->device_name[i] = '_';
-		}
+		p2p_copy_filter_devname(msg->device_name,
+					sizeof(msg->device_name), pos, nlen);
 		wpa_printf(MSG_DEBUG, "P2P: * Device Info: addr " MACSTR
 			   " primary device type %s device name '%s' "
 			   "config methods 0x%x",
@@ -548,6 +559,9 @@
 	}
 #endif /* CONFIG_WIFI_DISPLAY */
 
+	msg->pref_freq_list = elems.pref_freq_list;
+	msg->pref_freq_list_len = elems.pref_freq_list_len;
+
 	return 0;
 }
 
@@ -634,49 +648,48 @@
 	gend = gi + gi_len;
 	while (g < gend) {
 		struct p2p_client_info *cli;
-		const u8 *t, *cend;
-		int count;
+		const u8 *cend;
+		u16 count;
+		u8 len;
 
 		cli = &info->client[info->num_clients];
-		cend = g + 1 + g[0];
-		if (cend > gend)
+		len = *g++;
+		if (len > gend - g || len < 2 * ETH_ALEN + 1 + 2 + 8 + 1)
 			return -1; /* invalid data */
+		cend = g + len;
 		/* g at start of P2P Client Info Descriptor */
-		/* t at Device Capability Bitmap */
-		t = g + 1 + 2 * ETH_ALEN;
-		if (t > cend)
-			return -1; /* invalid data */
-		cli->p2p_device_addr = g + 1;
-		cli->p2p_interface_addr = g + 1 + ETH_ALEN;
-		cli->dev_capab = t[0];
+		cli->p2p_device_addr = g;
+		g += ETH_ALEN;
+		cli->p2p_interface_addr = g;
+		g += ETH_ALEN;
+		cli->dev_capab = *g++;
 
-		if (t + 1 + 2 + 8 + 1 > cend)
-			return -1; /* invalid data */
+		cli->config_methods = WPA_GET_BE16(g);
+		g += 2;
+		cli->pri_dev_type = g;
+		g += 8;
 
-		cli->config_methods = WPA_GET_BE16(&t[1]);
-		cli->pri_dev_type = &t[3];
-
-		t += 1 + 2 + 8;
-		/* t at Number of Secondary Device Types */
-		cli->num_sec_dev_types = *t++;
-		if (t + 8 * cli->num_sec_dev_types > cend)
+		/* g at Number of Secondary Device Types */
+		len = *g++;
+		if (8 * len > cend - g)
 			return -1; /* invalid data */
-		cli->sec_dev_types = t;
-		t += 8 * cli->num_sec_dev_types;
+		cli->num_sec_dev_types = len;
+		cli->sec_dev_types = g;
+		g += 8 * len;
 
-		/* t at Device Name in WPS TLV format */
-		if (t + 2 + 2 > cend)
+		/* g at Device Name in WPS TLV format */
+		if (cend - g < 2 + 2)
 			return -1; /* invalid data */
-		if (WPA_GET_BE16(t) != ATTR_DEV_NAME)
+		if (WPA_GET_BE16(g) != ATTR_DEV_NAME)
 			return -1; /* invalid Device Name TLV */
-		t += 2;
-		count = WPA_GET_BE16(t);
-		t += 2;
-		if (count > cend - t)
+		g += 2;
+		count = WPA_GET_BE16(g);
+		g += 2;
+		if (count > cend - g)
 			return -1; /* invalid Device Name TLV */
 		if (count >= WPS_DEV_NAME_MAX_LEN)
 			count = WPS_DEV_NAME_MAX_LEN;
-		cli->dev_name = (const char *) t;
+		cli->dev_name = (const char *) g;
 		cli->dev_name_len = count;
 
 		g = cend;
diff --git a/src/p2p/p2p_pd.c b/src/p2p/p2p_pd.c
index 86558f7..c416ab6 100644
--- a/src/p2p/p2p_pd.c
+++ b/src/p2p/p2p_pd.c
@@ -40,24 +40,38 @@
 }
 
 
-static void p2ps_add_new_group_info(struct p2p_data *p2p, struct wpabuf *buf)
+static void p2ps_add_new_group_info(struct p2p_data *p2p,
+				    struct p2p_device *dev,
+				    struct wpabuf *buf)
 {
 	int found;
 	u8 intended_addr[ETH_ALEN];
 	u8 ssid[SSID_MAX_LEN];
 	size_t ssid_len;
 	int group_iface;
+	unsigned int force_freq;
 
 	if (!p2p->cfg->get_go_info)
 		return;
 
 	found = p2p->cfg->get_go_info(
 		p2p->cfg->cb_ctx, intended_addr, ssid,
-		&ssid_len, &group_iface);
+		&ssid_len, &group_iface, &force_freq);
 	if (found) {
+		if (force_freq > 0) {
+			p2p->p2ps_prov->force_freq = force_freq;
+			p2p->p2ps_prov->pref_freq = 0;
+
+			if (dev)
+				p2p_prepare_channel(p2p, dev, force_freq, 0, 0);
+		}
 		p2p_buf_add_group_id(buf, p2p->cfg->dev_addr,
 				     ssid, ssid_len);
-		p2p_buf_add_intended_addr(buf, intended_addr);
+
+		if (group_iface)
+			p2p_buf_add_intended_addr(buf, p2p->intended_addr);
+		else
+			p2p_buf_add_intended_addr(buf, intended_addr);
 	} else {
 		if (!p2p->ssid_set) {
 			p2p_build_ssid(p2p, p2p->ssid, &p2p->ssid_len);
@@ -82,76 +96,82 @@
 				  struct wpabuf *buf, u16 config_methods)
 {
 	struct p2ps_provision *prov = p2p->p2ps_prov;
-	u8 feat_cap_mask[] = { 1, 0 };
+	struct p2ps_feature_capab fcap = { prov->cpt_mask, 0 };
 	int shared_group = 0;
 	u8 ssid[SSID_MAX_LEN];
 	size_t ssid_len;
 	u8 go_dev_addr[ETH_ALEN];
+	u8 intended_addr[ETH_ALEN];
+	int follow_on_req_fail = prov->status >= 0 &&
+		prov->status != P2P_SC_SUCCESS_DEFERRED;
 
 	/* If we might be explicite group owner, add GO details */
-	if (prov->conncap & (P2PS_SETUP_GROUP_OWNER |
-			     P2PS_SETUP_NEW))
-		p2ps_add_new_group_info(p2p, buf);
+	if (!follow_on_req_fail &&
+	    (prov->conncap & (P2PS_SETUP_GROUP_OWNER | P2PS_SETUP_NEW)))
+		p2ps_add_new_group_info(p2p, dev, buf);
 
 	if (prov->status >= 0)
 		p2p_buf_add_status(buf, (u8) prov->status);
 	else
 		prov->method = config_methods;
 
-	if (p2p->cfg->get_persistent_group) {
-		shared_group = p2p->cfg->get_persistent_group(
-			p2p->cfg->cb_ctx, dev->info.p2p_device_addr, NULL, 0,
-			go_dev_addr, ssid, &ssid_len);
-	}
+	if (!follow_on_req_fail) {
+		if (p2p->cfg->get_persistent_group) {
+			shared_group = p2p->cfg->get_persistent_group(
+				p2p->cfg->cb_ctx, dev->info.p2p_device_addr,
+				NULL, 0, go_dev_addr, ssid, &ssid_len,
+				intended_addr);
+		}
 
-	/* Add Operating Channel if conncap includes GO */
-	if (shared_group ||
-	    (prov->conncap & (P2PS_SETUP_GROUP_OWNER |
-			      P2PS_SETUP_NEW))) {
-		u8 tmp;
+		if (shared_group ||
+		    (prov->conncap & (P2PS_SETUP_CLIENT | P2PS_SETUP_NEW)))
+			p2p_buf_add_channel_list(buf, p2p->cfg->country,
+						 &p2p->channels);
 
-		p2p_go_select_channel(p2p, dev, &tmp);
-
-		if (p2p->op_reg_class && p2p->op_channel)
+		if ((shared_group && !is_zero_ether_addr(intended_addr)) ||
+		    (prov->conncap & (P2PS_SETUP_GROUP_OWNER | P2PS_SETUP_NEW)))
 			p2p_buf_add_operating_channel(buf, p2p->cfg->country,
 						      p2p->op_reg_class,
 						      p2p->op_channel);
-		else
-			p2p_buf_add_operating_channel(buf, p2p->cfg->country,
-						      p2p->cfg->op_reg_class,
-						      p2p->cfg->op_channel);
 	}
 
-	p2p_buf_add_channel_list(buf, p2p->cfg->country, &p2p->cfg->channels);
-
-	if (prov->info[0])
+	if (prov->status < 0 && prov->info[0])
 		p2p_buf_add_session_info(buf, prov->info);
 
-	p2p_buf_add_connection_capability(buf, prov->conncap);
+	if (!follow_on_req_fail)
+		p2p_buf_add_connection_capability(buf, prov->conncap);
 
 	p2p_buf_add_advertisement_id(buf, prov->adv_id, prov->adv_mac);
 
-	if (shared_group || prov->conncap == P2PS_SETUP_NEW ||
-	    prov->conncap ==
-	    (P2PS_SETUP_GROUP_OWNER | P2PS_SETUP_NEW) ||
-	    prov->conncap ==
-	    (P2PS_SETUP_GROUP_OWNER | P2PS_SETUP_CLIENT)) {
-		/* Add Config Timeout */
-		p2p_buf_add_config_timeout(buf, p2p->go_timeout,
-					   p2p->client_timeout);
-	}
+	if (!follow_on_req_fail) {
+		if (shared_group || prov->conncap == P2PS_SETUP_NEW ||
+		    prov->conncap ==
+		    (P2PS_SETUP_GROUP_OWNER | P2PS_SETUP_NEW) ||
+		    prov->conncap ==
+		    (P2PS_SETUP_GROUP_OWNER | P2PS_SETUP_CLIENT)) {
+			/* Add Config Timeout */
+			p2p_buf_add_config_timeout(buf, p2p->go_timeout,
+						   p2p->client_timeout);
+		}
 
-	p2p_buf_add_listen_channel(buf, p2p->cfg->country, p2p->cfg->reg_class,
-				   p2p->cfg->channel);
+		p2p_buf_add_listen_channel(buf, p2p->cfg->country,
+					   p2p->cfg->reg_class,
+					   p2p->cfg->channel);
+	}
 
 	p2p_buf_add_session_id(buf, prov->session_id, prov->session_mac);
 
-	p2p_buf_add_feature_capability(buf, sizeof(feat_cap_mask),
-				       feat_cap_mask);
+	p2p_buf_add_feature_capability(buf, sizeof(fcap), (const u8 *) &fcap);
 
-	if (shared_group)
+	if (shared_group) {
 		p2p_buf_add_persistent_group_info(buf, go_dev_addr,
 						  ssid, ssid_len);
+		/* Add intended interface address if it is not added yet */
+		if ((prov->conncap == P2PS_SETUP_NONE ||
+		     prov->conncap == P2PS_SETUP_CLIENT) &&
+		    !is_zero_ether_addr(intended_addr))
+			p2p_buf_add_intended_addr(buf, intended_addr);
+	}
 }
 
 
@@ -232,7 +252,9 @@
 						const u8 *group_id,
 						size_t group_id_len,
 						const u8 *persist_ssid,
-						size_t persist_ssid_len)
+						size_t persist_ssid_len,
+						const u8 *fcap,
+						u16 fcap_len)
 {
 	struct wpabuf *buf;
 	size_t extra = 0;
@@ -270,10 +292,14 @@
 
 	/* Add P2P IE for P2PS */
 	if (p2p->p2ps_prov && p2p->p2ps_prov->adv_id == adv_id) {
-		u8 feat_cap_mask[] = { 1, 0 };
 		u8 *len = p2p_buf_add_ie_hdr(buf);
 		struct p2ps_provision *prov = p2p->p2ps_prov;
 		u8 group_capab;
+		u8 conncap = 0;
+
+		if (status == P2P_SC_SUCCESS ||
+		    status == P2P_SC_SUCCESS_DEFERRED)
+			conncap = prov->conncap;
 
 		if (!status && prov->status != -1)
 			status = prov->status;
@@ -290,33 +316,33 @@
 				       group_capab);
 		p2p_buf_add_device_info(buf, p2p, NULL);
 
-		if (persist_ssid && p2p->cfg->get_persistent_group &&
+		if (persist_ssid && p2p->cfg->get_persistent_group && dev &&
 		    (status == P2P_SC_SUCCESS ||
 		     status == P2P_SC_SUCCESS_DEFERRED)) {
 			u8 ssid[SSID_MAX_LEN];
 			size_t ssid_len;
 			u8 go_dev_addr[ETH_ALEN];
+			u8 intended_addr[ETH_ALEN];
 
 			persist = p2p->cfg->get_persistent_group(
 				p2p->cfg->cb_ctx,
 				dev->info.p2p_device_addr,
 				persist_ssid, persist_ssid_len, go_dev_addr,
-				ssid, &ssid_len);
-			if (persist)
+				ssid, &ssid_len, intended_addr);
+			if (persist) {
 				p2p_buf_add_persistent_group_info(
 					buf, go_dev_addr, ssid, ssid_len);
+				if (!is_zero_ether_addr(intended_addr))
+					p2p_buf_add_intended_addr(
+						buf, intended_addr);
+			}
 		}
 
-		if (!persist && (prov->conncap & P2PS_SETUP_GROUP_OWNER))
-			p2ps_add_new_group_info(p2p, buf);
+		if (!persist && (conncap & P2PS_SETUP_GROUP_OWNER))
+			p2ps_add_new_group_info(p2p, dev, buf);
 
 		/* Add Operating Channel if conncap indicates GO */
-		if (persist || (prov->conncap & P2PS_SETUP_GROUP_OWNER)) {
-			u8 tmp;
-
-			if (dev)
-				p2p_go_select_channel(p2p, dev, &tmp);
-
+		if (persist || (conncap & P2PS_SETUP_GROUP_OWNER)) {
 			if (p2p->op_reg_class && p2p->op_channel)
 				p2p_buf_add_operating_channel(
 					buf, p2p->cfg->country,
@@ -329,23 +355,25 @@
 					p2p->cfg->op_channel);
 		}
 
-		p2p_buf_add_channel_list(buf, p2p->cfg->country,
-					 &p2p->cfg->channels);
+		if (persist ||
+		    (conncap & (P2PS_SETUP_CLIENT | P2PS_SETUP_GROUP_OWNER)))
+			p2p_buf_add_channel_list(buf, p2p->cfg->country,
+						 &p2p->channels);
 
-		if (!persist && (status == P2P_SC_SUCCESS ||
-				 status == P2P_SC_SUCCESS_DEFERRED))
-			p2p_buf_add_connection_capability(buf, prov->conncap);
+		if (!persist && conncap)
+			p2p_buf_add_connection_capability(buf, conncap);
 
 		p2p_buf_add_advertisement_id(buf, adv_id, prov->adv_mac);
 
-		p2p_buf_add_config_timeout(buf, p2p->go_timeout,
-					   p2p->client_timeout);
+		if (persist ||
+		    (conncap & (P2PS_SETUP_CLIENT | P2PS_SETUP_GROUP_OWNER)))
+			p2p_buf_add_config_timeout(buf, p2p->go_timeout,
+						   p2p->client_timeout);
 
 		p2p_buf_add_session_id(buf, prov->session_id,
 				       prov->session_mac);
 
-		p2p_buf_add_feature_capability(buf, sizeof(feat_cap_mask),
-					       feat_cap_mask);
+		p2p_buf_add_feature_capability(buf, fcap_len, fcap);
 		p2p_buf_update_ie_hdr(buf, len);
 	} else if (status != P2P_SC_SUCCESS || adv_id) {
 		u8 *len = p2p_buf_add_ie_hdr(buf);
@@ -400,6 +428,129 @@
 }
 
 
+static u8 p2ps_own_preferred_cpt(const u8 *cpt_priority, u8 req_cpt_mask)
+{
+	int i;
+
+	for (i = 0; cpt_priority[i]; i++)
+		if (req_cpt_mask & cpt_priority[i])
+			return cpt_priority[i];
+
+	return 0;
+}
+
+
+/* Check if the message contains a valid P2PS PD Request */
+static int p2ps_validate_pd_req(struct p2p_data *p2p, struct p2p_message *msg,
+				const u8 *addr)
+{
+	u8 group_id = 0;
+	u8 intended_addr = 0;
+	u8 operating_channel = 0;
+	u8 channel_list = 0;
+	u8 config_timeout = 0;
+	u8 listen_channel = 0;
+
+#define P2PS_PD_REQ_CHECK(_val, _attr) \
+do { \
+	if ((_val) && !msg->_attr) { \
+		p2p_dbg(p2p, "Not P2PS PD Request. Missing %s", #_attr); \
+		return -1; \
+	} \
+} while (0)
+
+	P2PS_PD_REQ_CHECK(1, adv_id);
+	P2PS_PD_REQ_CHECK(1, session_id);
+	P2PS_PD_REQ_CHECK(1, session_mac);
+	P2PS_PD_REQ_CHECK(1, adv_mac);
+	P2PS_PD_REQ_CHECK(1, capability);
+	P2PS_PD_REQ_CHECK(1, p2p_device_info);
+	P2PS_PD_REQ_CHECK(1, feature_cap);
+
+	/*
+	 * We don't need to check Connection Capability, Persistent Group,
+	 * and related attributes for follow-on PD Request with a status
+	 * other than SUCCESS_DEFERRED.
+	 */
+	if (msg->status && *msg->status != P2P_SC_SUCCESS_DEFERRED)
+		return 0;
+
+	P2PS_PD_REQ_CHECK(1, conn_cap);
+
+	/*
+	 * Note 1: A feature capability attribute structure can be changed
+	 * in the future. The assumption is that such modifications are
+	 * backward compatible, therefore we allow processing of msg.feature_cap
+	 * exceeding the size of the p2ps_feature_capab structure.
+	 * Note 2: Verification of msg.feature_cap_len below has to be changed
+	 * to allow 2 byte feature capability processing if
+	 * struct p2ps_feature_capab is extended to include additional fields
+	 * and it affects the structure size.
+	 */
+	if (msg->feature_cap_len < sizeof(struct p2ps_feature_capab)) {
+		p2p_dbg(p2p, "P2PS: Invalid feature capability len");
+		return -1;
+	}
+
+	switch (*msg->conn_cap) {
+	case P2PS_SETUP_NEW:
+		group_id = 1;
+		intended_addr = 1;
+		operating_channel = 1;
+		channel_list = 1;
+		config_timeout = 1;
+		listen_channel = 1;
+		break;
+	case P2PS_SETUP_CLIENT:
+		channel_list = 1;
+		listen_channel = 1;
+		break;
+	case P2PS_SETUP_GROUP_OWNER:
+		group_id = 1;
+		intended_addr = 1;
+		operating_channel = 1;
+		break;
+	case P2PS_SETUP_NEW | P2PS_SETUP_GROUP_OWNER:
+		group_id = 1;
+		operating_channel = 1;
+		intended_addr = 1;
+		channel_list = 1;
+		config_timeout = 1;
+		break;
+	case P2PS_SETUP_CLIENT | P2PS_SETUP_GROUP_OWNER:
+		group_id = 1;
+		intended_addr = 1;
+		operating_channel = 1;
+		channel_list = 1;
+		config_timeout = 1;
+		break;
+	default:
+		p2p_dbg(p2p, "Invalid P2PS PD connection capability");
+		return -1;
+	}
+
+	if (msg->persistent_dev) {
+		channel_list = 1;
+		config_timeout = 1;
+		if (os_memcmp(msg->persistent_dev, addr, ETH_ALEN) == 0) {
+			intended_addr = 1;
+			operating_channel = 1;
+		}
+	}
+
+	P2PS_PD_REQ_CHECK(group_id, group_id);
+	P2PS_PD_REQ_CHECK(intended_addr, intended_addr);
+	P2PS_PD_REQ_CHECK(operating_channel, operating_channel);
+	P2PS_PD_REQ_CHECK(channel_list, channel_list);
+	P2PS_PD_REQ_CHECK(config_timeout, config_timeout);
+	P2PS_PD_REQ_CHECK(listen_channel, listen_channel);
+
+#undef P2PS_PD_REQ_CHECK
+
+	return 0;
+}
+
+
 void p2p_process_prov_disc_req(struct p2p_data *p2p, const u8 *sa,
 			       const u8 *data, size_t len, int rx_freq)
 {
@@ -413,11 +564,16 @@
 	u8 conncap = P2PS_SETUP_NEW;
 	u8 auto_accept = 0;
 	u32 session_id = 0;
-	u8 session_mac[ETH_ALEN];
-	u8 adv_mac[ETH_ALEN];
-	u8 group_mac[ETH_ALEN];
+	u8 session_mac[ETH_ALEN] = { 0, 0, 0, 0, 0, 0 };
+	u8 adv_mac[ETH_ALEN] = { 0, 0, 0, 0, 0, 0 };
+	const u8 *group_mac;
 	int passwd_id = DEV_PW_DEFAULT;
 	u16 config_methods;
+	u16 allowed_config_methods = WPS_CONFIG_DISPLAY | WPS_CONFIG_KEYPAD;
+	struct p2ps_feature_capab resp_fcap = { 0, 0 };
+	struct p2ps_feature_capab *req_fcap = NULL;
+	u8 remote_conncap;
+	u16 method;
 
 	if (p2p_parse(data, len, &msg))
 		return;
@@ -425,6 +581,7 @@
 	p2p_dbg(p2p, "Received Provision Discovery Request from " MACSTR
 		" with config methods 0x%x (freq=%d)",
 		MAC2STR(sa), msg.wps_config_methods, rx_freq);
+	group_mac = msg.intended_addr;
 
 	dev = p2p_get_device(p2p, sa);
 	if (dev == NULL || (dev->flags & P2P_DEV_PROBE_REQ_ONLY)) {
@@ -435,256 +592,431 @@
 				   0)) {
 			p2p_dbg(p2p, "Provision Discovery Request add device failed "
 				MACSTR, MAC2STR(sa));
+			goto out;
+		}
+
+		if (!dev) {
+			dev = p2p_get_device(p2p, sa);
+			if (!dev) {
+				p2p_dbg(p2p,
+					"Provision Discovery device not found "
+					MACSTR, MAC2STR(sa));
+				goto out;
+			}
 		}
 	} else if (msg.wfd_subelems) {
 		wpabuf_free(dev->info.wfd_subelems);
 		dev->info.wfd_subelems = wpabuf_dup(msg.wfd_subelems);
 	}
 
-	if (!(msg.wps_config_methods &
-	      (WPS_CONFIG_DISPLAY | WPS_CONFIG_KEYPAD |
-	       WPS_CONFIG_PUSHBUTTON | WPS_CONFIG_P2PS))) {
-		p2p_dbg(p2p, "Unsupported Config Methods in Provision Discovery Request");
-		goto out;
-	}
-
-	/* Legacy (non-P2PS) - Unknown groups allowed for P2PS */
-	if (!msg.adv_id && msg.group_id) {
-		size_t i;
-		for (i = 0; i < p2p->num_groups; i++) {
-			if (p2p_group_is_group_id_match(p2p->groups[i],
-							msg.group_id,
-							msg.group_id_len))
-				break;
-		}
-		if (i == p2p->num_groups) {
-			p2p_dbg(p2p, "PD request for unknown P2P Group ID - reject");
+	if (!msg.adv_id) {
+		allowed_config_methods |= WPS_CONFIG_PUSHBUTTON;
+		if (!(msg.wps_config_methods & allowed_config_methods)) {
+			p2p_dbg(p2p,
+				"Unsupported Config Methods in Provision Discovery Request");
 			goto out;
 		}
-	}
 
-	if (dev) {
-		dev->flags &= ~(P2P_DEV_PD_PEER_DISPLAY |
-				P2P_DEV_PD_PEER_KEYPAD |
-				P2P_DEV_PD_PEER_P2PS);
+		/* Legacy (non-P2PS) - Unknown groups allowed for P2PS */
+		if (msg.group_id) {
+			size_t i;
 
-		/* Remove stale persistent groups */
-		if (p2p->cfg->remove_stale_groups) {
-			p2p->cfg->remove_stale_groups(
-				p2p->cfg->cb_ctx, dev->info.p2p_device_addr,
-				msg.persistent_dev,
-				msg.persistent_ssid, msg.persistent_ssid_len);
+			for (i = 0; i < p2p->num_groups; i++) {
+				if (p2p_group_is_group_id_match(
+					    p2p->groups[i],
+					    msg.group_id, msg.group_id_len))
+					break;
+			}
+			if (i == p2p->num_groups) {
+				p2p_dbg(p2p,
+					"PD request for unknown P2P Group ID - reject");
+				goto out;
+			}
 		}
-	}
-	if (msg.wps_config_methods & WPS_CONFIG_DISPLAY) {
-		p2p_dbg(p2p, "Peer " MACSTR
-			" requested us to show a PIN on display", MAC2STR(sa));
-		if (dev)
-			dev->flags |= P2P_DEV_PD_PEER_KEYPAD;
-		passwd_id = DEV_PW_USER_SPECIFIED;
-	} else if (msg.wps_config_methods & WPS_CONFIG_KEYPAD) {
-		p2p_dbg(p2p, "Peer " MACSTR
-			" requested us to write its PIN using keypad",
-			MAC2STR(sa));
-		if (dev)
-			dev->flags |= P2P_DEV_PD_PEER_DISPLAY;
-		passwd_id = DEV_PW_REGISTRAR_SPECIFIED;
-	} else if (msg.wps_config_methods & WPS_CONFIG_P2PS) {
-		p2p_dbg(p2p, "Peer " MACSTR " requesting P2PS PIN",
-			MAC2STR(sa));
-		if (dev)
-			dev->flags |= P2P_DEV_PD_PEER_P2PS;
-		passwd_id = DEV_PW_P2PS_DEFAULT;
-	}
+	} else {
+		allowed_config_methods |= WPS_CONFIG_P2PS;
 
-	reject = P2P_SC_SUCCESS;
+		/*
+		 * Set adv_id here, so in case of an error, a P2PS PD Response
+		 * will be sent.
+		 */
+		adv_id = WPA_GET_LE32(msg.adv_id);
+		if (p2ps_validate_pd_req(p2p, &msg, sa) < 0) {
+			reject = P2P_SC_FAIL_INVALID_PARAMS;
+			goto out;
+		}
 
-	os_memset(session_mac, 0, ETH_ALEN);
-	os_memset(adv_mac, 0, ETH_ALEN);
-	os_memset(group_mac, 0, ETH_ALEN);
-
-	if (msg.adv_id && msg.session_id && msg.session_mac && msg.adv_mac &&
-	    (msg.status || msg.conn_cap)) {
-		u8 remote_conncap;
-
-		if (msg.intended_addr)
-			os_memcpy(group_mac, msg.intended_addr, ETH_ALEN);
+		req_fcap = (struct p2ps_feature_capab *) msg.feature_cap;
 
 		os_memcpy(session_mac, msg.session_mac, ETH_ALEN);
 		os_memcpy(adv_mac, msg.adv_mac, ETH_ALEN);
 
 		session_id = WPA_GET_LE32(msg.session_id);
-		adv_id = WPA_GET_LE32(msg.adv_id);
-
-		if (!msg.status)
-			p2ps_adv = p2p_service_p2ps_id(p2p, adv_id);
-
-		p2p_dbg(p2p, "adv_id: %x - p2ps_adv - %p", adv_id, p2ps_adv);
 
 		if (msg.conn_cap)
 			conncap = *msg.conn_cap;
-		remote_conncap = conncap;
 
-		if (p2ps_adv) {
-			auto_accept = p2ps_adv->auto_accept;
-			conncap = p2p->cfg->p2ps_group_capability(
-				p2p->cfg->cb_ctx, conncap, auto_accept);
+		/*
+		 * We need to verify a P2PS config methog in an initial PD
+		 * request or in a follow-on PD request with the status
+		 * SUCCESS_DEFERRED.
+		 */
+		if ((!msg.status || *msg.status == P2P_SC_SUCCESS_DEFERRED) &&
+		    !(msg.wps_config_methods & allowed_config_methods)) {
+			p2p_dbg(p2p,
+				"Unsupported Config Methods in Provision Discovery Request");
+			goto out;
+		}
 
-			p2p_dbg(p2p, "Conncap: local:%d remote:%d result:%d",
-				auto_accept, remote_conncap, conncap);
+		/*
+		 * TODO: since we don't support multiple PD, reject PD request
+		 * if we are in the middle of P2PS PD with some other peer
+		 */
+	}
 
-			if (p2ps_adv->config_methods &&
-			    !(msg.wps_config_methods &
-			      p2ps_adv->config_methods)) {
-				p2p_dbg(p2p,
-					"Unsupported config methods in Provision Discovery Request (own=0x%x peer=0x%x)",
-					p2ps_adv->config_methods,
-					msg.wps_config_methods);
-				reject = P2P_SC_FAIL_INCOMPATIBLE_PARAMS;
-			} else if (!p2ps_adv->state) {
-				p2p_dbg(p2p, "P2PS state unavailable");
-				reject = P2P_SC_FAIL_UNABLE_TO_ACCOMMODATE;
-			} else if (!conncap) {
-				p2p_dbg(p2p, "Conncap resolution failed");
-				reject = P2P_SC_FAIL_INCOMPATIBLE_PARAMS;
-			}
+	dev->flags &= ~(P2P_DEV_PD_PEER_DISPLAY |
+			P2P_DEV_PD_PEER_KEYPAD |
+			P2P_DEV_PD_PEER_P2PS);
 
-			if (msg.wps_config_methods & WPS_CONFIG_KEYPAD) {
-				p2p_dbg(p2p, "Keypad - always defer");
-				auto_accept = 0;
-			}
+	if (msg.wps_config_methods & WPS_CONFIG_DISPLAY) {
+		p2p_dbg(p2p, "Peer " MACSTR
+			" requested us to show a PIN on display", MAC2STR(sa));
+		dev->flags |= P2P_DEV_PD_PEER_KEYPAD;
+		passwd_id = DEV_PW_USER_SPECIFIED;
+	} else if (msg.wps_config_methods & WPS_CONFIG_KEYPAD) {
+		p2p_dbg(p2p, "Peer " MACSTR
+			" requested us to write its PIN using keypad",
+			MAC2STR(sa));
+		dev->flags |= P2P_DEV_PD_PEER_DISPLAY;
+		passwd_id = DEV_PW_REGISTRAR_SPECIFIED;
+	} else if (msg.wps_config_methods & WPS_CONFIG_P2PS) {
+		p2p_dbg(p2p, "Peer " MACSTR " requesting P2PS PIN",
+			MAC2STR(sa));
+		dev->flags |= P2P_DEV_PD_PEER_P2PS;
+		passwd_id = DEV_PW_P2PS_DEFAULT;
+	}
 
-			if (auto_accept || reject != P2P_SC_SUCCESS) {
-				struct p2ps_provision *tmp;
+	/* Remove stale persistent groups */
+	if (p2p->cfg->remove_stale_groups) {
+		p2p->cfg->remove_stale_groups(
+			p2p->cfg->cb_ctx, dev->info.p2p_device_addr,
+			msg.persistent_dev,
+			msg.persistent_ssid, msg.persistent_ssid_len);
+	}
 
-				if (reject == P2P_SC_SUCCESS && !conncap) {
-					reject =
-						P2P_SC_FAIL_INCOMPATIBLE_PARAMS;
-				}
+	reject = P2P_SC_SUCCESS;
 
-				if (p2ps_setup_p2ps_prov(
-					    p2p, adv_id, session_id,
-					    msg.wps_config_methods,
-					    session_mac, adv_mac) < 0) {
-					reject = P2P_SC_FAIL_UNABLE_TO_ACCOMMODATE;
-					goto out;
-				}
+	/*
+	 * End of a legacy P2P PD Request processing, from this point continue
+	 * with P2PS one.
+	 */
+	if (!msg.adv_id)
+		goto out;
 
-				tmp = p2p->p2ps_prov;
-				if (conncap) {
-					tmp->conncap = conncap;
-					tmp->status = P2P_SC_SUCCESS;
-				} else {
-					tmp->conncap = auto_accept;
-					tmp->status = P2P_SC_FAIL_INCOMPATIBLE_PARAMS;
-				}
+	remote_conncap = conncap;
 
-				if (reject != P2P_SC_SUCCESS)
-					goto out;
-			}
-		} else if (!msg.status) {
+	if (!msg.status) {
+		unsigned int forced_freq, pref_freq;
+
+		if (os_memcmp(p2p->cfg->dev_addr, msg.adv_mac, ETH_ALEN)) {
+			p2p_dbg(p2p,
+				"P2PS PD adv mac does not match the local one");
 			reject = P2P_SC_FAIL_INCOMPATIBLE_PARAMS;
 			goto out;
 		}
 
-		if (!msg.status && !auto_accept &&
-		    (!p2p->p2ps_prov || p2p->p2ps_prov->adv_id != adv_id)) {
-			struct p2ps_provision *tmp;
+		p2ps_adv = p2p_service_p2ps_id(p2p, adv_id);
+		if (!p2ps_adv) {
+			p2p_dbg(p2p, "P2PS PD invalid adv_id=0x%X", adv_id);
+			reject = P2P_SC_FAIL_INCOMPATIBLE_PARAMS;
+			goto out;
+		}
+		p2p_dbg(p2p, "adv_id: 0x%X, p2ps_adv: %p", adv_id, p2ps_adv);
 
-			if (!conncap) {
-				reject = P2P_SC_FAIL_INCOMPATIBLE_PARAMS;
-				goto out;
+		auto_accept = p2ps_adv->auto_accept;
+		conncap = p2p->cfg->p2ps_group_capability(p2p->cfg->cb_ctx,
+							  conncap, auto_accept,
+							  &forced_freq,
+							  &pref_freq);
+
+		p2p_dbg(p2p, "Conncap: local:%d remote:%d result:%d",
+			auto_accept, remote_conncap, conncap);
+
+		p2p_prepare_channel(p2p, dev, forced_freq, pref_freq, 0);
+
+		resp_fcap.cpt = p2ps_own_preferred_cpt(p2ps_adv->cpt_priority,
+						       req_fcap->cpt);
+
+		p2p_dbg(p2p, "cpt: service:0x%x remote:0x%x result:0x%x",
+			p2ps_adv->cpt_mask, req_fcap->cpt, resp_fcap.cpt);
+
+		if (!resp_fcap.cpt) {
+			p2p_dbg(p2p,
+				"Incompatible P2PS feature capability CPT bitmask");
+			reject = P2P_SC_FAIL_INCOMPATIBLE_PARAMS;
+		} else if (p2ps_adv->config_methods &&
+			   !(msg.wps_config_methods &
+			     p2ps_adv->config_methods)) {
+			p2p_dbg(p2p,
+				"Unsupported config methods in Provision Discovery Request (own=0x%x peer=0x%x)",
+				p2ps_adv->config_methods,
+				msg.wps_config_methods);
+			reject = P2P_SC_FAIL_INCOMPATIBLE_PARAMS;
+		} else if (!p2ps_adv->state) {
+			p2p_dbg(p2p, "P2PS state unavailable");
+			reject = P2P_SC_FAIL_UNABLE_TO_ACCOMMODATE;
+		} else if (!conncap) {
+			p2p_dbg(p2p, "Conncap resolution failed");
+			reject = P2P_SC_FAIL_INCOMPATIBLE_PARAMS;
+		}
+
+		if (msg.wps_config_methods & WPS_CONFIG_KEYPAD) {
+			p2p_dbg(p2p, "Keypad - always defer");
+			auto_accept = 0;
+		}
+
+		if ((remote_conncap & (P2PS_SETUP_NEW | P2PS_SETUP_CLIENT) ||
+		     msg.persistent_dev) && conncap != P2PS_SETUP_NEW &&
+		    msg.channel_list && msg.channel_list_len &&
+		    p2p_peer_channels_check(p2p, &p2p->channels, dev,
+					    msg.channel_list,
+					    msg.channel_list_len) < 0) {
+			p2p_dbg(p2p,
+				"No common channels - force deferred flow");
+			auto_accept = 0;
+		}
+
+		if (((remote_conncap & P2PS_SETUP_GROUP_OWNER) ||
+		     msg.persistent_dev) && msg.operating_channel) {
+			struct p2p_channels intersect;
+
+			/*
+			 * There are cases where only the operating channel is
+			 * provided. This requires saving the channel as the
+			 * supported channel list, and verifying that it is
+			 * supported.
+			 */
+			if (dev->channels.reg_classes == 0 ||
+			    !p2p_channels_includes(&dev->channels,
+						   msg.operating_channel[3],
+						   msg.operating_channel[4])) {
+				struct p2p_channels *ch = &dev->channels;
+
+				os_memset(ch, 0, sizeof(*ch));
+				ch->reg_class[0].reg_class =
+					msg.operating_channel[3];
+				ch->reg_class[0].channel[0] =
+					msg.operating_channel[4];
+				ch->reg_class[0].channels = 1;
+				ch->reg_classes = 1;
 			}
 
+			p2p_channels_intersect(&p2p->channels, &dev->channels,
+					       &intersect);
+
+			if (intersect.reg_classes == 0) {
+				p2p_dbg(p2p,
+					"No common channels - force deferred flow");
+				auto_accept = 0;
+			}
+		}
+
+		if (auto_accept || reject != P2P_SC_SUCCESS) {
+			struct p2ps_provision *tmp;
+
 			if (p2ps_setup_p2ps_prov(p2p, adv_id, session_id,
 						 msg.wps_config_methods,
 						 session_mac, adv_mac) < 0) {
 				reject = P2P_SC_FAIL_UNABLE_TO_ACCOMMODATE;
 				goto out;
 			}
+
 			tmp = p2p->p2ps_prov;
-			reject = P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE;
-			tmp->status = reject;
-		}
-
-		if (msg.status) {
-			if (*msg.status &&
-			    *msg.status != P2P_SC_SUCCESS_DEFERRED) {
-				reject = *msg.status;
-			} else if (*msg.status == P2P_SC_SUCCESS_DEFERRED &&
-				   p2p->p2ps_prov) {
-				u16 method = p2p->p2ps_prov->method;
-
-				conncap = p2p->cfg->p2ps_group_capability(
-					p2p->cfg->cb_ctx, remote_conncap,
-					p2p->p2ps_prov->conncap);
-
-				p2p_dbg(p2p,
-					"Conncap: local:%d remote:%d result:%d",
-					p2p->p2ps_prov->conncap,
-					remote_conncap, conncap);
-
-				/*
-				 * Ensure that if we asked for PIN originally,
-				 * our method is consistent with original
-				 * request.
-				 */
-				if (method & WPS_CONFIG_DISPLAY)
-					method = WPS_CONFIG_KEYPAD;
-				else if (method & WPS_CONFIG_KEYPAD)
-					method = WPS_CONFIG_DISPLAY;
-
-				/* Reject this "Deferred Accept* if incompatible
-				 * conncap or method */
-				if (!conncap ||
-				    !(msg.wps_config_methods & method))
-					reject =
-						P2P_SC_FAIL_INCOMPATIBLE_PARAMS;
-				else
-					reject = P2P_SC_SUCCESS;
-
-				p2p->p2ps_prov->status = reject;
-				p2p->p2ps_prov->conncap = conncap;
+			tmp->force_freq = forced_freq;
+			tmp->pref_freq = pref_freq;
+			if (conncap) {
+				tmp->conncap = conncap;
+				tmp->status = P2P_SC_SUCCESS;
+			} else {
+				tmp->conncap = auto_accept;
+				tmp->status = P2P_SC_FAIL_INCOMPATIBLE_PARAMS;
 			}
+
+			if (reject != P2P_SC_SUCCESS)
+				goto out;
 		}
 	}
 
+	if (!msg.status && !auto_accept &&
+	    (!p2p->p2ps_prov || p2p->p2ps_prov->adv_id != adv_id)) {
+		struct p2ps_provision *tmp;
+
+		if (!conncap) {
+			reject = P2P_SC_FAIL_INCOMPATIBLE_PARAMS;
+			goto out;
+		}
+
+		if (p2ps_setup_p2ps_prov(p2p, adv_id, session_id,
+					 msg.wps_config_methods,
+					 session_mac, adv_mac) < 0) {
+			reject = P2P_SC_FAIL_UNABLE_TO_ACCOMMODATE;
+			goto out;
+		}
+		tmp = p2p->p2ps_prov;
+		reject = P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE;
+		tmp->status = reject;
+	}
+
+	/* Not a P2PS Follow-on PD */
+	if (!msg.status)
+		goto out;
+
+	if (*msg.status && *msg.status != P2P_SC_SUCCESS_DEFERRED) {
+		reject = *msg.status;
+		goto out;
+	}
+
+	if (*msg.status != P2P_SC_SUCCESS_DEFERRED || !p2p->p2ps_prov)
+		goto out;
+
+	if (p2p->p2ps_prov->adv_id != adv_id ||
+	    os_memcmp(p2p->p2ps_prov->adv_mac, msg.adv_mac, ETH_ALEN)) {
+		p2p_dbg(p2p,
+			"P2PS Follow-on PD with mismatch Advertisement ID/MAC");
+		goto out;
+	}
+
+	if (p2p->p2ps_prov->session_id != session_id ||
+	    os_memcmp(p2p->p2ps_prov->session_mac, msg.session_mac, ETH_ALEN)) {
+		p2p_dbg(p2p, "P2PS Follow-on PD with mismatch Session ID/MAC");
+		goto out;
+	}
+
+	method = p2p->p2ps_prov->method;
+
+	conncap = p2p->cfg->p2ps_group_capability(p2p->cfg->cb_ctx,
+						  remote_conncap,
+						  p2p->p2ps_prov->conncap,
+						  &p2p->p2ps_prov->force_freq,
+						  &p2p->p2ps_prov->pref_freq);
+
+	resp_fcap.cpt = p2ps_own_preferred_cpt(p2p->p2ps_prov->cpt_priority,
+					       req_fcap->cpt);
+
+	p2p_dbg(p2p, "cpt: local:0x%x remote:0x%x result:0x%x",
+		p2p->p2ps_prov->cpt_mask, req_fcap->cpt, resp_fcap.cpt);
+
+	p2p_prepare_channel(p2p, dev, p2p->p2ps_prov->force_freq,
+			    p2p->p2ps_prov->pref_freq, 0);
+
+	/*
+	 * Ensure that if we asked for PIN originally, our method is consistent
+	 * with original request.
+	 */
+	if (method & WPS_CONFIG_DISPLAY)
+		method = WPS_CONFIG_KEYPAD;
+	else if (method & WPS_CONFIG_KEYPAD)
+		method = WPS_CONFIG_DISPLAY;
+
+	if (!conncap || !(msg.wps_config_methods & method)) {
+		/*
+		 * Reject this "Deferred Accept*
+		 * if incompatible conncap or method
+		 */
+		reject = P2P_SC_FAIL_INCOMPATIBLE_PARAMS;
+	} else if (!resp_fcap.cpt) {
+		p2p_dbg(p2p,
+			"Incompatible P2PS feature capability CPT bitmask");
+		reject = P2P_SC_FAIL_INCOMPATIBLE_PARAMS;
+	} else if ((remote_conncap & (P2PS_SETUP_NEW | P2PS_SETUP_CLIENT) ||
+		    msg.persistent_dev) && conncap != P2PS_SETUP_NEW &&
+		   msg.channel_list && msg.channel_list_len &&
+		   p2p_peer_channels_check(p2p, &p2p->channels, dev,
+					   msg.channel_list,
+					   msg.channel_list_len) < 0) {
+		p2p_dbg(p2p,
+			"No common channels in Follow-On Provision Discovery Request");
+		reject = P2P_SC_FAIL_NO_COMMON_CHANNELS;
+	} else {
+		reject = P2P_SC_SUCCESS;
+	}
+
+	dev->oper_freq = 0;
+	if (reject == P2P_SC_SUCCESS || reject == P2P_SC_SUCCESS_DEFERRED) {
+		u8 tmp;
+
+		if (msg.operating_channel)
+			dev->oper_freq =
+				p2p_channel_to_freq(msg.operating_channel[3],
+						    msg.operating_channel[4]);
+
+		if ((conncap & P2PS_SETUP_GROUP_OWNER) &&
+		    p2p_go_select_channel(p2p, dev, &tmp) < 0)
+			reject = P2P_SC_FAIL_NO_COMMON_CHANNELS;
+	}
+
+	p2p->p2ps_prov->status = reject;
+	p2p->p2ps_prov->conncap = conncap;
+
 out:
 	if (reject == P2P_SC_SUCCESS ||
 	    reject == P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE)
 		config_methods = msg.wps_config_methods;
 	else
 		config_methods = 0;
-	resp = p2p_build_prov_disc_resp(p2p, dev, msg.dialog_token, reject,
-					config_methods, adv_id,
-					msg.group_id, msg.group_id_len,
-					msg.persistent_ssid,
-					msg.persistent_ssid_len);
-	if (resp == NULL) {
-		p2p_parse_free(&msg);
-		return;
-	}
-	p2p_dbg(p2p, "Sending Provision Discovery Response");
-	if (rx_freq > 0)
-		freq = rx_freq;
-	else
-		freq = p2p_channel_to_freq(p2p->cfg->reg_class,
-					   p2p->cfg->channel);
-	if (freq < 0) {
-		p2p_dbg(p2p, "Unknown regulatory class/channel");
-		wpabuf_free(resp);
-		p2p_parse_free(&msg);
-		return;
-	}
-	p2p->pending_action_state = P2P_PENDING_PD_RESPONSE;
-	if (p2p_send_action(p2p, freq, sa, p2p->cfg->dev_addr,
-			    p2p->cfg->dev_addr,
-			    wpabuf_head(resp), wpabuf_len(resp), 200) < 0) {
-		p2p_dbg(p2p, "Failed to send Action frame");
-	} else
-		p2p->send_action_in_progress = 1;
 
-	wpabuf_free(resp);
+	/*
+	 * Send PD Response for an initial PD Request or for follow-on
+	 * PD Request with P2P_SC_SUCCESS_DEFERRED status.
+	 */
+	if (!msg.status || *msg.status == P2P_SC_SUCCESS_DEFERRED) {
+		resp = p2p_build_prov_disc_resp(p2p, dev, msg.dialog_token,
+						reject, config_methods, adv_id,
+						msg.group_id, msg.group_id_len,
+						msg.persistent_ssid,
+						msg.persistent_ssid_len,
+						(const u8 *) &resp_fcap,
+						sizeof(resp_fcap));
+		if (!resp) {
+			p2p_parse_free(&msg);
+			return;
+		}
+		p2p_dbg(p2p, "Sending Provision Discovery Response");
+		if (rx_freq > 0)
+			freq = rx_freq;
+		else
+			freq = p2p_channel_to_freq(p2p->cfg->reg_class,
+						   p2p->cfg->channel);
+		if (freq < 0) {
+			p2p_dbg(p2p, "Unknown regulatory class/channel");
+			wpabuf_free(resp);
+			p2p_parse_free(&msg);
+			return;
+		}
+		p2p->pending_action_state = P2P_PENDING_PD_RESPONSE;
+		if (p2p_send_action(p2p, freq, sa, p2p->cfg->dev_addr,
+				    p2p->cfg->dev_addr,
+				    wpabuf_head(resp), wpabuf_len(resp),
+				    200) < 0)
+			p2p_dbg(p2p, "Failed to send Action frame");
+		else
+			p2p->send_action_in_progress = 1;
+
+		wpabuf_free(resp);
+	}
+
+	if (!dev) {
+		p2p_parse_free(&msg);
+		return;
+	}
+
+	freq = 0;
+	if (reject == P2P_SC_SUCCESS && conncap == P2PS_SETUP_GROUP_OWNER) {
+		freq = p2p_channel_to_freq(p2p->op_reg_class,
+					   p2p->op_channel);
+		if (freq < 0)
+			freq = 0;
+	}
 
 	if (!p2p->cfg->p2ps_prov_complete) {
 		/* Don't emit anything */
@@ -696,7 +1028,7 @@
 					     NULL, adv_id, session_id,
 					     0, 0, msg.persistent_ssid,
 					     msg.persistent_ssid_len,
-					     0, 0, NULL);
+					     0, 0, NULL, NULL, 0, freq);
 	} else if (msg.status && *msg.status == P2P_SC_SUCCESS_DEFERRED &&
 		   p2p->p2ps_prov) {
 		p2p->p2ps_prov->status = reject;
@@ -709,7 +1041,7 @@
 						     session_id, conncap, 0,
 						     msg.persistent_ssid,
 						     msg.persistent_ssid_len, 0,
-						     0, NULL);
+						     0, NULL, NULL, 0, freq);
 		else
 			p2p->cfg->p2ps_prov_complete(p2p->cfg->cb_ctx,
 						     *msg.status,
@@ -719,7 +1051,9 @@
 						     passwd_id,
 						     msg.persistent_ssid,
 						     msg.persistent_ssid_len, 0,
-						     0, NULL);
+						     0, NULL,
+						     (const u8 *) &resp_fcap,
+						     sizeof(resp_fcap), freq);
 	} else if (msg.status && p2p->p2ps_prov) {
 		p2p->p2ps_prov->status = P2P_SC_SUCCESS;
 		p2p->cfg->p2ps_prov_complete(p2p->cfg->cb_ctx, *msg.status, sa,
@@ -728,7 +1062,9 @@
 					     passwd_id,
 					     msg.persistent_ssid,
 					     msg.persistent_ssid_len,
-					     0, 0, NULL);
+					     0, 0, NULL,
+					     (const u8 *) &resp_fcap,
+					     sizeof(resp_fcap), freq);
 	} else if (msg.status) {
 	} else if (auto_accept && reject == P2P_SC_SUCCESS) {
 		p2p->cfg->p2ps_prov_complete(p2p->cfg->cb_ctx, P2P_SC_SUCCESS,
@@ -737,7 +1073,9 @@
 					     conncap, passwd_id,
 					     msg.persistent_ssid,
 					     msg.persistent_ssid_len,
-					     0, 0, NULL);
+					     0, 0, NULL,
+					     (const u8 *) &resp_fcap,
+					     sizeof(resp_fcap), freq);
 	} else if (reject == P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE &&
 		   (!msg.session_info || !msg.session_info_len)) {
 		p2p->p2ps_prov->method = msg.wps_config_methods;
@@ -748,7 +1086,9 @@
 					     conncap, passwd_id,
 					     msg.persistent_ssid,
 					     msg.persistent_ssid_len,
-					     0, 1, NULL);
+					     0, 1, NULL,
+					     (const u8 *) &resp_fcap,
+					     sizeof(resp_fcap), freq);
 	} else if (reject == P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE) {
 		size_t buf_len = msg.session_info_len;
 		char *buf = os_malloc(2 * buf_len + 1);
@@ -764,14 +1104,46 @@
 				adv_mac, session_mac, group_mac, adv_id,
 				session_id, conncap, passwd_id,
 				msg.persistent_ssid, msg.persistent_ssid_len,
-				0, 1, buf);
+				0, 1, buf,
+				(const u8 *) &resp_fcap, sizeof(resp_fcap),
+				freq);
 
 			os_free(buf);
 		}
 	}
 
-	if (reject == P2P_SC_SUCCESS && p2p->cfg->prov_disc_req) {
+	/*
+	 * prov_disc_req callback is used to generate P2P-PROV-DISC-ENTER-PIN,
+	 * P2P-PROV-DISC-SHOW-PIN, and P2P-PROV-DISC-PBC-REQ events.
+	 * Call it either on legacy P2P PD or on P2PS PD only if we need to
+	 * enter/show PIN.
+	 *
+	 * The callback is called in the following cases:
+	 * 1. Legacy P2P PD request, response status SUCCESS
+	 * 2. P2PS advertiser, method: DISPLAY, autoaccept: TRUE,
+	 *    response status: SUCCESS
+	 * 3. P2PS advertiser, method  DISPLAY, autoaccept: FALSE,
+	 *    response status: INFO_CURRENTLY_UNAVAILABLE
+	 * 4. P2PS advertiser, method: KEYPAD, autoaccept==any,
+	 *    response status: INFO_CURRENTLY_UNAVAILABLE
+	 * 5. P2PS follow-on with SUCCESS_DEFERRED,
+	 *    advertiser role: DISPLAY, autoaccept: FALSE,
+	 *    seeker: KEYPAD, response status: SUCCESS
+	 */
+	if (p2p->cfg->prov_disc_req &&
+	    ((reject == P2P_SC_SUCCESS && !msg.adv_id) ||
+	     (!msg.status &&
+	     (reject == P2P_SC_SUCCESS ||
+	      reject == P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE) &&
+	      passwd_id == DEV_PW_USER_SPECIFIED) ||
+	     (!msg.status &&
+	      reject == P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE &&
+	      passwd_id == DEV_PW_REGISTRAR_SPECIFIED) ||
+	     (reject == P2P_SC_SUCCESS &&
+	      msg.status && *msg.status == P2P_SC_SUCCESS_DEFERRED &&
+	       passwd_id == DEV_PW_REGISTRAR_SPECIFIED))) {
 		const u8 *dev_addr = sa;
+
 		if (msg.p2p_device_addr)
 			dev_addr = msg.p2p_device_addr;
 		p2p->cfg->prov_disc_req(p2p->cfg->cb_ctx, sa,
@@ -783,10 +1155,133 @@
 					0,
 					msg.group_id, msg.group_id_len);
 	}
+
+	if (reject == P2P_SC_SUCCESS) {
+		switch (config_methods) {
+		case WPS_CONFIG_DISPLAY:
+			dev->wps_prov_info = WPS_CONFIG_KEYPAD;
+			break;
+		case WPS_CONFIG_KEYPAD:
+			dev->wps_prov_info = WPS_CONFIG_DISPLAY;
+			break;
+		case WPS_CONFIG_PUSHBUTTON:
+			dev->wps_prov_info = WPS_CONFIG_PUSHBUTTON;
+			break;
+		case WPS_CONFIG_P2PS:
+			dev->wps_prov_info = WPS_CONFIG_P2PS;
+			break;
+		default:
+			dev->wps_prov_info = 0;
+			break;
+		}
+
+		if (msg.intended_addr)
+			os_memcpy(dev->interface_addr, msg.intended_addr,
+				  ETH_ALEN);
+	}
 	p2p_parse_free(&msg);
 }
 
 
+static int p2p_validate_p2ps_pd_resp(struct p2p_data *p2p,
+				     struct p2p_message *msg)
+{
+	u8 conn_cap_go = 0;
+	u8 conn_cap_cli = 0;
+	u32 session_id;
+	u32 adv_id;
+
+#define P2PS_PD_RESP_CHECK(_val, _attr) \
+	do { \
+		if ((_val) && !msg->_attr) { \
+			p2p_dbg(p2p, "P2PS PD Response missing " #_attr); \
+			return -1; \
+		} \
+	} while (0)
+
+	P2PS_PD_RESP_CHECK(1, status);
+	P2PS_PD_RESP_CHECK(1, adv_id);
+	P2PS_PD_RESP_CHECK(1, adv_mac);
+	P2PS_PD_RESP_CHECK(1, capability);
+	P2PS_PD_RESP_CHECK(1, p2p_device_info);
+	P2PS_PD_RESP_CHECK(1, session_id);
+	P2PS_PD_RESP_CHECK(1, session_mac);
+	P2PS_PD_RESP_CHECK(1, feature_cap);
+
+	session_id = WPA_GET_LE32(msg->session_id);
+	adv_id = WPA_GET_LE32(msg->adv_id);
+
+	if (p2p->p2ps_prov->session_id != session_id) {
+		p2p_dbg(p2p,
+			"Ignore PD Response with unexpected Session ID");
+		return -1;
+	}
+
+	if (os_memcmp(p2p->p2ps_prov->session_mac, msg->session_mac,
+		      ETH_ALEN)) {
+		p2p_dbg(p2p,
+			"Ignore PD Response with unexpected Session MAC");
+		return -1;
+	}
+
+	if (p2p->p2ps_prov->adv_id != adv_id) {
+		p2p_dbg(p2p,
+			"Ignore PD Response with unexpected Advertisement ID");
+		return -1;
+	}
+
+	if (os_memcmp(p2p->p2ps_prov->adv_mac, msg->adv_mac, ETH_ALEN) != 0) {
+		p2p_dbg(p2p,
+			"Ignore PD Response with unexpected Advertisement MAC");
+		return -1;
+	}
+
+	if (msg->listen_channel) {
+		p2p_dbg(p2p,
+			"Ignore malformed PD Response - unexpected Listen Channel");
+		return -1;
+	}
+
+	if (*msg->status == P2P_SC_SUCCESS &&
+	    !(!!msg->conn_cap ^ !!msg->persistent_dev)) {
+		p2p_dbg(p2p,
+			"Ignore malformed PD Response - either conn_cap or persistent group should be present");
+		return -1;
+	}
+
+	if (msg->persistent_dev && *msg->status != P2P_SC_SUCCESS) {
+		p2p_dbg(p2p,
+			"Ignore malformed PD Response - persistent group is present, but the status isn't success");
+		return -1;
+	}
+
+	if (msg->conn_cap) {
+		conn_cap_go = *msg->conn_cap == P2PS_SETUP_GROUP_OWNER;
+		conn_cap_cli = *msg->conn_cap == P2PS_SETUP_CLIENT;
+	}
+
+	P2PS_PD_RESP_CHECK(msg->persistent_dev || conn_cap_go || conn_cap_cli,
+			   channel_list);
+	P2PS_PD_RESP_CHECK(msg->persistent_dev || conn_cap_go || conn_cap_cli,
+			   config_timeout);
+
+	P2PS_PD_RESP_CHECK(conn_cap_go, group_id);
+	P2PS_PD_RESP_CHECK(conn_cap_go, intended_addr);
+	P2PS_PD_RESP_CHECK(conn_cap_go, operating_channel);
+	/*
+	 * TODO: Also validate that operating channel is present if the device
+	 * is a GO in a persistent group. We can't do it here since we don't
+	 * know what is the role of the peer. It should be probably done in
+	 * p2ps_prov_complete callback, but currently operating channel isn't
+	 * passed to it.
+	 */
+
+#undef P2PS_PD_RESP_CHECK
+
+	return 0;
+}
+
+
 void p2p_process_prov_disc_resp(struct p2p_data *p2p, const u8 *sa,
 				const u8 *data, size_t len)
 {
@@ -794,24 +1289,26 @@
 	struct p2p_device *dev;
 	u16 report_config_methods = 0, req_config_methods;
 	u8 status = P2P_SC_SUCCESS;
-	int success = 0;
 	u32 adv_id = 0;
 	u8 conncap = P2PS_SETUP_NEW;
 	u8 adv_mac[ETH_ALEN];
-	u8 group_mac[ETH_ALEN];
+	const u8 *group_mac;
 	int passwd_id = DEV_PW_DEFAULT;
+	int p2ps_seeker;
 
 	if (p2p_parse(data, len, &msg))
 		return;
 
+	if (p2p->p2ps_prov && p2p_validate_p2ps_pd_resp(p2p, &msg)) {
+		p2p_parse_free(&msg);
+		return;
+	}
+
 	/* Parse the P2PS members present */
 	if (msg.status)
 		status = *msg.status;
 
-	if (msg.intended_addr)
-		os_memcpy(group_mac, msg.intended_addr, ETH_ALEN);
-	else
-		os_memset(group_mac, 0, ETH_ALEN);
+	group_mac = msg.intended_addr;
 
 	if (msg.adv_mac)
 		os_memcpy(adv_mac, msg.adv_mac, ETH_ALEN);
@@ -859,6 +1356,8 @@
 		p2p->pending_action_state = P2P_NO_PENDING_ACTION;
 	}
 
+	p2ps_seeker = p2p->p2ps_prov && p2p->p2ps_prov->pd_seeker;
+
 	/*
 	 * Use a local copy of the requested config methods since
 	 * p2p_reset_pending_pd() can clear this in the peer entry.
@@ -907,30 +1406,80 @@
 		passwd_id = DEV_PW_P2PS_DEFAULT;
 	}
 
-	if ((msg.conn_cap || msg.persistent_dev) &&
-	    msg.adv_id &&
-	    (status == P2P_SC_SUCCESS || status == P2P_SC_SUCCESS_DEFERRED) &&
+	if ((status == P2P_SC_SUCCESS || status == P2P_SC_SUCCESS_DEFERRED) &&
 	    p2p->p2ps_prov) {
+		dev->oper_freq = 0;
+
+		/*
+		 * Save the reported channel list and operating frequency.
+		 * Note that the specification mandates that the responder
+		 * should include in the channel list only channels reported by
+		 * the initiator, so this is only a sanity check, and if this
+		 * fails the flow would continue, although it would probably
+		 * fail. Same is true for the operating channel.
+		 */
+		if (msg.channel_list && msg.channel_list_len &&
+		    p2p_peer_channels_check(p2p, &p2p->channels, dev,
+					    msg.channel_list,
+					    msg.channel_list_len) < 0)
+			p2p_dbg(p2p, "P2PS PD Response - no common channels");
+
+		if (msg.operating_channel) {
+			if (p2p_channels_includes(&p2p->channels,
+						  msg.operating_channel[3],
+						  msg.operating_channel[4]) &&
+			    p2p_channels_includes(&dev->channels,
+						  msg.operating_channel[3],
+						  msg.operating_channel[4])) {
+				dev->oper_freq =
+					p2p_channel_to_freq(
+						msg.operating_channel[3],
+						msg.operating_channel[4]);
+			} else {
+				p2p_dbg(p2p,
+					"P2PS PD Response - invalid operating channel");
+			}
+		}
+
 		if (p2p->cfg->p2ps_prov_complete) {
+			int freq = 0;
+
+			if (conncap == P2PS_SETUP_GROUP_OWNER) {
+				u8 tmp;
+
+				/*
+				 * Re-select the operating channel as it is
+				 * possible that original channel is no longer
+				 * valid. This should not really fail.
+				 */
+				if (p2p_go_select_channel(p2p, dev, &tmp) < 0)
+					p2p_dbg(p2p,
+						"P2PS PD channel selection failed");
+
+				freq = p2p_channel_to_freq(p2p->op_reg_class,
+							   p2p->op_channel);
+				if (freq < 0)
+					freq = 0;
+			}
+
 			p2p->cfg->p2ps_prov_complete(
 				p2p->cfg->cb_ctx, status, sa, adv_mac,
 				p2p->p2ps_prov->session_mac,
 				group_mac, adv_id, p2p->p2ps_prov->session_id,
 				conncap, passwd_id, msg.persistent_ssid,
-				msg.persistent_ssid_len, 1, 0, NULL);
+				msg.persistent_ssid_len, 1, 0, NULL,
+				msg.feature_cap, msg.feature_cap_len, freq);
 		}
 		p2ps_prov_free(p2p);
-	}
-
-	if (status != P2P_SC_SUCCESS &&
-	    status != P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE &&
-	    status != P2P_SC_SUCCESS_DEFERRED && p2p->p2ps_prov) {
+	} else if (status != P2P_SC_SUCCESS &&
+		   status != P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE &&
+		   status != P2P_SC_SUCCESS_DEFERRED && p2p->p2ps_prov) {
 		if (p2p->cfg->p2ps_prov_complete)
 			p2p->cfg->p2ps_prov_complete(
 				p2p->cfg->cb_ctx, status, sa, adv_mac,
 				p2p->p2ps_prov->session_mac,
 				group_mac, adv_id, p2p->p2ps_prov->session_id,
-				0, 0, NULL, 0, 1, 0, NULL);
+				0, 0, NULL, 0, 1, 0, NULL, NULL, 0, 0);
 		p2ps_prov_free(p2p);
 	}
 
@@ -966,13 +1515,12 @@
 					p2p->cfg->cb_ctx, sa,
 					P2P_PROV_DISC_INFO_UNAVAILABLE,
 					adv_id, adv_mac, NULL);
-	} else if (msg.wps_config_methods != dev->req_config_methods ||
-		   status != P2P_SC_SUCCESS) {
+	} else if (status != P2P_SC_SUCCESS) {
 		p2p_dbg(p2p, "Peer rejected our Provision Discovery Request");
 		if (p2p->cfg->prov_disc_fail)
 			p2p->cfg->prov_disc_fail(p2p->cfg->cb_ctx, sa,
-						 P2P_PROV_DISC_REJECTED, 0,
-						 NULL, NULL);
+						 P2P_PROV_DISC_REJECTED,
+						 adv_id, adv_mac, NULL);
 		p2p_parse_free(&msg);
 		p2ps_prov_free(p2p);
 		goto out;
@@ -980,9 +1528,10 @@
 
 	/* Store the provisioning info */
 	dev->wps_prov_info = msg.wps_config_methods;
+	if (msg.intended_addr)
+		os_memcpy(dev->interface_addr, msg.intended_addr, ETH_ALEN);
 
 	p2p_parse_free(&msg);
-	success = 1;
 
 out:
 	dev->req_config_methods = 0;
@@ -994,7 +1543,28 @@
 		p2p_connect_send(p2p, dev);
 		return;
 	}
-	if (success && p2p->cfg->prov_disc_resp)
+
+	/*
+	 * prov_disc_resp callback is used to generate P2P-PROV-DISC-ENTER-PIN,
+	 * P2P-PROV-DISC-SHOW-PIN, and P2P-PROV-DISC-PBC-REQ events.
+	 * Call it only for a legacy P2P PD or for P2PS PD scenarios where
+	 * show/enter PIN events are needed.
+	 *
+	 * The callback is called in the following cases:
+	 * 1. Legacy P2P PD response with a status SUCCESS
+	 * 2. P2PS, advertiser method: DISPLAY, autoaccept: true,
+	 *    response status: SUCCESS, local method KEYPAD
+	 * 3. P2PS, advertiser method: KEYPAD,Seeker side,
+	 *    response status: INFO_CURRENTLY_UNAVAILABLE,
+	 *    local method: DISPLAY
+	 */
+	if (p2p->cfg->prov_disc_resp &&
+	    ((status == P2P_SC_SUCCESS && !adv_id) ||
+	     (p2ps_seeker && status == P2P_SC_SUCCESS &&
+	      passwd_id == DEV_PW_REGISTRAR_SPECIFIED) ||
+	     (p2ps_seeker &&
+	      status == P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE &&
+	      passwd_id == DEV_PW_USER_SPECIFIED)))
 		p2p->cfg->prov_disc_resp(p2p->cfg->cb_ctx, sa,
 					 report_config_methods);
 
@@ -1058,6 +1628,10 @@
 			"Building PD Request based on P2PS config method 0x%x status %d --> req_config_methods 0x%x",
 			p2p->p2ps_prov->method, p2p->p2ps_prov->status,
 			dev->req_config_methods);
+
+		if (p2p_prepare_channel(p2p, dev, p2p->p2ps_prov->force_freq,
+					p2p->p2ps_prov->pref_freq, 1) < 0)
+			return -1;
 	}
 
 	req = p2p_build_prov_disc_req(p2p, dev, join);
diff --git a/src/p2p/p2p_sd.c b/src/p2p/p2p_sd.c
index 1a2af04..559af9d 100644
--- a/src/p2p/p2p_sd.c
+++ b/src/p2p/p2p_sd.c
@@ -28,11 +28,11 @@
 	pos = wpabuf_head(wfd);
 	end = pos + wpabuf_len(wfd);
 
-	while (pos + 3 <= end) {
+	while (end - pos >= 3) {
 		subelem = *pos++;
 		len = WPA_GET_BE16(pos);
 		pos += 2;
-		if (pos + len > end)
+		if (len > end - pos)
 			break;
 
 		if (subelem == WFD_SUBELEM_DEVICE_INFO && len >= 6) {
@@ -355,11 +355,11 @@
 	pos++;
 
 	slen = *pos++;
-	next = pos + slen;
-	if (next > end || slen < 2) {
+	if (slen > end - pos || slen < 2) {
 		p2p_dbg(p2p, "Invalid IE in GAS Initial Request");
 		return;
 	}
+	next = pos + slen;
 	pos++; /* skip QueryRespLenLimit and PAME-BI */
 
 	if (*pos != ACCESS_NETWORK_QUERY_PROTOCOL) {
@@ -370,16 +370,16 @@
 
 	pos = next;
 	/* Query Request */
-	if (pos + 2 > end)
+	if (end - pos < 2)
 		return;
 	slen = WPA_GET_LE16(pos);
 	pos += 2;
-	if (pos + slen > end)
+	if (slen > end - pos)
 		return;
 	end = pos + slen;
 
 	/* ANQP Query Request */
-	if (pos + 4 > end)
+	if (end - pos < 4)
 		return;
 	if (WPA_GET_LE16(pos) != ANQP_VENDOR_SPECIFIC) {
 		p2p_dbg(p2p, "Unsupported ANQP Info ID %u", WPA_GET_LE16(pos));
@@ -389,7 +389,7 @@
 
 	slen = WPA_GET_LE16(pos);
 	pos += 2;
-	if (pos + slen > end || slen < 3 + 1) {
+	if (slen > end - pos || slen < 3 + 1) {
 		p2p_dbg(p2p, "Invalid ANQP Query Request length");
 		return;
 	}
@@ -401,7 +401,7 @@
 	}
 	pos += 4;
 
-	if (pos + 2 > end)
+	if (end - pos < 2)
 		return;
 	update_indic = WPA_GET_LE16(pos);
 	p2p_dbg(p2p, "Service Update Indicator: %u", update_indic);
@@ -512,11 +512,11 @@
 	pos++;
 
 	slen = *pos++;
-	next = pos + slen;
-	if (next > end || slen < 2) {
+	if (slen > end - pos || slen < 2) {
 		p2p_dbg(p2p, "Invalid IE in GAS Initial Response");
 		return;
 	}
+	next = pos + slen;
 	pos++; /* skip QueryRespLenLimit and PAME-BI */
 
 	if (*pos != ACCESS_NETWORK_QUERY_PROTOCOL) {
@@ -527,14 +527,14 @@
 
 	pos = next;
 	/* Query Response */
-	if (pos + 2 > end) {
+	if (end - pos < 2) {
 		p2p_dbg(p2p, "Too short Query Response");
 		return;
 	}
 	slen = WPA_GET_LE16(pos);
 	pos += 2;
 	p2p_dbg(p2p, "Query Response Length: %d", slen);
-	if (pos + slen > end) {
+	if (slen > end - pos) {
 		p2p_dbg(p2p, "Not enough Query Response data");
 		return;
 	}
@@ -552,7 +552,7 @@
 	}
 
 	/* ANQP Query Response */
-	if (pos + 4 > end)
+	if (end - pos < 4)
 		return;
 	if (WPA_GET_LE16(pos) != ANQP_VENDOR_SPECIFIC) {
 		p2p_dbg(p2p, "Unsupported ANQP Info ID %u", WPA_GET_LE16(pos));
@@ -562,7 +562,7 @@
 
 	slen = WPA_GET_LE16(pos);
 	pos += 2;
-	if (pos + slen > end || slen < 3 + 1) {
+	if (slen > end - pos || slen < 3 + 1) {
 		p2p_dbg(p2p, "Invalid ANQP Query Response length");
 		return;
 	}
@@ -574,7 +574,7 @@
 	}
 	pos += 4;
 
-	if (pos + 2 > end)
+	if (end - pos < 2)
 		return;
 	update_indic = WPA_GET_LE16(pos);
 	p2p_dbg(p2p, "Service Update Indicator: %u", update_indic);
@@ -727,11 +727,11 @@
 	pos++;
 
 	slen = *pos++;
-	next = pos + slen;
-	if (next > end || slen < 2) {
+	if (slen > end - pos || slen < 2) {
 		p2p_dbg(p2p, "Invalid IE in GAS Comeback Response");
 		return;
 	}
+	next = pos + slen;
 	pos++; /* skip QueryRespLenLimit and PAME-BI */
 
 	if (*pos != ACCESS_NETWORK_QUERY_PROTOCOL) {
@@ -742,14 +742,14 @@
 
 	pos = next;
 	/* Query Response */
-	if (pos + 2 > end) {
+	if (end - pos < 2) {
 		p2p_dbg(p2p, "Too short Query Response");
 		return;
 	}
 	slen = WPA_GET_LE16(pos);
 	pos += 2;
 	p2p_dbg(p2p, "Query Response Length: %d", slen);
-	if (pos + slen > end) {
+	if (slen > end - pos) {
 		p2p_dbg(p2p, "Not enough Query Response data");
 		return;
 	}
@@ -768,7 +768,7 @@
 	}
 
 	/* ANQP Query Response */
-	if (pos + 4 > end)
+	if (end - pos < 4)
 		return;
 	if (WPA_GET_LE16(pos) != ANQP_VENDOR_SPECIFIC) {
 		p2p_dbg(p2p, "Unsupported ANQP Info ID %u", WPA_GET_LE16(pos));
@@ -783,7 +783,7 @@
 		p2p_dbg(p2p, "Invalid ANQP Query Response length");
 		return;
 	}
-	if (pos + 4 > end)
+	if (end - pos < 4)
 		return;
 
 	if (WPA_GET_BE32(pos) != P2P_IE_VENDOR_TYPE) {
@@ -793,7 +793,7 @@
 	}
 	pos += 4;
 
-	if (pos + 2 > end)
+	if (end - pos < 2)
 		return;
 	p2p->sd_rx_update_indic = WPA_GET_LE16(pos);
 	p2p_dbg(p2p, "Service Update Indicator: %u", p2p->sd_rx_update_indic);
diff --git a/src/p2p/p2p_utils.c b/src/p2p/p2p_utils.c
index eee3c5a..2e2aa8a 100644
--- a/src/p2p/p2p_utils.c
+++ b/src/p2p/p2p_utils.c
@@ -9,6 +9,7 @@
 #include "includes.h"
 
 #include "common.h"
+#include "common/defs.h"
 #include "common/ieee802_11_common.h"
 #include "p2p_i.h"
 
@@ -67,59 +68,11 @@
  */
 int p2p_freq_to_channel(unsigned int freq, u8 *op_class, u8 *channel)
 {
-	/* TODO: more operating classes */
-	if (freq >= 2412 && freq <= 2472) {
-		if ((freq - 2407) % 5)
-			return -1;
+	if (ieee80211_freq_to_channel_ext(freq, 0, 0, op_class, channel) ==
+	    NUM_HOSTAPD_MODES)
+		return -1;
 
-		*op_class = 81; /* 2.407 GHz, channels 1..13 */
-		*channel = (freq - 2407) / 5;
-		return 0;
-	}
-
-	if (freq == 2484) {
-		*op_class = 82; /* channel 14 */
-		*channel = 14;
-		return 0;
-	}
-
-	if (freq >= 5180 && freq <= 5240) {
-		if ((freq - 5000) % 5)
-			return -1;
-
-		*op_class = 115; /* 5 GHz, channels 36..48 */
-		*channel = (freq - 5000) / 5;
-		return 0;
-	}
-
-	if (freq >= 5745 && freq <= 5805) {
-		if ((freq - 5000) % 5)
-			return -1;
-
-		*op_class = 124; /* 5 GHz, channels 149..161 */
-		*channel = (freq - 5000) / 5;
-		return 0;
-	}
-
-	if (freq >= 5745 && freq <= 5845) {
-		if ((freq - 5000) % 5)
-			return -1;
-
-		*op_class = 125; /* 5 GHz, channels 149..169 */
-		*channel = (freq - 5000) / 5;
-		return 0;
-	}
-
-	if (freq >= 58320 && freq <= 64800) {
-		if ((freq - 58320) % 2160)
-			return -1;
-
-		*op_class = 180; /* 60 GHz, channels 1..4 */
-		*channel = (freq - 56160) / 2160;
-		return 0;
-	}
-
-	return -1;
+	return 0;
 }
 
 
@@ -506,12 +459,22 @@
 			break;
 		for (j = 0; j < c->channels; j++) {
 			int freq;
+			unsigned int k;
+
 			if (idx + 1 == max_len)
 				break;
 			freq = p2p_channel_to_freq(c->reg_class,
 						   c->channel[j]);
 			if (freq < 0)
 				continue;
+
+			for (k = 0; k < idx; k++) {
+				if (freq_list[k] == freq)
+					break;
+			}
+
+			if (k < idx)
+				continue;
 			freq_list[idx++] = freq;
 		}
 	}
diff --git a/src/radius/radius.c b/src/radius/radius.c
index 1ebfd11..266b29f 100644
--- a/src/radius/radius.c
+++ b/src/radius/radius.c
@@ -1,6 +1,6 @@
 /*
  * RADIUS message processing
- * Copyright (c) 2002-2009, 2011-2014, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2002-2009, 2011-2015, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -173,6 +173,7 @@
 	{ RADIUS_ATTR_USER_PASSWORD, "User-Password", RADIUS_ATTR_UNDIST },
 	{ RADIUS_ATTR_NAS_IP_ADDRESS, "NAS-IP-Address", RADIUS_ATTR_IP },
 	{ RADIUS_ATTR_NAS_PORT, "NAS-Port", RADIUS_ATTR_INT32 },
+	{ RADIUS_ATTR_FRAMED_IP_ADDRESS, "Framed-IP-Address", RADIUS_ATTR_IP },
 	{ RADIUS_ATTR_FRAMED_MTU, "Framed-MTU", RADIUS_ATTR_INT32 },
 	{ RADIUS_ATTR_REPLY_MESSAGE, "Reply-Message", RADIUS_ATTR_TEXT },
 	{ RADIUS_ATTR_STATE, "State", RADIUS_ATTR_UNDIST },
@@ -703,7 +704,7 @@
 
 		attr = (struct radius_attr_hdr *) pos;
 
-		if (pos + attr->length > end || attr->length < sizeof(*attr))
+		if (attr->length > end - pos || attr->length < sizeof(*attr))
 			goto fail;
 
 		/* TODO: check that attr->length is suitable for attr->type */
diff --git a/src/radius/radius.h b/src/radius/radius.h
index 5977339..f14de53 100644
--- a/src/radius/radius.h
+++ b/src/radius/radius.h
@@ -1,6 +1,6 @@
 /*
  * RADIUS message processing
- * Copyright (c) 2002-2009, 2012, 2014, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2002-2009, 2012, 2014-2015, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -52,6 +52,7 @@
        RADIUS_ATTR_USER_PASSWORD = 2,
        RADIUS_ATTR_NAS_IP_ADDRESS = 4,
        RADIUS_ATTR_NAS_PORT = 5,
+       RADIUS_ATTR_FRAMED_IP_ADDRESS = 8,
        RADIUS_ATTR_FRAMED_MTU = 12,
        RADIUS_ATTR_REPLY_MESSAGE = 18,
        RADIUS_ATTR_STATE = 24,
diff --git a/src/radius/radius_client.c b/src/radius/radius_client.c
index 693f61e..0bcdb45 100644
--- a/src/radius/radius_client.c
+++ b/src/radius/radius_client.c
@@ -407,7 +407,6 @@
 static void radius_client_timer(void *eloop_ctx, void *timeout_ctx)
 {
 	struct radius_client_data *radius = eloop_ctx;
-	struct hostapd_radius_servers *conf = radius->conf;
 	struct os_reltime now;
 	os_time_t first;
 	struct radius_msg_list *entry, *prev, *tmp;
@@ -476,10 +475,10 @@
 			       (long int) (first - now.sec));
 	}
 
-	if (auth_failover && conf->num_auth_servers > 1)
+	if (auth_failover)
 		radius_client_auth_failover(radius);
 
-	if (acct_failover && conf->num_acct_servers > 1)
+	if (acct_failover)
 		radius_client_acct_failover(radius);
 }
 
@@ -1015,6 +1014,9 @@
 	int sel_sock;
 	struct radius_msg_list *entry;
 	struct hostapd_radius_servers *conf = radius->conf;
+	struct sockaddr_in disconnect_addr = {
+		.sin_family = AF_UNSPEC,
+	};
 
 	hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS,
 		       HOSTAPD_LEVEL_INFO,
@@ -1023,6 +1025,12 @@
 		       hostapd_ip_txt(&nserv->addr, abuf, sizeof(abuf)),
 		       nserv->port);
 
+	if (oserv && oserv == nserv) {
+		/* Reconnect to same server, flush */
+		if (auth)
+			radius_client_flush(radius, 1);
+	}
+
 	if (oserv && oserv != nserv &&
 	    (nserv->shared_secret_len != oserv->shared_secret_len ||
 	     os_memcmp(nserv->shared_secret, oserv->shared_secret,
@@ -1125,6 +1133,11 @@
 		}
 	}
 
+	/* Force a reconnect by disconnecting the socket first */
+	if (connect(sel_sock, (struct sockaddr *) &disconnect_addr,
+		    sizeof(disconnect_addr)) < 0)
+		wpa_printf(MSG_INFO, "disconnect[radius]: %s", strerror(errno));
+
 	if (connect(sel_sock, addr, addrlen) < 0) {
 		wpa_printf(MSG_INFO, "connect[radius]: %s", strerror(errno));
 		return -1;
diff --git a/src/radius/radius_das.c b/src/radius/radius_das.c
index 39ceea8..b7d991b 100644
--- a/src/radius/radius_das.c
+++ b/src/radius/radius_das.c
@@ -245,7 +245,7 @@
 				  (u8 *) &val, 4);
 	if (res == 4) {
 		u32 timestamp = ntohl(val);
-		if ((unsigned int) abs(now.sec - timestamp) >
+		if ((unsigned int) abs((int) (now.sec - timestamp)) >
 		    das->time_window) {
 			wpa_printf(MSG_DEBUG, "DAS: Unacceptable "
 				   "Event-Timestamp (%u; local time %u) in "
diff --git a/src/radius/radius_server.c b/src/radius/radius_server.c
index bdb7e42..744283c 100644
--- a/src/radius/radius_server.c
+++ b/src/radius/radius_server.c
@@ -265,6 +265,8 @@
 
 	struct dl_list erp_keys; /* struct eap_server_erp_key */
 
+	unsigned int tls_session_lifetime;
+
 	/**
 	 * wps - Wi-Fi Protected Setup context
 	 *
@@ -688,6 +690,7 @@
 	eap_conf.server_id = (const u8 *) data->server_id;
 	eap_conf.server_id_len = os_strlen(data->server_id);
 	eap_conf.erp = data->erp;
+	eap_conf.tls_session_lifetime = data->tls_session_lifetime;
 	radius_server_testing_options(sess, &eap_conf);
 	sess->eap = eap_server_sm_init(sess, &radius_server_eapol_cb,
 				       &eap_conf);
@@ -1745,6 +1748,7 @@
 	}
 	data->erp = conf->erp;
 	data->erp_domain = conf->erp_domain;
+	data->tls_session_lifetime = conf->tls_session_lifetime;
 
 	if (conf->subscr_remediation_url) {
 		data->subscr_remediation_url =
diff --git a/src/radius/radius_server.h b/src/radius/radius_server.h
index ca4e38c..7a25802 100644
--- a/src/radius/radius_server.h
+++ b/src/radius/radius_server.h
@@ -170,6 +170,8 @@
 
 	const char *erp_domain;
 
+	unsigned int tls_session_lifetime;
+
 	/**
 	 * wps - Wi-Fi Protected Setup context
 	 *
diff --git a/src/rsn_supp/pmksa_cache.c b/src/rsn_supp/pmksa_cache.c
index ef7b683..b221e12 100644
--- a/src/rsn_supp/pmksa_cache.c
+++ b/src/rsn_supp/pmksa_cache.c
@@ -130,7 +130,7 @@
 	struct rsn_pmksa_cache_entry *entry, *pos, *prev;
 	struct os_reltime now;
 
-	if (pmk_len > PMK_LEN)
+	if (pmk_len > PMK_LEN_MAX)
 		return NULL;
 
 	if (wpa_key_mgmt_suite_b(akmp) && !kck)
diff --git a/src/rsn_supp/pmksa_cache.h b/src/rsn_supp/pmksa_cache.h
index f8e040e..7ec09ea 100644
--- a/src/rsn_supp/pmksa_cache.h
+++ b/src/rsn_supp/pmksa_cache.h
@@ -15,7 +15,7 @@
 struct rsn_pmksa_cache_entry {
 	struct rsn_pmksa_cache_entry *next;
 	u8 pmkid[PMKID_LEN];
-	u8 pmk[PMK_LEN];
+	u8 pmk[PMK_LEN_MAX];
 	size_t pmk_len;
 	os_time_t expiration;
 	int akmp; /* WPA_KEY_MGMT_* */
diff --git a/src/rsn_supp/tdls.c b/src/rsn_supp/tdls.c
index 6b1df71..48752d7 100644
--- a/src/rsn_supp/tdls.c
+++ b/src/rsn_supp/tdls.c
@@ -12,6 +12,7 @@
 #include "utils/eloop.h"
 #include "utils/os.h"
 #include "common/ieee802_11_defs.h"
+#include "common/ieee802_11_common.h"
 #include "crypto/sha256.h"
 #include "crypto/crypto.h"
 #include "crypto/aes_wrap.h"
@@ -626,9 +627,15 @@
 	 */
 
 	if (peer->initiator) {
+		u8 addr[ETH_ALEN];
+
 		wpa_printf(MSG_DEBUG, "TDLS: TPK lifetime expired for " MACSTR
 			   " - try to renew", MAC2STR(peer->addr));
-		wpa_tdls_start(sm, peer->addr);
+		/* cache the peer address before do_teardown */
+		os_memcpy(addr, peer->addr, ETH_ALEN);
+		wpa_tdls_do_teardown(sm, peer,
+				     WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED);
+		wpa_tdls_start(sm, addr);
 	} else {
 		wpa_printf(MSG_DEBUG, "TDLS: TPK lifetime expired for " MACSTR
 			   " - tear down", MAC2STR(peer->addr));
@@ -2385,7 +2392,7 @@
 	wpa_printf(MSG_DEBUG, "TDLS: Sending TDLS Setup Confirm / "
 		   "TPK Handshake Message 3");
 	if (wpa_tdls_send_tpk_m3(sm, src_addr, dtoken, lnkid, peer) < 0)
-		goto error;
+		goto error_no_msg;
 
 	if (!peer->tpk_success) {
 		/*
@@ -2406,6 +2413,7 @@
 error:
 	wpa_tdls_send_error(sm, src_addr, WLAN_TDLS_SETUP_CONFIRM, dtoken, 1,
 			    status);
+error_no_msg:
 	wpa_tdls_disable_peer_link(sm, peer);
 	return -1;
 }
@@ -2859,14 +2867,14 @@
 }
 
 
-static int wpa_tdls_prohibited(struct wpa_eapol_ie_parse *elems)
+static int wpa_tdls_prohibited(struct ieee802_11_elems *elems)
 {
 	/* bit 38 - TDLS Prohibited */
 	return !!(elems->ext_capab[2 + 4] & 0x40);
 }
 
 
-static int wpa_tdls_chan_switch_prohibited(struct wpa_eapol_ie_parse *elems)
+static int wpa_tdls_chan_switch_prohibited(struct ieee802_11_elems *elems)
 {
 	/* bit 39 - TDLS Channel Switch Prohibited */
 	return !!(elems->ext_capab[2 + 4] & 0x80);
@@ -2875,12 +2883,13 @@
 
 void wpa_tdls_ap_ies(struct wpa_sm *sm, const u8 *ies, size_t len)
 {
-	struct wpa_eapol_ie_parse elems;
+	struct ieee802_11_elems elems;
 
 	sm->tdls_prohibited = 0;
 	sm->tdls_chan_switch_prohibited = 0;
 
-	if (ies == NULL || wpa_supplicant_parse_ies(ies, len, &elems) < 0 ||
+	if (ies == NULL ||
+	    ieee802_11_parse_elems(ies, len, &elems, 0) == ParseFailed ||
 	    elems.ext_capab == NULL || elems.ext_capab_len < 2 + 5)
 		return;
 
@@ -2896,9 +2905,10 @@
 
 void wpa_tdls_assoc_resp_ies(struct wpa_sm *sm, const u8 *ies, size_t len)
 {
-	struct wpa_eapol_ie_parse elems;
+	struct ieee802_11_elems elems;
 
-	if (ies == NULL || wpa_supplicant_parse_ies(ies, len, &elems) < 0 ||
+	if (ies == NULL ||
+	    ieee802_11_parse_elems(ies, len, &elems, 0) == ParseFailed ||
 	    elems.ext_capab == NULL || elems.ext_capab_len < 2 + 5)
 		return;
 
diff --git a/src/rsn_supp/wpa.c b/src/rsn_supp/wpa.c
index faffe36..0b5fe51 100644
--- a/src/rsn_supp/wpa.c
+++ b/src/rsn_supp/wpa.c
@@ -1,6 +1,7 @@
 /*
  * WPA Supplicant - WPA state machine and EAPOL-Key processing
  * Copyright (c) 2003-2015, Jouni Malinen <j@w1.fi>
+ * Copyright(c) 2015 Intel Deutschland GmbH
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -23,6 +24,9 @@
 #include "peerkey.h"
 
 
+static const u8 null_rsc[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
+
+
 /**
  * wpa_eapol_key_send - Send WPA/RSN EAPOL-Key message
  * @sm: Pointer to WPA state machine data from wpa_sm_init()
@@ -34,11 +38,13 @@
  * @msg: EAPOL-Key message
  * @msg_len: Length of message
  * @key_mic: Pointer to the buffer to which the EAPOL-Key MIC is written
+ * Returns: >= 0 on success, < 0 on failure
  */
-void wpa_eapol_key_send(struct wpa_sm *sm, const u8 *kck, size_t kck_len,
-			int ver, const u8 *dest, u16 proto,
-			u8 *msg, size_t msg_len, u8 *key_mic)
+int wpa_eapol_key_send(struct wpa_sm *sm, const u8 *kck, size_t kck_len,
+		       int ver, const u8 *dest, u16 proto,
+		       u8 *msg, size_t msg_len, u8 *key_mic)
 {
+	int ret = -1;
 	size_t mic_len = wpa_mic_len(sm->key_mgmt);
 
 	if (is_zero_ether_addr(dest) && is_zero_ether_addr(sm->bssid)) {
@@ -69,10 +75,11 @@
 	wpa_hexdump_key(MSG_DEBUG, "WPA: KCK", kck, kck_len);
 	wpa_hexdump(MSG_DEBUG, "WPA: Derived Key MIC", key_mic, mic_len);
 	wpa_hexdump(MSG_MSGDUMP, "WPA: TX EAPOL-Key", msg, msg_len);
-	wpa_sm_ether_send(sm, dest, proto, msg, msg_len);
+	ret = wpa_sm_ether_send(sm, dest, proto, msg, msg_len);
 	eapol_sm_notify_tx_eapol_key(sm->eapol);
 out:
 	os_free(msg);
+	return ret;
 }
 
 
@@ -206,15 +213,21 @@
 #endif /* CONFIG_IEEE80211R */
 	} else if (wpa_key_mgmt_wpa_ieee8021x(sm->key_mgmt) && sm->eapol) {
 		int res, pmk_len;
-		pmk_len = PMK_LEN;
-		res = eapol_sm_get_key(sm->eapol, sm->pmk, PMK_LEN);
+
+		if (sm->key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B_192)
+			pmk_len = PMK_LEN_SUITE_B_192;
+		else
+			pmk_len = PMK_LEN;
+		res = eapol_sm_get_key(sm->eapol, sm->pmk, pmk_len);
 		if (res) {
-			/*
-			 * EAP-LEAP is an exception from other EAP methods: it
-			 * uses only 16-byte PMK.
-			 */
-			res = eapol_sm_get_key(sm->eapol, sm->pmk, 16);
-			pmk_len = 16;
+			if (pmk_len == PMK_LEN) {
+				/*
+				 * EAP-LEAP is an exception from other EAP
+				 * methods: it uses only 16-byte PMK.
+				 */
+				res = eapol_sm_get_key(sm->eapol, sm->pmk, 16);
+				pmk_len = 16;
+			}
 		} else {
 #ifdef CONFIG_IEEE80211R
 			u8 buf[2 * PMK_LEN];
@@ -318,7 +331,7 @@
  * @wpa_ie: WPA/RSN IE
  * @wpa_ie_len: Length of the WPA/RSN IE
  * @ptk: PTK to use for keyed hash and encryption
- * Returns: 0 on success, -1 on failure
+ * Returns: >= 0 on success, < 0 on failure
  */
 int wpa_supplicant_send_2_of_4(struct wpa_sm *sm, const unsigned char *dst,
 			       const struct wpa_eapol_key *key,
@@ -409,10 +422,8 @@
 	os_memcpy(reply->key_nonce, nonce, WPA_NONCE_LEN);
 
 	wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Sending EAPOL-Key 2/4");
-	wpa_eapol_key_send(sm, ptk->kck, ptk->kck_len, ver, dst, ETH_P_EAPOL,
-			   rbuf, rlen, key_mic);
-
-	return 0;
+	return wpa_eapol_key_send(sm, ptk->kck, ptk->kck_len, ver, dst,
+				  ETH_P_EAPOL, rbuf, rlen, key_mic);
 }
 
 
@@ -500,6 +511,7 @@
 		os_memset(buf, 0, sizeof(buf));
 	}
 	sm->tptk_set = 1;
+	sm->tk_to_set = 1;
 
 	kde = sm->assoc_wpa_ie;
 	kde_len = sm->assoc_wpa_ie_len;
@@ -525,7 +537,7 @@
 #endif /* CONFIG_P2P */
 
 	if (wpa_supplicant_send_2_of_4(sm, sm->bssid, key, ver, sm->snonce,
-				       kde, kde_len, ptk))
+				       kde, kde_len, ptk) < 0)
 		goto failed;
 
 	os_free(kde_buf);
@@ -603,7 +615,12 @@
 	int keylen, rsclen;
 	enum wpa_alg alg;
 	const u8 *key_rsc;
-	u8 null_rsc[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
+
+	if (!sm->tk_to_set) {
+		wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+			"WPA: Do not re-install same PTK to the driver");
+		return 0;
+	}
 
 	wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
 		"WPA: Installing PTK to the driver");
@@ -643,6 +660,7 @@
 
 	/* TK is not needed anymore in supplicant */
 	os_memset(sm->ptk.tk, 0, WPA_TK_MAX_LEN);
+	sm->tk_to_set = 0;
 
 	if (sm->wpa_ptk_rekey) {
 		eloop_cancel_timeout(wpa_sm_rekey_ptk, sm, NULL);
@@ -753,12 +771,43 @@
 }
 
 
+static int wpa_supplicant_rsc_relaxation(const struct wpa_sm *sm,
+					 const u8 *rsc)
+{
+	int rsclen;
+
+	if (!sm->wpa_rsc_relaxation)
+		return 0;
+
+	rsclen = wpa_cipher_rsc_len(sm->group_cipher);
+
+	/*
+	 * Try to detect RSC (endian) corruption issue where the AP sends
+	 * the RSC bytes in EAPOL-Key message in the wrong order, both if
+	 * it's actually a 6-byte field (as it should be) and if it treats
+	 * it as an 8-byte field.
+	 * An AP model known to have this bug is the Sapido RB-1632.
+	 */
+	if (rsclen == 6 && ((rsc[5] && !rsc[0]) || rsc[6] || rsc[7])) {
+		wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+			"RSC %02x%02x%02x%02x%02x%02x%02x%02x is likely bogus, using 0",
+			rsc[0], rsc[1], rsc[2], rsc[3],
+			rsc[4], rsc[5], rsc[6], rsc[7]);
+
+		return 1;
+	}
+
+	return 0;
+}
+
+
 static int wpa_supplicant_pairwise_gtk(struct wpa_sm *sm,
 				       const struct wpa_eapol_key *key,
 				       const u8 *gtk, size_t gtk_len,
 				       int key_info)
 {
 	struct wpa_gtk_data gd;
+	const u8 *key_rsc;
 
 	/*
 	 * IEEE Std 802.11i-2004 - 8.5.2 EAPOL-Key frames - Figure 43x
@@ -784,11 +833,15 @@
 	os_memcpy(gd.gtk, gtk, gtk_len);
 	gd.gtk_len = gtk_len;
 
+	key_rsc = key->key_rsc;
+	if (wpa_supplicant_rsc_relaxation(sm, key->key_rsc))
+		key_rsc = null_rsc;
+
 	if (sm->group_cipher != WPA_CIPHER_GTK_NOT_USED &&
 	    (wpa_supplicant_check_group_cipher(sm, sm->group_cipher,
 					       gtk_len, gtk_len,
 					       &gd.key_rsc_len, &gd.alg) ||
-	     wpa_supplicant_install_gtk(sm, &gd, key->key_rsc))) {
+	     wpa_supplicant_install_gtk(sm, &gd, key_rsc))) {
 		wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
 			"RSN: Failed to install GTK");
 		os_memset(&gd, 0, sizeof(gd));
@@ -989,8 +1042,8 @@
 	if (sm->assoc_resp_ies) {
 		pos = sm->assoc_resp_ies;
 		end = pos + sm->assoc_resp_ies_len;
-		while (pos + 2 < end) {
-			if (pos + 2 + pos[1] > end)
+		while (end - pos > 2) {
+			if (2 + pos[1] > end - pos)
 				break;
 			switch (*pos) {
 			case WLAN_EID_MOBILITY_DOMAIN:
@@ -1086,7 +1139,7 @@
  * @ver: Version bits from EAPOL-Key Key Info
  * @key_info: Key Info
  * @ptk: PTK to use for keyed hash and encryption
- * Returns: 0 on success, -1 on failure
+ * Returns: >= 0 on success, < 0 on failure
  */
 int wpa_supplicant_send_4_of_4(struct wpa_sm *sm, const unsigned char *dst,
 			       const struct wpa_eapol_key *key,
@@ -1126,10 +1179,8 @@
 		WPA_PUT_BE16(reply->key_data_length, 0);
 
 	wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Sending EAPOL-Key 4/4");
-	wpa_eapol_key_send(sm, ptk->kck, ptk->kck_len, ver, dst, ETH_P_EAPOL,
-			   rbuf, rlen, key_mic);
-
-	return 0;
+	return wpa_eapol_key_send(sm, ptk->kck, ptk->kck_len, ver, dst,
+				  ETH_P_EAPOL, rbuf, rlen, key_mic);
 }
 
 
@@ -1202,7 +1253,7 @@
 #endif /* CONFIG_P2P */
 
 	if (wpa_supplicant_send_4_of_4(sm, sm->bssid, key, ver, key_info,
-				       &sm->ptk)) {
+				       &sm->ptk) < 0) {
 		goto failed;
 	}
 
@@ -1292,8 +1343,8 @@
 					      &gd->key_rsc_len, &gd->alg))
 		return -1;
 
-	wpa_hexdump(MSG_DEBUG, "RSN: received GTK in group key handshake",
-		    ie.gtk, ie.gtk_len);
+	wpa_hexdump_key(MSG_DEBUG, "RSN: received GTK in group key handshake",
+			ie.gtk, ie.gtk_len);
 	gd->keyidx = ie.gtk[0] & 0x3;
 	gd->tx = wpa_supplicant_gtk_tx_bit_workaround(sm,
 						      !!(ie.gtk[0] & BIT(2)));
@@ -1344,6 +1395,11 @@
 	gd->keyidx = (key_info & WPA_KEY_INFO_KEY_INDEX_MASK) >>
 		WPA_KEY_INFO_KEY_INDEX_SHIFT;
 	if (ver == WPA_KEY_INFO_TYPE_HMAC_MD5_RC4 && sm->ptk.kek_len == 16) {
+#ifdef CONFIG_NO_RC4
+		wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+			"WPA: RC4 not supported in the build");
+		return -1;
+#else /* CONFIG_NO_RC4 */
 		u8 ek[32];
 		if (key_data_len > sizeof(gd->gtk)) {
 			wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
@@ -1361,6 +1417,7 @@
 			return -1;
 		}
 		os_memset(ek, 0, sizeof(ek));
+#endif /* CONFIG_NO_RC4 */
 	} else if (ver == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) {
 		if (maxkeylen % 8) {
 			wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
@@ -1431,10 +1488,8 @@
 		WPA_PUT_BE16(reply->key_data_length, 0);
 
 	wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Sending EAPOL-Key 2/2");
-	wpa_eapol_key_send(sm, sm->ptk.kck, sm->ptk.kck_len, ver, sm->bssid,
-			   ETH_P_EAPOL, rbuf, rlen, key_mic);
-
-	return 0;
+	return wpa_eapol_key_send(sm, sm->ptk.kck, sm->ptk.kck_len, ver,
+				  sm->bssid, ETH_P_EAPOL, rbuf, rlen, key_mic);
 }
 
 
@@ -1447,6 +1502,7 @@
 	u16 key_info;
 	int rekey, ret;
 	struct wpa_gtk_data gd;
+	const u8 *key_rsc;
 
 	if (!sm->msg_3_of_4_ok) {
 		wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
@@ -1477,8 +1533,12 @@
 	if (ret)
 		goto failed;
 
-	if (wpa_supplicant_install_gtk(sm, &gd, key->key_rsc) ||
-	    wpa_supplicant_send_2_of_2(sm, key, ver, key_info))
+	key_rsc = key->key_rsc;
+	if (wpa_supplicant_rsc_relaxation(sm, key->key_rsc))
+		key_rsc = null_rsc;
+
+	if (wpa_supplicant_install_gtk(sm, &gd, key_rsc) ||
+	    wpa_supplicant_send_2_of_2(sm, key, ver, key_info) < 0)
 		goto failed;
 	os_memset(&gd, 0, sizeof(gd));
 
@@ -1575,6 +1635,11 @@
 	/* Decrypt key data here so that this operation does not need
 	 * to be implemented separately for each message type. */
 	if (ver == WPA_KEY_INFO_TYPE_HMAC_MD5_RC4 && sm->ptk.kek_len == 16) {
+#ifdef CONFIG_NO_RC4
+		wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+			"WPA: RC4 not supported in the build");
+		return -1;
+#else /* CONFIG_NO_RC4 */
 		u8 ek[32];
 		os_memcpy(ek, key->key_iv, 16);
 		os_memcpy(ek + 16, sm->ptk.kek, sm->ptk.kek_len);
@@ -1585,6 +1650,7 @@
 			return -1;
 		}
 		os_memset(ek, 0, sizeof(ek));
+#endif /* CONFIG_NO_RC4 */
 	} else if (ver == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES ||
 		   ver == WPA_KEY_INFO_TYPE_AES_128_CMAC ||
 		   sm->key_mgmt == WPA_KEY_MGMT_OSEN ||
@@ -2427,6 +2493,7 @@
 			sm->ssid_len = 0;
 		sm->wpa_ptk_rekey = config->wpa_ptk_rekey;
 		sm->p2p = config->p2p;
+		sm->wpa_rsc_relaxation = config->wpa_rsc_relaxation;
 	} else {
 		sm->network_ctx = NULL;
 		sm->peerkey_enabled = 0;
@@ -2437,6 +2504,7 @@
 		sm->ssid_len = 0;
 		sm->wpa_ptk_rekey = 0;
 		sm->p2p = 0;
+		sm->wpa_rsc_relaxation = 0;
 	}
 }
 
diff --git a/src/rsn_supp/wpa.h b/src/rsn_supp/wpa.h
index e163b70..1d27453 100644
--- a/src/rsn_supp/wpa.h
+++ b/src/rsn_supp/wpa.h
@@ -104,6 +104,7 @@
 	size_t ssid_len;
 	int wpa_ptk_rekey;
 	int p2p;
+	int wpa_rsc_relaxation;
 };
 
 #ifndef CONFIG_NO_WPA
@@ -180,7 +181,7 @@
 }
 
 static inline void wpa_sm_set_pmk(struct wpa_sm *sm, const u8 *pmk,
-				  size_t pmk_len)
+				  size_t pmk_len, const u8 *bssid)
 {
 }
 
@@ -320,7 +321,8 @@
 }
 
 static inline void wpa_sm_set_ptk_kck_kek(struct wpa_sm *sm, const u8 *ptk_kck,
-					  const u8 *ptk_kek)
+					  size_t ptk_kck_len,
+					  const u8 *ptk_kek, size_t ptk_kek_len)
 {
 }
 
diff --git a/src/rsn_supp/wpa_ft.c b/src/rsn_supp/wpa_ft.c
index 06dea05..205793e 100644
--- a/src/rsn_supp/wpa_ft.c
+++ b/src/rsn_supp/wpa_ft.c
@@ -168,9 +168,7 @@
 	pos = (u8 *) (rsnie + 1);
 
 	/* Group Suite Selector */
-	if (sm->group_cipher != WPA_CIPHER_CCMP &&
-	    sm->group_cipher != WPA_CIPHER_GCMP &&
-	    sm->group_cipher != WPA_CIPHER_TKIP) {
+	if (!wpa_cipher_valid_group(sm->group_cipher)) {
 		wpa_printf(MSG_WARNING, "FT: Invalid group cipher (%d)",
 			   sm->group_cipher);
 		os_free(buf);
diff --git a/src/rsn_supp/wpa_i.h b/src/rsn_supp/wpa_i.h
index 965a9c1..dba6b62 100644
--- a/src/rsn_supp/wpa_i.h
+++ b/src/rsn_supp/wpa_i.h
@@ -19,11 +19,12 @@
  * struct wpa_sm - Internal WPA state machine data
  */
 struct wpa_sm {
-	u8 pmk[PMK_LEN];
+	u8 pmk[PMK_LEN_MAX];
 	size_t pmk_len;
 	struct wpa_ptk ptk, tptk;
 	int ptk_set, tptk_set;
 	unsigned int msg_3_of_4_ok:1;
+	unsigned int tk_to_set:1;
 	u8 snonce[WPA_NONCE_LEN];
 	u8 anonce[WPA_NONCE_LEN]; /* ANonce from the last 1/4 msg */
 	int renew_snonce;
@@ -60,6 +61,7 @@
 	size_t ssid_len;
 	int wpa_ptk_rekey;
 	int p2p;
+	int wpa_rsc_relaxation;
 
 	u8 own_addr[ETH_ALEN];
 	const char *ifname;
@@ -342,16 +344,14 @@
 static inline int wpa_sm_key_mgmt_set_pmk(struct wpa_sm *sm,
 					  const u8 *pmk, size_t pmk_len)
 {
-	if (!sm->proactive_key_caching)
-		return 0;
 	if (!sm->ctx->key_mgmt_set_pmk)
 		return -1;
 	return sm->ctx->key_mgmt_set_pmk(sm->ctx->ctx, pmk, pmk_len);
 }
 
-void wpa_eapol_key_send(struct wpa_sm *sm, const u8 *kck, size_t kck_len,
-			int ver, const u8 *dest, u16 proto,
-			u8 *msg, size_t msg_len, u8 *key_mic);
+int wpa_eapol_key_send(struct wpa_sm *sm, const u8 *kck, size_t kck_len,
+		       int ver, const u8 *dest, u16 proto,
+		       u8 *msg, size_t msg_len, u8 *key_mic);
 int wpa_supplicant_send_2_of_4(struct wpa_sm *sm, const unsigned char *dst,
 			       const struct wpa_eapol_key *key,
 			       int ver, const u8 *nonce,
diff --git a/src/rsn_supp/wpa_ie.c b/src/rsn_supp/wpa_ie.c
index 0c37b35..c44844e 100644
--- a/src/rsn_supp/wpa_ie.c
+++ b/src/rsn_supp/wpa_ie.c
@@ -378,7 +378,7 @@
 		return 0;
 	}
 
-	if (pos + 1 + RSN_SELECTOR_LEN < end &&
+	if (1 + RSN_SELECTOR_LEN < end - pos &&
 	    pos[1] >= RSN_SELECTOR_LEN + PMKID_LEN &&
 	    RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_PMKID) {
 		ie->pmkid = pos + 2 + RSN_SELECTOR_LEN;
@@ -491,13 +491,13 @@
 	int ret = 0;
 
 	os_memset(ie, 0, sizeof(*ie));
-	for (pos = buf, end = pos + len; pos + 1 < end; pos += 2 + pos[1]) {
+	for (pos = buf, end = pos + len; end - pos > 1; pos += 2 + pos[1]) {
 		if (pos[0] == 0xdd &&
 		    ((pos == buf + len - 1) || pos[1] == 0)) {
 			/* Ignore padding */
 			break;
 		}
-		if (pos + 2 + pos[1] > end) {
+		if (2 + pos[1] > end - pos) {
 			wpa_printf(MSG_DEBUG, "WPA: EAPOL-Key Key Data "
 				   "underflow (ie=%d len=%d pos=%d)",
 				   pos[0], pos[1], (int) (pos - buf));
diff --git a/src/tls/tlsv1_client.c b/src/tls/tlsv1_client.c
index 533286c..846d293 100644
--- a/src/tls/tlsv1_client.c
+++ b/src/tls/tlsv1_client.c
@@ -691,18 +691,16 @@
 	if (data == NULL || data_len == 0)
 		return 0;
 
-	pos = conn->client_hello_ext = os_malloc(6 + data_len);
+	pos = conn->client_hello_ext = os_malloc(4 + data_len);
 	if (pos == NULL)
 		return -1;
 
-	WPA_PUT_BE16(pos, 4 + data_len);
-	pos += 2;
 	WPA_PUT_BE16(pos, ext_type);
 	pos += 2;
 	WPA_PUT_BE16(pos, data_len);
 	pos += 2;
 	os_memcpy(pos, data, data_len);
-	conn->client_hello_ext_len = 6 + data_len;
+	conn->client_hello_ext_len = 4 + data_len;
 
 	if (ext_type == TLS_EXT_PAC_OPAQUE) {
 		conn->session_ticket_included = 1;
@@ -714,12 +712,12 @@
 
 
 /**
- * tlsv1_client_get_keys - Get master key and random data from TLS connection
+ * tlsv1_client_get_random - Get random data from TLS connection
  * @conn: TLSv1 client connection data from tlsv1_client_init()
- * @keys: Structure of key/random data (filled on success)
+ * @keys: Structure of random data (filled on success)
  * Returns: 0 on success, -1 on failure
  */
-int tlsv1_client_get_keys(struct tlsv1_client *conn, struct tls_keys *keys)
+int tlsv1_client_get_random(struct tlsv1_client *conn, struct tls_random *keys)
 {
 	os_memset(keys, 0, sizeof(*keys));
 	if (conn->state == CLIENT_HELLO)
@@ -813,9 +811,14 @@
 }
 
 
-void tlsv1_client_set_time_checks(struct tlsv1_client *conn, int enabled)
+/**
+ * tlsv1_client_set_flags - Set connection flags
+ * @conn: TLSv1 client connection data from tlsv1_client_init()
+ * @flags: TLS_CONN_* bitfield
+ */
+void tlsv1_client_set_flags(struct tlsv1_client *conn, unsigned int flags)
 {
-	conn->disable_time_checks = !enabled;
+	conn->flags = flags;
 }
 
 
@@ -828,3 +831,38 @@
 	conn->session_ticket_cb = cb;
 	conn->session_ticket_cb_ctx = ctx;
 }
+
+
+void tlsv1_client_set_cb(struct tlsv1_client *conn,
+			 void (*event_cb)(void *ctx, enum tls_event ev,
+					  union tls_event_data *data),
+			 void *cb_ctx,
+			 int cert_in_cb)
+{
+	conn->event_cb = event_cb;
+	conn->cb_ctx = cb_ctx;
+	conn->cert_in_cb = !!cert_in_cb;
+}
+
+
+int tlsv1_client_get_version(struct tlsv1_client *conn, char *buf,
+			     size_t buflen)
+{
+	if (!conn)
+		return -1;
+	switch (conn->rl.tls_version) {
+	case TLS_VERSION_1:
+		os_strlcpy(buf, "TLSv1", buflen);
+		break;
+	case TLS_VERSION_1_1:
+		os_strlcpy(buf, "TLSv1.1", buflen);
+		break;
+	case TLS_VERSION_1_2:
+		os_strlcpy(buf, "TLSv1.2", buflen);
+		break;
+	default:
+		return -1;
+	}
+
+	return 0;
+}
diff --git a/src/tls/tlsv1_client.h b/src/tls/tlsv1_client.h
index 8ec85f1..40fa6c7 100644
--- a/src/tls/tlsv1_client.h
+++ b/src/tls/tlsv1_client.h
@@ -36,12 +36,12 @@
 int tlsv1_client_resumed(struct tlsv1_client *conn);
 int tlsv1_client_hello_ext(struct tlsv1_client *conn, int ext_type,
 			   const u8 *data, size_t data_len);
-int tlsv1_client_get_keys(struct tlsv1_client *conn, struct tls_keys *keys);
+int tlsv1_client_get_random(struct tlsv1_client *conn, struct tls_random *data);
 int tlsv1_client_get_keyblock_size(struct tlsv1_client *conn);
 int tlsv1_client_set_cipher_list(struct tlsv1_client *conn, u8 *ciphers);
 int tlsv1_client_set_cred(struct tlsv1_client *conn,
 			  struct tlsv1_credentials *cred);
-void tlsv1_client_set_time_checks(struct tlsv1_client *conn, int enabled);
+void tlsv1_client_set_flags(struct tlsv1_client *conn, unsigned int flags);
 
 typedef int (*tlsv1_client_session_ticket_cb)
 (void *ctx, const u8 *ticket, size_t len, const u8 *client_random,
@@ -51,4 +51,12 @@
 					tlsv1_client_session_ticket_cb cb,
 					void *ctx);
 
+void tlsv1_client_set_cb(struct tlsv1_client *conn,
+			 void (*event_cb)(void *ctx, enum tls_event ev,
+					  union tls_event_data *data),
+			 void *cb_ctx,
+			 int cert_in_cb);
+int tlsv1_client_get_version(struct tlsv1_client *conn, char *buf,
+			     size_t buflen);
+
 #endif /* TLSV1_CLIENT_H */
diff --git a/src/tls/tlsv1_client_i.h b/src/tls/tlsv1_client_i.h
index 55fdcf8..6c4dbc7 100644
--- a/src/tls/tlsv1_client_i.h
+++ b/src/tls/tlsv1_client_i.h
@@ -29,11 +29,13 @@
 	u8 alert_level;
 	u8 alert_description;
 
+	unsigned int flags; /* TLS_CONN_* bitfield */
+
 	unsigned int certificate_requested:1;
 	unsigned int session_resumed:1;
 	unsigned int session_ticket_included:1;
 	unsigned int use_session_ticket:1;
-	unsigned int disable_time_checks:1;
+	unsigned int cert_in_cb:1;
 
 	struct crypto_public_key *server_rsa_key;
 
@@ -64,6 +66,10 @@
 	void *session_ticket_cb_ctx;
 
 	struct wpabuf *partial_input;
+
+	void (*event_cb)(void *ctx, enum tls_event ev,
+			 union tls_event_data *data);
+	void *cb_ctx;
 };
 
 
diff --git a/src/tls/tlsv1_client_read.c b/src/tls/tlsv1_client_read.c
index 9ce9680..40c6a46 100644
--- a/src/tls/tlsv1_client_read.c
+++ b/src/tls/tlsv1_client_read.c
@@ -27,6 +27,17 @@
 					 const u8 *in_data, size_t *in_len);
 
 
+static int tls_version_disabled(struct tlsv1_client *conn, u16 ver)
+{
+	return (((conn->flags & TLS_CONN_DISABLE_TLSv1_0) &&
+		 ver == TLS_VERSION_1) ||
+		((conn->flags & TLS_CONN_DISABLE_TLSv1_1) &&
+		 ver == TLS_VERSION_1_1) ||
+		((conn->flags & TLS_CONN_DISABLE_TLSv1_2) &&
+		 ver == TLS_VERSION_1_2));
+}
+
+
 static int tls_process_server_hello(struct tlsv1_client *conn, u8 ct,
 				    const u8 *in_data, size_t *in_len)
 {
@@ -76,7 +87,8 @@
 	if (end - pos < 2)
 		goto decode_error;
 	tls_version = WPA_GET_BE16(pos);
-	if (!tls_version_ok(tls_version)) {
+	if (!tls_version_ok(tls_version) ||
+	    tls_version_disabled(conn, tls_version)) {
 		wpa_printf(MSG_DEBUG, "TLSv1: Unexpected protocol version in "
 			   "ServerHello %u.%u", pos[0], pos[1]);
 		tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
@@ -211,6 +223,73 @@
 }
 
 
+static void tls_peer_cert_event(struct tlsv1_client *conn, int depth,
+				struct x509_certificate *cert)
+{
+	union tls_event_data ev;
+	struct wpabuf *cert_buf = NULL;
+#ifdef CONFIG_SHA256
+	u8 hash[32];
+#endif /* CONFIG_SHA256 */
+	char subject[128];
+
+	if (!conn->event_cb)
+		return;
+
+	os_memset(&ev, 0, sizeof(ev));
+	if (conn->cred->cert_probe || conn->cert_in_cb) {
+		cert_buf = wpabuf_alloc_copy(cert->cert_start,
+					     cert->cert_len);
+		ev.peer_cert.cert = cert_buf;
+	}
+#ifdef CONFIG_SHA256
+	if (cert_buf) {
+		const u8 *addr[1];
+		size_t len[1];
+		addr[0] = wpabuf_head(cert_buf);
+		len[0] = wpabuf_len(cert_buf);
+		if (sha256_vector(1, addr, len, hash) == 0) {
+			ev.peer_cert.hash = hash;
+			ev.peer_cert.hash_len = sizeof(hash);
+		}
+	}
+#endif /* CONFIG_SHA256 */
+
+	ev.peer_cert.depth = depth;
+	x509_name_string(&cert->subject, subject, sizeof(subject));
+	ev.peer_cert.subject = subject;
+
+	conn->event_cb(conn->cb_ctx, TLS_PEER_CERTIFICATE, &ev);
+	wpabuf_free(cert_buf);
+}
+
+
+static void tls_cert_chain_failure_event(struct tlsv1_client *conn, int depth,
+					 struct x509_certificate *cert,
+					 enum tls_fail_reason reason,
+					 const char *reason_txt)
+{
+	struct wpabuf *cert_buf = NULL;
+	union tls_event_data ev;
+	char subject[128];
+
+	if (!conn->event_cb)
+		return;
+
+	os_memset(&ev, 0, sizeof(ev));
+	ev.cert_fail.depth = depth;
+	x509_name_string(&cert->subject, subject, sizeof(subject));
+	ev.peer_cert.subject = subject;
+	ev.cert_fail.reason = reason;
+	ev.cert_fail.reason_txt = reason_txt;
+	cert_buf = wpabuf_alloc_copy(cert->cert_start,
+				     cert->cert_len);
+	ev.cert_fail.cert = cert_buf;
+	conn->event_cb(conn->cb_ctx, TLS_CERT_CHAIN_FAILURE, &ev);
+	wpabuf_free(cert_buf);
+}
+
+
 static int tls_process_certificate(struct tlsv1_client *conn, u8 ct,
 				   const u8 *in_data, size_t *in_len)
 {
@@ -354,6 +433,8 @@
 			return -1;
 		}
 
+		tls_peer_cert_event(conn, idx, cert);
+
 		if (last == NULL)
 			chain = cert;
 		else
@@ -364,31 +445,99 @@
 		pos += cert_len;
 	}
 
-	if (conn->cred &&
-	    x509_certificate_chain_validate(conn->cred->trusted_certs, chain,
-					    &reason, conn->disable_time_checks)
-	    < 0) {
+	if (conn->cred && conn->cred->server_cert_only && chain) {
+		u8 hash[SHA256_MAC_LEN];
+		char buf[128];
+
+		wpa_printf(MSG_DEBUG,
+			   "TLSv1: Validate server certificate hash");
+		x509_name_string(&chain->subject, buf, sizeof(buf));
+		wpa_printf(MSG_DEBUG, "TLSv1: 0: %s", buf);
+		if (sha256_vector(1, &chain->cert_start, &chain->cert_len,
+				  hash) < 0 ||
+		    os_memcmp(conn->cred->srv_cert_hash, hash,
+			      SHA256_MAC_LEN) != 0) {
+			wpa_printf(MSG_DEBUG,
+				   "TLSv1: Server certificate hash mismatch");
+			wpa_hexdump(MSG_MSGDUMP, "TLSv1: SHA256 hash",
+				    hash, SHA256_MAC_LEN);
+			if (conn->event_cb) {
+				union tls_event_data ev;
+
+				os_memset(&ev, 0, sizeof(ev));
+				ev.cert_fail.reason = TLS_FAIL_UNSPECIFIED;
+				ev.cert_fail.reason_txt =
+					"Server certificate mismatch";
+				ev.cert_fail.subject = buf;
+				conn->event_cb(conn->cb_ctx,
+					       TLS_CERT_CHAIN_FAILURE, &ev);
+			}
+			tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+				  TLS_ALERT_BAD_CERTIFICATE);
+			x509_certificate_chain_free(chain);
+			return -1;
+		}
+	} else if (conn->cred && conn->cred->cert_probe) {
+		wpa_printf(MSG_DEBUG,
+			   "TLSv1: Reject server certificate on probe-only rune");
+		if (conn->event_cb) {
+			union tls_event_data ev;
+			char buf[128];
+
+			os_memset(&ev, 0, sizeof(ev));
+			ev.cert_fail.reason = TLS_FAIL_SERVER_CHAIN_PROBE;
+			ev.cert_fail.reason_txt =
+				"Server certificate chain probe";
+			if (chain) {
+				x509_name_string(&chain->subject, buf,
+						 sizeof(buf));
+				ev.cert_fail.subject = buf;
+			}
+			conn->event_cb(conn->cb_ctx, TLS_CERT_CHAIN_FAILURE,
+				       &ev);
+		}
+		tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+			  TLS_ALERT_BAD_CERTIFICATE);
+		x509_certificate_chain_free(chain);
+		return -1;
+	} else if (conn->cred && conn->cred->ca_cert_verify &&
+		   x509_certificate_chain_validate(
+			   conn->cred->trusted_certs, chain, &reason,
+			   !!(conn->flags & TLS_CONN_DISABLE_TIME_CHECKS))
+		   < 0) {
 		int tls_reason;
 		wpa_printf(MSG_DEBUG, "TLSv1: Server certificate chain "
 			   "validation failed (reason=%d)", reason);
 		switch (reason) {
 		case X509_VALIDATE_BAD_CERTIFICATE:
 			tls_reason = TLS_ALERT_BAD_CERTIFICATE;
+			tls_cert_chain_failure_event(
+				conn, 0, chain, TLS_FAIL_BAD_CERTIFICATE,
+				"bad certificate");
 			break;
 		case X509_VALIDATE_UNSUPPORTED_CERTIFICATE:
 			tls_reason = TLS_ALERT_UNSUPPORTED_CERTIFICATE;
 			break;
 		case X509_VALIDATE_CERTIFICATE_REVOKED:
 			tls_reason = TLS_ALERT_CERTIFICATE_REVOKED;
+			tls_cert_chain_failure_event(
+				conn, 0, chain, TLS_FAIL_REVOKED,
+				"certificate revoked");
 			break;
 		case X509_VALIDATE_CERTIFICATE_EXPIRED:
 			tls_reason = TLS_ALERT_CERTIFICATE_EXPIRED;
+			tls_cert_chain_failure_event(
+				conn, 0, chain, TLS_FAIL_EXPIRED,
+				"certificate has expired or is not yet valid");
 			break;
 		case X509_VALIDATE_CERTIFICATE_UNKNOWN:
 			tls_reason = TLS_ALERT_CERTIFICATE_UNKNOWN;
 			break;
 		case X509_VALIDATE_UNKNOWN_CA:
 			tls_reason = TLS_ALERT_UNKNOWN_CA;
+			tls_cert_chain_failure_event(
+				conn, 0, chain, TLS_FAIL_UNTRUSTED,
+				"unknown CA");
 			break;
 		default:
 			tls_reason = TLS_ALERT_BAD_CERTIFICATE;
@@ -399,6 +548,19 @@
 		return -1;
 	}
 
+	if (conn->cred && !conn->cred->server_cert_only && chain &&
+	    (chain->extensions_present & X509_EXT_EXT_KEY_USAGE) &&
+	    !(chain->ext_key_usage &
+	      (X509_EXT_KEY_USAGE_ANY | X509_EXT_KEY_USAGE_SERVER_AUTH))) {
+		tls_cert_chain_failure_event(
+			conn, 0, chain, TLS_FAIL_BAD_CERTIFICATE,
+			"certificate not allowed for server authentication");
+		tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+			  TLS_ALERT_BAD_CERTIFICATE);
+		x509_certificate_chain_free(chain);
+		return -1;
+	}
+
 	x509_certificate_chain_free(chain);
 
 	*in_len = end - in_data;
@@ -507,7 +669,7 @@
 	server_params_end = pos;
 
 	if (key_exchange == TLS_KEY_X_DHE_RSA) {
-		u8 hash[MD5_MAC_LEN + SHA1_MAC_LEN];
+		u8 hash[64];
 		int hlen;
 
 		if (conn->rl.tls_version == TLS_VERSION_1_2) {
@@ -524,18 +686,21 @@
 			 */
 			if (end - pos < 2)
 				goto fail;
-			if (pos[0] != TLS_HASH_ALG_SHA256 ||
+			if ((pos[0] != TLS_HASH_ALG_SHA256 &&
+			     pos[0] != TLS_HASH_ALG_SHA384 &&
+			     pos[0] != TLS_HASH_ALG_SHA512) ||
 			    pos[1] != TLS_SIGN_ALG_RSA) {
 				wpa_printf(MSG_DEBUG, "TLSv1.2: Unsupported hash(%u)/signature(%u) algorithm",
 					   pos[0], pos[1]);
 				goto fail;
 			}
-			pos += 2;
 
 			hlen = tlsv12_key_x_server_params_hash(
-				conn->rl.tls_version, conn->client_random,
+				conn->rl.tls_version, pos[0],
+				conn->client_random,
 				conn->server_random, server_params,
 				server_params_end - server_params, hash);
+			pos += 2;
 #else /* CONFIG_TLSV12 */
 			goto fail;
 #endif /* CONFIG_TLSV12 */
diff --git a/src/tls/tlsv1_client_write.c b/src/tls/tlsv1_client_write.c
index d192f44..b1906b2 100644
--- a/src/tls/tlsv1_client_write.c
+++ b/src/tls/tlsv1_client_write.c
@@ -47,8 +47,28 @@
 	u8 *hello, *end, *pos, *hs_length, *hs_start, *rhdr;
 	struct os_time now;
 	size_t len, i;
+	u8 *ext_start;
+	u16 tls_version = TLS_VERSION;
 
-	wpa_printf(MSG_DEBUG, "TLSv1: Send ClientHello");
+	/* Pick the highest locally enabled TLS version */
+#ifdef CONFIG_TLSV12
+	if ((conn->flags & TLS_CONN_DISABLE_TLSv1_2) &&
+	    tls_version == TLS_VERSION_1_2)
+		tls_version = TLS_VERSION_1_1;
+#endif /* CONFIG_TLSV12 */
+#ifdef CONFIG_TLSV11
+	if ((conn->flags & TLS_CONN_DISABLE_TLSv1_1) &&
+	    tls_version == TLS_VERSION_1_1)
+		tls_version = TLS_VERSION_1;
+#endif /* CONFIG_TLSV11 */
+	if ((conn->flags & TLS_CONN_DISABLE_TLSv1_0) &&
+	    tls_version == TLS_VERSION_1) {
+		wpa_printf(MSG_INFO, "TLSv1: No TLS version allowed");
+		return NULL;
+	}
+
+	wpa_printf(MSG_DEBUG, "TLSv1: Send ClientHello (ver %s)",
+		   tls_version_str(tls_version));
 	*out_len = 0;
 
 	os_get_time(&now);
@@ -61,7 +81,7 @@
 	wpa_hexdump(MSG_MSGDUMP, "TLSv1: client_random",
 		    conn->client_random, TLS_RANDOM_LEN);
 
-	len = 100 + conn->num_cipher_suites * 2 + conn->client_hello_ext_len;
+	len = 150 + conn->num_cipher_suites * 2 + conn->client_hello_ext_len;
 	hello = os_malloc(len);
 	if (hello == NULL)
 		return NULL;
@@ -81,7 +101,7 @@
 	pos += 3;
 	/* body - ClientHello */
 	/* ProtocolVersion client_version */
-	WPA_PUT_BE16(pos, TLS_VERSION);
+	WPA_PUT_BE16(pos, tls_version);
 	pos += 2;
 	/* Random random: uint32 gmt_unix_time, opaque random_bytes */
 	os_memcpy(pos, conn->client_random, TLS_RANDOM_LEN);
@@ -101,12 +121,46 @@
 	*pos++ = 1;
 	*pos++ = TLS_COMPRESSION_NULL;
 
+	/* Extension */
+	ext_start = pos;
+	pos += 2;
+
+#ifdef CONFIG_TLSV12
+	if (conn->rl.tls_version >= TLS_VERSION_1_2) {
+		/*
+		 * Add signature_algorithms extension since we support only
+		 * SHA256 (and not the default SHA1) with TLSv1.2.
+		 */
+		/* ExtensionsType extension_type = signature_algorithms(13) */
+		WPA_PUT_BE16(pos, TLS_EXT_SIGNATURE_ALGORITHMS);
+		pos += 2;
+		/* opaque extension_data<0..2^16-1> length */
+		WPA_PUT_BE16(pos, 8);
+		pos += 2;
+		/* supported_signature_algorithms<2..2^16-2> length */
+		WPA_PUT_BE16(pos, 6);
+		pos += 2;
+		/* supported_signature_algorithms */
+		*pos++ = TLS_HASH_ALG_SHA512;
+		*pos++ = TLS_SIGN_ALG_RSA;
+		*pos++ = TLS_HASH_ALG_SHA384;
+		*pos++ = TLS_SIGN_ALG_RSA;
+		*pos++ = TLS_HASH_ALG_SHA256;
+		*pos++ = TLS_SIGN_ALG_RSA;
+	}
+#endif /* CONFIG_TLSV12 */
+
 	if (conn->client_hello_ext) {
 		os_memcpy(pos, conn->client_hello_ext,
 			  conn->client_hello_ext_len);
 		pos += conn->client_hello_ext_len;
 	}
 
+	if (pos == ext_start + 2)
+		pos -= 2; /* no extensions */
+	else
+		WPA_PUT_BE16(ext_start, pos - ext_start - 2);
+
 	WPA_PUT_BE24(hs_length, pos - hs_length - 3);
 	tls_verify_hash_add(&conn->verify, hs_start, pos - hs_start);
 
@@ -134,6 +188,11 @@
 	struct x509_certificate *cert;
 
 	pos = *msgpos;
+	if (TLS_RECORD_HEADER_LEN + 1 + 3 + 3 > end - pos) {
+		tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+			  TLS_ALERT_INTERNAL_ERROR);
+		return -1;
+	}
 
 	wpa_printf(MSG_DEBUG, "TLSv1: Send Certificate");
 	rhdr = pos;
@@ -154,7 +213,7 @@
 	pos += 3;
 	cert = conn->cred ? conn->cred->cert : NULL;
 	while (cert) {
-		if (pos + 3 + cert->cert_len > end) {
+		if (3 + cert->cert_len > (size_t) (end - pos)) {
 			wpa_printf(MSG_DEBUG, "TLSv1: Not enough buffer space "
 				   "for Certificate (cert_len=%lu left=%lu)",
 				   (unsigned long) cert->cert_len,
@@ -265,9 +324,16 @@
 	wpa_hexdump(MSG_DEBUG, "TLSv1: DH Yc (client's public value)",
 		    dh_yc, dh_yc_len);
 
+	if (end - *pos < 2) {
+		tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+			  TLS_ALERT_INTERNAL_ERROR);
+		os_free(csecret);
+		os_free(dh_yc);
+		return -1;
+	}
 	WPA_PUT_BE16(*pos, dh_yc_len);
 	*pos += 2;
-	if (*pos + dh_yc_len > end) {
+	if (dh_yc_len > (size_t) (end - *pos)) {
 		wpa_printf(MSG_DEBUG, "TLSv1: Not enough room in the "
 			   "message buffer for Yc");
 		tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
@@ -789,6 +855,8 @@
 
 	wpa_printf(MSG_DEBUG, "TLSv1: Session resumption completed "
 		   "successfully");
+	if (!conn->session_resumed && conn->use_session_ticket)
+		conn->session_resumed = 1;
 	conn->state = ESTABLISHED;
 
 	return msg;
diff --git a/src/tls/tlsv1_common.c b/src/tls/tlsv1_common.c
index dabc12a..6b28417 100644
--- a/src/tls/tlsv1_common.c
+++ b/src/tls/tlsv1_common.c
@@ -335,7 +335,7 @@
 
 
 #ifdef CONFIG_TLSV12
-int tlsv12_key_x_server_params_hash(u16 tls_version,
+int tlsv12_key_x_server_params_hash(u16 tls_version, u8 hash_alg,
 				    const u8 *client_random,
 				    const u8 *server_random,
 				    const u8 *server_params,
@@ -343,14 +343,30 @@
 {
 	size_t hlen;
 	struct crypto_hash *ctx;
+	enum crypto_hash_alg alg;
 
-	ctx = crypto_hash_init(CRYPTO_HASH_ALG_SHA256, NULL, 0);
+	switch (hash_alg) {
+	case TLS_HASH_ALG_SHA256:
+		alg = CRYPTO_HASH_ALG_SHA256;
+		hlen = SHA256_MAC_LEN;
+		break;
+	case TLS_HASH_ALG_SHA384:
+		alg = CRYPTO_HASH_ALG_SHA384;
+		hlen = 48;
+		break;
+	case TLS_HASH_ALG_SHA512:
+		alg = CRYPTO_HASH_ALG_SHA512;
+		hlen = 64;
+		break;
+	default:
+		return -1;
+	}
+	ctx = crypto_hash_init(alg, NULL, 0);
 	if (ctx == NULL)
 		return -1;
 	crypto_hash_update(ctx, client_random, TLS_RANDOM_LEN);
 	crypto_hash_update(ctx, server_random, TLS_RANDOM_LEN);
 	crypto_hash_update(ctx, server_params, server_params_len);
-	hlen = SHA256_MAC_LEN;
 	if (crypto_hash_finish(ctx, hash, &hlen) < 0)
 		return -1;
 
@@ -469,6 +485,21 @@
 			wpa_printf(MSG_DEBUG, "TLSv1.2: DigestAlgorithn = SHA-256");
 			decrypted = buf + 19;
 			buflen -= 19;
+		} else if (buflen >= 19 + 48 &&
+		    os_memcmp(buf, "\x30\x41\x30\x0d\x06\x09\x60\x86\x48\x01"
+			      "\x65\x03\x04\x02\x02\x05\x00\x04\x30", 19) == 0)
+		{
+			wpa_printf(MSG_DEBUG, "TLSv1.2: DigestAlgorithn = SHA-384");
+			decrypted = buf + 19;
+			buflen -= 19;
+		} else if (buflen >= 19 + 64 &&
+		    os_memcmp(buf, "\x30\x51\x30\x0d\x06\x09\x60\x86\x48\x01"
+			      "\x65\x03\x04\x02\x03\x05\x00\x04\x40", 19) == 0)
+		{
+			wpa_printf(MSG_DEBUG, "TLSv1.2: DigestAlgorithn = SHA-512");
+			decrypted = buf + 19;
+			buflen -= 19;
+
 		} else {
 			wpa_printf(MSG_DEBUG, "TLSv1.2: Unrecognized DigestInfo");
 			os_free(buf);
diff --git a/src/tls/tlsv1_common.h b/src/tls/tlsv1_common.h
index 26e68af..7a252fe 100644
--- a/src/tls/tlsv1_common.h
+++ b/src/tls/tlsv1_common.h
@@ -169,6 +169,7 @@
 #define TLS_EXT_TRUSTED_CA_KEYS			3 /* RFC 4366 */
 #define TLS_EXT_TRUNCATED_HMAC			4 /* RFC 4366 */
 #define TLS_EXT_STATUS_REQUEST			5 /* RFC 4366 */
+#define TLS_EXT_SIGNATURE_ALGORITHMS		13 /* RFC 5246 */
 #define TLS_EXT_SESSION_TICKET			35 /* RFC 4507 */
 
 #define TLS_EXT_PAC_OPAQUE TLS_EXT_SESSION_TICKET /* EAP-FAST terminology */
@@ -257,7 +258,8 @@
 const char * tls_version_str(u16 ver);
 int tls_prf(u16 ver, const u8 *secret, size_t secret_len, const char *label,
 	    const u8 *seed, size_t seed_len, u8 *out, size_t outlen);
-int tlsv12_key_x_server_params_hash(u16 tls_version, const u8 *client_random,
+int tlsv12_key_x_server_params_hash(u16 tls_version, u8 hash_Alg,
+				    const u8 *client_random,
 				    const u8 *server_random,
 				    const u8 *server_params,
 				    size_t server_params_len, u8 *hash);
diff --git a/src/tls/tlsv1_cred.c b/src/tls/tlsv1_cred.c
index 1ea6827..067562b 100644
--- a/src/tls/tlsv1_cred.c
+++ b/src/tls/tlsv1_cred.c
@@ -190,6 +190,43 @@
 		      const u8 *cert_blob, size_t cert_blob_len,
 		      const char *path)
 {
+	if (cert && os_strncmp(cert, "hash://", 7) == 0) {
+		const char *pos = cert + 7;
+		if (os_strncmp(pos, "server/sha256/", 14) != 0) {
+			wpa_printf(MSG_DEBUG,
+				   "TLSv1: Unsupported ca_cert hash value '%s'",
+				   cert);
+			return -1;
+		}
+		pos += 14;
+		if (os_strlen(pos) != 32 * 2) {
+			wpa_printf(MSG_DEBUG,
+				   "TLSv1: Unexpected SHA256 hash length in ca_cert '%s'",
+				   cert);
+			return -1;
+		}
+		if (hexstr2bin(pos, cred->srv_cert_hash, 32) < 0) {
+			wpa_printf(MSG_DEBUG,
+				   "TLSv1: Invalid SHA256 hash value in ca_cert '%s'",
+				   cert);
+			return -1;
+		}
+		cred->server_cert_only = 1;
+		cred->ca_cert_verify = 0;
+		wpa_printf(MSG_DEBUG,
+			   "TLSv1: Checking only server certificate match");
+		return 0;
+	}
+
+	if (cert && os_strncmp(cert, "probe://", 8) == 0) {
+		cred->cert_probe = 1;
+		cred->ca_cert_verify = 0;
+		wpa_printf(MSG_DEBUG, "TLSv1: Only probe server certificate");
+		return 0;
+	}
+
+	cred->ca_cert_verify = cert || cert_blob || path;
+
 	if (tlsv1_set_cert_chain(&cred->trusted_certs, cert,
 				 cert_blob, cert_blob_len) < 0)
 		return -1;
diff --git a/src/tls/tlsv1_cred.h b/src/tls/tlsv1_cred.h
index 68fbdc9..b4bfe38 100644
--- a/src/tls/tlsv1_cred.h
+++ b/src/tls/tlsv1_cred.h
@@ -14,6 +14,11 @@
 	struct x509_certificate *cert;
 	struct crypto_private_key *key;
 
+	unsigned int cert_probe:1;
+	unsigned int ca_cert_verify:1;
+	unsigned int server_cert_only:1;
+	u8 srv_cert_hash[32];
+
 	/* Diffie-Hellman parameters */
 	u8 *dh_p; /* prime */
 	size_t dh_p_len;
diff --git a/src/tls/tlsv1_server.c b/src/tls/tlsv1_server.c
index 4df756f..ba47337 100644
--- a/src/tls/tlsv1_server.c
+++ b/src/tls/tlsv1_server.c
@@ -610,12 +610,12 @@
 
 
 /**
- * tlsv1_server_get_keys - Get master key and random data from TLS connection
+ * tlsv1_server_get_random - Get random data from TLS connection
  * @conn: TLSv1 server connection data from tlsv1_server_init()
- * @keys: Structure of key/random data (filled on success)
+ * @keys: Structure of random data (filled on success)
  * Returns: 0 on success, -1 on failure
  */
-int tlsv1_server_get_keys(struct tlsv1_server *conn, struct tls_keys *keys)
+int tlsv1_server_get_random(struct tlsv1_server *conn, struct tls_random *keys)
 {
 	os_memset(keys, 0, sizeof(*keys));
 	if (conn->state == CLIENT_HELLO)
diff --git a/src/tls/tlsv1_server.h b/src/tls/tlsv1_server.h
index b2b28d1..10e7699 100644
--- a/src/tls/tlsv1_server.h
+++ b/src/tls/tlsv1_server.h
@@ -32,7 +32,7 @@
 			    size_t buflen);
 int tlsv1_server_shutdown(struct tlsv1_server *conn);
 int tlsv1_server_resumed(struct tlsv1_server *conn);
-int tlsv1_server_get_keys(struct tlsv1_server *conn, struct tls_keys *keys);
+int tlsv1_server_get_random(struct tlsv1_server *conn, struct tls_random *data);
 int tlsv1_server_get_keyblock_size(struct tlsv1_server *conn);
 int tlsv1_server_set_cipher_list(struct tlsv1_server *conn, u8 *ciphers);
 int tlsv1_server_set_verify(struct tlsv1_server *conn, int verify_peer);
diff --git a/src/tls/tlsv1_server_read.c b/src/tls/tlsv1_server_read.c
index 0f237ba..8347d7a 100644
--- a/src/tls/tlsv1_server_read.c
+++ b/src/tls/tlsv1_server_read.c
@@ -471,6 +471,15 @@
 		return -1;
 	}
 
+	if (chain && (chain->extensions_present & X509_EXT_EXT_KEY_USAGE) &&
+	    !(chain->ext_key_usage &
+	      (X509_EXT_KEY_USAGE_ANY | X509_EXT_KEY_USAGE_CLIENT_AUTH))) {
+		tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+				   TLS_ALERT_BAD_CERTIFICATE);
+		x509_certificate_chain_free(chain);
+		return -1;
+	}
+
 	x509_certificate_chain_free(chain);
 
 	*in_len = end - in_data;
diff --git a/src/tls/tlsv1_server_write.c b/src/tls/tlsv1_server_write.c
index 15e6692..e7c5e22 100644
--- a/src/tls/tlsv1_server_write.c
+++ b/src/tls/tlsv1_server_write.c
@@ -168,6 +168,11 @@
 	}
 
 	pos = *msgpos;
+	if (TLS_RECORD_HEADER_LEN + 1 + 3 + 3 > end - pos) {
+		tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+				   TLS_ALERT_INTERNAL_ERROR);
+		return -1;
+	}
 
 	tlsv1_server_log(conn, "Send Certificate");
 	rhdr = pos;
@@ -188,7 +193,7 @@
 	pos += 3;
 	cert = conn->cred->cert;
 	while (cert) {
-		if (pos + 3 + cert->cert_len > end) {
+		if (3 + cert->cert_len > (size_t) (end - pos)) {
 			wpa_printf(MSG_DEBUG, "TLSv1: Not enough buffer space "
 				   "for Certificate (cert_len=%lu left=%lu)",
 				   (unsigned long) cert->cert_len,
@@ -371,7 +376,7 @@
 	/* body - ServerDHParams */
 	server_params = pos;
 	/* dh_p */
-	if (pos + 2 + dh_p_len > end) {
+	if (2 + dh_p_len > (size_t) (end - pos)) {
 		wpa_printf(MSG_DEBUG, "TLSv1: Not enough buffer space for "
 			   "dh_p");
 		tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
@@ -385,7 +390,7 @@
 	pos += dh_p_len;
 
 	/* dh_g */
-	if (pos + 2 + conn->cred->dh_g_len > end) {
+	if (2 + conn->cred->dh_g_len > (size_t) (end - pos)) {
 		wpa_printf(MSG_DEBUG, "TLSv1: Not enough buffer space for "
 			   "dh_g");
 		tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
@@ -399,7 +404,7 @@
 	pos += conn->cred->dh_g_len;
 
 	/* dh_Ys */
-	if (pos + 2 + dh_ys_len > end) {
+	if (2 + dh_ys_len > (size_t) (end - pos)) {
 		wpa_printf(MSG_DEBUG, "TLSv1: Not enough buffer space for "
 			   "dh_Ys");
 		tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
@@ -443,7 +448,8 @@
 		if (conn->rl.tls_version >= TLS_VERSION_1_2) {
 #ifdef CONFIG_TLSV12
 			hlen = tlsv12_key_x_server_params_hash(
-				conn->rl.tls_version, conn->client_random,
+				conn->rl.tls_version, TLS_HASH_ALG_SHA256,
+				conn->client_random,
 				conn->server_random, server_params,
 				pos - server_params, hash + 19);
 
@@ -457,7 +463,7 @@
 			 *   SignatureAlgorithm signature;
 			 * } SignatureAndHashAlgorithm;
 			 */
-			if (hlen < 0 || pos + 2 > end) {
+			if (hlen < 0 || end - pos < 2) {
 				tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
 						   TLS_ALERT_INTERNAL_ERROR);
 				return -1;
diff --git a/src/tls/x509v3.c b/src/tls/x509v3.c
index b51dfcd..75e3285 100644
--- a/src/tls/x509v3.c
+++ b/src/tls/x509v3.c
@@ -199,12 +199,11 @@
 			   hdr.class, hdr.tag);
 		return -1;
 	}
+	if (hdr.length > buf + len - hdr.payload)
+		return -1;
 	pos = hdr.payload;
 	end = pos + hdr.length;
 
-	if (end > buf + len)
-		return -1;
-
 	*next = end;
 
 	if (asn1_get_oid(pos, end - pos, &id->oid, &pos))
@@ -243,7 +242,7 @@
 	}
 	pos = hdr.payload;
 
-	if (pos + hdr.length > end)
+	if (hdr.length > end - pos)
 		return -1;
 	end = pos + hdr.length;
 	*next = end;
@@ -319,7 +318,7 @@
 	}
 	pos = hdr.payload;
 
-	if (pos + hdr.length > buf + len)
+	if (hdr.length > buf + len - pos)
 		return -1;
 
 	end = *next = pos + hdr.length;
@@ -677,7 +676,7 @@
 	pos = hdr.payload;
 	plen = hdr.length;
 
-	if (pos + plen > buf + len)
+	if (plen > (size_t) (buf + len - pos))
 		return -1;
 
 	*next = pos + plen;
@@ -721,6 +720,15 @@
 }
 
 
+static int x509_any_ext_key_usage_oid(struct asn1_oid *oid)
+{
+	return oid->len == 6 &&
+		x509_id_ce_oid(oid) &&
+		oid->oid[3] == 37 /* extKeyUsage */ &&
+		oid->oid[4] == 0 /* anyExtendedKeyUsage */;
+}
+
+
 static int x509_parse_ext_key_usage(struct x509_certificate *cert,
 				    const u8 *pos, size_t len)
 {
@@ -801,7 +809,7 @@
 		}
 		cert->ca = hdr.payload[0];
 
-		if (hdr.payload + hdr.length == pos + len) {
+		if (hdr.length == pos + len - hdr.payload) {
 			wpa_printf(MSG_DEBUG, "X509: BasicConstraints - cA=%d",
 				   cert->ca);
 			return 0;
@@ -1074,6 +1082,100 @@
 }
 
 
+static int x509_id_pkix_oid(struct asn1_oid *oid)
+{
+	return oid->len >= 7 &&
+		oid->oid[0] == 1 /* iso */ &&
+		oid->oid[1] == 3 /* identified-organization */ &&
+		oid->oid[2] == 6 /* dod */ &&
+		oid->oid[3] == 1 /* internet */ &&
+		oid->oid[4] == 5 /* security */ &&
+		oid->oid[5] == 5 /* mechanisms */ &&
+		oid->oid[6] == 7 /* id-pkix */;
+}
+
+
+static int x509_id_kp_oid(struct asn1_oid *oid)
+{
+	/* id-kp */
+	return oid->len >= 8 &&
+		x509_id_pkix_oid(oid) &&
+		oid->oid[7] == 3 /* id-kp */;
+}
+
+
+static int x509_id_kp_server_auth_oid(struct asn1_oid *oid)
+{
+	/* id-kp */
+	return oid->len == 9 &&
+		x509_id_kp_oid(oid) &&
+		oid->oid[8] == 1 /* id-kp-serverAuth */;
+}
+
+
+static int x509_id_kp_client_auth_oid(struct asn1_oid *oid)
+{
+	/* id-kp */
+	return oid->len == 9 &&
+		x509_id_kp_oid(oid) &&
+		oid->oid[8] == 2 /* id-kp-clientAuth */;
+}
+
+
+static int x509_parse_ext_ext_key_usage(struct x509_certificate *cert,
+					const u8 *pos, size_t len)
+{
+	struct asn1_hdr hdr;
+	const u8 *end;
+	struct asn1_oid oid;
+
+	/*
+	 * ExtKeyUsageSyntax ::= SEQUENCE SIZE (1..MAX) OF KeyPurposeId
+	 *
+	 * KeyPurposeId ::= OBJECT IDENTIFIER
+	 */
+
+	if (asn1_get_next(pos, len, &hdr) < 0 ||
+	    hdr.class != ASN1_CLASS_UNIVERSAL ||
+	    hdr.tag != ASN1_TAG_SEQUENCE) {
+		wpa_printf(MSG_DEBUG, "X509: Expected SEQUENCE "
+			   "(ExtKeyUsageSyntax) - found class %d tag 0x%x",
+			   hdr.class, hdr.tag);
+		return -1;
+	}
+	if (hdr.length > pos + len - hdr.payload)
+		return -1;
+	pos = hdr.payload;
+	end = pos + hdr.length;
+
+	wpa_hexdump(MSG_MSGDUMP, "X509: ExtKeyUsageSyntax", pos, end - pos);
+
+	while (pos < end) {
+		char buf[80];
+
+		if (asn1_get_oid(pos, end - pos, &oid, &pos))
+			return -1;
+		if (x509_any_ext_key_usage_oid(&oid)) {
+			os_strlcpy(buf, "anyExtendedKeyUsage", sizeof(buf));
+			cert->ext_key_usage |= X509_EXT_KEY_USAGE_ANY;
+		} else if (x509_id_kp_server_auth_oid(&oid)) {
+			os_strlcpy(buf, "id-kp-serverAuth", sizeof(buf));
+			cert->ext_key_usage |= X509_EXT_KEY_USAGE_SERVER_AUTH;
+		} else if (x509_id_kp_client_auth_oid(&oid)) {
+			os_strlcpy(buf, "id-kp-clientAuth", sizeof(buf));
+			cert->ext_key_usage |= X509_EXT_KEY_USAGE_CLIENT_AUTH;
+		} else {
+			asn1_oid_to_str(&oid, buf, sizeof(buf));
+		}
+		wpa_printf(MSG_DEBUG, "ExtKeyUsage KeyPurposeId: %s", buf);
+	}
+
+	cert->extensions_present |= X509_EXT_EXT_KEY_USAGE;
+
+	return 0;
+}
+
+
 static int x509_parse_extension_data(struct x509_certificate *cert,
 				     struct asn1_oid *oid,
 				     const u8 *pos, size_t len)
@@ -1085,7 +1187,6 @@
 	 * certificate policies (section 4.2.1.5)
 	 * name constraints (section 4.2.1.11)
 	 * policy constraints (section 4.2.1.12)
-	 * extended key usage (section 4.2.1.13)
 	 * inhibit any-policy (section 4.2.1.15)
 	 */
 	switch (oid->oid[3]) {
@@ -1097,6 +1198,8 @@
 		return x509_parse_ext_issuer_alt_name(cert, pos, len);
 	case 19: /* id-ce-basicConstraints */
 		return x509_parse_ext_basic_constraints(cert, pos, len);
+	case 37: /* id-ce-extKeyUsage */
+		return x509_parse_ext_ext_key_usage(cert, pos, len);
 	default:
 		return 1;
 	}
@@ -1449,7 +1552,7 @@
 }
 
 
-static int x509_sha256_oid(struct asn1_oid *oid)
+static int x509_sha2_oid(struct asn1_oid *oid)
 {
 	return oid->len == 9 &&
 		oid->oid[0] == 2 /* joint-iso-itu-t */ &&
@@ -1459,11 +1562,31 @@
 		oid->oid[4] == 101 /* gov */ &&
 		oid->oid[5] == 3 /* csor */ &&
 		oid->oid[6] == 4 /* nistAlgorithm */ &&
-		oid->oid[7] == 2 /* hashAlgs */ &&
+		oid->oid[7] == 2 /* hashAlgs */;
+}
+
+
+static int x509_sha256_oid(struct asn1_oid *oid)
+{
+	return x509_sha2_oid(oid) &&
 		oid->oid[8] == 1 /* sha256 */;
 }
 
 
+static int x509_sha384_oid(struct asn1_oid *oid)
+{
+	return x509_sha2_oid(oid) &&
+		oid->oid[8] == 2 /* sha384 */;
+}
+
+
+static int x509_sha512_oid(struct asn1_oid *oid)
+{
+	return x509_sha2_oid(oid) &&
+		oid->oid[8] == 3 /* sha512 */;
+}
+
+
 /**
  * x509_certificate_parse - Parse a X.509 certificate in DER format
  * @buf: Pointer to the X.509 certificate in DER format
@@ -1503,12 +1626,12 @@
 	}
 	pos = hdr.payload;
 
-	if (pos + hdr.length > end) {
+	if (hdr.length > end - pos) {
 		x509_certificate_free(cert);
 		return NULL;
 	}
 
-	if (pos + hdr.length < end) {
+	if (hdr.length < end - pos) {
 		wpa_hexdump(MSG_MSGDUMP, "X509: Ignoring extra data after DER "
 			    "encoded certificate",
 			    pos + hdr.length, end - (pos + hdr.length));
@@ -1588,7 +1711,7 @@
 	size_t data_len;
 	struct asn1_hdr hdr;
 	struct asn1_oid oid;
-	u8 hash[32];
+	u8 hash[64];
 	size_t hash_len;
 
 	if (!x509_pkcs_oid(&cert->signature.oid) ||
@@ -1700,6 +1823,32 @@
 		goto skip_digest_oid;
 	}
 
+	if (x509_sha384_oid(&oid)) {
+		if (cert->signature.oid.oid[6] !=
+		    12 /* sha384WithRSAEncryption */) {
+			wpa_printf(MSG_DEBUG, "X509: digestAlgorithm SHA384 "
+				   "does not match with certificate "
+				   "signatureAlgorithm (%lu)",
+				   cert->signature.oid.oid[6]);
+			os_free(data);
+			return -1;
+		}
+		goto skip_digest_oid;
+	}
+
+	if (x509_sha512_oid(&oid)) {
+		if (cert->signature.oid.oid[6] !=
+		    13 /* sha512WithRSAEncryption */) {
+			wpa_printf(MSG_DEBUG, "X509: digestAlgorithm SHA512 "
+				   "does not match with certificate "
+				   "signatureAlgorithm (%lu)",
+				   cert->signature.oid.oid[6]);
+			os_free(data);
+			return -1;
+		}
+		goto skip_digest_oid;
+	}
+
 	if (!x509_digest_oid(&oid)) {
 		wpa_printf(MSG_DEBUG, "X509: Unrecognized digestAlgorithm");
 		os_free(data);
@@ -1765,9 +1914,21 @@
 		wpa_hexdump(MSG_MSGDUMP, "X509: Certificate hash (SHA256)",
 			    hash, hash_len);
 		break;
-	case 2: /* md2WithRSAEncryption */
 	case 12: /* sha384WithRSAEncryption */
+		sha384_vector(1, &cert->tbs_cert_start, &cert->tbs_cert_len,
+			      hash);
+		hash_len = 48;
+		wpa_hexdump(MSG_MSGDUMP, "X509: Certificate hash (SHA384)",
+			    hash, hash_len);
+		break;
 	case 13: /* sha512WithRSAEncryption */
+		sha512_vector(1, &cert->tbs_cert_start, &cert->tbs_cert_len,
+			      hash);
+		hash_len = 64;
+		wpa_hexdump(MSG_MSGDUMP, "X509: Certificate hash (SHA512)",
+			    hash, hash_len);
+		break;
+	case 2: /* md2WithRSAEncryption */
 	default:
 		wpa_printf(MSG_INFO, "X509: Unsupported certificate signature "
 			   "algorithm (%lu)", cert->signature.oid.oid[6]);
diff --git a/src/tls/x509v3.h b/src/tls/x509v3.h
index 91a35ba..12ef86e 100644
--- a/src/tls/x509v3.h
+++ b/src/tls/x509v3.h
@@ -68,6 +68,7 @@
 #define X509_EXT_KEY_USAGE			(1 << 2)
 #define X509_EXT_SUBJECT_ALT_NAME		(1 << 3)
 #define X509_EXT_ISSUER_ALT_NAME		(1 << 4)
+#define X509_EXT_EXT_KEY_USAGE			(1 << 5)
 
 	/* BasicConstraints */
 	int ca; /* cA */
@@ -85,6 +86,12 @@
 #define X509_KEY_USAGE_ENCIPHER_ONLY		(1 << 7)
 #define X509_KEY_USAGE_DECIPHER_ONLY		(1 << 8)
 
+	/* ExtKeyUsage */
+	unsigned long ext_key_usage;
+#define X509_EXT_KEY_USAGE_ANY			(1 << 0)
+#define X509_EXT_KEY_USAGE_SERVER_AUTH		(1 << 1)
+#define X509_EXT_KEY_USAGE_CLIENT_AUTH		(1 << 2)
+
 	/*
 	 * The DER format certificate follows struct x509_certificate. These
 	 * pointers point to that buffer.
diff --git a/src/utils/browser-android.c b/src/utils/browser-android.c
index 9ce1a5c..71a1652 100644
--- a/src/utils/browser-android.c
+++ b/src/utils/browser-android.c
@@ -95,7 +95,7 @@
 
 	if (pid == 0) {
 		/* run the external command in the child process */
-		char *argv[9];
+		char *argv[7];
 
 		argv[0] = "browser-android";
 		argv[1] = "start";
@@ -103,9 +103,7 @@
 		argv[3] = "android.intent.action.VIEW";
 		argv[4] = "-d";
 		argv[5] = (void *) url;
-		argv[6] = "-n";
-		argv[7] = "com.android.browser/.BrowserActivity";
-		argv[8] = NULL;
+		argv[6] = NULL;
 
 		execv("/system/bin/am", argv);
 		wpa_printf(MSG_ERROR, "execv: %s", strerror(errno));
diff --git a/src/utils/browser-wpadebug.c b/src/utils/browser-wpadebug.c
index 5fc40fa..59ba4d1 100644
--- a/src/utils/browser-wpadebug.c
+++ b/src/utils/browser-wpadebug.c
@@ -96,7 +96,7 @@
 
 	if (pid == 0) {
 		/* run the external command in the child process */
-		char *argv[12];
+		char *argv[14];
 
 		argv[0] = "browser-wpadebug";
 		argv[1] = "start";
@@ -109,7 +109,9 @@
 		argv[8] = "-e";
 		argv[9] = "w1.fi.wpadebug.URL";
 		argv[10] = (void *) url;
-		argv[11] = NULL;
+		argv[11] = "--user";
+		argv[12] = "-3"; /* USER_CURRENT_OR_SELF */
+		argv[13] = NULL;
 
 		execv("/system/bin/am", argv);
 		wpa_printf(MSG_ERROR, "execv: %s", strerror(errno));
diff --git a/src/utils/common.c b/src/utils/common.c
index 5cf0d57..e74a2ae 100644
--- a/src/utils/common.c
+++ b/src/utils/common.c
@@ -498,7 +498,7 @@
 			*txt++ = 't';
 			break;
 		default:
-			if (data[i] >= 32 && data[i] <= 127) {
+			if (data[i] >= 32 && data[i] <= 126) {
 				*txt++ = data[i];
 			} else {
 				txt += os_snprintf(txt, end - txt, "\\x%02x",
@@ -973,6 +973,48 @@
 
 
 /**
+ * cstr_token - Get next token from const char string
+ * @str: a constant string to tokenize
+ * @delim: a string of delimiters
+ * @last: a pointer to a character following the returned token
+ *      It has to be set to NULL for the first call and passed for any
+ *      futher call.
+ * Returns: a pointer to token position in str or NULL
+ *
+ * This function is similar to str_token, but it can be used with both
+ * char and const char strings. Differences:
+ * - The str buffer remains unmodified
+ * - The returned token is not a NULL terminated string, but a token
+ *   position in str buffer. If a return value is not NULL a size
+ *   of the returned token could be calculated as (last - token).
+ */
+const char * cstr_token(const char *str, const char *delim, const char **last)
+{
+	const char *end, *token = str;
+
+	if (!str || !delim || !last)
+		return NULL;
+
+	if (*last)
+		token = *last;
+
+	while (*token && os_strchr(delim, *token))
+		token++;
+
+	if (!*token)
+		return NULL;
+
+	end = token + 1;
+
+	while (*end && !os_strchr(delim, *end))
+		end++;
+
+	*last = end;
+	return token;
+}
+
+
+/**
  * str_token - Get next token from a string
  * @buf: String to tokenize. Note that the string might be modified.
  * @delim: String of delimiters
@@ -982,25 +1024,12 @@
  */
 char * str_token(char *str, const char *delim, char **context)
 {
-	char *end, *pos = str;
+	char *token = (char *) cstr_token(str, delim, (const char **) context);
 
-	if (*context)
-		pos = *context;
+	if (token && **context)
+		*(*context)++ = '\0';
 
-	while (*pos && os_strchr(delim, *pos))
-		pos++;
-	if (!*pos)
-		return NULL;
-
-	end = pos + 1;
-	while (*end && !os_strchr(delim, *end))
-		end++;
-
-	if (*end)
-		*end++ = '\0';
-
-	*context = end;
-	return pos;
+	return token;
 }
 
 
diff --git a/src/utils/common.h b/src/utils/common.h
index 88318f5..0b9cc3d 100644
--- a/src/utils/common.h
+++ b/src/utils/common.h
@@ -53,16 +53,6 @@
 }
 #endif /* __APPLE__ */
 
-#ifdef CONFIG_TI_COMPILER
-#define __BIG_ENDIAN 4321
-#define __LITTLE_ENDIAN 1234
-#ifdef __big_endian__
-#define __BYTE_ORDER __BIG_ENDIAN
-#else
-#define __BYTE_ORDER __LITTLE_ENDIAN
-#endif
-#endif /* CONFIG_TI_COMPILER */
-
 #ifdef CONFIG_NATIVE_WINDOWS
 #include <winsock.h>
 
@@ -110,22 +100,6 @@
 #define WPA_TYPES_DEFINED
 #endif /* __vxworks */
 
-#ifdef CONFIG_TI_COMPILER
-#ifdef _LLONG_AVAILABLE
-typedef unsigned long long u64;
-#else
-/*
- * TODO: 64-bit variable not available. Using long as a workaround to test the
- * build, but this will likely not work for all operations.
- */
-typedef unsigned long u64;
-#endif
-typedef unsigned int u32;
-typedef unsigned short u16;
-typedef unsigned char u8;
-#define WPA_TYPES_DEFINED
-#endif /* CONFIG_TI_COMPILER */
-
 #ifndef WPA_TYPES_DEFINED
 #ifdef CONFIG_USE_INTTYPES_H
 #include <inttypes.h>
@@ -262,7 +236,7 @@
 
 static inline u32 WPA_GET_BE32(const u8 *a)
 {
-	return (a[0] << 24) | (a[1] << 16) | (a[2] << 8) | a[3];
+	return ((u32) a[0] << 24) | (a[1] << 16) | (a[2] << 8) | a[3];
 }
 
 static inline void WPA_PUT_BE32(u8 *a, u32 val)
@@ -275,7 +249,7 @@
 
 static inline u32 WPA_GET_LE32(const u8 *a)
 {
-	return (a[3] << 24) | (a[2] << 16) | (a[1] << 8) | a[0];
+	return ((u32) a[3] << 24) | (a[2] << 16) | (a[1] << 8) | a[0];
 }
 
 static inline void WPA_PUT_LE32(u8 *a, u32 val)
@@ -433,7 +407,7 @@
 #endif
 
 #ifndef BIT
-#define BIT(x) (1 << (x))
+#define BIT(x) (1U << (x))
 #endif
 
 /*
@@ -518,6 +492,11 @@
 	return (a[0] & a[1] & a[2] & a[3] & a[4] & a[5]) == 0xff;
 }
 
+static inline int is_multicast_ether_addr(const u8 *a)
+{
+	return a[0] & 0x01;
+}
+
 #define broadcast_ether_addr (const u8 *) "\xff\xff\xff\xff\xff\xff"
 
 #include "wpa_debug.h"
@@ -549,6 +528,7 @@
 int random_mac_addr(u8 *addr);
 int random_mac_addr_keep_oui(u8 *addr);
 
+const char * cstr_token(const char *str, const char *delim, const char **last);
 char * str_token(char *str, const char *delim, char **context);
 size_t utf8_escape(const char *inp, size_t in_size,
 		   char *outp, size_t out_size);
diff --git a/src/utils/eloop.c b/src/utils/eloop.c
index 4a565eb..8647229 100644
--- a/src/utils/eloop.c
+++ b/src/utils/eloop.c
@@ -61,11 +61,8 @@
 struct eloop_sock_table {
 	int count;
 	struct eloop_sock *table;
-#ifdef CONFIG_ELOOP_EPOLL
 	eloop_event_type type;
-#else /* CONFIG_ELOOP_EPOLL */
 	int changed;
-#endif /* CONFIG_ELOOP_EPOLL */
 };
 
 struct eloop_data {
@@ -256,9 +253,7 @@
 	table->table = tmp;
 	eloop.max_sock = new_max_sock;
 	eloop.count++;
-#ifndef CONFIG_ELOOP_EPOLL
 	table->changed = 1;
-#endif /* CONFIG_ELOOP_EPOLL */
 	eloop_trace_sock_add_ref(table);
 
 #ifdef CONFIG_ELOOP_EPOLL
@@ -314,9 +309,7 @@
 	}
 	table->count--;
 	eloop.count--;
-#ifndef CONFIG_ELOOP_EPOLL
 	table->changed = 1;
-#endif /* CONFIG_ELOOP_EPOLL */
 	eloop_trace_sock_add_ref(table);
 #ifdef CONFIG_ELOOP_EPOLL
 	if (epoll_ctl(eloop.epollfd, EPOLL_CTL_DEL, sock, NULL) < 0) {
@@ -523,6 +516,10 @@
 			continue;
 		table->handler(table->sock, table->eloop_data,
 			       table->user_data);
+		if (eloop.readers.changed ||
+		    eloop.writers.changed ||
+		    eloop.exceptions.changed)
+			break;
 	}
 }
 #endif /* CONFIG_ELOOP_EPOLL */
@@ -923,6 +920,20 @@
 	       (!dl_list_empty(&eloop.timeout) || eloop.readers.count > 0 ||
 		eloop.writers.count > 0 || eloop.exceptions.count > 0)) {
 		struct eloop_timeout *timeout;
+
+		if (eloop.pending_terminate) {
+			/*
+			 * This may happen in some corner cases where a signal
+			 * is received during a blocking operation. We need to
+			 * process the pending signals and exit if requested to
+			 * avoid hitting the SIGALRM limit if the blocking
+			 * operation took more than two seconds.
+			 */
+			eloop_process_pending_signals();
+			if (eloop.terminate)
+				break;
+		}
+
 		timeout = dl_list_first(&eloop.timeout, struct eloop_timeout,
 					list);
 		if (timeout) {
@@ -977,6 +988,11 @@
 				   , strerror(errno));
 			goto out;
 		}
+
+		eloop.readers.changed = 0;
+		eloop.writers.changed = 0;
+		eloop.exceptions.changed = 0;
+
 		eloop_process_pending_signals();
 
 		/* check if some registered timeouts have occurred */
@@ -998,6 +1014,19 @@
 		if (res <= 0)
 			continue;
 
+		if (eloop.readers.changed ||
+		    eloop.writers.changed ||
+		    eloop.exceptions.changed) {
+			 /*
+			  * Sockets may have been closed and reopened with the
+			  * same FD in the signal or timeout handlers, so we
+			  * must skip the previous results and check again
+			  * whether any of the currently registered sockets have
+			  * events.
+			  */
+			continue;
+		}
+
 #ifdef CONFIG_ELOOP_POLL
 		eloop_sock_table_dispatch(&eloop.readers, &eloop.writers,
 					  &eloop.exceptions, eloop.pollfds_map,
@@ -1073,7 +1102,7 @@
 
 int eloop_terminated(void)
 {
-	return eloop.terminate;
+	return eloop.terminate || eloop.pending_terminate;
 }
 
 
diff --git a/src/utils/http_curl.c b/src/utils/http_curl.c
index 653eb54..9c49680 100644
--- a/src/utils/http_curl.c
+++ b/src/utils/http_curl.c
@@ -26,6 +26,9 @@
 #include "common.h"
 #include "xml-utils.h"
 #include "http-utils.h"
+#ifdef EAP_TLS_OPENSSL
+#include "crypto/tls_openssl.h"
+#endif /* EAP_TLS_OPENSSL */
 
 
 struct http_ctx {
@@ -421,6 +424,28 @@
 
 IMPLEMENT_ASN1_FUNCTIONS(LogotypeExtn);
 
+#ifdef OPENSSL_IS_BORINGSSL
+#define sk_LogotypeInfo_num(st) \
+sk_num(CHECKED_CAST(_STACK *, STACK_OF(LogotypeInfo) *, (st)))
+#define sk_LogotypeInfo_value(st, i) (LogotypeInfo *) \
+sk_value(CHECKED_CAST(_STACK *, const STACK_OF(LogotypeInfo) *, (st)), (i))
+#define sk_LogotypeImage_num(st) \
+sk_num(CHECKED_CAST(_STACK *, STACK_OF(LogotypeImage) *, (st)))
+#define sk_LogotypeImage_value(st, i) (LogotypeImage *) \
+sk_value(CHECKED_CAST(_STACK *, const STACK_OF(LogotypeImage) *, (st)), (i))
+#define sk_LogotypeAudio_num(st) \
+sk_num(CHECKED_CAST(_STACK *, STACK_OF(LogotypeAudio) *, (st)))
+#define sk_LogotypeAudio_value(st, i) (LogotypeAudio *) \
+sk_value(CHECK_CAST(_STACK *, const STACK_OF(LogotypeAudio) *, (st)), (i))
+#define sk_HashAlgAndValue_num(st) \
+sk_num(CHECKED_CAST(_STACK *, STACK_OF(HashAlgAndValue) *, (st)))
+#define sk_HashAlgAndValue_value(st, i) (HashAlgAndValue *) \
+sk_value(CHECKED_CAST(_STACK *, const STACK_OF(HashAlgAndValue) *, (st)), (i))
+#define sk_ASN1_IA5STRING_num(st) \
+sk_num(CHECKED_CAST(_STACK *, STACK_OF(ASN1_IA5STRING) *, (st)))
+#define sk_ASN1_IA5STRING_value(st, i) (ASN1_IA5STRING *) \
+sk_value(CHECKED_CAST(_STACK *, const STACK_OF(ASN1_IA5STRING) *, (st)), (i))
+#else /* OPENSSL_IS_BORINGSSL */
 #define sk_LogotypeInfo_num(st) SKM_sk_num(LogotypeInfo, (st))
 #define sk_LogotypeInfo_value(st, i) SKM_sk_value(LogotypeInfo, (st), (i))
 #define sk_LogotypeImage_num(st) SKM_sk_num(LogotypeImage, (st))
@@ -431,6 +456,7 @@
 #define sk_HashAlgAndValue_value(st, i) SKM_sk_value(HashAlgAndValue, (st), (i))
 #define sk_ASN1_IA5STRING_num(st) SKM_sk_num(ASN1_IA5STRING, (st))
 #define sk_ASN1_IA5STRING_value(st, i) SKM_sk_value(ASN1_IA5STRING, (st), (i))
+#endif /* OPENSSL_IS_BORINGSSL */
 
 
 static void add_logo(struct http_ctx *ctx, struct http_cert *hcert,
@@ -981,6 +1007,26 @@
 	if (depth == 0 && preverify_ok && validate_server_cert(ctx, cert) < 0)
 		return 0;
 
+#ifdef OPENSSL_IS_BORINGSSL
+	if (depth == 0 && ctx->ocsp != NO_OCSP && preverify_ok) {
+		enum ocsp_result res;
+
+		res = check_ocsp_resp(ssl_ctx, ssl, cert, ctx->peer_issuer,
+				      ctx->peer_issuer_issuer);
+		if (res == OCSP_REVOKED) {
+			preverify_ok = 0;
+			wpa_printf(MSG_INFO, "OCSP: certificate revoked");
+			if (err == X509_V_OK)
+				X509_STORE_CTX_set_error(
+					x509_ctx, X509_V_ERR_CERT_REVOKED);
+		} else if (res != OCSP_GOOD && (ctx->ocsp == MANDATORY_OCSP)) {
+			preverify_ok = 0;
+			wpa_printf(MSG_INFO,
+				   "OCSP: bad certificate status response");
+		}
+	}
+#endif /* OPENSSL_IS_BORINGSSL */
+
 	if (!preverify_ok)
 		ctx->last_err = "TLS validation failed";
 
@@ -1273,6 +1319,16 @@
 #ifdef EAP_TLS_OPENSSL
 		curl_easy_setopt(curl, CURLOPT_SSL_CTX_FUNCTION, curl_cb_ssl);
 		curl_easy_setopt(curl, CURLOPT_SSL_CTX_DATA, ctx);
+#ifdef OPENSSL_IS_BORINGSSL
+		/* For now, using the CURLOPT_SSL_VERIFYSTATUS option only
+		 * with BoringSSL since the OpenSSL specific callback hack to
+		 * enable OCSP is not available with BoringSSL. The OCSP
+		 * implementation within libcurl is not sufficient for the
+		 * Hotspot 2.0 OSU needs, so cannot use this with OpenSSL.
+		 */
+		if (ctx->ocsp != NO_OCSP)
+			curl_easy_setopt(curl, CURLOPT_SSL_VERIFYSTATUS, 1L);
+#endif /* OPENSSL_IS_BORINGSSL */
 #endif /* EAP_TLS_OPENSSL */
 	} else {
 		curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);
diff --git a/src/utils/includes.h b/src/utils/includes.h
index 6c6ec87..75513fc 100644
--- a/src/utils/includes.h
+++ b/src/utils/includes.h
@@ -17,26 +17,22 @@
 #include "build_config.h"
 
 #include <stdlib.h>
+#include <stddef.h>
 #include <stdio.h>
 #include <stdarg.h>
 #include <string.h>
 #ifndef _WIN32_WCE
-#ifndef CONFIG_TI_COMPILER
 #include <signal.h>
 #include <sys/types.h>
-#endif /* CONFIG_TI_COMPILER */
 #include <errno.h>
 #endif /* _WIN32_WCE */
 #include <ctype.h>
 
-#ifndef CONFIG_TI_COMPILER
 #ifndef _MSC_VER
 #include <unistd.h>
 #endif /* _MSC_VER */
-#endif /* CONFIG_TI_COMPILER */
 
 #ifndef CONFIG_NATIVE_WINDOWS
-#ifndef CONFIG_TI_COMPILER
 #include <sys/socket.h>
 #include <netinet/in.h>
 #include <arpa/inet.h>
@@ -44,7 +40,6 @@
 #include <sys/uio.h>
 #include <sys/time.h>
 #endif /* __vxworks */
-#endif /* CONFIG_TI_COMPILER */
 #endif /* CONFIG_NATIVE_WINDOWS */
 
 #endif /* INCLUDES_H */
diff --git a/src/utils/os.h b/src/utils/os.h
index 8913854..9e496fb 100644
--- a/src/utils/os.h
+++ b/src/utils/os.h
@@ -247,11 +247,11 @@
 int os_file_exists(const char *fname);
 
 /**
- * os_fsync - Sync a file's (for a given stream) state with storage device
+ * os_fdatasync - Sync a file's (for a given stream) state with storage device
  * @stream: the stream to be flushed
  * Returns: 0 if the operation succeeded or -1 on failure
  */
-int os_fsync(FILE *stream);
+int os_fdatasync(FILE *stream);
 
 /**
  * os_zalloc - Allocate and zero memory
@@ -653,4 +653,12 @@
 #define strcpy OS_DO_NOT_USE_strcpy
 #endif /* OS_REJECT_C_LIB_FUNCTIONS */
 
+
+#if defined(WPA_TRACE_BFD) && defined(CONFIG_TESTING_OPTIONS)
+#define TEST_FAIL() testing_test_fail()
+int testing_test_fail(void);
+#else
+#define TEST_FAIL() 0
+#endif
+
 #endif /* OS_H */
diff --git a/src/utils/os_internal.c b/src/utils/os_internal.c
index b8fb2db..ed6eb3c 100644
--- a/src/utils/os_internal.c
+++ b/src/utils/os_internal.c
@@ -243,7 +243,7 @@
 }
 
 
-int os_fsync(FILE *stream)
+int os_fdatasync(FILE *stream)
 {
 	return 0;
 }
diff --git a/src/utils/os_none.c b/src/utils/os_none.c
index 96d243d..0c3214d 100644
--- a/src/utils/os_none.c
+++ b/src/utils/os_none.c
@@ -102,7 +102,7 @@
 }
 
 
-int os_fsync(FILE *stream)
+int os_fdatasync(FILE *stream)
 {
 	return 0;
 }
diff --git a/src/utils/os_unix.c b/src/utils/os_unix.c
index ac73f7a..8f8dc5b 100644
--- a/src/utils/os_unix.c
+++ b/src/utils/os_unix.c
@@ -17,6 +17,12 @@
 #include <private/android_filesystem_config.h>
 #endif /* ANDROID */
 
+#ifdef __MACH__
+#include <CoreServices/CoreServices.h>
+#include <mach/mach.h>
+#include <mach/mach_time.h>
+#endif /* __MACH__ */
+
 #include "os.h"
 #include "common.h"
 
@@ -36,7 +42,7 @@
 	struct dl_list list;
 	size_t len;
 	WPA_TRACE_INFO
-};
+} __attribute__((aligned(16)));
 
 #endif /* WPA_TRACE */
 
@@ -63,6 +69,7 @@
 
 int os_get_reltime(struct os_reltime *t)
 {
+#ifndef __MACH__
 #if defined(CLOCK_BOOTTIME)
 	static clockid_t clock_id = CLOCK_BOOTTIME;
 #elif defined(CLOCK_MONOTONIC)
@@ -95,6 +102,23 @@
 			return -1;
 		}
 	}
+#else /* __MACH__ */
+	uint64_t abstime, nano;
+	static mach_timebase_info_data_t info = { 0, 0 };
+
+	if (!info.denom) {
+		if (mach_timebase_info(&info) != KERN_SUCCESS)
+			return -1;
+	}
+
+	abstime = mach_absolute_time();
+	nano = (abstime * info.numer) / info.denom;
+
+	t->sec = nano / NSEC_PER_SEC;
+	t->usec = (nano - (((uint64_t) t->sec) * NSEC_PER_SEC)) / NSEC_PER_USEC;
+
+	return 0;
+#endif /* __MACH__ */
 }
 
 
@@ -226,6 +250,9 @@
 	FILE *f;
 	size_t rc;
 
+	if (TEST_FAIL())
+		return -1;
+
 	f = fopen("/dev/urandom", "rb");
 	if (f == NULL) {
 		printf("Could not open /dev/urandom.\n");
@@ -415,10 +442,21 @@
 }
 
 
-int os_fsync(FILE *stream)
+int os_fdatasync(FILE *stream)
 {
-	if (!fflush(stream))
+	if (!fflush(stream)) {
+#ifdef __linux__
+		return fdatasync(fileno(stream));
+#else /* !__linux__ */
+#ifdef F_FULLFSYNC
+		/* OS X does not implement fdatasync(). */
+		return fcntl(fileno(stream), F_FULLFSYNC);
+#else /* F_FULLFSYNC */
 		return fsync(fileno(stream));
+#endif /* F_FULLFSYNC */
+#endif /* __linux__ */
+	}
+
 	return -1;
 }
 
@@ -556,6 +594,78 @@
 	return 0;
 }
 
+
+char wpa_trace_test_fail_func[256] = { 0 };
+unsigned int wpa_trace_test_fail_after;
+
+int testing_test_fail(void)
+{
+	const char *func[WPA_TRACE_LEN];
+	size_t i, res, len;
+	char *pos, *next;
+	int match;
+
+	if (!wpa_trace_test_fail_after)
+		return 0;
+
+	res = wpa_trace_calling_func(func, WPA_TRACE_LEN);
+	i = 0;
+	if (i < res && os_strcmp(func[i], __func__) == 0)
+		i++;
+
+	pos = wpa_trace_test_fail_func;
+
+	match = 0;
+	while (i < res) {
+		int allow_skip = 1;
+		int maybe = 0;
+
+		if (*pos == '=') {
+			allow_skip = 0;
+			pos++;
+		} else if (*pos == '?') {
+			maybe = 1;
+			pos++;
+		}
+		next = os_strchr(pos, ';');
+		if (next)
+			len = next - pos;
+		else
+			len = os_strlen(pos);
+		if (os_memcmp(pos, func[i], len) != 0) {
+			if (maybe && next) {
+				pos = next + 1;
+				continue;
+			}
+			if (allow_skip) {
+				i++;
+				continue;
+			}
+			return 0;
+		}
+		if (!next) {
+			match = 1;
+			break;
+		}
+		pos = next + 1;
+		i++;
+	}
+	if (!match)
+		return 0;
+
+	wpa_trace_test_fail_after--;
+	if (wpa_trace_test_fail_after == 0) {
+		wpa_printf(MSG_INFO, "TESTING: fail at %s",
+			   wpa_trace_test_fail_func);
+		for (i = 0; i < res; i++)
+			wpa_printf(MSG_INFO, "backtrace[%d] = %s",
+				   (int) i, func[i]);
+		return 1;
+	}
+
+	return 0;
+}
+
 #else
 
 static inline int testing_fail_alloc(void)
diff --git a/src/utils/os_win32.c b/src/utils/os_win32.c
index 890abf4..dea27b9 100644
--- a/src/utils/os_win32.c
+++ b/src/utils/os_win32.c
@@ -216,18 +216,18 @@
 }
 
 
-int os_fsync(FILE *stream)
+int os_fdatasync(FILE *stream)
 {
-	HANDLE hFile;
+	HANDLE h;
 
 	if (stream == NULL)
 		return -1;
 
-	hFile = _get_osfhandle(_fileno(stream));
-	if (hFile == INVALID_HANDLE_VALUE)
+	h = (HANDLE) _get_osfhandle(_fileno(stream));
+	if (h == INVALID_HANDLE_VALUE)
 		return -1;
 
-	if (!FlushFileBuffers(hFile))
+	if (!FlushFileBuffers(h))
 		return -1;
 
 	return 0;
diff --git a/src/utils/pcsc_funcs.c b/src/utils/pcsc_funcs.c
index 6f5ea93..2f1157b 100644
--- a/src/utils/pcsc_funcs.c
+++ b/src/utils/pcsc_funcs.c
@@ -275,7 +275,7 @@
 	pos++;
 	if (pos >= end)
 		return -1;
-	if ((pos + pos[0]) < end)
+	if (pos[0] < end - pos)
 		end = pos + 1 + pos[0];
 	pos++;
 	wpa_hexdump(MSG_DEBUG, "SCARD: file header FSP template",
@@ -1385,7 +1385,7 @@
 		end = buf + len;
 
 		/* RES */
-		if (pos[0] > RES_MAX_LEN || pos + pos[0] > end) {
+		if (pos[0] > RES_MAX_LEN || pos[0] > end - pos) {
 			wpa_printf(MSG_DEBUG, "SCARD: Invalid RES");
 			return -1;
 		}
@@ -1395,7 +1395,7 @@
 		wpa_hexdump(MSG_DEBUG, "SCARD: RES", res, *res_len);
 
 		/* CK */
-		if (pos[0] != CK_LEN || pos + CK_LEN > end) {
+		if (pos[0] != CK_LEN || CK_LEN > end - pos) {
 			wpa_printf(MSG_DEBUG, "SCARD: Invalid CK");
 			return -1;
 		}
@@ -1405,7 +1405,7 @@
 		wpa_hexdump(MSG_DEBUG, "SCARD: CK", ck, CK_LEN);
 
 		/* IK */
-		if (pos[0] != IK_LEN || pos + IK_LEN > end) {
+		if (pos[0] != IK_LEN || IK_LEN > end - pos) {
 			wpa_printf(MSG_DEBUG, "SCARD: Invalid IK");
 			return -1;
 		}
diff --git a/src/utils/radiotap.c b/src/utils/radiotap.c
index f8f815a..c9a5023 100644
--- a/src/utils/radiotap.c
+++ b/src/utils/radiotap.c
@@ -123,13 +123,13 @@
 
 	/* find payload start allowing for extended bitmap(s) */
 
-	if (iterator->_bitmap_shifter & (1<<IEEE80211_RADIOTAP_EXT)) {
+	if (iterator->_bitmap_shifter & BIT(IEEE80211_RADIOTAP_EXT)) {
 		if ((unsigned long)iterator->_arg -
 		    (unsigned long)iterator->_rtheader + sizeof(uint32_t) >
 		    (unsigned long)iterator->_max_length)
 			return -EINVAL;
 		while (get_unaligned_le32(iterator->_arg) &
-					(1 << IEEE80211_RADIOTAP_EXT)) {
+		       BIT(IEEE80211_RADIOTAP_EXT)) {
 			iterator->_arg += sizeof(uint32_t);
 
 			/*
diff --git a/src/utils/utils_module_tests.c b/src/utils/utils_module_tests.c
index b2c7e08..41511b9 100644
--- a/src/utils/utils_module_tests.c
+++ b/src/utils/utils_module_tests.c
@@ -14,6 +14,8 @@
 #include "utils/ext_password.h"
 #include "utils/trace.h"
 #include "utils/base64.h"
+#include "utils/ip_addr.h"
+#include "utils/eloop.h"
 
 
 struct printf_test_data {
@@ -44,6 +46,7 @@
 	char buf[100];
 	u8 bin[100];
 	int errors = 0;
+	int array[10];
 
 	wpa_printf(MSG_INFO, "printf encode/decode tests");
 
@@ -92,9 +95,24 @@
 	if (printf_decode(bin, 3, "\\xa") != 1 || bin[0] != 10)
 		errors++;
 
+	if (printf_decode(bin, 3, "\\xq") != 1 || bin[0] != 'q')
+		errors++;
+
 	if (printf_decode(bin, 3, "\\a") != 1 || bin[0] != 'a')
 		errors++;
 
+	array[0] = 10;
+	array[1] = 10;
+	array[2] = 5;
+	array[3] = 10;
+	array[4] = 5;
+	array[5] = 0;
+	if (int_array_len(array) != 5)
+		errors++;
+	int_array_sort_unique(array);
+	if (int_array_len(array) != 2)
+		errors++;
+
 	if (errors) {
 		wpa_printf(MSG_ERROR, "%d printf test(s) failed", errors);
 		return -1;
@@ -336,7 +354,7 @@
 
 static int common_tests(void)
 {
-	char buf[3];
+	char buf[3], longbuf[100];
 	u8 addr[ETH_ALEN] = { 1, 2, 3, 4, 5, 6 };
 	u8 bin[3];
 	int errors = 0;
@@ -409,6 +427,11 @@
 		errors++;
 	}
 
+	if (wpa_snprintf_hex_sep(longbuf, 0, addr, ETH_ALEN, '-') != 0 ||
+	    wpa_snprintf_hex_sep(longbuf, 5, addr, ETH_ALEN, '-') != 3 ||
+	    os_strcmp(longbuf, "01-0") != 0)
+		errors++;
+
 	if (errors) {
 		wpa_printf(MSG_ERROR, "%d common test(s) failed", errors);
 		return -1;
@@ -418,6 +441,403 @@
 }
 
 
+static int os_tests(void)
+{
+	int errors = 0;
+	void *ptr;
+	os_time_t t;
+
+	wpa_printf(MSG_INFO, "os tests");
+
+	ptr = os_calloc((size_t) -1, (size_t) -1);
+	if (ptr) {
+		errors++;
+		os_free(ptr);
+	}
+	ptr = os_calloc((size_t) 2, (size_t) -1);
+	if (ptr) {
+		errors++;
+		os_free(ptr);
+	}
+	ptr = os_calloc((size_t) -1, (size_t) 2);
+	if (ptr) {
+		errors++;
+		os_free(ptr);
+	}
+
+	ptr = os_realloc_array(NULL, (size_t) -1, (size_t) -1);
+	if (ptr) {
+		errors++;
+		os_free(ptr);
+	}
+
+	os_sleep(1, 1);
+
+	if (os_mktime(1969, 1, 1, 1, 1, 1, &t) == 0 ||
+	    os_mktime(1971, 0, 1, 1, 1, 1, &t) == 0 ||
+	    os_mktime(1971, 13, 1, 1, 1, 1, &t) == 0 ||
+	    os_mktime(1971, 1, 0, 1, 1, 1, &t) == 0 ||
+	    os_mktime(1971, 1, 32, 1, 1, 1, &t) == 0 ||
+	    os_mktime(1971, 1, 1, -1, 1, 1, &t) == 0 ||
+	    os_mktime(1971, 1, 1, 24, 1, 1, &t) == 0 ||
+	    os_mktime(1971, 1, 1, 1, -1, 1, &t) == 0 ||
+	    os_mktime(1971, 1, 1, 1, 60, 1, &t) == 0 ||
+	    os_mktime(1971, 1, 1, 1, 1, -1, &t) == 0 ||
+	    os_mktime(1971, 1, 1, 1, 1, 61, &t) == 0 ||
+	    os_mktime(1971, 1, 1, 1, 1, 1, &t) != 0 ||
+	    os_mktime(2020, 1, 2, 3, 4, 5, &t) != 0 ||
+	    os_mktime(2015, 12, 31, 23, 59, 59, &t) != 0)
+		errors++;
+
+	if (os_setenv("hwsim_test_env", "test value", 0) != 0 ||
+	    os_setenv("hwsim_test_env", "test value 2", 1) != 0 ||
+	    os_unsetenv("hwsim_test_env") != 0)
+		errors++;
+
+	if (os_file_exists("/this-file-does-not-exists-hwsim") != 0)
+		errors++;
+
+	if (errors) {
+		wpa_printf(MSG_ERROR, "%d os test(s) failed", errors);
+		return -1;
+	}
+
+	return 0;
+}
+
+
+static int wpabuf_tests(void)
+{
+	int errors = 0;
+	void *ptr;
+	struct wpabuf *buf;
+
+	wpa_printf(MSG_INFO, "wpabuf tests");
+
+	ptr = os_malloc(100);
+	if (ptr) {
+		buf = wpabuf_alloc_ext_data(ptr, 100);
+		if (buf) {
+			if (wpabuf_resize(&buf, 100) < 0)
+				errors++;
+			else
+				wpabuf_put(buf, 100);
+			wpabuf_free(buf);
+		} else {
+			errors++;
+			os_free(ptr);
+		}
+	} else {
+		errors++;
+	}
+
+	buf = wpabuf_alloc(100);
+	if (buf) {
+		struct wpabuf *buf2;
+
+		wpabuf_put(buf, 100);
+		if (wpabuf_resize(&buf, 100) < 0)
+			errors++;
+		else
+			wpabuf_put(buf, 100);
+		buf2 = wpabuf_concat(buf, NULL);
+		if (buf2 != buf)
+			errors++;
+		wpabuf_free(buf2);
+	} else {
+		errors++;
+	}
+
+	buf = NULL;
+	buf = wpabuf_zeropad(buf, 10);
+	if (buf != NULL)
+		errors++;
+
+	if (errors) {
+		wpa_printf(MSG_ERROR, "%d wpabuf test(s) failed", errors);
+		return -1;
+	}
+
+	return 0;
+}
+
+
+static int ip_addr_tests(void)
+{
+	int errors = 0;
+	struct hostapd_ip_addr addr;
+	char buf[100];
+
+	wpa_printf(MSG_INFO, "ip_addr tests");
+
+	if (hostapd_parse_ip_addr("1.2.3.4", &addr) != 0 ||
+	    addr.af != AF_INET ||
+	    hostapd_ip_txt(NULL, buf, sizeof(buf)) != NULL ||
+	    hostapd_ip_txt(&addr, buf, 1) != buf || buf[0] != '\0' ||
+	    hostapd_ip_txt(&addr, buf, 0) != NULL ||
+	    hostapd_ip_txt(&addr, buf, sizeof(buf)) != buf)
+		errors++;
+
+	if (hostapd_parse_ip_addr("::", &addr) != 0 ||
+	    addr.af != AF_INET6 ||
+	    hostapd_ip_txt(&addr, buf, 1) != buf || buf[0] != '\0' ||
+	    hostapd_ip_txt(&addr, buf, sizeof(buf)) != buf)
+		errors++;
+
+	if (errors) {
+		wpa_printf(MSG_ERROR, "%d ip_addr test(s) failed", errors);
+		return -1;
+	}
+
+	return 0;
+}
+
+
+struct test_eloop {
+	unsigned int magic;
+	int close_in_timeout;
+	int pipefd1[2];
+	int pipefd2[2];
+};
+
+
+static void eloop_tests_start(int close_in_timeout);
+
+
+static void eloop_test_read_2(int sock, void *eloop_ctx, void *sock_ctx)
+{
+	struct test_eloop *t = eloop_ctx;
+	ssize_t res;
+	char buf[10];
+
+	wpa_printf(MSG_INFO, "%s: sock=%d", __func__, sock);
+
+	if (t->magic != 0x12345678) {
+		wpa_printf(MSG_INFO, "%s: unexpected magic 0x%x",
+			   __func__, t->magic);
+	}
+
+	if (t->pipefd2[0] != sock) {
+		wpa_printf(MSG_INFO, "%s: unexpected sock %d != %d",
+			   __func__, sock, t->pipefd2[0]);
+	}
+
+	res = read(sock, buf, sizeof(buf));
+	wpa_printf(MSG_INFO, "%s: sock=%d --> res=%d",
+		   __func__, sock, (int) res);
+}
+
+
+static void eloop_test_read_2_wrong(int sock, void *eloop_ctx, void *sock_ctx)
+{
+	struct test_eloop *t = eloop_ctx;
+
+	wpa_printf(MSG_INFO, "%s: sock=%d", __func__, sock);
+
+	if (t->magic != 0x12345678) {
+		wpa_printf(MSG_INFO, "%s: unexpected magic 0x%x",
+			   __func__, t->magic);
+	}
+
+	if (t->pipefd2[0] != sock) {
+		wpa_printf(MSG_INFO, "%s: unexpected sock %d != %d",
+			   __func__, sock, t->pipefd2[0]);
+	}
+
+	/*
+	 * This is expected to block due to the original socket with data having
+	 * been closed and no new data having been written to the new socket
+	 * with the same fd. To avoid blocking the process during test, skip the
+	 * read here.
+	 */
+	wpa_printf(MSG_ERROR, "%s: FAIL - should not have called this function",
+		   __func__);
+}
+
+
+static void reopen_pipefd2(struct test_eloop *t)
+{
+	if (t->pipefd2[0] < 0) {
+		wpa_printf(MSG_INFO, "pipefd2 had been closed");
+	} else {
+		int res;
+
+		wpa_printf(MSG_INFO, "close pipefd2");
+		eloop_unregister_read_sock(t->pipefd2[0]);
+		close(t->pipefd2[0]);
+		t->pipefd2[0] = -1;
+		close(t->pipefd2[1]);
+		t->pipefd2[1] = -1;
+
+		res = pipe(t->pipefd2);
+		if (res < 0) {
+			wpa_printf(MSG_INFO, "pipe: %s", strerror(errno));
+			t->pipefd2[0] = -1;
+			t->pipefd2[1] = -1;
+			return;
+		}
+
+		wpa_printf(MSG_INFO,
+			   "re-register pipefd2 with new sockets %d,%d",
+			   t->pipefd2[0], t->pipefd2[1]);
+		eloop_register_read_sock(t->pipefd2[0], eloop_test_read_2_wrong,
+					 t, NULL);
+	}
+}
+
+
+static void eloop_test_read_1(int sock, void *eloop_ctx, void *sock_ctx)
+{
+	struct test_eloop *t = eloop_ctx;
+	ssize_t res;
+	char buf[10];
+
+	wpa_printf(MSG_INFO, "%s: sock=%d", __func__, sock);
+
+	if (t->magic != 0x12345678) {
+		wpa_printf(MSG_INFO, "%s: unexpected magic 0x%x",
+			   __func__, t->magic);
+	}
+
+	if (t->pipefd1[0] != sock) {
+		wpa_printf(MSG_INFO, "%s: unexpected sock %d != %d",
+			   __func__, sock, t->pipefd1[0]);
+	}
+
+	res = read(sock, buf, sizeof(buf));
+	wpa_printf(MSG_INFO, "%s: sock=%d --> res=%d",
+		   __func__, sock, (int) res);
+
+	if (!t->close_in_timeout)
+		reopen_pipefd2(t);
+}
+
+
+static void eloop_test_cb(void *eloop_data, void *user_ctx)
+{
+	struct test_eloop *t = eloop_data;
+
+	wpa_printf(MSG_INFO, "%s", __func__);
+
+	if (t->magic != 0x12345678) {
+		wpa_printf(MSG_INFO, "%s: unexpected magic 0x%x",
+			   __func__, t->magic);
+	}
+
+	if (t->close_in_timeout)
+		reopen_pipefd2(t);
+}
+
+
+static void eloop_test_timeout(void *eloop_data, void *user_ctx)
+{
+	struct test_eloop *t = eloop_data;
+	int next_run = 0;
+
+	wpa_printf(MSG_INFO, "%s", __func__);
+
+	if (t->magic != 0x12345678) {
+		wpa_printf(MSG_INFO, "%s: unexpected magic 0x%x",
+			   __func__, t->magic);
+	}
+
+	if (t->pipefd1[0] >= 0) {
+		wpa_printf(MSG_INFO, "pipefd1 had not been closed");
+		eloop_unregister_read_sock(t->pipefd1[0]);
+		close(t->pipefd1[0]);
+		t->pipefd1[0] = -1;
+		close(t->pipefd1[1]);
+		t->pipefd1[1] = -1;
+	}
+
+	if (t->pipefd2[0] >= 0) {
+		wpa_printf(MSG_INFO, "pipefd2 had not been closed");
+		eloop_unregister_read_sock(t->pipefd2[0]);
+		close(t->pipefd2[0]);
+		t->pipefd2[0] = -1;
+		close(t->pipefd2[1]);
+		t->pipefd2[1] = -1;
+	}
+
+	next_run = t->close_in_timeout;
+	t->magic = 0;
+	wpa_printf(MSG_INFO, "%s - free(%p)", __func__, t);
+	os_free(t);
+
+	if (next_run)
+		eloop_tests_start(0);
+}
+
+
+static void eloop_tests_start(int close_in_timeout)
+{
+	struct test_eloop *t;
+	int res;
+
+	t = os_zalloc(sizeof(*t));
+	if (!t)
+		return;
+	t->magic = 0x12345678;
+	t->close_in_timeout = close_in_timeout;
+
+	wpa_printf(MSG_INFO, "starting eloop tests (%p) (close_in_timeout=%d)",
+		   t, close_in_timeout);
+
+	res = pipe(t->pipefd1);
+	if (res < 0) {
+		wpa_printf(MSG_INFO, "pipe: %s", strerror(errno));
+		os_free(t);
+		return;
+	}
+
+	res = pipe(t->pipefd2);
+	if (res < 0) {
+		wpa_printf(MSG_INFO, "pipe: %s", strerror(errno));
+		close(t->pipefd1[0]);
+		close(t->pipefd1[1]);
+		os_free(t);
+		return;
+	}
+
+	wpa_printf(MSG_INFO, "pipe fds: %d,%d %d,%d",
+		   t->pipefd1[0], t->pipefd1[1],
+		   t->pipefd2[0], t->pipefd2[1]);
+
+	eloop_register_read_sock(t->pipefd1[0], eloop_test_read_1, t, NULL);
+	eloop_register_read_sock(t->pipefd2[0], eloop_test_read_2, t, NULL);
+	eloop_register_timeout(0, 0, eloop_test_cb, t, NULL);
+	eloop_register_timeout(0, 200000, eloop_test_timeout, t, NULL);
+
+	if (write(t->pipefd1[1], "HELLO", 5) < 0)
+		wpa_printf(MSG_INFO, "write: %s", strerror(errno));
+	if (write(t->pipefd2[1], "TEST", 4) < 0)
+		wpa_printf(MSG_INFO, "write: %s", strerror(errno));
+	os_sleep(0, 50000);
+	wpa_printf(MSG_INFO, "waiting for eloop callbacks");
+}
+
+
+static void eloop_tests_run(void *eloop_data, void *user_ctx)
+{
+	eloop_tests_start(1);
+}
+
+
+static int eloop_tests(void)
+{
+	wpa_printf(MSG_INFO, "schedule eloop tests to be run");
+
+	/*
+	 * Cannot return error from these without a significant design change,
+	 * so for now, run the tests from a scheduled timeout and require
+	 * separate verification of the results from the debug log.
+	 */
+	eloop_register_timeout(0, 0, eloop_tests_run, NULL, NULL);
+
+	return 0;
+}
+
+
 int utils_module_tests(void)
 {
 	int ret = 0;
@@ -430,6 +850,10 @@
 	    bitfield_tests() < 0 ||
 	    base64_tests() < 0 ||
 	    common_tests() < 0 ||
+	    os_tests() < 0 ||
+	    wpabuf_tests() < 0 ||
+	    ip_addr_tests() < 0 ||
+	    eloop_tests() < 0 ||
 	    int_array_tests() < 0)
 		ret = -1;
 
diff --git a/src/utils/wpa_debug.c b/src/utils/wpa_debug.c
index 3c26301..3275524 100644
--- a/src/utils/wpa_debug.c
+++ b/src/utils/wpa_debug.c
@@ -517,16 +517,18 @@
 {
 #ifdef CONFIG_DEBUG_FILE
 	int rv;
-	if (last_path) {
-		char *tmp = os_strdup(last_path);
-		wpa_debug_close_file();
-		rv = wpa_debug_open_file(tmp);
-		os_free(tmp);
-	} else {
-		wpa_printf(MSG_ERROR, "Last-path was not set, cannot "
-			   "re-open log file.");
-		rv = -1;
-	}
+	char *tmp;
+
+	if (!last_path)
+		return 0; /* logfile not used */
+
+	tmp = os_strdup(last_path);
+	if (!tmp)
+		return -1;
+
+	wpa_debug_close_file();
+	rv = wpa_debug_open_file(tmp);
+	os_free(tmp);
 	return rv;
 #else /* CONFIG_DEBUG_FILE */
 	return 0;
@@ -819,3 +821,42 @@
 	bin_clear_free(buf, buflen);
 }
 #endif /* CONFIG_NO_HOSTAPD_LOGGER */
+
+
+const char * debug_level_str(int level)
+{
+	switch (level) {
+	case MSG_EXCESSIVE:
+		return "EXCESSIVE";
+	case MSG_MSGDUMP:
+		return "MSGDUMP";
+	case MSG_DEBUG:
+		return "DEBUG";
+	case MSG_INFO:
+		return "INFO";
+	case MSG_WARNING:
+		return "WARNING";
+	case MSG_ERROR:
+		return "ERROR";
+	default:
+		return "?";
+	}
+}
+
+
+int str_to_debug_level(const char *s)
+{
+	if (os_strcasecmp(s, "EXCESSIVE") == 0)
+		return MSG_EXCESSIVE;
+	if (os_strcasecmp(s, "MSGDUMP") == 0)
+		return MSG_MSGDUMP;
+	if (os_strcasecmp(s, "DEBUG") == 0)
+		return MSG_DEBUG;
+	if (os_strcasecmp(s, "INFO") == 0)
+		return MSG_INFO;
+	if (os_strcasecmp(s, "WARNING") == 0)
+		return MSG_WARNING;
+	if (os_strcasecmp(s, "ERROR") == 0)
+		return MSG_ERROR;
+	return -1;
+}
diff --git a/src/utils/wpa_debug.h b/src/utils/wpa_debug.h
index 87bd7fa..17d8f96 100644
--- a/src/utils/wpa_debug.h
+++ b/src/utils/wpa_debug.h
@@ -364,4 +364,7 @@
 #define WPA_ASSERT(a) do { } while (0)
 #endif
 
+const char * debug_level_str(int level);
+int str_to_debug_level(const char *s);
+
 #endif /* WPA_DEBUG_H */
diff --git a/src/utils/wpabuf.c b/src/utils/wpabuf.c
index 7aafa0a..11e7323 100644
--- a/src/utils/wpabuf.c
+++ b/src/utils/wpabuf.c
@@ -17,7 +17,7 @@
 
 struct wpabuf_trace {
 	unsigned int magic;
-};
+} __attribute__((aligned(8)));
 
 static struct wpabuf_trace * wpabuf_get_trace(const struct wpabuf *buf)
 {
diff --git a/src/wps/http_client.c b/src/wps/http_client.c
index 0290013..cdf3a51 100644
--- a/src/wps/http_client.c
+++ b/src/wps/http_client.c
@@ -85,15 +85,16 @@
 {
 	struct http_client *c = eloop_ctx;
 	int res;
+	size_t send_len;
 
+	send_len = wpabuf_len(c->req) - c->req_pos;
 	wpa_printf(MSG_DEBUG, "HTTP: Send client request to %s:%d (%lu of %lu "
 		   "bytes remaining)",
 		   inet_ntoa(c->dst.sin_addr), ntohs(c->dst.sin_port),
 		   (unsigned long) wpabuf_len(c->req),
-		   (unsigned long) wpabuf_len(c->req) - c->req_pos);
+		   (unsigned long) send_len);
 
-	res = send(c->sd, wpabuf_head_u8(c->req) + c->req_pos,
-		   wpabuf_len(c->req) - c->req_pos, 0);
+	res = send(c->sd, wpabuf_head_u8(c->req) + c->req_pos, send_len, 0);
 	if (res < 0) {
 		wpa_printf(MSG_DEBUG, "HTTP: Failed to send buffer: %s",
 			   strerror(errno));
@@ -102,12 +103,11 @@
 		return;
 	}
 
-	if ((size_t) res < wpabuf_len(c->req) - c->req_pos) {
+	if ((size_t) res < send_len) {
 		wpa_printf(MSG_DEBUG, "HTTP: Sent %d of %lu bytes; %lu bytes "
 			   "remaining",
 			   res, (unsigned long) wpabuf_len(c->req),
-			   (unsigned long) wpabuf_len(c->req) - c->req_pos -
-			   res);
+			   (unsigned long) send_len - res);
 		c->req_pos += res;
 		return;
 	}
@@ -146,24 +146,20 @@
 	c->cb_ctx = cb_ctx;
 
 	c->sd = socket(AF_INET, SOCK_STREAM, 0);
-	if (c->sd < 0) {
-		http_client_free(c);
-		return NULL;
-	}
+	if (c->sd < 0)
+		goto fail;
 
 	if (fcntl(c->sd, F_SETFL, O_NONBLOCK) != 0) {
 		wpa_printf(MSG_DEBUG, "HTTP: fnctl(O_NONBLOCK) failed: %s",
 			   strerror(errno));
-		http_client_free(c);
-		return NULL;
+		goto fail;
 	}
 
 	if (connect(c->sd, (struct sockaddr *) dst, sizeof(*dst))) {
 		if (errno != EINPROGRESS) {
 			wpa_printf(MSG_DEBUG, "HTTP: Failed to connect: %s",
 				   strerror(errno));
-			http_client_free(c);
-			return NULL;
+			goto fail;
 		}
 
 		/*
@@ -173,20 +169,18 @@
 	}
 
 	if (eloop_register_sock(c->sd, EVENT_TYPE_WRITE, http_client_tx_ready,
-				c, NULL)) {
-		http_client_free(c);
-		return NULL;
-	}
-
-	if (eloop_register_timeout(HTTP_CLIENT_TIMEOUT_SEC, 0,
-				   http_client_timeout, c, NULL)) {
-		http_client_free(c);
-		return NULL;
-	}
+				c, NULL) ||
+	    eloop_register_timeout(HTTP_CLIENT_TIMEOUT_SEC, 0,
+				   http_client_timeout, c, NULL))
+		goto fail;
 
 	c->req = req;
 
 	return c;
+
+fail:
+	http_client_free(c);
+	return NULL;
 }
 
 
diff --git a/src/wps/http_server.c b/src/wps/http_server.c
index ac088c4..507abe8 100644
--- a/src/wps/http_server.c
+++ b/src/wps/http_server.c
@@ -277,11 +277,9 @@
 			   "%s", srv->port, strerror(errno));
 		goto fail;
 	}
-	if (listen(srv->fd, 10 /* max backlog */) < 0)
-		goto fail;
-	if (fcntl(srv->fd, F_SETFL, O_NONBLOCK) < 0)
-		goto fail;
-	if (eloop_register_sock(srv->fd, EVENT_TYPE_READ, http_server_cb,
+	if (listen(srv->fd, 10 /* max backlog */) < 0 ||
+	    fcntl(srv->fd, F_SETFL, O_NONBLOCK) < 0 ||
+	    eloop_register_sock(srv->fd, EVENT_TYPE_READ, http_server_cb,
 				srv, NULL))
 		goto fail;
 
diff --git a/src/wps/httpread.c b/src/wps/httpread.c
index 180b572..7a2ba50 100644
--- a/src/wps/httpread.c
+++ b/src/wps/httpread.c
@@ -278,8 +278,6 @@
 			}
 		}
 		*uri = 0;       /* null terminate */
-		while (isgraph(*hbp))
-			hbp++;
 		while (*hbp == ' ' || *hbp == '\t')
 			hbp++;
 		/* get version */
@@ -506,10 +504,13 @@
 			    new_alloc_nbytes < (h->content_length + 1))
 				new_alloc_nbytes = h->content_length + 1;
 			if (new_alloc_nbytes < h->body_alloc_nbytes ||
-			    new_alloc_nbytes > h->max_bytes) {
+			    new_alloc_nbytes > h->max_bytes +
+			    HTTPREAD_BODYBUF_DELTA) {
 				wpa_printf(MSG_DEBUG,
-					   "httpread: Unacceptable body length %d",
-					   new_alloc_nbytes);
+					   "httpread: Unacceptable body length %d (body_alloc_nbytes=%u max_bytes=%u)",
+					   new_alloc_nbytes,
+					   h->body_alloc_nbytes,
+					   h->max_bytes);
 				goto bad;
 			}
 			if ((new_body = os_realloc(h->body, new_alloc_nbytes))
diff --git a/src/wps/ndef.c b/src/wps/ndef.c
index 8d1ce1e..bb3c055 100644
--- a/src/wps/ndef.c
+++ b/src/wps/ndef.c
@@ -45,9 +45,14 @@
 			return -1;
 		record->payload_length = *pos++;
 	} else {
+		u32 len;
+
 		if (size < 6)
 			return -1;
-		record->payload_length = ntohl(*(u32 *)pos);
+		len = WPA_GET_BE32(pos);
+		if (len > size - 6 || len > 20000)
+			return -1;
+		record->payload_length = len;
 		pos += sizeof(u32);
 	}
 
@@ -68,7 +73,8 @@
 	pos += record->payload_length;
 
 	record->total_length = pos - data;
-	if (record->total_length > size)
+	if (record->total_length > size ||
+	    record->total_length < record->payload_length)
 		return -1;
 	return 0;
 }
diff --git a/src/wps/wps.c b/src/wps/wps.c
index 498f11f..fbaf85a 100644
--- a/src/wps/wps.c
+++ b/src/wps/wps.c
@@ -355,16 +355,16 @@
 int wps_ap_priority_compar(const struct wpabuf *wps_a,
 			   const struct wpabuf *wps_b)
 {
-	struct wps_parse_attr attr_a, attr_b;
+	struct wps_parse_attr attr;
 	int sel_a, sel_b;
 
-	if (wps_a == NULL || wps_parse_msg(wps_a, &attr_a) < 0)
+	if (wps_a == NULL || wps_parse_msg(wps_a, &attr) < 0)
 		return 1;
-	if (wps_b == NULL || wps_parse_msg(wps_b, &attr_b) < 0)
-		return -1;
+	sel_a = attr.selected_registrar && *attr.selected_registrar != 0;
 
-	sel_a = attr_a.selected_registrar && *attr_a.selected_registrar != 0;
-	sel_b = attr_b.selected_registrar && *attr_b.selected_registrar != 0;
+	if (wps_b == NULL || wps_parse_msg(wps_b, &attr) < 0)
+		return -1;
+	sel_b = attr.selected_registrar && *attr.selected_registrar != 0;
 
 	if (sel_a && !sel_b)
 		return -1;
diff --git a/src/wps/wps_attr_parse.c b/src/wps/wps_attr_parse.c
index 11a967b..756d57e 100644
--- a/src/wps/wps_attr_parse.c
+++ b/src/wps/wps_attr_parse.c
@@ -83,10 +83,10 @@
 	const u8 *end = pos + len;
 	u8 id, elen;
 
-	while (pos + 2 <= end) {
+	while (end - pos >= 2) {
 		id = *pos++;
 		elen = *pos++;
-		if (pos + elen > end)
+		if (elen > end - pos)
 			break;
 		if (wps_set_vendor_ext_wfa_subelem(attr, id, elen, pos) < 0)
 			return -1;
diff --git a/src/wps/wps_attr_parse.h b/src/wps/wps_attr_parse.h
index 82c4739..8188fe9 100644
--- a/src/wps/wps_attr_parse.h
+++ b/src/wps/wps_attr_parse.h
@@ -59,43 +59,44 @@
 
 	/* variable length fields */
 	const u8 *manufacturer;
-	size_t manufacturer_len;
 	const u8 *model_name;
-	size_t model_name_len;
 	const u8 *model_number;
-	size_t model_number_len;
 	const u8 *serial_number;
-	size_t serial_number_len;
 	const u8 *dev_name;
-	size_t dev_name_len;
 	const u8 *public_key;
-	size_t public_key_len;
 	const u8 *encr_settings;
-	size_t encr_settings_len;
 	const u8 *ssid; /* <= 32 octets */
-	size_t ssid_len;
 	const u8 *network_key; /* <= 64 octets */
-	size_t network_key_len;
 	const u8 *authorized_macs; /* <= 30 octets */
-	size_t authorized_macs_len;
 	const u8 *sec_dev_type_list; /* <= 128 octets */
-	size_t sec_dev_type_list_len;
 	const u8 *oob_dev_password; /* 38..54 octets */
-	size_t oob_dev_password_len;
+	u16 manufacturer_len;
+	u16 model_name_len;
+	u16 model_number_len;
+	u16 serial_number_len;
+	u16 dev_name_len;
+	u16 public_key_len;
+	u16 encr_settings_len;
+	u16 ssid_len;
+	u16 network_key_len;
+	u16 authorized_macs_len;
+	u16 sec_dev_type_list_len;
+	u16 oob_dev_password_len;
 
 	/* attributes that can occur multiple times */
 #define MAX_CRED_COUNT 10
-	const u8 *cred[MAX_CRED_COUNT];
-	size_t cred_len[MAX_CRED_COUNT];
-	size_t num_cred;
-
 #define MAX_REQ_DEV_TYPE_COUNT 10
-	const u8 *req_dev_type[MAX_REQ_DEV_TYPE_COUNT];
-	size_t num_req_dev_type;
 
+	unsigned int num_cred;
+	unsigned int num_req_dev_type;
+	unsigned int num_vendor_ext;
+
+	u16 cred_len[MAX_CRED_COUNT];
+	u16 vendor_ext_len[MAX_WPS_PARSE_VENDOR_EXT];
+
+	const u8 *cred[MAX_CRED_COUNT];
+	const u8 *req_dev_type[MAX_REQ_DEV_TYPE_COUNT];
 	const u8 *vendor_ext[MAX_WPS_PARSE_VENDOR_EXT];
-	size_t vendor_ext_len[MAX_WPS_PARSE_VENDOR_EXT];
-	size_t num_vendor_ext;
 };
 
 int wps_parse_msg(const struct wpabuf *msg, struct wps_parse_attr *attr);
diff --git a/src/wps/wps_common.c b/src/wps/wps_common.c
index 16d466e..88f85fe 100644
--- a/src/wps/wps_common.c
+++ b/src/wps/wps_common.c
@@ -528,7 +528,7 @@
 {
 	u16 methods = 0;
 
-	if (str == NULL) {
+	if (str == NULL || str[0] == '\0') {
 		/* Default to enabling methods based on build configuration */
 		methods |= WPS_CONFIG_DISPLAY | WPS_CONFIG_KEYPAD;
 		methods |= WPS_CONFIG_VIRT_DISPLAY;
diff --git a/src/wps/wps_enrollee.c b/src/wps/wps_enrollee.c
index 89957b1..9321b72 100644
--- a/src/wps/wps_enrollee.c
+++ b/src/wps/wps_enrollee.c
@@ -759,7 +759,7 @@
 
 
 static int wps_process_creds(struct wps_data *wps, const u8 *cred[],
-			     size_t cred_len[], size_t num_cred, int wps2)
+			     u16 cred_len[], unsigned int num_cred, int wps2)
 {
 	size_t i;
 	int ok = 0;
@@ -799,6 +799,7 @@
 				     struct wpabuf *attrs, int wps2)
 {
 	struct wps_credential cred;
+	int ret = 0;
 
 	if (!wps->wps->ap)
 		return 0;
@@ -877,10 +878,10 @@
 	if (wps->wps->cred_cb) {
 		cred.cred_attr = wpabuf_head(attrs);
 		cred.cred_attr_len = wpabuf_len(attrs);
-		wps->wps->cred_cb(wps->wps->cb_ctx, &cred);
+		ret = wps->wps->cred_cb(wps->wps->cb_ctx, &cred);
 	}
 
-	return 0;
+	return ret;
 }
 
 
diff --git a/src/wps/wps_er.c b/src/wps/wps_er.c
index 078ff72..b840acd 100644
--- a/src/wps/wps_er.c
+++ b/src/wps/wps_er.c
@@ -1649,11 +1649,15 @@
 	case HTTP_CLIENT_OK:
 		wpa_printf(MSG_DEBUG, "WPS ER: PutMessage OK");
 		reply = http_client_get_body(c);
-		if (reply == NULL)
+		if (reply)
+			msg = os_zalloc(wpabuf_len(reply) + 1);
+		if (msg == NULL) {
+			if (ap->wps) {
+				wps_deinit(ap->wps);
+				ap->wps = NULL;
+			}
 			break;
-		msg = os_zalloc(wpabuf_len(reply) + 1);
-		if (msg == NULL)
-			break;
+		}
 		os_memcpy(msg, wpabuf_head(reply), wpabuf_len(reply));
 		break;
 	case HTTP_CLIENT_FAILED:
@@ -1709,21 +1713,30 @@
 	url = http_client_url_parse(ap->control_url, &dst, &path);
 	if (url == NULL) {
 		wpa_printf(MSG_DEBUG, "WPS ER: Failed to parse controlURL");
-		return;
+		goto fail;
 	}
 
 	buf = wps_er_soap_hdr(msg, "PutMessage", "NewInMessage", path, &dst,
 			      &len_ptr, &body_ptr);
 	os_free(url);
 	if (buf == NULL)
-		return;
+		goto fail;
 
 	wps_er_soap_end(buf, "PutMessage", len_ptr, body_ptr);
 
 	ap->http = http_client_addr(&dst, buf, 10000,
 				    wps_er_http_put_message_cb, ap);
-	if (ap->http == NULL)
+	if (ap->http == NULL) {
 		wpabuf_free(buf);
+		goto fail;
+	}
+	return;
+
+fail:
+	if (ap->wps) {
+		wps_deinit(ap->wps);
+		ap->wps = NULL;
+	}
 }
 
 
diff --git a/src/wps/wps_er_ssdp.c b/src/wps/wps_er_ssdp.c
index e381fec..280b2b3 100644
--- a/src/wps/wps_er_ssdp.c
+++ b/src/wps/wps_er_ssdp.c
@@ -78,9 +78,7 @@
 			if (os_strstr(start, "ssdp:byebye"))
 				byebye = 1;
 		} else if (os_strncasecmp(start, "CACHE-CONTROL:", 14) == 0) {
-			start += 9;
-			while (*start == ' ')
-				start++;
+			start += 14;
 			pos2 = os_strstr(start, "max-age=");
 			if (pos2 == NULL)
 				continue;
diff --git a/src/wps/wps_registrar.c b/src/wps/wps_registrar.c
index 8bcf2b3..4ca3a42 100644
--- a/src/wps/wps_registrar.c
+++ b/src/wps/wps_registrar.c
@@ -2605,13 +2605,16 @@
 		token = wps_get_nfc_pw_token(
 			&wps->wps->registrar->nfc_pw_tokens, wps->dev_pw_id);
 		if (token && token->peer_pk_hash_known) {
+			size_t len;
+
 			wpa_printf(MSG_DEBUG, "WPS: Found matching NFC "
 				   "Password Token");
 			dl_list_del(&token->list);
 			wps->nfc_pw_token = token;
 
 			addr[0] = attr->public_key;
-			sha256_vector(1, addr, &attr->public_key_len, hash);
+			len = attr->public_key_len;
+			sha256_vector(1, addr, &len, hash);
 			if (os_memcmp_const(hash,
 					    wps->nfc_pw_token->pubkey_hash,
 					    WPS_OOB_PUBKEY_HASH_LEN) != 0) {
diff --git a/src/wps/wps_upnp.c b/src/wps/wps_upnp.c
index 933d734..0c458c6 100644
--- a/src/wps/wps_upnp.c
+++ b/src/wps/wps_upnp.c
@@ -695,6 +695,7 @@
 	struct subscription *s;
 	time_t now = time(NULL);
 	time_t expire = now + UPNP_SUBSCRIBE_SEC;
+	char str[80];
 
 	/* Get rid of expired subscriptions so we have room */
 	subscription_list_age(sm, now);
@@ -743,8 +744,10 @@
 		subscription_destroy(s);
 		return NULL;
 	}
-	wpa_printf(MSG_DEBUG, "WPS UPnP: Subscription %p started with %s",
-		   s, callback_urls);
+	uuid_bin2str(s->uuid, str, sizeof(str));
+	wpa_printf(MSG_DEBUG,
+		   "WPS UPnP: Subscription %p (SID %s) started with %s",
+		   s, str, callback_urls);
 	/* Schedule sending this */
 	event_send_all_later(sm);
 	return s;
@@ -1079,6 +1082,7 @@
 void upnp_wps_device_deinit(struct upnp_wps_device_sm *sm, void *priv)
 {
 	struct upnp_wps_device_interface *iface;
+	struct upnp_wps_peer *peer;
 
 	if (!sm)
 		return;
@@ -1099,8 +1103,13 @@
 					    iface->wps->registrar);
 	dl_list_del(&iface->list);
 
-	if (iface->peer.wps)
-		wps_deinit(iface->peer.wps);
+	while ((peer = dl_list_first(&iface->peers, struct upnp_wps_peer,
+				     list))) {
+		if (peer->wps)
+			wps_deinit(peer->wps);
+		dl_list_del(&peer->list);
+		os_free(peer);
+	}
 	os_free(iface->ctx->ap_pin);
 	os_free(iface->ctx);
 	os_free(iface);
@@ -1138,6 +1147,7 @@
 	}
 	wpa_printf(MSG_DEBUG, "WPS UPnP: Init interface instance %p", iface);
 
+	dl_list_init(&iface->peers);
 	iface->ctx = ctx;
 	iface->wps = wps;
 	iface->priv = priv;
diff --git a/src/wps/wps_upnp.h b/src/wps/wps_upnp.h
index 87b7ab1..b6f6df5 100644
--- a/src/wps/wps_upnp.h
+++ b/src/wps/wps_upnp.h
@@ -11,11 +11,14 @@
 #ifndef WPS_UPNP_H
 #define WPS_UPNP_H
 
+#include "utils/list.h"
+
 struct upnp_wps_device_sm;
 struct wps_context;
 struct wps_data;
 
 struct upnp_wps_peer {
+	struct dl_list list;
 	struct wps_data *wps;
 };
 
diff --git a/src/wps/wps_upnp_ap.c b/src/wps/wps_upnp_ap.c
index 2949f14..cca3905 100644
--- a/src/wps/wps_upnp_ap.c
+++ b/src/wps/wps_upnp_ap.c
@@ -34,10 +34,8 @@
 
 	wpa_hexdump_buf(MSG_MSGDUMP, "WPS: SetSelectedRegistrar attributes",
 			msg);
-	if (wps_validate_upnp_set_selected_registrar(msg) < 0)
-		return -1;
-
-	if (wps_parse_msg(msg, &attr) < 0)
+	if (wps_validate_upnp_set_selected_registrar(msg) < 0 ||
+	    wps_parse_msg(msg, &attr) < 0)
 		return -1;
 
 	s->reg = reg;
diff --git a/src/wps/wps_upnp_event.c b/src/wps/wps_upnp_event.c
index 2c8ed4f..94aae75 100644
--- a/src/wps/wps_upnp_event.c
+++ b/src/wps/wps_upnp_event.c
@@ -276,11 +276,9 @@
 	 * Assume we are called ONLY with no current event and ONLY with
 	 * nonempty event queue and ONLY with at least one address to send to.
 	 */
-	if (dl_list_empty(&s->addr_list))
-		return -1;
-	if (s->current_event)
-		return -1;
-	if (dl_list_empty(&s->event_queue))
+	if (dl_list_empty(&s->addr_list) ||
+	    s->current_event ||
+	    dl_list_empty(&s->event_queue))
 		return -1;
 
 	s->current_event = e = event_dequeue(s);
diff --git a/src/wps/wps_upnp_i.h b/src/wps/wps_upnp_i.h
index f289fe6..6a7c627 100644
--- a/src/wps/wps_upnp_i.h
+++ b/src/wps/wps_upnp_i.h
@@ -109,8 +109,7 @@
 	struct wps_context *wps;
 	void *priv;
 
-	/* FIX: maintain separate structures for each UPnP peer */
-	struct upnp_wps_peer peer;
+	struct dl_list peers; /* active UPnP peer sessions */
 };
 
 /*
diff --git a/src/wps/wps_upnp_ssdp.c b/src/wps/wps_upnp_ssdp.c
index 26a740d..968fc03 100644
--- a/src/wps/wps_upnp_ssdp.c
+++ b/src/wps/wps_upnp_ssdp.c
@@ -139,7 +139,7 @@
 	uuid_bin2str(iface->wps->uuid, uuid_string, sizeof(uuid_string));
 	msg = wpabuf_alloc(800); /* more than big enough */
 	if (msg == NULL)
-		goto fail;
+		return NULL;
 	switch (a->type) {
 	case ADVERTISE_UP:
 	case ADVERTISE_DOWN:
@@ -213,10 +213,6 @@
 		*islast = 1;
 
 	return msg;
-
-fail:
-	wpabuf_free(msg);
-	return NULL;
 }
 
 
@@ -744,11 +740,9 @@
 	int sd;
 
 	sd = socket(AF_INET, SOCK_DGRAM, 0);
-	if (sd < 0)
-		goto fail;
-	if (fcntl(sd, F_SETFL, O_NONBLOCK) != 0)
-		goto fail;
-	if (setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)))
+	if (sd < 0 ||
+	    fcntl(sd, F_SETFL, O_NONBLOCK) != 0 ||
+	    setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)))
 		goto fail;
 	os_memset(&addr, 0, sizeof(addr));
 	addr.sin_family = AF_INET;
@@ -760,9 +754,8 @@
 	mcast_addr.imr_interface.s_addr = htonl(INADDR_ANY);
 	mcast_addr.imr_multiaddr.s_addr = inet_addr(UPNP_MULTICAST_ADDRESS);
 	if (setsockopt(sd, IPPROTO_IP, IP_ADD_MEMBERSHIP,
-		       (char *) &mcast_addr, sizeof(mcast_addr)))
-		goto fail;
-	if (setsockopt(sd, IPPROTO_IP, IP_MULTICAST_TTL,
+		       (char *) &mcast_addr, sizeof(mcast_addr)) ||
+	    setsockopt(sd, IPPROTO_IP, IP_MULTICAST_TTL,
 		       &ttl, sizeof(ttl)))
 		goto fail;
 
diff --git a/src/wps/wps_upnp_web.c b/src/wps/wps_upnp_web.c
index b1cf571..7548e84 100644
--- a/src/wps/wps_upnp_web.c
+++ b/src/wps/wps_upnp_web.c
@@ -300,7 +300,8 @@
  * would appear to be required (given that we will be closing it!).
  */
 static void web_connection_parse_get(struct upnp_wps_device_sm *sm,
-				     struct http_request *hreq, char *filename)
+				     struct http_request *hreq,
+				     const char *filename)
 {
 	struct wpabuf *buf; /* output buffer, allocated */
 	char *put_length_here;
@@ -409,6 +410,15 @@
 }
 
 
+static void wps_upnp_peer_del(struct upnp_wps_peer *peer)
+{
+	dl_list_del(&peer->list);
+	if (peer->wps)
+		wps_deinit(peer->wps);
+	os_free(peer);
+}
+
+
 static enum http_reply_code
 web_process_get_device_info(struct upnp_wps_device_sm *sm,
 			    struct wpabuf **reply, const char **replyname)
@@ -426,7 +436,9 @@
 	if (!iface || iface->ctx->ap_pin == NULL)
 		return HTTP_INTERNAL_SERVER_ERROR;
 
-	peer = &iface->peer;
+	peer = os_zalloc(sizeof(*peer));
+	if (!peer)
+		return HTTP_INTERNAL_SERVER_ERROR;
 
 	/*
 	 * Request for DeviceInfo, i.e., M1 TLVs. This is a start of WPS
@@ -436,9 +448,6 @@
 	 * registration.
 	 */
 
-	if (peer->wps)
-		wps_deinit(peer->wps);
-
 	os_memset(&cfg, 0, sizeof(cfg));
 	cfg.wps = iface->wps;
 	cfg.pin = (u8 *) iface->ctx->ap_pin;
@@ -455,8 +464,22 @@
 		*reply = NULL;
 	if (*reply == NULL) {
 		wpa_printf(MSG_INFO, "WPS UPnP: Failed to get DeviceInfo");
+		os_free(peer);
 		return HTTP_INTERNAL_SERVER_ERROR;
 	}
+
+	if (dl_list_len(&iface->peers) > 3) {
+		struct upnp_wps_peer *old;
+
+		old = dl_list_first(&iface->peers, struct upnp_wps_peer, list);
+		if (old) {
+			wpa_printf(MSG_DEBUG, "WPS UPnP: Drop oldest active session");
+			wps_upnp_peer_del(old);
+		}
+	}
+	dl_list_add_tail(&iface->peers, &peer->list);
+	/* TODO: Could schedule a timeout to free the entry */
+
 	*replyname = name;
 	return HTTP_OK;
 }
@@ -472,6 +495,8 @@
 	enum wps_process_res res;
 	enum wsc_op_code op_code;
 	struct upnp_wps_device_interface *iface;
+	struct wps_parse_attr attr;
+	struct upnp_wps_peer *tmp, *peer;
 
 	iface = dl_list_first(&sm->interfaces,
 			      struct upnp_wps_device_interface, list);
@@ -487,11 +512,56 @@
 	msg = xml_get_base64_item(data, "NewInMessage", &ret);
 	if (msg == NULL)
 		return ret;
-	res = wps_process_msg(iface->peer.wps, WSC_UPnP, msg);
-	if (res == WPS_FAILURE)
+
+	if (wps_parse_msg(msg, &attr)) {
+		wpa_printf(MSG_DEBUG,
+			   "WPS UPnP: Could not parse PutMessage - NewInMessage");
+		wpabuf_free(msg);
+		return HTTP_BAD_REQUEST;
+	}
+
+	/* Find a matching active peer session */
+	peer = NULL;
+	dl_list_for_each(tmp, &iface->peers, struct upnp_wps_peer, list) {
+		if (!tmp->wps)
+			continue;
+		if (attr.enrollee_nonce &&
+		    os_memcmp(tmp->wps->nonce_e, attr.enrollee_nonce,
+			      WPS_NONCE_LEN) != 0)
+			continue; /* Enrollee nonce mismatch */
+		if (attr.msg_type &&
+		    *attr.msg_type != WPS_M2 &&
+		    *attr.msg_type != WPS_M2D &&
+		    attr.registrar_nonce &&
+		    os_memcmp(tmp->wps->nonce_r, attr.registrar_nonce,
+			      WPS_NONCE_LEN) != 0)
+			continue; /* Registrar nonce mismatch */
+		peer = tmp;
+		break;
+	}
+	if (!peer) {
+		/*
+		  Try to use the first entry in case message could work with
+		 * it. The actual handler function will reject this, if needed.
+		 * This maintains older behavior where only a single peer entry
+		 * was supported.
+		 */
+		peer = dl_list_first(&iface->peers, struct upnp_wps_peer, list);
+	}
+	if (!peer || !peer->wps) {
+		wpa_printf(MSG_DEBUG, "WPS UPnP: No active peer entry found");
+		wpabuf_free(msg);
+		return HTTP_BAD_REQUEST;
+	}
+
+	res = wps_process_msg(peer->wps, WSC_UPnP, msg);
+	if (res == WPS_FAILURE) {
 		*reply = NULL;
-	else
-		*reply = wps_get_msg(iface->peer.wps, &op_code);
+		wpa_printf(MSG_DEBUG, "WPS UPnP: Drop active peer session");
+		wps_upnp_peer_del(peer);
+	} else {
+		*reply = wps_get_msg(peer->wps, &op_code);
+	}
 	wpabuf_free(msg);
 	if (*reply == NULL)
 		return HTTP_INTERNAL_SERVER_ERROR;
@@ -1003,6 +1073,8 @@
 				ret = HTTP_INTERNAL_SERVER_ERROR;
 				goto error;
 			}
+			if (len > 0 && callback_urls[len - 1] == '\r')
+				callback_urls[len - 1] = '\0';
 			continue;
 		}
 		/* SID is only for renewal */
@@ -1214,18 +1286,25 @@
 	}
 
 	if (got_uuid) {
+		char str[80];
+
+		uuid_bin2str(uuid, str, sizeof(str));
+
 		s = subscription_find(sm, uuid);
 		if (s) {
 			struct subscr_addr *sa;
 			sa = dl_list_first(&s->addr_list, struct subscr_addr,
 					   list);
-			wpa_printf(MSG_DEBUG, "WPS UPnP: Unsubscribing %p %s",
-				   s, (sa && sa->domain_and_port) ?
+			wpa_printf(MSG_DEBUG,
+				   "WPS UPnP: Unsubscribing %p (SID %s) %s",
+				   s, str, (sa && sa->domain_and_port) ?
 				   sa->domain_and_port : "-null-");
 			dl_list_del(&s->list);
 			subscription_destroy(s);
 		} else {
-			wpa_printf(MSG_INFO, "WPS UPnP: Could not find matching subscription to unsubscribe");
+			wpa_printf(MSG_INFO,
+				   "WPS UPnP: Could not find matching subscription to unsubscribe (SID %s)",
+				   str);
 			ret = HTTP_PRECONDITION_FAILED;
 			goto send_msg;
 		}
diff --git a/wpa_supplicant/Android.mk b/wpa_supplicant/Android.mk
index e3020ec..e748785 100644
--- a/wpa_supplicant/Android.mk
+++ b/wpa_supplicant/Android.mk
@@ -39,7 +39,7 @@
 
 # Use Android specific directory for control interface sockets
 L_CFLAGS += -DCONFIG_CTRL_IFACE_CLIENT_DIR=\"/data/misc/wifi/sockets\"
-L_CFLAGS += -DCONFIG_CTRL_IFACE_DIR=\"/data/system/wpa_supplicant\"
+L_CFLAGS += -DCONFIG_CTRL_IFACE_DIR=\"/data/misc/wifi/sockets\"
 
 # Use Android specific directory for wpa_cli command completion history
 L_CFLAGS += -DCONFIG_WPA_CLI_HISTORY_DIR=\"/data/misc/wifi\"
@@ -315,6 +315,22 @@
 NEED_GAS=y
 endif
 
+ifdef CONFIG_FST
+L_CFLAGS += -DCONFIG_FST
+OBJS += src/fst/fst.c
+OBJS += src/fst/fst_session.c
+OBJS += src/fst/fst_iface.c
+OBJS += src/fst/fst_group.c
+OBJS += src/fst/fst_ctrl_aux.c
+ifdef CONFIG_FST_TEST
+L_CFLAGS += -DCONFIG_FST_TEST
+endif
+ifdef CONFIG_CTRL_IFACE
+OBJS += src/fst/fst_ctrl_iface.c
+endif
+endif
+
+
 include $(LOCAL_PATH)/src/drivers/drivers.mk
 
 ifdef CONFIG_AP
@@ -369,7 +385,6 @@
 else
 L_CFLAGS += -DEAP_TLS
 OBJS += src/eap_peer/eap_tls.c
-OBJS_h += src/eap_server/eap_server_tls.c
 endif
 TLS_FUNCS=y
 CONFIG_IEEE8021X_EAPOL=y
@@ -380,7 +395,6 @@
 L_CFLAGS += -DEAP_UNAUTH_TLS
 ifndef CONFIG_EAP_TLS
 OBJS += src/eap_peer/eap_tls.c
-OBJS_h += src/eap_server/eap_server_tls.c
 TLS_FUNCS=y
 endif
 CONFIG_IEEE8021X_EAPOL=y
@@ -395,7 +409,6 @@
 L_CFLAGS += -DEAP_PEAP
 OBJS += src/eap_peer/eap_peap.c
 OBJS += src/eap_common/eap_peap_common.c
-OBJS_h += src/eap_server/eap_server_peap.c
 endif
 TLS_FUNCS=y
 CONFIG_IEEE8021X_EAPOL=y
@@ -409,11 +422,12 @@
 else
 L_CFLAGS += -DEAP_TTLS
 OBJS += src/eap_peer/eap_ttls.c
-OBJS_h += src/eap_server/eap_server_ttls.c
 endif
-MS_FUNCS=y
 TLS_FUNCS=y
+ifndef CONFIG_FIPS
+MS_FUNCS=y
 CHAP=y
+endif
 CONFIG_IEEE8021X_EAPOL=y
 endif
 
@@ -425,7 +439,6 @@
 else
 L_CFLAGS += -DEAP_MD5
 OBJS += src/eap_peer/eap_md5.c
-OBJS_h += src/eap_server/eap_server_md5.c
 endif
 CHAP=y
 CONFIG_IEEE8021X_EAPOL=y
@@ -448,7 +461,6 @@
 L_CFLAGS += -DEAP_MSCHAPv2
 OBJS += src/eap_peer/eap_mschapv2.c
 OBJS += src/eap_peer/mschapv2.c
-OBJS_h += src/eap_server/eap_server_mschapv2.c
 endif
 MS_FUNCS=y
 CONFIG_IEEE8021X_EAPOL=y
@@ -462,7 +474,6 @@
 else
 L_CFLAGS += -DEAP_GTC
 OBJS += src/eap_peer/eap_gtc.c
-OBJS_h += src/eap_server/eap_server_gtc.c
 endif
 CONFIG_IEEE8021X_EAPOL=y
 endif
@@ -487,7 +498,6 @@
 else
 L_CFLAGS += -DEAP_SIM
 OBJS += src/eap_peer/eap_sim.c
-OBJS_h += src/eap_server/eap_server_sim.c
 endif
 CONFIG_IEEE8021X_EAPOL=y
 CONFIG_EAP_SIM_COMMON=y
@@ -515,7 +525,6 @@
 else
 L_CFLAGS += -DEAP_PSK
 OBJS += src/eap_peer/eap_psk.c src/eap_common/eap_psk_common.c
-OBJS_h += src/eap_server/eap_server_psk.c
 endif
 CONFIG_IEEE8021X_EAPOL=y
 NEED_AES=y
@@ -532,7 +541,6 @@
 else
 L_CFLAGS += -DEAP_AKA
 OBJS += src/eap_peer/eap_aka.c
-OBJS_h += src/eap_server/eap_server_aka.c
 endif
 CONFIG_IEEE8021X_EAPOL=y
 CONFIG_EAP_SIM_COMMON=y
@@ -558,7 +566,6 @@
 
 ifdef CONFIG_EAP_SIM_COMMON
 OBJS += src/eap_common/eap_sim_common.c
-OBJS_h += src/eap_server/eap_sim_db.c
 NEED_AES=y
 NEED_FIPS186_2_PRF=y
 endif
@@ -573,7 +580,6 @@
 L_CFLAGS += -DEAP_FAST
 OBJS += src/eap_peer/eap_fast.c src/eap_peer/eap_fast_pac.c
 OBJS += src/eap_common/eap_fast_common.c
-OBJS_h += src/eap_server/eap_server_fast.c
 endif
 TLS_FUNCS=y
 CONFIG_IEEE8021X_EAPOL=y
@@ -588,7 +594,6 @@
 else
 L_CFLAGS += -DEAP_PAX
 OBJS += src/eap_peer/eap_pax.c src/eap_common/eap_pax_common.c
-OBJS_h += src/eap_server/eap_server_pax.c
 endif
 CONFIG_IEEE8021X_EAPOL=y
 endif
@@ -601,7 +606,6 @@
 else
 L_CFLAGS += -DEAP_SAKE
 OBJS += src/eap_peer/eap_sake.c src/eap_common/eap_sake_common.c
-OBJS_h += src/eap_server/eap_server_sake.c
 endif
 CONFIG_IEEE8021X_EAPOL=y
 endif
@@ -614,7 +618,6 @@
 else
 L_CFLAGS += -DEAP_GPSK
 OBJS += src/eap_peer/eap_gpsk.c src/eap_common/eap_gpsk_common.c
-OBJS_h += src/eap_server/eap_server_gpsk.c
 endif
 CONFIG_IEEE8021X_EAPOL=y
 ifdef CONFIG_EAP_GPSK_SHA256
@@ -627,7 +630,6 @@
 ifdef CONFIG_EAP_PWD
 L_CFLAGS += -DEAP_PWD
 OBJS += src/eap_peer/eap_pwd.c src/eap_common/eap_pwd_common.c
-OBJS_h += src/eap_server/eap_server_pwd.c
 CONFIG_IEEE8021X_EAPOL=y
 NEED_SHA256=y
 endif
@@ -640,12 +642,12 @@
 else
 L_CFLAGS += -DEAP_EKE
 OBJS += src/eap_peer/eap_eke.c src/eap_common/eap_eke_common.c
-OBJS_h += src/eap_server/eap_server_eke.c
 endif
 CONFIG_IEEE8021X_EAPOL=y
 NEED_DH_GROUPS=y
 NEED_DH_GROUPS_ALL=y
 NEED_SHA256=y
+NEED_AES_CBC=y
 endif
 
 ifdef CONFIG_WPS
@@ -662,7 +664,6 @@
 OBJS += src/wps/wps_dev_attr.c
 OBJS += src/wps/wps_enrollee.c
 OBJS += src/wps/wps_registrar.c
-OBJS_h += src/eap_server/eap_server_wsc.c
 CONFIG_IEEE8021X_EAPOL=y
 NEED_DH_GROUPS=y
 NEED_SHA256=y
@@ -725,8 +726,6 @@
 L_CFLAGS += -DEAP_IKEV2
 OBJS += src/eap_peer/eap_ikev2.c src/eap_peer/ikev2.c
 OBJS += src/eap_common/eap_ikev2_common.c src/eap_common/ikev2_common.c
-OBJS_h += src/eap_server/eap_server_ikev2.c
-OBJS_h += src/eap_server/ikev2.c
 endif
 CONFIG_IEEE8021X_EAPOL=y
 NEED_DH_GROUPS=y
@@ -742,7 +741,6 @@
 else
 L_CFLAGS += -DEAP_VENDOR_TEST
 OBJS += src/eap_peer/eap_vendor_test.c
-OBJS_h += src/eap_server/eap_server_vendor_test.c
 endif
 CONFIG_IEEE8021X_EAPOL=y
 endif
@@ -752,8 +750,6 @@
 L_CFLAGS += -DEAP_TNC
 OBJS += src/eap_peer/eap_tnc.c
 OBJS += src/eap_peer/tncc.c
-OBJS_h += src/eap_server/eap_server_tnc.c
-OBJS_h += src/eap_server/tncs.c
 NEED_BASE64=y
 ifndef CONFIG_NATIVE_WINDOWS
 ifndef CONFIG_DRIVER_BSD
@@ -860,36 +856,6 @@
 endif
 endif
 
-ifdef CONFIG_EAP_SERVER
-L_CFLAGS += -DEAP_SERVER
-OBJS_h += src/eap_server/eap_server.c
-OBJS_h += src/eap_server/eap_server_identity.c
-OBJS_h += src/eap_server/eap_server_methods.c
-endif
-
-ifdef CONFIG_RADIUS_CLIENT
-OBJS_h += src/utils/ip_addr.c
-OBJS_h += src/radius/radius.c
-OBJS_h += src/radius/radius_client.c
-endif
-
-ifdef CONFIG_AUTHENTICATOR
-OBJS_h += src/eapol_auth/eapol_auth_sm.c
-OBJS_h += src/ap/ieee802_1x.c
-endif
-
-ifdef CONFIG_WPA_AUTHENTICATOR
-OBJS_h += src/ap/wpa_auth.c
-OBJS_h += src/ap/wpa_auth_ie.c
-OBJS_h += src/ap/pmksa_cache_auth.c
-ifdef CONFIG_IEEE80211R
-OBJS_h += src/ap/wpa_auth_ft.c
-endif
-ifdef CONFIG_PEERKEY
-OBJS_h += src/ap/peerkey_auth.c
-endif
-endif
-
 ifdef CONFIG_PCSC
 # PC/SC interface for smartcards (USIM, GSM SIM)
 L_CFLAGS += -DPCSC_FUNCS -I/usr/include/PCSC
@@ -941,7 +907,6 @@
 NEED_DES=y
 # Shared TLS functions (needed for EAP_TLS, EAP_PEAP, EAP_TTLS, and EAP_FAST)
 OBJS += src/eap_peer/eap_tls_common.c
-OBJS_h += src/eap_server/eap_server_tls_common.c
 ifndef CONFIG_FIPS
 NEED_TLS_PRF=y
 NEED_SHA1=y
@@ -966,6 +931,7 @@
 ifdef TLS_FUNCS
 L_CFLAGS += -DEAP_TLS_OPENSSL
 OBJS += src/crypto/tls_openssl.c
+OBJS += src/crypto/tls_openssl_ocsp.c
 LIBS += -lssl
 endif
 OBJS += src/crypto/crypto_openssl.c
@@ -973,6 +939,8 @@
 ifdef NEED_FIPS186_2_PRF
 OBJS += src/crypto/fips_prf_openssl.c
 endif
+NEED_SHA256=y
+NEED_TLS_PRF_SHA256=y
 LIBS += -lcrypto
 LIBS_p += -lcrypto
 ifdef CONFIG_TLS_ADD_DL
@@ -1065,6 +1033,8 @@
 CONFIG_INTERNAL_MD4=y
 CONFIG_INTERNAL_MD5=y
 CONFIG_INTERNAL_SHA256=y
+CONFIG_INTERNAL_SHA384=y
+CONFIG_INTERNAL_SHA512=y
 CONFIG_INTERNAL_RC4=y
 CONFIG_INTERNAL_DH_GROUP5=y
 endif
@@ -1117,6 +1087,20 @@
 endif
 
 ifneq ($(CONFIG_TLS), openssl)
+NEED_INTERNAL_AES_WRAP=y
+endif
+ifdef CONFIG_OPENSSL_INTERNAL_AES_WRAP
+# Seems to be needed at least with BoringSSL
+NEED_INTERNAL_AES_WRAP=y
+L_CFLAGS += -DCONFIG_OPENSSL_INTERNAL_AES_WRAP
+endif
+ifdef CONFIG_FIPS
+# Have to use internal AES key wrap routines to use OpenSSL EVP since the
+# OpenSSL AES_wrap_key()/AES_unwrap_key() API is not available in FIPS mode.
+NEED_INTERNAL_AES_WRAP=y
+endif
+
+ifdef NEED_INTERNAL_AES_WRAP
 AESOBJS += src/crypto/aes-unwrap.c
 endif
 ifdef NEED_AES_EAX
@@ -1139,7 +1123,7 @@
 endif
 ifdef NEED_AES_WRAP
 NEED_AES_ENC=y
-ifneq ($(CONFIG_TLS), openssl)
+ifdef NEED_INTERNAL_AES_WRAP
 AESOBJS += src/crypto/aes-wrap.c
 endif
 endif
@@ -1215,11 +1199,17 @@
 endif
 endif
 
+ifdef CONFIG_NO_RC4
+L_CFLAGS += -DCONFIG_NO_RC4
+endif
+
 ifdef NEED_RC4
 ifdef CONFIG_INTERNAL_RC4
+ifndef CONFIG_NO_RC4
 OBJS += src/crypto/rc4.c
 endif
 endif
+endif
 
 SHA256OBJS = # none by default
 ifdef NEED_SHA256
@@ -1231,16 +1221,26 @@
 ifdef CONFIG_INTERNAL_SHA256
 SHA256OBJS += src/crypto/sha256-internal.c
 endif
+ifdef CONFIG_INTERNAL_SHA384
+L_CFLAGS += -DCONFIG_INTERNAL_SHA384
+SHA256OBJS += src/crypto/sha384-internal.c
+endif
+ifdef CONFIG_INTERNAL_SHA512
+L_CFLAGS += -DCONFIG_INTERNAL_SHA512
+SHA256OBJS += src/crypto/sha512-internal.c
+endif
 ifdef NEED_TLS_PRF_SHA256
 SHA256OBJS += src/crypto/sha256-tlsprf.c
 endif
 ifdef NEED_HMAC_SHA256_KDF
+L_CFLAGS += -DCONFIG_HMAC_SHA256_KDF
 SHA256OBJS += src/crypto/sha256-kdf.c
 endif
 OBJS += $(SHA256OBJS)
 endif
 ifdef NEED_SHA384
 L_CFLAGS += -DCONFIG_SHA384
+OBJS += src/crypto/sha384-prf.c
 endif
 
 ifdef NEED_DH_GROUPS
@@ -1473,12 +1473,6 @@
 
 OBJS += src/drivers/driver_common.c
 
-OBJS_wpa_rm := ctrl_iface.c ctrl_iface_unix.c
-OBJS_wpa := $(filter-out $(OBJS_wpa_rm),$(OBJS)) $(OBJS_h) tests/test_wpa.c
-ifdef CONFIG_AUTHENTICATOR
-OBJS_wpa += tests/link_test.c
-endif
-OBJS_wpa += $(OBJS_l2)
 OBJS += wpa_supplicant.c events.c blacklist.c wpas_glue.c scan.c
 OBJS_t := $(OBJS) $(OBJS_l2) eapol_test.c
 OBJS_t += src/radius/radius_client.c
diff --git a/wpa_supplicant/ChangeLog b/wpa_supplicant/ChangeLog
index 1ac79b4..facd90e 100644
--- a/wpa_supplicant/ChangeLog
+++ b/wpa_supplicant/ChangeLog
@@ -1,5 +1,68 @@
 ChangeLog for wpa_supplicant
 
+2015-09-27 - v2.5
+	* fixed P2P validation of SSID element length before copying it
+	  [http://w1.fi/security/2015-1/] (CVE-2015-1863)
+	* fixed WPS UPnP vulnerability with HTTP chunked transfer encoding
+	  [http://w1.fi/security/2015-2/] (CVE-2015-4141)
+	* fixed WMM Action frame parser (AP mode)
+	  [http://w1.fi/security/2015-3/] (CVE-2015-4142)
+	* fixed EAP-pwd peer missing payload length validation
+	  [http://w1.fi/security/2015-4/]
+	  (CVE-2015-4143, CVE-2015-4144, CVE-2015-4145, CVE-2015-4146)
+	* fixed validation of WPS and P2P NFC NDEF record payload length
+	  [http://w1.fi/security/2015-5/]
+	* nl80211:
+	  - added VHT configuration for IBSS
+	  - fixed vendor command handling to check OUI properly
+	  - allow driver-based roaming to change ESS
+	* added AVG_BEACON_RSSI to SIGNAL_POLL output
+	* wpa_cli: added tab completion for number of commands
+	* removed unmaintained and not yet completed SChannel/CryptoAPI support
+	* modified Extended Capabilities element use in Probe Request frames to
+	  include all cases if any of the values are non-zero
+	* added support for dynamically creating/removing a virtual interface
+	  with interface_add/interface_remove
+	* added support for hashed password (NtHash) in EAP-pwd peer
+	* added support for memory-only PSK/passphrase (mem_only_psk=1 and
+	  CTRL-REQ/RSP-PSK_PASSPHRASE)
+	* P2P
+	  - optimize scan frequencies list when re-joining a persistent group
+	  - fixed number of sequences with nl80211 P2P Device interface
+	  - added operating class 125 for P2P use cases (this allows 5 GHz
+	    channels 161 and 169 to be used if they are enabled in the current
+	    regulatory domain)
+	  - number of fixes to P2PS functionality
+	  - do not allow 40 MHz co-ex PRI/SEC switch to force MCC
+	  - extended support for preferred channel listing
+	* D-Bus:
+	  - fixed WPS property of fi.w1.wpa_supplicant1.BSS interface
+	  - fixed PresenceRequest to use group interface
+	  - added new signals: FindStopped, WPS pbc-overlap,
+	    GroupFormationFailure, WPS timeout, InvitationReceived
+	  - added new methods: WPS Cancel, P2P Cancel, Reconnect, RemoveClient
+	  - added manufacturer info
+	* added EAP-EKE peer support for deriving Session-Id
+	* added wps_priority configuration parameter to set the default priority
+	  for all network profiles added by WPS
+	* added support to request a scan with specific SSIDs with the SCAN
+	  command (optional "ssid <hexdump>" arguments)
+	* removed support for WEP40/WEP104 as a group cipher with WPA/WPA2
+	* fixed SAE group selection in an error case
+	* modified SAE routines to be more robust and PWE generation to be
+	  stronger against timing attacks
+	* added support for Brainpool Elliptic Curves with SAE
+	* added support for CCMP-256 and GCMP-256 as group ciphers with FT
+	* fixed BSS selection based on estimated throughput
+	* added option to disable TLSv1.0 with OpenSSL
+	  (phase1="tls_disable_tlsv1_0=1")
+	* added Fast Session Transfer (FST) module
+	* fixed OpenSSL PKCS#12 extra certificate handling
+	* fixed key derivation for Suite B 192-bit AKM (this breaks
+	  compatibility with the earlier version)
+	* added RSN IE to Mesh Peering Open/Confirm frames
+	* number of small fixes
+
 2015-03-15 - v2.4
 	* allow OpenSSL cipher configuration to be set for internal EAP server
 	  (openssl_ciphers parameter)
diff --git a/wpa_supplicant/Makefile b/wpa_supplicant/Makefile
index 05d8e0a..e3d3acf 100644
--- a/wpa_supplicant/Makefile
+++ b/wpa_supplicant/Makefile
@@ -17,6 +17,16 @@
 
 -include .config
 
+ifndef CONFIG_NO_GITVER
+# Add VERSION_STR postfix for builds from a git repository
+ifeq ($(wildcard ../.git),../.git)
+GITVER := $(shell git describe --dirty=+)
+ifneq ($(GITVER),)
+CFLAGS += -DGIT_VERSION_STR_POSTFIX=\"-$(GITVER)\"
+endif
+endif
+endif
+
 ifdef CONFIG_TESTING_OPTIONS
 CFLAGS += -DCONFIG_TESTING_OPTIONS
 CONFIG_WPS_TESTING=y
@@ -131,12 +141,15 @@
 OBJS += ../src/utils/$(CONFIG_ELOOP).o
 OBJS_c += ../src/utils/$(CONFIG_ELOOP).o
 
+ifndef CONFIG_OSX
 ifeq ($(CONFIG_ELOOP), eloop)
 # Using glibc < 2.17 requires -lrt for clock_gettime()
+# OS X has an alternate implementation
 LIBS += -lrt
 LIBS_c += -lrt
 LIBS_p += -lrt
 endif
+endif
 
 ifdef CONFIG_ELOOP_POLL
 CFLAGS += -DCONFIG_ELOOP_POLL
@@ -275,6 +288,10 @@
 NEED_RC4=y
 else
 CFLAGS += -DCONFIG_NO_WPA
+ifeq ($(CONFIG_TLS), internal)
+NEED_SHA1=y
+NEED_MD5=y
+endif
 endif
 
 ifdef CONFIG_IBSS_RSN
@@ -383,7 +400,6 @@
 else
 CFLAGS += -DEAP_TLS
 OBJS += ../src/eap_peer/eap_tls.o
-OBJS_h += ../src/eap_server/eap_server_tls.o
 endif
 TLS_FUNCS=y
 CONFIG_IEEE8021X_EAPOL=y
@@ -394,7 +410,6 @@
 CFLAGS += -DEAP_UNAUTH_TLS
 ifndef CONFIG_EAP_TLS
 OBJS += ../src/eap_peer/eap_tls.o
-OBJS_h += ../src/eap_server/eap_server_tls.o
 TLS_FUNCS=y
 endif
 CONFIG_IEEE8021X_EAPOL=y
@@ -409,7 +424,6 @@
 CFLAGS += -DEAP_PEAP
 OBJS += ../src/eap_peer/eap_peap.o
 OBJS += ../src/eap_common/eap_peap_common.o
-OBJS_h += ../src/eap_server/eap_server_peap.o
 endif
 TLS_FUNCS=y
 CONFIG_IEEE8021X_EAPOL=y
@@ -423,11 +437,12 @@
 else
 CFLAGS += -DEAP_TTLS
 OBJS += ../src/eap_peer/eap_ttls.o
-OBJS_h += ../src/eap_server/eap_server_ttls.o
 endif
-MS_FUNCS=y
 TLS_FUNCS=y
+ifndef CONFIG_FIPS
+MS_FUNCS=y
 CHAP=y
+endif
 CONFIG_IEEE8021X_EAPOL=y
 endif
 
@@ -439,7 +454,6 @@
 else
 CFLAGS += -DEAP_MD5
 OBJS += ../src/eap_peer/eap_md5.o
-OBJS_h += ../src/eap_server/eap_server_md5.o
 endif
 CHAP=y
 CONFIG_IEEE8021X_EAPOL=y
@@ -462,7 +476,6 @@
 CFLAGS += -DEAP_MSCHAPv2
 OBJS += ../src/eap_peer/eap_mschapv2.o
 OBJS += ../src/eap_peer/mschapv2.o
-OBJS_h += ../src/eap_server/eap_server_mschapv2.o
 endif
 MS_FUNCS=y
 CONFIG_IEEE8021X_EAPOL=y
@@ -476,7 +489,6 @@
 else
 CFLAGS += -DEAP_GTC
 OBJS += ../src/eap_peer/eap_gtc.o
-OBJS_h += ../src/eap_server/eap_server_gtc.o
 endif
 CONFIG_IEEE8021X_EAPOL=y
 endif
@@ -501,7 +513,6 @@
 else
 CFLAGS += -DEAP_SIM
 OBJS += ../src/eap_peer/eap_sim.o
-OBJS_h += ../src/eap_server/eap_server_sim.o
 endif
 CONFIG_IEEE8021X_EAPOL=y
 CONFIG_EAP_SIM_COMMON=y
@@ -529,7 +540,6 @@
 else
 CFLAGS += -DEAP_PSK
 OBJS += ../src/eap_peer/eap_psk.o ../src/eap_common/eap_psk_common.o
-OBJS_h += ../src/eap_server/eap_server_psk.o
 endif
 CONFIG_IEEE8021X_EAPOL=y
 NEED_AES=y
@@ -546,7 +556,6 @@
 else
 CFLAGS += -DEAP_AKA
 OBJS += ../src/eap_peer/eap_aka.o
-OBJS_h += ../src/eap_server/eap_server_aka.o
 endif
 CONFIG_IEEE8021X_EAPOL=y
 CONFIG_EAP_SIM_COMMON=y
@@ -572,7 +581,6 @@
 
 ifdef CONFIG_EAP_SIM_COMMON
 OBJS += ../src/eap_common/eap_sim_common.o
-OBJS_h += ../src/eap_server/eap_sim_db.o
 NEED_AES=y
 NEED_FIPS186_2_PRF=y
 endif
@@ -587,7 +595,6 @@
 CFLAGS += -DEAP_FAST
 OBJS += ../src/eap_peer/eap_fast.o ../src/eap_peer/eap_fast_pac.o
 OBJS += ../src/eap_common/eap_fast_common.o
-OBJS_h += ../src/eap_server/eap_server_fast.o
 endif
 TLS_FUNCS=y
 CONFIG_IEEE8021X_EAPOL=y
@@ -602,7 +609,6 @@
 else
 CFLAGS += -DEAP_PAX
 OBJS += ../src/eap_peer/eap_pax.o ../src/eap_common/eap_pax_common.o
-OBJS_h += ../src/eap_server/eap_server_pax.o
 endif
 CONFIG_IEEE8021X_EAPOL=y
 endif
@@ -615,7 +621,6 @@
 else
 CFLAGS += -DEAP_SAKE
 OBJS += ../src/eap_peer/eap_sake.o ../src/eap_common/eap_sake_common.o
-OBJS_h += ../src/eap_server/eap_server_sake.o
 endif
 CONFIG_IEEE8021X_EAPOL=y
 endif
@@ -628,7 +633,6 @@
 else
 CFLAGS += -DEAP_GPSK
 OBJS += ../src/eap_peer/eap_gpsk.o ../src/eap_common/eap_gpsk_common.o
-OBJS_h += ../src/eap_server/eap_server_gpsk.o
 endif
 CONFIG_IEEE8021X_EAPOL=y
 ifdef CONFIG_EAP_GPSK_SHA256
@@ -641,7 +645,6 @@
 ifdef CONFIG_EAP_PWD
 CFLAGS += -DEAP_PWD
 OBJS += ../src/eap_peer/eap_pwd.o ../src/eap_common/eap_pwd_common.o
-OBJS_h += ../src/eap_server/eap_server_pwd.o
 CONFIG_IEEE8021X_EAPOL=y
 NEED_SHA256=y
 endif
@@ -654,12 +657,12 @@
 else
 CFLAGS += -DEAP_EKE
 OBJS += ../src/eap_peer/eap_eke.o ../src/eap_common/eap_eke_common.o
-OBJS_h += ../src/eap_server/eap_server_eke.o
 endif
 CONFIG_IEEE8021X_EAPOL=y
 NEED_DH_GROUPS=y
 NEED_DH_GROUPS_ALL=y
 NEED_SHA256=y
+NEED_AES_CBC=y
 endif
 
 ifdef CONFIG_WPS
@@ -676,7 +679,6 @@
 OBJS += ../src/wps/wps_dev_attr.o
 OBJS += ../src/wps/wps_enrollee.o
 OBJS += ../src/wps/wps_registrar.o
-OBJS_h += ../src/eap_server/eap_server_wsc.o
 CONFIG_IEEE8021X_EAPOL=y
 NEED_DH_GROUPS=y
 NEED_SHA256=y
@@ -739,8 +741,6 @@
 CFLAGS += -DEAP_IKEV2
 OBJS += ../src/eap_peer/eap_ikev2.o ../src/eap_peer/ikev2.o
 OBJS += ../src/eap_common/eap_ikev2_common.o ../src/eap_common/ikev2_common.o
-OBJS_h += ../src/eap_server/eap_server_ikev2.o
-OBJS_h += ../src/eap_server/ikev2.o
 endif
 CONFIG_IEEE8021X_EAPOL=y
 NEED_DH_GROUPS=y
@@ -756,7 +756,6 @@
 else
 CFLAGS += -DEAP_VENDOR_TEST
 OBJS += ../src/eap_peer/eap_vendor_test.o
-OBJS_h += ../src/eap_server/eap_server_vendor_test.o
 endif
 CONFIG_IEEE8021X_EAPOL=y
 endif
@@ -766,8 +765,6 @@
 CFLAGS += -DEAP_TNC
 OBJS += ../src/eap_peer/eap_tnc.o
 OBJS += ../src/eap_peer/tncc.o
-OBJS_h += ../src/eap_server/eap_server_tnc.o
-OBJS_h += ../src/eap_server/tncs.o
 NEED_BASE64=y
 ifndef CONFIG_NATIVE_WINDOWS
 ifndef CONFIG_DRIVER_BSD
@@ -887,36 +884,6 @@
 endif
 endif
 
-ifdef CONFIG_EAP_SERVER
-CFLAGS += -DEAP_SERVER
-OBJS_h += ../src/eap_server/eap_server.o
-OBJS_h += ../src/eap_server/eap_server_identity.o
-OBJS_h += ../src/eap_server/eap_server_methods.o
-endif
-
-ifdef CONFIG_RADIUS_CLIENT
-OBJS_h += ../src/utils/ip_addr.o
-OBJS_h += ../src/radius/radius.o
-OBJS_h += ../src/radius/radius_client.o
-endif
-
-ifdef CONFIG_AUTHENTICATOR
-OBJS_h += ../src/eapol_auth/eapol_auth_sm.o
-OBJS_h += ../src/ap/ieee802_1x.o
-endif
-
-ifdef CONFIG_WPA_AUTHENTICATOR
-OBJS_h += ../src/ap/wpa_auth.o
-OBJS_h += ../src/ap/wpa_auth_ie.o
-OBJS_h += ../src/ap/pmksa_cache_auth.o
-ifdef CONFIG_IEEE80211R
-OBJS_h += ../src/ap/wpa_auth_ft.o
-endif
-ifdef CONFIG_PEERKEY
-OBJS_h += ../src/ap/peerkey_auth.o
-endif
-endif
-
 ifdef CONFIG_PCSC
 # PC/SC interface for smartcards (USIM, GSM SIM)
 CFLAGS += -DPCSC_FUNCS -I/usr/include/PCSC
@@ -968,7 +935,6 @@
 NEED_DES=y
 # Shared TLS functions (needed for EAP_TLS, EAP_PEAP, EAP_TTLS, and EAP_FAST)
 OBJS += ../src/eap_peer/eap_tls_common.o
-OBJS_h += ../src/eap_server/eap_server_tls_common.o
 ifndef CONFIG_FIPS
 NEED_TLS_PRF=y
 NEED_SHA1=y
@@ -993,6 +959,7 @@
 ifdef TLS_FUNCS
 CFLAGS += -DEAP_TLS_OPENSSL
 OBJS += ../src/crypto/tls_openssl.o
+OBJS += ../src/crypto/tls_openssl_ocsp.o
 LIBS += -lssl
 endif
 OBJS += ../src/crypto/crypto_openssl.o
@@ -1001,6 +968,8 @@
 ifdef NEED_FIPS186_2_PRF
 OBJS += ../src/crypto/fips_prf_openssl.o
 endif
+NEED_SHA256=y
+NEED_TLS_PRF_SHA256=y
 LIBS += -lcrypto
 LIBS_p += -lcrypto
 ifdef CONFIG_TLS_ADD_DL
@@ -1094,6 +1063,8 @@
 CONFIG_INTERNAL_MD4=y
 CONFIG_INTERNAL_MD5=y
 CONFIG_INTERNAL_SHA256=y
+CONFIG_INTERNAL_SHA384=y
+CONFIG_INTERNAL_SHA512=y
 CONFIG_INTERNAL_RC4=y
 CONFIG_INTERNAL_DH_GROUP5=y
 endif
@@ -1146,6 +1117,20 @@
 endif
 
 ifneq ($(CONFIG_TLS), openssl)
+NEED_INTERNAL_AES_WRAP=y
+endif
+ifdef CONFIG_OPENSSL_INTERNAL_AES_WRAP
+# Seems to be needed at least with BoringSSL
+NEED_INTERNAL_AES_WRAP=y
+CFLAGS += -DCONFIG_OPENSSL_INTERNAL_AES_WRAP
+endif
+ifdef CONFIG_FIPS
+# Have to use internal AES key wrap routines to use OpenSSL EVP since the
+# OpenSSL AES_wrap_key()/AES_unwrap_key() API is not available in FIPS mode.
+NEED_INTERNAL_AES_WRAP=y
+endif
+
+ifdef NEED_INTERNAL_AES_WRAP
 AESOBJS += ../src/crypto/aes-unwrap.o
 endif
 ifdef NEED_AES_EAX
@@ -1171,7 +1156,7 @@
 endif
 ifdef NEED_AES_WRAP
 NEED_AES_ENC=y
-ifneq ($(CONFIG_TLS), openssl)
+ifdef NEED_INTERNAL_AES_WRAP
 AESOBJS += ../src/crypto/aes-wrap.o
 endif
 endif
@@ -1243,11 +1228,17 @@
 endif
 endif
 
+ifdef CONFIG_NO_RC4
+CFLAGS += -DCONFIG_NO_RC4
+endif
+
 ifdef NEED_RC4
 ifdef CONFIG_INTERNAL_RC4
+ifndef CONFIG_NO_RC4
 OBJS += ../src/crypto/rc4.o
 endif
 endif
+endif
 
 SHA256OBJS = # none by default
 ifdef NEED_SHA256
@@ -1259,16 +1250,26 @@
 ifdef CONFIG_INTERNAL_SHA256
 SHA256OBJS += ../src/crypto/sha256-internal.o
 endif
+ifdef CONFIG_INTERNAL_SHA384
+CFLAGS += -DCONFIG_INTERNAL_SHA384
+SHA256OBJS += ../src/crypto/sha384-internal.o
+endif
+ifdef CONFIG_INTERNAL_SHA512
+CFLAGS += -DCONFIG_INTERNAL_SHA512
+SHA256OBJS += ../src/crypto/sha512-internal.o
+endif
 ifdef NEED_TLS_PRF_SHA256
 SHA256OBJS += ../src/crypto/sha256-tlsprf.o
 endif
 ifdef NEED_HMAC_SHA256_KDF
+CFLAGS += -DCONFIG_HMAC_SHA256_KDF
 OBJS += ../src/crypto/sha256-kdf.o
 endif
 OBJS += $(SHA256OBJS)
 endif
 ifdef NEED_SHA384
 CFLAGS += -DCONFIG_SHA384
+OBJS += ../src/crypto/sha384-prf.o
 endif
 
 ifdef NEED_DH_GROUPS
@@ -1383,7 +1384,7 @@
 
 ifdef CONFIG_READLINE
 OBJS_c += ../src/utils/edit_readline.o
-LIBS_c += -lncurses -lreadline
+LIBS_c += -lreadline -lncurses
 else
 ifdef CONFIG_WPA_CLI_EDIT
 OBJS_c += ../src/utils/edit.o
@@ -1414,6 +1415,10 @@
 CFLAGS += -DCONFIG_IPV6
 endif
 
+ifdef CONFIG_NO_LINUX_PACKET_SOCKET_WAR
+CFLAGS += -DCONFIG_NO_LINUX_PACKET_SOCKET_WAR
+endif
+
 ifdef NEED_BASE64
 OBJS += ../src/utils/base64.o
 endif
@@ -1540,12 +1545,6 @@
 OBJS += ../src/drivers/driver_common.o
 OBJS_priv += ../src/drivers/driver_common.o
 
-OBJS_wpa_rm := ctrl_iface.o ctrl_iface_unix.o
-OBJS_wpa := $(filter-out $(OBJS_wpa_rm),$(OBJS)) $(OBJS_h) tests/test_wpa.o
-ifdef CONFIG_AUTHENTICATOR
-OBJS_wpa += tests/link_test.o
-endif
-OBJS_wpa += $(OBJS_l2)
 OBJS += wpa_supplicant.o events.o blacklist.o wpas_glue.o scan.o
 OBJS_t := $(OBJS) $(OBJS_l2) eapol_test.o
 OBJS_t += ../src/radius/radius_client.o
@@ -1591,6 +1590,24 @@
 endif
 endif
 
+ifdef CONFIG_FST
+CFLAGS += -DCONFIG_FST
+ifdef CONFIG_FST_TEST
+CFLAGS += -DCONFIG_FST_TEST
+endif
+FST_OBJS += ../src/fst/fst.o
+FST_OBJS += ../src/fst/fst_session.o
+FST_OBJS += ../src/fst/fst_iface.o
+FST_OBJS += ../src/fst/fst_group.o
+FST_OBJS += ../src/fst/fst_ctrl_aux.o
+ifdef CONFIG_CTRL_IFACE
+FST_OBJS += ../src/fst/fst_ctrl_iface.o
+endif
+OBJS += $(FST_OBJS)
+OBJS_t += $(FST_OBJS)
+OBJS_t2 += $(FST_OBJS)
+endif
+
 ifndef LDO
 LDO=$(CC)
 endif
@@ -1644,9 +1661,11 @@
 
 LIBCTRL += ../src/common/wpa_ctrl.o
 LIBCTRL += ../src/utils/os_$(CONFIG_OS).o
+LIBCTRL += ../src/utils/common.o
 LIBCTRL += ../src/utils/wpa_debug.o
 LIBCTRLSO += ../src/common/wpa_ctrl.c
 LIBCTRLSO += ../src/utils/os_$(CONFIG_OS).c
+LIBCTRLSO += ../src/utils/common.c
 LIBCTRLSO += ../src/utils/wpa_debug.c
 
 libwpa_client.a: $(LIBCTRL)
@@ -1658,12 +1677,12 @@
 	@$(E) "  CC  $@ ($^)"
 	$(Q)$(CC) $(LDFLAGS) -o $@ $(CFLAGS) -shared -fPIC $^
 
-link_test: $(OBJS) $(OBJS_h) tests/link_test.o
-	$(Q)$(LDO) $(LDFLAGS) -o link_test $(OBJS) $(OBJS_h) tests/link_test.o $(LIBS)
+libwpa_test1: libwpa_test.o libwpa_client.a
+	$(Q)$(LDO) $(LDFLAGS) -o libwpa_test1 libwpa_test.o libwpa_client.a $(LIBS_c)
 	@$(E) "  LD " $@
 
-test_wpa: $(OBJS_wpa) $(OBJS_h)
-	$(Q)$(LDO) $(LDFLAGS) -o test_wpa $(OBJS_wpa) $(LIBS)
+libwpa_test2: libwpa_test.o libwpa_client.so
+	$(Q)$(LDO) $(LDFLAGS) -o libwpa_test2 libwpa_test.o -L. -lwpa_client $(LIBS_c)
 	@$(E) "  LD " $@
 
 nfc_pw_token: $(OBJS_nfc)
@@ -1748,17 +1767,6 @@
 wpa_gui-qt4: wpa_gui-qt4/Makefile wpa_gui-qt4/lang/wpa_gui_de.qm
 	$(MAKE) -C wpa_gui-qt4
 
-TEST_EAP_SIM_COMMON_OBJS = $(SHA1OBJS) $(MD5OBJS) \
-	../src/utils/common.o ../src/utils/os_unix.o \
-	../src/utils/wpa_debug.o $(AESOBJS) \
-	tests/test_eap_sim_common.o
-test-eap_sim_common: $(TEST_EAP_SIM_COMMON_OBJS)
-	$(LDO) $(LDFLAGS) -o $@ $(TEST_EAP_SIM_COMMON_OBJS) $(LIBS)
-	./test-eap_sim_common
-	rm test-eap_sim_common
-
-tests: test-eap_sim_common
-
 FIPSDIR=/usr/local/ssl/fips-2.0
 FIPSLD=$(FIPSDIR)/bin/fipsld
 fips:
@@ -1779,5 +1787,6 @@
 	rm -rf lcov-html
 	rm -f libwpa_client.a
 	rm -f libwpa_client.so
+	rm -f libwpa_test1 libwpa_test2
 
 -include $(OBJS:%.o=%.d)
diff --git a/wpa_supplicant/android.config b/wpa_supplicant/android.config
index 8d27bb2..e9c0a01 100644
--- a/wpa_supplicant/android.config
+++ b/wpa_supplicant/android.config
@@ -32,6 +32,9 @@
 #CONFIG_DRIVER_NL80211=y
 CONFIG_LIBNL20=y
 
+# QCA vendor extensions to nl80211
+CONFIG_DRIVER_NL80211_QCA=y
+
 # Driver interface for FreeBSD net80211 layer (e.g., Atheros driver)
 #CONFIG_DRIVER_BSD=y
 #CFLAGS += -I/usr/local/include
@@ -476,4 +479,7 @@
 # External password backend for testing purposes (developer use)
 #CONFIG_EXT_PASSWORD_TEST=y
 
+# Enable Fast Session Transfer (FST)
+#CONFIG_FST=y
+
 include $(wildcard $(LOCAL_PATH)/android_config_*.inc)
diff --git a/wpa_supplicant/ap.c b/wpa_supplicant/ap.c
index f3960c5..27fa2a9 100644
--- a/wpa_supplicant/ap.c
+++ b/wpa_supplicant/ap.c
@@ -56,12 +56,32 @@
 	if (!conf->secondary_channel)
 		goto no_vht;
 
-	center_chan = wpas_p2p_get_vht80_center(wpa_s, mode, channel);
+	switch (conf->vht_oper_chwidth) {
+	case VHT_CHANWIDTH_80MHZ:
+	case VHT_CHANWIDTH_80P80MHZ:
+		center_chan = wpas_p2p_get_vht80_center(wpa_s, mode, channel);
+		break;
+	case VHT_CHANWIDTH_160MHZ:
+		center_chan = wpas_p2p_get_vht160_center(wpa_s, mode, channel);
+		break;
+	default:
+		/*
+		 * conf->vht_oper_chwidth might not be set for non-P2P GO cases,
+		 * try oper_cwidth 160 MHz first then VHT 80 MHz, if 160 MHz is
+		 * not supported.
+		 */
+		conf->vht_oper_chwidth = VHT_CHANWIDTH_160MHZ;
+		center_chan = wpas_p2p_get_vht160_center(wpa_s, mode, channel);
+		if (!center_chan) {
+			conf->vht_oper_chwidth = VHT_CHANWIDTH_80MHZ;
+			center_chan = wpas_p2p_get_vht80_center(wpa_s, mode,
+								channel);
+		}
+		break;
+	}
 	if (!center_chan)
 		goto no_vht;
 
-	/* Use 80 MHz channel */
-	conf->vht_oper_chwidth = 1;
 	conf->vht_oper_centr_freq_seg0_idx = center_chan;
 	return;
 
@@ -76,10 +96,19 @@
 #endif /* CONFIG_IEEE80211N */
 
 
-void wpa_supplicant_conf_ap_ht(struct wpa_supplicant *wpa_s,
-			       struct wpa_ssid *ssid,
-			       struct hostapd_config *conf)
+int wpa_supplicant_conf_ap_ht(struct wpa_supplicant *wpa_s,
+			      struct wpa_ssid *ssid,
+			      struct hostapd_config *conf)
 {
+	conf->hw_mode = ieee80211_freq_to_chan(ssid->frequency,
+					       &conf->channel);
+
+	if (conf->hw_mode == NUM_HOSTAPD_MODES) {
+		wpa_printf(MSG_ERROR, "Unsupported AP mode frequency: %d MHz",
+			   ssid->frequency);
+		return -1;
+	}
+
 	/* TODO: enable HT40 if driver supports it;
 	 * drop to 11b if driver does not support 11g */
 
@@ -142,7 +171,32 @@
 			}
 		}
 	}
+
+	if (conf->secondary_channel) {
+		struct wpa_supplicant *iface;
+
+		for (iface = wpa_s->global->ifaces; iface; iface = iface->next)
+		{
+			if (iface == wpa_s ||
+			    iface->wpa_state < WPA_AUTHENTICATING ||
+			    (int) iface->assoc_freq != ssid->frequency)
+				continue;
+
+			/*
+			 * Do not allow 40 MHz co-ex PRI/SEC switch to force us
+			 * to change our PRI channel since we have an existing,
+			 * concurrent connection on that channel and doing
+			 * multi-channel concurrency is likely to cause more
+			 * harm than using different PRI/SEC selection in
+			 * environment with multiple BSSes on these two channels
+			 * with mixed 20 MHz or PRI channel selection.
+			 */
+			conf->no_pri_sec_switch = 1;
+		}
+	}
 #endif /* CONFIG_IEEE80211N */
+
+	return 0;
 }
 
 
@@ -156,15 +210,8 @@
 
 	os_strlcpy(bss->iface, wpa_s->ifname, sizeof(bss->iface));
 
-	conf->hw_mode = ieee80211_freq_to_chan(ssid->frequency,
-					       &conf->channel);
-	if (conf->hw_mode == NUM_HOSTAPD_MODES) {
-		wpa_printf(MSG_ERROR, "Unsupported AP mode frequency: %d MHz",
-			   ssid->frequency);
+	if (wpa_supplicant_conf_ap_ht(wpa_s, ssid, conf))
 		return -1;
-	}
-
-	wpa_supplicant_conf_ap_ht(wpa_s, ssid, conf);
 
 	if (ieee80211_is_dfs(ssid->frequency) && wpa_s->conf->country[0]) {
 		conf->ieee80211h = 1;
@@ -274,13 +321,17 @@
 		conf->beacon_int = wpa_s->conf->beacon_int;
 
 #ifdef CONFIG_P2P
-	if (wpa_s->conf->p2p_go_ctwindow > conf->beacon_int) {
-		wpa_printf(MSG_INFO,
-			   "CTWindow (%d) is bigger than beacon interval (%d) - avoid configuring it",
-			   wpa_s->conf->p2p_go_ctwindow, conf->beacon_int);
-		conf->p2p_go_ctwindow = 0;
-	} else {
-		conf->p2p_go_ctwindow = wpa_s->conf->p2p_go_ctwindow;
+	if (ssid->mode == WPAS_MODE_P2P_GO ||
+	    ssid->mode == WPAS_MODE_P2P_GROUP_FORMATION) {
+		if (wpa_s->conf->p2p_go_ctwindow > conf->beacon_int) {
+			wpa_printf(MSG_INFO,
+				   "CTWindow (%d) is bigger than beacon interval (%d) - avoid configuring it",
+				   wpa_s->conf->p2p_go_ctwindow,
+				   conf->beacon_int);
+			conf->p2p_go_ctwindow = 0;
+		} else {
+			conf->p2p_go_ctwindow = wpa_s->conf->p2p_go_ctwindow;
+		}
 	}
 #endif /* CONFIG_P2P */
 
@@ -604,6 +655,13 @@
 		return -1;
 	}
 
+	/* Use the maximum oper channel width if it's given. */
+	if (ssid->max_oper_chwidth)
+		conf->vht_oper_chwidth = ssid->max_oper_chwidth;
+
+	ieee80211_freq_to_chan(ssid->vht_center_freq2,
+			       &conf->vht_oper_centr_freq_seg1_idx);
+
 	os_memcpy(wpa_s->ap_iface->conf->wmm_ac_params,
 		  wpa_s->conf->wmm_ac_params,
 		  sizeof(wpa_s->conf->wmm_ac_params));
@@ -1182,7 +1240,10 @@
 		return;
 
 	wpa_s->assoc_freq = freq;
-	hostapd_event_ch_switch(wpa_s->ap_iface->bss[0], freq, ht, offset, width, cf1, cf1);
+	if (wpa_s->current_ssid)
+		wpa_s->current_ssid->frequency = freq;
+	hostapd_event_ch_switch(wpa_s->ap_iface->bss[0], freq, ht,
+				offset, width, cf1, cf2);
 }
 
 
@@ -1346,3 +1407,10 @@
 				 radar->chan_width, radar->cf1, radar->cf2);
 }
 #endif /* NEED_AP_MLME */
+
+
+void ap_periodic(struct wpa_supplicant *wpa_s)
+{
+	if (wpa_s->ap_iface)
+		hostapd_periodic_iface(wpa_s->ap_iface);
+}
diff --git a/wpa_supplicant/ap.h b/wpa_supplicant/ap.h
index 3f4151d..2b8a1d4 100644
--- a/wpa_supplicant/ap.h
+++ b/wpa_supplicant/ap.h
@@ -76,9 +76,9 @@
 			   const struct wpabuf *pw, const u8 *pubkey_hash);
 
 struct hostapd_config;
-void wpa_supplicant_conf_ap_ht(struct wpa_supplicant *wpa_s,
-			       struct wpa_ssid *ssid,
-			       struct hostapd_config *conf);
+int wpa_supplicant_conf_ap_ht(struct wpa_supplicant *wpa_s,
+			      struct wpa_ssid *ssid,
+			      struct hostapd_config *conf);
 
 int wpas_ap_stop_ap(struct wpa_supplicant *wpa_s);
 
@@ -93,4 +93,6 @@
 void wpas_event_dfs_cac_nop_finished(struct wpa_supplicant *wpa_s,
 				     struct dfs_event *radar);
 
+void ap_periodic(struct wpa_supplicant *wpa_s);
+
 #endif /* AP_H */
diff --git a/wpa_supplicant/autoscan.c b/wpa_supplicant/autoscan.c
index a2cf7a5..d12eb21 100644
--- a/wpa_supplicant/autoscan.c
+++ b/wpa_supplicant/autoscan.c
@@ -1,6 +1,7 @@
 /*
  * WPA Supplicant - auto scan
  * Copyright (c) 2012, Intel Corporation. All rights reserved.
+ * Copyright 2015	Intel Deutschland GmbH
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -50,6 +51,11 @@
 	size_t nlen;
 	int i;
 	const struct autoscan_ops *ops = NULL;
+	struct sched_scan_plan *scan_plans;
+
+	/* Give preference to scheduled scan plans if supported/configured */
+	if (wpa_s->sched_scan_plans)
+		return 0;
 
 	if (wpa_s->autoscan && wpa_s->autoscan_priv)
 		return 0;
@@ -79,11 +85,23 @@
 		return -1;
 	}
 
+	scan_plans = os_malloc(sizeof(*wpa_s->sched_scan_plans));
+	if (!scan_plans)
+		return -1;
+
 	wpa_s->autoscan_params = NULL;
 
 	wpa_s->autoscan_priv = ops->init(wpa_s, params);
-	if (wpa_s->autoscan_priv == NULL)
+	if (!wpa_s->autoscan_priv) {
+		os_free(scan_plans);
 		return -1;
+	}
+
+	scan_plans[0].interval = 5;
+	scan_plans[0].iterations = 0;
+	os_free(wpa_s->sched_scan_plans);
+	wpa_s->sched_scan_plans = scan_plans;
+	wpa_s->sched_scan_plans_num = 1;
 	wpa_s->autoscan = ops;
 
 	wpa_printf(MSG_DEBUG, "autoscan: Initialized module '%s' with "
@@ -116,7 +134,10 @@
 		wpa_s->autoscan_priv = NULL;
 
 		wpa_s->scan_interval = 5;
-		wpa_s->sched_scan_interval = 0;
+
+		os_free(wpa_s->sched_scan_plans);
+		wpa_s->sched_scan_plans = NULL;
+		wpa_s->sched_scan_plans_num = 0;
 	}
 }
 
@@ -134,7 +155,7 @@
 			return -1;
 
 		wpa_s->scan_interval = interval;
-		wpa_s->sched_scan_interval = interval;
+		wpa_s->sched_scan_plans[0].interval = interval;
 
 		request_scan(wpa_s);
 	}
diff --git a/wpa_supplicant/bss.c b/wpa_supplicant/bss.c
index 8134562..24cc986 100644
--- a/wpa_supplicant/bss.c
+++ b/wpa_supplicant/bss.c
@@ -19,11 +19,6 @@
 #include "bss.h"
 
 
-/**
- * WPA_BSS_EXPIRATION_PERIOD - Period of expiration run in seconds
- */
-#define WPA_BSS_EXPIRATION_PERIOD 10
-
 #define WPA_BSS_FREQ_CHANGED_FLAG	BIT(0)
 #define WPA_BSS_SIGNAL_CHANGED_FLAG	BIT(1)
 #define WPA_BSS_PRIVACY_CHANGED_FLAG	BIT(2)
@@ -65,6 +60,9 @@
 	anqp = os_zalloc(sizeof(*anqp));
 	if (anqp == NULL)
 		return NULL;
+#ifdef CONFIG_INTERWORKING
+	dl_list_init(&anqp->anqp_elems);
+#endif /* CONFIG_INTERWORKING */
 	anqp->users = 1;
 	return anqp;
 }
@@ -85,6 +83,7 @@
 
 #define ANQP_DUP(f) if (anqp->f) n->f = wpabuf_dup(anqp->f)
 #ifdef CONFIG_INTERWORKING
+	dl_list_init(&n->anqp_elems);
 	ANQP_DUP(capability_list);
 	ANQP_DUP(venue_name);
 	ANQP_DUP(network_auth_type);
@@ -146,6 +145,10 @@
  */
 static void wpa_bss_anqp_free(struct wpa_bss_anqp *anqp)
 {
+#ifdef CONFIG_INTERWORKING
+	struct wpa_bss_anqp_elem *elem;
+#endif /* CONFIG_INTERWORKING */
+
 	if (anqp == NULL)
 		return;
 
@@ -164,6 +167,13 @@
 	wpabuf_free(anqp->nai_realm);
 	wpabuf_free(anqp->anqp_3gpp);
 	wpabuf_free(anqp->domain_name);
+
+	while ((elem = dl_list_first(&anqp->anqp_elems,
+				     struct wpa_bss_anqp_elem, list))) {
+		dl_list_del(&elem->list);
+		wpabuf_free(elem->payload);
+		os_free(elem);
+	}
 #endif /* CONFIG_INTERWORKING */
 #ifdef CONFIG_HS20
 	wpabuf_free(anqp->hs20_capability_list);
@@ -311,10 +321,18 @@
 
 static int wpa_bss_in_use(struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
 {
-	return bss == wpa_s->current_bss ||
-		(!is_zero_ether_addr(bss->bssid) &&
-		 (os_memcmp(bss->bssid, wpa_s->bssid, ETH_ALEN) == 0 ||
-		  os_memcmp(bss->bssid, wpa_s->pending_bssid, ETH_ALEN) == 0));
+	if (bss == wpa_s->current_bss)
+		return 1;
+
+	if (wpa_s->current_bss &&
+	    (bss->ssid_len != wpa_s->current_bss->ssid_len ||
+	     os_memcmp(bss->ssid, wpa_s->current_bss->ssid,
+		       bss->ssid_len) != 0))
+		return 0; /* SSID has changed */
+
+	return !is_zero_ether_addr(bss->bssid) &&
+		(os_memcmp(bss->bssid, wpa_s->bssid, ETH_ALEN) == 0 ||
+		 os_memcmp(bss->bssid, wpa_s->pending_bssid, ETH_ALEN) == 0);
 }
 
 
@@ -390,8 +408,9 @@
 	dl_list_add_tail(&wpa_s->bss_id, &bss->list_id);
 	wpa_s->num_bss++;
 	wpa_dbg(wpa_s, MSG_DEBUG, "BSS: Add new id %u BSSID " MACSTR
-		" SSID '%s'",
-		bss->id, MAC2STR(bss->bssid), wpa_ssid_txt(ssid, ssid_len));
+		" SSID '%s' freq %d",
+		bss->id, MAC2STR(bss->bssid), wpa_ssid_txt(ssid, ssid_len),
+		bss->freq);
 	wpas_notify_bss_added(wpa_s, bss->bssid, bss->id);
 	return bss;
 }
@@ -534,6 +553,9 @@
 	u32 changes;
 
 	changes = wpa_bss_compare_res(bss, res);
+	if (changes & WPA_BSS_FREQ_CHANGED_FLAG)
+		wpa_printf(MSG_DEBUG, "BSS: " MACSTR " changed freq %d --> %d",
+			   MAC2STR(bss->bssid), bss->freq, res->freq);
 	bss->scan_miss_count = 0;
 	bss->last_update_idx = wpa_s->bss_update_idx;
 	wpa_bss_copy_res(bss, res, fetch_time);
@@ -777,7 +799,7 @@
 	struct wpa_bss *bss, *n;
 
 	os_get_reltime(&wpa_s->last_scan);
-	if (!new_scan)
+	if ((info && info->aborted) || !new_scan)
 		return; /* do not expire entries without new scan */
 
 	dl_list_for_each_safe(bss, n, &wpa_s->bss, struct wpa_bss, list) {
@@ -828,16 +850,6 @@
 }
 
 
-static void wpa_bss_timeout(void *eloop_ctx, void *timeout_ctx)
-{
-	struct wpa_supplicant *wpa_s = eloop_ctx;
-
-	wpa_bss_flush_by_age(wpa_s, wpa_s->conf->bss_expiration_age);
-	eloop_register_timeout(WPA_BSS_EXPIRATION_PERIOD, 0,
-			       wpa_bss_timeout, wpa_s, NULL);
-}
-
-
 /**
  * wpa_bss_init - Initialize BSS table
  * @wpa_s: Pointer to wpa_supplicant data
@@ -850,8 +862,6 @@
 {
 	dl_list_init(&wpa_s->bss);
 	dl_list_init(&wpa_s->bss_id);
-	eloop_register_timeout(WPA_BSS_EXPIRATION_PERIOD, 0,
-			       wpa_bss_timeout, wpa_s, NULL);
 	return 0;
 }
 
@@ -883,7 +893,6 @@
  */
 void wpa_bss_deinit(struct wpa_supplicant *wpa_s)
 {
-	eloop_cancel_timeout(wpa_bss_timeout, wpa_s, NULL);
 	wpa_bss_flush(wpa_s);
 }
 
@@ -1015,8 +1024,8 @@
 	pos = (const u8 *) (bss + 1);
 	end = pos + bss->ie_len;
 
-	while (pos + 1 < end) {
-		if (pos + 2 + pos[1] > end)
+	while (end - pos > 1) {
+		if (2 + pos[1] > end - pos)
 			break;
 		if (pos[0] == ie)
 			return pos;
@@ -1043,8 +1052,8 @@
 	pos = (const u8 *) (bss + 1);
 	end = pos + bss->ie_len;
 
-	while (pos + 1 < end) {
-		if (pos + 2 + pos[1] > end)
+	while (end - pos > 1) {
+		if (2 + pos[1] > end - pos)
 			break;
 		if (pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 &&
 		    vendor_type == WPA_GET_BE32(&pos[2]))
@@ -1080,8 +1089,8 @@
 	pos += bss->ie_len;
 	end = pos + bss->beacon_ie_len;
 
-	while (pos + 1 < end) {
-		if (pos + 2 + pos[1] > end)
+	while (end - pos > 1) {
+		if (2 + pos[1] > end - pos)
 			break;
 		if (pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 &&
 		    vendor_type == WPA_GET_BE32(&pos[2]))
@@ -1116,8 +1125,8 @@
 	pos = (const u8 *) (bss + 1);
 	end = pos + bss->ie_len;
 
-	while (pos + 1 < end) {
-		if (pos + 2 + pos[1] > end)
+	while (end - pos > 1) {
+		if (2 + pos[1] > end - pos)
 			break;
 		if (pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 &&
 		    vendor_type == WPA_GET_BE32(&pos[2]))
@@ -1161,8 +1170,8 @@
 	pos += bss->ie_len;
 	end = pos + bss->beacon_ie_len;
 
-	while (pos + 1 < end) {
-		if (pos + 2 + pos[1] > end)
+	while (end - pos > 1) {
+		if (2 + pos[1] > end - pos)
 			break;
 		if (pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 &&
 		    vendor_type == WPA_GET_BE32(&pos[2]))
diff --git a/wpa_supplicant/bss.h b/wpa_supplicant/bss.h
index b215380..4a782af 100644
--- a/wpa_supplicant/bss.h
+++ b/wpa_supplicant/bss.h
@@ -19,6 +19,12 @@
 #define WPA_BSS_ASSOCIATED		BIT(5)
 #define WPA_BSS_ANQP_FETCH_TRIED	BIT(6)
 
+struct wpa_bss_anqp_elem {
+	struct dl_list list;
+	u16 infoid;
+	struct wpabuf *payload;
+};
+
 /**
  * struct wpa_bss_anqp - ANQP data for a BSS entry (struct wpa_bss)
  */
@@ -34,6 +40,7 @@
 	struct wpabuf *nai_realm;
 	struct wpabuf *anqp_3gpp;
 	struct wpabuf *domain_name;
+	struct dl_list anqp_elems; /* list of struct wpa_bss_anqp_elem */
 #endif /* CONFIG_INTERWORKING */
 #ifdef CONFIG_HS20
 	struct wpabuf *hs20_capability_list;
diff --git a/wpa_supplicant/config.c b/wpa_supplicant/config.c
index 4d801cc..f2ae4fd 100644
--- a/wpa_supplicant/config.c
+++ b/wpa_supplicant/config.c
@@ -15,6 +15,7 @@
 #include "rsn_supp/wpa.h"
 #include "eap_peer/eap.h"
 #include "p2p/p2p.h"
+#include "fst/fst.h"
 #include "config.h"
 
 
@@ -898,6 +899,9 @@
 
 static int wpa_config_parse_cipher(int line, const char *value)
 {
+#ifdef CONFIG_NO_WPA
+	return -1;
+#else /* CONFIG_NO_WPA */
 	int val = wpa_parse_cipher(value);
 	if (val < 0) {
 		wpa_printf(MSG_ERROR, "Line %d: invalid cipher '%s'.",
@@ -910,12 +914,16 @@
 		return -1;
 	}
 	return val;
+#endif /* CONFIG_NO_WPA */
 }
 
 
 #ifndef NO_CONFIG_WRITE
 static char * wpa_config_write_cipher(int cipher)
 {
+#ifdef CONFIG_NO_WPA
+	return NULL;
+#else /* CONFIG_NO_WPA */
 	char *buf = os_zalloc(50);
 	if (buf == NULL)
 		return NULL;
@@ -926,6 +934,7 @@
 	}
 
 	return buf;
+#endif /* CONFIG_NO_WPA */
 }
 #endif /* NO_CONFIG_WRITE */
 
@@ -1836,6 +1845,8 @@
 	{ FUNC(auth_alg) },
 	{ FUNC(scan_freq) },
 	{ FUNC(freq_list) },
+	{ INT_RANGE(max_oper_chwidth, VHT_CHANWIDTH_USE_HT,
+		    VHT_CHANWIDTH_80P80MHZ) },
 #ifdef IEEE8021X_EAPOL
 	{ FUNC(eap) },
 	{ STR_LENe(identity) },
@@ -2269,6 +2280,9 @@
 	os_free(config->osu_dir);
 	os_free(config->bgscan);
 	os_free(config->wowlan_triggers);
+	os_free(config->fst_group_id);
+	os_free(config->sched_scan_plans);
+
 	os_free(config);
 }
 
@@ -3515,9 +3529,12 @@
 	config->user_mpm = DEFAULT_USER_MPM;
 	config->max_peer_links = DEFAULT_MAX_PEER_LINKS;
 	config->mesh_max_inactivity = DEFAULT_MESH_MAX_INACTIVITY;
+	config->dot11RSNASAERetransPeriod =
+		DEFAULT_DOT11_RSNA_SAE_RETRANS_PERIOD;
 	config->fast_reauth = DEFAULT_FAST_REAUTH;
 	config->p2p_go_intent = DEFAULT_P2P_GO_INTENT;
 	config->p2p_intra_bss = DEFAULT_P2P_INTRA_BSS;
+	config->p2p_go_freq_change_policy = DEFAULT_P2P_GO_FREQ_MOVE;
 	config->p2p_go_max_inactivity = DEFAULT_P2P_GO_MAX_INACTIVITY;
 	config->p2p_optimize_listen_chan = DEFAULT_P2P_OPTIMIZE_LISTEN_CHAN;
 	config->p2p_go_ctwindow = DEFAULT_P2P_GO_CTWINDOW;
@@ -3535,6 +3552,7 @@
 	config->rand_addr_lifetime = DEFAULT_RAND_ADDR_LIFETIME;
 	config->key_mgmt_offload = DEFAULT_KEY_MGMT_OFFLOAD;
 	config->cert_in_cb = DEFAULT_CERT_IN_CB;
+	config->wpa_rsc_relaxation = DEFAULT_WPA_RSC_RELAXATION;
 
 	if (ctrl_interface)
 		config->ctrl_interface = os_strdup(ctrl_interface);
@@ -4128,6 +4146,7 @@
 	{ INT(user_mpm), 0 },
 	{ INT_RANGE(max_peer_links, 0, 255), 0 },
 	{ INT(mesh_max_inactivity), 0 },
+	{ INT(dot11RSNASAERetransPeriod), 0 },
 #endif /* CONFIG_MESH */
 	{ INT(disable_scan_offload), 0 },
 	{ INT(fast_reauth), 0 },
@@ -4162,8 +4181,8 @@
 #endif /* CONFIG_WPS */
 #ifdef CONFIG_P2P
 	{ FUNC(sec_device_type), CFG_CHANGED_SEC_DEVICE_TYPE },
-	{ INT(p2p_listen_reg_class), 0 },
-	{ INT(p2p_listen_channel), 0 },
+	{ INT(p2p_listen_reg_class), CFG_CHANGED_P2P_LISTEN_CHANNEL },
+	{ INT(p2p_listen_channel), CFG_CHANGED_P2P_LISTEN_CHANNEL },
 	{ INT(p2p_oper_reg_class), CFG_CHANGED_P2P_OPER_CHANNEL },
 	{ INT(p2p_oper_channel), CFG_CHANGED_P2P_OPER_CHANNEL },
 	{ INT_RANGE(p2p_go_intent, 0, 15), 0 },
@@ -4171,6 +4190,7 @@
 	{ INT_RANGE(persistent_reconnect, 0, 1), 0 },
 	{ INT_RANGE(p2p_intra_bss, 0, 1), CFG_CHANGED_P2P_INTRA_BSS },
 	{ INT(p2p_group_idle), 0 },
+	{ INT_RANGE(p2p_go_freq_change_policy, 0, P2P_GO_FREQ_MOVE_MAX), 0 },
 	{ INT_RANGE(p2p_passphrase_len, 8, 63),
 	  CFG_CHANGED_P2P_PASSPHRASE_LEN },
 	{ FUNC(p2p_pref_chan), CFG_CHANGED_P2P_PREF_CHAN },
@@ -4234,6 +4254,13 @@
 	{ INT(passive_scan), 0 },
 	{ INT(reassoc_same_bss_optim), 0 },
 	{ INT(wps_priority), 0},
+#ifdef CONFIG_FST
+	{ STR_RANGE(fst_group_id, 1, FST_MAX_GROUP_ID_LEN), 0 },
+	{ INT_RANGE(fst_priority, 1, FST_MAX_PRIO_VALUE), 0 },
+	{ INT_RANGE(fst_llt, 1, FST_MAX_LLT_MS), 0 },
+#endif /* CONFIG_FST */
+	{ INT_RANGE(wpa_rsc_relaxation, 0, 1), 0 },
+	{ STR(sched_scan_plans), CFG_CHANGED_SCHED_SCAN_PLANS },
 };
 
 #undef FUNC
@@ -4292,6 +4319,23 @@
 }
 
 
+int wpa_config_get_num_global_field_names(void)
+{
+	return NUM_GLOBAL_FIELDS;
+}
+
+
+const char * wpa_config_get_global_field_name(unsigned int i, int *no_var)
+{
+	if (i >= NUM_GLOBAL_FIELDS)
+		return NULL;
+
+	if (no_var)
+		*no_var = !global_fields[i].param1;
+	return global_fields[i].name;
+}
+
+
 int wpa_config_process_global(struct wpa_config *config, char *pos, int line)
 {
 	size_t i;
diff --git a/wpa_supplicant/config.h b/wpa_supplicant/config.h
index d8ca054..86f940d 100644
--- a/wpa_supplicant/config.h
+++ b/wpa_supplicant/config.h
@@ -18,6 +18,11 @@
 #define DEFAULT_USER_MPM 1
 #define DEFAULT_MAX_PEER_LINKS 99
 #define DEFAULT_MESH_MAX_INACTIVITY 300
+/*
+ * The default dot11RSNASAERetransPeriod is defined as 40 ms in the standard,
+ * but use 1000 ms in practice to avoid issues on low power CPUs.
+ */
+#define DEFAULT_DOT11_RSNA_SAE_RETRANS_PERIOD 1000
 #define DEFAULT_FAST_REAUTH 1
 #define DEFAULT_P2P_GO_INTENT 7
 #define DEFAULT_P2P_INTRA_BSS 1
@@ -34,6 +39,7 @@
 #define DEFAULT_KEY_MGMT_OFFLOAD 1
 #define DEFAULT_CERT_IN_CB 1
 #define DEFAULT_P2P_GO_CTWINDOW 0
+#define DEFAULT_WPA_RSC_RELAXATION 1
 
 #include "config_ssid.h"
 #include "wps/wps.h"
@@ -326,6 +332,7 @@
 #define CFG_CHANGED_EXT_PW_BACKEND BIT(14)
 #define CFG_CHANGED_NFC_PASSWORD_TOKEN BIT(15)
 #define CFG_CHANGED_P2P_PASSPHRASE_LEN BIT(16)
+#define CFG_CHANGED_SCHED_SCAN_PLANS BIT(17)
 
 /**
  * struct wpa_config - wpa_supplicant configuration data
@@ -401,6 +408,11 @@
 	 * one by one until the driver reports successful association; each
 	 * network block should have explicit security policy (i.e., only one
 	 * option in the lists) for key_mgmt, pairwise, group, proto variables.
+	 *
+	 * Note: ap_scan=2 should not be used with the nl80211 driver interface
+	 * (the current Linux interface). ap_scan=1 is optimized work working
+	 * with nl80211. For finding networks using hidden SSID, scan_ssid=1 in
+	 * the network block can be used with nl80211.
 	 */
 	int ap_scan;
 
@@ -734,6 +746,39 @@
 	int p2p_group_idle;
 
 	/**
+	 * p2p_go_freq_change_policy - The GO frequency change policy
+	 *
+	 * This controls the behavior of the GO when there is a change in the
+	 * map of the currently used frequencies in case more than one channel
+	 * is supported.
+	 *
+	 * @P2P_GO_FREQ_MOVE_SCM: Prefer working in a single channel mode if
+	 * possible. In case the GO is the only interface using its frequency
+	 * and there are other station interfaces on other frequencies, the GO
+	 * will migrate to one of these frequencies.
+	 *
+	 * @P2P_GO_FREQ_MOVE_SCM_PEER_SUPPORTS: Same as P2P_GO_FREQ_MOVE_SCM,
+	 * but a transition is possible only in case one of the other used
+	 * frequencies is one of the frequencies in the intersection of the
+	 * frequency list of the local device and the peer device.
+	 *
+	 * @P2P_GO_FREQ_MOVE_STAY: Prefer to stay on the current frequency.
+	 *
+	 * @P2P_GO_FREQ_MOVE_SCM_ECSA: Same as
+	 * P2P_GO_FREQ_MOVE_SCM_PEER_SUPPORTS but a transition is possible only
+	 * if all the group members advertise eCSA support.
+	 */
+	enum {
+		P2P_GO_FREQ_MOVE_SCM = 0,
+		P2P_GO_FREQ_MOVE_SCM_PEER_SUPPORTS = 1,
+		P2P_GO_FREQ_MOVE_STAY = 2,
+		P2P_GO_FREQ_MOVE_SCM_ECSA = 3,
+		P2P_GO_FREQ_MOVE_MAX = P2P_GO_FREQ_MOVE_SCM_ECSA,
+	} p2p_go_freq_change_policy;
+
+#define DEFAULT_P2P_GO_FREQ_MOVE P2P_GO_FREQ_MOVE_STAY
+
+	/**
 	 * p2p_passphrase_len - Passphrase length (8..63) for P2P GO
 	 *
 	 * This parameter controls the length of the random passphrase that is
@@ -1161,6 +1206,15 @@
 	int mesh_max_inactivity;
 
 	/**
+	 * dot11RSNASAERetransPeriod - Timeout to retransmit SAE Auth frame
+	 *
+	 * This timeout value is used in mesh STA to retransmit
+	 * SAE Authentication frame.
+	 * By default: 1000 milliseconds.
+	 */
+	int dot11RSNASAERetransPeriod;
+
+	/**
 	 * passive_scan - Whether to force passive scan for network connection
 	 *
 	 * This parameter can be used to force only passive scanning to be used
@@ -1184,6 +1238,43 @@
 	 * by executing the WPS protocol.
 	 */
 	int wps_priority;
+
+	/**
+	 * fst_group_id - FST group ID
+	 */
+	char *fst_group_id;
+
+	/**
+	 * fst_priority - priority of the interface within the FST group
+	 */
+	int fst_priority;
+
+	/**
+	 * fst_llt - default FST LLT (Link-Lost Timeout) to be used for the
+	 * interface.
+	 */
+	int fst_llt;
+
+	 /**
+	  * wpa_rsc_relaxation - RSC relaxation on GTK installation
+	  *
+	  * Values:
+	  * 0 - use the EAPOL-Key RSC value on GTK installation
+	  * 1 - use the null RSC if a bogus RSC value is detected in message 3
+	  * of 4-Way Handshake or message 1 of Group Key Handshake.
+	  */
+	 int wpa_rsc_relaxation;
+
+	/**
+	 * sched_scan_plans - Scan plans for scheduled scan
+	 *
+	 * Each scan plan specifies the interval between scans and the number of
+	 * iterations. The last scan plan only specifies the scan interval and
+	 * will be run infinitely.
+	 *
+	 * format: <interval:iterations> <interval2:iterations2> ... <interval>
+	 */
+	 char *sched_scan_plans;
 };
 
 
@@ -1242,6 +1333,9 @@
 /* Prototypes for common functions from config.c */
 int wpa_config_process_global(struct wpa_config *config, char *pos, int line);
 
+int wpa_config_get_num_global_field_names(void);
+
+const char * wpa_config_get_global_field_name(unsigned int i, int *no_var);
 
 /* Prototypes for backend specific functions from the selected config_*.c */
 
diff --git a/wpa_supplicant/config_file.c b/wpa_supplicant/config_file.c
index aeea70c..80e3e56 100644
--- a/wpa_supplicant/config_file.c
+++ b/wpa_supplicant/config_file.c
@@ -751,6 +751,7 @@
 	INT(disabled);
 	INT(peerkey);
 	INT(mixed_cell);
+	INT(max_oper_chwidth);
 #ifdef CONFIG_IEEE80211W
 	write_int(f, "ieee80211w", ssid->ieee80211w,
 		  MGMT_FRAME_PROTECTION_DEFAULT);
@@ -1133,6 +1134,9 @@
 			config->p2p_ignore_shared_freq);
 	if (config->p2p_cli_probe)
 		fprintf(f, "p2p_cli_probe=%d\n", config->p2p_cli_probe);
+	if (config->p2p_go_freq_change_policy != DEFAULT_P2P_GO_FREQ_MOVE)
+		fprintf(f, "p2p_go_freq_change_policy=%u\n",
+			config->p2p_go_freq_change_policy);
 #endif /* CONFIG_P2P */
 	if (config->country[0] && config->country[1]) {
 		fprintf(f, "country=%c%c\n",
@@ -1282,6 +1286,11 @@
 		fprintf(f, "mesh_max_inactivity=%d\n",
 			config->mesh_max_inactivity);
 
+	if (config->dot11RSNASAERetransPeriod !=
+	    DEFAULT_DOT11_RSNA_SAE_RETRANS_PERIOD)
+		fprintf(f, "dot11RSNASAERetransPeriod=%d\n",
+			config->dot11RSNASAERetransPeriod);
+
 	if (config->passive_scan)
 		fprintf(f, "passive_scan=%d\n", config->passive_scan);
 
@@ -1291,6 +1300,13 @@
 
 	if (config->wps_priority)
 		fprintf(f, "wps_priority=%d\n", config->wps_priority);
+
+	if (config->wpa_rsc_relaxation != DEFAULT_WPA_RSC_RELAXATION)
+		fprintf(f, "wpa_rsc_relaxation=%d\n",
+			config->wpa_rsc_relaxation);
+
+	if (config->sched_scan_plans)
+		fprintf(f, "sched_scan_plans=%s\n", config->sched_scan_plans);
 }
 
 #endif /* CONFIG_NO_CONFIG_WRITE */
@@ -1353,7 +1369,7 @@
 	}
 #endif /* CONFIG_NO_CONFIG_BLOBS */
 
-	os_fsync(f);
+	os_fdatasync(f);
 
 	fclose(f);
 
diff --git a/wpa_supplicant/config_ssid.h b/wpa_supplicant/config_ssid.h
index 1c63e51..de8157a 100644
--- a/wpa_supplicant/config_ssid.h
+++ b/wpa_supplicant/config_ssid.h
@@ -225,7 +225,9 @@
 	 *
 	 * scan_ssid can be used to scan for APs using hidden SSIDs.
 	 * Note: Many drivers do not support this. ap_mode=2 can be used with
-	 * such drivers to use hidden SSIDs.
+	 * such drivers to use hidden SSIDs. Note2: Most nl80211-based drivers
+	 * do support scan_ssid=1 and that should be used with them instead of
+	 * ap_scan=2.
 	 */
 	int scan_ssid;
 
@@ -447,6 +449,10 @@
 
 	int vht;
 
+	u8 max_oper_chwidth;
+
+	unsigned int vht_center_freq2;
+
 	/**
 	 * wpa_ptk_rekey - Maximum lifetime for PTK in seconds
 	 *
diff --git a/wpa_supplicant/ctrl_iface.c b/wpa_supplicant/ctrl_iface.c
index bab10ad..7b45a3a 100644
--- a/wpa_supplicant/ctrl_iface.c
+++ b/wpa_supplicant/ctrl_iface.c
@@ -28,6 +28,8 @@
 #include "rsn_supp/pmksa_cache.h"
 #include "l2_packet/l2_packet.h"
 #include "wps/wps.h"
+#include "fst/fst.h"
+#include "fst/fst_ctrl_iface.h"
 #include "config.h"
 #include "wpa_supplicant_i.h"
 #include "driver_i.h"
@@ -465,6 +467,8 @@
 		wpa_s->extra_roc_dur = atoi(value);
 	} else if (os_strcasecmp(cmd, "test_failure") == 0) {
 		wpa_s->test_failure = atoi(value);
+	} else if (os_strcasecmp(cmd, "p2p_go_csa_on_inv") == 0) {
+		wpa_s->p2p_go_csa_on_inv = !!atoi(value);
 #endif /* CONFIG_TESTING_OPTIONS */
 #ifndef CONFIG_NO_CONFIG_BLOBS
 	} else if (os_strcmp(cmd, "blob") == 0) {
@@ -2134,45 +2138,6 @@
 }
 
 
-static const char * debug_level_str(int level)
-{
-	switch (level) {
-	case MSG_EXCESSIVE:
-		return "EXCESSIVE";
-	case MSG_MSGDUMP:
-		return "MSGDUMP";
-	case MSG_DEBUG:
-		return "DEBUG";
-	case MSG_INFO:
-		return "INFO";
-	case MSG_WARNING:
-		return "WARNING";
-	case MSG_ERROR:
-		return "ERROR";
-	default:
-		return "?";
-	}
-}
-
-
-static int str_to_debug_level(const char *s)
-{
-	if (os_strcasecmp(s, "EXCESSIVE") == 0)
-		return MSG_EXCESSIVE;
-	if (os_strcasecmp(s, "MSGDUMP") == 0)
-		return MSG_MSGDUMP;
-	if (os_strcasecmp(s, "DEBUG") == 0)
-		return MSG_DEBUG;
-	if (os_strcasecmp(s, "INFO") == 0)
-		return MSG_INFO;
-	if (os_strcasecmp(s, "WARNING") == 0)
-		return MSG_WARNING;
-	if (os_strcasecmp(s, "ERROR") == 0)
-		return MSG_ERROR;
-	return -1;
-}
-
-
 static int wpa_supplicant_ctrl_iface_log_level(struct wpa_supplicant *wpa_s,
 					       char *cmd, char *buf,
 					       size_t buflen)
@@ -2205,7 +2170,7 @@
 		}
 	}
 
-	if (cmd && os_strlen(cmd)) {
+	if (os_strlen(cmd)) {
 		int level = str_to_debug_level(cmd);
 		if (level < 0)
 			return -1;
@@ -2582,6 +2547,14 @@
 		pos += ret;
 	}
 #endif /* CONFIG_HS20 */
+#ifdef CONFIG_FST
+	if (wpa_bss_get_ie(bss, WLAN_EID_MULTI_BAND)) {
+		ret = os_snprintf(pos, end - pos, "[FST]");
+		if (os_snprintf_error(end - pos, ret))
+			return -1;
+		pos += ret;
+	}
+#endif /* CONFIG_FST */
 
 	ret = os_snprintf(pos, end - pos, "\t%s",
 			  wpa_ssid_txt(bss->ssid, bss->ssid_len));
@@ -2773,6 +2746,8 @@
 		}
 	}
 
+	wpa_s->scan_min_time.sec = 0;
+	wpa_s->scan_min_time.usec = 0;
 	wpa_supplicant_select_network(wpa_s, ssid);
 
 	return 0;
@@ -2810,6 +2785,8 @@
 			return 0;
 		}
 	}
+	wpa_s->scan_min_time.sec = 0;
+	wpa_s->scan_min_time.usec = 0;
 	wpa_supplicant_enable_network(wpa_s, ssid);
 
 	return 0;
@@ -3064,19 +3041,19 @@
 	*name++ = '\0';
 
 	id = atoi(cmd);
-	wpa_printf(MSG_DEBUG, "CTRL_IFACE: GET_NETWORK id=%d name='%s'",
+	wpa_printf(MSG_EXCESSIVE, "CTRL_IFACE: GET_NETWORK id=%d name='%s'",
 		   id, name);
 
 	ssid = wpa_config_get_network(wpa_s->conf, id);
 	if (ssid == NULL) {
-		wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find network "
+		wpa_printf(MSG_EXCESSIVE, "CTRL_IFACE: Could not find network "
 			   "id=%d", id);
 		return -1;
 	}
 
 	value = wpa_config_get_no_key(ssid, name);
 	if (value == NULL) {
-		wpa_printf(MSG_DEBUG, "CTRL_IFACE: Failed to get network "
+		wpa_printf(MSG_EXCESSIVE, "CTRL_IFACE: Failed to get network "
 			   "variable '%s'", name);
 		return -1;
 	}
@@ -3094,7 +3071,8 @@
 
 
 static int wpa_supplicant_ctrl_iface_dup_network(
-	struct wpa_supplicant *wpa_s, char *cmd)
+	struct wpa_supplicant *wpa_s, char *cmd,
+	struct wpa_supplicant *dst_wpa_s)
 {
 	struct wpa_ssid *ssid_s, *ssid_d;
 	char *name, *id, *value;
@@ -3113,8 +3091,10 @@
 
 	id_s = atoi(cmd);
 	id_d = atoi(id);
-	wpa_printf(MSG_DEBUG, "CTRL_IFACE: DUP_NETWORK id=%d -> %d name='%s'",
-		   id_s, id_d, name);
+
+	wpa_printf(MSG_DEBUG,
+		   "CTRL_IFACE: DUP_NETWORK ifname=%s->%s id=%d->%d name='%s'",
+		   wpa_s->ifname, dst_wpa_s->ifname, id_s, id_d, name);
 
 	ssid_s = wpa_config_get_network(wpa_s->conf, id_s);
 	if (ssid_s == NULL) {
@@ -3123,7 +3103,7 @@
 		return -1;
 	}
 
-	ssid_d = wpa_config_get_network(wpa_s->conf, id_d);
+	ssid_d = wpa_config_get_network(dst_wpa_s->conf, id_d);
 	if (ssid_d == NULL) {
 		wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find "
 			   "network id=%d", id_d);
@@ -3137,7 +3117,7 @@
 		return -1;
 	}
 
-	ret = wpa_supplicant_ctrl_iface_update_network(wpa_s, ssid_d, name,
+	ret = wpa_supplicant_ctrl_iface_update_network(dst_wpa_s, ssid_d, name,
 						       value);
 
 	os_free(value);
@@ -3948,6 +3928,15 @@
 	}
 #endif /* CONFIG_EPR */
 
+#ifdef CONFIG_FIPS
+	if (os_strcmp(field, "fips") == 0) {
+		res = os_snprintf(buf, buflen, "FIPS");
+		if (os_snprintf_error(buflen, res))
+			return -1;
+		return res;
+	}
+#endif /* CONFIG_FIPS */
+
 	wpa_printf(MSG_DEBUG, "CTRL_IFACE: Unknown GET_CAPABILITY field '%s'",
 		   field);
 
@@ -4197,9 +4186,10 @@
 	if (mask & WPA_BSS_MASK_WPS_SCAN) {
 		ie = (const u8 *) (bss + 1);
 		ret = wpas_wps_scan_result_text(ie, bss->ie_len, pos, end);
-		if (ret < 0 || ret >= end - pos)
+		if (ret >= end - pos)
 			return 0;
-		pos += ret;
+		if (ret > 0)
+			pos += ret;
 	}
 #endif /* CONFIG_WPS */
 
@@ -4243,6 +4233,8 @@
 #ifdef CONFIG_INTERWORKING
 	if ((mask & WPA_BSS_MASK_INTERNETW) && bss->anqp) {
 		struct wpa_bss_anqp *anqp = bss->anqp;
+		struct wpa_bss_anqp_elem *elem;
+
 		pos = anqp_add_hex(pos, end, "anqp_capability_list",
 				   anqp->capability_list);
 		pos = anqp_add_hex(pos, end, "anqp_venue_name",
@@ -4272,6 +4264,15 @@
 		pos = anqp_add_hex(pos, end, "hs20_osu_providers_list",
 				   anqp->hs20_osu_providers_list);
 #endif /* CONFIG_HS20 */
+
+		dl_list_for_each(elem, &anqp->anqp_elems,
+				 struct wpa_bss_anqp_elem, list) {
+			char title[20];
+
+			os_snprintf(title, sizeof(title), "anqp[%u]",
+				    elem->infoid);
+			pos = anqp_add_hex(pos, end, title, elem->payload);
+		}
 	}
 #endif /* CONFIG_INTERWORKING */
 
@@ -4300,6 +4301,15 @@
 		pos += ret;
 	}
 
+#ifdef CONFIG_FST
+	if (mask & WPA_BSS_MASK_FST) {
+		ret = fst_ctrl_iface_mb_info(bss->bssid, pos, end - pos);
+		if (ret < 0 || ret >= end - pos)
+			return 0;
+		pos += ret;
+	}
+#endif /* CONFIG_FST */
+
 	if (mask & WPA_BSS_MASK_DELIM) {
 		ret = os_snprintf(pos, end - pos, "====\n");
 		if (os_snprintf_error(end - pos, ret))
@@ -4647,6 +4657,48 @@
 }
 
 
+static int p2ps_ctrl_parse_cpt_priority(const char *pos, u8 *cpt)
+{
+	const char *last = NULL;
+	const char *token;
+	long int token_len;
+	unsigned int i;
+
+	/* Expected predefined CPT names delimited by ':' */
+	for (i = 0; (token = cstr_token(pos, ": \t", &last)); i++) {
+		if (i >= P2PS_FEATURE_CAPAB_CPT_MAX) {
+			wpa_printf(MSG_ERROR,
+				   "P2PS: CPT name list is too long, expected up to %d names",
+				   P2PS_FEATURE_CAPAB_CPT_MAX);
+			cpt[0] = 0;
+			return -1;
+		}
+
+		token_len = last - token;
+
+		if (token_len  == 3 &&
+		    os_memcmp(token, "UDP", token_len) == 0) {
+			cpt[i] = P2PS_FEATURE_CAPAB_UDP_TRANSPORT;
+		} else if (token_len == 3 &&
+			   os_memcmp(token, "MAC", token_len) == 0) {
+			cpt[i] = P2PS_FEATURE_CAPAB_MAC_TRANSPORT;
+		} else {
+			wpa_printf(MSG_ERROR,
+				   "P2PS: Unsupported CPT name '%s'", token);
+			cpt[0] = 0;
+			return -1;
+		}
+
+		if (isblank(*last)) {
+			i++;
+			break;
+		}
+	}
+	cpt[i] = 0;
+	return 0;
+}
+
+
 static struct p2ps_provision * p2p_parse_asp_provision_cmd(const char *cmd)
 {
 	struct p2ps_provision *p2ps_prov;
@@ -4655,6 +4707,7 @@
 	char *info = NULL;
 	u8 role = P2PS_SETUP_NONE;
 	long long unsigned val;
+	int i;
 
 	pos = os_strstr(cmd, "info=");
 	if (pos) {
@@ -4713,6 +4766,18 @@
 	if (!pos || hwaddr_aton(pos + 12, p2ps_prov->session_mac))
 		goto invalid_args;
 
+	pos = os_strstr(cmd, "cpt=");
+	if (pos) {
+		if (p2ps_ctrl_parse_cpt_priority(pos + 4,
+						 p2ps_prov->cpt_priority))
+			goto invalid_args;
+	} else {
+		p2ps_prov->cpt_priority[0] = P2PS_FEATURE_CAPAB_UDP_TRANSPORT;
+	}
+
+	for (i = 0; p2ps_prov->cpt_priority[i]; i++)
+		p2ps_prov->cpt_mask |= p2ps_prov->cpt_priority[i];
+
 	/* force conncap with tstCap (no sanity checks) */
 	pos = os_strstr(cmd, "tstCap=");
 	if (pos) {
@@ -4789,11 +4854,37 @@
 	if (!p2ps_prov)
 		return -1;
 
+	p2ps_prov->pd_seeker = 1;
+
 	return wpas_p2p_prov_disc(wpa_s, addr, NULL, WPAS_P2P_PD_FOR_ASP,
 				  p2ps_prov);
 }
 
 
+static int parse_freq(int chwidth, int freq2)
+{
+	if (freq2 < 0)
+		return -1;
+	if (freq2)
+		return VHT_CHANWIDTH_80P80MHZ;
+
+	switch (chwidth) {
+	case 0:
+	case 20:
+	case 40:
+		return VHT_CHANWIDTH_USE_HT;
+	case 80:
+		return VHT_CHANWIDTH_80MHZ;
+	case 160:
+		return VHT_CHANWIDTH_160MHZ;
+	default:
+		wpa_printf(MSG_DEBUG, "Unknown max oper bandwidth: %d",
+			   chwidth);
+		return -1;
+	}
+}
+
+
 static int p2p_ctrl_connect(struct wpa_supplicant *wpa_s, char *cmd,
 			    char *buf, size_t buflen)
 {
@@ -4810,7 +4901,7 @@
 	int go_intent = -1;
 	int freq = 0;
 	int pd;
-	int ht40, vht;
+	int ht40, vht, max_oper_chwidth, chwidth = 0, freq2 = 0;
 
 	if (!wpa_s->global->p2p_init_wpa_s)
 		return -1;
@@ -4871,6 +4962,18 @@
 			return -1;
 	}
 
+	pos2 = os_strstr(pos, " freq2=");
+	if (pos2)
+		freq2 = atoi(pos2 + 7);
+
+	pos2 = os_strstr(pos, " max_oper_chwidth=");
+	if (pos2)
+		chwidth = atoi(pos2 + 18);
+
+	max_oper_chwidth = parse_freq(chwidth, freq2);
+	if (max_oper_chwidth < 0)
+		return -1;
+
 	if (os_strncmp(pos, "pin", 3) == 0) {
 		/* Request random PIN (to be displayed) and enable the PIN */
 		wps_method = WPS_PIN_DISPLAY;
@@ -4895,8 +4998,8 @@
 
 	new_pin = wpas_p2p_connect(wpa_s, addr, pin, wps_method,
 				   persistent_group, automatic, join,
-				   auth, go_intent, freq, persistent_id, pd,
-				   ht40, vht);
+				   auth, go_intent, freq, freq2, persistent_id,
+				   pd, ht40, vht, max_oper_chwidth);
 	if (new_pin == -2) {
 		os_memcpy(buf, "FAIL-CHANNEL-UNAVAILABLE\n", 25);
 		return 25;
@@ -5208,6 +5311,8 @@
 	char *adv_str;
 	u32 auto_accept, adv_id, svc_state, config_methods;
 	char *svc_info = NULL;
+	char *cpt_prio_str;
+	u8 cpt_prio[P2PS_FEATURE_CAPAB_CPT_MAX + 1];
 
 	pos = os_strchr(cmd, ' ');
 	if (pos == NULL)
@@ -5280,6 +5385,19 @@
 	if (pos != NULL)
 		*pos++ = '\0';
 
+	cpt_prio_str = (pos && pos[0]) ? os_strstr(pos, "cpt=") : NULL;
+	if (cpt_prio_str) {
+		pos = os_strchr(pos, ' ');
+		if (pos != NULL)
+			*pos++ = '\0';
+
+		if (p2ps_ctrl_parse_cpt_priority(cpt_prio_str + 4, cpt_prio))
+			return -1;
+	} else {
+		cpt_prio[0] = P2PS_FEATURE_CAPAB_UDP_TRANSPORT;
+		cpt_prio[1] = 0;
+	}
+
 	/* Service and Response Information are optional */
 	if (pos && pos[0]) {
 		size_t len;
@@ -5297,7 +5415,7 @@
 
 	return wpas_p2p_service_add_asp(wpa_s, auto_accept, adv_id, adv_str,
 					(u8) svc_state, (u16) config_methods,
-					svc_info);
+					svc_info, cpt_prio);
 }
 
 
@@ -5436,7 +5554,7 @@
 	struct wpa_ssid *ssid;
 	u8 *_peer = NULL, peer[ETH_ALEN];
 	int freq = 0, pref_freq = 0;
-	int ht40, vht;
+	int ht40, vht, max_oper_chwidth, chwidth = 0, freq2 = 0;
 
 	id = atoi(cmd);
 	pos = os_strstr(cmd, " peer=");
@@ -5474,8 +5592,20 @@
 	ht40 = (os_strstr(cmd, " ht40") != NULL) || wpa_s->conf->p2p_go_ht40 ||
 		vht;
 
-	return wpas_p2p_invite(wpa_s, _peer, ssid, NULL, freq, ht40, vht,
-			       pref_freq);
+	pos = os_strstr(cmd, "freq2=");
+	if (pos)
+		freq2 = atoi(pos + 6);
+
+	pos = os_strstr(cmd, " max_oper_chwidth=");
+	if (pos)
+		chwidth = atoi(pos + 18);
+
+	max_oper_chwidth = parse_freq(chwidth, freq2);
+	if (max_oper_chwidth < 0)
+		return -1;
+
+	return wpas_p2p_invite(wpa_s, _peer, ssid, NULL, freq, freq2, ht40, vht,
+			       max_oper_chwidth, pref_freq);
 }
 
 
@@ -5522,7 +5652,8 @@
 
 
 static int p2p_ctrl_group_add_persistent(struct wpa_supplicant *wpa_s,
-					 int id, int freq, int ht40, int vht)
+					 int id, int freq, int vht_center_freq2,
+					 int ht40, int vht, int vht_chwidth)
 {
 	struct wpa_ssid *ssid;
 
@@ -5534,8 +5665,9 @@
 		return -1;
 	}
 
-	return wpas_p2p_group_add_persistent(wpa_s, ssid, 0, freq, 0, ht40, vht,
-					     NULL, 0);
+	return wpas_p2p_group_add_persistent(wpa_s, ssid, 0, freq,
+					     vht_center_freq2, 0, ht40, vht,
+					     vht_chwidth, NULL, 0, 0);
 }
 
 
@@ -5544,11 +5676,14 @@
 	int freq = 0, persistent = 0, group_id = -1;
 	int vht = wpa_s->conf->p2p_go_vht;
 	int ht40 = wpa_s->conf->p2p_go_ht40 || vht;
+	int max_oper_chwidth, chwidth = 0, freq2 = 0;
 	char *token, *context = NULL;
 
 	while ((token = str_token(cmd, " ", &context))) {
 		if (sscanf(token, "freq=%d", &freq) == 1 ||
-		    sscanf(token, "persistent=%d", &group_id) == 1) {
+		    sscanf(token, "freq2=%d", &freq2) == 1 ||
+		    sscanf(token, "persistent=%d", &group_id) == 1 ||
+		    sscanf(token, "max_oper_chwidth=%d", &chwidth) == 1) {
 			continue;
 		} else if (os_strcmp(token, "ht40") == 0) {
 			ht40 = 1;
@@ -5565,11 +5700,17 @@
 		}
 	}
 
+	max_oper_chwidth = parse_freq(chwidth, freq2);
+	if (max_oper_chwidth < 0)
+		return -1;
+
 	if (group_id >= 0)
 		return p2p_ctrl_group_add_persistent(wpa_s, group_id,
-						     freq, ht40, vht);
+						     freq, freq2, ht40, vht,
+						     max_oper_chwidth);
 
-	return wpas_p2p_group_add(wpa_s, persistent, freq, ht40, vht);
+	return wpas_p2p_group_add(wpa_s, persistent, freq, freq2, ht40, vht,
+				  max_oper_chwidth);
 }
 
 
@@ -5699,7 +5840,7 @@
 			   freq->min, freq->max);
 	}
 
-	wpas_p2p_update_channel_list(wpa_s);
+	wpas_p2p_update_channel_list(wpa_s, WPAS_P2P_CHANNEL_UPDATE_DISALLOW);
 	return 0;
 }
 
@@ -5916,6 +6057,7 @@
 	os_memset(wpa_s->p2p_auth_invite, 0, ETH_ALEN);
 	wpa_s->force_long_sd = 0;
 	wpas_p2p_stop_find(wpa_s);
+	wpa_s->parent->p2ps_method_config_any = 0;
 	if (wpa_s->global->p2p)
 		p2p_flush(wpa_s->global->p2p);
 }
@@ -6562,6 +6704,53 @@
 }
 
 
+static int wpas_ctrl_iface_get_pref_freq_list(
+	struct wpa_supplicant *wpa_s, char *cmd, char *buf, size_t buflen)
+{
+	unsigned int freq_list[100], num = 100, i;
+	int ret;
+	enum wpa_driver_if_type iface_type;
+	char *pos, *end;
+
+	pos = buf;
+	end = buf + buflen;
+
+	/* buf: "<interface_type>" */
+	if (os_strcmp(cmd, "STATION") == 0)
+		iface_type = WPA_IF_STATION;
+	else if (os_strcmp(cmd, "AP") == 0)
+		iface_type = WPA_IF_AP_BSS;
+	else if (os_strcmp(cmd, "P2P_GO") == 0)
+		iface_type = WPA_IF_P2P_GO;
+	else if (os_strcmp(cmd, "P2P_CLIENT") == 0)
+		iface_type = WPA_IF_P2P_CLIENT;
+	else if (os_strcmp(cmd, "IBSS") == 0)
+		iface_type = WPA_IF_IBSS;
+	else if (os_strcmp(cmd, "TDLS") == 0)
+		iface_type = WPA_IF_TDLS;
+	else
+		return -1;
+
+	wpa_printf(MSG_DEBUG,
+		   "CTRL_IFACE: GET_PREF_FREQ_LIST iface_type=%d (%s)",
+		   iface_type, buf);
+
+	ret = wpa_drv_get_pref_freq_list(wpa_s, iface_type, &num, freq_list);
+	if (ret)
+		return -1;
+
+	for (i = 0; i < num; i++) {
+		ret = os_snprintf(pos, end - pos, "%s%u",
+				  i > 0 ? "," : "", freq_list[i]);
+		if (os_snprintf_error(end - pos, ret))
+			return -1;
+		pos += ret;
+	}
+
+	return pos - buf;
+}
+
+
 static int wpa_supplicant_pktcnt_poll(struct wpa_supplicant *wpa_s, char *buf,
 				      size_t buflen)
 {
@@ -6673,6 +6862,8 @@
 
 	wpa_dbg(wpa_s, MSG_DEBUG, "Flush all wpa_supplicant state");
 
+	wpas_abort_ongoing_scan(wpa_s);
+
 #ifdef CONFIG_P2P
 	wpas_p2p_cancel(p2p_wpa_s);
 	p2p_ctrl_flush(p2p_wpa_s);
@@ -6684,7 +6875,9 @@
 	p2p_wpa_s->p2p_disable_ip_addr_req = 0;
 	os_free(p2p_wpa_s->global->p2p_go_avoid_freq.range);
 	p2p_wpa_s->global->p2p_go_avoid_freq.range = NULL;
+	p2p_wpa_s->global->p2p_go_avoid_freq.num = 0;
 	p2p_wpa_s->global->pending_p2ps_group = 0;
+	p2p_wpa_s->global->pending_p2ps_group_freq = 0;
 #endif /* CONFIG_P2P */
 
 #ifdef CONFIG_WPS_TESTING
@@ -6748,7 +6941,9 @@
 	wpa_s->next_ssid = NULL;
 
 #ifdef CONFIG_INTERWORKING
+#ifdef CONFIG_HS20
 	hs20_cancel_fetch_osu(wpa_s);
+#endif /* CONFIG_HS20 */
 #endif /* CONFIG_INTERWORKING */
 
 	wpa_s->ext_mgmt_frame_handling = 0;
@@ -6756,6 +6951,7 @@
 #ifdef CONFIG_TESTING_OPTIONS
 	wpa_s->extra_roc_dur = 0;
 	wpa_s->test_failure = WPAS_TEST_FAILURE_NONE;
+	wpa_s->p2p_go_csa_on_inv = 0;
 #endif /* CONFIG_TESTING_OPTIONS */
 
 	wpa_s->disconnected = 0;
@@ -6773,6 +6969,7 @@
 	}
 
 	eloop_cancel_timeout(wpas_network_reenabled, wpa_s, NULL);
+	wpa_s->wnmsleep_used = 0;
 }
 
 
@@ -7381,7 +7578,7 @@
 {
 	struct wpa_supplicant *wpa_s = ctx;
 	const struct ether_header *eth;
-	const struct iphdr *ip;
+	struct iphdr ip;
 	const u8 *pos;
 	unsigned int i;
 
@@ -7389,14 +7586,14 @@
 		return;
 
 	eth = (const struct ether_header *) buf;
-	ip = (const struct iphdr *) (eth + 1);
-	pos = (const u8 *) (ip + 1);
+	os_memcpy(&ip, eth + 1, sizeof(ip));
+	pos = &buf[sizeof(*eth) + sizeof(ip)];
 
-	if (ip->ihl != 5 || ip->version != 4 ||
-	    ntohs(ip->tot_len) != HWSIM_IP_LEN)
+	if (ip.ihl != 5 || ip.version != 4 ||
+	    ntohs(ip.tot_len) != HWSIM_IP_LEN)
 		return;
 
-	for (i = 0; i < HWSIM_IP_LEN - sizeof(*ip); i++) {
+	for (i = 0; i < HWSIM_IP_LEN - sizeof(ip); i++) {
 		if (*pos != (u8) i)
 			return;
 		pos++;
@@ -7443,7 +7640,7 @@
 	int used;
 	long int val;
 	u8 tos;
-	u8 buf[HWSIM_PACKETLEN];
+	u8 buf[2 + HWSIM_PACKETLEN];
 	struct ether_header *eth;
 	struct iphdr *ip;
 	u8 *dpos;
@@ -7471,7 +7668,7 @@
 		return -1;
 	tos = val;
 
-	eth = (struct ether_header *) buf;
+	eth = (struct ether_header *) &buf[2];
 	os_memcpy(eth->ether_dhost, dst, ETH_ALEN);
 	os_memcpy(eth->ether_shost, src, ETH_ALEN);
 	eth->ether_type = htons(ETHERTYPE_IP);
@@ -7483,14 +7680,14 @@
 	ip->tos = tos;
 	ip->tot_len = htons(HWSIM_IP_LEN);
 	ip->protocol = 1;
-	ip->saddr = htonl(192 << 24 | 168 << 16 | 1 << 8 | 1);
-	ip->daddr = htonl(192 << 24 | 168 << 16 | 1 << 8 | 2);
+	ip->saddr = htonl(192U << 24 | 168 << 16 | 1 << 8 | 1);
+	ip->daddr = htonl(192U << 24 | 168 << 16 | 1 << 8 | 2);
 	ip->check = ipv4_hdr_checksum(ip, sizeof(*ip));
 	dpos = (u8 *) (ip + 1);
 	for (i = 0; i < HWSIM_IP_LEN - sizeof(*ip); i++)
 		*dpos++ = i;
 
-	if (l2_packet_send(wpa_s->l2_test, dst, ETHERTYPE_IP, buf,
+	if (l2_packet_send(wpa_s->l2_test, dst, ETHERTYPE_IP, &buf[2],
 			   HWSIM_PACKETLEN) < 0)
 		return -1;
 
@@ -7580,6 +7777,44 @@
 }
 
 
+static int wpas_ctrl_test_fail(struct wpa_supplicant *wpa_s, char *cmd)
+{
+#ifdef WPA_TRACE_BFD
+	extern char wpa_trace_test_fail_func[256];
+	extern unsigned int wpa_trace_test_fail_after;
+	char *pos;
+
+	wpa_trace_test_fail_after = atoi(cmd);
+	pos = os_strchr(cmd, ':');
+	if (pos) {
+		pos++;
+		os_strlcpy(wpa_trace_test_fail_func, pos,
+			   sizeof(wpa_trace_test_fail_func));
+	} else {
+		wpa_trace_test_fail_after = 0;
+	}
+	return 0;
+#else /* WPA_TRACE_BFD */
+	return -1;
+#endif /* WPA_TRACE_BFD */
+}
+
+
+static int wpas_ctrl_get_fail(struct wpa_supplicant *wpa_s,
+				    char *buf, size_t buflen)
+{
+#ifdef WPA_TRACE_BFD
+	extern char wpa_trace_test_fail_func[256];
+	extern unsigned int wpa_trace_test_fail_after;
+
+	return os_snprintf(buf, buflen, "%u:%s", wpa_trace_test_fail_after,
+			   wpa_trace_test_fail_func);
+#else /* WPA_TRACE_BFD */
+	return -1;
+#endif /* WPA_TRACE_BFD */
+}
+
+
 static void wpas_ctrl_event_test_cb(void *eloop_ctx, void *timeout_ctx)
 {
 	struct wpa_supplicant *wpa_s = eloop_ctx;
@@ -7655,6 +7890,7 @@
 	case VENDOR_ELEM_P2P_INV_REQ:
 	case VENDOR_ELEM_P2P_INV_RESP:
 	case VENDOR_ELEM_P2P_ASSOC_REQ:
+	case VENDOR_ELEM_P2P_ASSOC_RESP:
 		return wpa_s->parent;
 #endif /* CONFIG_P2P */
 	default:
@@ -7983,6 +8219,19 @@
 }
 
 
+static int wpas_ctrl_cmd_debug_level(const char *cmd)
+{
+	if (os_strcmp(cmd, "PING") == 0 ||
+	    os_strncmp(cmd, "BSS ", 4) == 0 ||
+	    os_strncmp(cmd, "GET_NETWORK ", 12) == 0 ||
+	    os_strncmp(cmd, "STATUS", 6) == 0 ||
+	    os_strncmp(cmd, "STA ", 4) == 0 ||
+	    os_strncmp(cmd, "STA-", 4) == 0)
+		return MSG_EXCESSIVE;
+	return MSG_DEBUG;
+}
+
+
 char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s,
 					 char *buf, size_t *resp_len)
 {
@@ -8006,9 +8255,7 @@
 		wpa_hexdump_ascii_key(MSG_DEBUG, "RX ctrl_iface",
 				      (const u8 *) buf, os_strlen(buf));
 	} else {
-		int level = MSG_DEBUG;
-		if (os_strcmp(buf, "PING") == 0)
-			level = MSG_EXCESSIVE;
+		int level = wpas_ctrl_cmd_debug_level(buf);
 		wpa_dbg(wpa_s, level, "Control interface command '%s'", buf);
 	}
 
@@ -8425,6 +8672,9 @@
 	} else if (os_strcmp(buf, "SCAN_RESULTS") == 0) {
 		reply_len = wpa_supplicant_ctrl_iface_scan_results(
 			wpa_s, reply, reply_size);
+	} else if (os_strcmp(buf, "ABORT_SCAN") == 0) {
+		if (wpas_abort_ongoing_scan(wpa_s) < 0)
+			reply_len = -1;
 	} else if (os_strncmp(buf, "SELECT_NETWORK ", 15) == 0) {
 		if (wpa_supplicant_ctrl_iface_select_network(wpa_s, buf + 15))
 			reply_len = -1;
@@ -8447,7 +8697,8 @@
 		reply_len = wpa_supplicant_ctrl_iface_get_network(
 			wpa_s, buf + 12, reply, reply_size);
 	} else if (os_strncmp(buf, "DUP_NETWORK ", 12) == 0) {
-		if (wpa_supplicant_ctrl_iface_dup_network(wpa_s, buf + 12))
+		if (wpa_supplicant_ctrl_iface_dup_network(wpa_s, buf + 12,
+							  wpa_s))
 			reply_len = -1;
 	} else if (os_strcmp(buf, "LIST_CREDS") == 0) {
 		reply_len = wpa_supplicant_ctrl_iface_list_creds(
@@ -8623,6 +8874,11 @@
 			reply_len = -1;
 	} else if (os_strcmp(buf, "GET_ALLOC_FAIL") == 0) {
 		reply_len = wpas_ctrl_get_alloc_fail(wpa_s, reply, reply_size);
+	} else if (os_strncmp(buf, "TEST_FAIL ", 10) == 0) {
+		if (wpas_ctrl_test_fail(wpa_s, buf + 10) < 0)
+			reply_len = -1;
+	} else if (os_strcmp(buf, "GET_FAIL") == 0) {
+		reply_len = wpas_ctrl_get_fail(wpa_s, reply, reply_size);
 	} else if (os_strncmp(buf, "EVENT_TEST ", 11) == 0) {
 		if (wpas_ctrl_event_test(wpa_s, buf + 11) < 0)
 			reply_len = -1;
@@ -8644,6 +8900,9 @@
 	} else if (os_strncmp(buf, "MAC_RAND_SCAN ", 14) == 0) {
 		if (wpas_ctrl_iface_mac_rand_scan(wpa_s, buf + 14))
 			reply_len = -1;
+	} else if (os_strncmp(buf, "GET_PREF_FREQ_LIST ", 19) == 0) {
+		reply_len = wpas_ctrl_iface_get_pref_freq_list(
+			wpa_s, buf + 19, reply, reply_size);
 	} else {
 		os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
 		reply_len = 16;
@@ -8667,10 +8926,11 @@
 	struct wpa_supplicant *wpa_s;
 	unsigned int create_iface = 0;
 	u8 mac_addr[ETH_ALEN];
+	enum wpa_driver_if_type type = WPA_IF_STATION;
 
 	/*
 	 * <ifname>TAB<confname>TAB<driver>TAB<ctrl_interface>TAB<driver_param>
-	 * TAB<bridge_ifname>[TAB<create>]
+	 * TAB<bridge_ifname>[TAB<create>[TAB<interface_type>]]
 	 */
 	wpa_printf(MSG_DEBUG, "CTRL_IFACE GLOBAL INTERFACE_ADD '%s'", cmd);
 
@@ -8738,9 +8998,22 @@
 		if (!extra[0])
 			break;
 
-		if (os_strcmp(extra, "create") == 0)
+		if (os_strcmp(extra, "create") == 0) {
 			create_iface = 1;
-		else {
+			if (!pos)
+				break;
+
+			if (os_strcmp(pos, "sta") == 0) {
+				type = WPA_IF_STATION;
+			} else if (os_strcmp(pos, "ap") == 0) {
+				type = WPA_IF_AP_BSS;
+			} else {
+				wpa_printf(MSG_DEBUG,
+					   "INTERFACE_ADD unsupported interface type: '%s'",
+					   pos);
+				return -1;
+			}
+		} else {
 			wpa_printf(MSG_DEBUG,
 				   "INTERFACE_ADD unsupported extra parameter: '%s'",
 				   extra);
@@ -8753,7 +9026,7 @@
 			   iface.ifname);
 		if (!global->ifaces)
 			return -1;
-		if (wpa_drv_if_add(global->ifaces, WPA_IF_STATION, iface.ifname,
+		if (wpa_drv_if_add(global->ifaces, type, iface.ifname,
 				   NULL, NULL, NULL, mac_addr, NULL) < 0) {
 			wpa_printf(MSG_ERROR,
 				   "CTRL_IFACE interface creation failed");
@@ -9045,6 +9318,41 @@
 }
 
 
+static int wpas_global_ctrl_iface_dup_network(struct wpa_global *global,
+					      char *cmd)
+{
+	struct wpa_supplicant *wpa_s[2]; /* src, dst */
+	char *p;
+	unsigned int i;
+
+	/* cmd: "<src ifname> <dst ifname> <src network id> <dst network id>
+	 * <variable name> */
+
+	for (i = 0; i < ARRAY_SIZE(wpa_s) ; i++) {
+		p = os_strchr(cmd, ' ');
+		if (p == NULL)
+			return -1;
+		*p = '\0';
+
+		wpa_s[i] = global->ifaces;
+		for (; wpa_s[i]; wpa_s[i] = wpa_s[i]->next) {
+			if (os_strcmp(cmd, wpa_s[i]->ifname) == 0)
+				break;
+		}
+
+		if (!wpa_s[i]) {
+			wpa_printf(MSG_DEBUG,
+				   "CTRL_IFACE: Could not find iface=%s", cmd);
+			return -1;
+		}
+
+		cmd = p + 1;
+	}
+
+	return wpa_supplicant_ctrl_iface_dup_network(wpa_s[0], cmd, wpa_s[1]);
+}
+
+
 #ifndef CONFIG_NO_CONFIG_WRITE
 static int wpas_global_ctrl_iface_save_config(struct wpa_global *global)
 {
@@ -9126,6 +9434,59 @@
 }
 
 
+#ifdef CONFIG_FST
+
+static int wpas_global_ctrl_iface_fst_attach(struct wpa_global *global,
+					     char *cmd, char *buf,
+					     size_t reply_size)
+{
+	char ifname[IFNAMSIZ + 1];
+	struct fst_iface_cfg cfg;
+	struct wpa_supplicant *wpa_s;
+	struct fst_wpa_obj iface_obj;
+
+	if (!fst_parse_attach_command(cmd, ifname, sizeof(ifname), &cfg)) {
+		wpa_s = wpa_supplicant_get_iface(global, ifname);
+		if (wpa_s) {
+			if (wpa_s->fst) {
+				wpa_printf(MSG_INFO, "FST: Already attached");
+				return -1;
+			}
+			fst_wpa_supplicant_fill_iface_obj(wpa_s, &iface_obj);
+			wpa_s->fst = fst_attach(ifname, wpa_s->own_addr,
+						&iface_obj, &cfg);
+			if (wpa_s->fst)
+				return os_snprintf(buf, reply_size, "OK\n");
+		}
+	}
+
+	return -1;
+}
+
+
+static int wpas_global_ctrl_iface_fst_detach(struct wpa_global *global,
+					     char *cmd, char *buf,
+					     size_t reply_size)
+{
+	char ifname[IFNAMSIZ + 1];
+	struct wpa_supplicant *wpa_s;
+
+	if (!fst_parse_detach_command(cmd, ifname, sizeof(ifname))) {
+		wpa_s = wpa_supplicant_get_iface(global, ifname);
+		if (wpa_s) {
+			if (!fst_iface_detach(ifname)) {
+				wpa_s->fst = NULL;
+				return os_snprintf(buf, reply_size, "OK\n");
+			}
+		}
+	}
+
+	return -1;
+}
+
+#endif /* CONFIG_FST */
+
+
 char * wpa_supplicant_global_ctrl_iface_process(struct wpa_global *global,
 						char *buf, size_t *resp_len)
 {
@@ -9177,6 +9538,18 @@
 	} else if (os_strcmp(buf, "INTERFACES") == 0) {
 		reply_len = wpa_supplicant_global_iface_interfaces(
 			global, reply, reply_size);
+#ifdef CONFIG_FST
+	} else if (os_strncmp(buf, "FST-ATTACH ", 11) == 0) {
+		reply_len = wpas_global_ctrl_iface_fst_attach(global, buf + 11,
+							      reply,
+							      reply_size);
+	} else if (os_strncmp(buf, "FST-DETACH ", 11) == 0) {
+		reply_len = wpas_global_ctrl_iface_fst_detach(global, buf + 11,
+							      reply,
+							      reply_size);
+	} else if (os_strncmp(buf, "FST-MANAGER ", 12) == 0) {
+		reply_len = fst_ctrl_iface_receive(buf + 12, reply, reply_size);
+#endif /* CONFIG_FST */
 	} else if (os_strcmp(buf, "TERMINATE") == 0) {
 		wpa_supplicant_terminate_proc(global);
 	} else if (os_strcmp(buf, "SUSPEND") == 0) {
@@ -9197,6 +9570,9 @@
 #endif /* CONFIG_P2P */
 			reply_len = -1;
 		}
+	} else if (os_strncmp(buf, "DUP_NETWORK ", 12) == 0) {
+		if (wpas_global_ctrl_iface_dup_network(global, buf + 12))
+			reply_len = -1;
 #ifndef CONFIG_NO_CONFIG_WRITE
 	} else if (os_strcmp(buf, "SAVE_CONFIG") == 0) {
 		if (wpas_global_ctrl_iface_save_config(global))
diff --git a/wpa_supplicant/ctrl_iface_unix.c b/wpa_supplicant/ctrl_iface_unix.c
index 160a6f0..2c71b2d 100644
--- a/wpa_supplicant/ctrl_iface_unix.c
+++ b/wpa_supplicant/ctrl_iface_unix.c
@@ -95,7 +95,7 @@
 #ifdef __linux__
 	socklen_t optlen;
 	int sndbuf, outq;
-	int level = MSG_DEBUG;
+	int level = MSG_MSGDUMP;
 
 	if (len >= 5 && os_strncmp(buf, "PONG\n", 5) == 0)
 		level = MSG_EXCESSIVE;
@@ -774,6 +774,53 @@
 	if (wpa_s->conf->ctrl_interface == NULL)
 		return priv;
 
+#ifdef ANDROID
+	if (wpa_s->global->params.ctrl_interface) {
+		int same = 0;
+
+		if (wpa_s->global->params.ctrl_interface[0] == '/') {
+			if (os_strcmp(wpa_s->global->params.ctrl_interface,
+				      wpa_s->conf->ctrl_interface) == 0)
+				same = 1;
+		} else if (os_strncmp(wpa_s->global->params.ctrl_interface,
+				      "@android:", 9) == 0 ||
+			   os_strncmp(wpa_s->global->params.ctrl_interface,
+				      "@abstract:", 10) == 0) {
+			char *pos;
+
+			/*
+			 * Currently, Android uses @android:wpa_* as the naming
+			 * convention for the global ctrl interface. This logic
+			 * needs to be revisited if the above naming convention
+			 * is modified.
+			 */
+			pos = os_strchr(wpa_s->global->params.ctrl_interface,
+					'_');
+			if (pos &&
+			    os_strcmp(pos + 1,
+				      wpa_s->conf->ctrl_interface) == 0)
+				same = 1;
+		}
+
+		if (same) {
+			/*
+			 * The invalid configuration combination might be
+			 * possible to hit in an Android OTA upgrade case, so
+			 * instead of refusing to start the wpa_supplicant
+			 * process, do not open the per-interface ctrl_iface
+			 * and continue with the global control interface that
+			 * was set from the command line since the Wi-Fi
+			 * framework will use it for operations.
+			 */
+			wpa_printf(MSG_ERROR,
+				   "global ctrl interface %s matches ctrl interface %s - do not open per-interface ctrl interface",
+				   wpa_s->global->params.ctrl_interface,
+				   wpa_s->conf->ctrl_interface);
+			return priv;
+		}
+	}
+#endif /* ANDROID */
+
 	if (wpas_ctrl_iface_open_sock(wpa_s, priv) < 0) {
 		os_free(priv);
 		return NULL;
@@ -869,8 +916,10 @@
 
 free_dst:
 	dl_list_for_each_safe(dst, prev, &priv->ctrl_dst, struct wpa_ctrl_dst,
-			      list)
+			      list) {
+		dl_list_del(&dst->list);
 		os_free(dst);
+	}
 	dl_list_for_each_safe(msg, prev_msg, &priv->msg_queue,
 			      struct ctrl_iface_msg, list) {
 		dl_list_del(&msg->list);
@@ -958,7 +1007,8 @@
 		msg.msg_namelen = dst->addrlen;
 		wpas_ctrl_sock_debug("ctrl_sock-sendmsg", sock, buf, len);
 		if (sendmsg(sock, &msg, MSG_DONTWAIT) >= 0) {
-			wpa_printf(MSG_DEBUG, "CTRL_IFACE monitor sent successfully to %s",
+			wpa_printf(MSG_MSGDUMP,
+				   "CTRL_IFACE monitor sent successfully to %s",
 				   addr_txt);
 			dst->errors = 0;
 			continue;
@@ -1374,8 +1424,10 @@
 	if (priv->global->params.ctrl_interface)
 		unlink(priv->global->params.ctrl_interface);
 	dl_list_for_each_safe(dst, prev, &priv->ctrl_dst, struct wpa_ctrl_dst,
-			      list)
+			      list) {
+		dl_list_del(&dst->list);
 		os_free(dst);
+	}
 	dl_list_for_each_safe(msg, prev_msg, &priv->msg_queue,
 			      struct ctrl_iface_msg, list) {
 		dl_list_del(&msg->list);
diff --git a/wpa_supplicant/dbus/dbus_common_i.h b/wpa_supplicant/dbus/dbus_common_i.h
index a551ccd..95eb4bc 100644
--- a/wpa_supplicant/dbus/dbus_common_i.h
+++ b/wpa_supplicant/dbus/dbus_common_i.h
@@ -13,6 +13,8 @@
 
 #include <dbus/dbus.h>
 
+struct wpa_dbus_property_desc;
+
 struct wpas_dbus_priv {
 	DBusConnection *con;
 	int should_dispatch;
@@ -20,9 +22,13 @@
 	u32 next_objid;
 	int dbus_new_initialized;
 
-#if defined(CONFIG_CTRL_IFACE_DBUS_NEW) && defined(CONFIG_AP)
+#if defined(CONFIG_CTRL_IFACE_DBUS_NEW)
+	struct wpa_dbus_property_desc *all_interface_properties;
+	int globals_start;
+#if defined(CONFIG_AP)
 	int dbus_noc_refcnt;
-#endif /* CONFIG_CTRL_IFACE_DBUS_NEW && CONFIG_AP */
+#endif /* CONFIG_AP */
+#endif /* CONFIG_CTRL_IFACE_DBUS_NEW */
 };
 
 #endif /* DBUS_COMMON_I_H */
diff --git a/wpa_supplicant/dbus/dbus_dict_helpers.c b/wpa_supplicant/dbus/dbus_dict_helpers.c
index a0c44eb..e4e9b8d 100644
--- a/wpa_supplicant/dbus/dbus_dict_helpers.c
+++ b/wpa_supplicant/dbus/dbus_dict_helpers.c
@@ -205,24 +205,6 @@
 
 
 /**
- * Add a byte entry to the dict.
- *
- * @param iter_dict A valid DBusMessageIter returned from
- *    wpa_dbus_dict_open_write()
- * @param key The key of the dict item
- * @param value The byte value
- * @return TRUE on success, FALSE on failure
- *
- */
-dbus_bool_t wpa_dbus_dict_append_byte(DBusMessageIter *iter_dict,
-				      const char *key, const char value)
-{
-	return _wpa_dbus_add_dict_entry_basic(iter_dict, key, DBUS_TYPE_BYTE,
-					      &value);
-}
-
-
-/**
  * Add a boolean entry to the dict.
  *
  * @param iter_dict A valid DBusMessageIter returned from
@@ -317,62 +299,6 @@
 
 
 /**
- * Add a 64-bit integer entry to the dict.
- *
- * @param iter_dict A valid DBusMessageIter returned from
- *    wpa_dbus_dict_open_write()
- * @param key The key of the dict item
- * @param value The 64-bit integer value
- * @return TRUE on success, FALSE on failure
- *
- */
-dbus_bool_t wpa_dbus_dict_append_int64(DBusMessageIter *iter_dict,
-				       const char *key,
-				       const dbus_int64_t value)
-{
-	return _wpa_dbus_add_dict_entry_basic(iter_dict, key, DBUS_TYPE_INT64,
-					      &value);
-}
-
-
-/**
- * Add a 64-bit unsigned integer entry to the dict.
- *
- * @param iter_dict A valid DBusMessageIter returned from
- *    wpa_dbus_dict_open_write()
- * @param key The key of the dict item
- * @param value The 64-bit unsigned integer value
- * @return TRUE on success, FALSE on failure
- *
- */
-dbus_bool_t wpa_dbus_dict_append_uint64(DBusMessageIter *iter_dict,
-					const char *key,
-					const dbus_uint64_t value)
-{
-	return _wpa_dbus_add_dict_entry_basic(iter_dict, key, DBUS_TYPE_UINT64,
-					      &value);
-}
-
-
-/**
- * Add a double-precision floating point entry to the dict.
- *
- * @param iter_dict A valid DBusMessageIter returned from
- *    wpa_dbus_dict_open_write()
- * @param key The key of the dict item
- * @param value The double-precision floating point value
- * @return TRUE on success, FALSE on failure
- *
- */
-dbus_bool_t wpa_dbus_dict_append_double(DBusMessageIter *iter_dict,
-					const char *key, const double value)
-{
-	return _wpa_dbus_add_dict_entry_basic(iter_dict, key, DBUS_TYPE_DOUBLE,
-					      &value);
-}
-
-
-/**
  * Add a DBus object path entry to the dict.
  *
  * @param iter_dict A valid DBusMessageIter returned from
diff --git a/wpa_supplicant/dbus/dbus_dict_helpers.h b/wpa_supplicant/dbus/dbus_dict_helpers.h
index b068431..94a0efd 100644
--- a/wpa_supplicant/dbus/dbus_dict_helpers.h
+++ b/wpa_supplicant/dbus/dbus_dict_helpers.h
@@ -26,9 +26,6 @@
 dbus_bool_t wpa_dbus_dict_append_string(DBusMessageIter *iter_dict,
 					const char *key, const char *value);
 
-dbus_bool_t wpa_dbus_dict_append_byte(DBusMessageIter *iter_dict,
-				      const char *key, const char value);
-
 dbus_bool_t wpa_dbus_dict_append_bool(DBusMessageIter *iter_dict,
 				      const char *key,
 				      const dbus_bool_t value);
@@ -49,18 +46,6 @@
 					const char *key,
 					const dbus_uint32_t value);
 
-dbus_bool_t wpa_dbus_dict_append_int64(DBusMessageIter *iter_dict,
-				       const char *key,
-				       const dbus_int64_t value);
-
-dbus_bool_t wpa_dbus_dict_append_uint64(DBusMessageIter *iter_dict,
-					const char *key,
-					const dbus_uint64_t value);
-
-dbus_bool_t wpa_dbus_dict_append_double(DBusMessageIter *iter_dict,
-					const char *key,
-					const double value);
-
 dbus_bool_t wpa_dbus_dict_append_object_path(DBusMessageIter *iter_dict,
 					     const char *key,
 					     const char *value);
diff --git a/wpa_supplicant/dbus/dbus_new.c b/wpa_supplicant/dbus/dbus_new.c
index 1959ea7..440b8cf 100644
--- a/wpa_supplicant/dbus/dbus_new.c
+++ b/wpa_supplicant/dbus/dbus_new.c
@@ -633,6 +633,10 @@
 	if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &key) ||
 	    !wpa_dbus_dict_open_write(&iter, &dict_iter) ||
 	    !wpa_dbus_dict_append_int32(&dict_iter, "msg", fail->msg) ||
+	    !wpa_dbus_dict_append_int32(&dict_iter, "config_error",
+					fail->config_error) ||
+	    !wpa_dbus_dict_append_int32(&dict_iter, "error_indication",
+					fail->error_indication) ||
 	    !wpa_dbus_dict_close_write(&iter, &dict_iter))
 		wpa_printf(MSG_ERROR, "dbus: Failed to construct signal");
 	else
@@ -1857,6 +1861,99 @@
 	dbus_message_unref(msg);
 }
 
+
+/**
+ * wpas_dbus_signal_p2p_group_formation_failure - Signals GroupFormationFailure event
+ * @wpa_s: %wpa_supplicant network interface data
+ * @reason: indicates the reason code for group formation failure
+ *
+ * Sends Event dbus signal and string reason code when available.
+ */
+void wpas_dbus_signal_p2p_group_formation_failure(struct wpa_supplicant *wpa_s,
+						  const char *reason)
+{
+	DBusMessage *msg;
+	struct wpas_dbus_priv *iface;
+
+	iface = wpa_s->global->dbus;
+
+	/* Do nothing if the control interface is not turned on */
+	if (iface == NULL)
+		return;
+
+	msg = dbus_message_new_signal(wpa_s->dbus_new_path,
+				      WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+				      "GroupFormationFailure");
+	if (msg == NULL)
+		return;
+
+	if (dbus_message_append_args(msg, DBUS_TYPE_STRING, &reason,
+				     DBUS_TYPE_INVALID))
+		dbus_connection_send(iface->con, msg, NULL);
+	else
+		wpa_printf(MSG_ERROR, "dbus: Failed to construct signal");
+
+	dbus_message_unref(msg);
+}
+
+
+/**
+ * wpas_dbus_signal_p2p_invitation_received - Emit InvitationReceived signal
+ * @wpa_s: %wpa_supplicant network interface data
+ * @sa: Source address of the Invitation Request
+ * @dev_add: GO Device Address
+ * @bssid: P2P Group BSSID or %NULL if not received
+ * @id: Persistent group id or %0 if not persistent group
+ * @op_freq: Operating frequency for the group
+ */
+
+void wpas_dbus_signal_p2p_invitation_received(struct wpa_supplicant *wpa_s,
+					      const u8 *sa, const u8 *dev_addr,
+					      const u8 *bssid, int id,
+					      int op_freq)
+{
+	DBusMessage *msg;
+	DBusMessageIter iter, dict_iter;
+	struct wpas_dbus_priv *iface;
+
+	iface = wpa_s->global->dbus;
+
+	/* Do nothing if the control interface is not turned on */
+	if (iface == NULL)
+		return;
+
+	msg = dbus_message_new_signal(wpa_s->dbus_new_path,
+				      WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+				      "InvitationReceived");
+	if (msg == NULL)
+		return;
+
+	dbus_message_iter_init_append(msg, &iter);
+	if (!wpa_dbus_dict_open_write(&iter, &dict_iter) ||
+	    (sa &&
+	     !wpa_dbus_dict_append_byte_array(&dict_iter, "sa",
+					      (const char *) sa, ETH_ALEN)) ||
+	    (dev_addr &&
+	     !wpa_dbus_dict_append_byte_array(&dict_iter, "go_dev_addr",
+					      (const char *) dev_addr,
+					      ETH_ALEN)) ||
+	    (bssid &&
+	     !wpa_dbus_dict_append_byte_array(&dict_iter, "bssid",
+					      (const char *) bssid,
+					      ETH_ALEN)) ||
+	    (id &&
+	     !wpa_dbus_dict_append_int32(&dict_iter, "persistent_id", id)) ||
+	    !wpa_dbus_dict_append_int32(&dict_iter, "op_freq", op_freq) ||
+	    !wpa_dbus_dict_close_write(&iter, &dict_iter)) {
+		dbus_message_unref(msg);
+		return;
+	}
+
+	dbus_connection_send(iface->con, msg, NULL);
+	dbus_message_unref(msg);
+}
+
+
 #endif /* CONFIG_P2P */
 
 
@@ -2076,41 +2173,54 @@
 		  END_ARGS
 	  }
 	},
+	{ "ExpectDisconnect", WPAS_DBUS_NEW_INTERFACE,
+	  (WPADBusMethodHandler) wpas_dbus_handler_expect_disconnect,
+	  {
+		END_ARGS
+	  }
+	},
 	{ NULL, NULL, NULL, { END_ARGS } }
 };
 
 static const struct wpa_dbus_property_desc wpas_dbus_global_properties[] = {
 	{ "DebugLevel", WPAS_DBUS_NEW_INTERFACE, "s",
 	  wpas_dbus_getter_debug_level,
-	  wpas_dbus_setter_debug_level
+	  wpas_dbus_setter_debug_level,
+	  NULL
 	},
 	{ "DebugTimestamp", WPAS_DBUS_NEW_INTERFACE, "b",
 	  wpas_dbus_getter_debug_timestamp,
-	  wpas_dbus_setter_debug_timestamp
+	  wpas_dbus_setter_debug_timestamp,
+	  NULL
 	},
 	{ "DebugShowKeys", WPAS_DBUS_NEW_INTERFACE, "b",
 	  wpas_dbus_getter_debug_show_keys,
-	  wpas_dbus_setter_debug_show_keys
+	  wpas_dbus_setter_debug_show_keys,
+	  NULL
 	},
 	{ "Interfaces", WPAS_DBUS_NEW_INTERFACE, "ao",
 	  wpas_dbus_getter_interfaces,
+	  NULL,
 	  NULL
 	},
 	{ "EapMethods", WPAS_DBUS_NEW_INTERFACE, "as",
 	  wpas_dbus_getter_eap_methods,
+	  NULL,
 	  NULL
 	},
 	{ "Capabilities", WPAS_DBUS_NEW_INTERFACE, "as",
 	  wpas_dbus_getter_global_capabilities,
+	  NULL,
 	  NULL
 	},
 #ifdef CONFIG_WIFI_DISPLAY
 	{ "WFDIEs", WPAS_DBUS_NEW_INTERFACE, "ay",
 	  wpas_dbus_getter_global_wfd_ies,
-	  wpas_dbus_setter_global_wfd_ies
+	  wpas_dbus_setter_global_wfd_ies,
+	  NULL
 	},
 #endif /* CONFIG_WIFI_DISPLAY */
-	{ NULL, NULL, NULL, NULL, NULL }
+	{ NULL, NULL, NULL, NULL, NULL, NULL }
 };
 
 static const struct wpa_dbus_signal_desc wpas_dbus_global_signals[] = {
@@ -2138,12 +2248,50 @@
 };
 
 
+static char * uscore_to_dbus(const char *uscore)
+{
+	const char *p = uscore;
+	char *str, *s;
+	dbus_bool_t last_was_uscore = TRUE;
+
+	s = str = os_zalloc(os_strlen(uscore) + 1);
+	if (!str)
+		return NULL;
+	while (p && *p) {
+		if (*p == '_') {
+			last_was_uscore = TRUE;
+		} else {
+			*s++ = last_was_uscore ? toupper(*p) : *p;
+			last_was_uscore = FALSE;
+		}
+		p++;
+	}
+
+	return str;
+}
+
+
+static int wpa_dbus_ctrl_iface_props_init(struct wpas_dbus_priv *priv);
+
+
+static void wpa_dbus_ctrl_iface_props_deinit(struct wpas_dbus_priv *priv)
+{
+	int idx = priv->globals_start;
+
+	/* Free all allocated property values */
+	while (priv->all_interface_properties[idx].dbus_property)
+		os_free((char *)
+			priv->all_interface_properties[idx++].dbus_property);
+	os_free((char *) priv->all_interface_properties);
+}
+
+
 /**
  * wpas_dbus_ctrl_iface_init - Initialize dbus control interface
  * @global: Pointer to global data from wpa_supplicant_init()
  * Returns: 0 on success or -1 on failure
  *
- * Initialize the dbus control interface for wpa_supplicantand and start
+ * Initialize the dbus control interface for wpa_supplicant and start
  * receiving commands from external programs over the bus.
  */
 int wpas_dbus_ctrl_iface_init(struct wpas_dbus_priv *priv)
@@ -2151,11 +2299,18 @@
 	struct wpa_dbus_object_desc *obj_desc;
 	int ret;
 
+	ret = wpa_dbus_ctrl_iface_props_init(priv);
+	if (ret < 0) {
+		wpa_printf(MSG_ERROR,
+			   "dbus: Not enough memory to init interface properties");
+		return -1;
+	}
+
 	obj_desc = os_zalloc(sizeof(struct wpa_dbus_object_desc));
 	if (!obj_desc) {
 		wpa_printf(MSG_ERROR,
 			   "Not enough memory to create object description");
-		return -1;
+		goto error;
 	}
 
 	wpas_dbus_register(obj_desc, priv->global, NULL,
@@ -2168,31 +2323,36 @@
 	ret = wpa_dbus_ctrl_iface_init(priv, WPAS_DBUS_NEW_PATH,
 				       WPAS_DBUS_NEW_SERVICE,
 				       obj_desc);
-	if (ret < 0)
+	if (ret < 0) {
 		free_dbus_object_desc(obj_desc);
-	else
-		priv->dbus_new_initialized = 1;
+		goto error;
+	}
 
-	return ret;
+	priv->dbus_new_initialized = 1;
+	return 0;
+
+error:
+	wpa_dbus_ctrl_iface_props_deinit(priv);
+	return -1;
 }
 
 
 /**
  * wpas_dbus_ctrl_iface_deinit - Deinitialize dbus ctrl interface for
  * wpa_supplicant
- * @iface: Pointer to dbus private data from wpas_dbus_init()
+ * @priv: Pointer to dbus private data from wpas_dbus_init()
  *
  * Deinitialize the dbus control interface that was initialized with
  * wpas_dbus_ctrl_iface_init().
  */
-void wpas_dbus_ctrl_iface_deinit(struct wpas_dbus_priv *iface)
+void wpas_dbus_ctrl_iface_deinit(struct wpas_dbus_priv *priv)
 {
-	if (!iface->dbus_new_initialized)
+	if (!priv->dbus_new_initialized)
 		return;
 	wpa_printf(MSG_DEBUG, "dbus: Unregister D-Bus object '%s'",
 		   WPAS_DBUS_NEW_PATH);
-	dbus_connection_unregister_object_path(iface->con,
-					       WPAS_DBUS_NEW_PATH);
+	dbus_connection_unregister_object_path(priv->con, WPAS_DBUS_NEW_PATH);
+	wpa_dbus_ctrl_iface_props_deinit(priv);
 }
 
 
@@ -2205,13 +2365,15 @@
 static const struct wpa_dbus_property_desc wpas_dbus_network_properties[] = {
 	{ "Properties", WPAS_DBUS_NEW_IFACE_NETWORK, "a{sv}",
 	  wpas_dbus_getter_network_properties,
-	  wpas_dbus_setter_network_properties
+	  wpas_dbus_setter_network_properties,
+	  NULL
 	},
 	{ "Enabled", WPAS_DBUS_NEW_IFACE_NETWORK, "b",
 	  wpas_dbus_getter_enabled,
-	  wpas_dbus_setter_enabled
+	  wpas_dbus_setter_enabled,
+	  NULL
 	},
-	{ NULL, NULL, NULL, NULL, NULL }
+	{ NULL, NULL, NULL, NULL, NULL, NULL }
 };
 
 
@@ -2350,53 +2512,65 @@
 static const struct wpa_dbus_property_desc wpas_dbus_bss_properties[] = {
 	{ "SSID", WPAS_DBUS_NEW_IFACE_BSS, "ay",
 	  wpas_dbus_getter_bss_ssid,
+	  NULL,
 	  NULL
 	},
 	{ "BSSID", WPAS_DBUS_NEW_IFACE_BSS, "ay",
 	  wpas_dbus_getter_bss_bssid,
+	  NULL,
 	  NULL
 	},
 	{ "Privacy", WPAS_DBUS_NEW_IFACE_BSS, "b",
 	  wpas_dbus_getter_bss_privacy,
+	  NULL,
 	  NULL
 	},
 	{ "Mode", WPAS_DBUS_NEW_IFACE_BSS, "s",
 	  wpas_dbus_getter_bss_mode,
+	  NULL,
 	  NULL
 	},
 	{ "Signal", WPAS_DBUS_NEW_IFACE_BSS, "n",
 	  wpas_dbus_getter_bss_signal,
+	  NULL,
 	  NULL
 	},
 	{ "Frequency", WPAS_DBUS_NEW_IFACE_BSS, "q",
 	  wpas_dbus_getter_bss_frequency,
+	  NULL,
 	  NULL
 	},
 	{ "Rates", WPAS_DBUS_NEW_IFACE_BSS, "au",
 	  wpas_dbus_getter_bss_rates,
+	  NULL,
 	  NULL
 	},
 	{ "WPA", WPAS_DBUS_NEW_IFACE_BSS, "a{sv}",
 	  wpas_dbus_getter_bss_wpa,
+	  NULL,
 	  NULL
 	},
 	{ "RSN", WPAS_DBUS_NEW_IFACE_BSS, "a{sv}",
 	  wpas_dbus_getter_bss_rsn,
+	  NULL,
 	  NULL
 	},
 	{ "WPS", WPAS_DBUS_NEW_IFACE_BSS, "a{sv}",
 	  wpas_dbus_getter_bss_wps,
+	  NULL,
 	  NULL
 	},
 	{ "IEs", WPAS_DBUS_NEW_IFACE_BSS, "ay",
 	  wpas_dbus_getter_bss_ies,
+	  NULL,
 	  NULL
 	},
 	{ "Age", WPAS_DBUS_NEW_IFACE_BSS, "u",
 	  wpas_dbus_getter_bss_age,
+	  NULL,
 	  NULL
 	},
-	{ NULL, NULL, NULL, NULL, NULL }
+	{ NULL, NULL, NULL, NULL, NULL, NULL }
 };
 
 
@@ -2896,131 +3070,168 @@
 	  }
 	},
 #endif /* CONFIG_TDLS */
+#ifndef CONFIG_NO_CONFIG_WRITE
+	{ "SaveConfig", WPAS_DBUS_NEW_IFACE_INTERFACE,
+	  (WPADBusMethodHandler) wpas_dbus_handler_save_config,
+	  {
+		  END_ARGS
+	  }
+	},
+#endif /* CONFIG_NO_CONFIG_WRITE */
 	{ NULL, NULL, NULL, { END_ARGS } }
 };
 
 static const struct wpa_dbus_property_desc wpas_dbus_interface_properties[] = {
 	{ "Capabilities", WPAS_DBUS_NEW_IFACE_INTERFACE, "a{sv}",
 	  wpas_dbus_getter_capabilities,
+	  NULL,
 	  NULL
 	},
 	{ "State", WPAS_DBUS_NEW_IFACE_INTERFACE, "s",
 	  wpas_dbus_getter_state,
+	  NULL,
 	  NULL
 	},
 	{ "Scanning", WPAS_DBUS_NEW_IFACE_INTERFACE, "b",
 	  wpas_dbus_getter_scanning,
+	  NULL,
 	  NULL
 	},
 	{ "ApScan", WPAS_DBUS_NEW_IFACE_INTERFACE, "u",
 	  wpas_dbus_getter_ap_scan,
-	  wpas_dbus_setter_ap_scan
+	  wpas_dbus_setter_ap_scan,
+	  NULL
 	},
 	{ "BSSExpireAge", WPAS_DBUS_NEW_IFACE_INTERFACE, "u",
 	  wpas_dbus_getter_bss_expire_age,
-	  wpas_dbus_setter_bss_expire_age
+	  wpas_dbus_setter_bss_expire_age,
+	  NULL
 	},
 	{ "BSSExpireCount", WPAS_DBUS_NEW_IFACE_INTERFACE, "u",
 	  wpas_dbus_getter_bss_expire_count,
-	  wpas_dbus_setter_bss_expire_count
+	  wpas_dbus_setter_bss_expire_count,
+	  NULL
 	},
 	{ "Country", WPAS_DBUS_NEW_IFACE_INTERFACE, "s",
 	  wpas_dbus_getter_country,
-	  wpas_dbus_setter_country
+	  wpas_dbus_setter_country,
+	  NULL
 	},
 	{ "Ifname", WPAS_DBUS_NEW_IFACE_INTERFACE, "s",
 	  wpas_dbus_getter_ifname,
+	  NULL,
 	  NULL
 	},
 	{ "Driver", WPAS_DBUS_NEW_IFACE_INTERFACE, "s",
 	  wpas_dbus_getter_driver,
+	  NULL,
 	  NULL
 	},
 	{ "BridgeIfname", WPAS_DBUS_NEW_IFACE_INTERFACE, "s",
 	  wpas_dbus_getter_bridge_ifname,
+	  NULL,
 	  NULL
 	},
 	{ "CurrentBSS", WPAS_DBUS_NEW_IFACE_INTERFACE, "o",
 	  wpas_dbus_getter_current_bss,
+	  NULL,
 	  NULL
 	},
 	{ "CurrentNetwork", WPAS_DBUS_NEW_IFACE_INTERFACE, "o",
 	  wpas_dbus_getter_current_network,
+	  NULL,
 	  NULL
 	},
 	{ "CurrentAuthMode", WPAS_DBUS_NEW_IFACE_INTERFACE, "s",
 	  wpas_dbus_getter_current_auth_mode,
+	  NULL,
 	  NULL
 	},
 	{ "Blobs", WPAS_DBUS_NEW_IFACE_INTERFACE, "a{say}",
 	  wpas_dbus_getter_blobs,
+	  NULL,
 	  NULL
 	},
 	{ "BSSs", WPAS_DBUS_NEW_IFACE_INTERFACE, "ao",
 	  wpas_dbus_getter_bsss,
+	  NULL,
 	  NULL
 	},
 	{ "Networks", WPAS_DBUS_NEW_IFACE_INTERFACE, "ao",
 	  wpas_dbus_getter_networks,
+	  NULL,
 	  NULL
 	},
 	{ "FastReauth", WPAS_DBUS_NEW_IFACE_INTERFACE, "b",
 	  wpas_dbus_getter_fast_reauth,
-	  wpas_dbus_setter_fast_reauth
+	  wpas_dbus_setter_fast_reauth,
+	  NULL
 	},
 	{ "ScanInterval", WPAS_DBUS_NEW_IFACE_INTERFACE, "i",
 	  wpas_dbus_getter_scan_interval,
-	  wpas_dbus_setter_scan_interval
+	  wpas_dbus_setter_scan_interval,
+	  NULL
 	},
 	{ "PKCS11EnginePath", WPAS_DBUS_NEW_IFACE_INTERFACE, "s",
 	  wpas_dbus_getter_pkcs11_engine_path,
+	  NULL,
 	  NULL
 	},
 	{ "PKCS11ModulePath", WPAS_DBUS_NEW_IFACE_INTERFACE, "s",
 	  wpas_dbus_getter_pkcs11_module_path,
+	  NULL,
 	  NULL
 	},
 #ifdef CONFIG_WPS
 	{ "ProcessCredentials", WPAS_DBUS_NEW_IFACE_WPS, "b",
 	  wpas_dbus_getter_process_credentials,
-	  wpas_dbus_setter_process_credentials
+	  wpas_dbus_setter_process_credentials,
+	  NULL
 	},
 	{ "ConfigMethods", WPAS_DBUS_NEW_IFACE_WPS, "s",
 	  wpas_dbus_getter_config_methods,
-	  wpas_dbus_setter_config_methods
+	  wpas_dbus_setter_config_methods,
+	  NULL
 	},
 #endif /* CONFIG_WPS */
 #ifdef CONFIG_P2P
 	{ "P2PDeviceConfig", WPAS_DBUS_NEW_IFACE_P2PDEVICE, "a{sv}",
 	  wpas_dbus_getter_p2p_device_config,
-	  wpas_dbus_setter_p2p_device_config
+	  wpas_dbus_setter_p2p_device_config,
+	  NULL
 	},
 	{ "Peers", WPAS_DBUS_NEW_IFACE_P2PDEVICE, "ao",
 	  wpas_dbus_getter_p2p_peers,
+	  NULL,
 	  NULL
 	},
 	{ "Role", WPAS_DBUS_NEW_IFACE_P2PDEVICE, "s",
 	  wpas_dbus_getter_p2p_role,
+	  NULL,
 	  NULL
 	},
 	{ "Group", WPAS_DBUS_NEW_IFACE_P2PDEVICE, "o",
 	  wpas_dbus_getter_p2p_group,
+	  NULL,
 	  NULL
 	},
 	{ "PeerGO", WPAS_DBUS_NEW_IFACE_P2PDEVICE, "o",
 	  wpas_dbus_getter_p2p_peergo,
+	  NULL,
 	  NULL
 	},
 	{ "PersistentGroups", WPAS_DBUS_NEW_IFACE_P2PDEVICE, "ao",
 	  wpas_dbus_getter_persistent_groups,
+	  NULL,
 	  NULL
 	},
 #endif /* CONFIG_P2P */
 	{ "DisconnectReason", WPAS_DBUS_NEW_IFACE_INTERFACE, "i",
 	  wpas_dbus_getter_disconnect_reason,
+	  NULL,
 	  NULL
 	},
-	{ NULL, NULL, NULL, NULL, NULL }
+	{ NULL, NULL, NULL, NULL, NULL, NULL }
 };
 
 static const struct wpa_dbus_signal_desc wpas_dbus_interface_signals[] = {
@@ -3172,6 +3383,12 @@
 		  END_ARGS
 	  }
 	},
+	{ "GroupFormationFailure", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+	  {
+		  { "reason", "s", ARG_OUT },
+		  END_ARGS
+	  }
+	},
 	{ "GONegotiationSuccess", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
 	  {
 		  { "properties", "a{sv}", ARG_OUT },
@@ -3187,7 +3404,7 @@
 	{ "GONegotiationRequest", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
 	  {
 		  { "path", "o", ARG_OUT },
-		  { "dev_passwd_id", "i", ARG_OUT },
+		  { "dev_passwd_id", "q", ARG_OUT },
 		  { "device_go_intent", "y", ARG_OUT },
 		  END_ARGS
 	  }
@@ -3236,6 +3453,12 @@
 		  END_ARGS
 	  }
 	},
+	{ "InvitationReceived", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+	  {
+		  { "properties", "a{sv}", ARG_OUT },
+		  END_ARGS
+	  }
+	},
 #endif /* CONFIG_P2P */
 #ifdef CONFIG_AP
 	{ "ProbeRequest", WPAS_DBUS_NEW_IFACE_INTERFACE,
@@ -3282,6 +3505,77 @@
 };
 
 
+static int wpa_dbus_ctrl_iface_props_init(struct wpas_dbus_priv *priv)
+{
+	size_t all_size;
+	unsigned int i, j, count, num_const, num_globals;
+	const char *global_name;
+	static const char * const ignored_globals[] = {
+		"bss_expiration_age", "bss_expiration_scan_count",
+		"ap_scan", "country", "fast_reauth",
+		"pkcs11_engine_path", "pkcs11_module_path"
+	};
+
+	/* wpas_dbus_interface_properties terminates with a NULL element */
+	num_const = ARRAY_SIZE(wpas_dbus_interface_properties) - 1;
+
+	num_globals = wpa_config_get_num_global_field_names();
+	priv->globals_start = num_const;
+
+	/* allocate enough for all properties + terminating NULL element */
+	all_size = (num_globals + num_const + 1) *
+		sizeof(wpas_dbus_interface_properties[0]);
+	priv->all_interface_properties = os_zalloc(all_size);
+	if (!priv->all_interface_properties) {
+		wpa_printf(MSG_ERROR,
+			   "dbus: Not enough memory for interface properties");
+		return -1;
+	}
+
+	/* Copy constant interface properties to the start of the array */
+	os_memcpy(priv->all_interface_properties,
+		  wpas_dbus_interface_properties,
+		  sizeof(wpas_dbus_interface_properties));
+
+	/* Dynamically construct interface global properties */
+	for (i = 0, count = num_const; i < num_globals; i++) {
+		struct wpa_dbus_property_desc *desc;
+		int no_var = 0;
+
+		/* ignore globals that are actually just methods */
+		global_name = wpa_config_get_global_field_name(i, &no_var);
+		if (no_var)
+			continue;
+		/* Ignore fields already explicitly exposed */
+		for (j = 0; j < ARRAY_SIZE(ignored_globals); j++) {
+			if (os_strcmp(global_name, ignored_globals[j]) == 0)
+				break;
+		}
+		if (j < ARRAY_SIZE(ignored_globals))
+			continue;
+
+		desc = &priv->all_interface_properties[count++];
+		desc->dbus_property = uscore_to_dbus(global_name);
+		if (!desc->dbus_property) {
+			wpa_printf(MSG_ERROR,
+				   "dbus: Not enough memory for D-Bus property name");
+			goto error;
+		}
+		desc->dbus_interface = WPAS_DBUS_NEW_IFACE_INTERFACE;
+		desc->type = "s";
+		desc->getter = wpas_dbus_getter_iface_global;
+		desc->setter = wpas_dbus_setter_iface_global;
+		desc->data = global_name;
+	}
+
+	return 0;
+
+error:
+	wpa_dbus_ctrl_iface_props_deinit(priv);
+	return -1;
+}
+
+
 /**
  * wpas_dbus_register_interface - Register an interface with D-Bus
  * @wpa_s: wpa_supplicant interface structure
@@ -3289,7 +3583,6 @@
  */
 int wpas_dbus_register_interface(struct wpa_supplicant *wpa_s)
 {
-
 	struct wpa_dbus_object_desc *obj_desc = NULL;
 	struct wpas_dbus_priv *ctrl_iface = wpa_s->global->dbus;
 	int next;
@@ -3315,7 +3608,7 @@
 	}
 
 	wpas_dbus_register(obj_desc, wpa_s, NULL, wpas_dbus_interface_methods,
-			   wpas_dbus_interface_properties,
+			   ctrl_iface->all_interface_properties,
 			   wpas_dbus_interface_signals);
 
 	wpa_printf(MSG_DEBUG, "dbus: Register interface object '%s'",
@@ -3381,65 +3674,80 @@
 static const struct wpa_dbus_property_desc wpas_dbus_p2p_peer_properties[] = {
 	{ "DeviceName", WPAS_DBUS_NEW_IFACE_P2P_PEER, "s",
 	  wpas_dbus_getter_p2p_peer_device_name,
+	  NULL,
 	  NULL
 	},
 	{ "Manufacturer", WPAS_DBUS_NEW_IFACE_P2P_PEER, "s",
 	  wpas_dbus_getter_p2p_peer_manufacturer,
+	  NULL,
 	  NULL
 	},
 	{ "ModelName", WPAS_DBUS_NEW_IFACE_P2P_PEER, "s",
 	  wpas_dbus_getter_p2p_peer_modelname,
+	  NULL,
 	  NULL
 	},
 	{ "ModelNumber", WPAS_DBUS_NEW_IFACE_P2P_PEER, "s",
 	  wpas_dbus_getter_p2p_peer_modelnumber,
+	  NULL,
 	  NULL
 	},
 	{ "SerialNumber", WPAS_DBUS_NEW_IFACE_P2P_PEER, "s",
 	  wpas_dbus_getter_p2p_peer_serialnumber,
+	  NULL,
 	  NULL
 	},
 	{ "PrimaryDeviceType", WPAS_DBUS_NEW_IFACE_P2P_PEER, "ay",
 	  wpas_dbus_getter_p2p_peer_primary_device_type,
+	  NULL,
 	  NULL
 	},
 	{ "config_method", WPAS_DBUS_NEW_IFACE_P2P_PEER, "q",
 	  wpas_dbus_getter_p2p_peer_config_method,
+	  NULL,
 	  NULL
 	},
 	{ "level", WPAS_DBUS_NEW_IFACE_P2P_PEER, "i",
 	  wpas_dbus_getter_p2p_peer_level,
+	  NULL,
 	  NULL
 	},
 	{ "devicecapability", WPAS_DBUS_NEW_IFACE_P2P_PEER, "y",
 	  wpas_dbus_getter_p2p_peer_device_capability,
+	  NULL,
 	  NULL
 	},
 	{ "groupcapability", WPAS_DBUS_NEW_IFACE_P2P_PEER, "y",
 	  wpas_dbus_getter_p2p_peer_group_capability,
+	  NULL,
 	  NULL
 	},
 	{ "SecondaryDeviceTypes", WPAS_DBUS_NEW_IFACE_P2P_PEER, "aay",
 	  wpas_dbus_getter_p2p_peer_secondary_device_types,
+	  NULL,
 	  NULL
 	},
 	{ "VendorExtension", WPAS_DBUS_NEW_IFACE_P2P_PEER, "aay",
 	  wpas_dbus_getter_p2p_peer_vendor_extension,
+	  NULL,
 	  NULL
 	},
 	{ "IEs", WPAS_DBUS_NEW_IFACE_P2P_PEER, "ay",
 	  wpas_dbus_getter_p2p_peer_ies,
+	  NULL,
 	  NULL
 	},
 	{ "DeviceAddress", WPAS_DBUS_NEW_IFACE_P2P_PEER, "ay",
 	  wpas_dbus_getter_p2p_peer_device_address,
+	  NULL,
 	  NULL
 	},
 	{ "Groups", WPAS_DBUS_NEW_IFACE_P2P_PEER, "ao",
 	  wpas_dbus_getter_p2p_peer_groups,
+	  NULL,
 	  NULL
 	},
-	{ NULL, NULL, NULL, NULL, NULL }
+	{ NULL, NULL, NULL, NULL, NULL, NULL }
 };
 
 static const struct wpa_dbus_signal_desc wpas_dbus_p2p_peer_signals[] = {
@@ -3697,41 +4005,50 @@
 static const struct wpa_dbus_property_desc wpas_dbus_p2p_group_properties[] = {
 	{ "Members", WPAS_DBUS_NEW_IFACE_P2P_GROUP, "ao",
 	  wpas_dbus_getter_p2p_group_members,
+	  NULL,
 	  NULL
 	},
 	{ "Group", WPAS_DBUS_NEW_IFACE_P2P_GROUP, "o",
 	  wpas_dbus_getter_p2p_group,
+	  NULL,
 	  NULL
 	},
 	{ "Role", WPAS_DBUS_NEW_IFACE_P2P_GROUP, "s",
 	  wpas_dbus_getter_p2p_role,
+	  NULL,
 	  NULL
 	},
 	{ "SSID", WPAS_DBUS_NEW_IFACE_P2P_GROUP, "ay",
 	  wpas_dbus_getter_p2p_group_ssid,
+	  NULL,
 	  NULL
 	},
 	{ "BSSID", WPAS_DBUS_NEW_IFACE_P2P_GROUP, "ay",
 	  wpas_dbus_getter_p2p_group_bssid,
+	  NULL,
 	  NULL
 	},
 	{ "Frequency", WPAS_DBUS_NEW_IFACE_P2P_GROUP, "q",
 	  wpas_dbus_getter_p2p_group_frequency,
+	  NULL,
 	  NULL
 	},
 	{ "Passphrase", WPAS_DBUS_NEW_IFACE_P2P_GROUP, "s",
 	  wpas_dbus_getter_p2p_group_passphrase,
+	  NULL,
 	  NULL
 	},
 	{ "PSK", WPAS_DBUS_NEW_IFACE_P2P_GROUP, "ay",
 	  wpas_dbus_getter_p2p_group_psk,
+	  NULL,
 	  NULL
 	},
 	{ "WPSVendorExtensions", WPAS_DBUS_NEW_IFACE_P2P_GROUP, "aay",
 	  wpas_dbus_getter_p2p_group_vendor_ext,
-	  wpas_dbus_setter_p2p_group_vendor_ext
+	  wpas_dbus_setter_p2p_group_vendor_ext,
+	  NULL
 	},
-	{ NULL, NULL, NULL, NULL, NULL }
+	{ NULL, NULL, NULL, NULL, NULL, NULL }
 };
 
 static const struct wpa_dbus_signal_desc wpas_dbus_p2p_group_signals[] = {
@@ -3858,9 +4175,10 @@
 	wpas_dbus_persistent_group_properties[] = {
 	{ "Properties", WPAS_DBUS_NEW_IFACE_PERSISTENT_GROUP, "a{sv}",
 	  wpas_dbus_getter_persistent_group_properties,
-	  wpas_dbus_setter_persistent_group_properties
+	  wpas_dbus_setter_persistent_group_properties,
+	  NULL
 	},
-	{ NULL, NULL, NULL, NULL, NULL }
+	{ NULL, NULL, NULL, NULL, NULL, NULL }
 };
 
 /* No signals intended for persistent group objects */
diff --git a/wpa_supplicant/dbus/dbus_new.h b/wpa_supplicant/dbus/dbus_new.h
index 7503348..6d240ff 100644
--- a/wpa_supplicant/dbus/dbus_new.h
+++ b/wpa_supplicant/dbus/dbus_new.h
@@ -191,6 +191,8 @@
 void wpas_dbus_signal_p2p_group_started(struct wpa_supplicant *wpa_s,
 					const struct wpa_ssid *ssid,
 					int client, int network_id);
+void wpas_dbus_signal_p2p_group_formation_failure(struct wpa_supplicant *wpa_s,
+						  const char *reason);
 void wpas_dbus_register_p2p_group(struct wpa_supplicant *wpa_s,
 				  struct wpa_ssid *ssid);
 void wpas_dbus_signal_p2p_go_neg_resp(struct wpa_supplicant *wpa_s,
@@ -231,6 +233,10 @@
 				     const u8 *sta);
 void wpas_dbus_signal_sta_deauthorized(struct wpa_supplicant *wpa_s,
 				       const u8 *sta);
+void wpas_dbus_signal_p2p_invitation_received(struct wpa_supplicant *wpa_s,
+					      const u8 *sa, const u8 *dev_addr,
+					      const u8 *bssid, int id,
+					      int op_freq);
 
 #else /* CONFIG_CTRL_IFACE_DBUS_NEW */
 
@@ -400,6 +406,12 @@
 }
 
 static inline void
+wpas_dbus_signal_p2p_group_formation_failure(struct wpa_supplicant *wpa_s,
+					     const char *reason)
+{
+}
+
+static inline void
 wpas_dbus_register_p2p_group(struct wpa_supplicant *wpa_s,
 			     struct wpa_ssid *ssid)
 {
@@ -532,6 +544,14 @@
 {
 }
 
+static inline
+void wpas_dbus_signal_p2p_invitation_received(struct wpa_supplicant *wpa_s,
+					      const u8 *sa, const u8 *dev_addr,
+					      const u8 *bssid, int id,
+					      int op_freq)
+{
+}
+
 #endif /* CONFIG_CTRL_IFACE_DBUS_NEW */
 
 #endif /* CTRL_IFACE_DBUS_H_NEW */
diff --git a/wpa_supplicant/dbus/dbus_new_handlers.c b/wpa_supplicant/dbus/dbus_new_handlers.c
index 083dca6..fbfcdc9 100644
--- a/wpa_supplicant/dbus/dbus_new_handlers.c
+++ b/wpa_supplicant/dbus/dbus_new_handlers.c
@@ -435,7 +435,8 @@
 
 	for (i = 0; i < array_len; i++) {
 		if (!dbus_message_iter_append_basic(&array_iter, type,
-						    (char *)array + i * element_size)) {
+						    (const char *) array +
+						    i * element_size)) {
 			dbus_set_error(error, DBUS_ERROR_FAILED,
 				       "%s: failed to construct message 2.5",
 				       __func__);
@@ -711,9 +712,9 @@
  *
  * Getter for "DebugLevel" property.
  */
-dbus_bool_t wpas_dbus_getter_debug_level(DBusMessageIter *iter,
-					 DBusError *error,
-					 void *user_data)
+dbus_bool_t wpas_dbus_getter_debug_level(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
 {
 	const char *str;
 	int idx = wpa_debug_level;
@@ -737,9 +738,9 @@
  *
  * Getter for "DebugTimestamp" property.
  */
-dbus_bool_t wpas_dbus_getter_debug_timestamp(DBusMessageIter *iter,
-					     DBusError *error,
-					     void *user_data)
+dbus_bool_t wpas_dbus_getter_debug_timestamp(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
 {
 	return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_BOOLEAN,
 						&wpa_debug_timestamp, error);
@@ -756,9 +757,9 @@
  *
  * Getter for "DebugShowKeys" property.
  */
-dbus_bool_t wpas_dbus_getter_debug_show_keys(DBusMessageIter *iter,
-					     DBusError *error,
-					     void *user_data)
+dbus_bool_t wpas_dbus_getter_debug_show_keys(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
 {
 	return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_BOOLEAN,
 						&wpa_debug_show_keys, error);
@@ -774,8 +775,9 @@
  *
  * Setter for "DebugLevel" property.
  */
-dbus_bool_t wpas_dbus_setter_debug_level(DBusMessageIter *iter,
-					 DBusError *error, void *user_data)
+dbus_bool_t wpas_dbus_setter_debug_level(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
 {
 	struct wpa_global *global = user_data;
 	const char *str = NULL;
@@ -812,9 +814,9 @@
  *
  * Setter for "DebugTimestamp" property.
  */
-dbus_bool_t wpas_dbus_setter_debug_timestamp(DBusMessageIter *iter,
-					     DBusError *error,
-					     void *user_data)
+dbus_bool_t wpas_dbus_setter_debug_timestamp(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
 {
 	struct wpa_global *global = user_data;
 	dbus_bool_t val;
@@ -838,9 +840,9 @@
  *
  * Setter for "DebugShowKeys" property.
  */
-dbus_bool_t wpas_dbus_setter_debug_show_keys(DBusMessageIter *iter,
-					     DBusError *error,
-					     void *user_data)
+dbus_bool_t wpas_dbus_setter_debug_show_keys(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
 {
 	struct wpa_global *global = user_data;
 	dbus_bool_t val;
@@ -867,9 +869,9 @@
  * by dbus clients to return list of registered interfaces objects
  * paths
  */
-dbus_bool_t wpas_dbus_getter_interfaces(DBusMessageIter *iter,
-					DBusError *error,
-					void *user_data)
+dbus_bool_t wpas_dbus_getter_interfaces(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
 {
 	struct wpa_global *global = user_data;
 	struct wpa_supplicant *wpa_s;
@@ -912,8 +914,9 @@
  * Getter for "EapMethods" property. Handles requests
  * by dbus clients to return list of strings with supported EAP methods
  */
-dbus_bool_t wpas_dbus_getter_eap_methods(DBusMessageIter *iter,
-					 DBusError *error, void *user_data)
+dbus_bool_t wpas_dbus_getter_eap_methods(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
 {
 	char **eap_methods;
 	size_t num_items = 0;
@@ -948,9 +951,9 @@
  * return a list of strings with supported capabilities like AP, RSN IBSS,
  * and P2P that are determined at compile time.
  */
-dbus_bool_t wpas_dbus_getter_global_capabilities(DBusMessageIter *iter,
-						 DBusError *error,
-						 void *user_data)
+dbus_bool_t wpas_dbus_getter_global_capabilities(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
 {
 	const char *capabilities[5] = { NULL, NULL, NULL, NULL, NULL };
 	size_t num_items = 0;
@@ -1341,6 +1344,7 @@
 			}
 
 			if (params.freqs && params.freqs[0]) {
+				wpa_s->last_scan_req = MANUAL_SCAN_REQ;
 				if (wpa_supplicant_trigger_scan(wpa_s,
 								&params)) {
 					reply = wpas_dbus_error_scan_error(
@@ -1367,6 +1371,7 @@
 			wpa_supplicant_cancel_sched_scan(wpa_s);
 		}
 
+		wpa_s->last_scan_req = MANUAL_SCAN_REQ;
 		if (wpa_supplicant_trigger_scan(wpa_s, &params)) {
 			reply = wpas_dbus_error_scan_error(
 				message, "Scan request rejected");
@@ -1578,6 +1583,27 @@
 
 
 /**
+ * wpas_dbus_handler_expect_disconnect - ExpectDisconnect
+ * @message: Pointer to incoming dbus message
+ * @global: %wpa_supplicant global data structure
+ * Returns: NULL
+ *
+ * Handler function for notifying system there will be a expected disconnect.
+ * This will prevent wpa_supplicant from adding blacklists upon next disconnect..
+ */
+DBusMessage * wpas_dbus_handler_expect_disconnect(DBusMessage *message,
+						  struct wpa_global *global)
+{
+	struct wpa_supplicant *wpa_s = global->ifaces;
+
+	for (; wpa_s; wpa_s = wpa_s->next)
+		if (wpa_s->wpa_state >= WPA_ASSOCIATED)
+			wpa_s->own_disconnect_req = 1;
+	return NULL;
+}
+
+
+/**
  * wpas_dbus_handler_reattach - Reattach to current AP
  * @message: Pointer to incoming dbus message
  * @wpa_s: wpa_supplicant structure for a network interface
@@ -1852,7 +1878,7 @@
 	os_free(iface);
 	return reply;
 #else /* IEEE8021X_EAPOL */
-	wpa_printf(MSG_DEBUG, "CTRL_IFACE: 802.1X not included");
+	wpa_printf(MSG_DEBUG, "dbus: 802.1X not included");
 	return wpas_dbus_error_unknown_error(message, "802.1X not included");
 #endif /* IEEE8021X_EAPOL */
 }
@@ -2269,6 +2295,35 @@
 #endif /* CONFIG_TDLS */
 
 
+#ifndef CONFIG_NO_CONFIG_WRITE
+/**
+ * wpas_dbus_handler_save_config - Save configuration to configuration file
+ * @message: Pointer to incoming dbus message
+ * @wpa_s: wpa_supplicant structure for a network interface
+ * Returns: NULL on Success, Otherwise errror message
+ *
+ * Handler function for "SaveConfig" method call of network interface.
+ */
+DBusMessage * wpas_dbus_handler_save_config(DBusMessage *message,
+					    struct wpa_supplicant *wpa_s)
+{
+	int ret;
+
+	if (!wpa_s->conf->update_config) {
+		return wpas_dbus_error_unknown_error(
+			message,
+			"Not allowed to update configuration (update_config=0)");
+	}
+
+	ret = wpa_config_write(wpa_s->confname, wpa_s->conf);
+	if (ret)
+		return wpas_dbus_error_unknown_error(
+			message, "Failed to update configuration");
+	return NULL;
+}
+#endif /* CONFIG_NO_CONFIG_WRITE */
+
+
 /**
  * wpas_dbus_handler_set_pkcs11_engine_and_module_path - Set PKCS #11 engine and module path
  * @message: Pointer to incoming dbus message
@@ -2336,8 +2391,9 @@
  *
  * Getter for "Capabilities" property of an interface.
  */
-dbus_bool_t wpas_dbus_getter_capabilities(DBusMessageIter *iter,
-					  DBusError *error, void *user_data)
+dbus_bool_t wpas_dbus_getter_capabilities(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
 {
 	struct wpa_supplicant *wpa_s = user_data;
 	struct wpa_driver_capa capa;
@@ -2627,8 +2683,9 @@
  *
  * Getter for "State" property.
  */
-dbus_bool_t wpas_dbus_getter_state(DBusMessageIter *iter, DBusError *error,
-				   void *user_data)
+dbus_bool_t wpas_dbus_getter_state(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
 {
 	struct wpa_supplicant *wpa_s = user_data;
 	const char *str_state;
@@ -2667,8 +2724,9 @@
  *
  * Getter for "scanning" property.
  */
-dbus_bool_t wpas_dbus_getter_scanning(DBusMessageIter *iter, DBusError *error,
-				      void *user_data)
+dbus_bool_t wpas_dbus_getter_scanning(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
 {
 	struct wpa_supplicant *wpa_s = user_data;
 	dbus_bool_t scanning = wpa_s->scanning ? TRUE : FALSE;
@@ -2687,8 +2745,9 @@
  *
  * Getter function for "ApScan" property.
  */
-dbus_bool_t wpas_dbus_getter_ap_scan(DBusMessageIter *iter, DBusError *error,
-				     void *user_data)
+dbus_bool_t wpas_dbus_getter_ap_scan(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
 {
 	struct wpa_supplicant *wpa_s = user_data;
 	dbus_uint32_t ap_scan = wpa_s->conf->ap_scan;
@@ -2707,8 +2766,9 @@
  *
  * Setter function for "ApScan" property.
  */
-dbus_bool_t wpas_dbus_setter_ap_scan(DBusMessageIter *iter, DBusError *error,
-				     void *user_data)
+dbus_bool_t wpas_dbus_setter_ap_scan(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
 {
 	struct wpa_supplicant *wpa_s = user_data;
 	dbus_uint32_t ap_scan;
@@ -2736,9 +2796,9 @@
  *
  * Getter function for "FastReauth" property.
  */
-dbus_bool_t wpas_dbus_getter_fast_reauth(DBusMessageIter *iter,
-					 DBusError *error,
-					 void *user_data)
+dbus_bool_t wpas_dbus_getter_fast_reauth(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
 {
 	struct wpa_supplicant *wpa_s = user_data;
 	dbus_bool_t fast_reauth = wpa_s->conf->fast_reauth ? TRUE : FALSE;
@@ -2758,9 +2818,9 @@
  *
  * Setter function for "FastReauth" property.
  */
-dbus_bool_t wpas_dbus_setter_fast_reauth(DBusMessageIter *iter,
-				     DBusError *error,
-				     void *user_data)
+dbus_bool_t wpas_dbus_setter_fast_reauth(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
 {
 	struct wpa_supplicant *wpa_s = user_data;
 	dbus_bool_t fast_reauth;
@@ -2784,9 +2844,9 @@
  * Getter for "DisconnectReason" property.  The reason is negative if it is
  * locally generated.
  */
-dbus_bool_t wpas_dbus_getter_disconnect_reason(DBusMessageIter *iter,
-					       DBusError *error,
-					       void *user_data)
+dbus_bool_t wpas_dbus_getter_disconnect_reason(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
 {
 	struct wpa_supplicant *wpa_s = user_data;
 	dbus_int32_t reason = wpa_s->disconnect_reason;
@@ -2805,9 +2865,9 @@
  *
  * Getter function for "BSSExpireAge" property.
  */
-dbus_bool_t wpas_dbus_getter_bss_expire_age(DBusMessageIter *iter,
-					    DBusError *error,
-					    void *user_data)
+dbus_bool_t wpas_dbus_getter_bss_expire_age(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
 {
 	struct wpa_supplicant *wpa_s = user_data;
 	dbus_uint32_t expire_age = wpa_s->conf->bss_expiration_age;
@@ -2826,9 +2886,9 @@
  *
  * Setter function for "BSSExpireAge" property.
  */
-dbus_bool_t wpas_dbus_setter_bss_expire_age(DBusMessageIter *iter,
-					    DBusError *error,
-					    void *user_data)
+dbus_bool_t wpas_dbus_setter_bss_expire_age(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
 {
 	struct wpa_supplicant *wpa_s = user_data;
 	dbus_uint32_t expire_age;
@@ -2855,9 +2915,9 @@
  *
  * Getter function for "BSSExpireCount" property.
  */
-dbus_bool_t wpas_dbus_getter_bss_expire_count(DBusMessageIter *iter,
-					      DBusError *error,
-					      void *user_data)
+dbus_bool_t wpas_dbus_getter_bss_expire_count(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
 {
 	struct wpa_supplicant *wpa_s = user_data;
 	dbus_uint32_t expire_count = wpa_s->conf->bss_expiration_scan_count;
@@ -2876,9 +2936,9 @@
  *
  * Setter function for "BSSExpireCount" property.
  */
-dbus_bool_t wpas_dbus_setter_bss_expire_count(DBusMessageIter *iter,
-					      DBusError *error,
-					      void *user_data)
+dbus_bool_t wpas_dbus_setter_bss_expire_count(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
 {
 	struct wpa_supplicant *wpa_s = user_data;
 	dbus_uint32_t expire_count;
@@ -2905,8 +2965,9 @@
  *
  * Getter function for "Country" property.
  */
-dbus_bool_t wpas_dbus_getter_country(DBusMessageIter *iter, DBusError *error,
-				     void *user_data)
+dbus_bool_t wpas_dbus_getter_country(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
 {
 	struct wpa_supplicant *wpa_s = user_data;
 	char country[3];
@@ -2930,8 +2991,9 @@
  *
  * Setter function for "Country" property.
  */
-dbus_bool_t wpas_dbus_setter_country(DBusMessageIter *iter, DBusError *error,
-				     void *user_data)
+dbus_bool_t wpas_dbus_setter_country(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
 {
 	struct wpa_supplicant *wpa_s = user_data;
 	const char *country;
@@ -2968,9 +3030,9 @@
  *
  * Getter function for "ScanInterval" property.
  */
-dbus_bool_t wpas_dbus_getter_scan_interval(DBusMessageIter *iter,
-					   DBusError *error,
-					   void *user_data)
+dbus_bool_t wpas_dbus_getter_scan_interval(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
 {
 	struct wpa_supplicant *wpa_s = user_data;
 	dbus_int32_t scan_interval = wpa_s->scan_interval;
@@ -2989,9 +3051,9 @@
  *
  * Setter function for "ScanInterval" property.
  */
-dbus_bool_t wpas_dbus_setter_scan_interval(DBusMessageIter *iter,
-					   DBusError *error,
-					   void *user_data)
+dbus_bool_t wpas_dbus_setter_scan_interval(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
 {
 	struct wpa_supplicant *wpa_s = user_data;
 	dbus_int32_t scan_interval;
@@ -3018,8 +3080,9 @@
  *
  * Getter for "Ifname" property.
  */
-dbus_bool_t wpas_dbus_getter_ifname(DBusMessageIter *iter, DBusError *error,
-				    void *user_data)
+dbus_bool_t wpas_dbus_getter_ifname(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
 {
 	struct wpa_supplicant *wpa_s = user_data;
 	const char *ifname = wpa_s->ifname;
@@ -3038,8 +3101,9 @@
  *
  * Getter for "Driver" property.
  */
-dbus_bool_t wpas_dbus_getter_driver(DBusMessageIter *iter, DBusError *error,
-				    void *user_data)
+dbus_bool_t wpas_dbus_getter_driver(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
 {
 	struct wpa_supplicant *wpa_s = user_data;
 	const char *driver;
@@ -3067,9 +3131,9 @@
  *
  * Getter for "CurrentBSS" property.
  */
-dbus_bool_t wpas_dbus_getter_current_bss(DBusMessageIter *iter,
-					 DBusError *error,
-					 void *user_data)
+dbus_bool_t wpas_dbus_getter_current_bss(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
 {
 	struct wpa_supplicant *wpa_s = user_data;
 	char path_buf[WPAS_DBUS_OBJECT_PATH_MAX], *bss_obj_path = path_buf;
@@ -3095,9 +3159,9 @@
  *
  * Getter for "CurrentNetwork" property.
  */
-dbus_bool_t wpas_dbus_getter_current_network(DBusMessageIter *iter,
-					     DBusError *error,
-					     void *user_data)
+dbus_bool_t wpas_dbus_getter_current_network(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
 {
 	struct wpa_supplicant *wpa_s = user_data;
 	char path_buf[WPAS_DBUS_OBJECT_PATH_MAX], *net_obj_path = path_buf;
@@ -3123,9 +3187,9 @@
  *
  * Getter for "CurrentAuthMode" property.
  */
-dbus_bool_t wpas_dbus_getter_current_auth_mode(DBusMessageIter *iter,
-					       DBusError *error,
-					       void *user_data)
+dbus_bool_t wpas_dbus_getter_current_auth_mode(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
 {
 	struct wpa_supplicant *wpa_s = user_data;
 	const char *eap_mode;
@@ -3160,9 +3224,9 @@
  *
  * Getter for "BridgeIfname" property.
  */
-dbus_bool_t wpas_dbus_getter_bridge_ifname(DBusMessageIter *iter,
-					   DBusError *error,
-					   void *user_data)
+dbus_bool_t wpas_dbus_getter_bridge_ifname(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
 {
 	struct wpa_supplicant *wpa_s = user_data;
 	const char *bridge_ifname = wpa_s->bridge_ifname;
@@ -3181,8 +3245,9 @@
  *
  * Getter for "BSSs" property.
  */
-dbus_bool_t wpas_dbus_getter_bsss(DBusMessageIter *iter, DBusError *error,
-				  void *user_data)
+dbus_bool_t wpas_dbus_getter_bsss(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
 {
 	struct wpa_supplicant *wpa_s = user_data;
 	struct wpa_bss *bss;
@@ -3238,8 +3303,9 @@
  *
  * Getter for "Networks" property.
  */
-dbus_bool_t wpas_dbus_getter_networks(DBusMessageIter *iter, DBusError *error,
-				      void *user_data)
+dbus_bool_t wpas_dbus_getter_networks(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
 {
 	struct wpa_supplicant *wpa_s = user_data;
 	struct wpa_ssid *ssid;
@@ -3301,9 +3367,9 @@
  *
  * Getter for "PKCS11EnginePath" property.
  */
-dbus_bool_t wpas_dbus_getter_pkcs11_engine_path(DBusMessageIter *iter,
-						DBusError *error,
-						void *user_data)
+dbus_bool_t wpas_dbus_getter_pkcs11_engine_path(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
 {
 	struct wpa_supplicant *wpa_s = user_data;
 	const char *pkcs11_engine_path;
@@ -3326,9 +3392,9 @@
  *
  * Getter for "PKCS11ModulePath" property.
  */
-dbus_bool_t wpas_dbus_getter_pkcs11_module_path(DBusMessageIter *iter,
-						DBusError *error,
-						void *user_data)
+dbus_bool_t wpas_dbus_getter_pkcs11_module_path(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
 {
 	struct wpa_supplicant *wpa_s = user_data;
 	const char *pkcs11_module_path;
@@ -3351,8 +3417,9 @@
  *
  * Getter for "Blobs" property.
  */
-dbus_bool_t wpas_dbus_getter_blobs(DBusMessageIter *iter, DBusError *error,
-				   void *user_data)
+dbus_bool_t wpas_dbus_getter_blobs(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
 {
 	struct wpa_supplicant *wpa_s = user_data;
 	DBusMessageIter variant_iter, dict_iter, entry_iter, array_iter;
@@ -3404,6 +3471,79 @@
 }
 
 
+dbus_bool_t wpas_dbus_getter_iface_global(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
+{
+	struct wpa_supplicant *wpa_s = user_data;
+	int ret;
+	char buf[250];
+	char *p = buf;
+
+	if (!property_desc->data) {
+		dbus_set_error(error, DBUS_ERROR_INVALID_ARGS,
+			       "Unhandled interface property %s",
+			       property_desc->dbus_property);
+		return FALSE;
+	}
+
+	ret = wpa_config_get_value(property_desc->data, wpa_s->conf, buf,
+				   sizeof(buf));
+	if (ret < 0)
+		*p = '\0';
+
+	return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_STRING, &p,
+						error);
+}
+
+
+dbus_bool_t wpas_dbus_setter_iface_global(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
+{
+	struct wpa_supplicant *wpa_s = user_data;
+	const char *new_value = NULL;
+	char buf[250];
+	size_t combined_len;
+	int ret;
+
+	if (!wpas_dbus_simple_property_setter(iter, error, DBUS_TYPE_STRING,
+					      &new_value))
+		return FALSE;
+
+	combined_len = os_strlen(property_desc->data) + os_strlen(new_value) +
+		3;
+	if (combined_len >= sizeof(buf)) {
+		dbus_set_error(error, DBUS_ERROR_INVALID_ARGS,
+			       "Interface property %s value too large",
+			       property_desc->dbus_property);
+		return FALSE;
+	}
+
+	if (!new_value[0])
+		new_value = "NULL";
+
+	ret = os_snprintf(buf, combined_len, "%s=%s", property_desc->data,
+			  new_value);
+	if (os_snprintf_error(combined_len, ret)) {
+		dbus_set_error(error,  WPAS_DBUS_ERROR_UNKNOWN_ERROR,
+			       "Failed to construct new interface property %s",
+			       property_desc->dbus_property);
+		return FALSE;
+	}
+
+	if (wpa_config_process_global(wpa_s->conf, buf, -1)) {
+		dbus_set_error(error, DBUS_ERROR_INVALID_ARGS,
+			       "Failed to set interface property %s",
+			       property_desc->dbus_property);
+		return FALSE;
+	}
+
+	wpa_supplicant_update_config(wpa_s);
+	return TRUE;
+}
+
+
 static struct wpa_bss * get_bss_helper(struct bss_handler_args *args,
 				       DBusError *error, const char *func_name)
 {
@@ -3430,8 +3570,9 @@
  *
  * Getter for "BSSID" property.
  */
-dbus_bool_t wpas_dbus_getter_bss_bssid(DBusMessageIter *iter, DBusError *error,
-				       void *user_data)
+dbus_bool_t wpas_dbus_getter_bss_bssid(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
 {
 	struct bss_handler_args *args = user_data;
 	struct wpa_bss *res;
@@ -3455,8 +3596,9 @@
  *
  * Getter for "SSID" property.
  */
-dbus_bool_t wpas_dbus_getter_bss_ssid(DBusMessageIter *iter, DBusError *error,
-				      void *user_data)
+dbus_bool_t wpas_dbus_getter_bss_ssid(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
 {
 	struct bss_handler_args *args = user_data;
 	struct wpa_bss *res;
@@ -3480,8 +3622,9 @@
  *
  * Getter for "Privacy" property.
  */
-dbus_bool_t wpas_dbus_getter_bss_privacy(DBusMessageIter *iter,
-					 DBusError *error, void *user_data)
+dbus_bool_t wpas_dbus_getter_bss_privacy(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
 {
 	struct bss_handler_args *args = user_data;
 	struct wpa_bss *res;
@@ -3506,8 +3649,9 @@
  *
  * Getter for "Mode" property.
  */
-dbus_bool_t wpas_dbus_getter_bss_mode(DBusMessageIter *iter, DBusError *error,
-				      void *user_data)
+dbus_bool_t wpas_dbus_getter_bss_mode(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
 {
 	struct bss_handler_args *args = user_data;
 	struct wpa_bss *res;
@@ -3547,8 +3691,9 @@
  *
  * Getter for "Level" property.
  */
-dbus_bool_t wpas_dbus_getter_bss_signal(DBusMessageIter *iter,
-					DBusError *error, void *user_data)
+dbus_bool_t wpas_dbus_getter_bss_signal(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
 {
 	struct bss_handler_args *args = user_data;
 	struct wpa_bss *res;
@@ -3573,8 +3718,9 @@
  *
  * Getter for "Frequency" property.
  */
-dbus_bool_t wpas_dbus_getter_bss_frequency(DBusMessageIter *iter,
-					   DBusError *error, void *user_data)
+dbus_bool_t wpas_dbus_getter_bss_frequency(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
 {
 	struct bss_handler_args *args = user_data;
 	struct wpa_bss *res;
@@ -3605,8 +3751,9 @@
  *
  * Getter for "Rates" property.
  */
-dbus_bool_t wpas_dbus_getter_bss_rates(DBusMessageIter *iter,
-				       DBusError *error, void *user_data)
+dbus_bool_t wpas_dbus_getter_bss_rates(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
 {
 	struct bss_handler_args *args = user_data;
 	struct wpa_bss *res;
@@ -3645,9 +3792,9 @@
 }
 
 
-static dbus_bool_t wpas_dbus_get_bss_security_prop(DBusMessageIter *iter,
-						   struct wpa_ie_data *ie_data,
-						   DBusError *error)
+static dbus_bool_t wpas_dbus_get_bss_security_prop(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, struct wpa_ie_data *ie_data, DBusError *error)
 {
 	DBusMessageIter iter_dict, variant_iter;
 	const char *group;
@@ -3778,8 +3925,9 @@
  *
  * Getter for "WPA" property.
  */
-dbus_bool_t wpas_dbus_getter_bss_wpa(DBusMessageIter *iter, DBusError *error,
-				     void *user_data)
+dbus_bool_t wpas_dbus_getter_bss_wpa(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
 {
 	struct bss_handler_args *args = user_data;
 	struct wpa_bss *res;
@@ -3798,7 +3946,7 @@
 		return FALSE;
 	}
 
-	return wpas_dbus_get_bss_security_prop(iter, &wpa_data, error);
+	return wpas_dbus_get_bss_security_prop(property_desc, iter, &wpa_data, error);
 }
 
 
@@ -3811,8 +3959,9 @@
  *
  * Getter for "RSN" property.
  */
-dbus_bool_t wpas_dbus_getter_bss_rsn(DBusMessageIter *iter, DBusError *error,
-				     void *user_data)
+dbus_bool_t wpas_dbus_getter_bss_rsn(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
 {
 	struct bss_handler_args *args = user_data;
 	struct wpa_bss *res;
@@ -3831,7 +3980,7 @@
 		return FALSE;
 	}
 
-	return wpas_dbus_get_bss_security_prop(iter, &wpa_data, error);
+	return wpas_dbus_get_bss_security_prop(property_desc, iter, &wpa_data, error);
 }
 
 
@@ -3844,8 +3993,9 @@
  *
  * Getter for "WPS" property.
  */
-dbus_bool_t wpas_dbus_getter_bss_wps(DBusMessageIter *iter, DBusError *error,
-				     void *user_data)
+dbus_bool_t wpas_dbus_getter_bss_wps(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
 {
 	struct bss_handler_args *args = user_data;
 	struct wpa_bss *res;
@@ -3900,8 +4050,9 @@
  *
  * Getter for "IEs" property.
  */
-dbus_bool_t wpas_dbus_getter_bss_ies(DBusMessageIter *iter, DBusError *error,
-				     void *user_data)
+dbus_bool_t wpas_dbus_getter_bss_ies(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
 {
 	struct bss_handler_args *args = user_data;
 	struct wpa_bss *res;
@@ -3925,8 +4076,9 @@
  *
  * Getter for BSS age
  */
-dbus_bool_t wpas_dbus_getter_bss_age(DBusMessageIter *iter, DBusError *error,
-				     void *user_data)
+dbus_bool_t wpas_dbus_getter_bss_age(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
 {
 	struct bss_handler_args *args = user_data;
 	struct wpa_bss *res;
@@ -3954,8 +4106,9 @@
  *
  * Getter for "enabled" property of a configured network.
  */
-dbus_bool_t wpas_dbus_getter_enabled(DBusMessageIter *iter, DBusError *error,
-				     void *user_data)
+dbus_bool_t wpas_dbus_getter_enabled(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
 {
 	struct network_handler_args *net = user_data;
 	dbus_bool_t enabled = net->ssid->disabled ? FALSE : TRUE;
@@ -3974,8 +4127,9 @@
  *
  * Setter for "Enabled" property of a configured network.
  */
-dbus_bool_t wpas_dbus_setter_enabled(DBusMessageIter *iter, DBusError *error,
-				     void *user_data)
+dbus_bool_t wpas_dbus_setter_enabled(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
 {
 	struct network_handler_args *net = user_data;
 	struct wpa_supplicant *wpa_s;
@@ -4007,9 +4161,9 @@
  *
  * Getter for "Properties" property of a configured network.
  */
-dbus_bool_t wpas_dbus_getter_network_properties(DBusMessageIter *iter,
-						DBusError *error,
-						void *user_data)
+dbus_bool_t wpas_dbus_getter_network_properties(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
 {
 	struct network_handler_args *net = user_data;
 	DBusMessageIter	variant_iter, dict_iter;
@@ -4069,9 +4223,9 @@
  *
  * Setter for "Properties" property of a configured network.
  */
-dbus_bool_t wpas_dbus_setter_network_properties(DBusMessageIter *iter,
-						DBusError *error,
-						void *user_data)
+dbus_bool_t wpas_dbus_setter_network_properties(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
 {
 	struct network_handler_args *net = user_data;
 	struct wpa_ssid *ssid = net->ssid;
diff --git a/wpa_supplicant/dbus/dbus_new_handlers.h b/wpa_supplicant/dbus/dbus_new_handlers.h
index 50f72ec..86c8359 100644
--- a/wpa_supplicant/dbus/dbus_new_handlers.h
+++ b/wpa_supplicant/dbus/dbus_new_handlers.h
@@ -10,6 +10,8 @@
 #ifndef CTRL_IFACE_DBUS_NEW_HANDLERS_H
 #define CTRL_IFACE_DBUS_NEW_HANDLERS_H
 
+#include "dbus_new_helpers.h"
+
 struct network_handler_args {
 	struct wpa_supplicant *wpa_s;
 	struct wpa_ssid *ssid;
@@ -50,39 +52,20 @@
 DBusMessage * wpas_dbus_handler_get_interface(DBusMessage *message,
 					      struct wpa_global *global);
 
-dbus_bool_t wpas_dbus_getter_debug_level(DBusMessageIter *iter,
-					 DBusError *error,
-					 void *user_data);
+DBusMessage * wpas_dbus_handler_expect_disconnect(DBusMessage *message,
+						  struct wpa_global *global);
 
-dbus_bool_t wpas_dbus_getter_debug_timestamp(DBusMessageIter *iter,
-					     DBusError *error,
-					     void *user_data);
-
-dbus_bool_t wpas_dbus_getter_debug_show_keys(DBusMessageIter *iter,
-					     DBusError *error,
-					     void *user_data);
-
-dbus_bool_t wpas_dbus_setter_debug_level(DBusMessageIter *iter,
-					 DBusError *error, void *user_data);
-
-dbus_bool_t wpas_dbus_setter_debug_timestamp(DBusMessageIter *iter,
-					     DBusError *error,
-					     void *user_data);
-
-dbus_bool_t wpas_dbus_setter_debug_show_keys(DBusMessageIter *iter,
-					     DBusError *error,
-					     void *user_data);
-
-dbus_bool_t wpas_dbus_getter_interfaces(DBusMessageIter *iter,
-					DBusError *error,
-					void *user_data);
-
-dbus_bool_t wpas_dbus_getter_eap_methods(DBusMessageIter *iter,
-					 DBusError *error, void *user_data);
-
-dbus_bool_t wpas_dbus_getter_global_capabilities(DBusMessageIter *iter,
-						 DBusError *error,
-						 void *user_data);
+DECLARE_ACCESSOR(wpas_dbus_getter_debug_level);
+DECLARE_ACCESSOR(wpas_dbus_getter_debug_timestamp);
+DECLARE_ACCESSOR(wpas_dbus_getter_debug_show_keys);
+DECLARE_ACCESSOR(wpas_dbus_setter_debug_level);
+DECLARE_ACCESSOR(wpas_dbus_setter_debug_timestamp);
+DECLARE_ACCESSOR(wpas_dbus_setter_debug_show_keys);
+DECLARE_ACCESSOR(wpas_dbus_getter_interfaces);
+DECLARE_ACCESSOR(wpas_dbus_getter_eap_methods);
+DECLARE_ACCESSOR(wpas_dbus_getter_global_capabilities);
+DECLARE_ACCESSOR(wpas_dbus_getter_iface_global);
+DECLARE_ACCESSOR(wpas_dbus_setter_iface_global);
 
 DBusMessage * wpas_dbus_handler_scan(DBusMessage *message,
 				     struct wpa_supplicant *wpa_s);
@@ -146,150 +129,50 @@
 DBusMessage * wpas_dbus_handler_eap_logon(DBusMessage *message,
 					  struct wpa_supplicant *wpa_s);
 
-dbus_bool_t wpas_dbus_getter_capabilities(DBusMessageIter *iter,
-					  DBusError *error, void *user_data);
-
-dbus_bool_t wpas_dbus_getter_state(DBusMessageIter *iter, DBusError *error,
-				   void *user_data);
-
-dbus_bool_t wpas_dbus_getter_scanning(DBusMessageIter *iter, DBusError *error,
-				      void *user_data);
-
-dbus_bool_t wpas_dbus_getter_ap_scan(DBusMessageIter *iter, DBusError *error,
-				     void *user_data);
-
-dbus_bool_t wpas_dbus_setter_ap_scan(DBusMessageIter *iter, DBusError *error,
-				     void *user_data);
-
-dbus_bool_t wpas_dbus_getter_fast_reauth(DBusMessageIter *iter,
-					 DBusError *error,
-					 void *user_data);
-
-dbus_bool_t wpas_dbus_setter_fast_reauth(DBusMessageIter *iter,
-					 DBusError *error,
-					 void *user_data);
-
-dbus_bool_t wpas_dbus_getter_disconnect_reason(DBusMessageIter *iter,
-					       DBusError *error,
-					       void *user_data);
-
-dbus_bool_t wpas_dbus_getter_bss_expire_age(DBusMessageIter *iter,
-					    DBusError *error, void *user_data);
-
-dbus_bool_t wpas_dbus_setter_bss_expire_age(DBusMessageIter *iter,
-					    DBusError *error,
-					    void *user_data);
-
-dbus_bool_t wpas_dbus_getter_bss_expire_count(DBusMessageIter *iter,
-					      DBusError *error,
-					      void *user_data);
-
-dbus_bool_t wpas_dbus_setter_bss_expire_count(DBusMessageIter *iter,
-					      DBusError *error,
-					      void *user_data);
-
-dbus_bool_t wpas_dbus_getter_country(DBusMessageIter *iter, DBusError *error,
-				     void *user_data);
-
-dbus_bool_t wpas_dbus_setter_country(DBusMessageIter *iter, DBusError *error,
-				     void *user_data);
-
-dbus_bool_t wpas_dbus_getter_scan_interval(DBusMessageIter *iter,
-					   DBusError *error,
-					   void *user_data);
-
-dbus_bool_t wpas_dbus_setter_scan_interval(DBusMessageIter *iter,
-					   DBusError *error,
-					   void *user_data);
-
-dbus_bool_t wpas_dbus_getter_ifname(DBusMessageIter *iter, DBusError *error,
-				    void *user_data);
-
-dbus_bool_t wpas_dbus_getter_driver(DBusMessageIter *iter, DBusError *error,
-				    void *user_data);
-
-dbus_bool_t wpas_dbus_getter_bridge_ifname(DBusMessageIter *iter,
-					   DBusError *error,
-					   void *user_data);
-
-dbus_bool_t wpas_dbus_getter_current_bss(DBusMessageIter *iter,
-					 DBusError *error,
-					 void *user_data);
-
-dbus_bool_t wpas_dbus_getter_current_network(DBusMessageIter *iter,
-					     DBusError *error,
-					     void *user_data);
-
-dbus_bool_t wpas_dbus_getter_current_auth_mode(DBusMessageIter *iter,
-					       DBusError *error,
-					       void *user_data);
-
-dbus_bool_t wpas_dbus_getter_bsss(DBusMessageIter *iter, DBusError *error,
-				  void *user_data);
-
-dbus_bool_t wpas_dbus_getter_networks(DBusMessageIter *iter, DBusError *error,
-				      void *user_data);
-
-dbus_bool_t wpas_dbus_getter_pkcs11_engine_path(DBusMessageIter *iter,
-						DBusError *error,
-						void *user_data);
-
-dbus_bool_t wpas_dbus_getter_pkcs11_module_path(DBusMessageIter *iter,
-						DBusError *error,
-						void *user_data);
-
-dbus_bool_t wpas_dbus_getter_blobs(DBusMessageIter *iter, DBusError *error,
-				   void *user_data);
-
-dbus_bool_t wpas_dbus_getter_bss_bssid(DBusMessageIter *iter, DBusError *error,
-				       void *user_data);
-
-dbus_bool_t wpas_dbus_getter_bss_ssid(DBusMessageIter *iter, DBusError *error,
-				      void *user_data);
-
-dbus_bool_t wpas_dbus_getter_bss_privacy(DBusMessageIter *iter,
-					 DBusError *error, void *user_data);
-
-dbus_bool_t wpas_dbus_getter_bss_mode(DBusMessageIter *iter, DBusError *error,
-				      void *user_data);
-
-dbus_bool_t wpas_dbus_getter_bss_signal(DBusMessageIter *iter,
-					DBusError *error, void *user_data);
-
-dbus_bool_t wpas_dbus_getter_bss_frequency(DBusMessageIter *iter,
-					   DBusError *error, void *user_data);
-
-dbus_bool_t wpas_dbus_getter_bss_rates(DBusMessageIter *iter,
-				       DBusError *error, void *user_data);
-
-dbus_bool_t wpas_dbus_getter_bss_wpa(DBusMessageIter *iter, DBusError *error,
-				     void *user_data);
-
-dbus_bool_t wpas_dbus_getter_bss_rsn(DBusMessageIter *iter, DBusError *error,
-				     void *user_data);
-
-dbus_bool_t wpas_dbus_getter_bss_wps(DBusMessageIter *iter, DBusError *error,
-				     void *user_data);
-
-dbus_bool_t wpas_dbus_getter_bss_ies(DBusMessageIter *iter, DBusError *error,
-				     void *user_data);
-
-dbus_bool_t wpas_dbus_getter_bss_age(DBusMessageIter *iter, DBusError *error,
-				     void *user_data);
-
-dbus_bool_t wpas_dbus_getter_enabled(DBusMessageIter *iter, DBusError *error,
-				     void *user_data);
-
-dbus_bool_t wpas_dbus_setter_enabled(DBusMessageIter *iter, DBusError *error,
-				     void *user_data);
-
-dbus_bool_t wpas_dbus_getter_network_properties(DBusMessageIter *iter,
-						DBusError *error,
-						void *user_data);
-
-dbus_bool_t wpas_dbus_setter_network_properties(DBusMessageIter *iter,
-						DBusError *error,
-						void *user_data);
+DECLARE_ACCESSOR(wpas_dbus_getter_capabilities);
+DECLARE_ACCESSOR(wpas_dbus_getter_state);
+DECLARE_ACCESSOR(wpas_dbus_getter_scanning);
+DECLARE_ACCESSOR(wpas_dbus_getter_ap_scan);
+DECLARE_ACCESSOR(wpas_dbus_setter_ap_scan);
+DECLARE_ACCESSOR(wpas_dbus_getter_fast_reauth);
+DECLARE_ACCESSOR(wpas_dbus_setter_fast_reauth);
+DECLARE_ACCESSOR(wpas_dbus_getter_disconnect_reason);
+DECLARE_ACCESSOR(wpas_dbus_getter_disassociate_reason);
+DECLARE_ACCESSOR(wpas_dbus_getter_bss_expire_age);
+DECLARE_ACCESSOR(wpas_dbus_setter_bss_expire_age);
+DECLARE_ACCESSOR(wpas_dbus_getter_bss_expire_count);
+DECLARE_ACCESSOR(wpas_dbus_setter_bss_expire_count);
+DECLARE_ACCESSOR(wpas_dbus_getter_country);
+DECLARE_ACCESSOR(wpas_dbus_setter_country);
+DECLARE_ACCESSOR(wpas_dbus_getter_scan_interval);
+DECLARE_ACCESSOR(wpas_dbus_setter_scan_interval);
+DECLARE_ACCESSOR(wpas_dbus_getter_ifname);
+DECLARE_ACCESSOR(wpas_dbus_getter_driver);
+DECLARE_ACCESSOR(wpas_dbus_getter_bridge_ifname);
+DECLARE_ACCESSOR(wpas_dbus_getter_current_bss);
+DECLARE_ACCESSOR(wpas_dbus_getter_current_network);
+DECLARE_ACCESSOR(wpas_dbus_getter_current_auth_mode);
+DECLARE_ACCESSOR(wpas_dbus_getter_bsss);
+DECLARE_ACCESSOR(wpas_dbus_getter_networks);
+DECLARE_ACCESSOR(wpas_dbus_getter_pkcs11_engine_path);
+DECLARE_ACCESSOR(wpas_dbus_getter_pkcs11_module_path);
+DECLARE_ACCESSOR(wpas_dbus_getter_blobs);
+DECLARE_ACCESSOR(wpas_dbus_getter_bss_bssid);
+DECLARE_ACCESSOR(wpas_dbus_getter_bss_ssid);
+DECLARE_ACCESSOR(wpas_dbus_getter_bss_privacy);
+DECLARE_ACCESSOR(wpas_dbus_getter_bss_mode);
+DECLARE_ACCESSOR(wpas_dbus_getter_bss_signal);
+DECLARE_ACCESSOR(wpas_dbus_getter_bss_frequency);
+DECLARE_ACCESSOR(wpas_dbus_getter_bss_rates);
+DECLARE_ACCESSOR(wpas_dbus_getter_bss_wpa);
+DECLARE_ACCESSOR(wpas_dbus_getter_bss_rsn);
+DECLARE_ACCESSOR(wpas_dbus_getter_bss_wps);
+DECLARE_ACCESSOR(wpas_dbus_getter_bss_ies);
+DECLARE_ACCESSOR(wpas_dbus_getter_bss_age);
+DECLARE_ACCESSOR(wpas_dbus_getter_enabled);
+DECLARE_ACCESSOR(wpas_dbus_setter_enabled);
+DECLARE_ACCESSOR(wpas_dbus_getter_network_properties);
+DECLARE_ACCESSOR(wpas_dbus_setter_network_properties);
 
 DBusMessage * wpas_dbus_handler_wps_start(DBusMessage *message,
 					  struct wpa_supplicant *wpa_s);
@@ -297,20 +180,10 @@
 DBusMessage * wpas_dbus_handler_wps_cancel(DBusMessage *message,
 					   struct wpa_supplicant *wpa_s);
 
-dbus_bool_t wpas_dbus_getter_process_credentials(DBusMessageIter *iter,
-	DBusError *error, void *user_data);
-
-dbus_bool_t wpas_dbus_setter_process_credentials(DBusMessageIter *iter,
-						 DBusError *error,
-						 void *user_data);
-
-dbus_bool_t wpas_dbus_getter_config_methods(DBusMessageIter *iter,
-					    DBusError *error,
-					    void *user_data);
-
-dbus_bool_t wpas_dbus_setter_config_methods(DBusMessageIter *iter,
-					    DBusError *error,
-					    void *user_data);
+DECLARE_ACCESSOR(wpas_dbus_getter_process_credentials);
+DECLARE_ACCESSOR(wpas_dbus_setter_process_credentials);
+DECLARE_ACCESSOR(wpas_dbus_getter_config_methods);
+DECLARE_ACCESSOR(wpas_dbus_setter_config_methods);
 
 DBusMessage * wpas_dbus_handler_tdls_discover(DBusMessage *message,
 					      struct wpa_supplicant *wpa_s);
@@ -321,6 +194,9 @@
 DBusMessage * wpas_dbus_handler_tdls_teardown(DBusMessage *message,
 					      struct wpa_supplicant *wpa_s);
 
+DBusMessage * wpas_dbus_handler_save_config(DBusMessage *message,
+					    struct wpa_supplicant *wpa_s);
+
 DBusMessage * wpas_dbus_error_invalid_args(DBusMessage *message,
 					   const char *arg);
 DBusMessage * wpas_dbus_error_unknown_error(DBusMessage *message,
diff --git a/wpa_supplicant/dbus/dbus_new_handlers_p2p.c b/wpa_supplicant/dbus/dbus_new_handlers_p2p.c
index e9d60df..a0c5ff7 100644
--- a/wpa_supplicant/dbus/dbus_new_handlers_p2p.c
+++ b/wpa_supplicant/dbus/dbus_new_handlers_p2p.c
@@ -364,13 +364,14 @@
 			goto inv_args;
 
 		if (wpas_p2p_group_add_persistent(wpa_s, ssid, 0, freq, 0, 0, 0,
-						  NULL, 0)) {
+						  0, 0, NULL, 0, 0)) {
 			reply = wpas_dbus_error_unknown_error(
 				message,
 				"Failed to reinvoke a persistent group");
 			goto out;
 		}
-	} else if (wpas_p2p_group_add(wpa_s, persistent_group, freq, 0, 0))
+	} else if (wpas_p2p_group_add(wpa_s, persistent_group, freq, 0, 0, 0,
+				      0))
 		goto inv_args;
 
 out:
@@ -582,7 +583,7 @@
 
 	new_pin = wpas_p2p_connect(wpa_s, addr, pin, wps_method,
 				   persistent_group, 0, join, authorize_only,
-				   go_intent, freq, -1, 0, 0, 0);
+				   go_intent, freq, 0, -1, 0, 0, 0, 0);
 
 	if (new_pin >= 0) {
 		char npin[9];
@@ -733,8 +734,8 @@
 		if (ssid == NULL || ssid->disabled != 2)
 			goto err;
 
-		if (wpas_p2p_invite(wpa_s, peer_addr, ssid, NULL, 0, 0, 0, 0) <
-		    0) {
+		if (wpas_p2p_invite(wpa_s, peer_addr, ssid, NULL, 0, 0, 0, 0, 0,
+				    0) < 0) {
 			reply = wpas_dbus_error_unknown_error(
 				message,
 				"Failed to reinvoke a persistent group");
@@ -807,9 +808,9 @@
  * P2P Device property accessor methods.
  */
 
-dbus_bool_t wpas_dbus_getter_p2p_device_config(DBusMessageIter *iter,
-					       DBusError *error,
-					       void *user_data)
+dbus_bool_t wpas_dbus_getter_p2p_device_config(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
 {
 	struct wpa_supplicant *wpa_s = user_data;
 	DBusMessageIter variant_iter, dict_iter;
@@ -916,9 +917,9 @@
 }
 
 
-dbus_bool_t wpas_dbus_setter_p2p_device_config(DBusMessageIter *iter,
-					       DBusError *error,
-					       void *user_data)
+dbus_bool_t wpas_dbus_setter_p2p_device_config(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
 {
 	struct wpa_supplicant *wpa_s = user_data;
 	DBusMessageIter variant_iter, iter_dict;
@@ -1087,8 +1088,9 @@
 }
 
 
-dbus_bool_t wpas_dbus_getter_p2p_peers(DBusMessageIter *iter, DBusError *error,
-				       void *user_data)
+dbus_bool_t wpas_dbus_getter_p2p_peers(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
 {
 	struct wpa_supplicant *wpa_s = user_data;
 	struct p2p_data *p2p = wpa_s->global->p2p;
@@ -1201,8 +1203,9 @@
 }
 
 
-dbus_bool_t wpas_dbus_getter_p2p_role(DBusMessageIter *iter, DBusError *error,
-				      void *user_data)
+dbus_bool_t wpas_dbus_getter_p2p_role(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
 {
 	struct wpa_supplicant *wpa_s = user_data;
 	char *str;
@@ -1224,8 +1227,9 @@
 }
 
 
-dbus_bool_t wpas_dbus_getter_p2p_group(DBusMessageIter *iter, DBusError *error,
-				       void *user_data)
+dbus_bool_t wpas_dbus_getter_p2p_group(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
 {
 	struct wpa_supplicant *wpa_s = user_data;
 	char path_buf[WPAS_DBUS_OBJECT_PATH_MAX];
@@ -1243,8 +1247,9 @@
 }
 
 
-dbus_bool_t wpas_dbus_getter_p2p_peergo(DBusMessageIter *iter,
-					DBusError *error, void *user_data)
+dbus_bool_t wpas_dbus_getter_p2p_peergo(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
 {
 	struct wpa_supplicant *wpa_s = user_data;
 	char go_peer_obj_path[WPAS_DBUS_OBJECT_PATH_MAX], *path;
@@ -1271,9 +1276,9 @@
  * Peer object properties accessor methods
  */
 
-dbus_bool_t wpas_dbus_getter_p2p_peer_device_name(DBusMessageIter *iter,
-						  DBusError *error,
-						  void *user_data)
+dbus_bool_t wpas_dbus_getter_p2p_peer_device_name(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
 {
 	struct peer_handler_args *peer_args = user_data;
 	const struct p2p_peer_info *info;
@@ -1309,9 +1314,9 @@
 }
 
 
-dbus_bool_t wpas_dbus_getter_p2p_peer_manufacturer(DBusMessageIter *iter,
-						   DBusError *error,
-						   void *user_data)
+dbus_bool_t wpas_dbus_getter_p2p_peer_manufacturer(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
 {
 	struct peer_handler_args *peer_args = user_data;
 	const struct p2p_peer_info *info;
@@ -1346,9 +1351,9 @@
 }
 
 
-dbus_bool_t wpas_dbus_getter_p2p_peer_modelname(DBusMessageIter *iter,
-						DBusError *error,
-						void *user_data)
+dbus_bool_t wpas_dbus_getter_p2p_peer_modelname(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
 {
 	struct peer_handler_args *peer_args = user_data;
 	const struct p2p_peer_info *info;
@@ -1383,9 +1388,9 @@
 }
 
 
-dbus_bool_t wpas_dbus_getter_p2p_peer_modelnumber(DBusMessageIter *iter,
-						  DBusError *error,
-						  void *user_data)
+dbus_bool_t wpas_dbus_getter_p2p_peer_modelnumber(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
 {
 	struct peer_handler_args *peer_args = user_data;
 	const struct p2p_peer_info *info;
@@ -1420,9 +1425,9 @@
 }
 
 
-dbus_bool_t wpas_dbus_getter_p2p_peer_serialnumber(DBusMessageIter *iter,
-						   DBusError *error,
-						   void *user_data)
+dbus_bool_t wpas_dbus_getter_p2p_peer_serialnumber(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
 {
 	struct peer_handler_args *peer_args = user_data;
 	const struct p2p_peer_info *info;
@@ -1458,6 +1463,7 @@
 
 
 dbus_bool_t wpas_dbus_getter_p2p_peer_primary_device_type(
+	const struct wpa_dbus_property_desc *property_desc,
 	DBusMessageIter *iter, DBusError *error, void *user_data)
 {
 	struct peer_handler_args *peer_args = user_data;
@@ -1483,9 +1489,9 @@
 }
 
 
-dbus_bool_t wpas_dbus_getter_p2p_peer_config_method(DBusMessageIter *iter,
-						    DBusError *error,
-						    void *user_data)
+dbus_bool_t wpas_dbus_getter_p2p_peer_config_method(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
 {
 	struct peer_handler_args *peer_args = user_data;
 	const struct p2p_peer_info *info;
@@ -1508,9 +1514,9 @@
 }
 
 
-dbus_bool_t wpas_dbus_getter_p2p_peer_level(DBusMessageIter *iter,
-					    DBusError *error,
-					    void *user_data)
+dbus_bool_t wpas_dbus_getter_p2p_peer_level(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
 {
 	struct peer_handler_args *peer_args = user_data;
 	const struct p2p_peer_info *info;
@@ -1533,9 +1539,9 @@
 }
 
 
-dbus_bool_t wpas_dbus_getter_p2p_peer_device_capability(DBusMessageIter *iter,
-							DBusError *error,
-							void *user_data)
+dbus_bool_t wpas_dbus_getter_p2p_peer_device_capability(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
 {
 	struct peer_handler_args *peer_args = user_data;
 	const struct p2p_peer_info *info;
@@ -1558,9 +1564,9 @@
 }
 
 
-dbus_bool_t wpas_dbus_getter_p2p_peer_group_capability(DBusMessageIter *iter,
-						       DBusError *error,
-						       void *user_data)
+dbus_bool_t wpas_dbus_getter_p2p_peer_group_capability(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
 {
 	struct peer_handler_args *peer_args = user_data;
 	const struct p2p_peer_info *info;
@@ -1584,6 +1590,7 @@
 
 
 dbus_bool_t wpas_dbus_getter_p2p_peer_secondary_device_types(
+	const struct wpa_dbus_property_desc *property_desc,
 	DBusMessageIter *iter, DBusError *error, void *user_data)
 {
 	struct peer_handler_args *peer_args = user_data;
@@ -1649,9 +1656,9 @@
 }
 
 
-dbus_bool_t wpas_dbus_getter_p2p_peer_vendor_extension(DBusMessageIter *iter,
-						       DBusError *error,
-						       void *user_data)
+dbus_bool_t wpas_dbus_getter_p2p_peer_vendor_extension(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
 {
 	struct wpabuf *vendor_extension[P2P_MAX_WPS_VENDOR_EXT];
 	unsigned int i, num = 0;
@@ -1684,8 +1691,9 @@
 }
 
 
-dbus_bool_t wpas_dbus_getter_p2p_peer_ies(DBusMessageIter *iter,
-					  DBusError *error, void *user_data)
+dbus_bool_t wpas_dbus_getter_p2p_peer_ies(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
 {
 	struct peer_handler_args *peer_args = user_data;
 	const struct p2p_peer_info *info;
@@ -1709,9 +1717,9 @@
 }
 
 
-dbus_bool_t wpas_dbus_getter_p2p_peer_device_address(DBusMessageIter *iter,
-						     DBusError *error,
-						     void *user_data)
+dbus_bool_t wpas_dbus_getter_p2p_peer_device_address(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
 {
 	struct peer_handler_args *peer_args = user_data;
 	const struct p2p_peer_info *info;
@@ -1774,9 +1782,9 @@
 }
 
 
-dbus_bool_t wpas_dbus_getter_p2p_peer_groups(DBusMessageIter *iter,
-					     DBusError *error,
-					     void *user_data)
+dbus_bool_t wpas_dbus_getter_p2p_peer_groups(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
 {
 	struct peer_handler_args *peer_args = user_data;
 	const struct p2p_peer_info *info;
@@ -1842,9 +1850,9 @@
  *
  * Getter for "PersistentGroups" property.
  */
-dbus_bool_t wpas_dbus_getter_persistent_groups(DBusMessageIter *iter,
-					       DBusError *error,
-					       void *user_data)
+dbus_bool_t wpas_dbus_getter_persistent_groups(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
 {
 	struct wpa_supplicant *wpa_s = user_data;
 	struct wpa_ssid *ssid;
@@ -1904,21 +1912,21 @@
  *
  * Getter for "Properties" property of a persistent group.
  */
-dbus_bool_t wpas_dbus_getter_persistent_group_properties(DBusMessageIter *iter,
-							 DBusError *error,
-							 void *user_data)
+dbus_bool_t wpas_dbus_getter_persistent_group_properties(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
 {
 	struct network_handler_args *net = user_data;
 
 	/* Leveraging the fact that persistent group object is still
 	 * represented in same manner as network within.
 	 */
-	return wpas_dbus_getter_network_properties(iter, error, net);
+	return wpas_dbus_getter_network_properties(property_desc, iter, error, net);
 }
 
 
 /**
- * wpas_dbus_setter_persistent_group_properties - Get options for a persistent
+ * wpas_dbus_setter_persistent_group_properties - Set options for a persistent
  *	group
  * @iter: Pointer to incoming dbus message iter
  * @error: Location to store error on failure
@@ -1927,9 +1935,9 @@
  *
  * Setter for "Properties" property of a persistent group.
  */
-dbus_bool_t wpas_dbus_setter_persistent_group_properties(DBusMessageIter *iter,
-							 DBusError *error,
-							 void *user_data)
+dbus_bool_t wpas_dbus_setter_persistent_group_properties(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
 {
 	struct network_handler_args *net = user_data;
 	struct wpa_ssid *ssid = net->ssid;
@@ -2142,9 +2150,9 @@
  * Group object properties accessor methods
  */
 
-dbus_bool_t wpas_dbus_getter_p2p_group_members(DBusMessageIter *iter,
-					       DBusError *error,
-					       void *user_data)
+dbus_bool_t wpas_dbus_getter_p2p_group_members(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
 {
 	struct wpa_supplicant *wpa_s = user_data;
 	struct wpa_ssid *ssid;
@@ -2211,8 +2219,9 @@
 }
 
 
-dbus_bool_t wpas_dbus_getter_p2p_group_ssid(DBusMessageIter *iter,
-					    DBusError *error, void *user_data)
+dbus_bool_t wpas_dbus_getter_p2p_group_ssid(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
 {
 	struct wpa_supplicant *wpa_s = user_data;
 
@@ -2224,9 +2233,9 @@
 }
 
 
-dbus_bool_t wpas_dbus_getter_p2p_group_bssid(DBusMessageIter *iter,
-					     DBusError *error,
-					     void *user_data)
+dbus_bool_t wpas_dbus_getter_p2p_group_bssid(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
 {
 	struct wpa_supplicant *wpa_s = user_data;
 	u8 role = wpas_get_p2p_role(wpa_s);
@@ -2248,9 +2257,9 @@
 }
 
 
-dbus_bool_t wpas_dbus_getter_p2p_group_frequency(DBusMessageIter *iter,
-						 DBusError *error,
-						 void *user_data)
+dbus_bool_t wpas_dbus_getter_p2p_group_frequency(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
 {
 	struct wpa_supplicant *wpa_s = user_data;
 	u16 op_freq;
@@ -2271,9 +2280,9 @@
 }
 
 
-dbus_bool_t wpas_dbus_getter_p2p_group_passphrase(DBusMessageIter *iter,
-						  DBusError *error,
-						  void *user_data)
+dbus_bool_t wpas_dbus_getter_p2p_group_passphrase(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
 {
 	struct wpa_supplicant *wpa_s = user_data;
 	char *p_pass;
@@ -2292,8 +2301,9 @@
 }
 
 
-dbus_bool_t wpas_dbus_getter_p2p_group_psk(DBusMessageIter *iter,
-					   DBusError *error, void *user_data)
+dbus_bool_t wpas_dbus_getter_p2p_group_psk(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
 {
 	struct wpa_supplicant *wpa_s = user_data;
 	u8 *p_psk = NULL;
@@ -2313,9 +2323,9 @@
 }
 
 
-dbus_bool_t wpas_dbus_getter_p2p_group_vendor_ext(DBusMessageIter *iter,
-						  DBusError *error,
-						  void *user_data)
+dbus_bool_t wpas_dbus_getter_p2p_group_vendor_ext(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
 {
 	struct wpa_supplicant *wpa_s = user_data;
 	struct hostapd_data *hapd;
@@ -2348,9 +2358,9 @@
 }
 
 
-dbus_bool_t wpas_dbus_setter_p2p_group_vendor_ext(DBusMessageIter *iter,
-						  DBusError *error,
-						  void *user_data)
+dbus_bool_t wpas_dbus_setter_p2p_group_vendor_ext(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
 {
 	struct wpa_supplicant *wpa_s = user_data;
 	DBusMessageIter variant_iter, iter_dict, array_iter, sub;
@@ -2876,8 +2886,9 @@
 
 #ifdef CONFIG_WIFI_DISPLAY
 
-dbus_bool_t wpas_dbus_getter_global_wfd_ies(DBusMessageIter *iter,
-					    DBusError *error, void *user_data)
+dbus_bool_t wpas_dbus_getter_global_wfd_ies(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
 {
 	struct wpa_global *global = user_data;
 	struct wpabuf *ie;
@@ -2898,8 +2909,9 @@
 }
 
 
-dbus_bool_t wpas_dbus_setter_global_wfd_ies(DBusMessageIter *iter,
-					    DBusError *error, void *user_data)
+dbus_bool_t wpas_dbus_setter_global_wfd_ies(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
 {
 	struct wpa_global *global = user_data;
 	DBusMessageIter variant, array;
diff --git a/wpa_supplicant/dbus/dbus_new_handlers_p2p.h b/wpa_supplicant/dbus/dbus_new_handlers_p2p.h
index 2aecbbe..c4c0261 100644
--- a/wpa_supplicant/dbus/dbus_new_handlers_p2p.h
+++ b/wpa_supplicant/dbus/dbus_new_handlers_p2p.h
@@ -89,139 +89,50 @@
 /*
  * P2P Device property accessor methods.
  */
-dbus_bool_t wpas_dbus_setter_p2p_device_config(DBusMessageIter *iter,
-					       DBusError *error,
-					       void *user_data);
-
-dbus_bool_t wpas_dbus_getter_p2p_device_config(DBusMessageIter *iter,
-					       DBusError *error,
-					       void *user_data);
-
-dbus_bool_t wpas_dbus_getter_p2p_peers(DBusMessageIter *iter, DBusError *error,
-				       void *user_data);
-
-dbus_bool_t wpas_dbus_getter_p2p_role(DBusMessageIter *iter, DBusError *error,
-				      void *user_data);
-
-dbus_bool_t wpas_dbus_getter_p2p_group(DBusMessageIter *iter, DBusError *error,
-				       void *user_data);
-
-dbus_bool_t wpas_dbus_getter_p2p_peergo(DBusMessageIter *iter,
-					DBusError *error,
-					void *user_data);
+DECLARE_ACCESSOR(wpas_dbus_setter_p2p_device_config);
+DECLARE_ACCESSOR(wpas_dbus_getter_p2p_device_config);
+DECLARE_ACCESSOR(wpas_dbus_getter_p2p_peers);
+DECLARE_ACCESSOR(wpas_dbus_getter_p2p_role);
+DECLARE_ACCESSOR(wpas_dbus_getter_p2p_group);
+DECLARE_ACCESSOR(wpas_dbus_getter_p2p_peergo);
 
 /*
  * P2P Peer properties.
  */
-
-dbus_bool_t wpas_dbus_getter_p2p_peer_device_name(DBusMessageIter *iter,
-						  DBusError *error,
-						  void *user_data);
-
-dbus_bool_t wpas_dbus_getter_p2p_peer_manufacturer(DBusMessageIter *iter,
-						   DBusError *error,
-						   void *user_data);
-
-dbus_bool_t wpas_dbus_getter_p2p_peer_modelname(DBusMessageIter *iter,
-						DBusError *error,
-						void *user_data);
-
-dbus_bool_t wpas_dbus_getter_p2p_peer_modelnumber(DBusMessageIter *iter,
-						  DBusError *error,
-						  void *user_data);
-
-dbus_bool_t wpas_dbus_getter_p2p_peer_serialnumber(DBusMessageIter *iter,
-						   DBusError *error,
-						   void *user_data);
-
-dbus_bool_t wpas_dbus_getter_p2p_peer_primary_device_type(
-	DBusMessageIter *iter, DBusError *error, void *user_data);
-
-dbus_bool_t wpas_dbus_getter_p2p_peer_config_method(DBusMessageIter *iter,
-						    DBusError *error,
-						    void *user_data);
-
-dbus_bool_t wpas_dbus_getter_p2p_peer_level(DBusMessageIter *iter,
-					    DBusError *error,
-					    void *user_data);
-
-dbus_bool_t wpas_dbus_getter_p2p_peer_device_capability(DBusMessageIter *iter,
-							DBusError *error,
-							void *user_data);
-
-dbus_bool_t wpas_dbus_getter_p2p_peer_group_capability(DBusMessageIter *iter,
-						       DBusError *error,
-						       void *user_data);
-
-dbus_bool_t wpas_dbus_getter_p2p_peer_secondary_device_types(
-	DBusMessageIter *iter, DBusError *error, void *user_data);
-
-dbus_bool_t wpas_dbus_getter_p2p_peer_vendor_extension(DBusMessageIter *iter,
-						       DBusError *error,
-						       void *user_data);
-
-dbus_bool_t wpas_dbus_getter_p2p_peer_ies(DBusMessageIter *iter,
-					  DBusError *error,
-					  void *user_data);
-
-dbus_bool_t wpas_dbus_getter_p2p_peer_device_address(DBusMessageIter *iter,
-						     DBusError *error,
-						     void *user_data);
-
-dbus_bool_t wpas_dbus_getter_p2p_peer_groups(DBusMessageIter *iter,
-					     DBusError *error,
-					     void *user_data);
+DECLARE_ACCESSOR(wpas_dbus_getter_p2p_peer_device_name);
+DECLARE_ACCESSOR(wpas_dbus_getter_p2p_peer_manufacturer);
+DECLARE_ACCESSOR(wpas_dbus_getter_p2p_peer_modelname);
+DECLARE_ACCESSOR(wpas_dbus_getter_p2p_peer_modelnumber);
+DECLARE_ACCESSOR(wpas_dbus_getter_p2p_peer_serialnumber);
+DECLARE_ACCESSOR(wpas_dbus_getter_p2p_peer_primary_device_type);
+DECLARE_ACCESSOR(wpas_dbus_getter_p2p_peer_config_method);
+DECLARE_ACCESSOR(wpas_dbus_getter_p2p_peer_level);
+DECLARE_ACCESSOR(wpas_dbus_getter_p2p_peer_device_capability);
+DECLARE_ACCESSOR(wpas_dbus_getter_p2p_peer_group_capability);
+DECLARE_ACCESSOR(wpas_dbus_getter_p2p_peer_secondary_device_types);
+DECLARE_ACCESSOR(wpas_dbus_getter_p2p_peer_vendor_extension);
+DECLARE_ACCESSOR(wpas_dbus_getter_p2p_peer_ies);
+DECLARE_ACCESSOR(wpas_dbus_getter_p2p_peer_device_address);
+DECLARE_ACCESSOR(wpas_dbus_getter_p2p_peer_groups);
 
 /*
  * P2P Group properties
  */
-
-dbus_bool_t wpas_dbus_getter_p2p_group_members(DBusMessageIter *iter,
-					       DBusError *error,
-					       void *user_data);
-
-dbus_bool_t wpas_dbus_getter_p2p_group_ssid(DBusMessageIter *iter,
-					    DBusError *error,
-					    void *user_data);
-
-dbus_bool_t wpas_dbus_getter_p2p_group_bssid(DBusMessageIter *iter,
-					     DBusError *error,
-					     void *user_data);
-
-dbus_bool_t wpas_dbus_getter_p2p_group_frequency(DBusMessageIter *iter,
-						 DBusError *error,
-						 void *user_data);
-
-dbus_bool_t wpas_dbus_getter_p2p_group_passphrase(DBusMessageIter *iter,
-						  DBusError *error,
-						  void *user_data);
-
-dbus_bool_t wpas_dbus_getter_p2p_group_psk(DBusMessageIter *iter,
-					   DBusError *error,
-					   void *user_data);
-
-dbus_bool_t wpas_dbus_getter_p2p_group_vendor_ext(DBusMessageIter *iter,
-						  DBusError *error,
-						  void *user_data);
-
-dbus_bool_t wpas_dbus_setter_p2p_group_vendor_ext(DBusMessageIter *iter,
-						  DBusError *error,
-						  void *user_data);
+DECLARE_ACCESSOR(wpas_dbus_getter_p2p_group_members);
+DECLARE_ACCESSOR(wpas_dbus_getter_p2p_group_ssid);
+DECLARE_ACCESSOR(wpas_dbus_getter_p2p_group_bssid);
+DECLARE_ACCESSOR(wpas_dbus_getter_p2p_group_frequency);
+DECLARE_ACCESSOR(wpas_dbus_getter_p2p_group_passphrase);
+DECLARE_ACCESSOR(wpas_dbus_getter_p2p_group_psk);
+DECLARE_ACCESSOR(wpas_dbus_getter_p2p_group_vendor_ext);
+DECLARE_ACCESSOR(wpas_dbus_setter_p2p_group_vendor_ext);
 
 /*
  * P2P Persistent Groups and properties
  */
-
-dbus_bool_t wpas_dbus_getter_persistent_groups(DBusMessageIter *iter,
-					       DBusError *error,
-					       void *user_data);
-
-dbus_bool_t wpas_dbus_getter_persistent_group_properties(DBusMessageIter *iter,
-	DBusError *error, void *user_data);
-
-dbus_bool_t wpas_dbus_setter_persistent_group_properties(DBusMessageIter *iter,
-							 DBusError *error,
-							 void *user_data);
+DECLARE_ACCESSOR(wpas_dbus_getter_persistent_groups);
+DECLARE_ACCESSOR(wpas_dbus_getter_persistent_group_properties);
+DECLARE_ACCESSOR(wpas_dbus_setter_persistent_group_properties);
 
 DBusMessage * wpas_dbus_handler_add_persistent_group(
 	DBusMessage *message, struct wpa_supplicant *wpa_s);
@@ -233,15 +144,8 @@
 	DBusMessage *message, struct wpa_supplicant *wpa_s);
 
 #ifdef CONFIG_WIFI_DISPLAY
-
-dbus_bool_t wpas_dbus_getter_global_wfd_ies(DBusMessageIter *iter,
-					    DBusError *error,
-					    void *user_data);
-
-dbus_bool_t wpas_dbus_setter_global_wfd_ies(DBusMessageIter *iter,
-					    DBusError *error,
-					    void *user_data);
-
+DECLARE_ACCESSOR(wpas_dbus_getter_global_wfd_ies);
+DECLARE_ACCESSOR(wpas_dbus_setter_global_wfd_ies);
 #endif /* CONFIG_WIFI_DISPLAY */
 
 #endif /* DBUS_NEW_HANDLERS_P2P_H */
diff --git a/wpa_supplicant/dbus/dbus_new_handlers_wps.c b/wpa_supplicant/dbus/dbus_new_handlers_wps.c
index b2251ba..1d5dd1c 100644
--- a/wpa_supplicant/dbus/dbus_new_handlers_wps.c
+++ b/wpa_supplicant/dbus/dbus_new_handlers_wps.c
@@ -349,9 +349,9 @@
  * true if wps_cred_processing configuration field is not equal to 1 or false
  * if otherwise.
  */
-dbus_bool_t wpas_dbus_getter_process_credentials(DBusMessageIter *iter,
-						 DBusError *error,
-						 void *user_data)
+dbus_bool_t wpas_dbus_getter_process_credentials(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
 {
 	struct wpa_supplicant *wpa_s = user_data;
 	dbus_bool_t process = wpa_s->conf->wps_cred_processing != 1;
@@ -371,9 +371,9 @@
  * Setter for "ProcessCredentials" property. Sets credentials_processed on 2
  * if boolean argument is true or on 1 if otherwise.
  */
-dbus_bool_t wpas_dbus_setter_process_credentials(DBusMessageIter *iter,
-						 DBusError *error,
-						 void *user_data)
+dbus_bool_t wpas_dbus_setter_process_credentials(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
 {
 	struct wpa_supplicant *wpa_s = user_data;
 	dbus_bool_t process_credentials, old_pc;
@@ -407,9 +407,9 @@
  * Getter for "ConfigMethods" property. Returned boolean will be true if
  * providing the relevant string worked, or false otherwise.
  */
-dbus_bool_t wpas_dbus_getter_config_methods(DBusMessageIter *iter,
-					    DBusError *error,
-					    void *user_data)
+dbus_bool_t wpas_dbus_getter_config_methods(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
 {
 	struct wpa_supplicant *wpa_s = user_data;
 	char *methods = wpa_s->conf->config_methods;
@@ -431,9 +431,9 @@
  * Setter for "ConfigMethods" property. Sets the methods string, apply such
  * change and returns true on success. Returns false otherwise.
  */
-dbus_bool_t wpas_dbus_setter_config_methods(DBusMessageIter *iter,
-					    DBusError *error,
-					    void *user_data)
+dbus_bool_t wpas_dbus_setter_config_methods(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
 {
 	struct wpa_supplicant *wpa_s = user_data;
 	char *methods, *new_methods;
diff --git a/wpa_supplicant/dbus/dbus_new_helpers.c b/wpa_supplicant/dbus/dbus_new_helpers.c
index 45623f3..0115e32 100644
--- a/wpa_supplicant/dbus/dbus_new_helpers.c
+++ b/wpa_supplicant/dbus/dbus_new_helpers.c
@@ -46,7 +46,7 @@
 			goto error;
 
 		/* An error getting a property fails the request entirely */
-		if (!dsc->getter(&entry_iter, error, user_data)) {
+		if (!dsc->getter(dsc, &entry_iter, error, user_data)) {
 			wpa_printf(MSG_INFO,
 				   "dbus: %s dbus_interface=%s dbus_property=%s getter failed",
 				   __func__, dsc->dbus_interface,
@@ -176,7 +176,7 @@
 	dbus_message_iter_init_append(reply, &iter);
 
 	dbus_error_init(&error);
-	if (dsc->getter(&iter, &error, user_data) == FALSE) {
+	if (dsc->getter(dsc, &iter, &error, user_data) == FALSE) {
 		dbus_message_unref(reply);
 		reply = wpas_dbus_reply_new_from_error(
 			message, &error, DBUS_ERROR_FAILED,
@@ -213,7 +213,7 @@
 
 	/* Iter will now point to the property's new value */
 	dbus_error_init(&error);
-	if (dsc->setter(&iter, &error, user_data) == TRUE) {
+	if (dsc->setter(dsc, &iter, &error, user_data) == TRUE) {
 		/* Success */
 		reply = dbus_message_new_method_return(message);
 	} else {
@@ -627,7 +627,8 @@
 			return FALSE;
 
 		dbus_error_init(&error);
-		if (!dsc->getter(&entry_iter, &error, obj_dsc->user_data)) {
+		if (!dsc->getter(dsc, &entry_iter, &error, obj_dsc->user_data))
+		{
 			if (dbus_error_is_set(&error)) {
 				wpa_printf(MSG_ERROR,
 					   "dbus: %s: Cannot get new value of property %s: (%s) %s",
diff --git a/wpa_supplicant/dbus/dbus_new_helpers.h b/wpa_supplicant/dbus/dbus_new_helpers.h
index 6e2c1f1..7b63b28 100644
--- a/wpa_supplicant/dbus/dbus_new_helpers.h
+++ b/wpa_supplicant/dbus/dbus_new_helpers.h
@@ -16,9 +16,13 @@
 					      void *user_data);
 typedef void (*WPADBusArgumentFreeFunction)(void *handler_arg);
 
-typedef dbus_bool_t (*WPADBusPropertyAccessor)(DBusMessageIter *iter,
-					       DBusError *error,
-					       void *user_data);
+struct wpa_dbus_property_desc;
+typedef dbus_bool_t (*WPADBusPropertyAccessor)(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data);
+#define DECLARE_ACCESSOR(f) \
+dbus_bool_t f(const struct wpa_dbus_property_desc *property_desc, \
+	      DBusMessageIter *iter, DBusError *error, void *user_data)
 
 struct wpa_dbus_object_desc {
 	DBusConnection *connection;
@@ -89,6 +93,8 @@
 	WPADBusPropertyAccessor getter;
 	/* property setter function */
 	WPADBusPropertyAccessor setter;
+	/* other data */
+	const char *data;
 };
 
 
diff --git a/wpa_supplicant/dbus/dbus_new_introspect.c b/wpa_supplicant/dbus/dbus_new_introspect.c
index 6209c67..aee105b 100644
--- a/wpa_supplicant/dbus/dbus_new_introspect.c
+++ b/wpa_supplicant/dbus/dbus_new_introspect.c
@@ -38,7 +38,7 @@
 	if (!iface)
 		return NULL;
 	iface->dbus_interface = os_strdup(dbus_interface);
-	iface->xml = wpabuf_alloc(6000);
+	iface->xml = wpabuf_alloc(15000);
 	if (iface->dbus_interface == NULL || iface->xml == NULL) {
 		os_free(iface->dbus_interface);
 		wpabuf_free(iface->xml);
@@ -257,7 +257,7 @@
 	DBusMessage *reply;
 	struct wpabuf *xml;
 
-	xml = wpabuf_alloc(10000);
+	xml = wpabuf_alloc(20000);
 	if (xml == NULL)
 		return NULL;
 
diff --git a/wpa_supplicant/dbus/dbus_old_handlers.c b/wpa_supplicant/dbus/dbus_old_handlers.c
index 462c713..e8f62ef 100644
--- a/wpa_supplicant/dbus/dbus_old_handlers.c
+++ b/wpa_supplicant/dbus/dbus_old_handlers.c
@@ -809,10 +809,10 @@
 }
 
 
-static const char  const *dont_quote[] = {
+static const char * const dont_quote[] = {
 	"key_mgmt", "proto", "pairwise", "auth_alg", "group", "eap",
 	"opensc_engine_path", "pkcs11_engine_path", "pkcs11_module_path",
-	"bssid", NULL
+	"bssid", "scan_freq", "freq_list", NULL
 };
 
 
diff --git a/wpa_supplicant/defconfig b/wpa_supplicant/defconfig
index 7f627fd..e2e4bdc 100644
--- a/wpa_supplicant/defconfig
+++ b/wpa_supplicant/defconfig
@@ -31,6 +31,9 @@
 # Driver interface for Linux drivers using the nl80211 kernel interface
 CONFIG_DRIVER_NL80211=y
 
+# QCA vendor extensions to nl80211
+#CONFIG_DRIVER_NL80211_QCA=y
+
 # driver_nl80211.c requires libnl. If you are compiling it yourself
 # you may need to point hostapd to your version of libnl.
 #
@@ -276,6 +279,12 @@
 # none = Empty template
 #CONFIG_L2_PACKET=linux
 
+# Disable Linux packet socket workaround applicable for station interface
+# in a bridge for EAPOL frames. This should be uncommented only if the kernel
+# is known to not have the regression issue in packet socket behavior with
+# bridge interfaces (commit 'bridge: respect RFC2863 operational state')').
+#CONFIG_NO_LINUX_PACKET_SOCKET_WAR=y
+
 # PeerKey handshake for Station to Station Link (IEEE 802.11e DLS)
 CONFIG_PEERKEY=y
 
@@ -495,3 +504,12 @@
 #
 # External password backend for testing purposes (developer use)
 #CONFIG_EXT_PASSWORD_TEST=y
+
+# Enable Fast Session Transfer (FST)
+#CONFIG_FST=y
+
+# Enable CLI commands for FST testing
+#CONFIG_FST_TEST=y
+
+# OS X builds. This is only for building eapol_test.
+#CONFIG_OSX=y
diff --git a/wpa_supplicant/driver_i.h b/wpa_supplicant/driver_i.h
index d1f9f8b..699fd4f 100644
--- a/wpa_supplicant/driver_i.h
+++ b/wpa_supplicant/driver_i.h
@@ -100,12 +100,10 @@
 }
 
 static inline int wpa_drv_sched_scan(struct wpa_supplicant *wpa_s,
-				     struct wpa_driver_scan_params *params,
-				     u32 interval)
+				     struct wpa_driver_scan_params *params)
 {
 	if (wpa_s->driver->sched_scan)
-		return wpa_s->driver->sched_scan(wpa_s->drv_priv,
-						 params, interval);
+		return wpa_s->driver->sched_scan(wpa_s->drv_priv, params);
 	return -1;
 }
 
@@ -292,7 +290,7 @@
 	if (wpa_s->driver->send_mlme)
 		return wpa_s->driver->send_mlme(wpa_s->drv_priv,
 						data, data_len, noack,
-						freq);
+						freq, NULL, 0);
 	return -1;
 }
 
@@ -401,7 +399,7 @@
 	if (wpa_s->driver->if_add)
 		return wpa_s->driver->if_add(wpa_s->drv_priv, type, ifname,
 					     addr, bss_ctx, NULL, force_ifname,
-					     if_addr, bridge, 0);
+					     if_addr, bridge, 0, 0);
 	return -1;
 }
 
@@ -893,4 +891,30 @@
 	return wpa_s->driver->set_band(wpa_s->drv_priv, band);
 }
 
+static inline int wpa_drv_get_pref_freq_list(struct wpa_supplicant *wpa_s,
+					     enum wpa_driver_if_type if_type,
+					     unsigned int *num,
+					     unsigned int *freq_list)
+{
+	if (!wpa_s->driver->get_pref_freq_list)
+		return -1;
+	return wpa_s->driver->get_pref_freq_list(wpa_s->drv_priv, if_type,
+						 num, freq_list);
+}
+
+static inline int wpa_drv_set_prob_oper_freq(struct wpa_supplicant *wpa_s,
+					     unsigned int freq)
+{
+	if (!wpa_s->driver->set_prob_oper_freq)
+		return 0;
+	return wpa_s->driver->set_prob_oper_freq(wpa_s->drv_priv, freq);
+}
+
+static inline int wpa_drv_abort_scan(struct wpa_supplicant *wpa_s)
+{
+	if (!wpa_s->driver->abort_scan)
+		return -1;
+	return wpa_s->driver->abort_scan(wpa_s->drv_priv);
+}
+
 #endif /* DRIVER_I_H */
diff --git a/wpa_supplicant/eapol_test.c b/wpa_supplicant/eapol_test.c
index bde7508..7c7d54a 100644
--- a/wpa_supplicant/eapol_test.c
+++ b/wpa_supplicant/eapol_test.c
@@ -14,6 +14,7 @@
 
 #include "common.h"
 #include "utils/ext_password.h"
+#include "common/version.h"
 #include "config.h"
 #include "eapol_supp/eapol_supp_sm.h"
 #include "eap_peer/eap.h"
@@ -76,6 +77,9 @@
 
 	const char *pcsc_reader;
 	const char *pcsc_pin;
+
+	unsigned int ctrl_iface:1;
+	unsigned int id_req_sent:1;
 };
 
 static struct eapol_test_data eapol_test;
@@ -329,7 +333,11 @@
 
 static void eapol_test_eapol_done_cb(void *ctx)
 {
+	struct eapol_test_data *e = ctx;
+
 	printf("WPA: EAPOL processing complete\n");
+	wpa_supplicant_cancel_auth_timeout(e->wpa_s);
+	wpa_supplicant_set_state(e->wpa_s, WPA_COMPLETED);
 }
 
 
@@ -407,6 +415,9 @@
 {
 	struct eapol_test_data *e = ctx;
 	printf("eapol_sm_cb: result=%d\n", result);
+	e->id_req_sent = 0;
+	if (e->ctrl_iface)
+		return;
 	e->eapol_test_num_reauths--;
 	if (e->eapol_test_num_reauths < 0)
 		eloop_terminate();
@@ -552,11 +563,21 @@
 }
 
 
+static enum wpa_states eapol_test_get_state(void *ctx)
+{
+	struct eapol_test_data *e = ctx;
+	struct wpa_supplicant *wpa_s = e->wpa_s;
+
+	return wpa_s->wpa_state;
+}
+
+
 static int test_eapol(struct eapol_test_data *e, struct wpa_supplicant *wpa_s,
 		      struct wpa_ssid *ssid)
 {
 	struct eapol_config eapol_conf;
 	struct eapol_ctx *ctx;
+	struct wpa_sm_ctx *wctx;
 
 	ctx = os_zalloc(sizeof(*ctx));
 	if (ctx == NULL) {
@@ -590,6 +611,25 @@
 		return -1;
 	}
 
+	wpa_s->key_mgmt = WPA_KEY_MGMT_IEEE8021X_NO_WPA;
+	wctx = os_zalloc(sizeof(*wctx));
+	if (wctx == NULL) {
+		os_free(ctx);
+		return -1;
+	}
+	wctx->ctx = e;
+	wctx->msg_ctx = wpa_s;
+	wctx->get_state = eapol_test_get_state;
+	wpa_s->wpa = wpa_sm_init(wctx);
+	if (!wpa_s->wpa) {
+		os_free(ctx);
+		os_free(wctx);
+		return -1;
+	}
+
+	if (!ssid)
+		return 0;
+
 	wpa_s->current_ssid = ssid;
 	os_memset(&eapol_conf, 0, sizeof(eapol_conf));
 	eapol_conf.accept_802_1x_keys = 1;
@@ -614,6 +654,8 @@
 {
 	struct extra_radius_attr *p, *prev;
 
+	wpa_sm_deinit(wpa_s->wpa);
+	wpa_s->wpa = NULL;
 	radius_client_deinit(e->radius);
 	wpabuf_free(e->last_eap_radius);
 	radius_msg_free(e->last_recv_radius);
@@ -757,6 +799,8 @@
 		break;
 	case EAP_CODE_FAILURE:
 		os_strlcpy(buf, "EAP Failure", sizeof(buf));
+		if (e->ctrl_iface)
+			break;
 		eloop_terminate();
 		break;
 	default:
@@ -901,25 +945,66 @@
 	if ((hdr->code == RADIUS_CODE_ACCESS_ACCEPT &&
 	     e->eapol_test_num_reauths < 0) ||
 	    hdr->code == RADIUS_CODE_ACCESS_REJECT) {
-		eloop_terminate();
+		if (!e->ctrl_iface)
+			eloop_terminate();
 	}
 
 	return RADIUS_RX_QUEUED;
 }
 
 
+static int driver_get_ssid(void *priv, u8 *ssid)
+{
+	ssid[0] = 0;
+	return 0;
+}
+
+
+static int driver_get_bssid(void *priv, u8 *bssid)
+{
+	struct eapol_test_data *e = priv;
+
+	if (e->ctrl_iface && !e->id_req_sent) {
+		eloop_register_timeout(0, 0, send_eap_request_identity,
+				       e->wpa_s, NULL);
+		e->id_req_sent = 1;
+	}
+
+	os_memset(bssid, 0, ETH_ALEN);
+	bssid[5] = 1;
+	return 0;
+}
+
+
+static int driver_get_capa(void *priv, struct wpa_driver_capa *capa)
+{
+	os_memset(capa, 0, sizeof(*capa));
+	capa->flags = WPA_DRIVER_FLAGS_WIRED;
+	return 0;
+}
+
+
+struct wpa_driver_ops eapol_test_drv_ops = {
+	.name = "test",
+	.get_ssid = driver_get_ssid,
+	.get_bssid = driver_get_bssid,
+	.get_capa = driver_get_capa,
+};
+
 static void wpa_init_conf(struct eapol_test_data *e,
 			  struct wpa_supplicant *wpa_s, const char *authsrv,
 			  int port, const char *secret,
-			  const char *cli_addr)
+			  const char *cli_addr, const char *ifname)
 {
 	struct hostapd_radius_server *as;
 	int res;
 
+	wpa_s->driver = &eapol_test_drv_ops;
+	wpa_s->drv_priv = e;
 	wpa_s->bssid[5] = 1;
 	os_memcpy(wpa_s->own_addr, e->own_addr, ETH_ALEN);
 	e->own_ip_addr.s_addr = htonl((127 << 24) | 1);
-	os_strlcpy(wpa_s->ifname, "test", sizeof(wpa_s->ifname));
+	os_strlcpy(wpa_s->ifname, ifname, sizeof(wpa_s->ifname));
 
 	e->radius_conf = os_zalloc(sizeof(struct hostapd_radius_servers));
 	assert(e->radius_conf != NULL);
@@ -1155,13 +1240,13 @@
 static void usage(void)
 {
 	printf("usage:\n"
-	       "eapol_test [-enWS] -c<conf> [-a<AS IP>] [-p<AS port>] "
+	       "eapol_test [-enWSv] -c<conf> [-a<AS IP>] [-p<AS port>] "
 	       "[-s<AS secret>]\\\n"
 	       "           [-r<count>] [-t<timeout>] [-C<Connect-Info>] \\\n"
 	       "           [-M<client MAC address>] [-o<server cert file] \\\n"
 	       "           [-N<attr spec>] [-R<PC/SC reader>] "
 	       "[-P<PC/SC PIN>] \\\n"
-	       "           [-A<client IP>]\n"
+	       "           [-A<client IP>] [-i<ifname>] [-T<ctrl_iface>]\n"
 	       "eapol_test scard\n"
 	       "eapol_test sim <PIN> <num triplets> [debug]\n"
 	       "\n");
@@ -1180,6 +1265,7 @@
 	       "  -W = wait for a control interface monitor before starting\n"
 	       "  -S = save configuration after authentication\n"
 	       "  -n = no MPPE keys expected\n"
+	       "  -v = show version\n"
 	       "  -t<timeout> = sets timeout in seconds (default: 30 s)\n"
 	       "  -C<Connect-Info> = RADIUS Connect-Info (default: "
 	       "CONNECT 11Mbps 802.11b)\n"
@@ -1216,6 +1302,8 @@
 	int timeout = 30;
 	char *pos;
 	struct extra_radius_attr *p = NULL, *p1;
+	const char *ifname = "test";
+	const char *ctrl_iface = NULL;
 
 	if (os_program_init())
 		return -1;
@@ -1231,7 +1319,7 @@
 	wpa_debug_show_keys = 1;
 
 	for (;;) {
-		c = getopt(argc, argv, "a:A:c:C:eM:nN:o:p:P:r:R:s:St:W");
+		c = getopt(argc, argv, "a:A:c:C:ei:M:nN:o:p:P:r:R:s:St:T:vW");
 		if (c < 0)
 			break;
 		switch (c) {
@@ -1250,6 +1338,9 @@
 		case 'e':
 			eapol_test.req_eap_key_name = 1;
 			break;
+		case 'i':
+			ifname = optarg;
+			break;
 		case 'M':
 			if (hwaddr_aton(optarg, eapol_test.own_addr)) {
 				usage();
@@ -1290,6 +1381,13 @@
 		case 't':
 			timeout = atoi(optarg);
 			break;
+		case 'T':
+			ctrl_iface = optarg;
+			eapol_test.ctrl_iface = 1;
+			break;
+		case 'v':
+			printf("eapol_test v" VERSION_STR "\n");
+			return 0;
 		case 'W':
 			wait_for_monitor++;
 			break;
@@ -1336,7 +1434,7 @@
 					  &argv[optind + 1]);
 	}
 
-	if (conf == NULL) {
+	if (conf == NULL && !ctrl_iface) {
 		usage();
 		printf("Configuration file is required.\n");
 		return -1;
@@ -1358,12 +1456,15 @@
 	eapol_test.wpa_s = &wpa_s;
 	dl_list_init(&wpa_s.bss);
 	dl_list_init(&wpa_s.bss_id);
-	wpa_s.conf = wpa_config_read(conf, NULL);
+	if (conf)
+		wpa_s.conf = wpa_config_read(conf, NULL);
+	else
+		wpa_s.conf = wpa_config_alloc_empty(ctrl_iface, NULL);
 	if (wpa_s.conf == NULL) {
 		printf("Failed to parse configuration file '%s'.\n", conf);
 		return -1;
 	}
-	if (wpa_s.conf->ssid == NULL) {
+	if (!ctrl_iface && wpa_s.conf->ssid == NULL) {
 		printf("No networks defined.\n");
 		return -1;
 	}
@@ -1374,7 +1475,7 @@
 	}
 
 	wpa_init_conf(&eapol_test, &wpa_s, as_addr, as_port, as_secret,
-		      cli_addr);
+		      cli_addr, ifname);
 	wpa_s.ctrl_iface = wpa_supplicant_ctrl_iface_init(&wpa_s);
 	if (wpa_s.ctrl_iface == NULL) {
 		printf("Failed to initialize control interface '%s'.\n"
@@ -1387,7 +1488,8 @@
 		       wpa_s.conf->ctrl_interface);
 		return -1;
 	}
-	if (wpa_supplicant_scard_init(&wpa_s, wpa_s.conf->ssid))
+	if (wpa_s.conf->ssid &&
+	    wpa_supplicant_scard_init(&wpa_s, wpa_s.conf->ssid))
 		return -1;
 
 	if (test_eapol(&eapol_test, &wpa_s, wpa_s.conf->ssid))
@@ -1399,9 +1501,12 @@
 	if (wait_for_monitor)
 		wpa_supplicant_ctrl_iface_wait(wpa_s.ctrl_iface);
 
-	eloop_register_timeout(timeout, 0, eapol_test_timeout, &eapol_test,
-			       NULL);
-	eloop_register_timeout(0, 0, send_eap_request_identity, &wpa_s, NULL);
+	if (!ctrl_iface) {
+		eloop_register_timeout(timeout, 0, eapol_test_timeout,
+				       &eapol_test, NULL);
+		eloop_register_timeout(0, 0, send_eap_request_identity, &wpa_s,
+				       NULL);
+	}
 	eloop_register_signal_terminate(eapol_test_terminate, &wpa_s);
 	eloop_register_signal_reconfig(eapol_test_terminate, &wpa_s);
 	eloop_run();
diff --git a/wpa_supplicant/eapol_test.py b/wpa_supplicant/eapol_test.py
new file mode 100644
index 0000000..80e7dfc
--- /dev/null
+++ b/wpa_supplicant/eapol_test.py
@@ -0,0 +1,142 @@
+#!/usr/bin/env python2
+#
+# eapol_test controller
+# Copyright (c) 2015, Jouni Malinen <j@w1.fi>
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+import argparse
+import logging
+import os
+import Queue
+import sys
+import threading
+
+logger = logging.getLogger()
+dir = os.path.dirname(os.path.realpath(sys.modules[__name__].__file__))
+sys.path.append(os.path.join(dir, '..', 'wpaspy'))
+import wpaspy
+wpas_ctrl = '/tmp/eapol_test'
+
+class eapol_test:
+    def __init__(self, ifname):
+        self.ifname = ifname
+        self.ctrl = wpaspy.Ctrl(os.path.join(wpas_ctrl, ifname))
+        if "PONG" not in self.ctrl.request("PING"):
+            raise Exception("Failed to connect to eapol_test (%s)" % ifname)
+        self.mon = wpaspy.Ctrl(os.path.join(wpas_ctrl, ifname))
+        self.mon.attach()
+
+    def add_network(self):
+        id = self.request("ADD_NETWORK")
+        if "FAIL" in id:
+            raise Exception("ADD_NETWORK failed")
+        return int(id)
+
+    def remove_network(self, id):
+        id = self.request("REMOVE_NETWORK " + str(id))
+        if "FAIL" in id:
+            raise Exception("REMOVE_NETWORK failed")
+        return None
+
+    def set_network(self, id, field, value):
+        res = self.request("SET_NETWORK " + str(id) + " " + field + " " + value)
+        if "FAIL" in res:
+            raise Exception("SET_NETWORK failed")
+        return None
+
+    def set_network_quoted(self, id, field, value):
+        res = self.request("SET_NETWORK " + str(id) + " " + field + ' "' + value + '"')
+        if "FAIL" in res:
+            raise Exception("SET_NETWORK failed")
+        return None
+
+    def request(self, cmd, timeout=10):
+        return self.ctrl.request(cmd, timeout=timeout)
+
+    def wait_event(self, events, timeout=10):
+        start = os.times()[4]
+        while True:
+            while self.mon.pending():
+                ev = self.mon.recv()
+                logger.debug(self.ifname + ": " + ev)
+                for event in events:
+                    if event in ev:
+                        return ev
+            now = os.times()[4]
+            remaining = start + timeout - now
+            if remaining <= 0:
+                break
+            if not self.mon.pending(timeout=remaining):
+                break
+        return None
+
+def run(ifname, count, no_fast_reauth, res):
+    et = eapol_test(ifname)
+
+    et.request("AP_SCAN 0")
+    if no_fast_reauth:
+        et.request("SET fast_reauth 0")
+    else:
+        et.request("SET fast_reauth 1")
+    id = et.add_network()
+    et.set_network(id, "key_mgmt", "IEEE8021X")
+    et.set_network(id, "eapol_flags", "0")
+    et.set_network(id, "eap", "TLS")
+    et.set_network_quoted(id, "identity", "user")
+    et.set_network_quoted(id, "ca_cert", 'ca.pem')
+    et.set_network_quoted(id, "client_cert", 'client.pem')
+    et.set_network_quoted(id, "private_key", 'client.key')
+    et.set_network_quoted(id, "private_key_passwd", 'whatever')
+    et.set_network(id, "disabled", "0")
+
+    fail = False
+    for i in range(count):
+        et.request("REASSOCIATE")
+        ev = et.wait_event(["CTRL-EVENT-CONNECTED", "CTRL-EVENT-EAP-FAILURE"])
+        if ev is None or "CTRL-EVENT-CONNECTED" not in ev:
+            fail = True
+            break
+
+    et.remove_network(id)
+
+    if fail:
+        res.put("FAIL (%d OK)" % i)
+    else:
+        res.put("PASS %d" % (i + 1))
+
+def main():
+    parser = argparse.ArgumentParser(description='eapol_test controller')
+    parser.add_argument('--ctrl', help='control interface directory')
+    parser.add_argument('--num', help='number of processes')
+    parser.add_argument('--iter', help='number of iterations')
+    parser.add_argument('--no-fast-reauth', action='store_true',
+                        dest='no_fast_reauth',
+                        help='disable TLS session resumption')
+    args = parser.parse_args()
+
+    num = int(args.num)
+    iter = int(args.iter)
+    if args.ctrl:
+        global wpas_ctrl
+        wpas_ctrl = args.ctrl
+
+    t = {}
+    res = {}
+    for i in range(num):
+        res[i] = Queue.Queue()
+        t[i] = threading.Thread(target=run, args=(str(i), iter,
+                                                  args.no_fast_reauth, res[i]))
+    for i in range(num):
+        t[i].start()
+    for i in range(num):
+        t[i].join()
+        try:
+            results = res[i].get(False)
+        except:
+            results = "N/A"
+        print "%d: %s" % (i, results)
+
+if __name__ == "__main__":
+    main()
diff --git a/wpa_supplicant/events.c b/wpa_supplicant/events.c
index 6b0c685..6eb3e88 100644
--- a/wpa_supplicant/events.c
+++ b/wpa_supplicant/events.c
@@ -23,6 +23,7 @@
 #include "eap_peer/eap.h"
 #include "ap/hostapd.h"
 #include "p2p/p2p.h"
+#include "fst/fst.h"
 #include "wnm_sta.h"
 #include "notify.h"
 #include "common/ieee802_11_defs.h"
@@ -71,6 +72,7 @@
 }
 
 
+#ifndef CONFIG_NO_SCAN_PROCESSING
 /**
  * wpas_reenabled_network_time - Time until first network is re-enabled
  * @wpa_s: Pointer to wpa_supplicant data
@@ -106,6 +108,7 @@
 
 	return res;
 }
+#endif /* CONFIG_NO_SCAN_PROCESSING */
 
 
 void wpas_network_reenabled(void *eloop_ctx, void *timeout_ctx)
@@ -286,9 +289,6 @@
 	os_memset(wpa_s->bssid, 0, ETH_ALEN);
 	os_memset(wpa_s->pending_bssid, 0, ETH_ALEN);
 	sme_clear_on_disassoc(wpa_s);
-#ifdef CONFIG_P2P
-	os_memset(wpa_s->go_dev_addr, 0, ETH_ALEN);
-#endif /* CONFIG_P2P */
 	wpa_s->current_bss = NULL;
 	wpa_s->assoc_freq = 0;
 
@@ -305,6 +305,7 @@
 	wpa_s->key_mgmt = 0;
 
 	wpas_rrm_reset(wpa_s);
+	wpa_s->wnmsleep_used = 0;
 }
 
 
@@ -566,6 +567,13 @@
 			break;
 		}
 #endif /* CONFIG_IEEE80211W */
+		if ((ie.capabilities & WPA_CAPABILITY_MFPR) &&
+		    wpas_get_ssid_pmf(wpa_s, ssid) ==
+		    NO_MGMT_FRAME_PROTECTION) {
+			wpa_dbg(wpa_s, MSG_DEBUG,
+				"   skip RSN IE - no mgmt frame protection enabled but AP requires it");
+			break;
+		}
 
 		wpa_dbg(wpa_s, MSG_DEBUG, "   selected based on RSN IE");
 		return 1;
@@ -830,9 +838,9 @@
 	osen = ie != NULL;
 
 	wpa_dbg(wpa_s, MSG_DEBUG, "%d: " MACSTR " ssid='%s' "
-		"wpa_ie_len=%u rsn_ie_len=%u caps=0x%x level=%d%s%s%s",
+		"wpa_ie_len=%u rsn_ie_len=%u caps=0x%x level=%d freq=%d %s%s%s",
 		i, MAC2STR(bss->bssid), wpa_ssid_txt(bss->ssid, bss->ssid_len),
-		wpa_ie_len, rsn_ie_len, bss->caps, bss->level,
+		wpa_ie_len, rsn_ie_len, bss->caps, bss->level, bss->freq,
 		wpa_bss_get_vendor_ie(bss, WPS_IE_VENDOR_TYPE) ? " wps" : "",
 		(wpa_bss_get_vendor_ie(bss, P2P_IE_VENDOR_TYPE) ||
 		 wpa_bss_get_vendor_ie_beacon(bss, P2P_IE_VENDOR_TYPE)) ?
@@ -1038,6 +1046,19 @@
 		 */
 #endif /* CONFIG_P2P */
 
+		if (os_reltime_before(&bss->last_update, &wpa_s->scan_min_time))
+		{
+			struct os_reltime diff;
+
+			os_reltime_sub(&wpa_s->scan_min_time,
+				       &bss->last_update, &diff);
+			wpa_dbg(wpa_s, MSG_DEBUG,
+				"   skip - scan result not recent enough (%u.%06u seconds too old)",
+				(unsigned int) diff.sec,
+				(unsigned int) diff.usec);
+			continue;
+		}
+
 		/* Matching configuration found */
 		return ssid;
 	}
@@ -1190,6 +1211,7 @@
 #endif /* CONFIG_P2P */
 
 #ifdef CONFIG_WPS
+		wpas_wps_pbc_overlap(wpa_s);
 		wpas_wps_cancel(wpa_s);
 #endif /* CONFIG_WPS */
 		return -1;
@@ -1405,6 +1427,8 @@
 			return -1;
 		if (!own_request)
 			return -1;
+		if (data && data->scan_info.external_scan)
+			return -1;
 		wpa_dbg(wpa_s, MSG_DEBUG, "Failed to get scan results - try "
 			"scanning again");
 		wpa_supplicant_req_new_scan(wpa_s, 1, 0);
@@ -1429,7 +1453,7 @@
 #endif /* CONFIG_NO_RANDOM_POOL */
 
 	if (own_request && wpa_s->scan_res_handler &&
-	    (wpa_s->own_scan_running || !wpa_s->radio->external_scan_running)) {
+	    !(data && data->scan_info.external_scan)) {
 		void (*scan_res_handler)(struct wpa_supplicant *wpa_s,
 					 struct wpa_scan_results *scan_res);
 
@@ -1450,9 +1474,11 @@
 	}
 
 	wpa_dbg(wpa_s, MSG_DEBUG, "New scan results available (own=%u ext=%u)",
-		wpa_s->own_scan_running, wpa_s->radio->external_scan_running);
+		wpa_s->own_scan_running,
+		data ? data->scan_info.external_scan : 0);
 	if (wpa_s->last_scan_req == MANUAL_SCAN_REQ &&
-	    wpa_s->manual_scan_use_id && wpa_s->own_scan_running) {
+	    wpa_s->manual_scan_use_id && wpa_s->own_scan_running &&
+	    own_request && !(data && data->scan_info.external_scan)) {
 		wpa_msg_ctrl(wpa_s, MSG_INFO, WPA_EVENT_SCAN_RESULTS "id=%u",
 			     wpa_s->manual_scan_id);
 		wpa_s->manual_scan_use_id = 0;
@@ -1463,7 +1489,7 @@
 
 	wpas_notify_scan_done(wpa_s, 1);
 
-	if (!wpa_s->own_scan_running && wpa_s->radio->external_scan_running) {
+	if (data && data->scan_info.external_scan) {
 		wpa_dbg(wpa_s, MSG_DEBUG, "Do not use results from externally requested scan operation for network selection");
 		wpa_scan_results_free(scan_res);
 		return 0;
@@ -1494,7 +1520,7 @@
 
 	wpa_scan_results_free(scan_res);
 
-	if (wpa_s->scan_work) {
+	if (own_request && wpa_s->scan_work) {
 		struct wpa_radio_work *work = wpa_s->scan_work;
 		wpa_s->scan_work = NULL;
 		radio_work_done(work);
@@ -1504,7 +1530,7 @@
 
 scan_work_done:
 	wpa_scan_results_free(scan_res);
-	if (wpa_s->scan_work) {
+	if (own_request && wpa_s->scan_work) {
 		struct wpa_radio_work *work = wpa_s->scan_work;
 		wpa_s->scan_work = NULL;
 		radio_work_done(work);
@@ -1818,6 +1844,50 @@
 #endif /* CONFIG_INTERWORKING */
 
 
+#ifdef CONFIG_FST
+static int wpas_fst_update_mbie(struct wpa_supplicant *wpa_s,
+				const u8 *ie, size_t ie_len)
+{
+	struct mb_ies_info mb_ies;
+
+	if (!ie || !ie_len || !wpa_s->fst)
+	    return -ENOENT;
+
+	os_memset(&mb_ies, 0, sizeof(mb_ies));
+
+	while (ie_len >= 2 && mb_ies.nof_ies < MAX_NOF_MB_IES_SUPPORTED) {
+		size_t len;
+
+		len = 2 + ie[1];
+		if (len > ie_len) {
+			wpa_hexdump(MSG_DEBUG, "FST: Truncated IE found",
+				    ie, ie_len);
+			break;
+		}
+
+		if (ie[0] == WLAN_EID_MULTI_BAND) {
+			wpa_printf(MSG_DEBUG, "MB IE of %u bytes found",
+				   (unsigned int) len);
+			mb_ies.ies[mb_ies.nof_ies].ie = ie + 2;
+			mb_ies.ies[mb_ies.nof_ies].ie_len = len - 2;
+			mb_ies.nof_ies++;
+		}
+
+		ie_len -= len;
+		ie += len;
+	}
+
+	if (mb_ies.nof_ies > 0) {
+		wpabuf_free(wpa_s->received_mb_ies);
+		wpa_s->received_mb_ies = mb_ies_by_info(&mb_ies);
+		return 0;
+	}
+
+	return -ENOENT;
+}
+#endif /* CONFIG_FST */
+
+
 static int wpa_supplicant_event_associnfo(struct wpa_supplicant *wpa_s,
 					  union wpa_event_data *data)
 {
@@ -2038,6 +2108,45 @@
 }
 
 
+static void wpas_fst_update_mb_assoc(struct wpa_supplicant *wpa_s,
+				     union wpa_event_data *data)
+{
+#ifdef CONFIG_FST
+	struct assoc_info *ai = data ? &data->assoc_info : NULL;
+	struct wpa_bss *bss = wpa_s->current_bss;
+	const u8 *ieprb, *iebcn;
+
+	wpabuf_free(wpa_s->received_mb_ies);
+	wpa_s->received_mb_ies = NULL;
+
+	if (ai &&
+	    !wpas_fst_update_mbie(wpa_s, ai->resp_ies, ai->resp_ies_len)) {
+		wpa_printf(MSG_DEBUG,
+			   "FST: MB IEs updated from Association Response frame");
+		return;
+	}
+
+	if (ai &&
+	    !wpas_fst_update_mbie(wpa_s, ai->beacon_ies, ai->beacon_ies_len)) {
+		wpa_printf(MSG_DEBUG,
+			   "FST: MB IEs updated from association event Beacon IEs");
+		return;
+	}
+
+	if (!bss)
+		return;
+
+	ieprb = (const u8 *) (bss + 1);
+	iebcn = ieprb + bss->ie_len;
+
+	if (!wpas_fst_update_mbie(wpa_s, ieprb, bss->ie_len))
+		wpa_printf(MSG_DEBUG, "FST: MB IEs updated from bss IE");
+	else if (!wpas_fst_update_mbie(wpa_s, iebcn, bss->beacon_ie_len))
+		wpa_printf(MSG_DEBUG, "FST: MB IEs updated from bss beacon IE");
+#endif /* CONFIG_FST */
+}
+
+
 static void wpa_supplicant_event_assoc(struct wpa_supplicant *wpa_s,
 				       union wpa_event_data *data)
 {
@@ -2102,6 +2211,8 @@
 				"WPA/RSN IEs not updated");
 	}
 
+	wpas_fst_update_mb_assoc(wpa_s, data);
+
 #ifdef CONFIG_SME
 	os_memcpy(wpa_s->sme.prev_bssid, bssid, ETH_ALEN);
 	wpa_s->sme.prev_bssid_set = 1;
@@ -2343,7 +2454,8 @@
 	if (!wpa_s->disconnected &&
 	    (!wpa_s->auto_reconnect_disabled ||
 	     wpa_s->key_mgmt == WPA_KEY_MGMT_WPS ||
-	     wpas_wps_searching(wpa_s))) {
+	     wpas_wps_searching(wpa_s) ||
+	     wpas_wps_reenable_networks_pending(wpa_s))) {
 		wpa_dbg(wpa_s, MSG_DEBUG, "Auto connect enabled: try to "
 			"reconnect (wps=%d/%d wpa_state=%d)",
 			wpa_s->key_mgmt == WPA_KEY_MGMT_WPS,
@@ -2372,7 +2484,8 @@
 			"try to re-connect");
 		wpa_s->reassociate = 0;
 		wpa_s->disconnected = 1;
-		wpa_supplicant_cancel_sched_scan(wpa_s);
+		if (!wpa_s->pno)
+			wpa_supplicant_cancel_sched_scan(wpa_s);
 	}
 	bssid = wpa_s->bssid;
 	if (is_zero_ether_addr(bssid))
@@ -2993,25 +3106,13 @@
 	if (wpa_s->drv_priv == NULL)
 		return; /* Ignore event during drv initialization */
 
-	free_hw_features(wpa_s);
-	wpa_s->hw.modes = wpa_drv_get_hw_feature_data(
-		wpa_s, &wpa_s->hw.num_modes, &wpa_s->hw.flags);
-
-	wpas_p2p_update_channel_list(wpa_s);
-
-	/*
-	 * Check other interfaces to see if they share the same radio. If
-	 * so, they get updated with this same hw mode info.
-	 */
 	dl_list_for_each(ifs, &wpa_s->radio->ifaces, struct wpa_supplicant,
 			 radio_list) {
-		if (ifs != wpa_s) {
-			wpa_printf(MSG_DEBUG, "%s: Updating hw mode",
-				   ifs->ifname);
-			free_hw_features(ifs);
-			ifs->hw.modes = wpa_drv_get_hw_feature_data(
-				ifs, &ifs->hw.num_modes, &ifs->hw.flags);
-		}
+		wpa_printf(MSG_DEBUG, "%s: Updating hw mode",
+			   ifs->ifname);
+		free_hw_features(ifs);
+		ifs->hw.modes = wpa_drv_get_hw_feature_data(
+			ifs, &ifs->hw.num_modes, &ifs->hw.flags);
 	}
 
 	/* Restart sched_scan with updated channel list */
@@ -3021,6 +3122,8 @@
 		wpa_supplicant_cancel_sched_scan(wpa_s);
 		wpa_supplicant_req_scan(wpa_s, 0, 0);
 	}
+
+	wpas_p2p_update_channel_list(wpa_s, WPAS_P2P_CHANNEL_UPDATE_DRIVER);
 }
 
 
@@ -3121,6 +3224,13 @@
 		return;
 	}
 
+#ifdef CONFIG_FST
+	if (mgmt->u.action.category == WLAN_ACTION_FST && wpa_s->fst) {
+		fst_rx_action(wpa_s->fst, mgmt, len);
+		return;
+	}
+#endif /* CONFIG_FST */
+
 	wpas_p2p_rx_action(wpa_s, mgmt->da, mgmt->sa, mgmt->bssid,
 			   category, payload, plen, freq);
 	if (wpa_s->ifmsh)
@@ -3131,9 +3241,6 @@
 static void wpa_supplicant_notify_avoid_freq(struct wpa_supplicant *wpa_s,
 					     union wpa_event_data *event)
 {
-#ifdef CONFIG_P2P
-	struct wpa_supplicant *ifs;
-#endif /* CONFIG_P2P */
 	struct wpa_freq_range_list *list;
 	char *str = NULL;
 
@@ -3150,29 +3257,13 @@
 			__func__);
 	} else {
 		wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Update channel list based on frequency avoid event");
-		wpas_p2p_update_channel_list(wpa_s);
-	}
 
-	for (ifs = wpa_s->global->ifaces; ifs; ifs = ifs->next) {
-		int freq;
-		if (!ifs->current_ssid ||
-		    !ifs->current_ssid->p2p_group ||
-		    (ifs->current_ssid->mode != WPAS_MODE_P2P_GO &&
-		     ifs->current_ssid->mode != WPAS_MODE_P2P_GROUP_FORMATION))
-			continue;
-
-		freq = ifs->current_ssid->frequency;
-		if (!freq_range_list_includes(list, freq)) {
-			wpa_dbg(ifs, MSG_DEBUG, "P2P GO operating frequency %d MHz in safe range",
-				freq);
-			continue;
-		}
-
-		wpa_dbg(ifs, MSG_DEBUG, "P2P GO operating in unsafe frequency %d MHz",
-			freq);
-		/* TODO: Consider using CSA or removing the group within
-		 * wpa_supplicant */
-		wpa_msg(ifs, MSG_INFO, P2P_EVENT_REMOVE_AND_REFORM_GROUP);
+		/*
+		 * The update channel flow will also take care of moving a GO
+		 * from the unsafe frequency if needed.
+		 */
+		wpas_p2p_update_channel_list(wpa_s,
+					     WPAS_P2P_CHANNEL_UPDATE_AVOID);
 	}
 #endif /* CONFIG_P2P */
 
@@ -3237,12 +3328,20 @@
 
 	switch (event) {
 	case EVENT_AUTH:
+#ifdef CONFIG_FST
+		wpas_fst_update_mbie(wpa_s, data->auth.ies, data->auth.ies_len);
+#endif /* CONFIG_FST */
 		sme_event_auth(wpa_s, data);
 		break;
 	case EVENT_ASSOC:
 		wpa_supplicant_event_assoc(wpa_s, data);
 		if (data && data->assoc_info.authorized)
 			wpa_supplicant_event_assoc_auth(wpa_s, data);
+		if (data) {
+			wpa_msg(wpa_s, MSG_INFO,
+				WPA_EVENT_SUBNET_STATUS_UPDATE "status=%u",
+				data->assoc_info.subnet_status);
+		}
 		break;
 	case EVENT_DISASSOC:
 		wpas_event_disassoc(wpa_s,
@@ -3257,10 +3356,11 @@
 		break;
 #ifndef CONFIG_NO_SCAN_PROCESSING
 	case EVENT_SCAN_STARTED:
-		os_get_reltime(&wpa_s->scan_start_time);
-		if (wpa_s->own_scan_requested) {
+		if (wpa_s->own_scan_requested ||
+		    (data && !data->scan_info.external_scan)) {
 			struct os_reltime diff;
 
+			os_get_reltime(&wpa_s->scan_start_time);
 			os_reltime_sub(&wpa_s->scan_start_time,
 				       &wpa_s->scan_trigger_time, &diff);
 			wpa_dbg(wpa_s, MSG_DEBUG, "Own scan request started a scan in %ld.%06ld seconds",
@@ -3283,7 +3383,8 @@
 		}
 		break;
 	case EVENT_SCAN_RESULTS:
-		if (os_reltime_initialized(&wpa_s->scan_start_time)) {
+		if (!(data && data->scan_info.external_scan) &&
+		    os_reltime_initialized(&wpa_s->scan_start_time)) {
 			struct os_reltime now, diff;
 			os_get_reltime(&now);
 			os_reltime_sub(&now, &wpa_s->scan_start_time, &diff);
@@ -3294,8 +3395,10 @@
 		}
 		if (wpa_supplicant_event_scan_results(wpa_s, data))
 			break; /* interface may have been removed */
-		wpa_s->own_scan_running = 0;
-		wpa_s->radio->external_scan_running = 0;
+		if (!(data && data->scan_info.external_scan))
+			wpa_s->own_scan_running = 0;
+		if (data && data->scan_info.nl_scan_event)
+			wpa_s->radio->external_scan_running = 0;
 		radio_work_check_next(wpa_s);
 		break;
 #endif /* CONFIG_NO_SCAN_PROCESSING */
@@ -3451,20 +3554,25 @@
 				       data->rx_from_unknown.wds);
 		break;
 	case EVENT_CH_SWITCH:
-		if (!data)
+		if (!data || !wpa_s->current_ssid)
 			break;
-		if (!wpa_s->ap_iface) {
-			wpa_dbg(wpa_s, MSG_DEBUG, "AP: Ignore channel switch "
-				"event in non-AP mode");
-			break;
+
+		wpa_s->assoc_freq = data->ch_switch.freq;
+		wpa_s->current_ssid->frequency = data->ch_switch.freq;
+
+		if (wpa_s->current_ssid->mode == WPAS_MODE_AP ||
+		    wpa_s->current_ssid->mode == WPAS_MODE_P2P_GO ||
+		    wpa_s->current_ssid->mode ==
+		    WPAS_MODE_P2P_GROUP_FORMATION) {
+			wpas_ap_ch_switch(wpa_s, data->ch_switch.freq,
+					  data->ch_switch.ht_enabled,
+					  data->ch_switch.ch_offset,
+					  data->ch_switch.ch_width,
+					  data->ch_switch.cf1,
+					  data->ch_switch.cf2);
 		}
 
-		wpas_ap_ch_switch(wpa_s, data->ch_switch.freq,
-				  data->ch_switch.ht_enabled,
-				  data->ch_switch.ch_offset,
-				  data->ch_switch.ch_width,
-				  data->ch_switch.cf1,
-				  data->ch_switch.cf2);
+		wpas_p2p_update_channel_list(wpa_s, WPAS_P2P_CHANNEL_UPDATE_CS);
 		break;
 #ifdef NEED_AP_MLME
 	case EVENT_DFS_RADAR_DETECTED:
diff --git a/wpa_supplicant/hs20_supplicant.c b/wpa_supplicant/hs20_supplicant.c
index a1afc85..e6c564b 100644
--- a/wpa_supplicant/hs20_supplicant.c
+++ b/wpa_supplicant/hs20_supplicant.c
@@ -617,7 +617,7 @@
 	prov->osu_ssid_len = osu_ssid_len;
 
 	/* OSU Friendly Name Length */
-	if (pos + 2 > end) {
+	if (end - pos < 2) {
 		wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for OSU "
 			   "Friendly Name Length");
 		return;
@@ -633,9 +633,9 @@
 	pos += len2;
 
 	/* OSU Friendly Name Duples */
-	while (pos2 + 4 <= pos && prov->friendly_name_count < OSU_MAX_ITEMS) {
+	while (pos - pos2 >= 4 && prov->friendly_name_count < OSU_MAX_ITEMS) {
 		struct osu_lang_string *f;
-		if (pos2 + 1 + pos2[0] > pos || pos2[0] < 3) {
+		if (1 + pos2[0] > pos - pos2 || pos2[0] < 3) {
 			wpa_printf(MSG_DEBUG, "Invalid OSU Friendly Name");
 			break;
 		}
@@ -646,7 +646,7 @@
 	}
 
 	/* OSU Server URI */
-	if (pos + 1 > end) {
+	if (end - pos < 1) {
 		wpa_printf(MSG_DEBUG,
 			   "HS 2.0: Not enough room for OSU Server URI length");
 		return;
@@ -661,7 +661,7 @@
 	pos += uri_len;
 
 	/* OSU Method list */
-	if (pos + 1 > end) {
+	if (end - pos < 1) {
 		wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for OSU Method "
 			   "list length");
 		return;
@@ -681,7 +681,7 @@
 	}
 
 	/* Icons Available Length */
-	if (pos + 2 > end) {
+	if (end - pos < 2) {
 		wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for Icons "
 			   "Available Length");
 		return;
@@ -701,7 +701,7 @@
 		struct osu_icon *icon = &prov->icon[prov->icon_count];
 		u8 flen;
 
-		if (pos2 + 2 + 2 + 3 + 1 + 1 > pos) {
+		if (2 + 2 + 3 + 1 + 1 > pos - pos2) {
 			wpa_printf(MSG_DEBUG, "HS 2.0: Invalid Icon Metadata");
 			break;
 		}
@@ -713,46 +713,46 @@
 		os_memcpy(icon->lang, pos2, 3);
 		pos2 += 3;
 
-		flen = pos2[0];
-		if (flen > pos - pos2 - 1) {
+		flen = *pos2++;
+		if (flen > pos - pos2) {
 			wpa_printf(MSG_DEBUG, "HS 2.0: Not room for Icon Type");
 			break;
 		}
-		os_memcpy(icon->icon_type, pos2 + 1, flen);
-		pos2 += 1 + flen;
+		os_memcpy(icon->icon_type, pos2, flen);
+		pos2 += flen;
 
-		if (pos2 + 1 > pos) {
+		if (pos - pos2 < 1) {
 			wpa_printf(MSG_DEBUG, "HS 2.0: Not room for Icon "
 				   "Filename length");
 			break;
 		}
-		flen = pos2[0];
-		if (flen > pos - pos2 - 1) {
+		flen = *pos2++;
+		if (flen > pos - pos2) {
 			wpa_printf(MSG_DEBUG, "HS 2.0: Not room for Icon "
 				   "Filename");
 			break;
 		}
-		os_memcpy(icon->filename, pos2 + 1, flen);
-		pos2 += 1 + flen;
+		os_memcpy(icon->filename, pos2, flen);
+		pos2 += flen;
 
 		prov->icon_count++;
 	}
 
 	/* OSU_NAI */
-	if (pos + 1 > end) {
+	if (end - pos < 1) {
 		wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for OSU_NAI");
 		return;
 	}
-	osu_nai_len = pos[0];
-	if (osu_nai_len > end - pos - 1) {
+	osu_nai_len = *pos++;
+	if (osu_nai_len > end - pos) {
 		wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for OSU_NAI");
 		return;
 	}
-	os_memcpy(prov->osu_nai, pos + 1, osu_nai_len);
-	pos += 1 + osu_nai_len;
+	os_memcpy(prov->osu_nai, pos, osu_nai_len);
+	pos += osu_nai_len;
 
 	/* OSU Service Description Length */
-	if (pos + 2 > end) {
+	if (end - pos < 2) {
 		wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for OSU "
 			   "Service Description Length");
 		return;
@@ -768,20 +768,20 @@
 	pos += len2;
 
 	/* OSU Service Description Duples */
-	while (pos2 + 4 <= pos && prov->serv_desc_count < OSU_MAX_ITEMS) {
+	while (pos - pos2 >= 4 && prov->serv_desc_count < OSU_MAX_ITEMS) {
 		struct osu_lang_string *f;
 		u8 descr_len;
 
-		descr_len = pos2[0];
-		if (descr_len > pos - pos2 - 1 || descr_len < 3) {
+		descr_len = *pos2++;
+		if (descr_len > pos - pos2 || descr_len < 3) {
 			wpa_printf(MSG_DEBUG, "Invalid OSU Service "
 				   "Description");
 			break;
 		}
 		f = &prov->serv_desc[prov->serv_desc_count++];
-		os_memcpy(f->lang, pos2 + 1, 3);
-		os_memcpy(f->text, pos2 + 1 + 3, descr_len - 3);
-		pos2 += 1 + descr_len;
+		os_memcpy(f->lang, pos2, 3);
+		os_memcpy(f->text, pos2 + 3, descr_len - 3);
+		pos2 += descr_len;
 	}
 
 	wpa_printf(MSG_DEBUG, "HS 2.0: Added OSU Provider through " MACSTR,
@@ -816,9 +816,9 @@
 		end = pos + wpabuf_len(prov_anqp);
 
 		/* OSU SSID */
-		if (pos + 1 > end)
+		if (end - pos < 1)
 			continue;
-		if (pos + 1 + pos[0] > end) {
+		if (1 + pos[0] > end - pos) {
 			wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for "
 				   "OSU SSID");
 			continue;
@@ -832,7 +832,7 @@
 		osu_ssid = pos;
 		pos += osu_ssid_len;
 
-		if (pos + 1 > end) {
+		if (end - pos < 1) {
 			wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for "
 				   "Number of OSU Providers");
 			continue;
@@ -842,7 +842,7 @@
 			   num_providers);
 
 		/* OSU Providers */
-		while (pos + 2 < end && num_providers > 0) {
+		while (end - pos > 2 && num_providers > 0) {
 			num_providers--;
 			len = WPA_GET_LE16(pos);
 			pos += 2;
diff --git a/wpa_supplicant/ibss_rsn.c b/wpa_supplicant/ibss_rsn.c
index 6299191..d9d0ae7 100644
--- a/wpa_supplicant/ibss_rsn.c
+++ b/wpa_supplicant/ibss_rsn.c
@@ -697,7 +697,8 @@
 		ibss_rsn_free(prev);
 	}
 
-	wpa_deinit(ibss_rsn->auth_group);
+	if (ibss_rsn->auth_group)
+		wpa_deinit(ibss_rsn->auth_group);
 	os_free(ibss_rsn);
 
 }
diff --git a/wpa_supplicant/interworking.c b/wpa_supplicant/interworking.c
index fd47c17..6a54d1e 100644
--- a/wpa_supplicant/interworking.c
+++ b/wpa_supplicant/interworking.c
@@ -362,13 +362,13 @@
 	u8 elen, auth_count, a;
 	const u8 *e_end;
 
-	if (pos + 3 > end) {
+	if (end - pos < 3) {
 		wpa_printf(MSG_DEBUG, "No room for EAP Method fixed fields");
 		return NULL;
 	}
 
 	elen = *pos++;
-	if (pos + elen > end || elen < 2) {
+	if (elen > end - pos || elen < 2) {
 		wpa_printf(MSG_DEBUG, "No room for EAP Method subfield");
 		return NULL;
 	}
@@ -381,14 +381,19 @@
 	for (a = 0; a < auth_count; a++) {
 		u8 id, len;
 
-		if (pos + 2 > end || pos + 2 + pos[1] > end) {
-			wpa_printf(MSG_DEBUG, "No room for Authentication "
-				   "Parameter subfield");
+		if (end - pos < 2) {
+			wpa_printf(MSG_DEBUG,
+				   "No room for Authentication Parameter subfield header");
 			return NULL;
 		}
 
 		id = *pos++;
 		len = *pos++;
+		if (len > end - pos) {
+			wpa_printf(MSG_DEBUG,
+				   "No room for Authentication Parameter subfield");
+			return NULL;
+		}
 
 		switch (id) {
 		case NAI_REALM_EAP_AUTH_NON_EAP_INNER_AUTH:
@@ -463,7 +468,7 @@
 
 	len = WPA_GET_LE16(pos); /* NAI Realm Data field Length */
 	pos += 2;
-	if (pos + len > end || len < 3) {
+	if (len > end - pos || len < 3) {
 		wpa_printf(MSG_DEBUG, "No room for NAI Realm Data "
 			   "(len=%u; left=%u)",
 			   len, (unsigned int) (end - pos));
@@ -473,7 +478,7 @@
 
 	r->encoding = *pos++;
 	realm_len = *pos++;
-	if (pos + realm_len > f_end) {
+	if (realm_len > f_end - pos) {
 		wpa_printf(MSG_DEBUG, "No room for NAI Realm "
 			   "(len=%u; left=%u)",
 			   realm_len, (unsigned int) (f_end - pos));
@@ -485,13 +490,13 @@
 		return NULL;
 	pos += realm_len;
 
-	if (pos + 1 > f_end) {
+	if (f_end - pos < 1) {
 		wpa_printf(MSG_DEBUG, "No room for EAP Method Count");
 		return NULL;
 	}
 	r->eap_count = *pos++;
 	wpa_printf(MSG_DEBUG, "EAP Count: %u", r->eap_count);
-	if (pos + r->eap_count * 3 > f_end) {
+	if (r->eap_count * 3 > f_end - pos) {
 		wpa_printf(MSG_DEBUG, "No room for EAP Methods");
 		return NULL;
 	}
@@ -746,7 +751,7 @@
 		return 0;
 	pos = wpabuf_head_u8(anqp);
 	end = pos + wpabuf_len(anqp);
-	if (pos + 2 > end)
+	if (end - pos < 2)
 		return 0;
 	if (*pos != 0) {
 		wpa_printf(MSG_DEBUG, "Unsupported GUD version 0x%x", *pos);
@@ -754,7 +759,7 @@
 	}
 	pos++;
 	udhl = *pos++;
-	if (pos + udhl > end) {
+	if (udhl > end - pos) {
 		wpa_printf(MSG_DEBUG, "Invalid UDHL");
 		return 0;
 	}
@@ -764,12 +769,12 @@
 		   plmn[0], plmn[1], plmn[2], plmn2[0], plmn2[1], plmn2[2],
 		   imsi, mnc_len);
 
-	while (pos + 2 <= end) {
+	while (end - pos >= 2) {
 		u8 iei, len;
 		const u8 *l_end;
 		iei = *pos++;
 		len = *pos++ & 0x7f;
-		if (pos + len > end)
+		if (len > end - pos)
 			break;
 		l_end = pos + len;
 
@@ -780,7 +785,7 @@
 				    pos, len);
 			num = *pos++;
 			for (i = 0; i < num; i++) {
-				if (pos + 3 > l_end)
+				if (l_end - pos < 3)
 					break;
 				if (os_memcmp(pos, plmn, 3) == 0 ||
 				    os_memcmp(pos, plmn2, 3) == 0)
@@ -1082,12 +1087,12 @@
 	 * OI #1, [OI #2], [OI #3]
 	 */
 
-	if (pos + 2 > end)
+	if (end - pos < 2)
 		return 0;
 
 	pos++; /* skip Number of ANQP OIs */
 	lens = *pos++;
-	if (pos + (lens & 0x0f) + (lens >> 4) > end)
+	if ((lens & 0x0f) + (lens >> 4) > end - pos)
 		return 0;
 
 	if ((lens & 0x0f) == rc_len && os_memcmp(pos, rc_id, rc_len) == 0)
@@ -1121,7 +1126,7 @@
 	/* Set of <OI Length, OI> duples */
 	while (pos < end) {
 		len = *pos++;
-		if (pos + len > end)
+		if (len > end - pos)
 			break;
 		if (len == rc_len && os_memcmp(pos, rc_id, rc_len) == 0)
 			return 1;
@@ -1182,6 +1187,7 @@
 static int cred_below_min_backhaul(struct wpa_supplicant *wpa_s,
 				   struct wpa_cred *cred, struct wpa_bss *bss)
 {
+#ifdef CONFIG_HS20
 	int res;
 	unsigned int dl_bandwidth, ul_bandwidth;
 	const u8 *wan;
@@ -1233,6 +1239,7 @@
 		if (cred->min_ul_bandwidth_roaming > ul_bandwidth)
 			return 1;
 	}
+#endif /* CONFIG_HS20 */
 
 	return 0;
 }
@@ -1260,9 +1267,11 @@
 }
 
 
+#ifdef CONFIG_HS20
+
 static int has_proto_match(const u8 *pos, const u8 *end, u8 proto)
 {
-	while (pos + 4 <= end) {
+	while (end - pos >= 4) {
 		if (pos[0] == proto && pos[3] == 1 /* Open */)
 			return 1;
 		pos += 4;
@@ -1275,7 +1284,7 @@
 static int has_proto_port_match(const u8 *pos, const u8 *end, u8 proto,
 				u16 port)
 {
-	while (pos + 4 <= end) {
+	while (end - pos >= 4) {
 		if (pos[0] == proto && WPA_GET_LE16(&pos[1]) == port &&
 		    pos[3] == 1 /* Open */)
 			return 1;
@@ -1285,10 +1294,13 @@
 	return 0;
 }
 
+#endif /* CONFIG_HS20 */
+
 
 static int cred_conn_capab_missing(struct wpa_supplicant *wpa_s,
 				   struct wpa_cred *cred, struct wpa_bss *bss)
 {
+#ifdef CONFIG_HS20
 	int res;
 	const u8 *capab, *end;
 	unsigned int i, j;
@@ -1325,6 +1337,7 @@
 			}
 		}
 	}
+#endif /* CONFIG_HS20 */
 
 	return 0;
 }
@@ -2125,23 +2138,27 @@
 	pos = wpabuf_head(domain_names);
 	end = pos + wpabuf_len(domain_names);
 
-	while (pos + 1 < end) {
-		if (pos + 1 + pos[0] > end)
+	while (end - pos > 1) {
+		u8 elen;
+
+		elen = *pos++;
+		if (elen > end - pos)
 			break;
 
 		wpa_hexdump_ascii(MSG_DEBUG, "Interworking: AP domain name",
-				  pos + 1, pos[0]);
-		if (pos[0] == len &&
-		    os_strncasecmp(domain, (const char *) (pos + 1), len) == 0)
+				  pos, elen);
+		if (elen == len &&
+		    os_strncasecmp(domain, (const char *) pos, len) == 0)
 			return 1;
-		if (!exact_match && pos[0] > len && pos[pos[0] - len] == '.') {
-			const char *ap = (const char *) (pos + 1);
-			int offset = pos[0] - len;
+		if (!exact_match && elen > len && pos[elen - len - 1] == '.') {
+			const char *ap = (const char *) pos;
+			int offset = elen - len;
+
 			if (os_strncasecmp(domain, ap + offset, len) == 0)
 				return 1;
 		}
 
-		pos += 1 + pos[0];
+		pos += elen;
 	}
 
 	return 0;
@@ -2564,11 +2581,13 @@
 		return;
 	}
 
+#ifdef CONFIG_HS20
 	if (wpa_s->fetch_osu_icon_in_progress) {
 		wpa_printf(MSG_DEBUG, "Interworking: Next icon (in progress)");
 		hs20_next_osu_icon(wpa_s);
 		return;
 	}
+#endif /* CONFIG_HS20 */
 
 	dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
 		if (!(bss->caps & IEEE80211_CAP_ESS))
@@ -2602,6 +2621,7 @@
 	}
 
 	if (found == 0) {
+#ifdef CONFIG_HS20
 		if (wpa_s->fetch_osu_info) {
 			if (wpa_s->num_prov_found == 0 &&
 			    wpa_s->fetch_osu_waiting_scan &&
@@ -2614,6 +2634,7 @@
 			hs20_osu_icon_fetch(wpa_s);
 			return;
 		}
+#endif /* CONFIG_HS20 */
 		wpa_msg(wpa_s, MSG_INFO, "ANQP fetch completed");
 		wpa_s->fetch_anqp_in_progress = 0;
 		if (wpa_s->network_select)
@@ -2716,6 +2737,41 @@
 }
 
 
+static void anqp_add_extra(struct wpa_supplicant *wpa_s,
+			   struct wpa_bss_anqp *anqp, u16 info_id,
+			   const u8 *data, size_t slen)
+{
+	struct wpa_bss_anqp_elem *tmp, *elem = NULL;
+
+	if (!anqp)
+		return;
+
+	dl_list_for_each(tmp, &anqp->anqp_elems, struct wpa_bss_anqp_elem,
+			 list) {
+		if (tmp->infoid == info_id) {
+			elem = tmp;
+			break;
+		}
+	}
+
+	if (!elem) {
+		elem = os_zalloc(sizeof(*elem));
+		if (!elem)
+			return;
+		elem->infoid = info_id;
+		dl_list_add(&anqp->anqp_elems, &elem->list);
+	} else {
+		wpabuf_free(elem->payload);
+	}
+
+	elem->payload = wpabuf_alloc_copy(data, slen);
+	if (!elem->payload) {
+		dl_list_del(&elem->list);
+		os_free(elem);
+	}
+}
+
+
 static void interworking_parse_rx_anqp_resp(struct wpa_supplicant *wpa_s,
 					    struct wpa_bss *bss, const u8 *sa,
 					    u16 info_id,
@@ -2849,6 +2905,7 @@
 	default:
 		wpa_msg(wpa_s, MSG_DEBUG,
 			"Interworking: Unsupported ANQP Info ID %u", info_id);
+		anqp_add_extra(wpa_s, anqp, info_id, data, slen);
 		break;
 	}
 }
@@ -2871,8 +2928,10 @@
 		   " dialog_token=%u result=%d status_code=%u",
 		   MAC2STR(dst), dialog_token, result, status_code);
 	if (result != GAS_QUERY_SUCCESS) {
+#ifdef CONFIG_HS20
 		if (wpa_s->fetch_osu_icon_in_progress)
 			hs20_icon_fetch_failed(wpa_s);
+#endif /* CONFIG_HS20 */
 		anqp_result = "FAILURE";
 		goto out;
 	}
@@ -2882,8 +2941,10 @@
 	    pos[1] < 2 || pos[3] != ACCESS_NETWORK_QUERY_PROTOCOL) {
 		wpa_msg(wpa_s, MSG_DEBUG,
 			"ANQP: Unexpected Advertisement Protocol in response");
+#ifdef CONFIG_HS20
 		if (wpa_s->fetch_osu_icon_in_progress)
 			hs20_icon_fetch_failed(wpa_s);
+#endif /* CONFIG_HS20 */
 		anqp_result = "INVALID_FRAME";
 		goto out;
 	}
@@ -2932,7 +2993,9 @@
 	}
 
 out_parse_done:
+#ifdef CONFIG_HS20
 	hs20_notify_parse_done(wpa_s);
+#endif /* CONFIG_HS20 */
 out:
 	wpa_msg(wpa_s, MSG_INFO, ANQP_QUERY_DONE "addr=" MACSTR " result=%s",
 		MAC2STR(dst), anqp_result);
diff --git a/wpa_supplicant/libwpa_test.c b/wpa_supplicant/libwpa_test.c
new file mode 100644
index 0000000..e51ab72
--- /dev/null
+++ b/wpa_supplicant/libwpa_test.c
@@ -0,0 +1,32 @@
+/*
+ * libwpa_test - Test program for libwpa_client.* library linking
+ * Copyright (c) 2015, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common/wpa_ctrl.h"
+
+int main(int argc, char *argv[])
+{
+	struct wpa_ctrl *ctrl;
+
+	ctrl = wpa_ctrl_open("foo");
+	if (!ctrl)
+		return -1;
+	if (wpa_ctrl_attach(ctrl) == 0)
+		wpa_ctrl_detach(ctrl);
+	if (wpa_ctrl_pending(ctrl)) {
+		char buf[10];
+		size_t len;
+
+		len = sizeof(buf);
+		wpa_ctrl_recv(ctrl, buf, &len);
+	}
+	wpa_ctrl_close(ctrl);
+
+	return 0;
+}
diff --git a/wpa_supplicant/main.c b/wpa_supplicant/main.c
index 1c93306..d5d47e1 100644
--- a/wpa_supplicant/main.c
+++ b/wpa_supplicant/main.c
@@ -12,6 +12,7 @@
 #endif /* __linux__ */
 
 #include "common.h"
+#include "fst/fst.h"
 #include "wpa_supplicant_i.h"
 #include "driver_i.h"
 #include "p2p_supplicant.h"
@@ -309,6 +310,17 @@
 			   "wpa_supplicant");
 	}
 
+	if (fst_global_init()) {
+		wpa_printf(MSG_ERROR, "Failed to initialize FST");
+		exitcode = -1;
+		goto out;
+	}
+
+#if defined(CONFIG_FST) && defined(CONFIG_CTRL_IFACE)
+	if (!fst_global_add_ctrl(fst_ctrl_cli))
+		wpa_printf(MSG_WARNING, "Failed to add CLI FST ctrl");
+#endif
+
 	for (i = 0; exitcode == 0 && i < iface_count; i++) {
 		struct wpa_supplicant *wpa_s;
 
@@ -334,6 +346,8 @@
 
 	wpa_supplicant_deinit(global);
 
+	fst_global_deinit();
+
 out:
 	wpa_supplicant_fd_workaround(0);
 	os_free(ifaces);
diff --git a/wpa_supplicant/mesh.c b/wpa_supplicant/mesh.c
index ca012e2..8f74b5d 100644
--- a/wpa_supplicant/mesh.c
+++ b/wpa_supplicant/mesh.c
@@ -47,8 +47,8 @@
 
 	if (ifmsh->mconf) {
 		mesh_mpm_deinit(wpa_s, ifmsh);
-		if (ifmsh->mconf->ies) {
-			ifmsh->mconf->ies = NULL;
+		if (ifmsh->mconf->rsn_ie) {
+			ifmsh->mconf->rsn_ie = NULL;
 			/* We cannot free this struct
 			 * because wpa_authenticator on
 			 * hostapd side is also using it
@@ -171,6 +171,8 @@
 	ifmsh->conf = conf;
 
 	ifmsh->bss[0]->max_plinks = wpa_s->conf->max_peer_links;
+	ifmsh->bss[0]->dot11RSNASAERetransPeriod =
+		wpa_s->conf->dot11RSNASAERetransPeriod;
 	os_strlcpy(bss->conf->iface, wpa_s->ifname, sizeof(bss->conf->iface));
 
 	mconf = mesh_config_create(ssid);
@@ -316,11 +318,22 @@
 
 	wpa_supplicant_mesh_deinit(wpa_s);
 
+	if (ssid->key_mgmt & WPA_KEY_MGMT_SAE) {
+		wpa_s->pairwise_cipher = WPA_CIPHER_CCMP;
+		wpa_s->group_cipher = WPA_CIPHER_CCMP;
+		wpa_s->mgmt_group_cipher = 0;
+	} else {
+		wpa_s->pairwise_cipher = WPA_CIPHER_NONE;
+		wpa_s->group_cipher = WPA_CIPHER_NONE;
+		wpa_s->mgmt_group_cipher = 0;
+	}
+
 	os_memset(&params, 0, sizeof(params));
 	params.meshid = ssid->ssid;
 	params.meshid_len = ssid->ssid_len;
 	ibss_mesh_setup_freq(wpa_s, ssid, &params.freq);
 	wpa_s->mesh_ht_enabled = !!params.freq.ht_enabled;
+	wpa_s->mesh_vht_enabled = !!params.freq.vht_enabled;
 	if (ssid->beacon_int > 0)
 		params.beacon_int = ssid->beacon_int;
 	else if (wpa_s->conf->beacon_int > 0)
@@ -350,8 +363,8 @@
 	}
 
 	if (wpa_s->ifmsh) {
-		params.ies = wpa_s->ifmsh->mconf->ies;
-		params.ie_len = wpa_s->ifmsh->mconf->ie_len;
+		params.ies = wpa_s->ifmsh->mconf->rsn_ie;
+		params.ie_len = wpa_s->ifmsh->mconf->rsn_ie_len;
 		params.basic_rates = wpa_s->ifmsh->basic_rates;
 	}
 
diff --git a/wpa_supplicant/mesh_mpm.c b/wpa_supplicant/mesh_mpm.c
index b29b5ff..7ebd4d2 100644
--- a/wpa_supplicant/mesh_mpm.c
+++ b/wpa_supplicant/mesh_mpm.c
@@ -212,9 +212,6 @@
 	struct hostapd_data *bss = ifmsh->bss[0];
 	struct mesh_conf *conf = ifmsh->mconf;
 	u8 supp_rates[2 + 2 + 32];
-#ifdef CONFIG_IEEE80211N
-	u8 ht_capa_oper[2 + 26 + 2 + 22];
-#endif /* CONFIG_IEEE80211N */
 	u8 *pos, *cat;
 	u8 ie_len, add_plid = 0;
 	int ret;
@@ -239,6 +236,15 @@
 			   2 + 22;  /* HT operation */
 	}
 #endif /* CONFIG_IEEE80211N */
+#ifdef CONFIG_IEEE80211AC
+	if (type != PLINK_CLOSE && wpa_s->mesh_vht_enabled) {
+		buf_len += 2 + 12 + /* VHT Capabilities */
+			   2 + 5;  /* VHT Operation */
+	}
+#endif /* CONFIG_IEEE80211AC */
+	if (type != PLINK_CLOSE)
+		buf_len += conf->rsn_ie_len; /* RSN IE */
+
 	buf = wpabuf_alloc(buf_len);
 	if (!buf)
 		return;
@@ -255,13 +261,16 @@
 
 		/* aid */
 		if (type == PLINK_CONFIRM)
-			wpabuf_put_le16(buf, sta->peer_lid);
+			wpabuf_put_le16(buf, sta->aid);
 
 		/* IE: supp + ext. supp rates */
 		pos = hostapd_eid_supp_rates(bss, supp_rates);
 		pos = hostapd_eid_ext_supp_rates(bss, pos);
 		wpabuf_put_data(buf, supp_rates, pos - supp_rates);
 
+		/* IE: RSN IE */
+		wpabuf_put_data(buf, conf->rsn_ie, conf->rsn_ie_len);
+
 		/* IE: Mesh ID */
 		wpabuf_put_u8(buf, WLAN_EID_MESH_ID);
 		wpabuf_put_u8(buf, conf->meshid_len);
@@ -328,11 +337,22 @@
 
 #ifdef CONFIG_IEEE80211N
 	if (type != PLINK_CLOSE && wpa_s->mesh_ht_enabled) {
+		u8 ht_capa_oper[2 + 26 + 2 + 22];
+
 		pos = hostapd_eid_ht_capabilities(bss, ht_capa_oper);
 		pos = hostapd_eid_ht_operation(bss, pos);
 		wpabuf_put_data(buf, ht_capa_oper, pos - ht_capa_oper);
 	}
 #endif /* CONFIG_IEEE80211N */
+#ifdef CONFIG_IEEE80211AC
+	if (type != PLINK_CLOSE && wpa_s->mesh_vht_enabled) {
+		u8 vht_capa_oper[2 + 12 + 2 + 5];
+
+		pos = hostapd_eid_vht_capabilities(bss, vht_capa_oper);
+		pos = hostapd_eid_vht_operation(bss, pos);
+		wpabuf_put_data(buf, vht_capa_oper, pos - vht_capa_oper);
+	}
+#endif /* CONFIG_IEEE80211AC */
 
 	if (ampe && mesh_rsn_protect_frame(wpa_s->mesh_rsn, sta, cat, buf)) {
 		wpa_msg(wpa_s, MSG_INFO,
@@ -542,6 +562,9 @@
 			return NULL;
 	}
 
+	/* Set WMM by default since Mesh STAs are QoS STAs */
+	sta->flags |= WLAN_STA_WMM;
+
 	/* initialize sta */
 	if (copy_supp_rates(wpa_s, sta, elems)) {
 		ap_free_sta(data, sta);
@@ -555,15 +578,27 @@
 	update_ht_state(data, sta);
 #endif /* CONFIG_IEEE80211N */
 
+#ifdef CONFIG_IEEE80211AC
+	copy_sta_vht_capab(data, sta, elems->vht_capabilities);
+	set_sta_vht_opmode(data, sta, elems->vht_opmode_notif);
+#endif /* CONFIG_IEEE80211AC */
+
+	if (hostapd_get_aid(data, sta) < 0) {
+		wpa_msg(wpa_s, MSG_ERROR, "No AIDs available");
+		ap_free_sta(data, sta);
+		return NULL;
+	}
+
 	/* insert into driver */
 	os_memset(&params, 0, sizeof(params));
 	params.supp_rates = sta->supported_rates;
 	params.supp_rates_len = sta->supported_rates_len;
 	params.addr = addr;
 	params.plink_state = sta->plink_state;
-	params.aid = sta->peer_lid;
+	params.aid = sta->aid;
 	params.listen_interval = 100;
 	params.ht_capabilities = sta->ht_capabilities;
+	params.vht_capabilities = sta->vht_capabilities;
 	params.flags |= WPA_STA_WMM;
 	params.flags_mask |= WPA_STA_AUTHENTICATED;
 	if (conf->security == MESH_CONF_SEC_NONE) {
diff --git a/wpa_supplicant/mesh_rsn.c b/wpa_supplicant/mesh_rsn.c
index 936002d..747f1ae 100644
--- a/wpa_supplicant/mesh_rsn.c
+++ b/wpa_supplicant/mesh_rsn.c
@@ -190,7 +190,8 @@
 static void mesh_rsn_deinit(struct mesh_rsn *rsn)
 {
 	os_memset(rsn->mgtk, 0, sizeof(rsn->mgtk));
-	wpa_deinit(rsn->auth);
+	if (rsn->auth)
+		wpa_deinit(rsn->auth);
 }
 
 
@@ -209,14 +210,15 @@
 
 	if (__mesh_rsn_auth_init(mesh_rsn, wpa_s->own_addr) < 0) {
 		mesh_rsn_deinit(mesh_rsn);
+		os_free(mesh_rsn);
 		return NULL;
 	}
 
 	bss->wpa_auth = mesh_rsn->auth;
 
 	ie = wpa_auth_get_wpa_ie(mesh_rsn->auth, &ie_len);
-	conf->ies = (u8 *) ie;
-	conf->ie_len = ie_len;
+	conf->rsn_ie = (u8 *) ie;
+	conf->rsn_ie_len = ie_len;
 
 	wpa_supplicant_rsn_supp_set_config(wpa_s, wpa_s->current_ssid);
 
diff --git a/wpa_supplicant/notify.c b/wpa_supplicant/notify.c
index 822db74..45d06bf 100644
--- a/wpa_supplicant/notify.c
+++ b/wpa_supplicant/notify.c
@@ -17,6 +17,7 @@
 #include "dbus/dbus_old.h"
 #include "dbus/dbus_new.h"
 #include "rsn_supp/wpa.h"
+#include "fst/fst.h"
 #include "driver_i.h"
 #include "scan.h"
 #include "p2p_supplicant.h"
@@ -88,6 +89,16 @@
 	/* notify the new DBus API */
 	wpas_dbus_signal_prop_changed(wpa_s, WPAS_DBUS_PROP_STATE);
 
+#ifdef CONFIG_FST
+	if (wpa_s->fst && !is_zero_ether_addr(wpa_s->bssid)) {
+		if (new_state == WPA_COMPLETED)
+			fst_notify_peer_connected(wpa_s->fst, wpa_s->bssid);
+		else if (old_state >= WPA_ASSOCIATED &&
+			 new_state < WPA_ASSOCIATED)
+			fst_notify_peer_disconnected(wpa_s->fst, wpa_s->bssid);
+	}
+#endif /* CONFIG_FST */
+
 	if (new_state == WPA_COMPLETED)
 		wpas_p2p_notif_connected(wpa_s);
 	else if (old_state >= WPA_ASSOCIATED && new_state < WPA_ASSOCIATED)
@@ -646,12 +657,30 @@
 }
 
 
+void wpas_notify_p2p_group_formation_failure(struct wpa_supplicant *wpa_s,
+					     const char *reason)
+{
+	/* Notify a group formation failed */
+	wpas_dbus_signal_p2p_group_formation_failure(wpa_s, reason);
+}
+
+
 void wpas_notify_p2p_wps_failed(struct wpa_supplicant *wpa_s,
 				struct wps_event_fail *fail)
 {
 	wpas_dbus_signal_p2p_wps_failed(wpa_s, fail);
 }
 
+
+void wpas_notify_p2p_invitation_received(struct wpa_supplicant *wpa_s,
+					 const u8 *sa, const u8 *go_dev_addr,
+					 const u8 *bssid, int id, int op_freq)
+{
+	/* Notify a P2P Invitation Request */
+	wpas_dbus_signal_p2p_invitation_received(wpa_s, sa, go_dev_addr, bssid,
+						 id, op_freq);
+}
+
 #endif /* CONFIG_P2P */
 
 
diff --git a/wpa_supplicant/notify.h b/wpa_supplicant/notify.h
index 1aeec47..d9f0f5a 100644
--- a/wpa_supplicant/notify.h
+++ b/wpa_supplicant/notify.h
@@ -114,6 +114,8 @@
 void wpas_notify_p2p_group_started(struct wpa_supplicant *wpa_s,
 				   struct wpa_ssid *ssid, int network_id,
 				   int client);
+void wpas_notify_p2p_group_formation_failure(struct wpa_supplicant *wpa_s,
+					     const char *reason);
 void wpas_notify_persistent_group_added(struct wpa_supplicant *wpa_s,
 					struct wpa_ssid *ssid);
 void wpas_notify_persistent_group_removed(struct wpa_supplicant *wpa_s,
@@ -135,5 +137,8 @@
 					   struct wpa_ssid *ssid);
 void wpas_notify_network_type_changed(struct wpa_supplicant *wpa_s,
 				      struct wpa_ssid *ssid);
+void wpas_notify_p2p_invitation_received(struct wpa_supplicant *wpa_s,
+					 const u8 *sa, const u8 *go_dev_addr,
+					 const u8 *bssid, int id, int op_freq);
 
 #endif /* NOTIFY_H */
diff --git a/wpa_supplicant/offchannel.c b/wpa_supplicant/offchannel.c
index 63af83a..6b3f83c 100644
--- a/wpa_supplicant/offchannel.c
+++ b/wpa_supplicant/offchannel.c
@@ -118,8 +118,9 @@
 	}
 
 	wpa_printf(MSG_DEBUG, "Off-channel: Sending pending Action frame to "
-		   MACSTR " using interface %s",
-		   MAC2STR(wpa_s->pending_action_dst), iface->ifname);
+		   MACSTR " using interface %s (pending_action_tx=%p)",
+		   MAC2STR(wpa_s->pending_action_dst), iface->ifname,
+		   wpa_s->pending_action_tx);
 	res = wpa_drv_send_action(iface, wpa_s->pending_action_freq, 0,
 				  wpa_s->pending_action_dst,
 				  wpa_s->pending_action_src,
@@ -183,8 +184,12 @@
 		return;
 	}
 
-	wpa_printf(MSG_DEBUG, "Off-channel: Delete matching pending action frame");
-
+	wpa_printf(MSG_DEBUG,
+		   "Off-channel: Delete matching pending action frame (dst="
+		   MACSTR " pending_action_tx=%p)", MAC2STR(dst),
+		   wpa_s->pending_action_tx);
+	wpa_hexdump_buf(MSG_MSGDUMP, "Pending TX frame",
+			wpa_s->pending_action_tx);
 	wpabuf_free(wpa_s->pending_action_tx);
 	wpa_s->pending_action_tx = NULL;
 
@@ -250,8 +255,11 @@
 
 	if (wpa_s->pending_action_tx) {
 		wpa_printf(MSG_DEBUG, "Off-channel: Dropped pending Action "
-			   "frame TX to " MACSTR,
-			   MAC2STR(wpa_s->pending_action_dst));
+			   "frame TX to " MACSTR " (pending_action_tx=%p)",
+			   MAC2STR(wpa_s->pending_action_dst),
+			   wpa_s->pending_action_tx);
+		wpa_hexdump_buf(MSG_MSGDUMP, "Pending TX frame",
+				wpa_s->pending_action_tx);
 		wpabuf_free(wpa_s->pending_action_tx);
 	}
 	wpa_s->pending_action_tx_done = 0;
@@ -268,6 +276,12 @@
 	os_memcpy(wpa_s->pending_action_bssid, bssid, ETH_ALEN);
 	wpa_s->pending_action_freq = freq;
 	wpa_s->pending_action_no_cck = no_cck;
+	wpa_printf(MSG_DEBUG,
+		   "Off-channel: Stored pending action frame (dst=" MACSTR
+		   " pending_action_tx=%p)",
+		   MAC2STR(dst), wpa_s->pending_action_tx);
+	wpa_hexdump_buf(MSG_MSGDUMP, "Pending TX frame",
+			wpa_s->pending_action_tx);
 
 	if (freq != 0 && wpa_s->drv_flags & WPA_DRIVER_FLAGS_OFFCHANNEL_TX) {
 		struct wpa_supplicant *iface;
@@ -428,6 +442,9 @@
  */
 void offchannel_clear_pending_action_tx(struct wpa_supplicant *wpa_s)
 {
+	wpa_printf(MSG_DEBUG,
+		   "Off-channel: Clear pending Action frame TX (pending_action_tx=%p",
+		   wpa_s->pending_action_tx);
 	wpabuf_free(wpa_s->pending_action_tx);
 	wpa_s->pending_action_tx = NULL;
 }
diff --git a/wpa_supplicant/p2p_supplicant.c b/wpa_supplicant/p2p_supplicant.c
index b9ebd38..c7ddc99 100644
--- a/wpa_supplicant/p2p_supplicant.c
+++ b/wpa_supplicant/p2p_supplicant.c
@@ -47,6 +47,19 @@
 
 #define P2P_AUTO_PD_SCAN_ATTEMPTS 5
 
+/**
+ * Defines time interval in seconds when a GO needs to evacuate a frequency that
+ * it is currently using, but is no longer valid for P2P use cases.
+ */
+#define P2P_GO_FREQ_CHANGE_TIME 5
+
+/**
+ * Defines CSA parameters which are used when GO evacuates the no longer valid
+ * channel (and if the driver supports channel switch).
+ */
+#define P2P_GO_CSA_COUNT 7
+#define P2P_GO_CSA_BLOCK_TX 0
+
 #ifndef P2P_MAX_CLIENT_IDLE
 /*
  * How many seconds to try to reconnect to the GO when connection in P2P client
@@ -85,6 +98,12 @@
 
 #define P2P_MGMT_DEVICE_PREFIX		"p2p-dev-"
 
+/*
+ * How many seconds to wait to re-attempt to move GOs, in case previous attempt
+ * was not possible.
+ */
+#define P2P_RECONSIDER_GO_MOVE_DELAY 30
+
 enum p2p_group_removal_reason {
 	P2P_GROUP_REMOVAL_UNKNOWN,
 	P2P_GROUP_REMOVAL_SILENT,
@@ -94,7 +113,8 @@
 	P2P_GROUP_REMOVAL_UNAVAILABLE,
 	P2P_GROUP_REMOVAL_GO_ENDING_SESSION,
 	P2P_GROUP_REMOVAL_PSK_FAILURE,
-	P2P_GROUP_REMOVAL_FREQ_CONFLICT
+	P2P_GROUP_REMOVAL_FREQ_CONFLICT,
+	P2P_GROUP_REMOVAL_GO_LEAVE_CHANNEL
 };
 
 
@@ -104,6 +124,10 @@
 			 int go);
 static int wpas_p2p_join_start(struct wpa_supplicant *wpa_s, int freq,
 			       const u8 *ssid, size_t ssid_len);
+static int wpas_p2p_setup_freqs(struct wpa_supplicant *wpa_s, int freq,
+				int *force_freq, int *pref_freq, int go,
+				unsigned int *pref_freq_list,
+				unsigned int *num_pref_freq);
 static void wpas_p2p_join_scan_req(struct wpa_supplicant *wpa_s, int freq,
 				   const u8 *ssid, size_t ssid_len);
 static void wpas_p2p_join_scan(void *eloop_ctx, void *timeout_ctx);
@@ -128,6 +152,16 @@
 					enum wpa_driver_if_type type);
 static void wpas_p2p_group_formation_failed(struct wpa_supplicant *wpa_s,
 					    int already_deleted);
+static void wpas_p2p_optimize_listen_channel(struct wpa_supplicant *wpa_s,
+					     struct wpa_used_freq_data *freqs,
+					     unsigned int num);
+static void wpas_p2p_move_go(void *eloop_ctx, void *timeout_ctx);
+static int wpas_p2p_go_is_peer_freq(struct wpa_supplicant *wpa_s, int freq);
+static void
+wpas_p2p_consider_moving_gos(struct wpa_supplicant *wpa_s,
+			     struct wpa_used_freq_data *freqs, unsigned int num,
+			     enum wpas_p2p_channel_update_trig trig);
+static void wpas_p2p_reconsider_moving_go(void *eloop_ctx, void *timeout_ctx);
 
 
 /*
@@ -515,27 +549,39 @@
 }
 
 
+static unsigned int p2p_is_active_persistent_group(struct wpa_supplicant *wpa_s)
+{
+	return !wpa_s->p2p_mgmt && wpa_s->current_ssid &&
+		!wpa_s->current_ssid->disabled &&
+		wpa_s->current_ssid->p2p_group &&
+		wpa_s->current_ssid->p2p_persistent_group;
+}
+
+
+static unsigned int p2p_is_active_persistent_go(struct wpa_supplicant *wpa_s)
+{
+	return p2p_is_active_persistent_group(wpa_s) &&
+		wpa_s->current_ssid->mode == WPAS_MODE_P2P_GO;
+}
+
+
 /* Find an interface for a P2P group where we are the GO */
 static struct wpa_supplicant *
 wpas_p2p_get_go_group(struct wpa_supplicant *wpa_s)
 {
 	struct wpa_supplicant *save = NULL;
-	struct wpa_ssid *s;
 
 	if (!wpa_s)
 		return NULL;
 
 	for (wpa_s = wpa_s->global->ifaces; wpa_s; wpa_s = wpa_s->next) {
-		for (s = wpa_s->conf->ssid; s; s = s->next) {
-			if (s->disabled || !s->p2p_group ||
-			    s->mode != WPAS_MODE_P2P_GO)
-				continue;
+		if (!p2p_is_active_persistent_go(wpa_s))
+			continue;
 
-			/* Prefer a group with connected clients */
-			if (p2p_get_group_num_members(wpa_s->p2p_group))
-				return wpa_s;
-			save = wpa_s;
-		}
+		/* Prefer a group with connected clients */
+		if (p2p_get_group_num_members(wpa_s->p2p_group))
+			return wpa_s;
+		save = wpa_s;
 	}
 
 	/* No group with connected clients, so pick the one without (if any) */
@@ -543,29 +589,23 @@
 }
 
 
-/* Find an active P2P group where we are the GO */
-static struct wpa_ssid * wpas_p2p_group_go_ssid(struct wpa_supplicant *wpa_s,
-						u8 *bssid)
+static unsigned int p2p_is_active_persistent_cli(struct wpa_supplicant *wpa_s)
 {
-	struct wpa_ssid *s, *empty = NULL;
+	return p2p_is_active_persistent_group(wpa_s) &&
+		wpa_s->current_ssid->mode == WPAS_MODE_INFRA;
+}
 
-	if (!wpa_s)
-		return 0;
 
+/* Find an interface for a P2P group where we are the P2P Client */
+static struct wpa_supplicant *
+wpas_p2p_get_cli_group(struct wpa_supplicant *wpa_s)
+{
 	for (wpa_s = wpa_s->global->ifaces; wpa_s; wpa_s = wpa_s->next) {
-		for (s = wpa_s->conf->ssid; s; s = s->next) {
-			if (s->disabled || !s->p2p_group ||
-			    s->mode != WPAS_MODE_P2P_GO)
-				continue;
-
-			os_memcpy(bssid, wpa_s->own_addr, ETH_ALEN);
-			if (p2p_get_group_num_members(wpa_s->p2p_group))
-				return s;
-			empty = s;
-		}
+		if (p2p_is_active_persistent_cli(wpa_s))
+			return wpa_s;
 	}
 
-	return empty;
+	return NULL;
 }
 
 
@@ -584,20 +624,34 @@
 }
 
 
-static u8 p2ps_group_capability(void *ctx, u8 incoming, u8 role)
+static u8 p2ps_group_capability(void *ctx, u8 incoming, u8 role,
+				unsigned int *force_freq,
+				unsigned int *pref_freq)
 {
-	struct wpa_supplicant *wpa_s = ctx, *tmp_wpa_s;
+	struct wpa_supplicant *wpa_s = ctx;
 	struct wpa_ssid *s;
 	u8 conncap = P2PS_SETUP_NONE;
 	unsigned int owned_members = 0;
-	unsigned int owner = 0;
-	unsigned int client = 0;
-	struct wpa_supplicant *go_wpa_s;
+	struct wpa_supplicant *go_wpa_s, *cli_wpa_s;
 	struct wpa_ssid *persistent_go;
 	int p2p_no_group_iface;
+	unsigned int pref_freq_list[P2P_MAX_PREF_CHANNELS], size;
 
 	wpa_printf(MSG_DEBUG, "P2P: Conncap - in:%d role:%d", incoming, role);
 
+	if (force_freq)
+		*force_freq = 0;
+	if (pref_freq)
+		*pref_freq = 0;
+
+	size = P2P_MAX_PREF_CHANNELS;
+	if (force_freq && pref_freq &&
+	    !wpas_p2p_setup_freqs(wpa_s, 0, (int *) force_freq,
+				  (int *) pref_freq, 0, pref_freq_list, &size))
+		wpas_p2p_set_own_freq_preference(wpa_s,
+						 *force_freq ? *force_freq :
+						 *pref_freq);
+
 	/*
 	 * For non-concurrent capable devices:
 	 * If persistent_go, then no new.
@@ -605,36 +659,21 @@
 	 * If client, then no GO.
 	 */
 	go_wpa_s = wpas_p2p_get_go_group(wpa_s);
+	if (go_wpa_s)
+		owned_members = p2p_get_group_num_members(go_wpa_s->p2p_group);
 	persistent_go = wpas_p2p_get_persistent_go(wpa_s);
-	p2p_no_group_iface = wpa_s->conf->p2p_no_group_iface;
+	p2p_no_group_iface = !wpas_p2p_create_iface(wpa_s);
+	cli_wpa_s = wpas_p2p_get_cli_group(wpa_s);
 
-	wpa_printf(MSG_DEBUG, "P2P: GO(iface)=%p persistent(ssid)=%p",
-		   go_wpa_s, persistent_go);
-
-	for (tmp_wpa_s = wpa_s->global->ifaces; tmp_wpa_s;
-	     tmp_wpa_s = tmp_wpa_s->next) {
-		for (s = tmp_wpa_s->conf->ssid; s; s = s->next) {
-			wpa_printf(MSG_DEBUG,
-				   "P2P: sup:%p ssid:%p disabled:%d p2p:%d mode:%d",
-				   tmp_wpa_s, s, s->disabled,
-				   s->p2p_group, s->mode);
-			if (!s->disabled && s->p2p_group) {
-				if (s->mode == WPAS_MODE_P2P_GO) {
-					owned_members +=
-						p2p_get_group_num_members(
-							tmp_wpa_s->p2p_group);
-					owner++;
-				} else
-					client++;
-			}
-		}
-	}
+	wpa_printf(MSG_DEBUG,
+		   "P2P: GO(iface)=%p members=%u CLI(iface)=%p persistent(ssid)=%p",
+		   go_wpa_s, owned_members, cli_wpa_s, persistent_go);
 
 	/* If not concurrent, restrict our choices */
 	if (p2p_no_group_iface) {
 		wpa_printf(MSG_DEBUG, "P2P: p2p_no_group_iface");
 
-		if (client)
+		if (cli_wpa_s)
 			return P2PS_SETUP_NONE;
 
 		if (go_wpa_s) {
@@ -666,10 +705,20 @@
 	/* If a required role has been specified, handle it here */
 	if (role && role != P2PS_SETUP_NEW) {
 		switch (incoming) {
+		case P2PS_SETUP_GROUP_OWNER | P2PS_SETUP_NEW:
+		case P2PS_SETUP_GROUP_OWNER | P2PS_SETUP_CLIENT:
+			/*
+			 * Peer has an active GO, so if the role allows it and
+			 * we do not have any active roles, become client.
+			 */
+			if ((role & P2PS_SETUP_CLIENT) && !go_wpa_s &&
+			    !cli_wpa_s)
+				return P2PS_SETUP_CLIENT;
+
+			/* fall through */
+
 		case P2PS_SETUP_NONE:
 		case P2PS_SETUP_NEW:
-		case P2PS_SETUP_GROUP_OWNER | P2PS_SETUP_CLIENT:
-		case P2PS_SETUP_GROUP_OWNER | P2PS_SETUP_NEW:
 			conncap = role;
 			goto grp_owner;
 
@@ -678,7 +727,7 @@
 			 * Must be a complimentary role - cannot be a client to
 			 * more than one peer.
 			 */
-			if (incoming == role || client)
+			if (incoming == role || cli_wpa_s)
 				return P2PS_SETUP_NONE;
 
 			return P2PS_SETUP_CLIENT;
@@ -704,7 +753,7 @@
 	switch (incoming) {
 	case P2PS_SETUP_NONE:
 	case P2PS_SETUP_NEW:
-		if (client)
+		if (cli_wpa_s)
 			conncap = P2PS_SETUP_GROUP_OWNER;
 		else if (!owned_members)
 			conncap = P2PS_SETUP_NEW;
@@ -719,13 +768,20 @@
 		break;
 
 	case P2PS_SETUP_GROUP_OWNER:
-		if (!client)
+		if (!cli_wpa_s)
 			conncap = P2PS_SETUP_CLIENT;
 		break;
 
 	case P2PS_SETUP_GROUP_OWNER | P2PS_SETUP_NEW:
 	case P2PS_SETUP_GROUP_OWNER | P2PS_SETUP_CLIENT:
-		if (client)
+		/*
+		 * Peer has an active GO, so if the role allows it and
+		 * we do not have any active roles, become client.
+		 */
+		if ((role & P2PS_SETUP_CLIENT) && !go_wpa_s && !cli_wpa_s)
+			return P2PS_SETUP_CLIENT;
+
+		if (cli_wpa_s)
 			conncap = P2PS_SETUP_GROUP_OWNER;
 		else {
 			u8 r;
@@ -747,15 +803,12 @@
 	    (!incoming && (conncap & P2PS_SETUP_NEW))) {
 		if (go_wpa_s && p2p_client_limit_reached(go_wpa_s->p2p_group))
 			conncap &= ~P2PS_SETUP_GROUP_OWNER;
-		wpa_printf(MSG_DEBUG, "P2P: GOs:%d members:%d conncap:%d",
-			   owner, owned_members, conncap);
 
 		s = wpas_p2p_get_persistent_go(wpa_s);
-
-		if (!s && !owner && p2p_no_group_iface) {
+		if (!s && !go_wpa_s && p2p_no_group_iface) {
 			p2p_set_intended_addr(wpa_s->global->p2p,
 					      wpa_s->own_addr);
-		} else if (!s && !owner) {
+		} else if (!s && !go_wpa_s) {
 			if (wpas_p2p_add_group_interface(wpa_s,
 							 WPA_IF_P2P_GO) < 0) {
 				wpa_printf(MSG_ERROR,
@@ -872,9 +925,12 @@
 		wpa_printf(MSG_DEBUG, "P2P: Cancelled P2P group formation "
 			   "timeout");
 		wpa_s->p2p_in_provisioning = 0;
+		wpas_p2p_group_formation_failed(wpa_s, 1);
 	}
 
 	wpa_s->p2p_in_invitation = 0;
+	eloop_cancel_timeout(wpas_p2p_move_go, wpa_s, NULL);
+	eloop_cancel_timeout(wpas_p2p_reconsider_moving_go, wpa_s, NULL);
 
 	/*
 	 * Make sure wait for the first client does not remain active after the
@@ -946,6 +1002,8 @@
 	else
 		wpa_drv_deinit_p2p_cli(wpa_s);
 
+	os_memset(wpa_s->go_dev_addr, 0, ETH_ALEN);
+
 	return 0;
 }
 
@@ -1258,6 +1316,7 @@
 	if (!success) {
 		wpa_msg_global(wpa_s->parent, MSG_INFO,
 			       P2P_EVENT_GROUP_FORMATION_FAILURE);
+		wpas_notify_p2p_group_formation_failure(wpa_s, "");
 		if (already_deleted)
 			return;
 		wpas_p2p_group_delete(wpa_s,
@@ -1339,6 +1398,25 @@
 };
 
 
+static void wpas_p2p_free_send_action_work(struct wpa_supplicant *wpa_s)
+{
+	struct send_action_work *awork = wpa_s->p2p_send_action_work->ctx;
+
+	wpa_printf(MSG_DEBUG,
+		   "P2P: Free Action frame radio work @%p (freq=%u dst="
+		   MACSTR " src=" MACSTR " bssid=" MACSTR " wait_time=%u)",
+		   wpa_s->p2p_send_action_work, awork->freq,
+		   MAC2STR(awork->dst), MAC2STR(awork->src),
+		   MAC2STR(awork->bssid), awork->wait_time);
+	wpa_hexdump(MSG_DEBUG, "P2P: Freeing pending Action frame",
+		    awork->buf, awork->len);
+	os_free(awork);
+	wpa_s->p2p_send_action_work->ctx = NULL;
+	radio_work_done(wpa_s->p2p_send_action_work);
+	wpa_s->p2p_send_action_work = NULL;
+}
+
+
 static void wpas_p2p_send_action_work_timeout(void *eloop_ctx,
 					      void *timeout_ctx)
 {
@@ -1348,9 +1426,7 @@
 		return;
 
 	wpa_printf(MSG_DEBUG, "P2P: Send Action frame radio work timed out");
-	os_free(wpa_s->p2p_send_action_work->ctx);
-	radio_work_done(wpa_s->p2p_send_action_work);
-	wpa_s->p2p_send_action_work = NULL;
+	wpas_p2p_free_send_action_work(wpa_s);
 }
 
 
@@ -1358,11 +1434,13 @@
 {
 	if (wpa_s->p2p_send_action_work) {
 		struct send_action_work *awork;
+
 		awork = wpa_s->p2p_send_action_work->ctx;
+		wpa_printf(MSG_DEBUG,
+			   "P2P: Clear Action TX work @%p (wait_time=%u)",
+			   wpa_s->p2p_send_action_work, awork->wait_time);
 		if (awork->wait_time == 0) {
-			os_free(awork);
-			radio_work_done(wpa_s->p2p_send_action_work);
-			wpa_s->p2p_send_action_work = NULL;
+			wpas_p2p_free_send_action_work(wpa_s);
 		} else {
 			/*
 			 * In theory, this should not be needed, but number of
@@ -1670,6 +1748,15 @@
 	struct wpa_ssid *ssid;
 	int network_id = -1;
 
+	wpa_s->ap_configured_cb = NULL;
+	wpa_s->ap_configured_cb_ctx = NULL;
+	wpa_s->ap_configured_cb_data = NULL;
+	if (!wpa_s->go_params) {
+		wpa_printf(MSG_ERROR,
+			   "P2P: p2p_go_configured() called with wpa_s->go_params == NULL");
+		return;
+	}
+
 	p2p_go_save_group_common_freqs(wpa_s, params);
 	p2p_go_dump_common_freqs(wpa_s);
 
@@ -1686,14 +1773,22 @@
 				       params->persistent_group, "");
 		wpa_s->group_formation_reported = 1;
 
-		if (wpa_s->parent->p2ps_join_addr_valid) {
-			wpa_dbg(wpa_s, MSG_DEBUG,
-				"P2PS: Setting default PIN for " MACSTR,
-				MAC2STR(wpa_s->parent->p2ps_join_addr));
-			wpa_supplicant_ap_wps_pin(wpa_s,
-						  wpa_s->parent->p2ps_join_addr,
-						  "12345670", NULL, 0, 0);
-			wpa_s->parent->p2ps_join_addr_valid = 0;
+		if (wpa_s->parent->p2ps_method_config_any) {
+			if (is_zero_ether_addr(wpa_s->parent->p2ps_join_addr)) {
+				wpa_dbg(wpa_s, MSG_DEBUG,
+					"P2PS: Setting default PIN for ANY");
+				wpa_supplicant_ap_wps_pin(wpa_s, NULL,
+							  "12345670", NULL, 0,
+							  0);
+			} else {
+				wpa_dbg(wpa_s, MSG_DEBUG,
+					"P2PS: Setting default PIN for " MACSTR,
+					MAC2STR(wpa_s->parent->p2ps_join_addr));
+				wpa_supplicant_ap_wps_pin(
+					wpa_s, wpa_s->parent->p2ps_join_addr,
+					"12345670", NULL, 0, 0);
+			}
+			wpa_s->parent->p2ps_method_config_any = 0;
 		}
 
 		os_get_reltime(&wpa_s->global->p2p_go_wait_client);
@@ -1780,6 +1875,7 @@
 	wpa_s->show_group_started = 0;
 	wpa_s->p2p_go_group_formation_completed = 0;
 	wpa_s->group_formation_reported = 0;
+	os_memset(wpa_s->go_dev_addr, 0, ETH_ALEN);
 
 	wpa_config_set_network_defaults(ssid);
 	ssid->temporary = 1;
@@ -1790,6 +1886,8 @@
 	ssid->frequency = params->freq;
 	ssid->ht40 = params->ht40;
 	ssid->vht = params->vht;
+	ssid->max_oper_chwidth = params->max_oper_chwidth;
+	ssid->vht_center_freq2 = params->vht_center_freq2;
 	ssid->ssid = os_zalloc(params->ssid_len + 1);
 	if (ssid->ssid) {
 		os_memcpy(ssid->ssid, params->ssid, params->ssid_len);
@@ -1862,6 +1960,7 @@
 	d->num_sec_device_types = s->num_sec_device_types;
 
 	d->p2p_group_idle = s->p2p_group_idle;
+	d->p2p_go_freq_change_policy = s->p2p_go_freq_change_policy;
 	d->p2p_intra_bss = s->p2p_intra_bss;
 	d->persistent_reconnect = s->persistent_reconnect;
 	d->max_num_sta = s->max_num_sta;
@@ -2081,10 +2180,17 @@
 		return;
 	}
 
+	if (!res->role_go) {
+		/* Inform driver of the operating channel of GO. */
+		wpa_drv_set_prob_oper_freq(wpa_s, res->freq);
+	}
+
 	if (wpa_s->p2p_go_ht40)
 		res->ht40 = 1;
 	if (wpa_s->p2p_go_vht)
 		res->vht = 1;
+	res->max_oper_chwidth = wpa_s->p2p_go_max_oper_chwidth;
+	res->vht_center_freq2 = wpa_s->p2p_go_vht_center_freq2;
 
 	wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_GO_NEG_SUCCESS "role=%s "
 		       "freq=%d ht40=%d peer_dev=" MACSTR " peer_iface=" MACSTR
@@ -2128,18 +2234,22 @@
 		wpa_s->pending_interface_name[0] = '\0';
 		group_wpa_s->p2p_in_provisioning = 1;
 
-		if (res->role_go)
+		if (res->role_go) {
 			wpas_start_wps_go(group_wpa_s, res, 1);
-		else
+		} else {
+			os_get_reltime(&group_wpa_s->scan_min_time);
 			wpas_start_wps_enrollee(group_wpa_s, res);
+		}
 	} else {
 		wpa_s->p2p_in_provisioning = 1;
 		wpa_s->global->p2p_group_formation = wpa_s;
 
-		if (res->role_go)
+		if (res->role_go) {
 			wpas_start_wps_go(wpa_s, res, 1);
-		else
+		} else {
+			os_get_reltime(&wpa_s->scan_min_time);
 			wpas_start_wps_enrollee(ctx, res);
+		}
 	}
 
 	wpa_s->p2p_long_listen = 0;
@@ -2578,12 +2688,85 @@
 }
 
 
-static int freq_included(const struct p2p_channels *channels, unsigned int freq)
+static int freq_included(struct wpa_supplicant *wpa_s,
+			 const struct p2p_channels *channels,
+			 unsigned int freq)
 {
-	if (channels == NULL)
-		return 1; /* Assume no restrictions */
-	return p2p_channels_includes_freq(channels, freq);
+	if ((channels == NULL || p2p_channels_includes_freq(channels, freq)) &&
+	    wpas_p2p_go_is_peer_freq(wpa_s, freq))
+		return 1;
+	return 0;
+}
 
+
+static void wpas_p2p_go_update_common_freqs(struct wpa_supplicant *wpa_s)
+{
+	unsigned int num = P2P_MAX_CHANNELS;
+	int *common_freqs;
+	int ret;
+
+	p2p_go_dump_common_freqs(wpa_s);
+	common_freqs = os_calloc(num, sizeof(int));
+	if (!common_freqs)
+		return;
+
+	ret = p2p_group_get_common_freqs(wpa_s->p2p_group, common_freqs, &num);
+	if (ret < 0) {
+		wpa_dbg(wpa_s, MSG_DEBUG,
+			"P2P: Failed to get group common freqs");
+		os_free(common_freqs);
+		return;
+	}
+
+	os_free(wpa_s->p2p_group_common_freqs);
+	wpa_s->p2p_group_common_freqs = common_freqs;
+	wpa_s->p2p_group_common_freqs_num = num;
+	p2p_go_dump_common_freqs(wpa_s);
+}
+
+
+/*
+ * Check if the given frequency is one of the possible operating frequencies
+ * set after the completion of the GO Negotiation.
+ */
+static int wpas_p2p_go_is_peer_freq(struct wpa_supplicant *wpa_s, int freq)
+{
+	unsigned int i;
+
+	p2p_go_dump_common_freqs(wpa_s);
+
+	/* assume no restrictions */
+	if (!wpa_s->p2p_group_common_freqs_num)
+		return 1;
+
+	for (i = 0; i < wpa_s->p2p_group_common_freqs_num; i++) {
+		if (wpa_s->p2p_group_common_freqs[i] == freq)
+			return 1;
+	}
+	return 0;
+}
+
+
+static int wpas_sta_check_ecsa(struct hostapd_data *hapd,
+			       struct sta_info *sta, void *ctx)
+{
+	int *ecsa_support = ctx;
+
+	*ecsa_support &= sta->ecsa_supported;
+
+	return 0;
+}
+
+
+/* Check if all the peers support eCSA */
+static int wpas_p2p_go_clients_support_ecsa(struct wpa_supplicant *wpa_s)
+{
+	int ecsa_support = 1;
+
+	ap_for_each_sta(wpa_s->ap_iface->bss[0], wpas_sta_check_ecsa,
+			&ecsa_support);
+
+	return ecsa_support;
 }
 
 
@@ -2759,7 +2942,7 @@
 				   "running a GO but we are capable of MCC, "
 				   "figure out the best channel to use");
 			*force_freq = 0;
-		} else if (!freq_included(channels, *force_freq)) {
+		} else if (!freq_included(wpa_s, channels, *force_freq)) {
 			/* We are the GO, and *force_freq is not in the
 			 * intersection */
 			wpa_printf(MSG_DEBUG, "P2P: Forced GO freq %d MHz not "
@@ -2796,8 +2979,9 @@
 		if (s) {
 			int go = s->mode == WPAS_MODE_P2P_GO;
 			wpas_p2p_group_add_persistent(
-				wpa_s, s, go, 0, op_freq, 0, 0, NULL,
-				go ? P2P_MAX_INITIAL_CONN_WAIT_GO_REINVOKE : 0);
+				wpa_s, s, go, 0, op_freq, 0, 0, 0, 0, NULL,
+				go ? P2P_MAX_INITIAL_CONN_WAIT_GO_REINVOKE : 0,
+				1);
 		} else if (bssid) {
 			wpa_s->user_initiated_pd = 0;
 			wpas_p2p_join(wpa_s, bssid, go_dev_addr,
@@ -2828,6 +3012,8 @@
 				       " unknown-network",
 				       MAC2STR(sa), MAC2STR(go_dev_addr));
 		}
+		wpas_notify_p2p_invitation_received(wpa_s, sa, go_dev_addr,
+						    bssid, 0, op_freq);
 		return;
 	}
 
@@ -2840,6 +3026,8 @@
 			       "sa=" MACSTR " persistent=%d",
 			       MAC2STR(sa), s->id);
 	}
+	wpas_notify_p2p_invitation_received(wpa_s, sa, go_dev_addr, bssid,
+					    s->id, op_freq);
 }
 
 
@@ -2924,9 +3112,37 @@
 	wpa_printf(MSG_DEBUG, "P2P: Invitation result - status=%d peer=" MACSTR,
 		   status, MAC2STR(peer));
 	if (wpa_s->pending_invite_ssid_id == -1) {
+		struct wpa_supplicant *group_if =
+			wpa_s->global->p2p_invite_group;
+
 		if (status == P2P_SC_FAIL_UNKNOWN_GROUP)
 			wpas_remove_persistent_client(wpa_s, peer);
-		return; /* Invitation to active group */
+
+		/*
+		 * Invitation to an active group. If this is successful and we
+		 * are the GO, set the client wait to postpone some concurrent
+		 * operations and to allow provisioning and connection to happen
+		 * more quickly.
+		 */
+		if (status == P2P_SC_SUCCESS &&
+		    group_if && group_if->current_ssid &&
+		    group_if->current_ssid->mode == WPAS_MODE_P2P_GO) {
+			os_get_reltime(&wpa_s->global->p2p_go_wait_client);
+#ifdef CONFIG_TESTING_OPTIONS
+			if (group_if->p2p_go_csa_on_inv) {
+				wpa_printf(MSG_DEBUG,
+					   "Testing: force P2P GO CSA after invitation");
+				eloop_cancel_timeout(
+					wpas_p2p_reconsider_moving_go,
+					wpa_s, NULL);
+				eloop_register_timeout(
+					0, 50000,
+					wpas_p2p_reconsider_moving_go,
+					wpa_s, NULL);
+			}
+#endif /* CONFIG_TESTING_OPTIONS */
+		}
+		return;
 	}
 
 	if (status == P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE) {
@@ -2966,10 +3182,10 @@
 	os_sleep(0, 50000);
 
 	if (neg_freq > 0 && ssid->mode == WPAS_MODE_P2P_GO &&
-	    freq_included(channels, neg_freq))
+	    freq_included(wpa_s, channels, neg_freq))
 		freq = neg_freq;
 	else if (peer_oper_freq > 0 && ssid->mode != WPAS_MODE_P2P_GO &&
-		 freq_included(channels, peer_oper_freq))
+		 freq_included(wpa_s, channels, peer_oper_freq))
 		freq = peer_oper_freq;
 	else
 		freq = 0;
@@ -2980,11 +3196,13 @@
 				      ssid->mode == WPAS_MODE_P2P_GO,
 				      wpa_s->p2p_persistent_go_freq,
 				      freq,
+				      wpa_s->p2p_go_vht_center_freq2,
 				      wpa_s->p2p_go_ht40, wpa_s->p2p_go_vht,
+				      wpa_s->p2p_go_max_oper_chwidth,
 				      channels,
 				      ssid->mode == WPAS_MODE_P2P_GO ?
 				      P2P_MAX_INITIAL_CONN_WAIT_GO_REINVOKE :
-				      0);
+				      0, 1);
 }
 
 
@@ -3120,7 +3338,7 @@
 	u8 min_chan;
 	u8 max_chan;
 	u8 inc;
-	enum { BW20, BW40PLUS, BW40MINUS, BW80, BW2160 } bw;
+	enum { BW20, BW40PLUS, BW40MINUS, BW80, BW2160, BW160, BW80P80 } bw;
 };
 
 static const struct p2p_oper_class_map op_class[] = {
@@ -3146,6 +3364,8 @@
 	 * removing invalid channels.
 	 */
 	{ HOSTAPD_MODE_IEEE80211A, 128, 36, 161, 4, BW80 },
+	{ HOSTAPD_MODE_IEEE80211A, 130, 36, 161, 4, BW80P80 },
+	{ HOSTAPD_MODE_IEEE80211A, 129, 50, 114, 16, BW160 },
 	{ HOSTAPD_MODE_IEEE80211AD, 180, 1, 4, 1, BW2160 },
 	{ -1, 0, 0, 0, 0, BW20 }
 };
@@ -3212,6 +3432,75 @@
 }
 
 
+static int wpas_p2p_get_center_160mhz(struct wpa_supplicant *wpa_s,
+				     struct hostapd_hw_modes *mode,
+				     u8 channel)
+{
+	u8 center_channels[] = { 50, 114 };
+	unsigned int i;
+
+	if (mode->mode != HOSTAPD_MODE_IEEE80211A)
+		return 0;
+
+	for (i = 0; i < ARRAY_SIZE(center_channels); i++)
+		/*
+		 * In 160 MHz, the bandwidth "spans" 28 channels (e.g., 36-64),
+		 * so the center channel is 14 channels away from the start/end.
+		 */
+		if (channel >= center_channels[i] - 14 &&
+		    channel <= center_channels[i] + 14)
+			return center_channels[i];
+
+	return 0;
+}
+
+
+static enum chan_allowed wpas_p2p_verify_160mhz(struct wpa_supplicant *wpa_s,
+					       struct hostapd_hw_modes *mode,
+					       u8 channel, u8 bw)
+{
+	u8 center_chan;
+	int i, flags;
+	enum chan_allowed res, ret = ALLOWED;
+
+	center_chan = wpas_p2p_get_center_160mhz(wpa_s, mode, channel);
+	if (!center_chan)
+		return NOT_ALLOWED;
+	/* VHT 160 MHz uses DFS channels in most countries. */
+
+	/* Check all the channels are available */
+	for (i = 0; i < 8; i++) {
+		int adj_chan = center_chan - 14 + i * 4;
+
+		res = has_channel(wpa_s->global, mode, adj_chan, &flags);
+		if (res == NOT_ALLOWED)
+			return NOT_ALLOWED;
+
+		if (res == NO_IR)
+			ret = NO_IR;
+
+		if (i == 0 && !(flags & HOSTAPD_CHAN_VHT_10_150))
+			return NOT_ALLOWED;
+		if (i == 1 && !(flags & HOSTAPD_CHAN_VHT_30_130))
+			return NOT_ALLOWED;
+		if (i == 2 && !(flags & HOSTAPD_CHAN_VHT_50_110))
+			return NOT_ALLOWED;
+		if (i == 3 && !(flags & HOSTAPD_CHAN_VHT_70_90))
+			return NOT_ALLOWED;
+		if (i == 4 && !(flags & HOSTAPD_CHAN_VHT_90_70))
+			return NOT_ALLOWED;
+		if (i == 5 && !(flags & HOSTAPD_CHAN_VHT_110_50))
+			return NOT_ALLOWED;
+		if (i == 6 && !(flags & HOSTAPD_CHAN_VHT_130_30))
+			return NOT_ALLOWED;
+		if (i == 7 && !(flags & HOSTAPD_CHAN_VHT_150_10))
+			return NOT_ALLOWED;
+	}
+
+	return ret;
+}
+
+
 static enum chan_allowed wpas_p2p_verify_channel(struct wpa_supplicant *wpa_s,
 						 struct hostapd_hw_modes *mode,
 						 u8 channel, u8 bw)
@@ -3230,6 +3519,8 @@
 		res2 = has_channel(wpa_s->global, mode, channel + 4, NULL);
 	} else if (bw == BW80) {
 		res2 = wpas_p2p_verify_80mhz(wpa_s, mode, channel, bw);
+	} else if (bw == BW160) {
+		res2 = wpas_p2p_verify_160mhz(wpa_s, mode, channel, bw);
 	}
 
 	if (res == NOT_ALLOWED || res2 == NOT_ALLOWED)
@@ -3343,6 +3634,15 @@
 }
 
 
+int wpas_p2p_get_vht160_center(struct wpa_supplicant *wpa_s,
+			       struct hostapd_hw_modes *mode, u8 channel)
+{
+	if (!wpas_p2p_verify_channel(wpa_s, mode, channel, BW160))
+		return 0;
+	return wpas_p2p_get_center_160mhz(wpa_s, mode, channel);
+}
+
+
 static int wpas_get_noa(void *ctx, const u8 *interface_addr, u8 *buf,
 			size_t buf_len)
 {
@@ -3386,12 +3686,7 @@
 {
 	for (wpa_s = wpa_s->global->ifaces; wpa_s; wpa_s = wpa_s->next) {
 		struct wpa_ssid *ssid = wpa_s->current_ssid;
-		if (ssid == NULL)
-			continue;
-		if (ssid->mode != WPAS_MODE_INFRA)
-			continue;
-		if (wpa_s->wpa_state != WPA_COMPLETED &&
-		    wpa_s->wpa_state != WPA_GROUP_HANDSHAKE)
+		if (ssid && (ssid->mode != WPAS_MODE_INFRA || !ssid->p2p_group))
 			continue;
 		if (os_memcmp(wpa_s->go_dev_addr, peer_dev_addr, ETH_ALEN) == 0)
 			return wpa_s;
@@ -3508,7 +3803,8 @@
 
 static int wpas_get_persistent_group(void *ctx, const u8 *addr, const u8 *ssid,
 				     size_t ssid_len, u8 *go_dev_addr,
-				     u8 *ret_ssid, size_t *ret_ssid_len)
+				     u8 *ret_ssid, size_t *ret_ssid_len,
+				     u8 *intended_iface_addr)
 {
 	struct wpa_supplicant *wpa_s = ctx;
 	struct wpa_ssid *s;
@@ -3518,6 +3814,19 @@
 		os_memcpy(ret_ssid, s->ssid, s->ssid_len);
 		*ret_ssid_len = s->ssid_len;
 		os_memcpy(go_dev_addr, s->bssid, ETH_ALEN);
+
+		if (s->mode != WPAS_MODE_P2P_GO) {
+			os_memset(intended_iface_addr, 0, ETH_ALEN);
+		} else if (wpas_p2p_create_iface(wpa_s)) {
+			if (wpas_p2p_add_group_interface(wpa_s, WPA_IF_P2P_GO))
+				return 0;
+
+			os_memcpy(intended_iface_addr,
+				  wpa_s->pending_interface_addr, ETH_ALEN);
+		} else {
+			os_memcpy(intended_iface_addr, wpa_s->own_addr,
+				  ETH_ALEN);
+		}
 		return 1;
 	}
 
@@ -3526,24 +3835,40 @@
 
 
 static int wpas_get_go_info(void *ctx, u8 *intended_addr,
-			    u8 *ssid, size_t *ssid_len, int *group_iface)
+			    u8 *ssid, size_t *ssid_len, int *group_iface,
+			    unsigned int *freq)
 {
 	struct wpa_supplicant *wpa_s = ctx;
+	struct wpa_supplicant *go;
 	struct wpa_ssid *s;
-	u8 bssid[ETH_ALEN];
 
-	s = wpas_p2p_group_go_ssid(wpa_s, bssid);
-	if (!s) {
+	/*
+	 * group_iface will be set to 1 only if a dedicated interface for P2P
+	 * role is required. First, we try to reuse an active GO. However,
+	 * if it is not present, we will try to reactivate an existing
+	 * persistent group and set group_iface to 1, so the caller will know
+	 * that the pending interface should be used.
+	 */
+	*group_iface = 0;
+
+	if (freq)
+		*freq = 0;
+
+	go = wpas_p2p_get_go_group(wpa_s);
+	if (!go) {
 		s = wpas_p2p_get_persistent_go(wpa_s);
+		*group_iface = wpas_p2p_create_iface(wpa_s);
 		if (s)
-			os_memcpy(bssid, s->bssid, ETH_ALEN);
+			os_memcpy(intended_addr, s->bssid, ETH_ALEN);
+		else
+			return 0;
+	} else {
+		s = go->current_ssid;
+		os_memcpy(intended_addr, go->own_addr, ETH_ALEN);
+		if (freq)
+			*freq = go->assoc_freq;
 	}
 
-	*group_iface = wpas_p2p_create_iface(wpa_s);
-	if (!s)
-		return 0;
-
-	os_memcpy(intended_addr, bssid, ETH_ALEN);
 	os_memcpy(ssid, s->ssid, s->ssid_len);
 	*ssid_len = s->ssid_len;
 
@@ -3596,19 +3921,50 @@
 }
 
 
+static void wpas_p2ps_get_feat_cap_str(char *buf, size_t buf_len,
+				       const u8 *feat_cap, size_t feat_cap_len)
+{
+	static const char pref[] = " feature_cap=";
+	int ret;
+
+	buf[0] = '\0';
+
+	/*
+	 * We expect a feature capability to contain at least one byte to be
+	 * reported. The string buffer provided by the caller function is
+	 * expected to be big enough to contain all bytes of the attribute for
+	 * known specifications. This function truncates the reported bytes if
+	 * the feature capability data exceeds the string buffer size.
+	 */
+	if (!feat_cap || !feat_cap_len || buf_len < sizeof(pref) + 2)
+		return;
+
+	os_memcpy(buf, pref, sizeof(pref));
+	ret = wpa_snprintf_hex(&buf[sizeof(pref) - 1],
+			       buf_len - sizeof(pref) + 1,
+			       feat_cap, feat_cap_len);
+
+	if (ret != (2 * (int) feat_cap_len))
+		wpa_printf(MSG_WARNING, "P2PS feature_cap bytes truncated");
+}
+
+
 static void wpas_p2ps_prov_complete(void *ctx, u8 status, const u8 *dev,
 				    const u8 *adv_mac, const u8 *ses_mac,
 				    const u8 *grp_mac, u32 adv_id, u32 ses_id,
 				    u8 conncap, int passwd_id,
 				    const u8 *persist_ssid,
 				    size_t persist_ssid_size, int response_done,
-				    int prov_start, const char *session_info)
+				    int prov_start, const char *session_info,
+				    const u8 *feat_cap, size_t feat_cap_len,
+				    unsigned int freq)
 {
 	struct wpa_supplicant *wpa_s = ctx;
 	u8 mac[ETH_ALEN];
-	struct wpa_ssid *persistent_go, *stale, *s;
+	struct wpa_ssid *persistent_go, *stale, *s = NULL;
 	int save_config = 0;
 	struct wpa_supplicant *go_wpa_s;
+	char feat_cap_str[256];
 
 	if (!dev)
 		return;
@@ -3621,6 +3977,9 @@
 	if (!grp_mac)
 		grp_mac = mac;
 
+	wpas_p2ps_get_feat_cap_str(feat_cap_str, sizeof(feat_cap_str),
+				   feat_cap, feat_cap_len);
+
 	if (prov_start) {
 		if (session_info == NULL) {
 			wpa_msg_global(wpa_s, MSG_INFO,
@@ -3628,22 +3987,22 @@
 				       " adv_id=%x conncap=%x"
 				       " adv_mac=" MACSTR
 				       " session=%x mac=" MACSTR
-				       " dev_passwd_id=%d",
+				       " dev_passwd_id=%d%s",
 				       MAC2STR(dev), adv_id, conncap,
 				       MAC2STR(adv_mac),
 				       ses_id, MAC2STR(ses_mac),
-				       passwd_id);
+				       passwd_id, feat_cap_str);
 		} else {
 			wpa_msg_global(wpa_s, MSG_INFO,
 				       P2P_EVENT_P2PS_PROVISION_START MACSTR
 				       " adv_id=%x conncap=%x"
 				       " adv_mac=" MACSTR
 				       " session=%x mac=" MACSTR
-				       " dev_passwd_id=%d info='%s'",
+				       " dev_passwd_id=%d info='%s'%s",
 				       MAC2STR(dev), adv_id, conncap,
 				       MAC2STR(adv_mac),
 				       ses_id, MAC2STR(ses_mac),
-				       passwd_id, session_info);
+				       passwd_id, session_info, feat_cap_str);
 		}
 		return;
 	}
@@ -3665,16 +4024,25 @@
 			       P2P_EVENT_P2PS_PROVISION_DONE MACSTR
 			       " status=%d"
 			       " adv_id=%x adv_mac=" MACSTR
-			       " session=%x mac=" MACSTR,
+			       " session=%x mac=" MACSTR "%s",
 			       MAC2STR(dev), status,
 			       adv_id, MAC2STR(adv_mac),
-			       ses_id, MAC2STR(ses_mac));
+			       ses_id, MAC2STR(ses_mac), feat_cap_str);
 		return;
 	}
 
 	/* Clean up stale persistent groups with this device */
-	s = wpas_p2p_get_persistent(wpa_s, dev, persist_ssid,
-				    persist_ssid_size);
+	if (persist_ssid && persist_ssid_size)
+		s = wpas_p2p_get_persistent(wpa_s, dev, persist_ssid,
+					    persist_ssid_size);
+
+	if (persist_ssid && s && s->mode != WPAS_MODE_P2P_GO &&
+	    is_zero_ether_addr(grp_mac)) {
+		wpa_dbg(wpa_s, MSG_ERROR,
+			"P2P: Peer device is a GO in a persistent group, but it did not provide the intended MAC address");
+		return;
+	}
+
 	for (;;) {
 		stale = wpas_p2p_get_persistent(wpa_s, dev, NULL, 0);
 		if (!stale)
@@ -3730,29 +4098,40 @@
 			       " status=%d"
 			       " adv_id=%x adv_mac=" MACSTR
 			       " session=%x mac=" MACSTR
-			       " persist=%d",
+			       " persist=%d%s",
 			       MAC2STR(dev), status,
 			       adv_id, MAC2STR(adv_mac),
-			       ses_id, MAC2STR(ses_mac), s->id);
+			       ses_id, MAC2STR(ses_mac), s->id, feat_cap_str);
 		return;
 	}
 
 	if (conncap == P2PS_SETUP_GROUP_OWNER) {
-		const char *go_ifname = NULL;
+		/*
+		 * We need to copy the interface name. Simply saving a
+		 * pointer isn't enough, since if we use pending_interface_name
+		 * it will be overwritten when the group is added.
+		 */
+		char go_ifname[100];
+
+		go_ifname[0] = '\0';
 		if (!go_wpa_s) {
 			wpa_s->global->pending_p2ps_group = 1;
+			wpa_s->global->pending_p2ps_group_freq = freq;
 
-			if (wpa_s->conf->p2p_no_group_iface)
-				go_ifname = wpa_s->ifname;
+			if (!wpas_p2p_create_iface(wpa_s))
+				os_memcpy(go_ifname, wpa_s->ifname,
+					  sizeof(go_ifname));
 			else if (wpa_s->pending_interface_name[0])
-				go_ifname = wpa_s->pending_interface_name;
+				os_memcpy(go_ifname,
+					  wpa_s->pending_interface_name,
+					  sizeof(go_ifname));
 
-			if (!go_ifname) {
+			if (!go_ifname[0]) {
 				wpas_p2ps_prov_complete(
 					wpa_s, P2P_SC_FAIL_UNKNOWN_GROUP,
 					dev, adv_mac, ses_mac,
-					NULL, adv_id, ses_id, 0, 0,
-					NULL, 0, 0, 0, NULL);
+					grp_mac, adv_id, ses_id, 0, 0,
+					NULL, 0, 0, 0, NULL, NULL, 0, 0);
 				return;
 			}
 
@@ -3760,34 +4139,41 @@
 			if (response_done && persistent_go) {
 				wpas_p2p_group_add_persistent(
 					wpa_s, persistent_go,
-					0, 0, 0, 0, 0, NULL,
+					0, 0, freq, 0, 0, 0, 0, NULL,
 					persistent_go->mode ==
 					WPAS_MODE_P2P_GO ?
 					P2P_MAX_INITIAL_CONN_WAIT_GO_REINVOKE :
-					0);
+					0, 0);
 			} else if (response_done) {
-				wpas_p2p_group_add(wpa_s, 1, 0, 0, 0);
+				wpas_p2p_group_add(wpa_s, 1, freq, 0, 0, 0, 0);
 			}
 
 			if (passwd_id == DEV_PW_P2PS_DEFAULT) {
-				os_memcpy(wpa_s->p2ps_join_addr, dev, ETH_ALEN);
-				wpa_s->p2ps_join_addr_valid = 1;
-				wpa_dbg(wpa_s, MSG_DEBUG,
-					"P2PS: Saving PIN for " MACSTR,
-					MAC2STR(dev));
+				os_memcpy(wpa_s->p2ps_join_addr, grp_mac,
+					  ETH_ALEN);
+				wpa_s->p2ps_method_config_any = 1;
 			}
 		} else if (passwd_id == DEV_PW_P2PS_DEFAULT) {
-			go_ifname = go_wpa_s->ifname;
+			os_memcpy(go_ifname, go_wpa_s->ifname,
+				  sizeof(go_ifname));
 
-			wpa_dbg(go_wpa_s, MSG_DEBUG,
-				"P2P: Setting PIN-1 For " MACSTR, MAC2STR(dev));
-			wpa_supplicant_ap_wps_pin(go_wpa_s, dev, "12345670",
-						  NULL, 0, 0);
+			if (is_zero_ether_addr(grp_mac)) {
+				wpa_dbg(go_wpa_s, MSG_DEBUG,
+					"P2P: Setting PIN-1 for ANY");
+				wpa_supplicant_ap_wps_pin(go_wpa_s, NULL,
+							  "12345670", NULL, 0,
+							  0);
+			} else {
+				wpa_dbg(go_wpa_s, MSG_DEBUG,
+					"P2P: Setting PIN-1 for " MACSTR,
+					MAC2STR(grp_mac));
+				wpa_supplicant_ap_wps_pin(go_wpa_s, grp_mac,
+							  "12345670", NULL, 0,
+							  0);
+			}
 
-			os_memcpy(wpa_s->p2ps_join_addr, dev, ETH_ALEN);
-			wpa_s->p2ps_join_addr_valid = 1;
-			wpa_dbg(wpa_s, MSG_DEBUG,
-				"P2PS: Saving PIN for " MACSTR, MAC2STR(dev));
+			os_memcpy(wpa_s->p2ps_join_addr, grp_mac, ETH_ALEN);
+			wpa_s->p2ps_method_config_any = 1;
 		}
 
 		wpa_msg_global(wpa_s, MSG_INFO,
@@ -3795,11 +4181,11 @@
 			       " status=%d conncap=%x"
 			       " adv_id=%x adv_mac=" MACSTR
 			       " session=%x mac=" MACSTR
-			       " dev_passwd_id=%d go=%s",
+			       " dev_passwd_id=%d go=%s%s",
 			       MAC2STR(dev), status, conncap,
 			       adv_id, MAC2STR(adv_mac),
 			       ses_id, MAC2STR(ses_mac),
-			       passwd_id, go_ifname);
+			       passwd_id, go_ifname, feat_cap_str);
 		return;
 	}
 
@@ -3817,22 +4203,22 @@
 			       " status=%d conncap=%x"
 			       " adv_id=%x adv_mac=" MACSTR
 			       " session=%x mac=" MACSTR
-			       " dev_passwd_id=%d join=" MACSTR,
+			       " dev_passwd_id=%d join=" MACSTR "%s",
 			       MAC2STR(dev), status, conncap,
 			       adv_id, MAC2STR(adv_mac),
 			       ses_id, MAC2STR(ses_mac),
-			       passwd_id, MAC2STR(grp_mac));
+			       passwd_id, MAC2STR(grp_mac), feat_cap_str);
 	} else {
 		wpa_msg_global(wpa_s, MSG_INFO,
 			       P2P_EVENT_P2PS_PROVISION_DONE MACSTR
 			       " status=%d conncap=%x"
 			       " adv_id=%x adv_mac=" MACSTR
 			       " session=%x mac=" MACSTR
-			       " dev_passwd_id=%d",
+			       " dev_passwd_id=%d%s",
 			       MAC2STR(dev), status, conncap,
 			       adv_id, MAC2STR(adv_mac),
 			       ses_id, MAC2STR(ses_mac),
-			       passwd_id);
+			       passwd_id, feat_cap_str);
 	}
 }
 
@@ -3848,10 +4234,13 @@
 {
 	struct wpa_supplicant *wpa_s = ctx;
 	struct wpa_ssid *persistent_go;
+	unsigned int freq;
 
 	if (!wpa_s->global->pending_p2ps_group)
 		return 0;
 
+	freq = wpa_s->global->pending_p2ps_group_freq;
+	wpa_s->global->pending_p2ps_group_freq = 0;
 	wpa_s->global->pending_p2ps_group = 0;
 
 	if (wpas_p2p_get_go_group(wpa_s))
@@ -3860,17 +4249,28 @@
 
 	if (persistent_go) {
 		wpas_p2p_group_add_persistent(
-			wpa_s, persistent_go, 0, 0, 0, 0, 0, NULL,
+			wpa_s, persistent_go, 0, 0, 0, 0, 0, 0, 0, NULL,
 			persistent_go->mode == WPAS_MODE_P2P_GO ?
-			P2P_MAX_INITIAL_CONN_WAIT_GO_REINVOKE : 0);
+			P2P_MAX_INITIAL_CONN_WAIT_GO_REINVOKE : 0, 0);
 	} else {
-		wpas_p2p_group_add(wpa_s, 1, 0, 0, 0);
+		wpas_p2p_group_add(wpa_s, 1, freq, 0, 0, 0, 0);
 	}
 
 	return 1;
 }
 
 
+static int wpas_p2p_get_pref_freq_list(void *ctx, int go,
+				       unsigned int *len,
+				       unsigned int *freq_list)
+{
+	struct wpa_supplicant *wpa_s = ctx;
+
+	return wpa_drv_get_pref_freq_list(wpa_s, go ? WPA_IF_P2P_GO :
+					  WPA_IF_P2P_CLIENT, len, freq_list);
+}
+
+
 /**
  * wpas_p2p_init - Initialize P2P module for %wpa_supplicant
  * @global: Pointer to global data from wpa_supplicant_init()
@@ -3924,6 +4324,7 @@
 	p2p.p2ps_prov_complete = wpas_p2ps_prov_complete;
 	p2p.prov_disc_resp_cb = wpas_prov_disc_resp_cb;
 	p2p.p2ps_group_capability = p2ps_group_capability;
+	p2p.get_pref_freq_list = wpas_p2p_get_pref_freq_list;
 
 	os_memcpy(wpa_s->global->p2p_dev_addr, wpa_s->own_addr, ETH_ALEN);
 	os_memcpy(p2p.dev_addr, wpa_s->global->p2p_dev_addr, ETH_ALEN);
@@ -4228,6 +4629,7 @@
 		}
 		wpa_msg_global(wpa_s->parent, MSG_INFO,
 			       P2P_EVENT_GROUP_FORMATION_FAILURE);
+		wpas_notify_p2p_group_formation_failure(wpa_s, "");
 	}
 }
 
@@ -4369,10 +4771,12 @@
 					 wpa_s->p2p_persistent_group, 0, 0, 0,
 					 wpa_s->p2p_go_intent,
 					 wpa_s->p2p_connect_freq,
+					 wpa_s->p2p_go_vht_center_freq2,
 					 wpa_s->p2p_persistent_id,
 					 wpa_s->p2p_pd_before_go_neg,
 					 wpa_s->p2p_go_ht40,
-					 wpa_s->p2p_go_vht);
+					 wpa_s->p2p_go_vht,
+					 wpa_s->p2p_go_max_oper_chwidth);
 			return;
 		}
 
@@ -4453,6 +4857,8 @@
 			wpa_msg_global(wpa_s->parent, MSG_INFO,
 				       P2P_EVENT_GROUP_FORMATION_FAILURE
 				       "reason=FREQ_CONFLICT");
+			wpas_notify_p2p_group_formation_failure(
+				wpa_s, "FREQ_CONFLICT");
 			return;
 		}
 
@@ -4472,6 +4878,9 @@
 		case WPS_PBC:
 			method = WPS_CONFIG_PUSHBUTTON;
 			break;
+		case WPS_P2PS:
+			method = WPS_CONFIG_P2PS;
+			break;
 		default:
 			method = 0;
 			break;
@@ -4714,11 +5123,16 @@
 
 
 static int wpas_p2p_setup_freqs(struct wpa_supplicant *wpa_s, int freq,
-				int *force_freq, int *pref_freq, int go)
+				int *force_freq, int *pref_freq, int go,
+				unsigned int *pref_freq_list,
+				unsigned int *num_pref_freq)
 {
 	struct wpa_used_freq_data *freqs;
 	int res, best_freq, num_unused;
-	unsigned int freq_in_use = 0, num, i;
+	unsigned int freq_in_use = 0, num, i, max_pref_freq;
+
+	max_pref_freq = *num_pref_freq;
+	*num_pref_freq = 0;
 
 	freqs = os_calloc(wpa_s->num_multichan_concurrent,
 			  sizeof(struct wpa_used_freq_data));
@@ -4783,6 +5197,47 @@
 
 	best_freq = wpas_p2p_pick_best_used_freq(wpa_s, freqs, num);
 
+	if (!wpa_s->conf->num_p2p_pref_chan && *pref_freq == 0) {
+		enum wpa_driver_if_type iface_type;
+
+		if (go)
+			iface_type = WPA_IF_P2P_GO;
+		else
+			iface_type = WPA_IF_P2P_CLIENT;
+
+		wpa_printf(MSG_DEBUG, "P2P: best_freq=%d, go=%d",
+			   best_freq, go);
+
+		res = wpa_drv_get_pref_freq_list(wpa_s, iface_type,
+						 &max_pref_freq,
+						 pref_freq_list);
+		if (!res && max_pref_freq > 0) {
+			*num_pref_freq = max_pref_freq;
+			i = 0;
+			while (wpas_p2p_disallowed_freq(wpa_s->global,
+							pref_freq_list[i]) &&
+			       i < *num_pref_freq) {
+				wpa_printf(MSG_DEBUG,
+					   "P2P: preferred_freq_list[%d]=%d is disallowed",
+					   i, pref_freq_list[i]);
+				i++;
+			}
+			if (i != *num_pref_freq) {
+				best_freq = pref_freq_list[i];
+				wpa_printf(MSG_DEBUG,
+					   "P2P: Using preferred_freq_list[%d]=%d",
+					   i, best_freq);
+			} else {
+				wpa_printf(MSG_DEBUG,
+					   "P2P: All driver preferred frequencies are disallowed for P2P use");
+				*num_pref_freq = 0;
+			}
+		} else {
+			wpa_printf(MSG_DEBUG,
+				   "P2P: No preferred frequency list available");
+		}
+	}
+
 	/* We have a candidate frequency to use */
 	if (best_freq > 0) {
 		if (*pref_freq == 0 && num_unused > 0) {
@@ -4826,12 +5281,15 @@
  *	initiating Group Owner negotiation
  * @go_intent: GO Intent or -1 to use default
  * @freq: Frequency for the group or 0 for auto-selection
+ * @freq2: Center frequency of segment 1 for the GO operating in VHT 80P80 mode
  * @persistent_id: Persistent group credentials to use for forcing GO
  *	parameters or -1 to generate new values (SSID/passphrase)
  * @pd: Whether to send Provision Discovery prior to GO Negotiation as an
  *	interoperability workaround when initiating group formation
  * @ht40: Start GO with 40 MHz channel width
  * @vht:  Start GO with VHT support
+ * @vht_chwidth: Channel width supported by GO operating with VHT support
+ *	(VHT_CHANWIDTH_*).
  * Returns: 0 or new PIN (if pin was %NULL) on success, -1 on unspecified
  *	failure, -2 on failure due to channel not currently available,
  *	-3 if forced channel is not supported
@@ -4839,14 +5297,16 @@
 int wpas_p2p_connect(struct wpa_supplicant *wpa_s, const u8 *peer_addr,
 		     const char *pin, enum p2p_wps_method wps_method,
 		     int persistent_group, int auto_join, int join, int auth,
-		     int go_intent, int freq, int persistent_id, int pd,
-		     int ht40, int vht)
+		     int go_intent, int freq, unsigned int vht_center_freq2,
+		     int persistent_id, int pd, int ht40, int vht,
+		     unsigned int vht_chwidth)
 {
 	int force_freq = 0, pref_freq = 0;
 	int ret = 0, res;
 	enum wpa_driver_if_type iftype;
 	const u8 *if_addr;
 	struct wpa_ssid *ssid = NULL;
+	unsigned int pref_freq_list[P2P_MAX_PREF_CHANNELS], size;
 
 	if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
 		return -1;
@@ -4863,6 +5323,8 @@
 
 	wpa_s->global->p2p_fail_on_wps_complete = 0;
 	wpa_s->global->pending_p2ps_group = 0;
+	wpa_s->global->pending_p2ps_group_freq = 0;
+	wpa_s->p2ps_method_config_any = 0;
 
 	if (go_intent < 0)
 		go_intent = wpa_s->conf->p2p_go_intent;
@@ -4879,6 +5341,8 @@
 	wpa_s->p2p_pd_before_go_neg = !!pd;
 	wpa_s->p2p_go_ht40 = !!ht40;
 	wpa_s->p2p_go_vht = !!vht;
+	wpa_s->p2p_go_vht_center_freq2 = vht_center_freq2;
+	wpa_s->p2p_go_max_oper_chwidth = vht_chwidth;
 
 	if (pin)
 		os_strlcpy(wpa_s->p2p_pin, pin, sizeof(wpa_s->p2p_pin));
@@ -4923,13 +5387,16 @@
 		return ret;
 	}
 
+	size = P2P_MAX_PREF_CHANNELS;
 	res = wpas_p2p_setup_freqs(wpa_s, freq, &force_freq, &pref_freq,
-				   go_intent == 15);
+				   go_intent == 15, pref_freq_list, &size);
 	if (res)
 		return res;
 	wpas_p2p_set_own_freq_preference(wpa_s,
 					 force_freq ? force_freq : pref_freq);
 
+	p2p_set_own_pref_freq_list(wpa_s->global->p2p, pref_freq_list, size);
+
 	wpa_s->create_p2p_iface = wpas_p2p_create_iface(wpa_s);
 
 	if (wpa_s->create_p2p_iface) {
@@ -4944,8 +5411,10 @@
 		}
 
 		if_addr = wpa_s->pending_interface_addr;
-	} else
+	} else {
 		if_addr = wpa_s->own_addr;
+		os_memset(wpa_s->go_dev_addr, 0, ETH_ALEN);
+	}
 
 	if (auth) {
 		if (wpas_p2p_auth_go_neg(wpa_s, peer_addr, wps_method,
@@ -5090,6 +5559,38 @@
 {
 	unsigned int r;
 
+	if (!wpa_s->conf->num_p2p_pref_chan && !freq) {
+		unsigned int i, size = P2P_MAX_PREF_CHANNELS;
+		unsigned int pref_freq_list[P2P_MAX_PREF_CHANNELS];
+		int res;
+
+		res = wpa_drv_get_pref_freq_list(wpa_s, WPA_IF_P2P_GO,
+						 &size, pref_freq_list);
+		if (!res && size > 0) {
+			i = 0;
+			while (wpas_p2p_disallowed_freq(wpa_s->global,
+							pref_freq_list[i]) &&
+			       i < size) {
+				wpa_printf(MSG_DEBUG,
+					   "P2P: preferred_freq_list[%d]=%d is disallowed",
+					   i, pref_freq_list[i]);
+				i++;
+			}
+			if (i != size) {
+				freq = pref_freq_list[i];
+				wpa_printf(MSG_DEBUG,
+					   "P2P: Using preferred_freq_list[%d]=%d",
+					   i, freq);
+			} else {
+				wpa_printf(MSG_DEBUG,
+					   "P2P: All driver preferred frequencies are disallowed for P2P use");
+			}
+		} else {
+			wpa_printf(MSG_DEBUG,
+				   "P2P: No preferred frequency list available");
+		}
+	}
+
 	if (freq == 2) {
 		wpa_printf(MSG_DEBUG, "P2P: Request to start GO on 2.4 GHz "
 			   "band");
@@ -5153,30 +5654,45 @@
 }
 
 
-static int wpas_p2p_select_freq_no_pref(struct wpa_supplicant *wpa_s,
-					struct p2p_go_neg_results *params,
-					const struct p2p_channels *channels)
+static int wpas_p2p_supported_freq_go(struct wpa_supplicant *wpa_s,
+				      const struct p2p_channels *channels,
+				      int freq)
+{
+	if (!wpas_p2p_disallowed_freq(wpa_s->global, freq) &&
+	    p2p_supported_freq_go(wpa_s->global->p2p, freq) &&
+	    freq_included(wpa_s, channels, freq))
+		return 1;
+	return 0;
+}
+
+
+static void wpas_p2p_select_go_freq_no_pref(struct wpa_supplicant *wpa_s,
+					    struct p2p_go_neg_results *params,
+					    const struct p2p_channels *channels)
 {
 	unsigned int i, r;
 
 	/* first try some random selection of the social channels */
 	if (os_get_random((u8 *) &r, sizeof(r)) < 0)
-		return -1;
+		return;
 
 	for (i = 0; i < 3; i++) {
 		params->freq = 2412 + ((r + i) % 3) * 25;
-		if (!wpas_p2p_disallowed_freq(wpa_s->global, params->freq) &&
-		    freq_included(channels, params->freq) &&
-		    p2p_supported_freq(wpa_s->global->p2p, params->freq))
+		if (wpas_p2p_supported_freq_go(wpa_s, channels, params->freq))
 			goto out;
 	}
 
-	/* try all channels in reg. class 81 */
+	/* try all other channels in operating class 81 */
 	for (i = 0; i < 11; i++) {
 		params->freq = 2412 + i * 5;
-		if (!wpas_p2p_disallowed_freq(wpa_s->global, params->freq) &&
-		    freq_included(channels, params->freq) &&
-		    p2p_supported_freq(wpa_s->global->p2p, params->freq))
+
+		/* skip social channels; covered in the previous loop */
+		if (params->freq == 2412 ||
+		    params->freq == 2437 ||
+		    params->freq == 2462)
+			continue;
+
+		if (wpas_p2p_supported_freq_go(wpa_s, channels, params->freq))
 			goto out;
 	}
 
@@ -5184,7 +5700,7 @@
 	for (i = 0; i < 4; i++) {
 		params->freq = 5180 + i * 20;
 		if (!wpas_p2p_disallowed_freq(wpa_s->global, params->freq) &&
-		    freq_included(channels, params->freq) &&
+		    freq_included(wpa_s, channels, params->freq) &&
 		    p2p_supported_freq(wpa_s->global->p2p, params->freq))
 			goto out;
 	}
@@ -5193,7 +5709,7 @@
 	for (i = 0; i < 4; i++) {
 		params->freq = 5745 + i * 20;
 		if (!wpas_p2p_disallowed_freq(wpa_s->global, params->freq) &&
-		    freq_included(channels, params->freq) &&
+		    freq_included(wpa_s, channels, params->freq) &&
 		    p2p_supported_freq(wpa_s->global->p2p, params->freq))
 			goto out;
 	}
@@ -5201,7 +5717,7 @@
 	/* try social channel class 180 channel 2 */
 	params->freq = 58320 + 1 * 2160;
 	if (!wpas_p2p_disallowed_freq(wpa_s->global, params->freq) &&
-	    freq_included(channels, params->freq) &&
+	    freq_included(wpa_s, channels, params->freq) &&
 	    p2p_supported_freq(wpa_s->global->p2p, params->freq))
 		goto out;
 
@@ -5209,150 +5725,229 @@
 	for (i = 0; i < 4; i++) {
 		params->freq = 58320 + i * 2160;
 		if (!wpas_p2p_disallowed_freq(wpa_s->global, params->freq) &&
-		    freq_included(channels, params->freq) &&
+		    freq_included(wpa_s, channels, params->freq) &&
 		    p2p_supported_freq(wpa_s->global->p2p, params->freq))
 			goto out;
 	}
 
+	params->freq = 0;
 	wpa_printf(MSG_DEBUG, "P2P: No 2.4, 5, or 60 GHz channel allowed");
-	return -1;
+	return;
 out:
 	wpa_printf(MSG_DEBUG, "P2P: Set GO freq %d MHz (no preference known)",
 		   params->freq);
-	return 0;
 }
 
 
 static int wpas_p2p_init_go_params(struct wpa_supplicant *wpa_s,
 				   struct p2p_go_neg_results *params,
-				   int freq, int ht40, int vht,
+				   int freq, int vht_center_freq2, int ht40,
+				   int vht, int max_oper_chwidth,
 				   const struct p2p_channels *channels)
 {
 	struct wpa_used_freq_data *freqs;
-	unsigned int pref_freq, cand_freq;
+	unsigned int cand;
 	unsigned int num, i;
+	int ignore_no_freqs = 0;
 
 	os_memset(params, 0, sizeof(*params));
 	params->role_go = 1;
 	params->ht40 = ht40;
 	params->vht = vht;
-	if (freq) {
-		if (!freq_included(channels, freq)) {
-			wpa_printf(MSG_DEBUG, "P2P: Forced GO freq %d MHz not "
-				   "accepted", freq);
-			return -1;
-		}
-		wpa_printf(MSG_DEBUG, "P2P: Set GO freq based on forced "
-			   "frequency %d MHz", freq);
-		params->freq = freq;
-	} else if (wpa_s->conf->p2p_oper_reg_class == 81 &&
-		   wpa_s->conf->p2p_oper_channel >= 1 &&
-		   wpa_s->conf->p2p_oper_channel <= 11 &&
-		   freq_included(channels,
-				 2407 + 5 * wpa_s->conf->p2p_oper_channel)) {
-		params->freq = 2407 + 5 * wpa_s->conf->p2p_oper_channel;
-		wpa_printf(MSG_DEBUG, "P2P: Set GO freq based on configured "
-			   "frequency %d MHz", params->freq);
-	} else if ((wpa_s->conf->p2p_oper_reg_class == 115 ||
-		    wpa_s->conf->p2p_oper_reg_class == 116 ||
-		    wpa_s->conf->p2p_oper_reg_class == 117 ||
-		    wpa_s->conf->p2p_oper_reg_class == 124 ||
-		    wpa_s->conf->p2p_oper_reg_class == 125 ||
-		    wpa_s->conf->p2p_oper_reg_class == 126 ||
-		    wpa_s->conf->p2p_oper_reg_class == 127) &&
-		   freq_included(channels,
-				 5000 + 5 * wpa_s->conf->p2p_oper_channel)) {
-		params->freq = 5000 + 5 * wpa_s->conf->p2p_oper_channel;
-		wpa_printf(MSG_DEBUG, "P2P: Set GO freq based on configured "
-			   "frequency %d MHz", params->freq);
-	} else if (wpa_s->conf->p2p_oper_channel == 0 &&
-		   wpa_s->best_overall_freq > 0 &&
-		   p2p_supported_freq_go(wpa_s->global->p2p,
-					 wpa_s->best_overall_freq) &&
-		   freq_included(channels, wpa_s->best_overall_freq)) {
-		params->freq = wpa_s->best_overall_freq;
-		wpa_printf(MSG_DEBUG, "P2P: Set GO freq based on best overall "
-			   "channel %d MHz", params->freq);
-	} else if (wpa_s->conf->p2p_oper_channel == 0 &&
-		   wpa_s->best_24_freq > 0 &&
-		   p2p_supported_freq_go(wpa_s->global->p2p,
-					 wpa_s->best_24_freq) &&
-		   freq_included(channels, wpa_s->best_24_freq)) {
-		params->freq = wpa_s->best_24_freq;
-		wpa_printf(MSG_DEBUG, "P2P: Set GO freq based on best 2.4 GHz "
-			   "channel %d MHz", params->freq);
-	} else if (wpa_s->conf->p2p_oper_channel == 0 &&
-		   wpa_s->best_5_freq > 0 &&
-		   p2p_supported_freq_go(wpa_s->global->p2p,
-					 wpa_s->best_5_freq) &&
-		   freq_included(channels, wpa_s->best_5_freq)) {
-		params->freq = wpa_s->best_5_freq;
-		wpa_printf(MSG_DEBUG, "P2P: Set GO freq based on best 5 GHz "
-			   "channel %d MHz", params->freq);
-	} else if ((pref_freq = p2p_get_pref_freq(wpa_s->global->p2p,
-						  channels))) {
-		params->freq = pref_freq;
-		wpa_printf(MSG_DEBUG, "P2P: Set GO freq %d MHz from preferred "
-			   "channels", params->freq);
-	} else {
-		/* no preference, select some channel */
-		if (wpas_p2p_select_freq_no_pref(wpa_s, params, channels) < 0)
-			return -1;
-	}
+	params->max_oper_chwidth = max_oper_chwidth;
+	params->vht_center_freq2 = vht_center_freq2;
 
 	freqs = os_calloc(wpa_s->num_multichan_concurrent,
 			  sizeof(struct wpa_used_freq_data));
 	if (!freqs)
 		return -1;
 
-	num = wpas_p2p_valid_oper_freqs(wpa_s, freqs,
-					wpa_s->num_multichan_concurrent);
+	num = get_shared_radio_freqs_data(wpa_s, freqs,
+					  wpa_s->num_multichan_concurrent);
 
-	cand_freq = wpas_p2p_pick_best_used_freq(wpa_s, freqs, num);
+	if (wpa_s->current_ssid &&
+	    wpa_s->current_ssid->mode == WPAS_MODE_P2P_GO &&
+	    wpa_s->wpa_state == WPA_COMPLETED) {
+		wpa_printf(MSG_DEBUG, "P2P: %s called for an active GO",
+			   __func__);
 
-	/* First try the best used frequency if possible */
-	if (!freq && cand_freq > 0 && freq_included(channels, cand_freq)) {
-		params->freq = cand_freq;
-	} else if (!freq) {
-		/* Try any of the used frequencies */
+		/*
+		 * If the frequency selection is done for an active P2P GO that
+		 * is not sharing a frequency, allow to select a new frequency
+		 * even if there are no unused frequencies as we are about to
+		 * move the P2P GO so its frequency can be re-used.
+		 */
 		for (i = 0; i < num; i++) {
-			if (freq_included(channels, freqs[i].freq)) {
-				wpa_printf(MSG_DEBUG, "P2P: Force GO on a channel we are already using (%u MHz)",
-					   freqs[i].freq);
-				params->freq = freqs[i].freq;
+			if (freqs[i].freq == wpa_s->current_ssid->frequency &&
+			    freqs[i].flags == 0) {
+				ignore_no_freqs = 1;
 				break;
 			}
 		}
-
-		if (i == num) {
-			if (wpas_p2p_num_unused_channels(wpa_s) <= 0) {
-				wpa_printf(MSG_DEBUG, "P2P: Cannot force GO on any of the channels we are already using");
-				os_free(freqs);
-				return -1;
-			} else {
-				wpa_printf(MSG_DEBUG, "P2P: Cannot force GO on any of the channels we are already using. Use one of the free channels");
-			}
-		}
-	} else {
-		for (i = 0; i < num; i++) {
-			if (freqs[i].freq == freq)
-				break;
-		}
-
-		if (i == num) {
-			if (wpas_p2p_num_unused_channels(wpa_s) <= 0) {
-				if (freq)
-					wpa_printf(MSG_DEBUG, "P2P: Cannot force GO on freq (%u MHz) as all the channels are in use", freq);
-				os_free(freqs);
-				return -1;
-			} else {
-				wpa_printf(MSG_DEBUG, "P2P: Use one of the free channels");
-			}
-		}
 	}
 
+	/* try using the forced freq */
+	if (freq) {
+		if (!wpas_p2p_supported_freq_go(wpa_s, channels, freq)) {
+			wpa_printf(MSG_DEBUG,
+				   "P2P: Forced GO freq %d MHz not accepted",
+				   freq);
+			goto fail;
+		}
+
+		for (i = 0; i < num; i++) {
+			if (freqs[i].freq == freq) {
+				wpa_printf(MSG_DEBUG,
+					   "P2P: forced freq (%d MHz) is also shared",
+					   freq);
+				params->freq = freq;
+				goto success;
+			}
+		}
+
+		if (!ignore_no_freqs &&
+		    wpas_p2p_num_unused_channels(wpa_s) <= 0) {
+			wpa_printf(MSG_DEBUG,
+				   "P2P: Cannot force GO on freq (%d MHz) as all the channels are in use",
+				   freq);
+			goto fail;
+		}
+
+		wpa_printf(MSG_DEBUG,
+			   "P2P: force GO freq (%d MHz) on a free channel",
+			   freq);
+		params->freq = freq;
+		goto success;
+	}
+
+	/* consider using one of the shared frequencies */
+	if (num) {
+		cand = wpas_p2p_pick_best_used_freq(wpa_s, freqs, num);
+		if (wpas_p2p_supported_freq_go(wpa_s, channels, cand)) {
+			wpa_printf(MSG_DEBUG,
+				   "P2P: Use shared freq (%d MHz) for GO",
+				   freq);
+			params->freq = cand;
+			goto success;
+		}
+
+		/* try using one of the shared freqs */
+		for (i = 0; i < num; i++) {
+			if (wpas_p2p_supported_freq_go(wpa_s, channels,
+						       freqs[i].freq)) {
+				wpa_printf(MSG_DEBUG,
+					   "P2P: Use shared freq (%d MHz) for GO",
+					   freq);
+				params->freq = freqs[i].freq;
+				goto success;
+			}
+		}
+	}
+
+	if (!ignore_no_freqs &&
+	    wpas_p2p_num_unused_channels(wpa_s) <= 0) {
+		wpa_printf(MSG_DEBUG,
+			   "P2P: Cannot force GO on any of the channels we are already using");
+		goto fail;
+	}
+
+	/* try using the setting from the configuration file */
+	if (wpa_s->conf->p2p_oper_reg_class == 81 &&
+	    wpa_s->conf->p2p_oper_channel >= 1 &&
+	    wpa_s->conf->p2p_oper_channel <= 11 &&
+	    wpas_p2p_supported_freq_go(
+		    wpa_s, channels,
+		    2407 + 5 * wpa_s->conf->p2p_oper_channel)) {
+		params->freq = 2407 + 5 * wpa_s->conf->p2p_oper_channel;
+		wpa_printf(MSG_DEBUG, "P2P: Set GO freq based on configured "
+			   "frequency %d MHz", params->freq);
+		goto success;
+	}
+
+	if ((wpa_s->conf->p2p_oper_reg_class == 115 ||
+	     wpa_s->conf->p2p_oper_reg_class == 116 ||
+	     wpa_s->conf->p2p_oper_reg_class == 117 ||
+	     wpa_s->conf->p2p_oper_reg_class == 124 ||
+	     wpa_s->conf->p2p_oper_reg_class == 125 ||
+	     wpa_s->conf->p2p_oper_reg_class == 126 ||
+	     wpa_s->conf->p2p_oper_reg_class == 127) &&
+	    wpas_p2p_supported_freq_go(wpa_s, channels,
+				       5000 +
+				       5 * wpa_s->conf->p2p_oper_channel)) {
+		params->freq = 5000 + 5 * wpa_s->conf->p2p_oper_channel;
+		wpa_printf(MSG_DEBUG, "P2P: Set GO freq based on configured "
+			   "frequency %d MHz", params->freq);
+		goto success;
+	}
+
+	/* Try using best channels */
+	if (wpa_s->conf->p2p_oper_channel == 0 &&
+	    wpa_s->best_overall_freq > 0 &&
+	    wpas_p2p_supported_freq_go(wpa_s, channels,
+				       wpa_s->best_overall_freq)) {
+		params->freq = wpa_s->best_overall_freq;
+		wpa_printf(MSG_DEBUG, "P2P: Set GO freq based on best overall "
+			   "channel %d MHz", params->freq);
+		goto success;
+	}
+
+	if (wpa_s->conf->p2p_oper_channel == 0 &&
+	    wpa_s->best_24_freq > 0 &&
+	    wpas_p2p_supported_freq_go(wpa_s, channels,
+				       wpa_s->best_24_freq)) {
+		params->freq = wpa_s->best_24_freq;
+		wpa_printf(MSG_DEBUG, "P2P: Set GO freq based on best 2.4 GHz "
+			   "channel %d MHz", params->freq);
+		goto success;
+	}
+
+	if (wpa_s->conf->p2p_oper_channel == 0 &&
+	    wpa_s->best_5_freq > 0 &&
+	    wpas_p2p_supported_freq_go(wpa_s, channels,
+				       wpa_s->best_5_freq)) {
+		params->freq = wpa_s->best_5_freq;
+		wpa_printf(MSG_DEBUG, "P2P: Set GO freq based on best 5 GHz "
+			   "channel %d MHz", params->freq);
+		goto success;
+	}
+
+	/* try using preferred channels */
+	cand = p2p_get_pref_freq(wpa_s->global->p2p, channels);
+	if (cand && wpas_p2p_supported_freq_go(wpa_s, channels, cand)) {
+		params->freq = cand;
+		wpa_printf(MSG_DEBUG, "P2P: Set GO freq %d MHz from preferred "
+			   "channels", params->freq);
+		goto success;
+	}
+
+	/* Try using one of the group common freqs */
+	if (wpa_s->p2p_group_common_freqs) {
+		for (i = 0; i < wpa_s->p2p_group_common_freqs_num; i++) {
+			cand = wpa_s->p2p_group_common_freqs[i];
+			if (wpas_p2p_supported_freq_go(wpa_s, channels, cand)) {
+				params->freq = cand;
+				wpa_printf(MSG_DEBUG,
+					   "P2P: Use freq %d MHz common with the peer",
+					   params->freq);
+				goto success;
+			}
+		}
+	}
+
+	/* no preference, select some channel */
+	wpas_p2p_select_go_freq_no_pref(wpa_s, params, channels);
+
+	if (params->freq == 0) {
+		wpa_printf(MSG_DEBUG, "P2P: did not find a freq for GO use");
+		goto fail;
+	}
+
+success:
 	os_free(freqs);
 	return 0;
+fail:
+	os_free(freqs);
+	return -1;
 }
 
 
@@ -5395,15 +5990,18 @@
  * @wpa_s: Pointer to wpa_supplicant data from wpa_supplicant_add_iface()
  * @persistent_group: Whether to create a persistent group
  * @freq: Frequency for the group or 0 to indicate no hardcoding
+ * @vht_center_freq2: segment_1 center frequency for GO operating in VHT 80P80
  * @ht40: Start GO with 40 MHz channel width
  * @vht:  Start GO with VHT support
+ * @vht_chwidth: channel bandwidth for GO operating with VHT support
  * Returns: 0 on success, -1 on failure
  *
  * This function creates a new P2P group with the local end as the Group Owner,
  * i.e., without using Group Owner Negotiation.
  */
 int wpas_p2p_group_add(struct wpa_supplicant *wpa_s, int persistent_group,
-		       int freq, int ht40, int vht)
+		       int freq, int vht_center_freq2, int ht40, int vht,
+		       int max_oper_chwidth)
 {
 	struct p2p_go_neg_results params;
 
@@ -5421,7 +6019,8 @@
 	if (freq < 0)
 		return -1;
 
-	if (wpas_p2p_init_go_params(wpa_s, &params, freq, ht40, vht, NULL))
+	if (wpas_p2p_init_go_params(wpa_s, &params, freq, vht_center_freq2,
+				    ht40, vht, max_oper_chwidth, NULL))
 		return -1;
 	if (params.freq &&
 	    !p2p_supported_freq_go(wpa_s->global->p2p, params.freq)) {
@@ -5455,13 +6054,15 @@
 
 static int wpas_start_p2p_client(struct wpa_supplicant *wpa_s,
 				 struct wpa_ssid *params, int addr_allocated,
-				 int freq)
+				 int freq, int force_scan)
 {
 	struct wpa_ssid *ssid;
 
 	wpa_s = wpas_p2p_get_group_iface(wpa_s, addr_allocated, 0);
 	if (wpa_s == NULL)
 		return -1;
+	if (force_scan)
+		os_get_reltime(&wpa_s->scan_min_time);
 	wpa_s->p2p_last_4way_hs_fail = NULL;
 
 	wpa_supplicant_ap_deinit(wpa_s);
@@ -5469,6 +6070,7 @@
 	ssid = wpa_config_add_network(wpa_s->conf);
 	if (ssid == NULL)
 		return -1;
+	os_memset(wpa_s->go_dev_addr, 0, ETH_ALEN);
 	wpa_config_set_network_defaults(ssid);
 	ssid->temporary = 1;
 	ssid->proto = WPA_PROTO_RSN;
@@ -5494,6 +6096,8 @@
 	wpa_s->show_group_started = 1;
 	wpa_s->p2p_in_invitation = 1;
 	wpa_s->p2p_invite_go_freq = freq;
+	wpa_s->p2p_go_group_formation_completed = 0;
+	wpa_s->global->p2p_group_formation = wpa_s;
 
 	eloop_cancel_timeout(wpas_p2p_group_formation_timeout, wpa_s->parent,
 			     NULL);
@@ -5508,9 +6112,11 @@
 
 int wpas_p2p_group_add_persistent(struct wpa_supplicant *wpa_s,
 				  struct wpa_ssid *ssid, int addr_allocated,
-				  int force_freq, int neg_freq, int ht40,
-				  int vht, const struct p2p_channels *channels,
-				  int connection_timeout)
+				  int force_freq, int neg_freq,
+				  int vht_center_freq2, int ht40,
+				  int vht, int max_oper_chwidth,
+				  const struct p2p_channels *channels,
+				  int connection_timeout, int force_scan)
 {
 	struct p2p_go_neg_results params;
 	int go = 0, freq;
@@ -5558,12 +6164,12 @@
 		} else {
 			freq = wpas_p2p_select_go_freq(wpa_s, neg_freq);
 			if (freq < 0 ||
-			    (freq > 0 && !freq_included(channels, freq)))
+			    (freq > 0 && !freq_included(wpa_s, channels, freq)))
 				freq = 0;
 		}
 	} else if (ssid->mode == WPAS_MODE_INFRA) {
 		freq = neg_freq;
-		if (freq <= 0 || !freq_included(channels, freq)) {
+		if (freq <= 0 || !freq_included(wpa_s, channels, freq)) {
 			struct os_reltime now;
 			struct wpa_bss *bss =
 				wpa_bss_get_p2p_dev_addr(wpa_s, ssid->bssid);
@@ -5571,18 +6177,20 @@
 			os_get_reltime(&now);
 			if (bss &&
 			    !os_reltime_expired(&now, &bss->last_update, 5) &&
-			    freq_included(channels, bss->freq))
+			    freq_included(wpa_s, channels, bss->freq))
 				freq = bss->freq;
 			else
 				freq = 0;
 		}
 
-		return wpas_start_p2p_client(wpa_s, ssid, addr_allocated, freq);
+		return wpas_start_p2p_client(wpa_s, ssid, addr_allocated, freq,
+					     force_scan);
 	} else {
 		return -1;
 	}
 
-	if (wpas_p2p_init_go_params(wpa_s, &params, freq, ht40, vht, channels))
+	if (wpas_p2p_init_go_params(wpa_s, &params, freq, vht_center_freq2,
+				    ht40, vht, max_oper_chwidth, channels))
 		return -1;
 
 	params.role_go = 1;
@@ -5734,6 +6342,8 @@
 		eloop_register_timeout(P2P_MAX_INITIAL_CONN_WAIT, 0,
 				       wpas_p2p_group_formation_timeout,
 				       wpa_s->parent, NULL);
+		/* Complete group formation on successful data connection. */
+		wpa_s->p2p_go_group_formation_completed = 0;
 	} else if (ssid) {
 		/*
 		 * Use a separate timeout for initial data connection to
@@ -5809,11 +6419,14 @@
 	u16 config_methods;
 
 	wpa_s->global->pending_p2ps_group = 0;
+	wpa_s->global->pending_p2ps_group_freq = 0;
 	wpa_s->p2p_fallback_to_go_neg = 0;
 	wpa_s->pending_pd_use = NORMAL_PD;
 	if (p2ps_prov && use == WPAS_P2P_PD_FOR_ASP) {
 		p2ps_prov->conncap = p2ps_group_capability(
-			wpa_s, P2PS_SETUP_NONE, p2ps_prov->role);
+			wpa_s, P2PS_SETUP_NONE, p2ps_prov->role,
+			&p2ps_prov->force_freq, &p2ps_prov->pref_freq);
+
 		wpa_printf(MSG_DEBUG,
 			   "P2P: %s conncap: %d - ASP parsed: %x %x %d %s",
 			   __func__, p2ps_prov->conncap,
@@ -5874,7 +6487,8 @@
 	if (!offchannel_pending_action_tx(wpa_s))
 		return;
 
-	wpas_p2p_action_tx_clear(wpa_s);
+	if (wpa_s->p2p_send_action_work)
+		wpas_p2p_free_send_action_work(wpa_s);
 
 	wpa_printf(MSG_DEBUG, "P2P: Drop pending Action TX due to new "
 		   "operation request");
@@ -6105,13 +6719,15 @@
 /* Invite to reinvoke a persistent group */
 int wpas_p2p_invite(struct wpa_supplicant *wpa_s, const u8 *peer_addr,
 		    struct wpa_ssid *ssid, const u8 *go_dev_addr, int freq,
-		    int ht40, int vht, int pref_freq)
+		    int vht_center_freq2, int ht40, int vht, int max_chwidth,
+		    int pref_freq)
 {
 	enum p2p_invite_role role;
 	u8 *bssid = NULL;
 	int force_freq = 0;
 	int res;
 	int no_pref_freq_given = pref_freq == 0;
+	unsigned int pref_freq_list[P2P_MAX_PREF_CHANNELS], size;
 
 	wpa_s->global->p2p_invite_group = NULL;
 	if (peer_addr)
@@ -6121,6 +6737,9 @@
 
 	wpa_s->p2p_persistent_go_freq = freq;
 	wpa_s->p2p_go_ht40 = !!ht40;
+	wpa_s->p2p_go_vht = !!vht;
+	wpa_s->p2p_go_max_oper_chwidth = max_chwidth;
+	wpa_s->p2p_go_vht_center_freq2 = vht_center_freq2;
 	if (ssid->mode == WPAS_MODE_P2P_GO) {
 		role = P2P_INVITE_ROLE_GO;
 		if (peer_addr == NULL) {
@@ -6145,10 +6764,13 @@
 	}
 	wpa_s->pending_invite_ssid_id = ssid->id;
 
+	size = P2P_MAX_PREF_CHANNELS;
 	res = wpas_p2p_setup_freqs(wpa_s, freq, &force_freq, &pref_freq,
-				   role == P2P_INVITE_ROLE_GO);
+				   role == P2P_INVITE_ROLE_GO,
+				   pref_freq_list, &size);
 	if (res)
 		return res;
+	p2p_set_own_pref_freq_list(wpa_s->global->p2p, pref_freq_list, size);
 
 	if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
 		return -1;
@@ -6185,10 +6807,13 @@
 	int persistent;
 	int freq = 0, force_freq = 0, pref_freq = 0;
 	int res;
+	unsigned int pref_freq_list[P2P_MAX_PREF_CHANNELS], size;
 
 	wpa_s->p2p_persistent_go_freq = 0;
 	wpa_s->p2p_go_ht40 = 0;
 	wpa_s->p2p_go_vht = 0;
+	wpa_s->p2p_go_vht_center_freq2 = 0;
+	wpa_s->p2p_go_max_oper_chwidth = 0;
 
 	for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) {
 		if (os_strcmp(wpa_s->ifname, ifname) == 0)
@@ -6236,8 +6861,10 @@
 	if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
 		return -1;
 
+	size = P2P_MAX_PREF_CHANNELS;
 	res = wpas_p2p_setup_freqs(wpa_s, freq, &force_freq, &pref_freq,
-				   role == P2P_INVITE_ROLE_ACTIVE_GO);
+				   role == P2P_INVITE_ROLE_ACTIVE_GO,
+				   pref_freq_list, &size);
 	if (res)
 		return res;
 	wpas_p2p_set_own_freq_preference(wpa_s, force_freq);
@@ -6267,6 +6894,15 @@
 		return;
 
 	wpa_s->show_group_started = 0;
+	if (!wpa_s->p2p_go_group_formation_completed &&
+	    wpa_s->global->p2p_group_formation == wpa_s) {
+		wpa_dbg(wpa_s, MSG_DEBUG,
+			"P2P: Marking group formation completed on client on data connection");
+		wpa_s->p2p_go_group_formation_completed = 1;
+		wpa_s->global->p2p_group_formation = NULL;
+		wpa_s->p2p_in_provisioning = 0;
+		wpa_s->p2p_in_invitation = 0;
+	}
 
 	os_memset(go_dev_addr, 0, ETH_ALEN);
 	if (ssid->bssid_set)
@@ -6782,14 +7418,22 @@
 }
 
 
-void wpas_p2p_update_channel_list(struct wpa_supplicant *wpa_s)
+void wpas_p2p_update_channel_list(struct wpa_supplicant *wpa_s,
+				  enum wpas_p2p_channel_update_trig trig)
 {
 	struct p2p_channels chan, cli_chan;
-	struct wpa_supplicant *ifs;
+	struct wpa_used_freq_data *freqs = NULL;
+	unsigned int num = wpa_s->num_multichan_concurrent;
 
 	if (wpa_s->global == NULL || wpa_s->global->p2p == NULL)
 		return;
 
+	freqs = os_calloc(num, sizeof(struct wpa_used_freq_data));
+	if (!freqs)
+		return;
+
+	num = get_shared_radio_freqs_data(wpa_s, freqs, num);
+
 	os_memset(&chan, 0, sizeof(chan));
 	os_memset(&cli_chan, 0, sizeof(cli_chan));
 	if (wpas_p2p_setup_channels(wpa_s, &chan, &cli_chan)) {
@@ -6800,27 +7444,17 @@
 
 	p2p_update_channel_list(wpa_s->global->p2p, &chan, &cli_chan);
 
-	for (ifs = wpa_s->global->ifaces; ifs; ifs = ifs->next) {
-		int freq;
-		if (!ifs->current_ssid ||
-		    !ifs->current_ssid->p2p_group ||
-		    (ifs->current_ssid->mode != WPAS_MODE_P2P_GO &&
-		     ifs->current_ssid->mode != WPAS_MODE_P2P_GROUP_FORMATION))
-				continue;
-		freq = ifs->current_ssid->frequency;
-		if (freq_included(&chan, freq)) {
-			wpa_dbg(ifs, MSG_DEBUG,
-				"P2P GO operating frequency %d MHz in valid range",
-				freq);
-			continue;
-		}
+	wpas_p2p_optimize_listen_channel(wpa_s, freqs, num);
 
-		wpa_dbg(ifs, MSG_DEBUG,
-			"P2P GO operating in invalid frequency %d MHz",	freq);
-		/* TODO: Consider using CSA or removing the group within
-		 * wpa_supplicant */
-		wpa_msg(ifs, MSG_INFO, P2P_EVENT_REMOVE_AND_REFORM_GROUP);
-	}
+	/*
+	 * The used frequencies map changed, so it is possible that a GO is
+	 * using a channel that is no longer valid for P2P use. It is also
+	 * possible that due to policy consideration, it would be preferable to
+	 * move it to a frequency already used by other station interfaces.
+	 */
+	wpas_p2p_consider_moving_gos(wpa_s, freqs, num, trig);
+
+	os_free(freqs);
 }
 
 
@@ -6891,6 +7525,7 @@
 				   wpa_s->ifname);
 			found = 1;
 			wpas_p2p_group_formation_failed(wpa_s, 0);
+			break;
 		}
 	}
 
@@ -7119,10 +7754,12 @@
 	wpas_p2p_connect(wpa_s, wpa_s->pending_join_dev_addr, wpa_s->p2p_pin,
 			 wpa_s->p2p_wps_method, wpa_s->p2p_persistent_group, 0,
 			 0, 0, wpa_s->p2p_go_intent, wpa_s->p2p_connect_freq,
+			 wpa_s->p2p_go_vht_center_freq2,
 			 wpa_s->p2p_persistent_id,
 			 wpa_s->p2p_pd_before_go_neg,
 			 wpa_s->p2p_go_ht40,
-			 wpa_s->p2p_go_vht);
+			 wpa_s->p2p_go_vht,
+			 wpa_s->p2p_go_max_oper_chwidth);
 	return ret;
 }
 
@@ -7656,7 +8293,8 @@
 
 	return wpas_p2p_connect(wpa_s, params->peer->p2p_device_addr, NULL,
 				WPS_NFC, 0, 0, 1, 0, wpa_s->conf->p2p_go_intent,
-				params->go_freq, -1, 0, 1, 1);
+				params->go_freq, wpa_s->p2p_go_vht_center_freq2,
+				-1, 0, 1, 1, wpa_s->p2p_go_max_oper_chwidth);
 }
 
 
@@ -7732,7 +8370,8 @@
 		   "connection handover");
 	return wpas_p2p_connect(wpa_s, params->peer->p2p_device_addr, NULL,
 				WPS_NFC, 0, 0, 0, 0, wpa_s->conf->p2p_go_intent,
-				forced_freq, -1, 0, 1, 1);
+				forced_freq, wpa_s->p2p_go_vht_center_freq2,
+				-1, 0, 1, 1, wpa_s->p2p_go_max_oper_chwidth);
 }
 
 
@@ -7746,7 +8385,8 @@
 		   "connection handover");
 	res = wpas_p2p_connect(wpa_s, params->peer->p2p_device_addr, NULL,
 			       WPS_NFC, 0, 0, 0, 1, wpa_s->conf->p2p_go_intent,
-			       forced_freq, -1, 0, 1, 1);
+			       forced_freq, wpa_s->p2p_go_vht_center_freq2,
+			       -1, 0, 1, 1, wpa_s->p2p_go_max_oper_chwidth);
 	if (res)
 		return res;
 
@@ -8080,6 +8720,16 @@
 	u8 curr_chan, cand, chan;
 	unsigned int i;
 
+	/*
+	 * If possible, optimize the Listen channel to be a channel that is
+	 * already used by one of the other interfaces.
+	 */
+	if (!wpa_s->conf->p2p_optimize_listen_chan)
+		return;
+
+	if (!wpa_s->current_ssid || wpa_s->wpa_state != WPA_COMPLETED)
+		return;
+
 	curr_chan = p2p_get_listen_channel(wpa_s->global->p2p);
 	for (i = 0, cand = 0; i < num; i++) {
 		ieee80211_freq_to_chan(freqs[i].freq, &chan);
@@ -8101,23 +8751,194 @@
 }
 
 
-void wpas_p2p_indicate_state_change(struct wpa_supplicant *wpa_s)
+static int wpas_p2p_move_go_csa(struct wpa_supplicant *wpa_s)
 {
-	struct wpa_used_freq_data *freqs;
-	unsigned int num = wpa_s->num_multichan_concurrent;
+	struct hostapd_config *conf;
+	struct p2p_go_neg_results params;
+	struct csa_settings csa_settings;
+	struct wpa_ssid *current_ssid = wpa_s->current_ssid;
+	int old_freq = current_ssid->frequency;
+	int ret;
 
-	if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
-		return;
+	if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_AP_CSA)) {
+		wpa_dbg(wpa_s, MSG_DEBUG, "CSA is not enabled");
+		return -1;
+	}
 
 	/*
-	 * If possible, optimize the Listen channel to be a channel that is
-	 * already used by one of the other interfaces.
+	 * TODO: This function may not always work correctly. For example,
+	 * when we have a running GO and a BSS on a DFS channel.
 	 */
-	if (!wpa_s->conf->p2p_optimize_listen_chan)
+	if (wpas_p2p_init_go_params(wpa_s, &params, 0, 0, 0, 0, 0, NULL)) {
+		wpa_dbg(wpa_s, MSG_DEBUG,
+			"P2P CSA: Failed to select new frequency for GO");
+		return -1;
+	}
+
+	if (current_ssid->frequency == params.freq) {
+		wpa_dbg(wpa_s, MSG_DEBUG,
+			"P2P CSA: Selected same frequency - not moving GO");
+		return 0;
+	}
+
+	conf = hostapd_config_defaults();
+	if (!conf) {
+		wpa_dbg(wpa_s, MSG_DEBUG,
+			"P2P CSA: Failed to allocate default config");
+		return -1;
+	}
+
+	current_ssid->frequency = params.freq;
+	if (wpa_supplicant_conf_ap_ht(wpa_s, current_ssid, conf)) {
+		wpa_dbg(wpa_s, MSG_DEBUG,
+			"P2P CSA: Failed to create new GO config");
+		ret = -1;
+		goto out;
+	}
+
+	if (conf->hw_mode != wpa_s->ap_iface->current_mode->mode) {
+		wpa_dbg(wpa_s, MSG_DEBUG,
+			"P2P CSA: CSA to a different band is not supported");
+		ret = -1;
+		goto out;
+	}
+
+	os_memset(&csa_settings, 0, sizeof(csa_settings));
+	csa_settings.cs_count = P2P_GO_CSA_COUNT;
+	csa_settings.block_tx = P2P_GO_CSA_BLOCK_TX;
+	csa_settings.freq_params.freq = params.freq;
+	csa_settings.freq_params.sec_channel_offset = conf->secondary_channel;
+	csa_settings.freq_params.ht_enabled = conf->ieee80211n;
+	csa_settings.freq_params.bandwidth = conf->secondary_channel ? 40 : 20;
+
+	if (conf->ieee80211ac) {
+		int freq1 = 0, freq2 = 0;
+		u8 chan, opclass;
+
+		if (ieee80211_freq_to_channel_ext(params.freq,
+						  conf->secondary_channel,
+						  conf->vht_oper_chwidth,
+						  &opclass, &chan) ==
+		    NUM_HOSTAPD_MODES) {
+			wpa_printf(MSG_ERROR, "P2P CSA: Bad freq");
+			ret = -1;
+			goto out;
+		}
+
+		if (conf->vht_oper_centr_freq_seg0_idx)
+			freq1 = ieee80211_chan_to_freq(
+				NULL, opclass,
+				conf->vht_oper_centr_freq_seg0_idx);
+
+		if (conf->vht_oper_centr_freq_seg1_idx)
+			freq2 = ieee80211_chan_to_freq(
+				NULL, opclass,
+				conf->vht_oper_centr_freq_seg1_idx);
+
+		if (freq1 < 0 || freq2 < 0) {
+			wpa_dbg(wpa_s, MSG_DEBUG,
+				"P2P CSA: Selected invalid VHT center freqs");
+			ret = -1;
+			goto out;
+		}
+
+		csa_settings.freq_params.vht_enabled = conf->ieee80211ac;
+		csa_settings.freq_params.center_freq1 = freq1;
+		csa_settings.freq_params.center_freq2 = freq2;
+
+		switch (conf->vht_oper_chwidth) {
+		case VHT_CHANWIDTH_80MHZ:
+		case VHT_CHANWIDTH_80P80MHZ:
+			csa_settings.freq_params.bandwidth = 80;
+			break;
+		case VHT_CHANWIDTH_160MHZ:
+			csa_settings.freq_params.bandwidth = 160;
+			break;
+		}
+	}
+
+	ret = ap_switch_channel(wpa_s, &csa_settings);
+out:
+	current_ssid->frequency = old_freq;
+	hostapd_config_free(conf);
+	return ret;
+}
+
+
+static void wpas_p2p_move_go_no_csa(struct wpa_supplicant *wpa_s)
+{
+	struct p2p_go_neg_results params;
+	struct wpa_ssid *current_ssid = wpa_s->current_ssid;
+
+	wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_REMOVE_AND_REFORM_GROUP);
+
+	wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Move GO from freq=%d MHz",
+		current_ssid->frequency);
+
+	/* Stop the AP functionality */
+	/* TODO: Should do this in a way that does not indicated to possible
+	 * P2P Clients in the group that the group is terminated. */
+	wpa_supplicant_ap_deinit(wpa_s);
+
+	/* Reselect the GO frequency */
+	if (wpas_p2p_init_go_params(wpa_s, &params, 0, 0, 0, 0, 0, NULL)) {
+		wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Failed to reselect freq");
+		wpas_p2p_group_delete(wpa_s,
+				      P2P_GROUP_REMOVAL_GO_LEAVE_CHANNEL);
+		return;
+	}
+	wpa_dbg(wpa_s, MSG_DEBUG, "P2P: New freq selected for the GO (%u MHz)",
+		params.freq);
+
+	if (params.freq &&
+	    !p2p_supported_freq_go(wpa_s->global->p2p, params.freq)) {
+		wpa_printf(MSG_DEBUG,
+			   "P2P: Selected freq (%u MHz) is not valid for P2P",
+			   params.freq);
+		wpas_p2p_group_delete(wpa_s,
+				      P2P_GROUP_REMOVAL_GO_LEAVE_CHANNEL);
+		return;
+	}
+
+	/* Update the frequency */
+	current_ssid->frequency = params.freq;
+	wpa_s->connect_without_scan = current_ssid;
+	wpa_s->reassociate = 1;
+	wpa_s->disconnected = 0;
+	wpa_supplicant_req_scan(wpa_s, 0, 0);
+}
+
+
+static void wpas_p2p_move_go(void *eloop_ctx, void *timeout_ctx)
+{
+	struct wpa_supplicant *wpa_s = eloop_ctx;
+
+	if (!wpa_s->ap_iface || !wpa_s->current_ssid)
 		return;
 
-	if (!wpa_s->current_ssid || wpa_s->wpa_state != WPA_COMPLETED)
+	wpas_p2p_go_update_common_freqs(wpa_s);
+
+	/* Do not move GO in the middle of a CSA */
+	if (hostapd_csa_in_progress(wpa_s->ap_iface)) {
+		wpa_printf(MSG_DEBUG,
+			   "P2P: CSA is in progress - not moving GO");
 		return;
+	}
+
+	/*
+	 * First, try a channel switch flow. If it is not supported or fails,
+	 * take down the GO and bring it up again.
+	 */
+	if (wpas_p2p_move_go_csa(wpa_s) < 0)
+		wpas_p2p_move_go_no_csa(wpa_s);
+}
+
+
+static void wpas_p2p_reconsider_moving_go(void *eloop_ctx, void *timeout_ctx)
+{
+	struct wpa_supplicant *wpa_s = eloop_ctx;
+	struct wpa_used_freq_data *freqs = NULL;
+	unsigned int num = wpa_s->num_multichan_concurrent;
 
 	freqs = os_calloc(num, sizeof(struct wpa_used_freq_data));
 	if (!freqs)
@@ -8125,11 +8946,187 @@
 
 	num = get_shared_radio_freqs_data(wpa_s, freqs, num);
 
-	wpas_p2p_optimize_listen_channel(wpa_s, freqs, num);
+	/* Previous attempt to move a GO was not possible -- try again. */
+	wpas_p2p_consider_moving_gos(wpa_s, freqs, num,
+				     WPAS_P2P_CHANNEL_UPDATE_ANY);
+
 	os_free(freqs);
 }
 
 
+/*
+ * Consider moving a GO from its currently used frequency:
+ * 1. It is possible that due to regulatory consideration the frequency
+ *    can no longer be used and there is a need to evacuate the GO.
+ * 2. It is possible that due to MCC considerations, it would be preferable
+ *    to move the GO to a channel that is currently used by some other
+ *    station interface.
+ *
+ * In case a frequency that became invalid is once again valid, cancel a
+ * previously initiated GO frequency change.
+ */
+static void wpas_p2p_consider_moving_one_go(struct wpa_supplicant *wpa_s,
+					    struct wpa_used_freq_data *freqs,
+					    unsigned int num)
+{
+	unsigned int i, invalid_freq = 0, policy_move = 0, flags = 0;
+	unsigned int timeout;
+	int freq;
+
+	wpas_p2p_go_update_common_freqs(wpa_s);
+
+	freq = wpa_s->current_ssid->frequency;
+	for (i = 0, invalid_freq = 0; i < num; i++) {
+		if (freqs[i].freq == freq) {
+			flags = freqs[i].flags;
+
+			/* The channel is invalid, must change it */
+			if (!p2p_supported_freq_go(wpa_s->global->p2p, freq)) {
+				wpa_dbg(wpa_s, MSG_DEBUG,
+					"P2P: Freq=%d MHz no longer valid for GO",
+					freq);
+				invalid_freq = 1;
+			}
+		} else if (freqs[i].flags == 0) {
+			/* Freq is not used by any other station interface */
+			continue;
+		} else if (!p2p_supported_freq(wpa_s->global->p2p,
+					       freqs[i].freq)) {
+			/* Freq is not valid for P2P use cases */
+			continue;
+		} else if (wpa_s->conf->p2p_go_freq_change_policy ==
+			   P2P_GO_FREQ_MOVE_SCM) {
+			policy_move = 1;
+		} else if (wpa_s->conf->p2p_go_freq_change_policy ==
+			   P2P_GO_FREQ_MOVE_SCM_PEER_SUPPORTS &&
+			   wpas_p2p_go_is_peer_freq(wpa_s, freqs[i].freq)) {
+			policy_move = 1;
+		} else if ((wpa_s->conf->p2p_go_freq_change_policy ==
+			    P2P_GO_FREQ_MOVE_SCM_ECSA) &&
+			   wpas_p2p_go_is_peer_freq(wpa_s, freqs[i].freq)) {
+			if (!p2p_get_group_num_members(wpa_s->p2p_group)) {
+				policy_move = 1;
+			} else if ((wpa_s->drv_flags &
+				    WPA_DRIVER_FLAGS_AP_CSA) &&
+				   wpas_p2p_go_clients_support_ecsa(wpa_s)) {
+				u8 chan;
+
+				/*
+				 * We do not support CSA between bands, so move
+				 * GO only within the same band.
+				 */
+				if (wpa_s->ap_iface->current_mode->mode ==
+				    ieee80211_freq_to_chan(freqs[i].freq,
+							   &chan))
+					policy_move = 1;
+			}
+		}
+	}
+
+	wpa_dbg(wpa_s, MSG_DEBUG,
+		"P2P: GO move: invalid_freq=%u, policy_move=%u, flags=0x%X",
+		invalid_freq, policy_move, flags);
+
+	/*
+	 * The channel is valid, or we are going to have a policy move, so
+	 * cancel timeout.
+	 */
+	if (!invalid_freq || policy_move) {
+		wpa_dbg(wpa_s, MSG_DEBUG,
+			"P2P: Cancel a GO move from freq=%d MHz", freq);
+		eloop_cancel_timeout(wpas_p2p_move_go, wpa_s, NULL);
+
+		if (wpas_p2p_in_progress(wpa_s)) {
+			wpa_dbg(wpa_s, MSG_DEBUG,
+				"P2P: GO move: policy CS is not allowed - setting timeout to re-consider GO move");
+			eloop_cancel_timeout(wpas_p2p_reconsider_moving_go,
+					     wpa_s, NULL);
+			eloop_register_timeout(P2P_RECONSIDER_GO_MOVE_DELAY, 0,
+					       wpas_p2p_reconsider_moving_go,
+					       wpa_s, NULL);
+			return;
+		}
+	}
+
+	if (!invalid_freq && (!policy_move || flags != 0)) {
+		wpa_dbg(wpa_s, MSG_DEBUG,
+			"P2P: Not initiating a GO frequency change");
+		return;
+	}
+
+	/*
+	 * Do not consider moving GO if it is in the middle of a CSA. When the
+	 * CSA is finished this flow should be retriggered.
+	 */
+	if (hostapd_csa_in_progress(wpa_s->ap_iface)) {
+		wpa_dbg(wpa_s, MSG_DEBUG,
+			"P2P: Not initiating a GO frequency change - CSA is in progress");
+		return;
+	}
+
+	if (invalid_freq && !wpas_p2p_disallowed_freq(wpa_s->global, freq))
+		timeout = P2P_GO_FREQ_CHANGE_TIME;
+	else
+		timeout = 0;
+
+	wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Move GO from freq=%d MHz in %d secs",
+		freq, timeout);
+	eloop_cancel_timeout(wpas_p2p_move_go, wpa_s, NULL);
+	eloop_register_timeout(timeout, 0, wpas_p2p_move_go, wpa_s, NULL);
+}
+
+
+static void wpas_p2p_consider_moving_gos(struct wpa_supplicant *wpa_s,
+					 struct wpa_used_freq_data *freqs,
+					 unsigned int num,
+					 enum wpas_p2p_channel_update_trig trig)
+{
+	struct wpa_supplicant *ifs;
+
+	eloop_cancel_timeout(wpas_p2p_reconsider_moving_go, ELOOP_ALL_CTX,
+			     NULL);
+
+	/*
+	 * Travers all the radio interfaces, and for each GO interface, check
+	 * if there is a need to move the GO from the frequency it is using,
+	 * or in case the frequency is valid again, cancel the evacuation flow.
+	 */
+	dl_list_for_each(ifs, &wpa_s->radio->ifaces, struct wpa_supplicant,
+			 radio_list) {
+		if (ifs->current_ssid == NULL ||
+		    ifs->current_ssid->mode != WPAS_MODE_P2P_GO)
+			continue;
+
+		/*
+		 * The GO was just started or completed channel switch, no need
+		 * to move it.
+		 */
+		if (wpa_s == ifs &&
+		    (trig == WPAS_P2P_CHANNEL_UPDATE_STATE_CHANGE ||
+		     trig == WPAS_P2P_CHANNEL_UPDATE_CS)) {
+			wpa_dbg(wpa_s, MSG_DEBUG,
+				"P2P: GO move - schedule re-consideration");
+			eloop_register_timeout(P2P_RECONSIDER_GO_MOVE_DELAY, 0,
+					       wpas_p2p_reconsider_moving_go,
+					       wpa_s, NULL);
+			continue;
+		}
+
+		wpas_p2p_consider_moving_one_go(ifs, freqs, num);
+	}
+}
+
+
+void wpas_p2p_indicate_state_change(struct wpa_supplicant *wpa_s)
+{
+	if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
+		return;
+
+	wpas_p2p_update_channel_list(wpa_s,
+				     WPAS_P2P_CHANNEL_UPDATE_STATE_CHANGE);
+}
+
+
 void wpas_p2p_deinit_iface(struct wpa_supplicant *wpa_s)
 {
 	if (wpa_s == wpa_s->global->p2p_init_wpa_s && wpa_s->global->p2p) {
diff --git a/wpa_supplicant/p2p_supplicant.h b/wpa_supplicant/p2p_supplicant.h
index 1df34d0..21ee41f 100644
--- a/wpa_supplicant/p2p_supplicant.h
+++ b/wpa_supplicant/p2p_supplicant.h
@@ -17,6 +17,15 @@
 struct wps_event_fail;
 struct p2ps_provision;
 
+enum wpas_p2p_channel_update_trig {
+	WPAS_P2P_CHANNEL_UPDATE_ANY,
+	WPAS_P2P_CHANNEL_UPDATE_DRIVER,
+	WPAS_P2P_CHANNEL_UPDATE_STATE_CHANGE,
+	WPAS_P2P_CHANNEL_UPDATE_AVOID,
+	WPAS_P2P_CHANNEL_UPDATE_DISALLOW,
+	WPAS_P2P_CHANNEL_UPDATE_CS,
+};
+
 int wpas_p2p_add_p2pdev_interface(struct wpa_supplicant *wpa_s,
 				  const char *conf_p2p_dev);
 struct wpa_supplicant * wpas_get_p2p_go_iface(struct wpa_supplicant *wpa_s,
@@ -26,17 +35,21 @@
 int wpas_p2p_connect(struct wpa_supplicant *wpa_s, const u8 *peer_addr,
 		     const char *pin, enum p2p_wps_method wps_method,
 		     int persistent_group, int auto_join, int join,
-		     int auth, int go_intent, int freq, int persistent_id,
-		     int pd, int ht40, int vht);
+		     int auth, int go_intent, int freq,
+		     unsigned int vht_center_freq2, int persistent_id,
+		     int pd, int ht40, int vht, unsigned int max_oper_chwidth);
 int wpas_p2p_handle_frequency_conflicts(struct wpa_supplicant *wpa_s,
                                           int freq, struct wpa_ssid *ssid);
 int wpas_p2p_group_add(struct wpa_supplicant *wpa_s, int persistent_group,
-		       int freq, int ht40, int vht);
+		       int freq, int vht_center_freq2, int ht40, int vht,
+		       int max_oper_chwidth);
 int wpas_p2p_group_add_persistent(struct wpa_supplicant *wpa_s,
 				  struct wpa_ssid *ssid, int addr_allocated,
-				  int force_freq, int neg_freq, int ht40,
-				  int vht, const struct p2p_channels *channels,
-				  int connection_timeout);
+				  int force_freq, int neg_freq,
+				  int vht_center_freq2, int ht40,
+				  int vht, int max_oper_chwidth,
+				  const struct p2p_channels *channels,
+				  int connection_timeout, int force_scan);
 struct p2p_group * wpas_p2p_group_init(struct wpa_supplicant *wpa_s,
 				       struct wpa_ssid *ssid);
 enum wpas_p2p_prov_disc_use {
@@ -90,7 +103,8 @@
 			      const char *service);
 int wpas_p2p_service_add_asp(struct wpa_supplicant *wpa_s, int auto_accept,
 			     u32 adv_id, const char *adv_str, u8 svc_state,
-			     u16 config_methods, const char *svc_info);
+			     u16 config_methods, const char *svc_info,
+			     const u8 *cpt_priority);
 int wpas_p2p_service_del_asp(struct wpa_supplicant *wpa_s, u32 adv_id);
 void wpas_p2p_service_flush_asp(struct wpa_supplicant *wpa_s);
 int wpas_p2p_service_p2ps_id_exists(struct wpa_supplicant *wpa_s, u32 adv_id);
@@ -101,7 +115,8 @@
 int wpas_p2p_reject(struct wpa_supplicant *wpa_s, const u8 *addr);
 int wpas_p2p_invite(struct wpa_supplicant *wpa_s, const u8 *peer_addr,
 		    struct wpa_ssid *ssid, const u8 *go_dev_addr, int freq,
-		    int ht40, int vht, int pref_freq);
+		    int vht_center_freq2, int ht40, int vht,
+		    int max_oper_chwidth, int pref_freq);
 int wpas_p2p_invite_group(struct wpa_supplicant *wpa_s, const char *ifname,
 			  const u8 *peer_addr, const u8 *go_dev_addr);
 int wpas_p2p_presence_req(struct wpa_supplicant *wpa_s, u32 duration1,
@@ -130,6 +145,8 @@
 			   struct hostapd_hw_modes *mode, u8 channel);
 int wpas_p2p_get_vht80_center(struct wpa_supplicant *wpa_s,
 			      struct hostapd_hw_modes *mode, u8 channel);
+int wpas_p2p_get_vht160_center(struct wpa_supplicant *wpa_s,
+			       struct hostapd_hw_modes *mode, u8 channel);
 unsigned int wpas_p2p_search_delay(struct wpa_supplicant *wpa_s);
 void wpas_p2p_new_psk_cb(struct wpa_supplicant *wpa_s, const u8 *mac_addr,
 			 const u8 *p2p_dev_addr,
@@ -160,7 +177,10 @@
 			  unsigned int rx_freq, int ssi_signal);
 void wpas_p2p_wps_success(struct wpa_supplicant *wpa_s, const u8 *peer_addr,
 			  int registrar);
-void wpas_p2p_update_channel_list(struct wpa_supplicant *wpa_s);
+
+void wpas_p2p_update_channel_list(struct wpa_supplicant *wpa_s,
+				  enum wpas_p2p_channel_update_trig trig);
+
 void wpas_p2p_update_best_channels(struct wpa_supplicant *wpa_s,
 				   int freq_24, int freq_5, int freq_overall);
 void wpas_p2p_rx_action(struct wpa_supplicant *wpa_s, const u8 *da,
@@ -221,7 +241,9 @@
 {
 }
 
-static inline void wpas_p2p_update_channel_list(struct wpa_supplicant *wpa_s)
+static inline void
+wpas_p2p_update_channel_list(struct wpa_supplicant *wpa_s,
+			     enum wpas_p2p_channel_update_trig trig)
 {
 }
 
diff --git a/wpa_supplicant/p2p_supplicant_sd.c b/wpa_supplicant/p2p_supplicant_sd.c
index f4aa3e0..f8675e6 100644
--- a/wpa_supplicant/p2p_supplicant_sd.c
+++ b/wpa_supplicant/p2p_supplicant_sd.c
@@ -48,7 +48,7 @@
 			u8 *spos_tmp;
 
 			/* Offset */
-			if (*spos + 2 > end) {
+			if (end - *spos < 2) {
 				wpa_printf(MSG_DEBUG, "P2P: No room for full "
 					   "DNS offset field");
 				return -1;
@@ -74,14 +74,14 @@
 			return 0;
 
 		(*spos)++;
-		if (*spos + len > end) {
+		if (len > end - *spos) {
 			wpa_printf(MSG_DEBUG, "P2P: Invalid domain name "
 				   "sequence - no room for label with length "
 				   "%u", len);
 			return -1;
 		}
 
-		if (*upos + len + 2 > uend)
+		if (len + 2 > uend - *upos)
 			return -2;
 
 		os_memcpy(*upos, *spos, len);
@@ -722,11 +722,11 @@
 	if (resp == NULL)
 		return;
 
-	while (pos + 1 < end) {
+	while (end - pos > 1) {
 		wpa_printf(MSG_DEBUG, "P2P: Service Request TLV");
 		slen = WPA_GET_LE16(pos);
 		pos += 2;
-		if (pos + slen > end || slen < 2) {
+		if (slen > end - pos || slen < 2) {
 			wpa_printf(MSG_DEBUG, "P2P: Unexpected Query Data "
 				   "length");
 			wpabuf_free(resp);
@@ -827,10 +827,10 @@
 		u8 svc_len;
 
 		/* Sanity check fixed length+svc_str */
-		if (pos + 6 >= tlv_end)
+		if (6 >= tlv_end - pos)
 			break;
 		svc_len = pos[6];
-		if (pos + svc_len + 10 > tlv_end)
+		if (svc_len + 10 > tlv_end - pos)
 			break;
 
 		/* Advertisement ID */
@@ -917,13 +917,13 @@
 		}
 	}
 
-	while (pos < end) {
+	while (end - pos >= 2) {
 		u8 srv_proto, srv_trans_id, status;
 
 		wpa_printf(MSG_DEBUG, "P2P: Service Response TLV");
 		slen = WPA_GET_LE16(pos);
 		pos += 2;
-		if (pos + slen > end || slen < 3) {
+		if (slen > end - pos || slen < 3) {
 			wpa_printf(MSG_DEBUG, "P2P: Unexpected Response Data "
 				   "length");
 			return;
@@ -1185,13 +1185,14 @@
 int wpas_p2p_service_add_asp(struct wpa_supplicant *wpa_s,
 			     int auto_accept, u32 adv_id,
 			     const char *adv_str, u8 svc_state,
-			     u16 config_methods, const char *svc_info)
+			     u16 config_methods, const char *svc_info,
+			     const u8 *cpt_priority)
 {
 	int ret;
 
 	ret = p2p_service_add_asp(wpa_s->global->p2p, auto_accept, adv_id,
 				  adv_str, svc_state, config_methods,
-				  svc_info);
+				  svc_info, cpt_priority);
 	if (ret == 0)
 		wpas_p2p_sd_service_update(wpa_s);
 	return ret;
diff --git a/wpa_supplicant/scan.c b/wpa_supplicant/scan.c
index e81465c..3c3f9e0 100644
--- a/wpa_supplicant/scan.c
+++ b/wpa_supplicant/scan.c
@@ -267,13 +267,12 @@
 
 
 int wpa_supplicant_start_sched_scan(struct wpa_supplicant *wpa_s,
-				    struct wpa_driver_scan_params *params,
-				    int interval)
+				    struct wpa_driver_scan_params *params)
 {
 	int ret;
 
 	wpa_supplicant_notify_scanning(wpa_s, 1);
-	ret = wpa_drv_sched_scan(wpa_s, params, interval * 1000);
+	ret = wpa_drv_sched_scan(wpa_s, params);
 	if (ret)
 		wpa_supplicant_notify_scanning(wpa_s, 0);
 	else
@@ -485,6 +484,12 @@
 		wpas_hs20_add_indication(extra_ie, -1);
 #endif /* CONFIG_HS20 */
 
+#ifdef CONFIG_FST
+	if (wpa_s->fst_ies &&
+	    wpabuf_resize(&extra_ie, wpabuf_len(wpa_s->fst_ies)) == 0)
+		wpabuf_put_buf(extra_ie, wpa_s->fst_ies);
+#endif /* CONFIG_FST */
+
 	return extra_ie;
 }
 
@@ -802,6 +807,9 @@
 	}
 
 	if (wpa_s->last_scan_req != MANUAL_SCAN_REQ &&
+#ifdef CONFIG_AP
+	    !wpa_s->ap_iface &&
+#endif /* CONFIG_AP */
 	    wpa_s->conf->ap_scan == 2) {
 		wpa_s->connect_without_scan = NULL;
 		wpa_s->prev_scan_wildcard = 0;
@@ -1173,6 +1181,7 @@
 	unsigned int max_sched_scan_ssids;
 	int wildcard = 0;
 	int need_ssids;
+	struct sched_scan_plan scan_plan;
 
 	if (!wpa_s->sched_scan_supported)
 		return -1;
@@ -1262,11 +1271,6 @@
 
 	if (!ssid || !wpa_s->prev_sched_ssid) {
 		wpa_dbg(wpa_s, MSG_DEBUG, "Beginning of SSID list");
-		if (wpa_s->conf->sched_scan_interval)
-			wpa_s->sched_scan_interval =
-				wpa_s->conf->sched_scan_interval;
-		if (wpa_s->sched_scan_interval == 0)
-			wpa_s->sched_scan_interval = 10;
 		wpa_s->sched_scan_timeout = max_sched_scan_ssids * 2;
 		wpa_s->first_sched_scan = 1;
 		ssid = wpa_s->conf->ssid;
@@ -1351,14 +1355,51 @@
 	scan_params = &params;
 
 scan:
+	wpa_s->sched_scan_timed_out = 0;
+
+	/*
+	 * We cannot support multiple scan plans if the scan request includes
+	 * too many SSID's, so in this case use only the last scan plan and make
+	 * it run infinitely. It will be stopped by the timeout.
+	 */
+	if (wpa_s->sched_scan_plans_num == 1 ||
+	    (wpa_s->sched_scan_plans_num && !ssid && wpa_s->first_sched_scan)) {
+		params.sched_scan_plans = wpa_s->sched_scan_plans;
+		params.sched_scan_plans_num = wpa_s->sched_scan_plans_num;
+	} else if (wpa_s->sched_scan_plans_num > 1) {
+		wpa_dbg(wpa_s, MSG_DEBUG,
+			"Too many SSIDs. Default to using single scheduled_scan plan");
+		params.sched_scan_plans =
+			&wpa_s->sched_scan_plans[wpa_s->sched_scan_plans_num -
+						 1];
+		params.sched_scan_plans_num = 1;
+	} else {
+		if (wpa_s->conf->sched_scan_interval)
+			scan_plan.interval = wpa_s->conf->sched_scan_interval;
+		else
+			scan_plan.interval = 10;
+
+		if (scan_plan.interval > wpa_s->max_sched_scan_plan_interval) {
+			wpa_printf(MSG_WARNING,
+				   "Scan interval too long(%u), use the maximum allowed(%u)",
+				   scan_plan.interval,
+				   wpa_s->max_sched_scan_plan_interval);
+			scan_plan.interval =
+				wpa_s->max_sched_scan_plan_interval;
+		}
+
+		scan_plan.iterations = 0;
+		params.sched_scan_plans = &scan_plan;
+		params.sched_scan_plans_num = 1;
+	}
+
 	if (ssid || !wpa_s->first_sched_scan) {
 		wpa_dbg(wpa_s, MSG_DEBUG,
-			"Starting sched scan: interval %d timeout %d",
-			wpa_s->sched_scan_interval, wpa_s->sched_scan_timeout);
+			"Starting sched scan: interval %u timeout %d",
+			params.sched_scan_plans[0].interval,
+			wpa_s->sched_scan_timeout);
 	} else {
-		wpa_dbg(wpa_s, MSG_DEBUG,
-			"Starting sched scan: interval %d (no timeout)",
-			wpa_s->sched_scan_interval);
+		wpa_dbg(wpa_s, MSG_DEBUG, "Starting sched scan (no timeout)");
 	}
 
 	wpa_setband_scan_freqs(wpa_s, scan_params);
@@ -1372,8 +1413,7 @@
 		}
 	}
 
-	ret = wpa_supplicant_start_sched_scan(wpa_s, scan_params,
-					      wpa_s->sched_scan_interval);
+	ret = wpa_supplicant_start_sched_scan(wpa_s, scan_params);
 	wpabuf_free(extra_ie);
 	os_free(params.filter_ssids);
 	if (ret) {
@@ -1391,9 +1431,12 @@
 				       wpa_s, NULL);
 		wpa_s->first_sched_scan = 0;
 		wpa_s->sched_scan_timeout /= 2;
-		wpa_s->sched_scan_interval *= 2;
-		if (wpa_s->sched_scan_timeout < wpa_s->sched_scan_interval) {
-			wpa_s->sched_scan_interval = 10;
+		params.sched_scan_plans[0].interval *= 2;
+		if ((unsigned int) wpa_s->sched_scan_timeout <
+		    params.sched_scan_plans[0].interval ||
+		    params.sched_scan_plans[0].interval >
+		    wpa_s->max_sched_scan_plan_interval) {
+			params.sched_scan_plans[0].interval = 10;
 			wpa_s->sched_scan_timeout = max_sched_scan_ssids * 2;
 		}
 	}
@@ -1512,8 +1555,8 @@
 	pos = (const u8 *) (res + 1);
 	end = pos + res->ie_len;
 
-	while (pos + 1 < end) {
-		if (pos + 2 + pos[1] > end)
+	while (end - pos > 1) {
+		if (2 + pos[1] > end - pos)
 			break;
 		if (pos[0] == ie)
 			return pos;
@@ -1541,8 +1584,8 @@
 	pos = (const u8 *) (res + 1);
 	end = pos + res->ie_len;
 
-	while (pos + 1 < end) {
-		if (pos + 2 + pos[1] > end)
+	while (end - pos > 1) {
+		if (2 + pos[1] > end - pos)
 			break;
 		if (pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 &&
 		    vendor_type == WPA_GET_BE32(&pos[2]))
@@ -1578,8 +1621,8 @@
 	pos += res->ie_len;
 	end = pos + res->beacon_ie_len;
 
-	while (pos + 1 < end) {
-		if (pos + 2 + pos[1] > end)
+	while (end - pos > 1) {
+		if (2 + pos[1] > end - pos)
 			break;
 		if (pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 &&
 		    vendor_type == WPA_GET_BE32(&pos[2]))
@@ -1614,8 +1657,8 @@
 	pos = (const u8 *) (res + 1);
 	end = pos + res->ie_len;
 
-	while (pos + 1 < end) {
-		if (pos + 2 + pos[1] > end)
+	while (end - pos > 1) {
+		if (2 + pos[1] > end - pos)
 			break;
 		if (pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 &&
 		    vendor_type == WPA_GET_BE32(&pos[2]))
@@ -1679,7 +1722,7 @@
 		snr_a_full = wa->snr;
 		snr_a = MIN(wa->snr, GREAT_SNR);
 		snr_b_full = wb->snr;
-		snr_b = MIN(wa->snr, GREAT_SNR);
+		snr_b = MIN(wb->snr, GREAT_SNR);
 	} else {
 		/* Level is not in dBm, so we can't calculate
 		 * SNR. Just use raw level (units unknown). */
@@ -2139,6 +2182,9 @@
 		wpa_s->scan_work = NULL;
 		radio_work_done(work);
 	}
+
+	if (wpa_s->wpa_state == WPA_SCANNING)
+		wpa_supplicant_set_state(wpa_s, wpa_s->scan_prev_wpa_state);
 }
 
 
@@ -2205,6 +2251,19 @@
 	params->only_new_results = src->only_new_results;
 	params->low_priority = src->low_priority;
 
+	if (src->sched_scan_plans_num > 0) {
+		params->sched_scan_plans =
+			os_malloc(sizeof(*src->sched_scan_plans) *
+				  src->sched_scan_plans_num);
+		if (!params->sched_scan_plans)
+			goto failed;
+
+		os_memcpy(params->sched_scan_plans, src->sched_scan_plans,
+			  sizeof(*src->sched_scan_plans) *
+			  src->sched_scan_plans_num);
+		params->sched_scan_plans_num = src->sched_scan_plans_num;
+	}
+
 	if (src->mac_addr_rand) {
 		params->mac_addr_rand = src->mac_addr_rand;
 
@@ -2242,6 +2301,7 @@
 	os_free((u8 *) params->extra_ies);
 	os_free(params->freqs);
 	os_free(params->filter_ssids);
+	os_free(params->sched_scan_plans);
 
 	/*
 	 * Note: params->mac_addr_mask points to same memory allocation and
@@ -2255,10 +2315,11 @@
 
 int wpas_start_pno(struct wpa_supplicant *wpa_s)
 {
-	int ret, interval, prio;
+	int ret, prio;
 	size_t i, num_ssid, num_match_ssid;
 	struct wpa_ssid *ssid;
 	struct wpa_driver_scan_params params;
+	struct sched_scan_plan scan_plan;
 
 	if (!wpa_s->sched_scan_supported)
 		return -1;
@@ -2352,8 +2413,20 @@
 	if (wpa_s->conf->filter_rssi)
 		params.filter_rssi = wpa_s->conf->filter_rssi;
 
-	interval = wpa_s->conf->sched_scan_interval ?
-		wpa_s->conf->sched_scan_interval : 10;
+	if (wpa_s->sched_scan_plans_num) {
+		params.sched_scan_plans = wpa_s->sched_scan_plans;
+		params.sched_scan_plans_num = wpa_s->sched_scan_plans_num;
+	} else {
+		/* Set one scan plan that will run infinitely */
+		if (wpa_s->conf->sched_scan_interval)
+			scan_plan.interval = wpa_s->conf->sched_scan_interval;
+		else
+			scan_plan.interval = 10;
+
+		scan_plan.iterations = 0;
+		params.sched_scan_plans = &scan_plan;
+		params.sched_scan_plans_num = 1;
+	}
 
 	if (params.freqs == NULL && wpa_s->manual_sched_scan_freqs) {
 		wpa_dbg(wpa_s, MSG_DEBUG, "Limit sched scan to specified channels");
@@ -2368,7 +2441,7 @@
 		}
 	}
 
-	ret = wpa_supplicant_start_sched_scan(wpa_s, &params, interval);
+	ret = wpa_supplicant_start_sched_scan(wpa_s, &params);
 	os_free(params.filter_ssids);
 	if (ret == 0)
 		wpa_s->pno = 1;
@@ -2453,3 +2526,133 @@
 	wpa_s->mac_addr_rand_enable |= type;
 	return 0;
 }
+
+
+int wpas_abort_ongoing_scan(struct wpa_supplicant *wpa_s)
+{
+	if (wpa_s->scan_work && wpa_s->own_scan_running) {
+		wpa_dbg(wpa_s, MSG_DEBUG, "Abort an ongoing scan");
+		return wpa_drv_abort_scan(wpa_s);
+	}
+
+	return 0;
+}
+
+
+int wpas_sched_scan_plans_set(struct wpa_supplicant *wpa_s, const char *cmd)
+{
+	struct sched_scan_plan *scan_plans = NULL;
+	const char *token, *context = NULL;
+	unsigned int num = 0;
+
+	if (!cmd)
+		return -1;
+
+	if (!cmd[0]) {
+		wpa_printf(MSG_DEBUG, "Clear sched scan plans");
+		os_free(wpa_s->sched_scan_plans);
+		wpa_s->sched_scan_plans = NULL;
+		wpa_s->sched_scan_plans_num = 0;
+		return 0;
+	}
+
+	while ((token = cstr_token(cmd, " ", &context))) {
+		int ret;
+		struct sched_scan_plan *scan_plan, *n;
+
+		n = os_realloc_array(scan_plans, num + 1, sizeof(*scan_plans));
+		if (!n)
+			goto fail;
+
+		scan_plans = n;
+		scan_plan = &scan_plans[num];
+		num++;
+
+		ret = sscanf(token, "%u:%u", &scan_plan->interval,
+			     &scan_plan->iterations);
+		if (ret <= 0 || ret > 2 || !scan_plan->interval) {
+			wpa_printf(MSG_ERROR,
+				   "Invalid sched scan plan input: %s", token);
+			goto fail;
+		}
+
+		if (!scan_plan->interval) {
+			wpa_printf(MSG_ERROR,
+				   "scan plan %u: Interval cannot be zero",
+				   num);
+			goto fail;
+		}
+
+		if (scan_plan->interval > wpa_s->max_sched_scan_plan_interval) {
+			wpa_printf(MSG_WARNING,
+				   "scan plan %u: Scan interval too long(%u), use the maximum allowed(%u)",
+				   num, scan_plan->interval,
+				   wpa_s->max_sched_scan_plan_interval);
+			scan_plan->interval =
+				wpa_s->max_sched_scan_plan_interval;
+		}
+
+		if (ret == 1) {
+			scan_plan->iterations = 0;
+			break;
+		}
+
+		if (!scan_plan->iterations) {
+			wpa_printf(MSG_ERROR,
+				   "scan plan %u: Number of iterations cannot be zero",
+				   num);
+			goto fail;
+		}
+
+		if (scan_plan->iterations >
+		    wpa_s->max_sched_scan_plan_iterations) {
+			wpa_printf(MSG_WARNING,
+				   "scan plan %u: Too many iterations(%u), use the maximum allowed(%u)",
+				   num, scan_plan->iterations,
+				   wpa_s->max_sched_scan_plan_iterations);
+			scan_plan->iterations =
+				wpa_s->max_sched_scan_plan_iterations;
+		}
+
+		wpa_printf(MSG_DEBUG,
+			   "scan plan %u: interval=%u iterations=%u",
+			   num, scan_plan->interval, scan_plan->iterations);
+	}
+
+	if (!scan_plans) {
+		wpa_printf(MSG_ERROR, "Invalid scan plans entry");
+		goto fail;
+	}
+
+	if (cstr_token(cmd, " ", &context) || scan_plans[num - 1].iterations) {
+		wpa_printf(MSG_ERROR,
+			   "All scan plans but the last must specify a number of iterations");
+		goto fail;
+	}
+
+	wpa_printf(MSG_DEBUG, "scan plan %u (last plan): interval=%u",
+		   num, scan_plans[num - 1].interval);
+
+	if (num > wpa_s->max_sched_scan_plans) {
+		wpa_printf(MSG_WARNING,
+			   "Too many scheduled scan plans (only %u supported)",
+			   wpa_s->max_sched_scan_plans);
+		wpa_printf(MSG_WARNING,
+			   "Use only the first %u scan plans, and the last one (in infinite loop)",
+			   wpa_s->max_sched_scan_plans - 1);
+		os_memcpy(&scan_plans[wpa_s->max_sched_scan_plans - 1],
+			  &scan_plans[num - 1], sizeof(*scan_plans));
+		num = wpa_s->max_sched_scan_plans;
+	}
+
+	os_free(wpa_s->sched_scan_plans);
+	wpa_s->sched_scan_plans = scan_plans;
+	wpa_s->sched_scan_plans_num = num;
+
+	return 0;
+
+fail:
+	os_free(scan_plans);
+	wpa_printf(MSG_ERROR, "invalid scan plans list");
+	return -1;
+}
diff --git a/wpa_supplicant/scan.h b/wpa_supplicant/scan.h
index 7650f5a..93ec9b3 100644
--- a/wpa_supplicant/scan.h
+++ b/wpa_supplicant/scan.h
@@ -40,8 +40,7 @@
 		       struct wpa_scan_results *scan_res);
 int wpas_scan_scheduled(struct wpa_supplicant *wpa_s);
 int wpa_supplicant_start_sched_scan(struct wpa_supplicant *wpa_s,
-				    struct wpa_driver_scan_params *params,
-				    int interval);
+				    struct wpa_driver_scan_params *params);
 int wpa_supplicant_stop_sched_scan(struct wpa_supplicant *wpa_s);
 struct wpa_driver_scan_params *
 wpa_scan_clone_params(const struct wpa_driver_scan_params *src);
@@ -54,5 +53,6 @@
 int wpas_mac_addr_rand_scan_set(struct wpa_supplicant *wpa_s,
 				unsigned int type, const u8 *addr,
 				const u8 *mask);
+int wpas_abort_ongoing_scan(struct wpa_supplicant *wpa_s);
 
 #endif /* SCAN_H */
diff --git a/wpa_supplicant/sme.c b/wpa_supplicant/sme.c
index a472feb..2a3a728 100644
--- a/wpa_supplicant/sme.c
+++ b/wpa_supplicant/sme.c
@@ -438,6 +438,21 @@
 	}
 #endif /* CONFIG_HS20 */
 
+#ifdef CONFIG_FST
+	if (wpa_s->fst_ies) {
+		int fst_ies_len = wpabuf_len(wpa_s->fst_ies);
+
+		if (wpa_s->sme.assoc_req_ie_len + fst_ies_len <=
+		    sizeof(wpa_s->sme.assoc_req_ie)) {
+			os_memcpy(wpa_s->sme.assoc_req_ie +
+				  wpa_s->sme.assoc_req_ie_len,
+				  wpabuf_head(wpa_s->fst_ies),
+				  fst_ies_len);
+			wpa_s->sme.assoc_req_ie_len += fst_ies_len;
+		}
+	}
+#endif /* CONFIG_FST */
+
 	ext_capab_len = wpas_build_ext_capab(wpa_s, ext_capab,
 					     sizeof(ext_capab));
 	if (ext_capab_len > 0) {
@@ -583,7 +598,8 @@
 	wpa_s->connect_work = work;
 
 	if (cwork->bss_removed ||
-	    !wpas_valid_bss_ssid(wpa_s, cwork->bss, cwork->ssid)) {
+	    !wpas_valid_bss_ssid(wpa_s, cwork->bss, cwork->ssid) ||
+	    wpas_network_disabled(wpa_s, cwork->ssid)) {
 		wpa_dbg(wpa_s, MSG_DEBUG, "SME: BSS/SSID entry for authentication not valid anymore - drop connection attempt");
 		wpas_connect_work_done(wpa_s);
 		return;
@@ -616,6 +632,8 @@
 		radio_remove_works(wpa_s, "sme-connect", 0);
 	}
 
+	wpas_abort_ongoing_scan(wpa_s);
+
 	cwork = os_zalloc(sizeof(*cwork));
 	if (cwork == NULL)
 		return;
@@ -801,8 +819,22 @@
 #endif /* CONFIG_SAE */
 
 	if (data->auth.status_code != WLAN_STATUS_SUCCESS) {
-		wpa_dbg(wpa_s, MSG_DEBUG, "SME: Authentication failed (status "
-			"code %d)", data->auth.status_code);
+		char *ie_txt = NULL;
+
+		if (data->auth.ies && data->auth.ies_len) {
+			size_t buflen = 2 * data->auth.ies_len + 1;
+			ie_txt = os_malloc(buflen);
+			if (ie_txt) {
+				wpa_snprintf_hex(ie_txt, buflen, data->auth.ies,
+						 data->auth.ies_len);
+			}
+		}
+		wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_AUTH_REJECT MACSTR
+			" auth_type=%u auth_transaction=%u status_code=%u ie=%s",
+			MAC2STR(data->auth.peer), data->auth.auth_type,
+			data->auth.auth_transaction, data->auth.status_code,
+			ie_txt);
+		os_free(ie_txt);
 
 		if (data->auth.status_code !=
 		    WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG ||
diff --git a/wpa_supplicant/systemd/wpa_supplicant-nl80211.service.arg.in b/wpa_supplicant/systemd/wpa_supplicant-nl80211.service.arg.in
index bfdee25..03ac507 100644
--- a/wpa_supplicant/systemd/wpa_supplicant-nl80211.service.arg.in
+++ b/wpa_supplicant/systemd/wpa_supplicant-nl80211.service.arg.in
@@ -2,6 +2,8 @@
 Description=WPA supplicant daemon (interface- and nl80211 driver-specific version)
 Requires=sys-subsystem-net-devices-%i.device
 After=sys-subsystem-net-devices-%i.device
+Before=network.target
+Wants=network.target
 
 # NetworkManager users will probably want the dbus version instead.
 
diff --git a/wpa_supplicant/systemd/wpa_supplicant-wired.service.arg.in b/wpa_supplicant/systemd/wpa_supplicant-wired.service.arg.in
index 20ba0ad..c8a744d 100644
--- a/wpa_supplicant/systemd/wpa_supplicant-wired.service.arg.in
+++ b/wpa_supplicant/systemd/wpa_supplicant-wired.service.arg.in
@@ -2,6 +2,8 @@
 Description=WPA supplicant daemon (interface- and wired driver-specific version)
 Requires=sys-subsystem-net-devices-%i.device
 After=sys-subsystem-net-devices-%i.device
+Before=network.target
+Wants=network.target
 
 # NetworkManager users will probably want the dbus version instead.
 
diff --git a/wpa_supplicant/systemd/wpa_supplicant.service.arg.in b/wpa_supplicant/systemd/wpa_supplicant.service.arg.in
index 10e62bc..7788b38 100644
--- a/wpa_supplicant/systemd/wpa_supplicant.service.arg.in
+++ b/wpa_supplicant/systemd/wpa_supplicant.service.arg.in
@@ -2,6 +2,8 @@
 Description=WPA supplicant daemon (interface-specific version)
 Requires=sys-subsystem-net-devices-%i.device
 After=sys-subsystem-net-devices-%i.device
+Before=network.target
+Wants=network.target
 
 # NetworkManager users will probably want the dbus version instead.
 
diff --git a/wpa_supplicant/systemd/wpa_supplicant.service.in b/wpa_supplicant/systemd/wpa_supplicant.service.in
index 4351ad8..ea964ce 100644
--- a/wpa_supplicant/systemd/wpa_supplicant.service.in
+++ b/wpa_supplicant/systemd/wpa_supplicant.service.in
@@ -1,5 +1,7 @@
 [Unit]
 Description=WPA supplicant
+Before=network.target
+Wants=network.target
 
 [Service]
 Type=dbus
diff --git a/wpa_supplicant/tests/link_test.c b/wpa_supplicant/tests/link_test.c
deleted file mode 100644
index 3bfbed5..0000000
--- a/wpa_supplicant/tests/link_test.c
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * Dummy functions to allow link_test to be linked. The need for these
- * functions should be removed to allow IEEE 802.1X/EAPOL authenticator to
- * be built outside hostapd.
- */
-
-#include "includes.h"
-
-#include "common.h"
-
-
-struct hostapd_data;
-struct sta_info;
-struct rsn_pmksa_cache_entry;
-struct eapol_state_machine;
-struct hostapd_eap_user;
-struct hostapd_bss_config;
-struct hostapd_vlan;
-
-
-struct sta_info * ap_get_sta(struct hostapd_data *hapd, const u8 *sta)
-{
-	return NULL;
-}
-
-
-int ap_for_each_sta(struct hostapd_data *hapd,
-		    int (*cb)(struct hostapd_data *hapd, struct sta_info *sta,
-			      void *ctx),
-		    void *ctx)
-{
-	return 0;
-}
-
-
-void ap_sta_session_timeout(struct hostapd_data *hapd, struct sta_info *sta,
-			    u32 session_timeout)
-{
-}
-
-
-int ap_sta_bind_vlan(struct hostapd_data *hapd, struct sta_info *sta,
-		     int old_vlanid)
-{
-	return 0;
-}
-
-
-void rsn_preauth_finished(struct hostapd_data *hapd, struct sta_info *sta,
-			  int success)
-{
-}
-
-
-void rsn_preauth_send(struct hostapd_data *hapd, struct sta_info *sta,
-		      u8 *buf, size_t len)
-{
-}
-
-
-void accounting_sta_start(struct hostapd_data *hapd, struct sta_info *sta)
-{
-}
-
-
-void pmksa_cache_to_eapol_data(struct rsn_pmksa_cache_entry *entry,
-			       struct eapol_state_machine *eapol)
-{
-}
-
-
-const struct hostapd_eap_user *
-hostapd_get_eap_user(const struct hostapd_bss_config *conf, const u8 *identity,
-		     size_t identity_len, int phase2)
-{
-	return NULL;
-}
-
-
-const char * hostapd_get_vlan_id_ifname(struct hostapd_vlan *vlan, int vlan_id)
-{
-	return NULL;
-}
diff --git a/wpa_supplicant/tests/test_eap_sim_common.c b/wpa_supplicant/tests/test_eap_sim_common.c
deleted file mode 100644
index f60b182..0000000
--- a/wpa_supplicant/tests/test_eap_sim_common.c
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Test program for EAP-SIM PRF
- * Copyright (c) 2004-2006, Jouni Malinen <j@w1.fi>
- *
- * This software may be distributed under the terms of the BSD license.
- * See README for more details.
- */
-
-#include "eap_common/eap_sim_common.c"
-
-
-static int test_eap_sim_prf(void)
-{
-	/* http://csrc.nist.gov/encryption/dss/Examples-1024bit.pdf */
-	u8 xkey[] = {
-		0xbd, 0x02, 0x9b, 0xbe, 0x7f, 0x51, 0x96, 0x0b,
-		0xcf, 0x9e, 0xdb, 0x2b, 0x61, 0xf0, 0x6f, 0x0f,
-		0xeb, 0x5a, 0x38, 0xb6
-	};
-	u8 w[] = {
-		0x20, 0x70, 0xb3, 0x22, 0x3d, 0xba, 0x37, 0x2f,
-		0xde, 0x1c, 0x0f, 0xfc, 0x7b, 0x2e, 0x3b, 0x49,
-		0x8b, 0x26, 0x06, 0x14, 0x3c, 0x6c, 0x18, 0xba,
-		0xcb, 0x0f, 0x6c, 0x55, 0xba, 0xbb, 0x13, 0x78,
-		0x8e, 0x20, 0xd7, 0x37, 0xa3, 0x27, 0x51, 0x16
-	};
-	u8 buf[40];
-
-	printf("Testing EAP-SIM PRF (FIPS 186-2 + change notice 1)\n");
-	eap_sim_prf(xkey, buf, sizeof(buf));
-	if (memcmp(w, buf, sizeof(w)) != 0) {
-		printf("eap_sim_prf failed\n");
-		return 1;
-	}
-
-	return 0;
-}
-
-
-int main(int argc, char *argv[])
-{
-	int errors = 0;
-
-	errors += test_eap_sim_prf();
-
-	return errors;
-}
diff --git a/wpa_supplicant/tests/test_wpa.c b/wpa_supplicant/tests/test_wpa.c
deleted file mode 100644
index 39971f2..0000000
--- a/wpa_supplicant/tests/test_wpa.c
+++ /dev/null
@@ -1,369 +0,0 @@
-/*
- * Test program for combined WPA authenticator/supplicant
- * Copyright (c) 2006-2007, Jouni Malinen <j@w1.fi>
- *
- * This software may be distributed under the terms of the BSD license.
- * See README for more details.
- */
-
-#include "includes.h"
-
-#include "common.h"
-#include "eloop.h"
-#include "common/ieee802_11_defs.h"
-#include "../config.h"
-#include "rsn_supp/wpa.h"
-#include "rsn_supp/wpa_ie.h"
-#include "ap/wpa_auth.h"
-
-
-struct wpa {
-	u8 auth_addr[ETH_ALEN];
-	u8 supp_addr[ETH_ALEN];
-	u8 psk[PMK_LEN];
-
-	/* from authenticator */
-	u8 auth_eapol_dst[ETH_ALEN];
-	u8 *auth_eapol;
-	size_t auth_eapol_len;
-
-	/* from supplicant */
-	u8 *supp_eapol;
-	size_t supp_eapol_len;
-
-	struct wpa_sm *supp;
-	struct wpa_authenticator *auth_group;
-	struct wpa_state_machine *auth;
-
-	struct wpa_ssid ssid;
-	u8 supp_ie[80];
-	size_t supp_ie_len;
-};
-
-
-static int supp_get_bssid(void *ctx, u8 *bssid)
-{
-	struct wpa *wpa = ctx;
-	wpa_printf(MSG_DEBUG, "SUPP: %s", __func__);
-	os_memcpy(bssid, wpa->auth_addr, ETH_ALEN);
-	return 0;
-}
-
-
-static void supp_set_state(void *ctx, enum wpa_states state)
-{
-	wpa_printf(MSG_DEBUG, "SUPP: %s(state=%d)", __func__, state);
-}
-
-
-static void auth_eapol_rx(void *eloop_data, void *user_ctx)
-{
-	struct wpa *wpa = eloop_data;
-
-	wpa_printf(MSG_DEBUG, "AUTH: RX EAPOL frame");
-	wpa_receive(wpa->auth_group, wpa->auth, wpa->supp_eapol,
-		    wpa->supp_eapol_len);
-}
-
-
-static int supp_ether_send(void *ctx, const u8 *dest, u16 proto, const u8 *buf,
-			   size_t len)
-{
-	struct wpa *wpa = ctx;
-
-	wpa_printf(MSG_DEBUG, "SUPP: %s(dest=" MACSTR " proto=0x%04x "
-		   "len=%lu)",
-		   __func__, MAC2STR(dest), proto, (unsigned long) len);
-
-	os_free(wpa->supp_eapol);
-	wpa->supp_eapol = os_malloc(len);
-	if (wpa->supp_eapol == NULL)
-		return -1;
-	os_memcpy(wpa->supp_eapol, buf, len);
-	wpa->supp_eapol_len = len;
-	eloop_register_timeout(0, 0, auth_eapol_rx, wpa, NULL);
-
-	return 0;
-}
-
-
-static u8 * supp_alloc_eapol(void *ctx, u8 type, const void *data,
-			     u16 data_len, size_t *msg_len, void **data_pos)
-{
-	struct ieee802_1x_hdr *hdr;
-
-	wpa_printf(MSG_DEBUG, "SUPP: %s(type=%d data_len=%d)",
-		   __func__, type, data_len);
-
-	*msg_len = sizeof(*hdr) + data_len;
-	hdr = os_malloc(*msg_len);
-	if (hdr == NULL)
-		return NULL;
-
-	hdr->version = 2;
-	hdr->type = type;
-	hdr->length = host_to_be16(data_len);
-
-	if (data)
-		os_memcpy(hdr + 1, data, data_len);
-	else
-		os_memset(hdr + 1, 0, data_len);
-
-	if (data_pos)
-		*data_pos = hdr + 1;
-
-	return (u8 *) hdr;
-}
-
-
-static int supp_get_beacon_ie(void *ctx)
-{
-	struct wpa *wpa = ctx;
-	const u8 *ie;
-	size_t ielen;
-
-	wpa_printf(MSG_DEBUG, "SUPP: %s", __func__);
-
-	ie = wpa_auth_get_wpa_ie(wpa->auth_group, &ielen);
-	if (ie == NULL || ielen < 1)
-		return -1;
-	if (ie[0] == WLAN_EID_RSN)
-		return wpa_sm_set_ap_rsn_ie(wpa->supp, ie, 2 + ie[1]);
-	return wpa_sm_set_ap_wpa_ie(wpa->supp, ie, 2 + ie[1]);
-}
-
-
-static int supp_set_key(void *ctx, enum wpa_alg alg,
-			const u8 *addr, int key_idx, int set_tx,
-			const u8 *seq, size_t seq_len,
-			const u8 *key, size_t key_len)
-{
-	wpa_printf(MSG_DEBUG, "SUPP: %s(alg=%d addr=" MACSTR " key_idx=%d "
-		   "set_tx=%d)",
-		   __func__, alg, MAC2STR(addr), key_idx, set_tx);
-	wpa_hexdump(MSG_DEBUG, "SUPP: set_key - seq", seq, seq_len);
-	wpa_hexdump(MSG_DEBUG, "SUPP: set_key - key", key, key_len);
-	return 0;
-}
-
-
-static int supp_mlme_setprotection(void *ctx, const u8 *addr,
-				   int protection_type, int key_type)
-{
-	wpa_printf(MSG_DEBUG, "SUPP: %s(addr=" MACSTR " protection_type=%d "
-		   "key_type=%d)",
-		   __func__, MAC2STR(addr), protection_type, key_type);
-	return 0;
-}
-
-
-static void supp_cancel_auth_timeout(void *ctx)
-{
-	wpa_printf(MSG_DEBUG, "SUPP: %s", __func__);
-}
-
-
-static int supp_init(struct wpa *wpa)
-{
-	struct wpa_sm_ctx *ctx = os_zalloc(sizeof(*ctx));
-	if (ctx == NULL)
-		return -1;
-
-	ctx->ctx = wpa;
-	ctx->msg_ctx = wpa;
-	ctx->set_state = supp_set_state;
-	ctx->get_bssid = supp_get_bssid;
-	ctx->ether_send = supp_ether_send;
-	ctx->get_beacon_ie = supp_get_beacon_ie;
-	ctx->alloc_eapol = supp_alloc_eapol;
-	ctx->set_key = supp_set_key;
-	ctx->mlme_setprotection = supp_mlme_setprotection;
-	ctx->cancel_auth_timeout = supp_cancel_auth_timeout;
-	wpa->supp = wpa_sm_init(ctx);
-	if (wpa->supp == NULL) {
-		wpa_printf(MSG_DEBUG, "SUPP: wpa_sm_init() failed");
-		return -1;
-	}
-
-	wpa_sm_set_own_addr(wpa->supp, wpa->supp_addr);
-	wpa_sm_set_param(wpa->supp, WPA_PARAM_RSN_ENABLED, 1);
-	wpa_sm_set_param(wpa->supp, WPA_PARAM_PROTO, WPA_PROTO_RSN);
-	wpa_sm_set_param(wpa->supp, WPA_PARAM_PAIRWISE, WPA_CIPHER_CCMP);
-	wpa_sm_set_param(wpa->supp, WPA_PARAM_GROUP, WPA_CIPHER_CCMP);
-	wpa_sm_set_param(wpa->supp, WPA_PARAM_KEY_MGMT, WPA_KEY_MGMT_PSK);
-	wpa_sm_set_pmk(wpa->supp, wpa->psk, PMK_LEN);
-
-	wpa->supp_ie_len = sizeof(wpa->supp_ie);
-	if (wpa_sm_set_assoc_wpa_ie_default(wpa->supp, wpa->supp_ie,
-					    &wpa->supp_ie_len) < 0) {
-		wpa_printf(MSG_DEBUG, "SUPP: wpa_sm_set_assoc_wpa_ie_default()"
-			   " failed");
-		return -1;
-	}
-
-	wpa_sm_notify_assoc(wpa->supp, wpa->auth_addr);
-
-	return 0;
-}
-
-
-static void auth_logger(void *ctx, const u8 *addr, logger_level level,
-			const char *txt)
-{
-	if (addr)
-		wpa_printf(MSG_DEBUG, "AUTH: " MACSTR " - %s",
-			   MAC2STR(addr), txt);
-	else
-		wpa_printf(MSG_DEBUG, "AUTH: %s", txt);
-}
-
-
-static void supp_eapol_rx(void *eloop_data, void *user_ctx)
-{
-	struct wpa *wpa = eloop_data;
-
-	wpa_printf(MSG_DEBUG, "SUPP: RX EAPOL frame");
-	wpa_sm_rx_eapol(wpa->supp, wpa->auth_addr, wpa->auth_eapol,
-			wpa->auth_eapol_len);
-}
-
-
-static int auth_send_eapol(void *ctx, const u8 *addr, const u8 *data,
-			   size_t data_len, int encrypt)
-{
-	struct wpa *wpa = ctx;
-
-	wpa_printf(MSG_DEBUG, "AUTH: %s(addr=" MACSTR " data_len=%lu "
-		   "encrypt=%d)",
-		   __func__, MAC2STR(addr), (unsigned long) data_len, encrypt);
-
-	os_free(wpa->auth_eapol);
-	wpa->auth_eapol = os_malloc(data_len);
-	if (wpa->auth_eapol == NULL)
-		return -1;
-	os_memcpy(wpa->auth_eapol_dst, addr, ETH_ALEN);
-	os_memcpy(wpa->auth_eapol, data, data_len);
-	wpa->auth_eapol_len = data_len;
-	eloop_register_timeout(0, 0, supp_eapol_rx, wpa, NULL);
-
-	return 0;
-}
-
-
-static const u8 * auth_get_psk(void *ctx, const u8 *addr, const u8 *prev_psk)
-{
-	struct wpa *wpa = ctx;
-	wpa_printf(MSG_DEBUG, "AUTH: %s (addr=" MACSTR " prev_psk=%p)",
-		   __func__, MAC2STR(addr), prev_psk);
-	if (prev_psk)
-		return NULL;
-	return wpa->psk;
-}
-
-
-static int auth_init_group(struct wpa *wpa)
-{
-	struct wpa_auth_config conf;
-	struct wpa_auth_callbacks cb;
-
-	wpa_printf(MSG_DEBUG, "AUTH: Initializing group state machine");
-
-	os_memset(&conf, 0, sizeof(conf));
-	conf.wpa = 2;
-	conf.wpa_key_mgmt = WPA_KEY_MGMT_PSK;
-	conf.wpa_pairwise = WPA_CIPHER_CCMP;
-	conf.rsn_pairwise = WPA_CIPHER_CCMP;
-	conf.wpa_group = WPA_CIPHER_CCMP;
-	conf.eapol_version = 2;
-
-	os_memset(&cb, 0, sizeof(cb));
-	cb.ctx = wpa;
-	cb.logger = auth_logger;
-	cb.send_eapol = auth_send_eapol;
-	cb.get_psk = auth_get_psk;
-
-	wpa->auth_group = wpa_init(wpa->auth_addr, &conf, &cb);
-	if (wpa->auth_group == NULL) {
-		wpa_printf(MSG_DEBUG, "AUTH: wpa_init() failed");
-		return -1;
-	}
-
-	return 0;
-}
-
-
-static int auth_init(struct wpa *wpa)
-{
-	wpa->auth = wpa_auth_sta_init(wpa->auth_group, wpa->supp_addr, NULL);
-	if (wpa->auth == NULL) {
-		wpa_printf(MSG_DEBUG, "AUTH: wpa_auth_sta_init() failed");
-		return -1;
-	}
-
-	if (wpa_validate_wpa_ie(wpa->auth_group, wpa->auth, wpa->supp_ie,
-				wpa->supp_ie_len, NULL, 0) != WPA_IE_OK) {
-		wpa_printf(MSG_DEBUG, "AUTH: wpa_validate_wpa_ie() failed");
-		return -1;
-	}
-
-	wpa_auth_sm_event(wpa->auth, WPA_ASSOC);
-
-	wpa_auth_sta_associated(wpa->auth_group, wpa->auth);
-
-	return 0;
-}
-
-
-static void deinit(struct wpa *wpa)
-{
-	wpa_auth_sta_deinit(wpa->auth);
-	wpa_sm_deinit(wpa->supp);
-	wpa_deinit(wpa->auth_group);
-	os_free(wpa->auth_eapol);
-	wpa->auth_eapol = NULL;
-	os_free(wpa->supp_eapol);
-	wpa->supp_eapol = NULL;
-}
-
-
-int main(int argc, char *argv[])
-{
-	struct wpa wpa;
-
-	if (os_program_init())
-		return -1;
-
-	os_memset(&wpa, 0, sizeof(wpa));
-	os_memset(wpa.auth_addr, 0x12, ETH_ALEN);
-	os_memset(wpa.supp_addr, 0x32, ETH_ALEN);
-	os_memset(wpa.psk, 0x44, PMK_LEN);
-
-	wpa_debug_level = 0;
-	wpa_debug_show_keys = 1;
-
-	if (eloop_init()) {
-		wpa_printf(MSG_ERROR, "Failed to initialize event loop");
-		return -1;
-	}
-
-	if (auth_init_group(&wpa) < 0)
-		return -1;
-
-	if (supp_init(&wpa) < 0)
-		return -1;
-
-	if (auth_init(&wpa) < 0)
-		return -1;
-
-	wpa_printf(MSG_DEBUG, "Starting eloop");
-	eloop_run();
-	wpa_printf(MSG_DEBUG, "eloop done");
-
-	deinit(&wpa);
-
-	eloop_destroy();
-
-	os_program_deinit();
-
-	return 0;
-}
diff --git a/wpa_supplicant/wnm_sta.c b/wpa_supplicant/wnm_sta.c
index 7d79499..e1596cb 100644
--- a/wpa_supplicant/wnm_sta.c
+++ b/wpa_supplicant/wnm_sta.c
@@ -37,12 +37,14 @@
 
 /* set the TFS IE to driver */
 static int ieee80211_11_set_tfs_ie(struct wpa_supplicant *wpa_s,
-				   const u8 *addr, u8 *buf, u16 *buf_len,
+				   const u8 *addr, const u8 *buf, u16 buf_len,
 				   enum wnm_oper oper)
 {
+	u16 len = buf_len;
+
 	wpa_printf(MSG_DEBUG, "%s: TFS set operation %d", __func__, oper);
 
-	return wpa_drv_wnm_oper(wpa_s, oper, addr, buf, buf_len);
+	return wpa_drv_wnm_oper(wpa_s, oper, addr, (u8 *) buf, &len);
 }
 
 
@@ -137,6 +139,8 @@
 	if (res < 0)
 		wpa_printf(MSG_DEBUG, "Failed to send WNM-Sleep Request "
 			   "(action=%d, intval=%d)", action, intval);
+	else
+		wpa_s->wnmsleep_used = 1;
 
 	os_free(wnmsleep_ie);
 	os_free(wnmtfs_ie);
@@ -147,8 +151,8 @@
 
 
 static void wnm_sleep_mode_enter_success(struct wpa_supplicant *wpa_s,
-					 u8 *tfsresp_ie_start,
-					 u8 *tfsresp_ie_end)
+					 const u8 *tfsresp_ie_start,
+					 const u8 *tfsresp_ie_end)
 {
 	wpa_drv_wnm_oper(wpa_s, WNM_SLEEP_ENTER_CONFIRM,
 			 wpa_s->bssid, NULL, NULL);
@@ -164,7 +168,7 @@
 		/* pass the TFS Resp IE(s) to driver for processing */
 		if (ieee80211_11_set_tfs_ie(wpa_s, wpa_s->bssid,
 					    tfsresp_ie_start,
-					    &tfsresp_ie_len,
+					    tfsresp_ie_len,
 					    WNM_SLEEP_TFS_RESP_IE_SET))
 			wpa_printf(MSG_DEBUG, "WNM: Fail to set TFS Resp IE");
 	}
@@ -193,8 +197,8 @@
 		return;
 	}
 
-	while (ptr + 1 < end) {
-		if (ptr + 2 + ptr[1] > end) {
+	while (end - ptr > 1) {
+		if (2 + ptr[1] > end - ptr) {
 			wpa_printf(MSG_DEBUG, "WNM: Invalid Key Data element "
 				   "length");
 			if (end > ptr) {
@@ -245,14 +249,20 @@
 	 * Action [1] | Dialog Token [1] | Key Data Len [2] | Key Data |
 	 * WNM-Sleep Mode IE | TFS Response IE
 	 */
-	u8 *pos = (u8 *) frm; /* point to payload after the action field */
+	const u8 *pos = frm; /* point to payload after the action field */
 	u16 key_len_total;
 	struct wnm_sleep_element *wnmsleep_ie = NULL;
 	/* multiple TFS Resp IE (assuming consecutive) */
-	u8 *tfsresp_ie_start = NULL;
-	u8 *tfsresp_ie_end = NULL;
+	const u8 *tfsresp_ie_start = NULL;
+	const u8 *tfsresp_ie_end = NULL;
 	size_t left;
 
+	if (!wpa_s->wnmsleep_used) {
+		wpa_printf(MSG_DEBUG,
+			   "WNM: Ignore WNM-Sleep Mode Response frame since WNM-Sleep Mode has not been used in this association");
+		return;
+	}
+
 	if (len < 3)
 		return;
 	key_len_total = WPA_GET_LE16(frm + 1);
@@ -265,14 +275,14 @@
 		return;
 	}
 	pos += 3 + key_len_total;
-	while (pos - frm < len) {
+	while (pos - frm + 1 < len) {
 		u8 ie_len = *(pos + 1);
-		if (pos + 2 + ie_len > frm + len) {
+		if (2 + ie_len > frm + len - pos) {
 			wpa_printf(MSG_INFO, "WNM: Invalid IE len %u", ie_len);
 			break;
 		}
 		wpa_hexdump(MSG_DEBUG, "WNM: Element", pos, 2 + ie_len);
-		if (*pos == WLAN_EID_WNMSLEEP)
+		if (*pos == WLAN_EID_WNMSLEEP && ie_len >= 4)
 			wnmsleep_ie = (struct wnm_sleep_element *) pos;
 		else if (*pos == WLAN_EID_TFS_RESP) {
 			if (!tfsresp_ie_start)
@@ -643,6 +653,7 @@
 	if (bss == wpa_s->current_bss) {
 		wpa_printf(MSG_DEBUG,
 			   "WNM: Already associated with the preferred candidate");
+		wnm_deallocate_memory(wpa_s);
 		return 1;
 	}
 
@@ -796,7 +807,7 @@
 	unsigned int beacon_int;
 	u8 valid_int;
 
-	if (pos + 5 > end)
+	if (end - pos < 5)
 		return;
 
 	if (wpa_s->current_bss)
@@ -819,7 +830,7 @@
 	pos += 5;
 
 	if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_BSS_TERMINATION_INCLUDED) {
-		if (pos + 12 > end) {
+		if (end - pos < 12) {
 			wpa_printf(MSG_DEBUG, "WNM: Too short BSS TM Request");
 			return;
 		}
@@ -830,7 +841,7 @@
 	if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_ESS_DISASSOC_IMMINENT) {
 		char url[256];
 
-		if (pos + 1 > end || pos + 1 + pos[0] > end) {
+		if (end - pos < 1 || 1 + pos[0] > end - pos) {
 			wpa_printf(MSG_DEBUG, "WNM: Invalid BSS Transition "
 				   "Management Request (URL)");
 			return;
@@ -866,7 +877,7 @@
 		if (wpa_s->wnm_neighbor_report_elements == NULL)
 			return;
 
-		while (pos + 2 <= end &&
+		while (end - pos >= 2 &&
 		       wpa_s->wnm_num_neighbor_report < WNM_MAX_NEIGHBOR_REPORT)
 		{
 			u8 tag = *pos++;
@@ -874,7 +885,7 @@
 
 			wpa_printf(MSG_DEBUG, "WNM: Neighbor report tag %u",
 				   tag);
-			if (pos + len > end) {
+			if (len > end - pos) {
 				wpa_printf(MSG_DEBUG, "WNM: Truncated request");
 				return;
 			}
@@ -977,7 +988,7 @@
 	pos = data;
 	end = data + len;
 
-	while (pos + 1 < end) {
+	while (end - pos > 1) {
 		ie = *pos++;
 		ie_len = *pos++;
 		wpa_printf(MSG_DEBUG, "WNM: WFA subelement %u len %u",
@@ -1015,7 +1026,7 @@
 				url = NULL;
 				osu_method = 1;
 			} else {
-				if (pos + url_len + 1 > ie_end) {
+				if (url_len + 1 > ie_end - pos) {
 					wpa_printf(MSG_DEBUG, "WNM: Not enough room for Server URL (len=%u) and Server Method (left %d)",
 						   url_len,
 						   (int) (ie_end - pos));
@@ -1054,7 +1065,7 @@
 				   "Imminent - Reason Code %u   "
 				   "Re-Auth Delay %u  URL Length %u",
 				   code, reauth_delay, url_len);
-			if (pos + url_len > ie_end)
+			if (url_len > ie_end - pos)
 				break;
 			url = os_malloc(url_len + 1);
 			if (url == NULL)
diff --git a/wpa_supplicant/wpa_cli.c b/wpa_supplicant/wpa_cli.c
index c5d8333..275bf39 100644
--- a/wpa_supplicant/wpa_cli.c
+++ b/wpa_supplicant/wpa_cli.c
@@ -66,6 +66,12 @@
 "OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n"
 "\n";
 
+#define VENDOR_ELEM_FRAME_ID \
+	"  0: Probe Req (P2P), 1: Probe Resp (P2P) , 2: Probe Resp (GO), " \
+	"3: Beacon (GO), 4: PD Req, 5: PD Resp, 6: GO Neg Req, " \
+	"7: GO Neg Resp, 8: GO Neg Conf, 9: Inv Req, 10: Inv Resp, " \
+	"11: Assoc Req (P2P), 12: Assoc Resp (P2P)"
+
 static struct wpa_ctrl *ctrl_conn;
 static struct wpa_ctrl *mon_conn;
 static int wpa_cli_quit = 0;
@@ -76,6 +82,7 @@
 #define CONFIG_CTRL_IFACE_DIR "/var/run/wpa_supplicant"
 #endif /* CONFIG_CTRL_IFACE_DIR */
 static const char *ctrl_iface_dir = CONFIG_CTRL_IFACE_DIR;
+static const char *client_socket_dir = NULL;
 static char *ctrl_ifname = NULL;
 static const char *pid_file = NULL;
 static const char *action_file = NULL;
@@ -107,7 +114,9 @@
 {
 	printf("wpa_cli [-p<path to ctrl sockets>] [-i<ifname>] [-hvB] "
 	       "[-a<action file>] \\\n"
-	       "        [-P<pid file>] [-g<global ctrl>] [-G<ping interval>]  "
+	       "        [-P<pid file>] [-g<global ctrl>] [-G<ping interval>] "
+	       "\\\n"
+	       "        [-s<wpa_client_socket_file_path>] "
 	       "[command..]\n"
 	       "  -h = help (show this usage text)\n"
 	       "  -v = shown version information\n"
@@ -330,6 +339,13 @@
 	}
 #endif /* ANDROID */
 
+	if (client_socket_dir && client_socket_dir[0] &&
+	    access(client_socket_dir, F_OK) < 0) {
+		perror(client_socket_dir);
+		os_free(cfile);
+		return -1;
+	}
+
 	if (cfile == NULL) {
 		flen = os_strlen(ctrl_iface_dir) + os_strlen(ifname) + 2;
 		cfile = os_malloc(flen);
@@ -343,14 +359,14 @@
 		}
 	}
 
-	ctrl_conn = wpa_ctrl_open(cfile);
+	ctrl_conn = wpa_ctrl_open2(cfile, client_socket_dir);
 	if (ctrl_conn == NULL) {
 		os_free(cfile);
 		return -1;
 	}
 
 	if (attach && interactive)
-		mon_conn = wpa_ctrl_open(cfile);
+		mon_conn = wpa_ctrl_open2(cfile, client_socket_dir);
 	else
 		mon_conn = NULL;
 	os_free(cfile);
@@ -502,6 +518,10 @@
 		return wpa_ctrl_command(ctrl, "STATUS-WPS");
 	if (argc > 0 && os_strcmp(argv[0], "driver") == 0)
 		return wpa_ctrl_command(ctrl, "STATUS-DRIVER");
+#ifdef ANDROID
+	if (argc > 0 && os_strcmp(argv[0], "no_events") == 0)
+		return wpa_ctrl_command(ctrl, "STATUS-NO_EVENTS");
+#endif /* ANDROID */
 	return wpa_ctrl_command(ctrl, "STATUS");
 }
 
@@ -618,6 +638,7 @@
 		"eapol_version", "ap_scan", "bgscan",
 #ifdef CONFIG_MESH
 		"user_mpm", "max_peer_links", "mesh_max_inactivity",
+		"dot11RSNASAERetransPeriod",
 #endif /* CONFIG_MESH */
 		"disable_scan_offload", "fast_reauth", "opensc_engine_path",
 		"pkcs11_engine_path", "pkcs11_module_path", "openssl_ciphers",
@@ -1533,7 +1554,7 @@
 	"ssid", "scan_ssid", "bssid", "bssid_blacklist",
 	"bssid_whitelist", "psk", "proto", "key_mgmt",
 	"bg_scan_period", "pairwise", "group", "auth_alg", "scan_freq",
-	"freq_list",
+	"freq_list", "max_oper_chwidth",
 #ifdef IEEE8021X_EAPOL
 	"eap", "identity", "anonymous_identity", "password", "ca_cert",
 	"ca_path", "client_cert", "private_key", "private_key_passwd",
@@ -1749,6 +1770,13 @@
 }
 
 
+static int wpa_cli_cmd_abort_scan(struct wpa_ctrl *ctrl, int argc,
+				  char *argv[])
+{
+	return wpa_ctrl_command(ctrl, "ABORT_SCAN");
+}
+
+
 static int wpa_cli_cmd_bss(struct wpa_ctrl *ctrl, int argc, char *argv[])
 {
 	return wpa_cli_cmd(ctrl, "BSS", 1, argc, argv);
@@ -1851,14 +1879,15 @@
 
 	/*
 	 * INTERFACE_ADD <ifname>TAB<confname>TAB<driver>TAB<ctrl_interface>TAB
-	 * <driver_param>TAB<bridge_name>[TAB<create>]
+	 * <driver_param>TAB<bridge_name>[TAB<create>[TAB<type>]]
 	 */
 	res = os_snprintf(cmd, sizeof(cmd),
-			  "INTERFACE_ADD %s\t%s\t%s\t%s\t%s\t%s\t%s",
+			  "INTERFACE_ADD %s\t%s\t%s\t%s\t%s\t%s\t%s\t%s",
 			  argv[0],
 			  argc > 1 ? argv[1] : "", argc > 2 ? argv[2] : "",
 			  argc > 3 ? argv[3] : "", argc > 4 ? argv[4] : "",
-			  argc > 5 ? argv[5] : "", argc > 6 ? argv[6] : "");
+			  argc > 5 ? argv[5] : "", argc > 6 ? argv[6] : "",
+			  argc > 7 ? argv[7] : "");
 	if (os_snprintf_error(sizeof(cmd), res))
 		return -1;
 	cmd[sizeof(cmd) - 1] = '\0';
@@ -2462,6 +2491,27 @@
 	return wpa_cli_cmd(ctrl, "P2P_REMOVE_CLIENT", 1, argc, argv);
 }
 
+
+static int wpa_cli_cmd_vendor_elem_add(struct wpa_ctrl *ctrl, int argc,
+				       char *argv[])
+{
+	return wpa_cli_cmd(ctrl, "VENDOR_ELEM_ADD", 2, argc, argv);
+}
+
+
+static int wpa_cli_cmd_vendor_elem_get(struct wpa_ctrl *ctrl, int argc,
+				       char *argv[])
+{
+	return wpa_cli_cmd(ctrl, "VENDOR_ELEM_GET", 1, argc, argv);
+}
+
+
+static int wpa_cli_cmd_vendor_elem_remove(struct wpa_ctrl *ctrl, int argc,
+					  char *argv[])
+{
+	return wpa_cli_cmd(ctrl, "VENDOR_ELEM_REMOVE", 2, argc, argv);
+}
+
 #endif /* CONFIG_P2P */
 
 #ifdef CONFIG_WIFI_DISPLAY
@@ -2801,6 +2851,13 @@
 }
 
 
+static int wpa_cli_cmd_get_pref_freq_list(struct wpa_ctrl *ctrl, int argc,
+					  char *argv[])
+{
+	return wpa_cli_cmd(ctrl, "GET_PREF_FREQ_LIST", 1, argc, argv);
+}
+
+
 enum wpa_cli_cmd_flags {
 	cli_cmd_flag_none		= 0x00,
 	cli_cmd_flag_sensitive		= 0x01
@@ -2979,6 +3036,9 @@
 	{ "scan_results", wpa_cli_cmd_scan_results, NULL,
 	  cli_cmd_flag_none,
 	  "= get latest scan results" },
+	{ "abort_scan", wpa_cli_cmd_abort_scan, NULL,
+	  cli_cmd_flag_none,
+	  "= request ongoing scan to be aborted" },
 	{ "bss", wpa_cli_cmd_bss, wpa_cli_complete_bss,
 	  cli_cmd_flag_none,
 	  "<<idx> | <bssid>> = get detailed scan result info" },
@@ -2995,8 +3055,10 @@
 	{ "interface_add", wpa_cli_cmd_interface_add, NULL,
 	  cli_cmd_flag_none,
 	  "<ifname> <confname> <driver> <ctrl_interface> <driver_param>\n"
-	  "  <bridge_name> = adds new interface, all parameters but <ifname>\n"
-	  "  are optional" },
+	  "  <bridge_name> <create> <type> = adds new interface, all "
+	  "parameters but\n"
+	  "  <ifname> are optional. Supported types are station ('sta') and "
+	  "AP ('ap')" },
 	{ "interface_remove", wpa_cli_cmd_interface_remove, NULL,
 	  cli_cmd_flag_none,
 	  "<ifname> = removes the interface" },
@@ -3226,6 +3288,18 @@
 	{ "p2p_remove_client", wpa_cli_cmd_p2p_remove_client,
 	  wpa_cli_complete_p2p_peer, cli_cmd_flag_none,
 	  "<address|iface=address> = remove a peer from all groups" },
+	{ "vendor_elem_add", wpa_cli_cmd_vendor_elem_add, NULL,
+	  cli_cmd_flag_none,
+	  "<frame id> <hexdump of elem(s)> = add vendor specific IEs to frame(s)\n"
+	  VENDOR_ELEM_FRAME_ID },
+	{ "vendor_elem_get", wpa_cli_cmd_vendor_elem_get, NULL,
+	  cli_cmd_flag_none,
+	  "<frame id> = get vendor specific IE(s) to frame(s)\n"
+	  VENDOR_ELEM_FRAME_ID },
+	{ "vendor_elem_remove", wpa_cli_cmd_vendor_elem_remove, NULL,
+	  cli_cmd_flag_none,
+	  "<frame id> <hexdump of elem(s)> = remove vendor specific IE(s) in frame(s)\n"
+	  VENDOR_ELEM_FRAME_ID },
 #endif /* CONFIG_P2P */
 #ifdef CONFIG_WIFI_DISPLAY
 	{ "wfd_subelem_set", wpa_cli_cmd_wfd_subelem_set, NULL,
@@ -3355,6 +3429,9 @@
 	  "<scan|sched|pno|all> enable=<0/1> [addr=mac-address "
 	  "mask=mac-address-mask] = scan MAC randomization"
 	},
+	{ "get_pref_freq_list", wpa_cli_cmd_get_pref_freq_list, NULL,
+	  cli_cmd_flag_none,
+	  "<interface type> = retrieve preferred freq list for the specified interface type" },
 	{ NULL, NULL, NULL, cli_cmd_flag_none, NULL }
 };
 
@@ -3585,6 +3662,9 @@
 	const char *ifname = ctrl_ifname;
 	char ifname_buf[100];
 
+	if (eloop_terminated())
+		return;
+
 	pos = msg;
 	if (os_strncmp(pos, "IFNAME=", 7) == 0) {
 		const char *end;
@@ -4166,18 +4246,17 @@
 {
 	char *ifname = NULL;
 
+#ifdef ANDROID
+	char ifprop[PROPERTY_VALUE_MAX];
+	if (property_get("wifi.interface", ifprop, NULL) != 0) {
+		ifname = os_strdup(ifprop);
+		printf("Using interface '%s'\n", ifname ? ifname : "N/A");
+	}
+#else /* ANDROID */
 #ifdef CONFIG_CTRL_IFACE_UNIX
 	struct dirent *dent;
 	DIR *dir = opendir(ctrl_iface_dir);
 	if (!dir) {
-#ifdef ANDROID
-		char ifprop[PROPERTY_VALUE_MAX];
-		if (property_get("wifi.interface", ifprop, NULL) != 0) {
-			ifname = os_strdup(ifprop);
-			printf("Using interface '%s'\n", ifname);
-			return ifname;
-		}
-#endif /* ANDROID */
 		return NULL;
 	}
 	while ((dent = readdir(dir))) {
@@ -4221,6 +4300,7 @@
 	}
 	wpa_ctrl_close(ctrl);
 #endif /* CONFIG_CTRL_IFACE_NAMED_PIPE */
+#endif /* ANDROID */
 
 	return ifname;
 }
@@ -4237,7 +4317,7 @@
 		return -1;
 
 	for (;;) {
-		c = getopt(argc, argv, "a:Bg:G:hi:p:P:v");
+		c = getopt(argc, argv, "a:Bg:G:hi:p:P:s:v");
 		if (c < 0)
 			break;
 		switch (c) {
@@ -4269,6 +4349,9 @@
 		case 'P':
 			pid_file = optarg;
 			break;
+		case 's':
+			client_socket_dir = optarg;
+			break;
 		default:
 			usage();
 			return -1;
diff --git a/wpa_supplicant/wpa_gui-qt4/wpagui.cpp b/wpa_supplicant/wpa_gui-qt4/wpagui.cpp
index 3bd3a9c..a0aa05e 100644
--- a/wpa_supplicant/wpa_gui-qt4/wpagui.cpp
+++ b/wpa_supplicant/wpa_gui-qt4/wpagui.cpp
@@ -639,7 +639,7 @@
 
 void WpaGui::updateNetworks()
 {
-	char buf[2048], *start, *end, *id, *ssid, *bssid, *flags;
+	char buf[4096], *start, *end, *id, *ssid, *bssid, *flags;
 	size_t len;
 	int first_active = -1;
 	int was_selected = -1;
diff --git a/wpa_supplicant/wpa_priv.c b/wpa_supplicant/wpa_priv.c
index 4611a1d..850ec40 100644
--- a/wpa_supplicant/wpa_priv.c
+++ b/wpa_supplicant/wpa_priv.c
@@ -31,6 +31,7 @@
 
 	const struct wpa_driver_ops *driver;
 	void *drv_priv;
+	void *drv_global_priv;
 	struct sockaddr_un drv_addr;
 	int wpas_registered;
 
@@ -48,6 +49,10 @@
 		if (iface->driver->deinit)
 			iface->driver->deinit(iface->drv_priv);
 		iface->drv_priv = NULL;
+		if (iface->drv_global_priv) {
+			iface->driver->global_deinit(iface->drv_global_priv);
+			iface->drv_global_priv = NULL;
+		}
 		iface->wpas_registered = 0;
 	}
 
@@ -58,10 +63,24 @@
 		iface->l2 = NULL;
 	}
 
-	if (iface->driver->init == NULL)
+	if (iface->driver->init2) {
+		if (iface->driver->global_init) {
+			iface->drv_global_priv = iface->driver->global_init();
+			if (!iface->drv_global_priv) {
+				wpa_printf(MSG_INFO,
+					   "Failed to initialize driver global context");
+				return;
+			}
+		} else {
+			iface->drv_global_priv = NULL;
+		}
+		iface->drv_priv = iface->driver->init2(iface, iface->ifname,
+						       iface->drv_global_priv);
+	} else if (iface->driver->init) {
+		iface->drv_priv = iface->driver->init(iface, iface->ifname);
+	} else {
 		return;
-
-	iface->drv_priv = iface->driver->init(iface, iface->ifname);
+	}
 	if (iface->drv_priv == NULL) {
 		wpa_printf(MSG_DEBUG, "Failed to initialize driver wrapper");
 		return;
@@ -87,6 +106,10 @@
 		if (iface->driver->deinit)
 			iface->driver->deinit(iface->drv_priv);
 		iface->drv_priv = NULL;
+		if (iface->drv_global_priv) {
+			iface->driver->global_deinit(iface->drv_global_priv);
+			iface->drv_global_priv = NULL;
+		}
 		iface->wpas_registered = 0;
 	}
 }
@@ -172,6 +195,58 @@
 }
 
 
+static void wpa_priv_cmd_authenticate(struct wpa_priv_interface *iface,
+				      void *buf, size_t len)
+{
+	struct wpa_driver_auth_params params;
+	struct privsep_cmd_authenticate *auth;
+	int res, i;
+
+	if (iface->drv_priv == NULL || iface->driver->authenticate == NULL)
+		return;
+
+	if (len < sizeof(*auth)) {
+		wpa_printf(MSG_DEBUG, "Invalid authentication request");
+		return;
+	}
+
+	auth = buf;
+	if (sizeof(*auth) + auth->ie_len + auth->sae_data_len > len) {
+		wpa_printf(MSG_DEBUG, "Authentication request overflow");
+		return;
+	}
+
+	os_memset(&params, 0, sizeof(params));
+	params.freq = auth->freq;
+	params.bssid = auth->bssid;
+	params.ssid = auth->ssid;
+	if (auth->ssid_len > SSID_MAX_LEN)
+		return;
+	params.ssid_len = auth->ssid_len;
+	params.auth_alg = auth->auth_alg;
+	for (i = 0; i < 4; i++) {
+		if (auth->wep_key_len[i]) {
+			params.wep_key[i] = auth->wep_key[i];
+			params.wep_key_len[i] = auth->wep_key_len[i];
+		}
+	}
+	params.wep_tx_keyidx = auth->wep_tx_keyidx;
+	params.local_state_change = auth->local_state_change;
+	params.p2p = auth->p2p;
+	if (auth->ie_len) {
+		params.ie = (u8 *) (auth + 1);
+		params.ie_len = auth->ie_len;
+	}
+	if (auth->sae_data_len) {
+		params.sae_data = ((u8 *) (auth + 1)) + auth->ie_len;
+		params.sae_data_len = auth->sae_data_len;
+	}
+
+	res = iface->driver->authenticate(iface->drv_priv, &params);
+	wpa_printf(MSG_DEBUG, "drv->authenticate: res=%d", res);
+}
+
+
 static void wpa_priv_cmd_associate(struct wpa_priv_interface *iface,
 				   void *buf, size_t len)
 {
@@ -307,6 +382,10 @@
 	    iface->driver->get_capa(iface->drv_priv, &capa) < 0)
 		goto fail;
 
+	/* For now, no support for passing extended_capa pointers */
+	capa.extended_capa = NULL;
+	capa.extended_capa_mask = NULL;
+	capa.extended_capa_len = 0;
 	sendto(iface->fd, &capa, sizeof(capa), 0, (struct sockaddr *) from,
 	       sizeof(*from));
 	return;
@@ -356,7 +435,8 @@
 	}
 
 	proto = reg_cmd[0];
-	if (proto != ETH_P_EAPOL && proto != ETH_P_RSN_PREAUTH) {
+	if (proto != ETH_P_EAPOL && proto != ETH_P_RSN_PREAUTH &&
+	    proto != ETH_P_80211_ENCAP) {
 		wpa_printf(MSG_DEBUG, "Refused l2_packet connection for "
 			   "ethertype 0x%x", proto);
 		return;
@@ -529,6 +609,9 @@
 		pos[cmd_len] = '\0';
 		wpa_priv_cmd_set_country(iface, pos);
 		break;
+	case PRIVSEP_CMD_AUTHENTICATE:
+		wpa_priv_cmd_authenticate(iface, cmd_buf, cmd_len);
+		break;
 	}
 }
 
@@ -698,6 +781,36 @@
 }
 
 
+static void wpa_priv_send_auth(struct wpa_priv_interface *iface,
+			       union wpa_event_data *data)
+{
+	size_t buflen = sizeof(struct privsep_event_auth) + data->auth.ies_len;
+	struct privsep_event_auth *auth;
+	u8 *buf, *pos;
+
+	buf = os_malloc(buflen);
+	if (buf == NULL)
+		return;
+
+	auth = (struct privsep_event_auth *) buf;
+	pos = (u8 *) (auth + 1);
+
+	os_memcpy(auth->peer, data->auth.peer, ETH_ALEN);
+	os_memcpy(auth->bssid, data->auth.bssid, ETH_ALEN);
+	auth->auth_type = data->auth.auth_type;
+	auth->auth_transaction = data->auth.auth_transaction;
+	auth->status_code = data->auth.status_code;
+	if (data->auth.ies) {
+		os_memcpy(pos, data->auth.ies, data->auth.ies_len);
+		auth->ies_len = data->auth.ies_len;
+	}
+
+	wpa_priv_send_event(iface, PRIVSEP_EVENT_AUTH, buf, buflen);
+
+	os_free(buf);
+}
+
+
 static void wpa_priv_send_assoc(struct wpa_priv_interface *iface, int event,
 				union wpa_event_data *data)
 {
@@ -851,6 +964,10 @@
 				    &data->michael_mic_failure.unicast,
 				    sizeof(int));
 		break;
+	case EVENT_SCAN_STARTED:
+		wpa_priv_send_event(iface, PRIVSEP_EVENT_SCAN_STARTED, NULL,
+				    0);
+		break;
 	case EVENT_SCAN_RESULTS:
 		wpa_priv_send_event(iface, PRIVSEP_EVENT_SCAN_RESULTS, NULL,
 				    0);
@@ -874,9 +991,12 @@
 	case EVENT_FT_RESPONSE:
 		wpa_priv_send_ft_response(iface, data);
 		break;
+	case EVENT_AUTH:
+		wpa_priv_send_auth(iface, data);
+		break;
 	default:
-		wpa_printf(MSG_DEBUG, "Unsupported driver event %d - TODO",
-			   event);
+		wpa_printf(MSG_DEBUG, "Unsupported driver event %d (%s) - TODO",
+			   event, event_to_string(event));
 		break;
 	}
 }
@@ -944,8 +1064,9 @@
 	       "contributors\n"
 	       "\n"
 	       "usage:\n"
-	       "  wpa_priv [-Bdd] [-P<pid file>] <driver:ifname> "
-	       "[driver:ifname ...]\n");
+	       "  wpa_priv [-Bdd] [-c<ctrl dir>] [-P<pid file>] "
+	       "<driver:ifname> \\\n"
+	       "           [driver:ifname ...]\n");
 }
 
 
@@ -982,20 +1103,20 @@
 			break;
 		default:
 			usage();
-			goto out;
+			goto out2;
 		}
 	}
 
 	if (optind >= argc) {
 		usage();
-		goto out;
+		goto out2;
 	}
 
 	wpa_printf(MSG_DEBUG, "wpa_priv control directory: '%s'", ctrl_dir);
 
 	if (eloop_init()) {
 		wpa_printf(MSG_ERROR, "Failed to initialize event loop");
-		goto out;
+		goto out2;
 	}
 
 	for (i = optind; i < argc; i++) {
@@ -1025,7 +1146,9 @@
 
 	eloop_destroy();
 
-	os_daemonize_terminate(pid_file);
+out2:
+	if (daemonize)
+		os_daemonize_terminate(pid_file);
 	os_free(pid_file);
 	os_program_deinit();
 
diff --git a/wpa_supplicant/wpa_supplicant.c b/wpa_supplicant/wpa_supplicant.c
index 722294d..7e5c07a 100644
--- a/wpa_supplicant/wpa_supplicant.c
+++ b/wpa_supplicant/wpa_supplicant.c
@@ -35,6 +35,7 @@
 #include "common/ieee802_11_defs.h"
 #include "common/hw_features_common.h"
 #include "p2p/p2p.h"
+#include "fst/fst.h"
 #include "blacklist.h"
 #include "wpas_glue.h"
 #include "wps_supplicant.h"
@@ -544,6 +545,10 @@
 	}
 
 	wmm_ac_notify_disassoc(wpa_s);
+
+	wpa_s->sched_scan_plans_num = 0;
+	os_free(wpa_s->sched_scan_plans);
+	wpa_s->sched_scan_plans = NULL;
 }
 
 
@@ -962,6 +967,11 @@
 			wpa_supplicant_terminate_proc(global);
 		}
 	}
+
+	if (wpa_debug_reopen_file() < 0) {
+		/* Ignore errors since we cannot really do much to fix this */
+		wpa_printf(MSG_DEBUG, "Could not reopen debug log file");
+	}
 }
 
 
@@ -1149,6 +1159,10 @@
 			return -1;
 	}
 
+#ifdef CONFIG_NO_WPA
+	wpa_s->group_cipher = WPA_CIPHER_NONE;
+	wpa_s->pairwise_cipher = WPA_CIPHER_NONE;
+#else /* CONFIG_NO_WPA */
 	sel = ie.group_cipher & ssid->group_cipher;
 	wpa_s->group_cipher = wpa_pick_group_cipher(sel);
 	if (wpa_s->group_cipher < 0) {
@@ -1168,6 +1182,7 @@
 	}
 	wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using PTK %s",
 		wpa_cipher_txt(wpa_s->pairwise_cipher));
+#endif /* CONFIG_NO_WPA */
 
 	sel = ie.key_mgmt & ssid->key_mgmt;
 #ifdef CONFIG_SAE
@@ -1585,6 +1600,8 @@
 	struct wpa_connect_work *cwork;
 	int rand_style;
 
+	wpa_s->own_disconnect_req = 0;
+
 	if (ssid->mac_addr == -1)
 		rand_style = wpa_s->conf->mac_addr;
 	else
@@ -1693,6 +1710,8 @@
 		return;
 	}
 
+	wpas_abort_ongoing_scan(wpa_s);
+
 	cwork = os_zalloc(sizeof(*cwork));
 	if (cwork == NULL)
 		return;
@@ -1714,6 +1733,36 @@
 }
 
 
+static int drv_supports_vht(struct wpa_supplicant *wpa_s,
+			    const struct wpa_ssid *ssid)
+{
+	enum hostapd_hw_mode hw_mode;
+	struct hostapd_hw_modes *mode = NULL;
+	u8 channel;
+	int i;
+
+#ifdef CONFIG_HT_OVERRIDES
+	if (ssid->disable_ht)
+		return 0;
+#endif /* CONFIG_HT_OVERRIDES */
+
+	hw_mode = ieee80211_freq_to_chan(ssid->frequency, &channel);
+	if (hw_mode == NUM_HOSTAPD_MODES)
+		return 0;
+	for (i = 0; wpa_s->hw.modes && i < wpa_s->hw.num_modes; i++) {
+		if (wpa_s->hw.modes[i].mode == hw_mode) {
+			mode = &wpa_s->hw.modes[i];
+			break;
+		}
+	}
+
+	if (!mode)
+		return 0;
+
+	return mode->vht_capab != 0;
+}
+
+
 void ibss_mesh_setup_freq(struct wpa_supplicant *wpa_s,
 			  const struct wpa_ssid *ssid,
 			  struct hostapd_freq_params *freq)
@@ -1726,8 +1775,10 @@
 	struct hostapd_channel_data *pri_chan = NULL, *sec_chan = NULL;
 	u8 channel;
 	int i, chan_idx, ht40 = -1, res, obss_scan = 1;
-	unsigned int j;
+	unsigned int j, k;
 	struct hostapd_freq_params vht_freq;
+	int chwidth, seg0, seg1;
+	u32 vht_caps = 0;
 
 	freq->freq = ssid->frequency;
 
@@ -1877,12 +1928,12 @@
 		   "IBSS/mesh: setup freq channel %d, sec_channel_offset %d",
 		   freq->channel, freq->sec_channel_offset);
 
-	/* Not sure if mesh is ready for VHT */
-	if (ssid->mode != WPAS_MODE_IBSS)
+	if (!drv_supports_vht(wpa_s, ssid))
 		return;
 
 	/* For IBSS check VHT_IBSS flag */
-	if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_VHT_IBSS))
+	if (ssid->mode == WPAS_MODE_IBSS &&
+	    !(wpa_s->drv_flags & WPA_DRIVER_FLAGS_VHT_IBSS))
 		return;
 
 	vht_freq = *freq;
@@ -1913,12 +1964,45 @@
 			return;
 	}
 
+	chwidth = VHT_CHANWIDTH_80MHZ;
+	seg0 = vht80[j] + 6;
+	seg1 = 0;
+
+	if (ssid->max_oper_chwidth == VHT_CHANWIDTH_80P80MHZ) {
+		/* setup center_freq2, bandwidth */
+		for (k = 0; k < ARRAY_SIZE(vht80); k++) {
+			/* Only accept 80 MHz segments separated by a gap */
+			if (j == k || abs(vht80[j] - vht80[k]) == 16)
+				continue;
+			for (i = vht80[k]; i < vht80[k] + 16; i += 4) {
+				struct hostapd_channel_data *chan;
+
+				chan = hw_get_channel_chan(mode, i, NULL);
+				if (!chan)
+					continue;
+
+				if (chan->flag & (HOSTAPD_CHAN_DISABLED |
+						  HOSTAPD_CHAN_NO_IR |
+						  HOSTAPD_CHAN_RADAR))
+					continue;
+
+				/* Found a suitable second segment for 80+80 */
+				chwidth = VHT_CHANWIDTH_80P80MHZ;
+				vht_caps |=
+					VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ;
+				seg1 = vht80[k] + 6;
+			}
+
+			if (chwidth == VHT_CHANWIDTH_80P80MHZ)
+				break;
+		}
+	}
+
 	if (hostapd_set_freq_params(&vht_freq, mode->mode, freq->freq,
 				    freq->channel, freq->ht_enabled,
 				    vht_freq.vht_enabled,
 				    freq->sec_channel_offset,
-				    VHT_CHANWIDTH_80MHZ,
-				    vht80[j] + 6, 0, 0) != 0)
+				    chwidth, seg0, seg1, vht_caps) != 0)
 		return;
 
 	*freq = vht_freq;
@@ -1966,7 +2050,8 @@
 
 	wpa_s->connect_work = work;
 
-	if (cwork->bss_removed || !wpas_valid_bss_ssid(wpa_s, bss, ssid)) {
+	if (cwork->bss_removed || !wpas_valid_bss_ssid(wpa_s, bss, ssid) ||
+	    wpas_network_disabled(wpa_s, ssid)) {
 		wpa_dbg(wpa_s, MSG_DEBUG, "BSS/SSID entry for association not valid anymore - drop connection attempt");
 		wpas_connect_work_done(wpa_s);
 		return;
@@ -2015,7 +2100,9 @@
 			wpa_ssid_txt(ssid->ssid, ssid->ssid_len));
 		os_memset(wpa_s->pending_bssid, 0, ETH_ALEN);
 	}
-	wpa_supplicant_cancel_sched_scan(wpa_s);
+	if (!wpa_s->pno)
+		wpa_supplicant_cancel_sched_scan(wpa_s);
+
 	wpa_supplicant_cancel_scan(wpa_s);
 
 	/* Starting new association, so clear the possibly used WPA IE from the
@@ -2188,6 +2275,18 @@
 		}
 	}
 
+#ifdef CONFIG_FST
+	if (wpa_s->fst_ies) {
+		int fst_ies_len = wpabuf_len(wpa_s->fst_ies);
+
+		if (wpa_ie_len + fst_ies_len <= sizeof(wpa_ie)) {
+			os_memcpy(wpa_ie + wpa_ie_len,
+				  wpabuf_head(wpa_s->fst_ies), fst_ies_len);
+			wpa_ie_len += fst_ies_len;
+		}
+	}
+#endif /* CONFIG_FST */
+
 	wpa_clear_keys(wpa_s, bss ? bss->bssid : NULL);
 	use_crypt = 1;
 	cipher_pairwise = wpa_s->pairwise_cipher;
@@ -2558,15 +2657,20 @@
 	} else
 		wpa_supplicant_enable_one_network(wpa_s, ssid);
 
-	if (wpa_s->reassociate && !wpa_s->disconnected) {
+	if (wpa_s->reassociate && !wpa_s->disconnected &&
+	    (!wpa_s->current_ssid ||
+	     wpa_s->wpa_state == WPA_DISCONNECTED ||
+	     wpa_s->wpa_state == WPA_SCANNING)) {
 		if (wpa_s->sched_scanning) {
 			wpa_printf(MSG_DEBUG, "Stop ongoing sched_scan to add "
 				   "new network to scan filters");
 			wpa_supplicant_cancel_sched_scan(wpa_s);
 		}
 
-		if (wpa_supplicant_fast_associate(wpa_s) != 1)
+		if (wpa_supplicant_fast_associate(wpa_s) != 1) {
+			wpa_s->scan_req = NORMAL_SCAN_REQ;
 			wpa_supplicant_req_scan(wpa_s, 0, 0);
+		}
 	}
 }
 
@@ -2667,7 +2771,8 @@
 			wpas_notify_network_enabled_changed(wpa_s, other_ssid);
 	}
 
-	if (ssid && ssid == wpa_s->current_ssid && wpa_s->current_ssid) {
+	if (ssid && ssid == wpa_s->current_ssid && wpa_s->current_ssid &&
+	    wpa_s->wpa_state >= WPA_AUTHENTICATING) {
 		/* We are already associated with the selected network */
 		wpa_printf(MSG_DEBUG, "Already associated with the "
 			   "selected network - do nothing");
@@ -2694,8 +2799,10 @@
 	wpa_s->reassociate = 1;
 
 	if (wpa_s->connect_without_scan ||
-	    wpa_supplicant_fast_associate(wpa_s) != 1)
+	    wpa_supplicant_fast_associate(wpa_s) != 1) {
+		wpa_s->scan_req = NORMAL_SCAN_REQ;
 		wpa_supplicant_req_scan(wpa_s, 0, disconnected ? 100000 : 0);
+	}
 
 	if (ssid)
 		wpas_notify_network_selected(wpa_s, ssid);
@@ -2770,6 +2877,11 @@
 	if (ap_scan < 0 || ap_scan > 2)
 		return -1;
 
+	if (ap_scan == 2 && os_strcmp(wpa_s->driver->name, "nl80211") == 0) {
+		wpa_printf(MSG_INFO,
+			   "Note: nl80211 driver interface is not designed to be used with ap_scan=2; this can result in connection failures");
+	}
+
 #ifdef ANDROID
 	if (ap_scan == 2 && ap_scan != wpa_s->conf->ap_scan &&
 	    wpa_s->wpa_state >= WPA_ASSOCIATING &&
@@ -3275,6 +3387,12 @@
 		}
 	}
 
+	if (wpa_s->conf->ap_scan == 2 &&
+	    os_strcmp(wpa_s->driver->name, "nl80211") == 0) {
+		wpa_printf(MSG_INFO,
+			   "Note: nl80211 driver interface is not designed to be used with ap_scan=2; this can result in connection failures");
+	}
+
 	wpa_clear_keys(wpa_s, NULL);
 
 	/* Make sure that TKIP countermeasures are not left enabled (could
@@ -3700,6 +3818,124 @@
 }
 
 
+#ifdef CONFIG_FST
+
+static const u8 * wpas_fst_get_bssid_cb(void *ctx)
+{
+	struct wpa_supplicant *wpa_s = ctx;
+
+	return (is_zero_ether_addr(wpa_s->bssid) ||
+		wpa_s->wpa_state != WPA_COMPLETED) ? NULL : wpa_s->bssid;
+}
+
+
+static void wpas_fst_get_channel_info_cb(void *ctx,
+					 enum hostapd_hw_mode *hw_mode,
+					 u8 *channel)
+{
+	struct wpa_supplicant *wpa_s = ctx;
+
+	if (wpa_s->current_bss) {
+		*hw_mode = ieee80211_freq_to_chan(wpa_s->current_bss->freq,
+						  channel);
+	} else if (wpa_s->hw.num_modes) {
+		*hw_mode = wpa_s->hw.modes[0].mode;
+	} else {
+		WPA_ASSERT(0);
+		*hw_mode = 0;
+	}
+}
+
+
+static int wpas_fst_get_hw_modes(void *ctx, struct hostapd_hw_modes **modes)
+{
+	struct wpa_supplicant *wpa_s = ctx;
+
+	*modes = wpa_s->hw.modes;
+	return wpa_s->hw.num_modes;
+}
+
+
+static void wpas_fst_set_ies_cb(void *ctx, const struct wpabuf *fst_ies)
+{
+	struct wpa_supplicant *wpa_s = ctx;
+
+	wpa_hexdump_buf(MSG_DEBUG, "FST: Set IEs", fst_ies);
+	wpa_s->fst_ies = fst_ies;
+}
+
+
+static int wpas_fst_send_action_cb(void *ctx, const u8 *da, struct wpabuf *data)
+{
+	struct wpa_supplicant *wpa_s = ctx;
+
+	WPA_ASSERT(os_memcmp(wpa_s->bssid, da, ETH_ALEN) == 0);
+	return wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid,
+					  wpa_s->own_addr, wpa_s->bssid,
+					  wpabuf_head(data), wpabuf_len(data),
+				   0);
+}
+
+
+static const struct wpabuf * wpas_fst_get_mb_ie_cb(void *ctx, const u8 *addr)
+{
+	struct wpa_supplicant *wpa_s = ctx;
+
+	WPA_ASSERT(os_memcmp(wpa_s->bssid, addr, ETH_ALEN) == 0);
+	return wpa_s->received_mb_ies;
+}
+
+
+static void wpas_fst_update_mb_ie_cb(void *ctx, const u8 *addr,
+				     const u8 *buf, size_t size)
+{
+	struct wpa_supplicant *wpa_s = ctx;
+	struct mb_ies_info info;
+
+	WPA_ASSERT(os_memcmp(wpa_s->bssid, addr, ETH_ALEN) == 0);
+
+	if (!mb_ies_info_by_ies(&info, buf, size)) {
+		wpabuf_free(wpa_s->received_mb_ies);
+		wpa_s->received_mb_ies = mb_ies_by_info(&info);
+	}
+}
+
+
+const u8 * wpas_fst_get_peer_first(void *ctx, struct fst_get_peer_ctx **get_ctx,
+				   Boolean mb_only)
+{
+	struct wpa_supplicant *wpa_s = ctx;
+
+	*get_ctx = NULL;
+	if (!is_zero_ether_addr(wpa_s->bssid))
+		return (wpa_s->received_mb_ies || !mb_only) ?
+			wpa_s->bssid : NULL;
+	return NULL;
+}
+
+
+const u8 * wpas_fst_get_peer_next(void *ctx, struct fst_get_peer_ctx **get_ctx,
+				  Boolean mb_only)
+{
+	return NULL;
+}
+
+void fst_wpa_supplicant_fill_iface_obj(struct wpa_supplicant *wpa_s,
+				       struct fst_wpa_obj *iface_obj)
+{
+	iface_obj->ctx              = wpa_s;
+	iface_obj->get_bssid        = wpas_fst_get_bssid_cb;
+	iface_obj->get_channel_info = wpas_fst_get_channel_info_cb;
+	iface_obj->get_hw_modes     = wpas_fst_get_hw_modes;
+	iface_obj->set_ies          = wpas_fst_set_ies_cb;
+	iface_obj->send_action      = wpas_fst_send_action_cb;
+	iface_obj->get_mb_ie        = wpas_fst_get_mb_ie_cb;
+	iface_obj->update_mb_ie     = wpas_fst_update_mb_ie_cb;
+	iface_obj->get_peer_first   = wpas_fst_get_peer_first;
+	iface_obj->get_peer_next    = wpas_fst_get_peer_next;
+}
+#endif /* CONFIG_FST */
+
 static int wpas_set_wowlan_triggers(struct wpa_supplicant *wpa_s,
 				    const struct wpa_driver_capa *capa)
 {
@@ -3718,6 +3954,56 @@
 }
 
 
+static enum wpa_radio_work_band wpas_freq_to_band(int freq)
+{
+	if (freq < 3000)
+		return BAND_2_4_GHZ;
+	if (freq > 50000)
+		return BAND_60_GHZ;
+	return BAND_5_GHZ;
+}
+
+
+static unsigned int wpas_get_bands(struct wpa_supplicant *wpa_s,
+				   const int *freqs)
+{
+	int i;
+	unsigned int band = 0;
+
+	if (freqs) {
+		/* freqs are specified for the radio work */
+		for (i = 0; freqs[i]; i++)
+			band |= wpas_freq_to_band(freqs[i]);
+	} else {
+		/*
+		 * freqs are not specified, implies all
+		 * the supported freqs by HW
+		 */
+		for (i = 0; i < wpa_s->hw.num_modes; i++) {
+			if (wpa_s->hw.modes[i].num_channels != 0) {
+				if (wpa_s->hw.modes[i].mode ==
+				    HOSTAPD_MODE_IEEE80211B ||
+				    wpa_s->hw.modes[i].mode ==
+				    HOSTAPD_MODE_IEEE80211G)
+					band |= BAND_2_4_GHZ;
+				else if (wpa_s->hw.modes[i].mode ==
+					 HOSTAPD_MODE_IEEE80211A)
+					band |= BAND_5_GHZ;
+				else if (wpa_s->hw.modes[i].mode ==
+					 HOSTAPD_MODE_IEEE80211AD)
+					band |= BAND_60_GHZ;
+				else if (wpa_s->hw.modes[i].mode ==
+					 HOSTAPD_MODE_IEEE80211ANY)
+					band = BAND_2_4_GHZ | BAND_5_GHZ |
+						BAND_60_GHZ;
+			}
+		}
+	}
+
+	return band;
+}
+
+
 static struct wpa_radio * radio_add_interface(struct wpa_supplicant *wpa_s,
 					      const char *rn)
 {
@@ -3770,11 +4056,103 @@
 	}
 #endif /* CONFIG_P2P */
 
+	if (work->started) {
+		work->wpa_s->radio->num_active_works--;
+		wpa_dbg(work->wpa_s, MSG_DEBUG,
+			"radio_work_free('%s'@%p: num_active_works --> %u",
+			work->type, work,
+			work->wpa_s->radio->num_active_works);
+	}
+
 	dl_list_del(&work->list);
 	os_free(work);
 }
 
 
+static struct wpa_radio_work * radio_work_get_next_work(struct wpa_radio *radio)
+{
+	struct wpa_radio_work *active_work = NULL;
+	struct wpa_radio_work *tmp;
+
+	/* Get the active work to know the type and band. */
+	dl_list_for_each(tmp, &radio->work, struct wpa_radio_work, list) {
+		if (tmp->started) {
+			active_work = tmp;
+			break;
+		}
+	}
+
+	if (!active_work) {
+		/* No active work, start one */
+		radio->num_active_works = 0;
+		dl_list_for_each(tmp, &radio->work, struct wpa_radio_work,
+				 list) {
+			if (os_strcmp(tmp->type, "scan") == 0 &&
+			    radio->external_scan_running &&
+			    (((struct wpa_driver_scan_params *)
+			      tmp->ctx)->only_new_results ||
+			     tmp->wpa_s->clear_driver_scan_cache))
+				continue;
+			return tmp;
+		}
+		return NULL;
+	}
+
+	if (os_strcmp(active_work->type, "sme-connect") == 0 ||
+	    os_strcmp(active_work->type, "connect") == 0) {
+		/*
+		 * If the active work is either connect or sme-connect,
+		 * do not parallelize them with other radio works.
+		 */
+		wpa_dbg(active_work->wpa_s, MSG_DEBUG,
+			"Do not parallelize radio work with %s",
+			active_work->type);
+		return NULL;
+	}
+
+	dl_list_for_each(tmp, &radio->work, struct wpa_radio_work, list) {
+		if (tmp->started)
+			continue;
+
+		/*
+		 * If connect or sme-connect are enqueued, parallelize only
+		 * those operations ahead of them in the queue.
+		 */
+		if (os_strcmp(tmp->type, "connect") == 0 ||
+		    os_strcmp(tmp->type, "sme-connect") == 0)
+			break;
+
+		/*
+		 * Check that the radio works are distinct and
+		 * on different bands.
+		 */
+		if (os_strcmp(active_work->type, tmp->type) != 0 &&
+		    (active_work->bands != tmp->bands)) {
+			/*
+			 * If a scan has to be scheduled through nl80211 scan
+			 * interface and if an external scan is already running,
+			 * do not schedule the scan since it is likely to get
+			 * rejected by kernel.
+			 */
+			if (os_strcmp(tmp->type, "scan") == 0 &&
+			    radio->external_scan_running &&
+			    (((struct wpa_driver_scan_params *)
+			      tmp->ctx)->only_new_results ||
+			     tmp->wpa_s->clear_driver_scan_cache))
+				continue;
+
+			wpa_dbg(active_work->wpa_s, MSG_DEBUG,
+				"active_work:%s new_work:%s",
+				active_work->type, tmp->type);
+			return tmp;
+		}
+	}
+
+	/* Did not find a radio work to schedule in parallel. */
+	return NULL;
+}
+
+
 static void radio_start_next_work(void *eloop_ctx, void *timeout_ctx)
 {
 	struct wpa_radio *radio = eloop_ctx;
@@ -3783,26 +4161,48 @@
 	struct wpa_supplicant *wpa_s;
 
 	work = dl_list_first(&radio->work, struct wpa_radio_work, list);
-	if (work == NULL)
-		return;
-
-	if (work->started)
-		return; /* already started and still in progress */
-
-	wpa_s = dl_list_first(&radio->ifaces, struct wpa_supplicant,
-			      radio_list);
-	if (wpa_s && wpa_s->radio->external_scan_running) {
-		wpa_printf(MSG_DEBUG, "Delay radio work start until externally triggered scan completes");
+	if (work == NULL) {
+		radio->num_active_works = 0;
 		return;
 	}
 
+	wpa_s = dl_list_first(&radio->ifaces, struct wpa_supplicant,
+			      radio_list);
+
+	if (!(wpa_s &&
+	      wpa_s->drv_flags & WPA_DRIVER_FLAGS_OFFCHANNEL_SIMULTANEOUS)) {
+		if (work->started)
+			return; /* already started and still in progress */
+
+		if (wpa_s && wpa_s->radio->external_scan_running) {
+			wpa_printf(MSG_DEBUG, "Delay radio work start until externally triggered scan completes");
+			return;
+		}
+	} else {
+		work = NULL;
+		if (radio->num_active_works < MAX_ACTIVE_WORKS) {
+			/* get the work to schedule next */
+			work = radio_work_get_next_work(radio);
+		}
+		if (!work)
+			return;
+	}
+
+	wpa_s = work->wpa_s;
 	os_get_reltime(&now);
 	os_reltime_sub(&now, &work->time, &diff);
-	wpa_dbg(work->wpa_s, MSG_DEBUG, "Starting radio work '%s'@%p after %ld.%06ld second wait",
+	wpa_dbg(wpa_s, MSG_DEBUG,
+		"Starting radio work '%s'@%p after %ld.%06ld second wait",
 		work->type, work, diff.sec, diff.usec);
 	work->started = 1;
 	work->time = now;
+	radio->num_active_works++;
+
 	work->cb(work, 0);
+
+	if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_OFFCHANNEL_SIMULTANEOUS) &&
+	    radio->num_active_works < MAX_ACTIVE_WORKS)
+		radio_work_check_next(wpa_s);
 }
 
 
@@ -3910,6 +4310,7 @@
 		   void (*cb)(struct wpa_radio_work *work, int deinit),
 		   void *ctx)
 {
+	struct wpa_radio *radio = wpa_s->radio;
 	struct wpa_radio_work *work;
 	int was_empty;
 
@@ -3924,6 +4325,16 @@
 	work->cb = cb;
 	work->ctx = ctx;
 
+	if (freq)
+		work->bands = wpas_freq_to_band(freq);
+	else if (os_strcmp(type, "scan") == 0 ||
+		 os_strcmp(type, "p2p-scan") == 0)
+		work->bands = wpas_get_bands(wpa_s,
+					     ((struct wpa_driver_scan_params *)
+					      ctx)->freqs);
+	else
+		work->bands = wpas_get_bands(wpa_s, NULL);
+
 	was_empty = dl_list_empty(&wpa_s->radio->work);
 	if (next)
 		dl_list_add(&wpa_s->radio->work, &work->list);
@@ -3932,6 +4343,12 @@
 	if (was_empty) {
 		wpa_dbg(wpa_s, MSG_DEBUG, "First radio work item in the queue - schedule start immediately");
 		radio_work_check_next(wpa_s);
+	} else if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_OFFCHANNEL_SIMULTANEOUS)
+		   && radio->num_active_works < MAX_ACTIVE_WORKS) {
+		wpa_dbg(wpa_s, MSG_DEBUG,
+			"Try to schedule a radio work (num_active_works=%u)",
+			radio->num_active_works);
+		radio_work_check_next(wpa_s);
 	}
 
 	return 0;
@@ -4187,6 +4604,11 @@
 		wpa_s->probe_resp_offloads = capa.probe_resp_offloads;
 		wpa_s->max_scan_ssids = capa.max_scan_ssids;
 		wpa_s->max_sched_scan_ssids = capa.max_sched_scan_ssids;
+		wpa_s->max_sched_scan_plans = capa.max_sched_scan_plans;
+		wpa_s->max_sched_scan_plan_interval =
+			capa.max_sched_scan_plan_interval;
+		wpa_s->max_sched_scan_plan_iterations =
+			capa.max_sched_scan_plan_iterations;
 		wpa_s->sched_scan_supported = capa.sched_scan_supported;
 		wpa_s->max_match_sets = capa.max_match_sets;
 		wpa_s->max_remain_on_chan = capa.max_remain_on_chan;
@@ -4238,6 +4660,28 @@
 		return -1;
 	}
 
+#ifdef CONFIG_FST
+	if (wpa_s->conf->fst_group_id) {
+		struct fst_iface_cfg cfg;
+		struct fst_wpa_obj iface_obj;
+
+		fst_wpa_supplicant_fill_iface_obj(wpa_s, &iface_obj);
+		os_strlcpy(cfg.group_id, wpa_s->conf->fst_group_id,
+			   sizeof(cfg.group_id));
+		cfg.priority = wpa_s->conf->fst_priority;
+		cfg.llt = wpa_s->conf->fst_llt;
+
+		wpa_s->fst = fst_attach(wpa_s->ifname, wpa_s->own_addr,
+					&iface_obj, &cfg);
+		if (!wpa_s->fst) {
+			wpa_msg(wpa_s, MSG_ERROR,
+				"FST: Cannot attach iface %s to group %s",
+				wpa_s->ifname, cfg.group_id);
+			return -1;
+		}
+	}
+#endif /* CONFIG_FST */
+
 	if (wpas_wps_init(wpa_s))
 		return -1;
 
@@ -4304,6 +4748,8 @@
 
 	wpas_rrm_reset(wpa_s);
 
+	wpas_sched_scan_plans_set(wpa_s, wpa_s->conf->sched_scan_plans);
+
 	return 0;
 }
 
@@ -4346,6 +4792,17 @@
 	wpas_ctrl_radio_work_flush(wpa_s);
 	radio_remove_interface(wpa_s);
 
+#ifdef CONFIG_FST
+	if (wpa_s->fst) {
+		fst_detach(wpa_s->fst);
+		wpa_s->fst = NULL;
+	}
+	if (wpa_s->received_mb_ies) {
+		wpabuf_free(wpa_s->received_mb_ies);
+		wpa_s->received_mb_ies = NULL;
+	}
+#endif /* CONFIG_FST */
+
 	if (wpa_s->drv_priv)
 		wpa_drv_deinit(wpa_s);
 
@@ -4578,6 +5035,33 @@
 #endif /* CONFIG_NO_WPA_MSG */
 
 
+#ifndef WPA_SUPPLICANT_CLEANUP_INTERVAL
+#define WPA_SUPPLICANT_CLEANUP_INTERVAL 10
+#endif /* WPA_SUPPLICANT_CLEANUP_INTERVAL */
+
+/* Periodic cleanup tasks */
+static void wpas_periodic(void *eloop_ctx, void *timeout_ctx)
+{
+	struct wpa_global *global = eloop_ctx;
+	struct wpa_supplicant *wpa_s;
+
+	eloop_register_timeout(WPA_SUPPLICANT_CLEANUP_INTERVAL, 0,
+			       wpas_periodic, global, NULL);
+
+#ifdef CONFIG_P2P
+	if (global->p2p)
+		p2p_expire_peers(global->p2p);
+#endif /* CONFIG_P2P */
+
+	for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) {
+		wpa_bss_flush_by_age(wpa_s, wpa_s->conf->bss_expiration_age);
+#ifdef CONFIG_AP
+		ap_periodic(wpa_s);
+#endif /* CONFIG_AP */
+	}
+}
+
+
 /**
  * wpa_supplicant_init - Initialize %wpa_supplicant
  * @params: Parameters for %wpa_supplicant
@@ -4652,9 +5136,11 @@
 	if (params->override_ctrl_interface)
 		global->params.override_ctrl_interface =
 			os_strdup(params->override_ctrl_interface);
+#ifdef CONFIG_P2P
 	if (params->conf_p2p_dev)
 		global->params.conf_p2p_dev =
 			os_strdup(params->conf_p2p_dev);
+#endif /* CONFIG_P2P */
 	wpa_debug_level = global->params.wpa_debug_level =
 		params->wpa_debug_level;
 	wpa_debug_show_keys = global->params.wpa_debug_show_keys =
@@ -4704,6 +5190,9 @@
 	}
 #endif /* CONFIG_WIFI_DISPLAY */
 
+	eloop_register_timeout(WPA_SUPPLICANT_CLEANUP_INTERVAL, 0,
+			       wpas_periodic, global, NULL);
+
 	return global;
 }
 
@@ -4755,6 +5244,8 @@
 	if (global == NULL)
 		return;
 
+	eloop_cancel_timeout(wpas_periodic, global, NULL);
+
 #ifdef CONFIG_WIFI_DISPLAY
 	wifi_display_deinit(global);
 #endif /* CONFIG_WIFI_DISPLAY */
@@ -4791,7 +5282,9 @@
 	os_free(global->params.ctrl_interface_group);
 	os_free(global->params.override_driver);
 	os_free(global->params.override_ctrl_interface);
+#ifdef CONFIG_P2P
 	os_free(global->params.conf_p2p_dev);
+#endif /* CONFIG_P2P */
 
 	os_free(global->p2p_disallow_freq.range);
 	os_free(global->p2p_go_avoid_freq.range);
@@ -4821,6 +5314,9 @@
 	if (wpa_s->conf->changed_parameters & CFG_CHANGED_EXT_PW_BACKEND)
 		wpas_init_ext_pw(wpa_s);
 
+	if (wpa_s->conf->changed_parameters & CFG_CHANGED_SCHED_SCAN_PLANS)
+		wpas_sched_scan_plans_set(wpa_s, wpa_s->conf->sched_scan_plans);
+
 #ifdef CONFIG_WPS
 	wpas_wps_update_config(wpa_s);
 #endif /* CONFIG_WPS */
@@ -5330,7 +5826,8 @@
 			continue;
 
 		if (ifs->current_ssid->mode == WPAS_MODE_AP ||
-		    ifs->current_ssid->mode == WPAS_MODE_P2P_GO)
+		    ifs->current_ssid->mode == WPAS_MODE_P2P_GO ||
+		    ifs->current_ssid->mode == WPAS_MODE_MESH)
 			freq = ifs->current_ssid->frequency;
 		else if (wpa_drv_get_bssid(ifs, bssid) == 0)
 			freq = ifs->assoc_freq;
@@ -5346,7 +5843,7 @@
 			freqs_data[idx++].freq = freq;
 
 		if (ifs->current_ssid->mode == WPAS_MODE_INFRA) {
-			freqs_data[i].flags = ifs->current_ssid->p2p_group ?
+			freqs_data[i].flags |= ifs->current_ssid->p2p_group ?
 				WPA_FREQ_USED_BY_P2P_CLIENT :
 				WPA_FREQ_USED_BY_INFRA_STATION;
 		}
diff --git a/wpa_supplicant/wpa_supplicant.conf b/wpa_supplicant/wpa_supplicant.conf
index d380965..2ce1cc4 100644
--- a/wpa_supplicant/wpa_supplicant.conf
+++ b/wpa_supplicant/wpa_supplicant.conf
@@ -108,12 +108,35 @@
 #    the driver reports successful association; each network block should have
 #    explicit security policy (i.e., only one option in the lists) for
 #    key_mgmt, pairwise, group, proto variables
+# Note: ap_scan=2 should not be used with the nl80211 driver interface (the
+# current Linux interface). ap_scan=1 is optimized work working with nl80211.
+# For finding networks using hidden SSID, scan_ssid=1 in the network block can
+# be used with nl80211.
 # When using IBSS or AP mode, ap_scan=2 mode can force the new network to be
 # created immediately regardless of scan results. ap_scan=1 mode will first try
 # to scan for existing networks and only if no matches with the enabled
 # networks are found, a new IBSS or AP mode network is created.
 ap_scan=1
 
+# Whether to force passive scan for network connection
+#
+# By default, scans will send out Probe Request frames on channels that allow
+# active scanning. This advertise the local station to the world. Normally this
+# is fine, but users may wish to do passive scanning where the radio should only
+# listen quietly for Beacon frames and not send any Probe Request frames. Actual
+# functionality may be driver dependent.
+#
+# This parameter can be used to force only passive scanning to be used
+# for network connection cases. It should be noted that this will slow
+# down scan operations and reduce likelihood of finding the AP. In
+# addition, some use cases will override this due to functional
+# requirements, e.g., for finding an AP that uses hidden SSID
+# (scan_ssid=1) or P2P device discovery.
+#
+# 0:  Do normal scans (allow active scans) (default)
+# 1:  Do passive scans.
+#passive_scan=0
+
 # MPM residency
 # By default, wpa_supplicant implements the mesh peering manager (MPM) for an
 # open mesh. However, if the driver can implement the MPM, you may set this to
@@ -291,7 +314,9 @@
 # up to the limit of 300 seconds (3, 9, 27 ... 300)
 # For periodic module, parameters would be <fixed interval>
 #autoscan=periodic:30
-# So a delay of 30 seconds will be applied between each scan
+# So a delay of 30 seconds will be applied between each scan.
+# Note: If sched_scan_plans are configured and supported by the driver,
+# autoscan is ignored.
 
 # filter_ssids - SSID-based scan result filtering
 # 0 = do not filter scan results (default)
@@ -593,6 +618,27 @@
 # Hotspot 2.0
 # hs20=1
 
+# Scheduled scan plans
+#
+# A space delimited list of scan plans. Each scan plan specifies the scan
+# interval and number of iterations, delimited by a colon. The last scan plan
+# will run infinitely and thus must specify only the interval and not the number
+# of iterations.
+#
+# The driver advertises the maximum number of scan plans supported. If more scan
+# plans than supported are configured, only the first ones are set (up to the
+# maximum supported). The last scan plan that specifies only the interval is
+# always set as the last plan.
+#
+# If the scan interval or the number of iterations for a scan plan exceeds the
+# maximum supported, it will be set to the maximum supported value.
+#
+# Format:
+# sched_scan_plans=<interval:iterations> <interval:iterations> ... <interval>
+#
+# Example:
+# sched_scan_plans=10:100 20:200 30
+
 # network block
 #
 # Each network (usually AP's sharing the same SSID) is configured as a separate
@@ -986,6 +1032,7 @@
 #	EAP workarounds are disabled with eap_workaround=0.
 #	For EAP-FAST, this must be set to 0 (or left unconfigured for the
 #	default value to be used automatically).
+# tls_disable_tlsv1_0=1 - disable use of TLSv1.0
 # tls_disable_tlsv1_1=1 - disable use of TLSv1.1 (a workaround for AAA servers
 #	that have issues interoperating with updated TLS version)
 # tls_disable_tlsv1_2=1 - disable use of TLSv1.2 (a workaround for AAA servers
@@ -1136,6 +1183,32 @@
 #  2: MCS 0-9
 #  3: not supported
 
+##### Fast Session Transfer (FST) support #####################################
+#
+# The options in this section are only available when the build configuration
+# option CONFIG_FST is set while compiling hostapd. They allow this interface
+# to be a part of FST setup.
+#
+# FST is the transfer of a session from a channel to another channel, in the
+# same or different frequency bands.
+#
+# For detals, see IEEE Std 802.11ad-2012.
+
+# Identifier of an FST Group  the interface belongs to.
+#fst_group_id=bond0
+
+# Interface priority within the FST Group.
+# Announcing a higher priority for an interface means declaring it more
+# preferable for FST switch.
+# fst_priority is in 1..255 range with 1 being the lowest priority.
+#fst_priority=100
+
+# Default LLT value for this interface in milliseconds. The value used in case
+# no value provided during session setup. Default is 50 msec.
+# fst_llt is in 1..4294967 range (due to spec limitation, see 10.32.2.2
+# Transitioning between states).
+#fst_llt=100
+
 # Example blocks:
 
 # Simple case: WPA-PSK, PSK as an ASCII passphrase, allow all valid ciphers
diff --git a/wpa_supplicant/wpa_supplicant_i.h b/wpa_supplicant/wpa_supplicant_i.h
index 4f63456..a8b273b 100644
--- a/wpa_supplicant/wpa_supplicant_i.h
+++ b/wpa_supplicant/wpa_supplicant_i.h
@@ -225,7 +225,7 @@
 	 * This can also be %NULL. In such a case, if a P2P Device dedicated
 	 * interfaces is created, the main configuration file will be used.
 	 */
-	const char *conf_p2p_dev;
+	char *conf_p2p_dev;
 #endif /* CONFIG_P2P */
 
 };
@@ -278,6 +278,7 @@
 	unsigned int p2p_24ghz_social_channels:1;
 	unsigned int pending_p2ps_group:1;
 	unsigned int pending_group_iface_for_p2ps:1;
+	unsigned int pending_p2ps_group_freq;
 
 #ifdef CONFIG_WIFI_DISPLAY
 	int wifi_display;
@@ -300,10 +301,19 @@
 	char name[16]; /* from driver_ops get_radio_name() or empty if not
 			* available */
 	unsigned int external_scan_running:1;
+	unsigned int num_active_works;
 	struct dl_list ifaces; /* struct wpa_supplicant::radio_list entries */
 	struct dl_list work; /* struct wpa_radio_work::list entries */
 };
 
+#define MAX_ACTIVE_WORKS 2
+
+enum wpa_radio_work_band {
+	BAND_2_4_GHZ = BIT(0),
+	BAND_5_GHZ = BIT(1),
+	BAND_60_GHZ = BIT(2),
+};
+
 /**
  * struct wpa_radio_work - Radio work item
  */
@@ -316,6 +326,7 @@
 	void *ctx;
 	unsigned int started:1;
 	struct os_reltime time;
+	unsigned int bands;
 };
 
 int radio_add_work(struct wpa_supplicant *wpa_s, unsigned int freq,
@@ -500,9 +511,10 @@
 
 	struct wpa_ssid *prev_sched_ssid; /* last SSID used in sched scan */
 	int sched_scan_timeout;
-	int sched_scan_interval;
 	int first_sched_scan;
 	int sched_scan_timed_out;
+	struct sched_scan_plan *sched_scan_plans;
+	size_t sched_scan_plans_num;
 
 	void (*scan_res_handler)(struct wpa_supplicant *wpa_s,
 				 struct wpa_scan_results *scan_res);
@@ -593,6 +605,8 @@
 	} scan_req, last_scan_req;
 	enum wpa_states scan_prev_wpa_state;
 	struct os_reltime scan_trigger_time, scan_start_time;
+	/* Minimum freshness requirement for connection purposes */
+	struct os_reltime scan_min_time;
 	int scan_runs; /* number of scan runs since WPS was started */
 	int *next_scan_freqs;
 	int *manual_scan_freqs;
@@ -632,6 +646,9 @@
 
 	int max_scan_ssids;
 	int max_sched_scan_ssids;
+	unsigned int max_sched_scan_plans;
+	unsigned int max_sched_scan_plan_interval;
+	unsigned int max_sched_scan_plan_iterations;
 	int sched_scan_supported;
 	unsigned int max_match_sets;
 	unsigned int max_remain_on_chan;
@@ -656,6 +673,7 @@
 	unsigned int reattach:1; /* reassociation to the same BSS requested */
 	unsigned int mac_addr_changed:1;
 	unsigned int added_vif:1;
+	unsigned int wnmsleep_used:1;
 
 	struct os_reltime last_mac_addr_change;
 	int last_mac_addr_style;
@@ -720,6 +738,7 @@
 	int mesh_if_idx;
 	unsigned int mesh_if_created:1;
 	unsigned int mesh_ht_enabled:1;
+	unsigned int mesh_vht_enabled:1;
 	int mesh_auth_block_duration; /* sec */
 #endif /* CONFIG_MESH */
 
@@ -820,7 +839,7 @@
 	unsigned int p2p_nfc_tag_enabled:1;
 	unsigned int p2p_peer_oob_pk_hash_known:1;
 	unsigned int p2p_disable_ip_addr_req:1;
-	unsigned int p2ps_join_addr_valid:1;
+	unsigned int p2ps_method_config_any:1;
 	unsigned int p2p_cli_probe:1;
 	int p2p_persistent_go_freq;
 	int p2p_persistent_id;
@@ -842,6 +861,9 @@
 	int *p2p_group_common_freqs;
 	unsigned int p2p_group_common_freqs_num;
 	u8 p2ps_join_addr[ETH_ALEN];
+
+	unsigned int p2p_go_max_oper_chwidth;
+	unsigned int p2p_go_vht_center_freq2;
 #endif /* CONFIG_P2P */
 
 	struct wpa_ssid *bgscan_ssid;
@@ -967,6 +989,7 @@
 	struct l2_packet_data *l2_test;
 	unsigned int extra_roc_dur;
 	enum wpa_supplicant_test_failure test_failure;
+	unsigned int p2p_go_csa_on_inv:1;
 #endif /* CONFIG_TESTING_OPTIONS */
 
 	struct wmm_ac_assoc_data *wmm_ac_assoc_info;
@@ -977,6 +1000,12 @@
 	u8 last_tspecs_count;
 
 	struct rrm_data rrm;
+
+#ifdef CONFIG_FST
+	struct fst_iface *fst;
+	const struct wpabuf *fst_ies;
+	struct wpabuf *received_mb_ies;
+#endif /* CONFIG_FST */
 };
 
 
@@ -1149,4 +1178,16 @@
 			   int *freq_array, unsigned int len);
 
 void wpas_network_reenabled(void *eloop_ctx, void *timeout_ctx);
+
+#ifdef CONFIG_FST
+
+struct fst_wpa_obj;
+
+void fst_wpa_supplicant_fill_iface_obj(struct wpa_supplicant *wpa_s,
+				       struct fst_wpa_obj *iface_obj);
+
+#endif /* CONFIG_FST */
+
+int wpas_sched_scan_plans_set(struct wpa_supplicant *wpa_s, const char *cmd);
+
 #endif /* WPA_SUPPLICANT_I_H */
diff --git a/wpa_supplicant/wpas_glue.c b/wpa_supplicant/wpas_glue.c
index 29c22ba..7986695 100644
--- a/wpa_supplicant/wpas_glue.c
+++ b/wpa_supplicant/wpas_glue.c
@@ -1013,7 +1013,6 @@
 
 	wpa_drv_set_rekey_info(wpa_s, kek, kek_len, kck, kck_len, replay_ctr);
 }
-#endif /* CONFIG_NO_WPA */
 
 
 static int wpa_supplicant_key_mgmt_set_pmk(void *ctx, const u8 *pmk,
@@ -1028,6 +1027,7 @@
 	else
 		return 0;
 }
+#endif /* CONFIG_NO_WPA */
 
 
 int wpa_supplicant_init_wpa(struct wpa_supplicant *wpa_s)
@@ -1124,6 +1124,7 @@
 			}
 		}
 #endif /* CONFIG_P2P */
+		conf.wpa_rsc_relaxation = wpa_s->conf->wpa_rsc_relaxation;
 	}
 	wpa_sm_set_config(wpa_s->wpa, ssid ? &conf : NULL);
 }
diff --git a/wpa_supplicant/wps_supplicant.c b/wpa_supplicant/wps_supplicant.c
index 2db7914..5c674b2 100644
--- a/wpa_supplicant/wps_supplicant.c
+++ b/wpa_supplicant/wps_supplicant.c
@@ -683,6 +683,13 @@
 }
 
 
+int wpas_wps_reenable_networks_pending(struct wpa_supplicant *wpa_s)
+{
+	return eloop_is_timeout_registered(wpas_wps_reenable_networks_cb,
+					   wpa_s, NULL);
+}
+
+
 static void wpa_supplicant_wps_event_success(struct wpa_supplicant *wpa_s)
 {
 	wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_SUCCESS);
@@ -955,8 +962,20 @@
 static void wpas_wps_timeout(void *eloop_ctx, void *timeout_ctx)
 {
 	struct wpa_supplicant *wpa_s = eloop_ctx;
+	union wps_event_data data;
+
 	wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_TIMEOUT "Requested operation timed "
 		"out");
+	os_memset(&data, 0, sizeof(data));
+	data.fail.config_error = WPS_CFG_MSG_TIMEOUT;
+	data.fail.error_indication = WPS_EI_NO_ERROR;
+	/*
+	 * Call wpas_notify_wps_event_fail() directly instead of through
+	 * wpa_supplicant_wps_event() which would end up registering unnecessary
+	 * timeouts (those are only for the case where the failure happens
+	 * during an EAP-WSC exchange).
+	 */
+	wpas_notify_wps_event_fail(wpa_s, &data.fail);
 	wpas_clear_wps(wpa_s);
 }
 
@@ -1236,6 +1255,22 @@
 }
 
 
+void wpas_wps_pbc_overlap(struct wpa_supplicant *wpa_s)
+{
+	union wps_event_data data;
+
+	os_memset(&data, 0, sizeof(data));
+	data.fail.config_error = WPS_CFG_MULTIPLE_PBC_DETECTED;
+	data.fail.error_indication = WPS_EI_NO_ERROR;
+	/*
+	 * Call wpas_notify_wps_event_fail() directly instead of through
+	 * wpa_supplicant_wps_event() which would end up registering unnecessary
+	 * timeouts (those are only for the case where the failure happens
+	 * during an EAP-WSC exchange).
+	 */
+	wpas_notify_wps_event_fail(wpa_s, &data.fail);
+}
+
 /* Cancel the wps pbc/pin requests */
 int wpas_wps_cancel(struct wpa_supplicant *wpa_s)
 {
diff --git a/wpa_supplicant/wps_supplicant.h b/wpa_supplicant/wps_supplicant.h
index 683bd50..c8fe47e 100644
--- a/wpa_supplicant/wps_supplicant.h
+++ b/wpa_supplicant/wps_supplicant.h
@@ -33,6 +33,7 @@
 		       int p2p_group);
 int wpas_wps_start_pin(struct wpa_supplicant *wpa_s, const u8 *bssid,
 		       const char *pin, int p2p_group, u16 dev_pw_id);
+void wpas_wps_pbc_overlap(struct wpa_supplicant *wpa_s);
 int wpas_wps_cancel(struct wpa_supplicant *wpa_s);
 int wpas_wps_start_reg(struct wpa_supplicant *wpa_s, const u8 *bssid,
 		       const char *pin, struct wps_new_ap_settings *settings);
@@ -84,6 +85,7 @@
 void wpas_wps_update_ap_info(struct wpa_supplicant *wpa_s,
 			     struct wpa_scan_results *scan_res);
 void wpas_wps_notify_assoc(struct wpa_supplicant *wpa_s, const u8 *bssid);
+int wpas_wps_reenable_networks_pending(struct wpa_supplicant *wpa_s);
 
 #else /* CONFIG_WPS */
 
@@ -146,6 +148,12 @@
 {
 }
 
+static inline int
+wpas_wps_reenable_networks_pending(struct wpa_supplicant *wpa_s)
+{
+	return 0;
+}
+
 #endif /* CONFIG_WPS */
 
 #endif /* WPS_SUPPLICANT_H */